diff options
| -rw-r--r-- | include/linux/ftrace_event.h | 3 | ||||
| -rw-r--r-- | include/linux/module.h | 4 | ||||
| -rw-r--r-- | include/linux/trace_seq.h | 2 | ||||
| -rw-r--r-- | include/trace/ftrace.h | 1 | ||||
| -rw-r--r-- | kernel/module.c | 7 | ||||
| -rw-r--r-- | kernel/trace/trace_events.c | 128 | 
6 files changed, 113 insertions, 32 deletions
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 17810853b4f..75f3ac01a87 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -7,6 +7,7 @@  struct trace_array;  struct tracer; +struct dentry;  /*   * The trace entry - the most basic unit of tracing. This is what @@ -87,6 +88,7 @@ struct ftrace_event_call {  	char			*name;  	char			*system;  	struct dentry		*dir; +	struct trace_event	*event;  	int			enabled;  	int			(*regfunc)(void);  	void			(*unregfunc)(void); @@ -97,6 +99,7 @@ struct ftrace_event_call {  	struct list_head	fields;  	int			n_preds;  	struct filter_pred	**preds; +	void			*mod;  #ifdef CONFIG_EVENT_PROFILE  	atomic_t	profile_count; diff --git a/include/linux/module.h b/include/linux/module.h index 627ac082e2a..6155fa44168 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -337,6 +337,10 @@ struct module  	const char **trace_bprintk_fmt_start;  	unsigned int num_trace_bprintk_fmt;  #endif +#ifdef CONFIG_EVENT_TRACING +	struct ftrace_event_call *trace_events; +	unsigned int num_trace_events; +#endif  #ifdef CONFIG_MODULE_UNLOAD  	/* What modules depend on me? */ diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 28051da876d..15ca2c71af1 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -1,6 +1,8 @@  #ifndef _LINUX_TRACE_SEQ_H  #define _LINUX_TRACE_SEQ_H +#include <linux/fs.h> +  /*   * Trace sequences are used to allow a function to call several other functions   * to create a string of data to use (up to a max of PAGE_SIZE. diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 955b967acd7..60c5323bee6 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -477,6 +477,7 @@ __attribute__((__aligned__(4)))						\  __attribute__((section("_ftrace_events"))) event_##call = {		\  	.name			= #call,				\  	.system			= __stringify(TRACE_SYSTEM),		\ +	.event			= &ftrace_event_type_##call,		\  	.raw_init		= ftrace_raw_init_event_##call,		\  	.regfunc		= ftrace_raw_reg_event_##call,		\  	.unregfunc		= ftrace_raw_unreg_event_##call,	\ diff --git a/kernel/module.c b/kernel/module.c index e797812a4d9..a0394706f10 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -18,6 +18,7 @@  */  #include <linux/module.h>  #include <linux/moduleloader.h> +#include <linux/ftrace_event.h>  #include <linux/init.h>  #include <linux/kallsyms.h>  #include <linux/fs.h> @@ -2172,6 +2173,12 @@ static noinline struct module *load_module(void __user *umod,  					sizeof(*mod->tracepoints),  					&mod->num_tracepoints);  #endif +#ifdef CONFIG_EVENT_TRACING +	mod->trace_events = section_objs(hdr, sechdrs, secstrings, +					 "_ftrace_events", +					 sizeof(*mod->trace_events), +					 &mod->num_trace_events); +#endif  #ifdef CONFIG_MODVERSIONS  	if ((mod->num_syms && !mod->crcs) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 8b9e621b80b..a4b177720a6 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -713,7 +713,13 @@ event_subsystem_dir(const char *name, struct dentry *d_events)  		return d_events;  	} -	system->name = name; +	system->name = kstrdup(name, GFP_KERNEL); +	if (!system->name) { +		debugfs_remove(system->entry); +		kfree(system); +		return d_events; +	} +  	list_add(&system->list, &event_subsystems);  	system->preds = NULL; @@ -738,7 +744,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)  	 * If the trace point header did not define TRACE_SYSTEM  	 * then the system would be called "TRACE_SYSTEM".  	 */ -	if (strcmp(call->system, "TRACE_SYSTEM") != 0) +	if (strcmp(call->system, TRACE_SYSTEM) != 0)  		d_events = event_subsystem_dir(call->system, d_events);  	if (call->raw_init) { @@ -757,21 +763,13 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)  		return -1;  	} -	if (call->regfunc) { -		entry = debugfs_create_file("enable", 0644, call->dir, call, -					    &ftrace_enable_fops); -		if (!entry) -			pr_warning("Could not create debugfs " -				   "'%s/enable' entry\n", call->name); -	} +	if (call->regfunc) +		entry = trace_create_file("enable", 0644, call->dir, call, +					  &ftrace_enable_fops); -	if (call->id) { -		entry = debugfs_create_file("id", 0444, call->dir, call, -				&ftrace_event_id_fops); -		if (!entry) -			pr_warning("Could not create debugfs '%s/id' entry\n", -					call->name); -	} +	if (call->id) +		entry = trace_create_file("id", 0444, call->dir, call, +					  &ftrace_event_id_fops);  	if (call->define_fields) {  		ret = call->define_fields(); @@ -780,40 +778,102 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)  				   " events/%s\n", call->name);  			return ret;  		} -		entry = debugfs_create_file("filter", 0644, call->dir, call, -					    &ftrace_event_filter_fops); -		if (!entry) -			pr_warning("Could not create debugfs " -				   "'%s/filter' entry\n", call->name); +		entry = trace_create_file("filter", 0644, call->dir, call, +					  &ftrace_event_filter_fops);  	}  	/* A trace may not want to export its format */  	if (!call->show_format)  		return 0; -	entry = debugfs_create_file("format", 0444, call->dir, call, -				    &ftrace_event_format_fops); -	if (!entry) -		pr_warning("Could not create debugfs " -			   "'%s/format' entry\n", call->name); +	entry = trace_create_file("format", 0444, call->dir, call, +				  &ftrace_event_format_fops);  	return 0;  } +#define for_each_event(event, start, end)			\ +	for (event = start;					\ +	     (unsigned long)event < (unsigned long)end;		\ +	     event++) + +static void trace_module_add_events(struct module *mod) +{ +	struct ftrace_event_call *call, *start, *end; +	struct dentry *d_events; + +	start = mod->trace_events; +	end = mod->trace_events + mod->num_trace_events; + +	if (start == end) +		return; + +	d_events = event_trace_events_dir(); +	if (!d_events) +		return; + +	for_each_event(call, start, end) { +		/* The linker may leave blanks */ +		if (!call->name) +			continue; +		call->mod = mod; +		list_add(&call->list, &ftrace_events); +		event_create_dir(call, d_events); +	} +} + +static void trace_module_remove_events(struct module *mod) +{ +	struct ftrace_event_call *call, *p; + +	list_for_each_entry_safe(call, p, &ftrace_events, list) { +		if (call->mod == mod) { +			if (call->enabled) { +				call->enabled = 0; +				call->unregfunc(); +			} +			if (call->event) +				unregister_ftrace_event(call->event); +			debugfs_remove_recursive(call->dir); +			list_del(&call->list); +		} +	} +} + +int trace_module_notify(struct notifier_block *self, +			unsigned long val, void *data) +{ +	struct module *mod = data; + +	mutex_lock(&event_mutex); +	switch (val) { +	case MODULE_STATE_COMING: +		trace_module_add_events(mod); +		break; +	case MODULE_STATE_GOING: +		trace_module_remove_events(mod); +		break; +	} +	mutex_unlock(&event_mutex); + +	return 0; +} + +struct notifier_block trace_module_nb = { +	.notifier_call = trace_module_notify, +	.priority = 0, +}; +  extern struct ftrace_event_call __start_ftrace_events[];  extern struct ftrace_event_call __stop_ftrace_events[]; -#define for_each_event(event)						\ -	for (event = __start_ftrace_events;				\ -	     (unsigned long)event < (unsigned long)__stop_ftrace_events; \ -	     event++) -  static __init int event_trace_init(void)  {  	struct ftrace_event_call *call;  	struct dentry *d_tracer;  	struct dentry *entry;  	struct dentry *d_events; +	int ret;  	d_tracer = tracing_init_dentry();  	if (!d_tracer) @@ -837,7 +897,7 @@ static __init int event_trace_init(void)  	if (!d_events)  		return 0; -	for_each_event(call) { +	for_each_event(call, __start_ftrace_events, __stop_ftrace_events) {  		/* The linker may leave blanks */  		if (!call->name)  			continue; @@ -845,6 +905,10 @@ static __init int event_trace_init(void)  		event_create_dir(call, d_events);  	} +	ret = register_module_notifier(&trace_module_nb); +	if (!ret) +		pr_warning("Failed to register trace events module notifier\n"); +  	return 0;  }  fs_initcall(event_trace_init);  |