[PATCH v4 2/3] hwspinlock: add list of mailboxes to debugfs
From: Wolfram Sang
Date: Sat Jul 18 2026 - 12:30:48 EST
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 <wsa+renesas@xxxxxxxxxxxxxxxxxxxx>
---
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 <ohad@xxxxxxxxxx>");
--
2.51.0