To help debugging, offer a list of registered hwspinlocks in debugfs. It
will also print the responsible hwspinlock_device and if the spinlock is
currently used. It does not show if the lock is taken because of
side-effects. Often, reading a lock means actually taking it.

Signed-off-by: Wolfram Sang <[email protected]>
---
 drivers/hwspinlock/hwspinlock_core.c | 67 ++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/drivers/hwspinlock/hwspinlock_core.c 
b/drivers/hwspinlock/hwspinlock_core.c
index e2b7a2271de6..84e9aae6ef44 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -9,6 +9,7 @@
 
 #define pr_fmt(fmt)    "%s: " fmt, __func__
 
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -21,6 +22,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/seq_file.h>
 
 #include "hwspinlock_internal.h"
 
@@ -858,5 +860,70 @@ struct hwspinlock 
*devm_hwspin_lock_request_specific(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_hwspin_lock_request_specific);
 
+#ifdef CONFIG_DEBUG_FS
+static void *hwspin_lock_seq_start(struct seq_file *s, loff_t *ppos)
+{
+       unsigned long index = *ppos;
+       struct hwspinlock *hwlock;
+
+       rcu_read_lock();
+       hwlock = xa_find(&hwspinlocks, &index, ULONG_MAX, XA_PRESENT);
+       *ppos = index;
+
+       return hwlock;
+}
+
+static void *hwspin_lock_seq_next(struct seq_file *s, void *v, loff_t *ppos)
+{
+       /* Increase ppos here to avoid endless loops. Don't use xa_find_after() 
*/
+       unsigned long index = *ppos + 1;
+       struct hwspinlock *hwlock;
+
+       hwlock = xa_find(&hwspinlocks, &index, ULONG_MAX, XA_PRESENT);
+       *ppos = index;
+
+       return hwlock;
+}
+
+static void hwspin_lock_seq_stop(struct seq_file *s, void *v)
+{
+       rcu_read_unlock();
+}
+
+static int hwspin_lock_seq_show(struct seq_file *s, void *v)
+{
+       struct hwspinlock *hwlock = v;
+       bool unused = xa_get_mark(&hwspinlocks, s->index, HWSPINLOCK_UNUSED);
+
+       seq_printf(s, "%4llu:\t%s\t%s\n", s->index, unused ? "free" : "in use",
+                  dev_name(hwlock->bank->dev));
+       return 0;
+}
+
+static const struct seq_operations hwspinlock_sops = {
+       .start = hwspin_lock_seq_start,
+       .next = hwspin_lock_seq_next,
+       .stop = hwspin_lock_seq_stop,
+       .show = hwspin_lock_seq_show,
+};
+DEFINE_SEQ_ATTRIBUTE(hwspinlock);
+
+/*
+ * subsys_initcall() is used here but controllers may already have been
+ * registered earlier or will be later. The rationale is that debugfs is
+ * accessed only late, i.e. from userspace. So, files created here must make no
+ * assumptions about initcall ordering.
+ */
+static int __init hwspin_lock_init(void)
+{
+       struct dentry *hwspinlock_debugfs = debugfs_create_dir("hwspinlock", 
NULL);
+
+       debugfs_create_file("hwspinlock_summary", 0444, hwspinlock_debugfs,
+                           NULL, &hwspinlock_fops);
+       return 0;
+}
+subsys_initcall(hwspin_lock_init);
+#endif /* DEBUG_FS */
+
 MODULE_DESCRIPTION("Hardware spinlock interface");
 MODULE_AUTHOR("Ohad Ben-Cohen <[email protected]>");
-- 
2.51.0


Reply via email to