Currently, there is no convenient way to see the info that the
ref_tracking infrastructure collects. Attempt to create a file in
debugfs when called from ref_tracker_dir_init().

The file is given the name "class@%px", as having the unmodified address
is helpful for debugging. This should be safe since this directory is only
accessible by root

If debugfs file creation fails, a pr_warn will be isssued.

Signed-off-by: Jeff Layton <jlay...@kernel.org>
---
 include/linux/ref_tracker.h | 13 ++++++++
 lib/ref_tracker.c           | 73 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/include/linux/ref_tracker.h b/include/linux/ref_tracker.h
index 
3968f993db81e95c0d58c81454311841c1b9cd35..17d5924d595ce95bfb5d8ec6d813490499bd89d0
 100644
--- a/include/linux/ref_tracker.h
+++ b/include/linux/ref_tracker.h
@@ -20,12 +20,17 @@ struct ref_tracker_dir {
        struct list_head        list; /* List of active trackers */
        struct list_head        quarantine; /* List of dead trackers */
        const char              *class; /* object classname */
+#ifdef CONFIG_DEBUG_FS
+       struct dentry           *dentry;
+#endif
        char                    name[32];
 #endif
 };
 
 #ifdef CONFIG_REF_TRACKER
 
+void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir);
+
 static inline void ref_tracker_dir_init(struct ref_tracker_dir *dir,
                                        unsigned int quarantine_count,
                                        const char *class,
@@ -39,7 +44,11 @@ static inline void ref_tracker_dir_init(struct 
ref_tracker_dir *dir,
        refcount_set(&dir->untracked, 1);
        refcount_set(&dir->no_tracker, 1);
        dir->class = class;
+#ifdef CONFIG_DEBUG_FS
+       dir->dentry = NULL;
+#endif
        strscpy(dir->name, name, sizeof(dir->name));
+       ref_tracker_dir_debugfs(dir);
        stack_depot_init();
 }
 
@@ -68,6 +77,10 @@ static inline void ref_tracker_dir_init(struct 
ref_tracker_dir *dir,
 {
 }
 
+static inline void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir)
+{
+}
+
 static inline void ref_tracker_dir_exit(struct ref_tracker_dir *dir)
 {
 }
diff --git a/lib/ref_tracker.c b/lib/ref_tracker.c
index 
66a765cdfc471968fd6434dafc5b654962f20667..723df31321242d6af267986cc56a9d80b6e5ad18
 100644
--- a/lib/ref_tracker.c
+++ b/lib/ref_tracker.c
@@ -31,6 +31,14 @@ struct ref_tracker_dir_stats {
        } stacks[];
 };
 
+#ifdef CONFIG_DEBUG_FS
+static void ref_tracker_debugfs_remove(struct ref_tracker_dir *dir);
+#else
+static inline void ref_tracker_debugfs_remove(struct ref_tracker_dir *dir)
+{
+}
+#endif
+
 static struct ref_tracker_dir_stats *
 ref_tracker_get_stats(struct ref_tracker_dir *dir, unsigned int limit)
 {
@@ -195,6 +203,7 @@ void ref_tracker_dir_exit(struct ref_tracker_dir *dir)
        bool leak = false;
 
        dir->dead = true;
+       ref_tracker_debugfs_remove(dir);
        spin_lock_irqsave(&dir->lock, flags);
        list_for_each_entry_safe(tracker, n, &dir->quarantine, head) {
                list_del(&tracker->head);
@@ -311,8 +320,7 @@ EXPORT_SYMBOL_GPL(ref_tracker_free);
 #ifdef CONFIG_DEBUG_FS
 #include <linux/debugfs.h>
 
-static __maybe_unused int
-ref_tracker_dir_seq_print(struct ref_tracker_dir *dir, struct seq_file *seq)
+static int ref_tracker_dir_seq_print(struct ref_tracker_dir *dir, struct 
seq_file *seq)
 {
        struct ostream os = { .func = pr_ostream_seq,
                              .prefix = "",
@@ -326,6 +334,67 @@ ref_tracker_dir_seq_print(struct ref_tracker_dir *dir, 
struct seq_file *seq)
        return os.used;
 }
 
+static int ref_tracker_debugfs_show(struct seq_file *f, void *v)
+{
+       struct ref_tracker_dir *dir = f->private;
+
+       return ref_tracker_dir_seq_print(dir, f);
+}
+
+static int ref_tracker_debugfs_open(struct inode *inode, struct file *filp)
+{
+       struct ref_tracker_dir *dir = inode->i_private;
+
+       return single_open(filp, ref_tracker_debugfs_show, dir);
+}
+
+static const struct file_operations ref_tracker_debugfs_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ref_tracker_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/**
+ * ref_tracker_dir_debugfs - create debugfs file for ref_tracker_dir
+ * @dir: ref_tracker_dir to be associated with debugfs file
+ *
+ * In most cases, a debugfs file will be created automatically for every
+ * ref_tracker_dir. If the object was created before debugfs is brought up
+ * then that may fail. In those cases, it is safe to call this at a later
+ * time to create the file.
+ */
+void ref_tracker_dir_debugfs(struct ref_tracker_dir *dir)
+{
+       char name[NAME_MAX + 1];
+       int ret;
+
+       /* No-op if already created */
+       if (!IS_ERR_OR_NULL(dir->dentry))
+               return;
+
+       ret = snprintf(name, sizeof(name), "%s@%px", dir->class, dir);
+       name[sizeof(name) - 1] = '\0';
+
+       if (ret < sizeof(name))
+               dir->dentry = debugfs_create_file(name, S_IFREG | 0400,
+                                                 ref_tracker_debug_dir, dir,
+                                                 &ref_tracker_debugfs_fops);
+       else
+               dir->dentry = ERR_PTR(-ENAMETOOLONG);
+
+       if (IS_ERR(dir->dentry))
+               pr_warn("ref_tracker: unable to create debugfs file for %s: 
%pe\n",
+                       name, dir->dentry);
+}
+EXPORT_SYMBOL(ref_tracker_dir_debugfs);
+
+static void ref_tracker_debugfs_remove(struct ref_tracker_dir *dir)
+{
+       debugfs_remove(dir->dentry);
+}
+
 static int __init ref_tracker_debugfs_init(void)
 {
        ref_tracker_debug_dir = debugfs_create_dir("ref_tracker", NULL);

-- 
2.49.0

Reply via email to