Merge branch 'for-2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[safe/jmp/linux-2.6] / kernel / trace / trace_stat.c
index 8030ec9..96cffb2 100644 (file)
@@ -10,6 +10,7 @@
 
 
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/rbtree.h>
 #include <linux/debugfs.h>
 #include "trace_stat.h"
@@ -49,7 +50,8 @@ static struct dentry          *stat_dir;
  * but it will at least advance closer to the next one
  * to be released.
  */
-static struct rb_node *release_next(struct rb_node *node)
+static struct rb_node *release_next(struct tracer_stat *ts,
+                                   struct rb_node *node)
 {
        struct stat_node *snode;
        struct rb_node *parent = rb_parent(node);
@@ -67,36 +69,50 @@ static struct rb_node *release_next(struct rb_node *node)
                        parent->rb_right = NULL;
 
                snode = container_of(node, struct stat_node, node);
+               if (ts->stat_release)
+                       ts->stat_release(snode->stat);
                kfree(snode);
 
                return parent;
        }
 }
 
-static void reset_stat_session(struct stat_session *session)
+static void __reset_stat_session(struct stat_session *session)
 {
        struct rb_node *node = session->stat_root.rb_node;
 
        while (node)
-               node = release_next(node);
+               node = release_next(session->ts, node);
 
        session->stat_root = RB_ROOT;
 }
 
+static void reset_stat_session(struct stat_session *session)
+{
+       mutex_lock(&session->stat_mutex);
+       __reset_stat_session(session);
+       mutex_unlock(&session->stat_mutex);
+}
+
 static void destroy_session(struct stat_session *session)
 {
        debugfs_remove(session->file);
-       reset_stat_session(session);
+       __reset_stat_session(session);
        mutex_destroy(&session->stat_mutex);
        kfree(session);
 }
 
 typedef int (*cmp_stat_t)(void *, void *);
 
-static void
-insert_stat(struct rb_root *root, struct stat_node *data, cmp_stat_t cmp)
+static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp)
 {
        struct rb_node **new = &(root->rb_node), *parent = NULL;
+       struct stat_node *data;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       data->stat = stat;
 
        /*
         * Figure out where to put new node
@@ -118,12 +134,13 @@ insert_stat(struct rb_root *root, struct stat_node *data, cmp_stat_t cmp)
 
        rb_link_node(&data->node, parent, new);
        rb_insert_color(&data->node, root);
+       return 0;
 }
 
 /*
  * For tracers that don't provide a stat_cmp callback.
- * This one will force an immediate insertion on tail of
- * the list.
+ * This one will force an insertion as right-most node
+ * in the rbtree.
  */
 static int dummy_cmp(void *p1, void *p2)
 {
@@ -131,21 +148,20 @@ static int dummy_cmp(void *p1, void *p2)
 }
 
 /*
- * Initialize the stat list at each trace_stat file opening.
+ * Initialize the stat rbtree at each trace_stat file opening.
  * All of these copies and sorting are required on all opening
  * since the stats could have changed between two file sessions.
  */
 static int stat_seq_init(struct stat_session *session)
 {
        struct tracer_stat *ts = session->ts;
-       struct stat_node *new_entry;
-       struct rb_root *root;
+       struct rb_root *root = &session->stat_root;
        void *stat;
        int ret = 0;
        int i;
 
        mutex_lock(&session->stat_mutex);
-       reset_stat_session(session);
+       __reset_stat_session(session);
 
        if (!ts->stat_cmp)
                ts->stat_cmp = dummy_cmp;
@@ -154,23 +170,12 @@ static int stat_seq_init(struct stat_session *session)
        if (!stat)
                goto exit;
 
-       /*
-        * The first entry. Actually this is the second, but the first
-        * one (the stat_list head) is pointless.
-        */
-       new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
-       if (!new_entry) {
-               ret = -ENOMEM;
+       ret = insert_stat(root, stat, ts->stat_cmp);
+       if (ret)
                goto exit;
-       }
-       root = &session->stat_root;
-       insert_stat(root, new_entry, dummy_cmp);
-
-       new_entry->stat = stat;
 
        /*
-        * Iterate over the tracer stat entries and store them in a sorted
-        * list.
+        * Iterate over the tracer stat entries and store them in an rbtree.
         */
        for (i = 1; ; i++) {
                stat = ts->stat_next(stat, i);
@@ -179,23 +184,17 @@ static int stat_seq_init(struct stat_session *session)
                if (!stat)
                        break;
 
-               new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
-               if (!new_entry) {
-                       ret = -ENOMEM;
-                       goto exit_free_list;
-               }
-
-               new_entry->stat = stat;
-
-               insert_stat(root, new_entry, ts->stat_cmp);
+               ret = insert_stat(root, stat, ts->stat_cmp);
+               if (ret)
+                       goto exit_free_rbtree;
        }
 
 exit:
        mutex_unlock(&session->stat_mutex);
        return ret;
 
-exit_free_list:
-       reset_stat_session(session);
+exit_free_rbtree:
+       __reset_stat_session(session);
        mutex_unlock(&session->stat_mutex);
        return ret;
 }
@@ -205,23 +204,23 @@ static void *stat_seq_start(struct seq_file *s, loff_t *pos)
 {
        struct stat_session *session = s->private;
        struct rb_node *node;
+       int n = *pos;
        int i;
 
-       /* Prevent from tracer switch or stat_list modification */
+       /* Prevent from tracer switch or rbtree modification */
        mutex_lock(&session->stat_mutex);
 
        /* If we are in the beginning of the file, print the headers */
-       if (!*pos && session->ts->stat_headers) {
-               (*pos)++;
-               return SEQ_START_TOKEN;
+       if (session->ts->stat_headers) {
+               if (n == 0)
+                       return SEQ_START_TOKEN;
+               n--;
        }
 
        node = rb_first(&session->stat_root);
-       for (i = 0; node && i < *pos; i++)
+       for (i = 0; node && i < n; i++)
                node = rb_next(node);
 
-       (*pos)++;
-
        return node;
 }
 
@@ -266,31 +265,34 @@ static const struct seq_operations trace_stat_seq_ops = {
 static int tracing_stat_open(struct inode *inode, struct file *file)
 {
        int ret;
-
+       struct seq_file *m;
        struct stat_session *session = inode->i_private;
 
+       ret = stat_seq_init(session);
+       if (ret)
+               return ret;
+
        ret = seq_open(file, &trace_stat_seq_ops);
-       if (!ret) {
-               struct seq_file *m = file->private_data;
-               m->private = session;
-               ret = stat_seq_init(session);
+       if (ret) {
+               reset_stat_session(session);
+               return ret;
        }
 
+       m = file->private_data;
+       m->private = session;
        return ret;
 }
 
 /*
- * Avoid consuming memory with our now useless list.
+ * Avoid consuming memory with our now useless rbtree.
  */
 static int tracing_stat_release(struct inode *i, struct file *f)
 {
        struct stat_session *session = i->i_private;
 
-       mutex_lock(&session->stat_mutex);
        reset_stat_session(session);
-       mutex_unlock(&session->stat_mutex);
 
-       return 0;
+       return seq_release(i, f);
 }
 
 static const struct file_operations tracing_stat_fops = {
@@ -328,7 +330,7 @@ static int init_stat_file(struct stat_session *session)
 
 int register_stat_tracer(struct tracer_stat *trace)
 {
-       struct stat_session *session, *node, *tmp;
+       struct stat_session *session, *node;
        int ret;
 
        if (!trace)
@@ -339,7 +341,7 @@ int register_stat_tracer(struct tracer_stat *trace)
 
        /* Already registered? */
        mutex_lock(&all_stat_sessions_mutex);
-       list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) {
+       list_for_each_entry(node, &all_stat_sessions, session_list) {
                if (node->ts == trace) {
                        mutex_unlock(&all_stat_sessions_mutex);
                        return -EINVAL;