diff options
Diffstat (limited to 'kernel/trace/trace_events_hist.c')
| -rw-r--r-- | kernel/trace/trace_events_hist.c | 179 | 
1 files changed, 150 insertions, 29 deletions
| diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index 1260c23cfa5f..1d536219b624 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -114,6 +114,7 @@ enum hist_field_fn {  	HIST_FIELD_FN_BUCKET,  	HIST_FIELD_FN_TIMESTAMP,  	HIST_FIELD_FN_CPU, +	HIST_FIELD_FN_COMM,  	HIST_FIELD_FN_STRING,  	HIST_FIELD_FN_DYNSTRING,  	HIST_FIELD_FN_RELDYNSTRING, @@ -506,6 +507,7 @@ enum hist_field_flags {  	HIST_FIELD_FL_CONST		= 1 << 18,  	HIST_FIELD_FL_PERCENT		= 1 << 19,  	HIST_FIELD_FL_GRAPH		= 1 << 20, +	HIST_FIELD_FL_COMM		= 1 << 21,  };  struct var_defs { @@ -885,6 +887,15 @@ static u64 hist_field_cpu(struct hist_field *hist_field,  	return cpu;  } +static u64 hist_field_comm(struct hist_field *hist_field, +			  struct tracing_map_elt *elt, +			  struct trace_buffer *buffer, +			  struct ring_buffer_event *rbe, +			  void *event) +{ +	return (u64)(unsigned long)current->comm; +} +  /**   * check_field_for_var_ref - Check if a VAR_REF field references a variable   * @hist_field: The VAR_REF field to check @@ -1338,6 +1349,8 @@ static const char *hist_field_name(struct hist_field *field,  		field_name = hist_field_name(field->operands[0], ++level);  	else if (field->flags & HIST_FIELD_FL_CPU)  		field_name = "common_cpu"; +	else if (field->flags & HIST_FIELD_FL_COMM) +		field_name = "common_comm";  	else if (field->flags & HIST_FIELD_FL_EXPR ||  		 field->flags & HIST_FIELD_FL_VAR_REF) {  		if (field->system) { @@ -2015,6 +2028,13 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,  		goto out;  	} +	if (flags & HIST_FIELD_FL_COMM) { +		hist_field->fn_num = HIST_FIELD_FN_COMM; +		hist_field->size = MAX_FILTER_STR_VAL; +		hist_field->type = "char[]"; +		goto out; +	} +  	if (WARN_ON_ONCE(!field))  		goto out; @@ -2359,9 +2379,11 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,  			hist_data->attrs->ts_in_usecs = true;  	} else if (strcmp(field_name, "common_stacktrace") == 0) {  		*flags |= HIST_FIELD_FL_STACKTRACE; -	} else if (strcmp(field_name, "common_cpu") == 0) +	} else if (strcmp(field_name, "common_cpu") == 0) {  		*flags |= HIST_FIELD_FL_CPU; -	else if (strcmp(field_name, "hitcount") == 0) +	} else if (strcmp(field_name, "common_comm") == 0) { +		*flags |= HIST_FIELD_FL_COMM | HIST_FIELD_FL_STRING; +	} else if (strcmp(field_name, "hitcount") == 0)  		*flags |= HIST_FIELD_FL_HITCOUNT;  	else {  		field = trace_find_event_field(file->event_call, field_name); @@ -2377,6 +2399,8 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,  				*flags |= HIST_FIELD_FL_CPU;  			} else if (field && field->filter_type == FILTER_STACKTRACE) {  				*flags |= HIST_FIELD_FL_STACKTRACE; +			} else if (field && field->filter_type == FILTER_COMM) { +				*flags |= HIST_FIELD_FL_COMM | HIST_FIELD_FL_STRING;  			} else {  				hist_err(tr, HIST_ERR_FIELD_NOT_FOUND,  					 errpos(field_name)); @@ -4327,6 +4351,8 @@ static u64 hist_fn_call(struct hist_field *hist_field,  		return hist_field_timestamp(hist_field, elt, buffer, rbe, event);  	case HIST_FIELD_FN_CPU:  		return hist_field_cpu(hist_field, elt, buffer, rbe, event); +	case HIST_FIELD_FN_COMM: +		return hist_field_comm(hist_field, elt, buffer, rbe, event);  	case HIST_FIELD_FN_STRING:  		return hist_field_string(hist_field, elt, buffer, rbe, event);  	case HIST_FIELD_FN_DYNSTRING: @@ -5212,22 +5238,25 @@ static inline void add_to_key(char *compound_key, void *key,  	size_t size = key_field->size;  	if (key_field->flags & HIST_FIELD_FL_STRING) { -		struct ftrace_event_field *field; -		field = key_field->field; -		if (field->filter_type == FILTER_DYN_STRING || -		    field->filter_type == FILTER_RDYN_STRING) -			size = *(u32 *)(rec + field->offset) >> 16; -		else if (field->filter_type == FILTER_STATIC_STRING) -			size = field->size; +		if (key_field->flags & HIST_FIELD_FL_COMM) { +			size = strlen((char *)key); +		} else { +			struct ftrace_event_field *field; + +			field = key_field->field; +			if (field->filter_type == FILTER_DYN_STRING || +			    field->filter_type == FILTER_RDYN_STRING) +				size = *(u32 *)(rec + field->offset) >> 16; +			else if (field->filter_type == FILTER_STATIC_STRING) +				size = field->size; +		}  		/* ensure NULL-termination */  		if (size > key_field->size - 1)  			size = key_field->size - 1; - -		strncpy(compound_key + key_field->offset, (char *)key, size); -	} else -		memcpy(compound_key + key_field->offset, key, size); +	} +	memcpy(compound_key + key_field->offset, key, size);  }  static void @@ -5246,17 +5275,94 @@ hist_trigger_actions(struct hist_trigger_data *hist_data,  	}  } +/* + * The hist_pad structure is used to save information to create + * a histogram from the histogram trigger. It's too big to store + * on the stack, so when the histogram trigger is initialized + * a percpu array of 4 hist_pad structures is allocated. + * This will cover every context from normal, softirq, irq and NMI + * in the very unlikely event that a tigger happens at each of + * these contexts and interrupts a currently active trigger. + */ +struct hist_pad { +	unsigned long		entries[HIST_STACKTRACE_DEPTH]; +	u64			var_ref_vals[TRACING_MAP_VARS_MAX]; +	char			compound_key[HIST_KEY_SIZE_MAX]; +}; + +static struct hist_pad __percpu *hist_pads; +static DEFINE_PER_CPU(int, hist_pad_cnt); +static refcount_t hist_pad_ref; + +/* One hist_pad for every context (normal, softirq, irq, NMI) */ +#define MAX_HIST_CNT 4 + +static int alloc_hist_pad(void) +{ +	lockdep_assert_held(&event_mutex); + +	if (refcount_read(&hist_pad_ref)) { +		refcount_inc(&hist_pad_ref); +		return 0; +	} + +	hist_pads = __alloc_percpu(sizeof(struct hist_pad) * MAX_HIST_CNT, +				   __alignof__(struct hist_pad)); +	if (!hist_pads) +		return -ENOMEM; + +	refcount_set(&hist_pad_ref, 1); +	return 0; +} + +static void free_hist_pad(void) +{ +	lockdep_assert_held(&event_mutex); + +	if (!refcount_dec_and_test(&hist_pad_ref)) +		return; + +	free_percpu(hist_pads); +	hist_pads = NULL; +} + +static struct hist_pad *get_hist_pad(void) +{ +	struct hist_pad *hist_pad; +	int cnt; + +	if (WARN_ON_ONCE(!hist_pads)) +		return NULL; + +	preempt_disable(); + +	hist_pad = per_cpu_ptr(hist_pads, smp_processor_id()); + +	if (this_cpu_read(hist_pad_cnt) == MAX_HIST_CNT) { +		preempt_enable(); +		return NULL; +	} + +	cnt = this_cpu_inc_return(hist_pad_cnt) - 1; + +	return &hist_pad[cnt]; +} + +static void put_hist_pad(void) +{ +	this_cpu_dec(hist_pad_cnt); +	preempt_enable(); +} +  static void event_hist_trigger(struct event_trigger_data *data,  			       struct trace_buffer *buffer, void *rec,  			       struct ring_buffer_event *rbe)  {  	struct hist_trigger_data *hist_data = data->private_data;  	bool use_compound_key = (hist_data->n_keys > 1); -	unsigned long entries[HIST_STACKTRACE_DEPTH]; -	u64 var_ref_vals[TRACING_MAP_VARS_MAX]; -	char compound_key[HIST_KEY_SIZE_MAX];  	struct tracing_map_elt *elt = NULL;  	struct hist_field *key_field; +	struct hist_pad *hist_pad;  	u64 field_contents;  	void *key = NULL;  	unsigned int i; @@ -5264,12 +5370,18 @@ static void event_hist_trigger(struct event_trigger_data *data,  	if (unlikely(!rbe))  		return; -	memset(compound_key, 0, hist_data->key_size); +	hist_pad = get_hist_pad(); +	if (!hist_pad) +		return; + +	memset(hist_pad->compound_key, 0, hist_data->key_size);  	for_each_hist_key_field(i, hist_data) {  		key_field = hist_data->fields[i];  		if (key_field->flags & HIST_FIELD_FL_STACKTRACE) { +			unsigned long *entries = hist_pad->entries; +  			memset(entries, 0, HIST_STACKTRACE_SIZE);  			if (key_field->field) {  				unsigned long *stack, n_entries; @@ -5293,26 +5405,31 @@ static void event_hist_trigger(struct event_trigger_data *data,  		}  		if (use_compound_key) -			add_to_key(compound_key, key, key_field, rec); +			add_to_key(hist_pad->compound_key, key, key_field, rec);  	}  	if (use_compound_key) -		key = compound_key; +		key = hist_pad->compound_key;  	if (hist_data->n_var_refs && -	    !resolve_var_refs(hist_data, key, var_ref_vals, false)) -		return; +	    !resolve_var_refs(hist_data, key, hist_pad->var_ref_vals, false)) +		goto out;  	elt = tracing_map_insert(hist_data->map, key);  	if (!elt) -		return; +		goto out; -	hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, var_ref_vals); +	hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, hist_pad->var_ref_vals); -	if (resolve_var_refs(hist_data, key, var_ref_vals, true)) -		hist_trigger_actions(hist_data, elt, buffer, rec, rbe, key, var_ref_vals); +	if (resolve_var_refs(hist_data, key, hist_pad->var_ref_vals, true)) { +		hist_trigger_actions(hist_data, elt, buffer, rec, rbe, +				     key, hist_pad->var_ref_vals); +	}  	hist_poll_wakeup(); + + out: +	put_hist_pad();  }  static void hist_trigger_stacktrace_print(struct seq_file *m, @@ -6011,6 +6128,8 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)  	if (hist_field->flags & HIST_FIELD_FL_CPU)  		seq_puts(m, "common_cpu"); +	if (hist_field->flags & HIST_FIELD_FL_COMM) +		seq_puts(m, "common_comm");  	else if (hist_field->flags & HIST_FIELD_FL_CONST)  		seq_printf(m, "%llu", hist_field->constant);  	else if (field_name) { @@ -6157,6 +6276,9 @@ static int event_hist_trigger_init(struct event_trigger_data *data)  {  	struct hist_trigger_data *hist_data = data->private_data; +	if (alloc_hist_pad() < 0) +		return -ENOMEM; +  	if (!data->ref && hist_data->attrs->name)  		save_named_trigger(hist_data->attrs->name, data); @@ -6201,6 +6323,7 @@ static void event_hist_trigger_free(struct event_trigger_data *data)  		destroy_hist_data(hist_data);  	} +	free_hist_pad();  }  static const struct event_trigger_ops event_hist_trigger_ops = { @@ -6216,9 +6339,7 @@ static int event_hist_trigger_named_init(struct event_trigger_data *data)  	save_named_trigger(data->named_data->name, data); -	event_hist_trigger_init(data->named_data); - -	return 0; +	return event_hist_trigger_init(data->named_data);  }  static void event_hist_trigger_named_free(struct event_trigger_data *data) @@ -6705,7 +6826,7 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,  		return PTR_ERR(hist_data);  	} -	trigger_data = event_trigger_alloc(cmd_ops, cmd, param, hist_data); +	trigger_data = trigger_data_alloc(cmd_ops, cmd, param, hist_data);  	if (!trigger_data) {  		ret = -ENOMEM;  		goto out_free; | 
