diff options
Diffstat (limited to 'kernel/trace/trace_events_trigger.c')
| -rw-r--r-- | kernel/trace/trace_events_trigger.c | 215 | 
1 files changed, 184 insertions, 31 deletions
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index d67992f3bb0e..a975571cde24 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -347,7 +347,7 @@ __init int register_event_command(struct event_command *cmd)   * Currently we only unregister event commands from __init, so mark   * this __init too.   */ -static __init int unregister_event_command(struct event_command *cmd) +__init int unregister_event_command(struct event_command *cmd)  {  	struct event_command *p, *n;  	int ret = -ENODEV; @@ -641,6 +641,7 @@ event_trigger_callback(struct event_command *cmd_ops,  	trigger_data->ops = trigger_ops;  	trigger_data->cmd_ops = cmd_ops;  	INIT_LIST_HEAD(&trigger_data->list); +	INIT_LIST_HEAD(&trigger_data->named_list);  	if (glob[0] == '!') {  		cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file); @@ -764,6 +765,148 @@ int set_trigger_filter(char *filter_str,  	return ret;  } +static LIST_HEAD(named_triggers); + +/** + * find_named_trigger - Find the common named trigger associated with @name + * @name: The name of the set of named triggers to find the common data for + * + * Named triggers are sets of triggers that share a common set of + * trigger data.  The first named trigger registered with a given name + * owns the common trigger data that the others subsequently + * registered with the same name will reference.  This function + * returns the common trigger data associated with that first + * registered instance. + * + * Return: the common trigger data for the given named trigger on + * success, NULL otherwise. + */ +struct event_trigger_data *find_named_trigger(const char *name) +{ +	struct event_trigger_data *data; + +	if (!name) +		return NULL; + +	list_for_each_entry(data, &named_triggers, named_list) { +		if (data->named_data) +			continue; +		if (strcmp(data->name, name) == 0) +			return data; +	} + +	return NULL; +} + +/** + * is_named_trigger - determine if a given trigger is a named trigger + * @test: The trigger data to test + * + * Return: true if 'test' is a named trigger, false otherwise. + */ +bool is_named_trigger(struct event_trigger_data *test) +{ +	struct event_trigger_data *data; + +	list_for_each_entry(data, &named_triggers, named_list) { +		if (test == data) +			return true; +	} + +	return false; +} + +/** + * save_named_trigger - save the trigger in the named trigger list + * @name: The name of the named trigger set + * @data: The trigger data to save + * + * Return: 0 if successful, negative error otherwise. + */ +int save_named_trigger(const char *name, struct event_trigger_data *data) +{ +	data->name = kstrdup(name, GFP_KERNEL); +	if (!data->name) +		return -ENOMEM; + +	list_add(&data->named_list, &named_triggers); + +	return 0; +} + +/** + * del_named_trigger - delete a trigger from the named trigger list + * @data: The trigger data to delete + */ +void del_named_trigger(struct event_trigger_data *data) +{ +	kfree(data->name); +	data->name = NULL; + +	list_del(&data->named_list); +} + +static void __pause_named_trigger(struct event_trigger_data *data, bool pause) +{ +	struct event_trigger_data *test; + +	list_for_each_entry(test, &named_triggers, named_list) { +		if (strcmp(test->name, data->name) == 0) { +			if (pause) { +				test->paused_tmp = test->paused; +				test->paused = true; +			} else { +				test->paused = test->paused_tmp; +			} +		} +	} +} + +/** + * pause_named_trigger - Pause all named triggers with the same name + * @data: The trigger data of a named trigger to pause + * + * Pauses a named trigger along with all other triggers having the + * same name.  Because named triggers share a common set of data, + * pausing only one is meaningless, so pausing one named trigger needs + * to pause all triggers with the same name. + */ +void pause_named_trigger(struct event_trigger_data *data) +{ +	__pause_named_trigger(data, true); +} + +/** + * unpause_named_trigger - Un-pause all named triggers with the same name + * @data: The trigger data of a named trigger to unpause + * + * Un-pauses a named trigger along with all other triggers having the + * same name.  Because named triggers share a common set of data, + * unpausing only one is meaningless, so unpausing one named trigger + * needs to unpause all triggers with the same name. + */ +void unpause_named_trigger(struct event_trigger_data *data) +{ +	__pause_named_trigger(data, false); +} + +/** + * set_named_trigger_data - Associate common named trigger data + * @data: The trigger data of a named trigger to unpause + * + * Named triggers are sets of triggers that share a common set of + * trigger data.  The first named trigger registered with a given name + * owns the common trigger data that the others subsequently + * registered with the same name will reference.  This function + * associates the common trigger data from the first trigger with the + * given trigger. + */ +void set_named_trigger_data(struct event_trigger_data *data, +			    struct event_trigger_data *named_data) +{ +	data->named_data = named_data; +} +  static void  traceon_trigger(struct event_trigger_data *data, void *rec)  { @@ -1062,15 +1205,6 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)  	unregister_event_command(&trigger_traceoff_cmd);  } -/* Avoid typos */ -#define ENABLE_EVENT_STR	"enable_event" -#define DISABLE_EVENT_STR	"disable_event" - -struct enable_trigger_data { -	struct trace_event_file		*file; -	bool				enable; -}; -  static void  event_enable_trigger(struct event_trigger_data *data, void *rec)  { @@ -1100,14 +1234,16 @@ event_enable_count_trigger(struct event_trigger_data *data, void *rec)  	event_enable_trigger(data, rec);  } -static int -event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, -			   struct event_trigger_data *data) +int event_enable_trigger_print(struct seq_file *m, +			       struct event_trigger_ops *ops, +			       struct event_trigger_data *data)  {  	struct enable_trigger_data *enable_data = data->private_data;  	seq_printf(m, "%s:%s:%s", -		   enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR, +		   enable_data->hist ? +		   (enable_data->enable ? ENABLE_HIST_STR : DISABLE_HIST_STR) : +		   (enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR),  		   enable_data->file->event_call->class->system,  		   trace_event_name(enable_data->file->event_call)); @@ -1124,9 +1260,8 @@ event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,  	return 0;  } -static void -event_enable_trigger_free(struct event_trigger_ops *ops, -			  struct event_trigger_data *data) +void event_enable_trigger_free(struct event_trigger_ops *ops, +			       struct event_trigger_data *data)  {  	struct enable_trigger_data *enable_data = data->private_data; @@ -1171,10 +1306,9 @@ static struct event_trigger_ops event_disable_count_trigger_ops = {  	.free			= event_enable_trigger_free,  }; -static int -event_enable_trigger_func(struct event_command *cmd_ops, -			  struct trace_event_file *file, -			  char *glob, char *cmd, char *param) +int event_enable_trigger_func(struct event_command *cmd_ops, +			      struct trace_event_file *file, +			      char *glob, char *cmd, char *param)  {  	struct trace_event_file *event_enable_file;  	struct enable_trigger_data *enable_data; @@ -1183,6 +1317,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,  	struct trace_array *tr = file->tr;  	const char *system;  	const char *event; +	bool hist = false;  	char *trigger;  	char *number;  	bool enable; @@ -1207,8 +1342,15 @@ event_enable_trigger_func(struct event_command *cmd_ops,  	if (!event_enable_file)  		goto out; -	enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; +#ifdef CONFIG_HIST_TRIGGERS +	hist = ((strcmp(cmd, ENABLE_HIST_STR) == 0) || +		(strcmp(cmd, DISABLE_HIST_STR) == 0)); +	enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) || +		  (strcmp(cmd, ENABLE_HIST_STR) == 0)); +#else +	enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; +#endif  	trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);  	ret = -ENOMEM; @@ -1228,6 +1370,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,  	INIT_LIST_HEAD(&trigger_data->list);  	RCU_INIT_POINTER(trigger_data->filter, NULL); +	enable_data->hist = hist;  	enable_data->enable = enable;  	enable_data->file = event_enable_file;  	trigger_data->private_data = enable_data; @@ -1305,10 +1448,10 @@ event_enable_trigger_func(struct event_command *cmd_ops,  	goto out;  } -static int event_enable_register_trigger(char *glob, -					 struct event_trigger_ops *ops, -					 struct event_trigger_data *data, -					 struct trace_event_file *file) +int event_enable_register_trigger(char *glob, +				  struct event_trigger_ops *ops, +				  struct event_trigger_data *data, +				  struct trace_event_file *file)  {  	struct enable_trigger_data *enable_data = data->private_data;  	struct enable_trigger_data *test_enable_data; @@ -1318,6 +1461,8 @@ static int event_enable_register_trigger(char *glob,  	list_for_each_entry_rcu(test, &file->triggers, list) {  		test_enable_data = test->private_data;  		if (test_enable_data && +		    (test->cmd_ops->trigger_type == +		     data->cmd_ops->trigger_type) &&  		    (test_enable_data->file == enable_data->file)) {  			ret = -EEXIST;  			goto out; @@ -1343,10 +1488,10 @@ out:  	return ret;  } -static void event_enable_unregister_trigger(char *glob, -					    struct event_trigger_ops *ops, -					    struct event_trigger_data *test, -					    struct trace_event_file *file) +void event_enable_unregister_trigger(char *glob, +				     struct event_trigger_ops *ops, +				     struct event_trigger_data *test, +				     struct trace_event_file *file)  {  	struct enable_trigger_data *test_enable_data = test->private_data;  	struct enable_trigger_data *enable_data; @@ -1356,6 +1501,8 @@ static void event_enable_unregister_trigger(char *glob,  	list_for_each_entry_rcu(data, &file->triggers, list) {  		enable_data = data->private_data;  		if (enable_data && +		    (data->cmd_ops->trigger_type == +		     test->cmd_ops->trigger_type) &&  		    (enable_data->file == test_enable_data->file)) {  			unregistered = true;  			list_del_rcu(&data->list); @@ -1375,8 +1522,12 @@ event_enable_get_trigger_ops(char *cmd, char *param)  	struct event_trigger_ops *ops;  	bool enable; +#ifdef CONFIG_HIST_TRIGGERS +	enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) || +		  (strcmp(cmd, ENABLE_HIST_STR) == 0)); +#else  	enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; - +#endif  	if (enable)  		ops = param ? &event_enable_count_trigger_ops :  			&event_enable_trigger_ops; @@ -1447,6 +1598,8 @@ __init int register_trigger_cmds(void)  	register_trigger_snapshot_cmd();  	register_trigger_stacktrace_cmd();  	register_trigger_enable_disable_cmds(); +	register_trigger_hist_enable_disable_cmds(); +	register_trigger_hist_cmd();  	return 0;  }  |