Register/unregister tracepoint probes with struct tracepoint pointer rather than tracepoint name.
This change, which vastly simplifies tracepoint.c, has been proposed by Steven Rostedt. >From this point on, the tracers need to pass a struct tracepoint pointer to probe register/unregister. A probe can now only be connected to a tracepoint that exists. Moreover, tracers are responsible for unregistering the probe before the module containing its associated tracepoint is unloaded. Changes since v1: - Adapt ftrace/perf callers, - Update tracepoint.h macro, - Build tested. Changes since v2: - Introduce for_each_tracepoint() iterator to allow listing the currently loaded tracepoints, for modules using coming/going notifiers to track tracepoints. Changes since v3: - Introduce module coming and going notifiers each with their own priority to ensure that other coming notifiers are called after, and coming notifiers are called before the tracepoint notifiers. - Fix: move for_each_tracepoint() outside of the CONFIG_MODULE ifdef. Still needs testing. This patch applies on top of 1b39437c61f562f82d6a4e3218470339a44620f7, after applying: - "Tracepoint cleanup: remove unused API functions" - "Tracepoint API doc update: data argument" Not-Signed-off-by: Mathieu Desnoyers <[email protected]> CC: Steven Rostedt <[email protected]> CC: Ingo Molnar <[email protected]> CC: Frederic Weisbecker <[email protected]> CC: Andrew Morton <[email protected]> CC: Frank Ch. Eigler <[email protected]> CC: Johannes Berg <[email protected]> --- include/linux/ftrace_event.h | 1 + include/linux/tracepoint.h | 23 +- include/trace/ftrace.h | 2 + kernel/trace/trace_events.c | 8 +- kernel/tracepoint.c | 528 ++++++++++++++++++------------------------ 5 files changed, 246 insertions(+), 316 deletions(-) diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 4e4cc28..1592c1c 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -230,6 +230,7 @@ struct ftrace_event_call { struct list_head list; struct ftrace_event_class *class; char *name; + struct tracepoint *tp; struct trace_event event; const char *print_fmt; struct event_filter *filter; diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 812b255..e52d012 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -37,16 +37,21 @@ struct tracepoint { /* * Connect a probe to a tracepoint. - * Internal API, should not be used directly. */ -extern int tracepoint_probe_register(const char *name, void *probe, void *data); +extern int tracepoint_probe_register(struct tracepoint *tp, void *probe, + void *data); /* * Disconnect a probe from a tracepoint. - * Internal API, should not be used directly. */ -extern int -tracepoint_probe_unregister(const char *name, void *probe, void *data); +extern int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, + void *data); + +/* + * Tracepoint iterator. + */ +void for_each_tracepoint(void (*fct)(struct tracepoint *tp, void *priv), + void *priv); #ifdef CONFIG_MODULES struct tp_module { @@ -160,14 +165,14 @@ static inline void tracepoint_synchronize_unregister(void) static inline int \ register_trace_##name(void (*probe)(data_proto), void *data) \ { \ - return tracepoint_probe_register(#name, (void *)probe, \ - data); \ + return tracepoint_probe_register(&__tracepoint_##name, \ + (void *)probe, data); \ } \ static inline int \ unregister_trace_##name(void (*probe)(data_proto), void *data) \ { \ - return tracepoint_probe_unregister(#name, (void *)probe, \ - data); \ + return tracepoint_probe_unregister(&__tracepoint_##name,\ + (void *)probe, data); \ } \ static inline void \ check_trace_callback_type_##name(void (*cb)(data_proto)) \ diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 1a8b28d..415b986 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -625,6 +625,7 @@ static struct ftrace_event_class __used __refdata event_class_##call = { \ \ static struct ftrace_event_call __used event_##call = { \ .name = #call, \ + .tp = &__tracepoint_##call, \ .class = &event_class_##template, \ .event.funcs = &ftrace_event_type_funcs_##template, \ .print_fmt = print_fmt_##template, \ @@ -639,6 +640,7 @@ static const char print_fmt_##call[] = print; \ \ static struct ftrace_event_call __used event_##call = { \ .name = #call, \ + .tp = &__tracepoint_##call, \ .class = &event_class_##template, \ .event.funcs = &ftrace_event_type_funcs_##call, \ .print_fmt = print_fmt_##call, \ diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index f3989ce..0e39692 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -201,22 +201,22 @@ int ftrace_event_reg(struct ftrace_event_call *call, switch (type) { case TRACE_REG_REGISTER: - return tracepoint_probe_register(call->name, + return tracepoint_probe_register(call->tp, call->class->probe, file); case TRACE_REG_UNREGISTER: - tracepoint_probe_unregister(call->name, + tracepoint_probe_unregister(call->tp, call->class->probe, file); return 0; #ifdef CONFIG_PERF_EVENTS case TRACE_REG_PERF_REGISTER: - return tracepoint_probe_register(call->name, + return tracepoint_probe_register(call->tp, call->class->perf_probe, call); case TRACE_REG_PERF_UNREGISTER: - tracepoint_probe_unregister(call->name, + tracepoint_probe_unregister(call->tp, call->class->perf_probe, call); return 0; diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index cc6c8b6..de9f4d9 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -34,8 +34,8 @@ extern struct tracepoint * const __stop___tracepoints_ptrs[]; static const int tracepoint_debug; /* - * Tracepoints mutex protects the builtin and module tracepoints and the hash - * table, as well as the local module list. + * Tracepoints mutex protects the builtin and module tracepoints, as + * well as the local module list. */ static DEFINE_MUTEX(tracepoints_mutex); @@ -45,26 +45,10 @@ static LIST_HEAD(tracepoint_module_list); #endif /* CONFIG_MODULES */ /* - * Tracepoint hash table, containing the active tracepoints. - * Protected by tracepoints_mutex. - */ -#define TRACEPOINT_HASH_BITS 6 -#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) -static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; - -/* * Note about RCU : * It is used to delay the free of multiple probes array until a quiescent * state is reached. - * Tracepoint entries modifications are protected by the tracepoints_mutex. */ -struct tracepoint_entry { - struct hlist_node hlist; - struct tracepoint_func *funcs; - int refcount; /* Number of times armed. 0 if disarmed. */ - char name[0]; -}; - struct tp_probes { union { struct rcu_head rcu; @@ -94,34 +78,35 @@ static inline void release_probes(struct tracepoint_func *old) } } -static void debug_print_probes(struct tracepoint_entry *entry) +static +void debug_print_probes(struct tracepoint_func *funcs) { int i; - if (!tracepoint_debug || !entry->funcs) + if (!tracepoint_debug || !funcs) return; - for (i = 0; entry->funcs[i].func; i++) - printk(KERN_DEBUG "Probe %d : %p\n", i, entry->funcs[i].func); + for (i = 0; funcs[i].func; i++) + printk(KERN_DEBUG "Probe %d : %p\n", i, funcs[i].func); } -static struct tracepoint_func * -tracepoint_entry_add_probe(struct tracepoint_entry *entry, - void *probe, void *data) +static +struct tracepoint_func *func_add(struct tracepoint_func **funcs, + struct tracepoint_func *tp_func) { int nr_probes = 0; struct tracepoint_func *old, *new; - if (WARN_ON(!probe)) + if (WARN_ON(!tp_func->func)) return ERR_PTR(-EINVAL); - debug_print_probes(entry); - old = entry->funcs; + debug_print_probes(*funcs); + old = *funcs; if (old) { /* (N -> N+1), (N != 0, 1) probes */ for (nr_probes = 0; old[nr_probes].func; nr_probes++) - if (old[nr_probes].func == probe && - old[nr_probes].data == data) + if (old[nr_probes].func == tp_func->func && + old[nr_probes].data == tp_func->data) return ERR_PTR(-EEXIST); } /* + 2 : one for new probe, one for NULL func */ @@ -130,33 +115,31 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, return ERR_PTR(-ENOMEM); if (old) memcpy(new, old, nr_probes * sizeof(struct tracepoint_func)); - new[nr_probes].func = probe; - new[nr_probes].data = data; + new[nr_probes] = *tp_func; new[nr_probes + 1].func = NULL; - entry->refcount = nr_probes + 1; - entry->funcs = new; - debug_print_probes(entry); + *funcs = new; + debug_print_probes(*funcs); return old; } -static void * -tracepoint_entry_remove_probe(struct tracepoint_entry *entry, - void *probe, void *data) +static +void *func_remove(struct tracepoint_func **funcs, + struct tracepoint_func *tp_func) { int nr_probes = 0, nr_del = 0, i; struct tracepoint_func *old, *new; - old = entry->funcs; + old = *funcs; if (!old) return ERR_PTR(-ENOENT); - debug_print_probes(entry); + debug_print_probes(*funcs); /* (N -> M), (N > 1, M >= 0) probes */ - if (probe) { + if (tp_func->func) { for (nr_probes = 0; old[nr_probes].func; nr_probes++) { - if (old[nr_probes].func == probe && - old[nr_probes].data == data) + if (old[nr_probes].func == tp_func->func && + old[nr_probes].data == tp_func->data) nr_del++; } } @@ -167,9 +150,8 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, */ if (nr_probes - nr_del == 0) { /* N -> 0, (N > 1) */ - entry->funcs = NULL; - entry->refcount = 0; - debug_print_probes(entry); + *funcs = NULL; + debug_print_probes(*funcs); return old; } else { int j = 0; @@ -179,90 +161,35 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, if (new == NULL) return ERR_PTR(-ENOMEM); for (i = 0; old[i].func; i++) - if (old[i].func != probe || old[i].data != data) + if (old[i].func != tp_func->func + || old[i].data != tp_func->data) new[j++] = old[i]; new[nr_probes - nr_del].func = NULL; - entry->refcount = nr_probes - nr_del; - entry->funcs = new; + *funcs = new; } - debug_print_probes(entry); + debug_print_probes(*funcs); return old; } /* - * Get tracepoint if the tracepoint is present in the tracepoint hash table. - * Must be called with tracepoints_mutex held. - * Returns NULL if not present. + * Add the probe function to a tracepoint. */ -static struct tracepoint_entry *get_tracepoint(const char *name) +static +int tracepoint_add_func(struct tracepoint *tp, + struct tracepoint_func *func) { - struct hlist_head *head; - struct tracepoint_entry *e; - u32 hash = jhash(name, strlen(name), 0); - - head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)]; - hlist_for_each_entry(e, head, hlist) { - if (!strcmp(name, e->name)) - return e; - } - return NULL; -} + struct tracepoint_func *old, *tp_funcs; -/* - * Add the tracepoint to the tracepoint hash table. Must be called with - * tracepoints_mutex held. - */ -static struct tracepoint_entry *add_tracepoint(const char *name) -{ - struct hlist_head *head; - struct tracepoint_entry *e; - size_t name_len = strlen(name) + 1; - u32 hash = jhash(name, name_len-1, 0); - - head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)]; - hlist_for_each_entry(e, head, hlist) { - if (!strcmp(name, e->name)) { - printk(KERN_NOTICE - "tracepoint %s busy\n", name); - return ERR_PTR(-EEXIST); /* Already there */ - } - } - /* - * Using kmalloc here to allocate a variable length element. Could - * cause some memory fragmentation if overused. - */ - e = kmalloc(sizeof(struct tracepoint_entry) + name_len, GFP_KERNEL); - if (!e) - return ERR_PTR(-ENOMEM); - memcpy(&e->name[0], name, name_len); - e->funcs = NULL; - e->refcount = 0; - hlist_add_head(&e->hlist, head); - return e; -} + if (tp->regfunc && !static_key_enabled(&tp->key)) + tp->regfunc(); -/* - * Remove the tracepoint from the tracepoint hash table. Must be called with - * mutex_lock held. - */ -static inline void remove_tracepoint(struct tracepoint_entry *e) -{ - hlist_del(&e->hlist); - kfree(e); -} - -/* - * Sets the probe callback corresponding to one tracepoint. - */ -static void set_tracepoint(struct tracepoint_entry **entry, - struct tracepoint *elem, int active) -{ - WARN_ON(strcmp((*entry)->name, elem->name) != 0); - - if (elem->regfunc && !static_key_enabled(&elem->key) && active) - elem->regfunc(); - else if (elem->unregfunc && static_key_enabled(&elem->key) && !active) - elem->unregfunc(); + tp_funcs = tp->funcs; + old = func_add(&tp_funcs, func); + if (IS_ERR(old)) { + WARN_ON_ONCE(1); + return PTR_ERR(old); + } + release_probes(old); /* * rcu_assign_pointer has a smp_wmb() which makes sure that the new @@ -271,203 +198,124 @@ static void set_tracepoint(struct tracepoint_entry **entry, * include/linux/tracepoints.h. A matching smp_read_barrier_depends() * is used. */ - rcu_assign_pointer(elem->funcs, (*entry)->funcs); - if (active && !static_key_enabled(&elem->key)) - static_key_slow_inc(&elem->key); - else if (!active && static_key_enabled(&elem->key)) - static_key_slow_dec(&elem->key); + rcu_assign_pointer(tp->funcs, tp_funcs); + if (!static_key_enabled(&tp->key)) + static_key_slow_inc(&tp->key); + return 0; } /* - * Disable a tracepoint and its probe callback. + * Remove a probe function from a tracepoint. * Note: only waiting an RCU period after setting elem->call to the empty * function insures that the original callback is not used anymore. This insured * by preempt_disable around the call site. */ -static void disable_tracepoint(struct tracepoint *elem) +static +int tracepoint_remove_func(struct tracepoint *tp, + struct tracepoint_func *func) { - if (elem->unregfunc && static_key_enabled(&elem->key)) - elem->unregfunc(); - - if (static_key_enabled(&elem->key)) - static_key_slow_dec(&elem->key); - rcu_assign_pointer(elem->funcs, NULL); -} - -/** - * tracepoint_update_probe_range - Update a probe range - * @begin: beginning of the range - * @end: end of the range - * - * Updates the probe callback corresponding to a range of tracepoints. - * Called with tracepoints_mutex held. - */ -static void tracepoint_update_probe_range(struct tracepoint * const *begin, - struct tracepoint * const *end) -{ - struct tracepoint * const *iter; - struct tracepoint_entry *mark_entry; - - if (!begin) - return; + struct tracepoint_func *old, *tp_funcs; - for (iter = begin; iter < end; iter++) { - mark_entry = get_tracepoint((*iter)->name); - if (mark_entry) { - set_tracepoint(&mark_entry, *iter, - !!mark_entry->refcount); - } else { - disable_tracepoint(*iter); - } + tp_funcs = tp->funcs; + old = func_remove(&tp_funcs, func); + if (IS_ERR(old)) { + WARN_ON_ONCE(1); + return PTR_ERR(old); } -} - -#ifdef CONFIG_MODULES -void module_update_tracepoints(void) -{ - struct tp_module *tp_mod; - - list_for_each_entry(tp_mod, &tracepoint_module_list, list) - tracepoint_update_probe_range(tp_mod->tracepoints_ptrs, - tp_mod->tracepoints_ptrs + tp_mod->num_tracepoints); -} -#else /* CONFIG_MODULES */ -void module_update_tracepoints(void) -{ -} -#endif /* CONFIG_MODULES */ + release_probes(old); + if (!tp_funcs) { + /* Removed last function */ + if (tp->unregfunc && static_key_enabled(&tp->key)) + tp->unregfunc(); -/* - * Update probes, removing the faulty probes. - * Called with tracepoints_mutex held. - */ -static void tracepoint_update_probes(void) -{ - /* Core kernel tracepoints */ - tracepoint_update_probe_range(__start___tracepoints_ptrs, - __stop___tracepoints_ptrs); - /* tracepoints in modules. */ - module_update_tracepoints(); -} - -static struct tracepoint_func * -tracepoint_add_probe(const char *name, void *probe, void *data) -{ - struct tracepoint_entry *entry; - struct tracepoint_func *old; - - entry = get_tracepoint(name); - if (!entry) { - entry = add_tracepoint(name); - if (IS_ERR(entry)) - return (struct tracepoint_func *)entry; + if (static_key_enabled(&tp->key)) + static_key_slow_dec(&tp->key); } - old = tracepoint_entry_add_probe(entry, probe, data); - if (IS_ERR(old) && !entry->refcount) - remove_tracepoint(entry); - return old; + rcu_assign_pointer(tp->funcs, tp_funcs); + return 0; } /** * tracepoint_probe_register - Connect a probe to a tracepoint - * @name: tracepoint name + * @tp: tracepoint * @probe: probe handler * @data: probe private data * * Returns 0 if ok, error value on error. - * The probe address must at least be aligned on the architecture pointer size. + * Note: if @tp is within a module, the caller is responsible for + * monitoring the module "going" notifier to unregister the probe before + * the module is gone. */ -int tracepoint_probe_register(const char *name, void *probe, void *data) +int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data) { - struct tracepoint_func *old; + struct tracepoint_func tp_func; + int ret; mutex_lock(&tracepoints_mutex); - old = tracepoint_add_probe(name, probe, data); - if (IS_ERR(old)) { - mutex_unlock(&tracepoints_mutex); - return PTR_ERR(old); - } - tracepoint_update_probes(); /* may update entry */ + tp_func.func = probe; + tp_func.data = data; + ret = tracepoint_add_func(tp, &tp_func); mutex_unlock(&tracepoints_mutex); - release_probes(old); - return 0; + return ret; } EXPORT_SYMBOL_GPL(tracepoint_probe_register); -static struct tracepoint_func * -tracepoint_remove_probe(const char *name, void *probe, void *data) -{ - struct tracepoint_entry *entry; - struct tracepoint_func *old; - - entry = get_tracepoint(name); - if (!entry) - return ERR_PTR(-ENOENT); - old = tracepoint_entry_remove_probe(entry, probe, data); - 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 + * @tp: tracepoint * @probe: probe function pointer * @data: probe private data * - * We do not need to call a synchronize_sched to make sure the probes have - * finished running before doing a module unload, because the module unload - * itself uses stop_machine(), which insures that every preempt disabled section - * have finished. + * Returns 0 if ok, error value on error. */ -int tracepoint_probe_unregister(const char *name, void *probe, void *data) +int tracepoint_probe_unregister(struct tracepoint *tp, void *probe, void *data) { - struct tracepoint_func *old; + struct tracepoint_func tp_func; + int ret; mutex_lock(&tracepoints_mutex); - old = tracepoint_remove_probe(name, probe, data); - if (IS_ERR(old)) { - mutex_unlock(&tracepoints_mutex); - return PTR_ERR(old); - } - tracepoint_update_probes(); /* may update entry */ + tp_func.func = probe; + tp_func.data = data; + ret = tracepoint_remove_func(tp, &tp_func); mutex_unlock(&tracepoints_mutex); - release_probes(old); - return 0; + 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); - } -} - #ifdef CONFIG_MODULES bool trace_module_has_bad_taint(struct module *mod) { return mod->taints & ~((1 << TAINT_OOT_MODULE) | (1 << TAINT_CRAP)); } -static int tracepoint_module_coming(struct module *mod) +/* + * Ensure the tracer unregistered the module's probes before the module + * teardown is performed. Prevents leaks of probe and data pointers. + */ +static +void tp_module_going_check_quiescent(struct tracepoint * const *begin, + struct tracepoint * const *end) { - struct tp_module *tp_mod, *iter; + struct tracepoint * const *iter; + + if (!begin) + return; + for (iter = begin; iter < end; iter++) + WARN_ON_ONCE((*iter)->funcs); +} + +static +int tracepoint_module_coming(struct module *mod) +{ + struct tp_module *tp_mod; int ret = 0; /* - * We skip modules that taint the kernel, especially those with different - * module headers (for forced load), to make sure we don't cause a crash. - * Staging and out-of-tree GPL modules are fine. + * We skip modules that taint the kernel, especially those with + * different module headers (for forced load), to make sure we + * don't cause a crash. Staging and out-of-tree GPL modules are + * fine. */ if (trace_module_has_bad_taint(mod)) return 0; @@ -479,41 +327,24 @@ static int tracepoint_module_coming(struct module *mod) } tp_mod->num_tracepoints = mod->num_tracepoints; tp_mod->tracepoints_ptrs = mod->tracepoints_ptrs; - - /* - * tracepoint_module_list is kept sorted by struct module pointer - * address for iteration on tracepoints from a seq_file that can release - * the mutex between calls. - */ - list_for_each_entry_reverse(iter, &tracepoint_module_list, list) { - BUG_ON(iter == tp_mod); /* Should never be in the list twice */ - if (iter < tp_mod) { - /* We belong to the location right after iter. */ - list_add(&tp_mod->list, &iter->list); - goto module_added; - } - } - /* We belong to the beginning of the list */ - list_add(&tp_mod->list, &tracepoint_module_list); -module_added: - tracepoint_update_probe_range(mod->tracepoints_ptrs, - mod->tracepoints_ptrs + mod->num_tracepoints); + list_add_tail(&tp_mod->list, &tracepoint_module_list); end: mutex_unlock(&tracepoints_mutex); return ret; } -static int tracepoint_module_going(struct module *mod) +static +void tracepoint_module_going(struct module *mod) { struct tp_module *pos; mutex_lock(&tracepoints_mutex); - tracepoint_update_probe_range(mod->tracepoints_ptrs, - mod->tracepoints_ptrs + mod->num_tracepoints); list_for_each_entry(pos, &tracepoint_module_list, list) { if (pos->tracepoints_ptrs == mod->tracepoints_ptrs) { list_del(&pos->list); kfree(pos); + tp_module_going_check_quiescent(mod->tracepoints_ptrs, + mod->tracepoints_ptrs + mod->num_tracepoints); break; } } @@ -524,40 +355,131 @@ static int tracepoint_module_going(struct module *mod) * loaded. */ mutex_unlock(&tracepoints_mutex); - return 0; } -int tracepoint_module_notify(struct notifier_block *self, - unsigned long val, void *data) +static +int tracepoint_module_notify_enter(struct notifier_block *self, + unsigned long val, void *data) { struct module *mod = data; int ret = 0; - switch (val) { - case MODULE_STATE_COMING: + if (val == MODULE_STATE_COMING) ret = tracepoint_module_coming(mod); - break; - case MODULE_STATE_LIVE: - break; - case MODULE_STATE_GOING: - ret = tracepoint_module_going(mod); - break; - } return ret; } -struct notifier_block tracepoint_module_nb = { - .notifier_call = tracepoint_module_notify, - .priority = 0, +static +int tracepoint_module_notify_exit(struct notifier_block *self, + unsigned long val, void *data) +{ + struct module *mod = data; + + if (val == MODULE_STATE_GOING) + tracepoint_module_going(mod); + return 0; +} + +static +struct notifier_block tracepoint_module_nb_enter = { + .notifier_call = tracepoint_module_notify_enter, + .priority = INT_MAX, /* Run before other module notifiers */ }; -static int init_tracepoints(void) +static +struct notifier_block tracepoint_module_nb_exit = { + .notifier_call = tracepoint_module_notify_exit, + .priority = INT_MIN, /* Run after other module notifiers */ +}; + +static __init int init_tracepoints(void) { - return register_module_notifier(&tracepoint_module_nb); + int ret; + + ret = register_module_notifier(&tracepoint_module_nb_enter); + if (ret) { + pr_warning("Failed to register tracepoint module enter notifier\n"); + goto error; + } + ret = register_module_notifier(&tracepoint_module_nb_exit); + if (ret) { + pr_warning("Failed to register tracepoint module exit notifier\n"); + goto error_unregister_enter; + } + return ret; + +error_unregister_enter: + WARN_ON(unregister_module_notifier(&tracepoint_module_nb_enter)); +error: + return ret; } __initcall(init_tracepoints); #endif /* CONFIG_MODULES */ +static +void for_each_tracepoint_range(struct tracepoint * const *begin, + struct tracepoint * const *end, + void (*fct)(struct tracepoint *tp, void *priv), + void *priv) +{ + struct tracepoint * const *iter; + + if (!begin) + return; + for (iter = begin; iter < end; iter++) + fct(*iter, priv); +} + +static +void for_each_kernel_tracepoint(void (*fct)(struct tracepoint *tp, void *priv), + void *priv) +{ + for_each_tracepoint_range(__start___tracepoints_ptrs, + __stop___tracepoints_ptrs, fct, priv); +} + +#ifdef CONFIG_MODULES +static +void for_each_module_tracepoint(void (*fct)(struct tracepoint *tp, void *priv), + void *priv) +{ + struct tp_module *tp_mod; + + list_for_each_entry(tp_mod, &tracepoint_module_list, list) + for_each_tracepoint_range(tp_mod->tracepoints_ptrs, + tp_mod->tracepoints_ptrs + tp_mod->num_tracepoints, + fct, priv); +} +#else /* CONFIG_MODULES */ +static +void for_each_module_tracepoint(void (*fct)(struct tracepoint *tp, void *priv), + void *priv) +{ +} +#endif /* CONFIG_MODULES */ + +/** + * for_each_tracepoint - iteration on all tracepoints + * @fct: callback + * @priv: private data + * + * Modules using "coming" and "going" notifiers to get notified of + * tracepoints within modules need to know the full listing of + * tracepoints beforehand. The callback function is called with the + * tracepoints mutex held. + */ +void for_each_tracepoint(void (*fct)(struct tracepoint *tp, void *priv), + void *priv) +{ + mutex_lock(&tracepoints_mutex); + /* Core kernel tracepoints */ + for_each_kernel_tracepoint(fct, priv); + /* Tracepoints in modules */ + for_each_module_tracepoint(fct, priv); + mutex_unlock(&tracepoints_mutex); +} +EXPORT_SYMBOL_GPL(for_each_tracepoint); + #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS /* NB: reg/unreg are called while guarded with the tracepoints_mutex */ -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/

