Add a subsystem entry in debugfs and place a summary file there. It
informs about registered locks, if they are in use, and to which device
they belong. The state of the lock itself is usually not accessible
without modifying the state, so there is no plan to add support for
that.

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

diff --git a/drivers/hwspinlock/hwspinlock_core.c 
b/drivers/hwspinlock/hwspinlock_core.c
index 7aa597a28eec..dbfedc47abc5 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"
 
@@ -888,5 +890,83 @@ struct hwspinlock 
*devm_hwspin_lock_request_specific(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_hwspin_lock_request_specific);
 
+#ifdef CONFIG_DEBUG_FS
+struct hwspinlock_seq_iterator {
+       void __rcu **slot;
+       struct radix_tree_iter iter;
+};
+
+static void *hwspinlock_seq_start(struct seq_file *s, loff_t *ppos)
+{
+       struct hwspinlock_seq_iterator *hwsp_seq_iter = 
kzalloc(sizeof(*hwsp_seq_iter), GFP_KERNEL);
+
+       seq_puts(s, "id\tstatus\tdevice\n");
+
+       if (!hwsp_seq_iter)
+               return NULL;
+
+       mutex_lock(&hwspinlock_tree_lock);
+       radix_tree_iter_init(&hwsp_seq_iter->iter, *ppos);
+       hwsp_seq_iter->slot = radix_tree_next_chunk(&hwspinlock_tree, 
&hwsp_seq_iter->iter, 0);
+
+       return hwsp_seq_iter->slot ? hwsp_seq_iter : NULL;
+}
+
+static void *hwspinlock_seq_next(struct seq_file *s, void *v, loff_t *ppos)
+{
+       struct hwspinlock_seq_iterator *hwsp_seq_iter = v;
+
+       ++*ppos;
+
+       hwsp_seq_iter->slot = radix_tree_next_slot(hwsp_seq_iter->slot, 
&hwsp_seq_iter->iter, 0);
+       if (!hwsp_seq_iter->slot)
+               hwsp_seq_iter->slot = radix_tree_next_chunk(&hwspinlock_tree, 
&hwsp_seq_iter->iter, 0);
+
+       return hwsp_seq_iter->slot ? hwsp_seq_iter : NULL;
+}
+
+static void hwspinlock_seq_stop(struct seq_file *s, void *v)
+{
+       kfree(v);
+       mutex_unlock(&hwspinlock_tree_lock);
+}
+
+static int hwspinlock_seq_show(struct seq_file *s, void *v)
+{
+       struct hwspinlock_seq_iterator *hwsp_seq_iter = v;
+       unsigned long id = hwsp_seq_iter->iter.index;
+       struct hwspinlock *hwlock;
+       int used;
+
+       used = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_USED);
+       hwlock = radix_tree_deref_slot(hwsp_seq_iter->slot);
+       seq_printf(s, "%4lu:\t%s\t%s\n", id, used ? "in use" : "free", 
dev_name(hwlock->bank->dev));
+       return 0;
+}
+
+static const struct seq_operations hwspinlock_sops = {
+       .start = hwspinlock_seq_start,
+       .next = hwspinlock_seq_next,
+       .stop = hwspinlock_seq_stop,
+       .show = hwspinlock_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 hwspinlock_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(hwspinlock_init);
+#endif /* DEBUG_FS */
+
 MODULE_DESCRIPTION("Hardware spinlock interface");
 MODULE_AUTHOR("Ohad Ben-Cohen <[email protected]>");
-- 
2.51.0


Reply via email to