1

hwspinlock: Introduce hwspin_lock_bust()

When a remoteproc crashes or goes down unexpectedly this can result in
a state where locks held by the remoteproc will remain locked possibly
resulting in deadlock. This new API hwspin_lock_bust() allows
hwspinlock implementers to define a bust operation for freeing previously
acquired hwspinlocks after verifying ownership of the acquired lock.

Signed-off-by: Richard Maina <quic_rmaina@quicinc.com>
Reviewed-by: Bjorn Andersson <andersson@kernel.org>
Signed-off-by: Chris Lew <quic_clew@quicinc.com>
Link: https://lore.kernel.org/r/20240529-hwspinlock-bust-v3-1-c8b924ffa5a2@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
This commit is contained in:
Richard Maina 2024-05-29 11:09:55 -07:00 committed by Bjorn Andersson
parent 1613e604df
commit 7c327d5659
4 changed files with 48 additions and 0 deletions

View File

@ -85,6 +85,17 @@ is already free).
Should be called from a process context (might sleep). Should be called from a process context (might sleep).
::
int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id);
After verifying the owner of the hwspinlock, release a previously acquired
hwspinlock; returns 0 on success, or an appropriate error code on failure
(e.g. -EOPNOTSUPP if the bust operation is not defined for the specific
hwspinlock).
Should be called from a process context (might sleep).
:: ::
int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout); int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout);

View File

@ -305,6 +305,34 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
} }
EXPORT_SYMBOL_GPL(__hwspin_unlock); EXPORT_SYMBOL_GPL(__hwspin_unlock);
/**
* hwspin_lock_bust() - bust a specific hwspinlock
* @hwlock: a previously-acquired hwspinlock which we want to bust
* @id: identifier of the remote lock holder, if applicable
*
* This function will bust a hwspinlock that was previously acquired as
* long as the current owner of the lock matches the id given by the caller.
*
* Context: Process context.
*
* Returns: 0 on success, or -EINVAL if the hwspinlock does not exist, or
* the bust operation fails, and -EOPNOTSUPP if the bust operation is not
* defined for the hwspinlock.
*/
int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id)
{
if (WARN_ON(!hwlock))
return -EINVAL;
if (!hwlock->bank->ops->bust) {
pr_err("bust operation not defined\n");
return -EOPNOTSUPP;
}
return hwlock->bank->ops->bust(hwlock, id);
}
EXPORT_SYMBOL_GPL(hwspin_lock_bust);
/** /**
* of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
* @hwlock_spec: hwlock specifier as found in the device tree * @hwlock_spec: hwlock specifier as found in the device tree

View File

@ -21,6 +21,8 @@ struct hwspinlock_device;
* @trylock: make a single attempt to take the lock. returns 0 on * @trylock: make a single attempt to take the lock. returns 0 on
* failure and true on success. may _not_ sleep. * failure and true on success. may _not_ sleep.
* @unlock: release the lock. always succeed. may _not_ sleep. * @unlock: release the lock. always succeed. may _not_ sleep.
* @bust: optional, platform-specific bust handler, called by hwspinlock
* core to bust a specific lock.
* @relax: optional, platform-specific relax handler, called by hwspinlock * @relax: optional, platform-specific relax handler, called by hwspinlock
* core while spinning on a lock, between two successive * core while spinning on a lock, between two successive
* invocations of @trylock. may _not_ sleep. * invocations of @trylock. may _not_ sleep.
@ -28,6 +30,7 @@ struct hwspinlock_device;
struct hwspinlock_ops { struct hwspinlock_ops {
int (*trylock)(struct hwspinlock *lock); int (*trylock)(struct hwspinlock *lock);
void (*unlock)(struct hwspinlock *lock); void (*unlock)(struct hwspinlock *lock);
int (*bust)(struct hwspinlock *lock, unsigned int id);
void (*relax)(struct hwspinlock *lock); void (*relax)(struct hwspinlock *lock);
}; };

View File

@ -68,6 +68,7 @@ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
int __hwspin_trylock(struct hwspinlock *, int, unsigned long *); int __hwspin_trylock(struct hwspinlock *, int, unsigned long *);
void __hwspin_unlock(struct hwspinlock *, int, unsigned long *); void __hwspin_unlock(struct hwspinlock *, int, unsigned long *);
int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name); int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name);
int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id);
int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock); int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock);
struct hwspinlock *devm_hwspin_lock_request(struct device *dev); struct hwspinlock *devm_hwspin_lock_request(struct device *dev);
struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev, struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev,
@ -127,6 +128,11 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
{ {
} }
static inline int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id)
{
return 0;
}
static inline int of_hwspin_lock_get_id(struct device_node *np, int index) static inline int of_hwspin_lock_get_id(struct device_node *np, int index)
{ {
return 0; return 0;