Here is a patch that will track live engines and expose them via debugfs.
This will show if there are stale engines and their refcounts, also to
determine if there are any engine slab leaks.

This is just for debug purposes. Needs tweaking if this needs to be
part of the core patch (ifdefs, etc).

Applies atop the rcu removal patch sent last week:
https://www.redhat.com/archives/utrace-devel/2009-January/msg00075.html

Signed-off-by: Ananth N Mavinakayanahalli <ana...@in.ibm.com>
---
 include/linux/utrace.h |    1 
 kernel/utrace.c        |   99 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 99 insertions(+), 1 deletion(-)

Index: utrace-20jan/include/linux/utrace.h
===================================================================
--- utrace-20jan.orig/include/linux/utrace.h
+++ utrace-20jan/include/linux/utrace.h
@@ -317,6 +317,7 @@ struct utrace_attached_engine {
 /* private: */
        struct kref kref;
        struct list_head entry;
+       struct list_head live;
 
 /* public: */
        const struct utrace_engine_ops *ops;
Index: utrace-20jan/kernel/utrace.c
===================================================================
--- utrace-20jan.orig/kernel/utrace.c
+++ utrace-20jan/kernel/utrace.c
@@ -21,8 +21,10 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/seq_file.h>
+#include <linux/debugfs.h>
 #include <linux/utrace.h>
 
+#include <asm/atomic.h>
 
 /*
  * struct utrace, defined in utrace.h is private to this file. Its
@@ -50,11 +52,16 @@
  * callbacks seen.
  */
 
+static spinlock_t live_lock;
+static struct list_head live_engines;
+
 static struct kmem_cache *utrace_engine_cachep;
 static const struct utrace_engine_ops utrace_detached_ops; /* forward decl */
 
 static int __init utrace_init(void)
 {
+       INIT_LIST_HEAD(&live_engines);
+       spin_lock_init(&live_lock);
        utrace_engine_cachep = KMEM_CACHE(utrace_attached_engine, SLAB_PANIC);
        return 0;
 }
@@ -79,6 +86,9 @@ void __utrace_engine_release(struct kref
        struct utrace_attached_engine *engine =
                container_of(kref, struct utrace_attached_engine, kref);
        BUG_ON(!list_empty(&engine->entry));
+       spin_lock(&live_lock);
+       list_del(&engine->live);
+       spin_unlock(&live_lock);
        kmem_cache_free(utrace_engine_cachep, engine);
 }
 EXPORT_SYMBOL_GPL(__utrace_engine_release);
@@ -322,6 +332,7 @@ restart:
        engine->flags = 0;
        engine->ops = ops;
        engine->data = data;
+       INIT_LIST_HEAD(&engine->live);
 
        if ((ret == 0) && (list_empty(&utrace->attached))) {
                /* First time here, set engines up */
@@ -338,8 +349,12 @@ restart:
                        goto restart;
                }
                engine = ERR_PTR(ret);
+       } else {
+               /* Debugging... engine leaks */
+               spin_lock(&live_lock);
+               list_add(&engine->live, &live_engines);
+               spin_unlock(&live_lock);
        }
-
        return engine;
 }
 EXPORT_SYMBOL_GPL(utrace_attach_task);
@@ -2431,3 +2446,85 @@ void task_utrace_proc_status(struct seq_
                   utrace->report ? " (report)" : "",
                   utrace->interrupt ? " (interrupt)" : "");
 }
+
+#ifdef CONFIG_DEBUG_FS
+/* Similar what's in to net/core/sock.c */
+static void *ut_eng_seq_start(struct seq_file *s, loff_t *pos)
+{
+       rcu_read_lock();
+       spin_lock(&live_lock);
+
+       return seq_list_start_head(&live_engines, *pos);
+}
+
+static void *ut_eng_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       return seq_list_next(v, &live_engines, pos);
+}
+
+static void ut_eng_seq_stop(struct seq_file *s, void *v)
+{
+       spin_unlock(&live_lock);
+       rcu_read_unlock();
+}
+
+void ut_eng_seq_printf(struct seq_file *seq,
+               struct utrace_attached_engine *engine)
+{
+       seq_printf(seq, "%p             %d\n",
+                       engine, atomic_read(&engine->kref.refcount));
+}
+
+static int ut_eng_seq_show(struct seq_file *seq, void *v)
+{
+       if (v == &live_engines)
+               seq_printf(seq, "engine                 ref_cnt\n");
+       else
+               ut_eng_seq_printf(seq, list_entry(v,
+                                       struct utrace_attached_engine,
+                                       live));
+       return 0;
+}
+
+static const struct seq_operations ut_eng_seq_ops = {
+       .start = ut_eng_seq_start,
+       .next = ut_eng_seq_next,
+       .stop = ut_eng_seq_stop,
+       .show = ut_eng_seq_show
+};
+
+static int utrace_eng_open(struct inode *inode, struct file *filp)
+{
+       return seq_open(filp, &ut_eng_seq_ops);
+}
+
+struct file_operations debugfs_utrace_ops = {
+       .open           = utrace_eng_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+static int debugfs_utrace_init(void)
+{
+       struct dentry *dir, *file;
+
+       dir = debugfs_create_dir("utrace", NULL);
+       if (!dir) {
+               printk(KERN_INFO "Unable to create utrace dir\n");
+               return -ENOMEM;
+       }
+
+       file = debugfs_create_file("engines", 0440, dir, NULL,
+                       &debugfs_utrace_ops);
+       if (!file) {
+               printk(KERN_INFO "Unable to create engines file\n");
+               debugfs_remove(dir);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+late_initcall(debugfs_utrace_init);
+
+#endif /* CONFIG_DEBUG_FS */

Reply via email to