tracepoint: introduce *_noupdate APIs.
[linux-2.6.git] / kernel / tracepoint.c
index 3e22867..e96590f 100644 (file)
@@ -59,7 +59,10 @@ struct tracepoint_entry {
 };
 
 struct tp_probes {
-       struct rcu_head rcu;
+       union {
+               struct rcu_head rcu;
+               struct list_head list;
+       } u;
        void *probes[0];
 };
 
@@ -72,7 +75,7 @@ static inline void *allocate_probes(int count)
 
 static void rcu_free_old_probes(struct rcu_head *head)
 {
-       kfree(container_of(head, struct tp_probes, rcu));
+       kfree(container_of(head, struct tp_probes, u.rcu));
 }
 
 static inline void release_probes(void *old)
@@ -80,7 +83,7 @@ static inline void release_probes(void *old)
        if (old) {
                struct tp_probes *tp_probes = container_of(old,
                        struct tp_probes, probes[0]);
-               call_rcu(&tp_probes->rcu, rcu_free_old_probes);
+               call_rcu_sched(&tp_probes->u.rcu, rcu_free_old_probes);
        }
 }
 
@@ -299,6 +302,23 @@ static void tracepoint_update_probes(void)
        module_update_tracepoints();
 }
 
+static void *tracepoint_add_probe(const char *name, void *probe)
+{
+       struct tracepoint_entry *entry;
+       void *old;
+
+       entry = get_tracepoint(name);
+       if (!entry) {
+               entry = add_tracepoint(name);
+               if (IS_ERR(entry))
+                       return entry;
+       }
+       old = tracepoint_entry_add_probe(entry, probe);
+       if (IS_ERR(old) && !entry->refcount)
+               remove_tracepoint(entry);
+       return old;
+}
+
 /**
  * tracepoint_probe_register -  Connect a probe to a tracepoint
  * @name: tracepoint name
@@ -309,36 +329,36 @@ static void tracepoint_update_probes(void)
  */
 int tracepoint_probe_register(const char *name, void *probe)
 {
-       struct tracepoint_entry *entry;
-       int ret = 0;
        void *old;
 
        mutex_lock(&tracepoints_mutex);
-       entry = get_tracepoint(name);
-       if (!entry) {
-               entry = add_tracepoint(name);
-               if (IS_ERR(entry)) {
-                       ret = PTR_ERR(entry);
-                       goto end;
-               }
-       }
-       old = tracepoint_entry_add_probe(entry, probe);
-       if (IS_ERR(old)) {
-               if (!entry->refcount)
-                       remove_tracepoint(entry);
-               ret = PTR_ERR(old);
-               goto end;
-       }
+       old = tracepoint_add_probe(name, probe);
        mutex_unlock(&tracepoints_mutex);
+       if (IS_ERR(old))
+               return PTR_ERR(old);
+
        tracepoint_update_probes();             /* may update entry */
        release_probes(old);
        return 0;
-end:
-       mutex_unlock(&tracepoints_mutex);
-       return ret;
 }
 EXPORT_SYMBOL_GPL(tracepoint_probe_register);
 
+static void *tracepoint_remove_probe(const char *name, void *probe)
+{
+       struct tracepoint_entry *entry;
+       void *old;
+
+       entry = get_tracepoint(name);
+       if (!entry)
+               return ERR_PTR(-ENOENT);
+       old = tracepoint_entry_remove_probe(entry, probe);
+       if (IS_ERR(old))
+               return old;
+       if (!entry->refcount)
+               remove_tracepoint(entry);
+       return old;
+}
+
 /**
  * tracepoint_probe_unregister -  Disconnect a probe from a tracepoint
  * @name: tracepoint name
@@ -351,31 +371,105 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_register);
  */
 int tracepoint_probe_unregister(const char *name, void *probe)
 {
-       struct tracepoint_entry *entry;
        void *old;
-       int ret = -ENOENT;
 
        mutex_lock(&tracepoints_mutex);
-       entry = get_tracepoint(name);
-       if (!entry)
-               goto end;
-       old = tracepoint_entry_remove_probe(entry, probe);
-       if (IS_ERR(old)) {
-               ret = PTR_ERR(old);
-               goto end;
-       }
-       if (!entry->refcount)
-               remove_tracepoint(entry);
+       old = tracepoint_remove_probe(name, probe);
        mutex_unlock(&tracepoints_mutex);
+       if (IS_ERR(old))
+               return PTR_ERR(old);
+
        tracepoint_update_probes();             /* may update entry */
        release_probes(old);
        return 0;
-end:
-       mutex_unlock(&tracepoints_mutex);
-       return ret;
 }
 EXPORT_SYMBOL_GPL(tracepoint_probe_unregister);
 
+static LIST_HEAD(old_probes);
+static int need_update;
+
+static void tracepoint_add_old_probes(void *old)
+{
+       need_update = 1;
+       if (old) {
+               struct tp_probes *tp_probes = container_of(old,
+                       struct tp_probes, probes[0]);
+               list_add(&tp_probes->u.list, &old_probes);
+       }
+}
+
+/**
+ * tracepoint_probe_register_noupdate -  register a probe but not connect
+ * @name: tracepoint name
+ * @probe: probe handler
+ *
+ * caller must call tracepoint_probe_update_all()
+ */
+int tracepoint_probe_register_noupdate(const char *name, void *probe)
+{
+       void *old;
+
+       mutex_lock(&tracepoints_mutex);
+       old = tracepoint_add_probe(name, probe);
+       if (IS_ERR(old)) {
+               mutex_unlock(&tracepoints_mutex);
+               return PTR_ERR(old);
+       }
+       tracepoint_add_old_probes(old);
+       mutex_unlock(&tracepoints_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate);
+
+/**
+ * tracepoint_probe_unregister_noupdate -  remove a probe but not disconnect
+ * @name: tracepoint name
+ * @probe: probe function pointer
+ *
+ * caller must call tracepoint_probe_update_all()
+ */
+int tracepoint_probe_unregister_noupdate(const char *name, void *probe)
+{
+       void *old;
+
+       mutex_lock(&tracepoints_mutex);
+       old = tracepoint_remove_probe(name, probe);
+       if (IS_ERR(old)) {
+               mutex_unlock(&tracepoints_mutex);
+               return PTR_ERR(old);
+       }
+       tracepoint_add_old_probes(old);
+       mutex_unlock(&tracepoints_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tracepoint_probe_unregister_noupdate);
+
+/**
+ * tracepoint_probe_update_all -  update tracepoints
+ */
+void tracepoint_probe_update_all(void)
+{
+       LIST_HEAD(release_probes);
+       struct tp_probes *pos, *next;
+
+       mutex_lock(&tracepoints_mutex);
+       if (!need_update) {
+               mutex_unlock(&tracepoints_mutex);
+               return;
+       }
+       if (!list_empty(&old_probes))
+               list_replace_init(&old_probes, &release_probes);
+       need_update = 0;
+       mutex_unlock(&tracepoints_mutex);
+
+       tracepoint_update_probes();
+       list_for_each_entry_safe(pos, next, &release_probes, u.list) {
+               list_del(&pos->u.list);
+               call_rcu_sched(&pos->u.rcu, rcu_free_old_probes);
+       }
+}
+EXPORT_SYMBOL_GPL(tracepoint_probe_update_all);
+
 /**
  * tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
  * @tracepoint: current tracepoints (in), next tracepoint (out)