|
@@ -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)
|