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

