From: Tom Zanussi Date: Mon, 26 Jun 2017 17:49:22 -0500 Subject: [PATCH 21/32] tracing: Add hist trigger action hook Add a hook for executing extra actions whenever a histogram entry is added or updated. The default 'action' when a hist entry is added to a histogram is to update the set of values associated with it. Some applications may want to perform additional actions at that point, such as generate another event, or compare and save a maximum. Add a simple framework for doing that; specific actions will be implemented on top of it in later patches. Signed-off-by: Tom Zanussi Signed-off-by: Sebastian Andrzej Siewior --- kernel/trace/trace_events_hist.c | 114 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 3 deletions(-) --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -33,6 +33,7 @@ typedef u64 (*hist_field_fn_t) (struct h #define HIST_FIELD_OPERANDS_MAX 2 #define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX) +#define HIST_ACTIONS_MAX 8 enum field_op_id { FIELD_OP_NONE, @@ -233,6 +234,9 @@ struct hist_trigger_attrs { char *assignment_str[TRACING_MAP_VARS_MAX]; unsigned int n_assignments; + + char *action_str[HIST_ACTIONS_MAX]; + unsigned int n_actions; }; struct hist_trigger_data { @@ -252,6 +256,21 @@ struct hist_trigger_data { bool remove; struct hist_field *var_refs[TRACING_MAP_VARS_MAX]; unsigned int n_var_refs; + + struct action_data *actions[HIST_ACTIONS_MAX]; + unsigned int n_actions; +}; + +struct action_data; + +typedef void (*action_fn_t) (struct hist_trigger_data *hist_data, + struct tracing_map_elt *elt, void *rec, + struct ring_buffer_event *rbe, + struct action_data *data, u64 *var_ref_vals); + +struct action_data { + action_fn_t fn; + unsigned int var_ref_idx; }; static u64 hist_field_timestamp(struct hist_field *hist_field, @@ -681,6 +700,9 @@ static void destroy_hist_trigger_attrs(s for (i = 0; i < attrs->n_assignments; i++) kfree(attrs->assignment_str[i]); + for (i = 0; i < attrs->n_actions; i++) + kfree(attrs->action_str[i]); + kfree(attrs->name); kfree(attrs->sort_key_str); kfree(attrs->keys_str); @@ -688,6 +710,16 @@ static void destroy_hist_trigger_attrs(s kfree(attrs); } +static int parse_action(char *str, struct hist_trigger_attrs *attrs) +{ + int ret = 0; + + if (attrs->n_actions >= HIST_ACTIONS_MAX) + return ret; + + return ret; +} + static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) { int ret = 0; @@ -755,8 +787,9 @@ static struct hist_trigger_attrs *parse_ else if (strcmp(str, "clear") == 0) attrs->clear = true; else { - ret = -EINVAL; - goto free; + ret = parse_action(str, attrs); + if (ret) + goto free; } } @@ -1722,11 +1755,63 @@ static int create_sort_keys(struct hist_ return ret; } +static void destroy_actions(struct hist_trigger_data *hist_data) +{ + unsigned int i; + + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + + kfree(data); + } +} + +static int create_actions(struct hist_trigger_data *hist_data, + struct trace_event_file *file) +{ + unsigned int i; + int ret = 0; + char *str; + + for (i = 0; i < hist_data->attrs->n_actions; i++) { + str = hist_data->attrs->action_str[i]; + } + + return ret; +} + +static void print_actions(struct seq_file *m, + struct hist_trigger_data *hist_data, + struct tracing_map_elt *elt) +{ + unsigned int i; + + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + } +} + +static void print_actions_spec(struct seq_file *m, + struct hist_trigger_data *hist_data) +{ + unsigned int i; + + for (i = 0; i < hist_data->n_actions; i++) { + struct action_data *data = hist_data->actions[i]; + } +} + static void destroy_hist_data(struct hist_trigger_data *hist_data) { + if (!hist_data) + return; + destroy_hist_trigger_attrs(hist_data->attrs); destroy_hist_fields(hist_data); tracing_map_destroy(hist_data->map); + + destroy_actions(hist_data); + kfree(hist_data); } @@ -1886,6 +1971,20 @@ static inline void add_to_key(char *comp memcpy(compound_key + key_field->offset, key, size); } +static void +hist_trigger_actions(struct hist_trigger_data *hist_data, + struct tracing_map_elt *elt, void *rec, + struct ring_buffer_event *rbe, u64 *var_ref_vals) +{ + struct action_data *data; + unsigned int i; + + for (i = 0; i < hist_data->n_actions; i++) { + data = hist_data->actions[i]; + data->fn(hist_data, elt, rec, rbe, data, var_ref_vals); + } +} + static void event_hist_trigger(struct event_trigger_data *data, void *rec, struct ring_buffer_event *rbe) { @@ -1941,6 +2040,9 @@ static void event_hist_trigger(struct ev return; hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals); + + if (resolve_var_refs(hist_data, key, var_ref_vals, true)) + hist_trigger_actions(hist_data, elt, rec, rbe, var_ref_vals); } static void hist_trigger_stacktrace_print(struct seq_file *m, @@ -2278,6 +2380,8 @@ static int event_hist_trigger_print(stru } seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits)); + print_actions_spec(m, hist_data); + if (data->filter_str) seq_printf(m, " if %s", data->filter_str); @@ -2740,6 +2844,10 @@ static int event_hist_trigger_func(struc if (has_hist_vars(hist_data)) save_hist_vars(hist_data); + ret = create_actions(hist_data, file); + if (ret) + goto out_unreg; + ret = tracing_map_init(hist_data->map); if (ret) goto out_unreg; @@ -2761,8 +2869,8 @@ static int event_hist_trigger_func(struc remove_hist_vars(hist_data); kfree(trigger_data); - destroy_hist_data(hist_data); + goto out; }