]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hwspinlock: Introduce hwspin_lock_bust()
authorRichard Maina <quic_rmaina@quicinc.com>
Wed, 29 May 2024 18:09:55 +0000 (11:09 -0700)
committerBjorn Andersson <andersson@kernel.org>
Wed, 29 May 2024 19:52:26 +0000 (12:52 -0700)
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>
Documentation/locking/hwspinlock.rst
drivers/hwspinlock/hwspinlock_core.c
drivers/hwspinlock/hwspinlock_internal.h
include/linux/hwspinlock.h

index 6f03713b70039020c82f6b12f8f323c2c0853c76..2ffaa3cbd63f1642a0049b2264752dc7b190a1fc 100644 (file)
@@ -85,6 +85,17 @@ is already free).
 
 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);
index 0c0a932c00f35e058b94a3e631c36819c83c5498..6505261e60686ea2c5cef16c453ed29e65e63de6 100644 (file)
@@ -305,6 +305,34 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
 }
 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
  * @hwlock_spec: hwlock specifier as found in the device tree
index 29892767bb7a0aee348ed7b3343ba1d756b018f0..f298fc0ee5adbc813e46d06c60617903bded51b9 100644 (file)
@@ -21,6 +21,8 @@ struct hwspinlock_device;
  * @trylock: make a single attempt to take the lock. returns 0 on
  *          failure and true on success. 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
  *          core while spinning on a lock, between two successive
  *          invocations of @trylock. may _not_ sleep.
@@ -28,6 +30,7 @@ struct hwspinlock_device;
 struct hwspinlock_ops {
        int (*trylock)(struct hwspinlock *lock);
        void (*unlock)(struct hwspinlock *lock);
+       int (*bust)(struct hwspinlock *lock, unsigned int id);
        void (*relax)(struct hwspinlock *lock);
 };
 
index bfe7c1f1ac6d1ce7221576e38edc97878c4713c2..f0231dbc47771023afda19734aa3b87f120f8b8f 100644 (file)
@@ -68,6 +68,7 @@ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
 int __hwspin_trylock(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 hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id);
 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_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)
 {
        return 0;