162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * trace_events_hist - trace event hist triggers
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/kallsyms.h>
1062306a36Sopenharmony_ci#include <linux/security.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/stacktrace.h>
1462306a36Sopenharmony_ci#include <linux/rculist.h>
1562306a36Sopenharmony_ci#include <linux/tracefs.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* for gfp flag names */
1862306a36Sopenharmony_ci#include <linux/trace_events.h>
1962306a36Sopenharmony_ci#include <trace/events/mmflags.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "tracing_map.h"
2262306a36Sopenharmony_ci#include "trace_synth.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define ERRORS								\
2562306a36Sopenharmony_ci	C(NONE,			"No error"),				\
2662306a36Sopenharmony_ci	C(DUPLICATE_VAR,	"Variable already defined"),		\
2762306a36Sopenharmony_ci	C(VAR_NOT_UNIQUE,	"Variable name not unique, need to use fully qualified name (subsys.event.var) for variable"), \
2862306a36Sopenharmony_ci	C(TOO_MANY_VARS,	"Too many variables defined"),		\
2962306a36Sopenharmony_ci	C(MALFORMED_ASSIGNMENT,	"Malformed assignment"),		\
3062306a36Sopenharmony_ci	C(NAMED_MISMATCH,	"Named hist trigger doesn't match existing named trigger (includes variables)"), \
3162306a36Sopenharmony_ci	C(TRIGGER_EEXIST,	"Hist trigger already exists"),		\
3262306a36Sopenharmony_ci	C(TRIGGER_ENOENT_CLEAR,	"Can't clear or continue a nonexistent hist trigger"), \
3362306a36Sopenharmony_ci	C(SET_CLOCK_FAIL,	"Couldn't set trace_clock"),		\
3462306a36Sopenharmony_ci	C(BAD_FIELD_MODIFIER,	"Invalid field modifier"),		\
3562306a36Sopenharmony_ci	C(TOO_MANY_SUBEXPR,	"Too many subexpressions (3 max)"),	\
3662306a36Sopenharmony_ci	C(TIMESTAMP_MISMATCH,	"Timestamp units in expression don't match"), \
3762306a36Sopenharmony_ci	C(TOO_MANY_FIELD_VARS,	"Too many field variables defined"),	\
3862306a36Sopenharmony_ci	C(EVENT_FILE_NOT_FOUND,	"Event file not found"),		\
3962306a36Sopenharmony_ci	C(HIST_NOT_FOUND,	"Matching event histogram not found"),	\
4062306a36Sopenharmony_ci	C(HIST_CREATE_FAIL,	"Couldn't create histogram for field"),	\
4162306a36Sopenharmony_ci	C(SYNTH_VAR_NOT_FOUND,	"Couldn't find synthetic variable"),	\
4262306a36Sopenharmony_ci	C(SYNTH_EVENT_NOT_FOUND,"Couldn't find synthetic event"),	\
4362306a36Sopenharmony_ci	C(SYNTH_TYPE_MISMATCH,	"Param type doesn't match synthetic event field type"), \
4462306a36Sopenharmony_ci	C(SYNTH_COUNT_MISMATCH,	"Param count doesn't match synthetic event field count"), \
4562306a36Sopenharmony_ci	C(FIELD_VAR_PARSE_FAIL,	"Couldn't parse field variable"),	\
4662306a36Sopenharmony_ci	C(VAR_CREATE_FIND_FAIL,	"Couldn't create or find variable"),	\
4762306a36Sopenharmony_ci	C(ONX_NOT_VAR,		"For onmax(x) or onchange(x), x must be a variable"), \
4862306a36Sopenharmony_ci	C(ONX_VAR_NOT_FOUND,	"Couldn't find onmax or onchange variable"), \
4962306a36Sopenharmony_ci	C(ONX_VAR_CREATE_FAIL,	"Couldn't create onmax or onchange variable"), \
5062306a36Sopenharmony_ci	C(FIELD_VAR_CREATE_FAIL,"Couldn't create field variable"),	\
5162306a36Sopenharmony_ci	C(TOO_MANY_PARAMS,	"Too many action params"),		\
5262306a36Sopenharmony_ci	C(PARAM_NOT_FOUND,	"Couldn't find param"),			\
5362306a36Sopenharmony_ci	C(INVALID_PARAM,	"Invalid action param"),		\
5462306a36Sopenharmony_ci	C(ACTION_NOT_FOUND,	"No action found"),			\
5562306a36Sopenharmony_ci	C(NO_SAVE_PARAMS,	"No params found for save()"),		\
5662306a36Sopenharmony_ci	C(TOO_MANY_SAVE_ACTIONS,"Can't have more than one save() action per hist"), \
5762306a36Sopenharmony_ci	C(ACTION_MISMATCH,	"Handler doesn't support action"),	\
5862306a36Sopenharmony_ci	C(NO_CLOSING_PAREN,	"No closing paren found"),		\
5962306a36Sopenharmony_ci	C(SUBSYS_NOT_FOUND,	"Missing subsystem"),			\
6062306a36Sopenharmony_ci	C(INVALID_SUBSYS_EVENT,	"Invalid subsystem or event name"),	\
6162306a36Sopenharmony_ci	C(INVALID_REF_KEY,	"Using variable references in keys not supported"), \
6262306a36Sopenharmony_ci	C(VAR_NOT_FOUND,	"Couldn't find variable"),		\
6362306a36Sopenharmony_ci	C(FIELD_NOT_FOUND,	"Couldn't find field"),			\
6462306a36Sopenharmony_ci	C(EMPTY_ASSIGNMENT,	"Empty assignment"),			\
6562306a36Sopenharmony_ci	C(INVALID_SORT_MODIFIER,"Invalid sort modifier"),		\
6662306a36Sopenharmony_ci	C(EMPTY_SORT_FIELD,	"Empty sort field"),			\
6762306a36Sopenharmony_ci	C(TOO_MANY_SORT_FIELDS,	"Too many sort fields (Max = 2)"),	\
6862306a36Sopenharmony_ci	C(INVALID_SORT_FIELD,	"Sort field must be a key or a val"),	\
6962306a36Sopenharmony_ci	C(INVALID_STR_OPERAND,	"String type can not be an operand in expression"), \
7062306a36Sopenharmony_ci	C(EXPECT_NUMBER,	"Expecting numeric literal"),		\
7162306a36Sopenharmony_ci	C(UNARY_MINUS_SUBEXPR,	"Unary minus not supported in sub-expressions"), \
7262306a36Sopenharmony_ci	C(DIVISION_BY_ZERO,	"Division by zero"),			\
7362306a36Sopenharmony_ci	C(NEED_NOHC_VAL,	"Non-hitcount value is required for 'nohitcount'"),
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#undef C
7662306a36Sopenharmony_ci#define C(a, b)		HIST_ERR_##a
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cienum { ERRORS };
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#undef C
8162306a36Sopenharmony_ci#define C(a, b)		b
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const char *err_text[] = { ERRORS };
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct hist_field;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_citypedef u64 (*hist_field_fn_t) (struct hist_field *field,
8862306a36Sopenharmony_ci				struct tracing_map_elt *elt,
8962306a36Sopenharmony_ci				struct trace_buffer *buffer,
9062306a36Sopenharmony_ci				struct ring_buffer_event *rbe,
9162306a36Sopenharmony_ci				void *event);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define HIST_FIELD_OPERANDS_MAX	2
9462306a36Sopenharmony_ci#define HIST_FIELDS_MAX		(TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
9562306a36Sopenharmony_ci#define HIST_ACTIONS_MAX	8
9662306a36Sopenharmony_ci#define HIST_CONST_DIGITS_MAX	21
9762306a36Sopenharmony_ci#define HIST_DIV_SHIFT		20  /* For optimizing division by constants */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cienum field_op_id {
10062306a36Sopenharmony_ci	FIELD_OP_NONE,
10162306a36Sopenharmony_ci	FIELD_OP_PLUS,
10262306a36Sopenharmony_ci	FIELD_OP_MINUS,
10362306a36Sopenharmony_ci	FIELD_OP_UNARY_MINUS,
10462306a36Sopenharmony_ci	FIELD_OP_DIV,
10562306a36Sopenharmony_ci	FIELD_OP_MULT,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cienum hist_field_fn {
10962306a36Sopenharmony_ci	HIST_FIELD_FN_NOP,
11062306a36Sopenharmony_ci	HIST_FIELD_FN_VAR_REF,
11162306a36Sopenharmony_ci	HIST_FIELD_FN_COUNTER,
11262306a36Sopenharmony_ci	HIST_FIELD_FN_CONST,
11362306a36Sopenharmony_ci	HIST_FIELD_FN_LOG2,
11462306a36Sopenharmony_ci	HIST_FIELD_FN_BUCKET,
11562306a36Sopenharmony_ci	HIST_FIELD_FN_TIMESTAMP,
11662306a36Sopenharmony_ci	HIST_FIELD_FN_CPU,
11762306a36Sopenharmony_ci	HIST_FIELD_FN_STRING,
11862306a36Sopenharmony_ci	HIST_FIELD_FN_DYNSTRING,
11962306a36Sopenharmony_ci	HIST_FIELD_FN_RELDYNSTRING,
12062306a36Sopenharmony_ci	HIST_FIELD_FN_PSTRING,
12162306a36Sopenharmony_ci	HIST_FIELD_FN_S64,
12262306a36Sopenharmony_ci	HIST_FIELD_FN_U64,
12362306a36Sopenharmony_ci	HIST_FIELD_FN_S32,
12462306a36Sopenharmony_ci	HIST_FIELD_FN_U32,
12562306a36Sopenharmony_ci	HIST_FIELD_FN_S16,
12662306a36Sopenharmony_ci	HIST_FIELD_FN_U16,
12762306a36Sopenharmony_ci	HIST_FIELD_FN_S8,
12862306a36Sopenharmony_ci	HIST_FIELD_FN_U8,
12962306a36Sopenharmony_ci	HIST_FIELD_FN_UMINUS,
13062306a36Sopenharmony_ci	HIST_FIELD_FN_MINUS,
13162306a36Sopenharmony_ci	HIST_FIELD_FN_PLUS,
13262306a36Sopenharmony_ci	HIST_FIELD_FN_DIV,
13362306a36Sopenharmony_ci	HIST_FIELD_FN_MULT,
13462306a36Sopenharmony_ci	HIST_FIELD_FN_DIV_POWER2,
13562306a36Sopenharmony_ci	HIST_FIELD_FN_DIV_NOT_POWER2,
13662306a36Sopenharmony_ci	HIST_FIELD_FN_DIV_MULT_SHIFT,
13762306a36Sopenharmony_ci	HIST_FIELD_FN_EXECNAME,
13862306a36Sopenharmony_ci	HIST_FIELD_FN_STACK,
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci * A hist_var (histogram variable) contains variable information for
14362306a36Sopenharmony_ci * hist_fields having the HIST_FIELD_FL_VAR or HIST_FIELD_FL_VAR_REF
14462306a36Sopenharmony_ci * flag set.  A hist_var has a variable name e.g. ts0, and is
14562306a36Sopenharmony_ci * associated with a given histogram trigger, as specified by
14662306a36Sopenharmony_ci * hist_data.  The hist_var idx is the unique index assigned to the
14762306a36Sopenharmony_ci * variable by the hist trigger's tracing_map.  The idx is what is
14862306a36Sopenharmony_ci * used to set a variable's value and, by a variable reference, to
14962306a36Sopenharmony_ci * retrieve it.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistruct hist_var {
15262306a36Sopenharmony_ci	char				*name;
15362306a36Sopenharmony_ci	struct hist_trigger_data	*hist_data;
15462306a36Sopenharmony_ci	unsigned int			idx;
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistruct hist_field {
15862306a36Sopenharmony_ci	struct ftrace_event_field	*field;
15962306a36Sopenharmony_ci	unsigned long			flags;
16062306a36Sopenharmony_ci	unsigned long			buckets;
16162306a36Sopenharmony_ci	const char			*type;
16262306a36Sopenharmony_ci	struct hist_field		*operands[HIST_FIELD_OPERANDS_MAX];
16362306a36Sopenharmony_ci	struct hist_trigger_data	*hist_data;
16462306a36Sopenharmony_ci	enum hist_field_fn		fn_num;
16562306a36Sopenharmony_ci	unsigned int			ref;
16662306a36Sopenharmony_ci	unsigned int			size;
16762306a36Sopenharmony_ci	unsigned int			offset;
16862306a36Sopenharmony_ci	unsigned int                    is_signed;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/*
17162306a36Sopenharmony_ci	 * Variable fields contain variable-specific info in var.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	struct hist_var			var;
17462306a36Sopenharmony_ci	enum field_op_id		operator;
17562306a36Sopenharmony_ci	char				*system;
17662306a36Sopenharmony_ci	char				*event_name;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * The name field is used for EXPR and VAR_REF fields.  VAR
18062306a36Sopenharmony_ci	 * fields contain the variable name in var.name.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	char				*name;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * When a histogram trigger is hit, if it has any references
18662306a36Sopenharmony_ci	 * to variables, the values of those variables are collected
18762306a36Sopenharmony_ci	 * into a var_ref_vals array by resolve_var_refs().  The
18862306a36Sopenharmony_ci	 * current value of each variable is read from the tracing_map
18962306a36Sopenharmony_ci	 * using the hist field's hist_var.idx and entered into the
19062306a36Sopenharmony_ci	 * var_ref_idx entry i.e. var_ref_vals[var_ref_idx].
19162306a36Sopenharmony_ci	 */
19262306a36Sopenharmony_ci	unsigned int			var_ref_idx;
19362306a36Sopenharmony_ci	bool                            read_once;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	unsigned int			var_str_idx;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Numeric literals are represented as u64 */
19862306a36Sopenharmony_ci	u64				constant;
19962306a36Sopenharmony_ci	/* Used to optimize division by constants */
20062306a36Sopenharmony_ci	u64				div_multiplier;
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic u64 hist_fn_call(struct hist_field *hist_field,
20462306a36Sopenharmony_ci			struct tracing_map_elt *elt,
20562306a36Sopenharmony_ci			struct trace_buffer *buffer,
20662306a36Sopenharmony_ci			struct ring_buffer_event *rbe,
20762306a36Sopenharmony_ci			void *event);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic u64 hist_field_const(struct hist_field *field,
21062306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
21162306a36Sopenharmony_ci			   struct trace_buffer *buffer,
21262306a36Sopenharmony_ci			   struct ring_buffer_event *rbe,
21362306a36Sopenharmony_ci			   void *event)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	return field->constant;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic u64 hist_field_counter(struct hist_field *field,
21962306a36Sopenharmony_ci			      struct tracing_map_elt *elt,
22062306a36Sopenharmony_ci			      struct trace_buffer *buffer,
22162306a36Sopenharmony_ci			      struct ring_buffer_event *rbe,
22262306a36Sopenharmony_ci			      void *event)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	return 1;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic u64 hist_field_string(struct hist_field *hist_field,
22862306a36Sopenharmony_ci			     struct tracing_map_elt *elt,
22962306a36Sopenharmony_ci			     struct trace_buffer *buffer,
23062306a36Sopenharmony_ci			     struct ring_buffer_event *rbe,
23162306a36Sopenharmony_ci			     void *event)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	char *addr = (char *)(event + hist_field->field->offset);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return (u64)(unsigned long)addr;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic u64 hist_field_dynstring(struct hist_field *hist_field,
23962306a36Sopenharmony_ci				struct tracing_map_elt *elt,
24062306a36Sopenharmony_ci				struct trace_buffer *buffer,
24162306a36Sopenharmony_ci				struct ring_buffer_event *rbe,
24262306a36Sopenharmony_ci				void *event)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	u32 str_item = *(u32 *)(event + hist_field->field->offset);
24562306a36Sopenharmony_ci	int str_loc = str_item & 0xffff;
24662306a36Sopenharmony_ci	char *addr = (char *)(event + str_loc);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return (u64)(unsigned long)addr;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic u64 hist_field_reldynstring(struct hist_field *hist_field,
25262306a36Sopenharmony_ci				   struct tracing_map_elt *elt,
25362306a36Sopenharmony_ci				   struct trace_buffer *buffer,
25462306a36Sopenharmony_ci				   struct ring_buffer_event *rbe,
25562306a36Sopenharmony_ci				   void *event)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	u32 *item = event + hist_field->field->offset;
25862306a36Sopenharmony_ci	u32 str_item = *item;
25962306a36Sopenharmony_ci	int str_loc = str_item & 0xffff;
26062306a36Sopenharmony_ci	char *addr = (char *)&item[1] + str_loc;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return (u64)(unsigned long)addr;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic u64 hist_field_pstring(struct hist_field *hist_field,
26662306a36Sopenharmony_ci			      struct tracing_map_elt *elt,
26762306a36Sopenharmony_ci			      struct trace_buffer *buffer,
26862306a36Sopenharmony_ci			      struct ring_buffer_event *rbe,
26962306a36Sopenharmony_ci			      void *event)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	char **addr = (char **)(event + hist_field->field->offset);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return (u64)(unsigned long)*addr;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic u64 hist_field_log2(struct hist_field *hist_field,
27762306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
27862306a36Sopenharmony_ci			   struct trace_buffer *buffer,
27962306a36Sopenharmony_ci			   struct ring_buffer_event *rbe,
28062306a36Sopenharmony_ci			   void *event)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct hist_field *operand = hist_field->operands[0];
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	u64 val = hist_fn_call(operand, elt, buffer, rbe, event);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return (u64) ilog2(roundup_pow_of_two(val));
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic u64 hist_field_bucket(struct hist_field *hist_field,
29062306a36Sopenharmony_ci			     struct tracing_map_elt *elt,
29162306a36Sopenharmony_ci			     struct trace_buffer *buffer,
29262306a36Sopenharmony_ci			     struct ring_buffer_event *rbe,
29362306a36Sopenharmony_ci			     void *event)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct hist_field *operand = hist_field->operands[0];
29662306a36Sopenharmony_ci	unsigned long buckets = hist_field->buckets;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	u64 val = hist_fn_call(operand, elt, buffer, rbe, event);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (WARN_ON_ONCE(!buckets))
30162306a36Sopenharmony_ci		return val;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (val >= LONG_MAX)
30462306a36Sopenharmony_ci		val = div64_ul(val, buckets);
30562306a36Sopenharmony_ci	else
30662306a36Sopenharmony_ci		val = (u64)((unsigned long)val / buckets);
30762306a36Sopenharmony_ci	return val * buckets;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic u64 hist_field_plus(struct hist_field *hist_field,
31162306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
31262306a36Sopenharmony_ci			   struct trace_buffer *buffer,
31362306a36Sopenharmony_ci			   struct ring_buffer_event *rbe,
31462306a36Sopenharmony_ci			   void *event)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
31762306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
32062306a36Sopenharmony_ci	u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return val1 + val2;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic u64 hist_field_minus(struct hist_field *hist_field,
32662306a36Sopenharmony_ci			    struct tracing_map_elt *elt,
32762306a36Sopenharmony_ci			    struct trace_buffer *buffer,
32862306a36Sopenharmony_ci			    struct ring_buffer_event *rbe,
32962306a36Sopenharmony_ci			    void *event)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
33262306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
33562306a36Sopenharmony_ci	u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return val1 - val2;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic u64 hist_field_div(struct hist_field *hist_field,
34162306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
34262306a36Sopenharmony_ci			   struct trace_buffer *buffer,
34362306a36Sopenharmony_ci			   struct ring_buffer_event *rbe,
34462306a36Sopenharmony_ci			   void *event)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
34762306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
35062306a36Sopenharmony_ci	u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Return -1 for the undefined case */
35362306a36Sopenharmony_ci	if (!val2)
35462306a36Sopenharmony_ci		return -1;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Use shift if the divisor is a power of 2 */
35762306a36Sopenharmony_ci	if (!(val2 & (val2 - 1)))
35862306a36Sopenharmony_ci		return val1 >> __ffs64(val2);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return div64_u64(val1, val2);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic u64 div_by_power_of_two(struct hist_field *hist_field,
36462306a36Sopenharmony_ci				struct tracing_map_elt *elt,
36562306a36Sopenharmony_ci				struct trace_buffer *buffer,
36662306a36Sopenharmony_ci				struct ring_buffer_event *rbe,
36762306a36Sopenharmony_ci				void *event)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
37062306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return val1 >> __ffs64(operand2->constant);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic u64 div_by_not_power_of_two(struct hist_field *hist_field,
37862306a36Sopenharmony_ci				struct tracing_map_elt *elt,
37962306a36Sopenharmony_ci				struct trace_buffer *buffer,
38062306a36Sopenharmony_ci				struct ring_buffer_event *rbe,
38162306a36Sopenharmony_ci				void *event)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
38462306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return div64_u64(val1, operand2->constant);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic u64 div_by_mult_and_shift(struct hist_field *hist_field,
39262306a36Sopenharmony_ci				struct tracing_map_elt *elt,
39362306a36Sopenharmony_ci				struct trace_buffer *buffer,
39462306a36Sopenharmony_ci				struct ring_buffer_event *rbe,
39562306a36Sopenharmony_ci				void *event)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
39862306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/*
40362306a36Sopenharmony_ci	 * If the divisor is a constant, do a multiplication and shift instead.
40462306a36Sopenharmony_ci	 *
40562306a36Sopenharmony_ci	 * Choose Z = some power of 2. If Y <= Z, then:
40662306a36Sopenharmony_ci	 *     X / Y = (X * (Z / Y)) / Z
40762306a36Sopenharmony_ci	 *
40862306a36Sopenharmony_ci	 * (Z / Y) is a constant (mult) which is calculated at parse time, so:
40962306a36Sopenharmony_ci	 *     X / Y = (X * mult) / Z
41062306a36Sopenharmony_ci	 *
41162306a36Sopenharmony_ci	 * The division by Z can be replaced by a shift since Z is a power of 2:
41262306a36Sopenharmony_ci	 *     X / Y = (X * mult) >> HIST_DIV_SHIFT
41362306a36Sopenharmony_ci	 *
41462306a36Sopenharmony_ci	 * As long, as X < Z the results will not be off by more than 1.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	if (val1 < (1 << HIST_DIV_SHIFT)) {
41762306a36Sopenharmony_ci		u64 mult = operand2->div_multiplier;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		return (val1 * mult + ((1 << HIST_DIV_SHIFT) - 1)) >> HIST_DIV_SHIFT;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return div64_u64(val1, operand2->constant);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic u64 hist_field_mult(struct hist_field *hist_field,
42662306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
42762306a36Sopenharmony_ci			   struct trace_buffer *buffer,
42862306a36Sopenharmony_ci			   struct ring_buffer_event *rbe,
42962306a36Sopenharmony_ci			   void *event)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct hist_field *operand1 = hist_field->operands[0];
43262306a36Sopenharmony_ci	struct hist_field *operand2 = hist_field->operands[1];
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	u64 val1 = hist_fn_call(operand1, elt, buffer, rbe, event);
43562306a36Sopenharmony_ci	u64 val2 = hist_fn_call(operand2, elt, buffer, rbe, event);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return val1 * val2;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic u64 hist_field_unary_minus(struct hist_field *hist_field,
44162306a36Sopenharmony_ci				  struct tracing_map_elt *elt,
44262306a36Sopenharmony_ci				  struct trace_buffer *buffer,
44362306a36Sopenharmony_ci				  struct ring_buffer_event *rbe,
44462306a36Sopenharmony_ci				  void *event)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct hist_field *operand = hist_field->operands[0];
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	s64 sval = (s64)hist_fn_call(operand, elt, buffer, rbe, event);
44962306a36Sopenharmony_ci	u64 val = (u64)-sval;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return val;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci#define DEFINE_HIST_FIELD_FN(type)					\
45562306a36Sopenharmony_ci	static u64 hist_field_##type(struct hist_field *hist_field,	\
45662306a36Sopenharmony_ci				     struct tracing_map_elt *elt,	\
45762306a36Sopenharmony_ci				     struct trace_buffer *buffer,	\
45862306a36Sopenharmony_ci				     struct ring_buffer_event *rbe,	\
45962306a36Sopenharmony_ci				     void *event)			\
46062306a36Sopenharmony_ci{									\
46162306a36Sopenharmony_ci	type *addr = (type *)(event + hist_field->field->offset);	\
46262306a36Sopenharmony_ci									\
46362306a36Sopenharmony_ci	return (u64)(unsigned long)*addr;				\
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(s64);
46762306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(u64);
46862306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(s32);
46962306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(u32);
47062306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(s16);
47162306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(u16);
47262306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(s8);
47362306a36Sopenharmony_ciDEFINE_HIST_FIELD_FN(u8);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci#define for_each_hist_field(i, hist_data)	\
47662306a36Sopenharmony_ci	for ((i) = 0; (i) < (hist_data)->n_fields; (i)++)
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci#define for_each_hist_val_field(i, hist_data)	\
47962306a36Sopenharmony_ci	for ((i) = 0; (i) < (hist_data)->n_vals; (i)++)
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci#define for_each_hist_key_field(i, hist_data)	\
48262306a36Sopenharmony_ci	for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++)
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci#define HITCOUNT_IDX		0
48562306a36Sopenharmony_ci#define HIST_KEY_SIZE_MAX	(MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE)
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cienum hist_field_flags {
48862306a36Sopenharmony_ci	HIST_FIELD_FL_HITCOUNT		= 1 << 0,
48962306a36Sopenharmony_ci	HIST_FIELD_FL_KEY		= 1 << 1,
49062306a36Sopenharmony_ci	HIST_FIELD_FL_STRING		= 1 << 2,
49162306a36Sopenharmony_ci	HIST_FIELD_FL_HEX		= 1 << 3,
49262306a36Sopenharmony_ci	HIST_FIELD_FL_SYM		= 1 << 4,
49362306a36Sopenharmony_ci	HIST_FIELD_FL_SYM_OFFSET	= 1 << 5,
49462306a36Sopenharmony_ci	HIST_FIELD_FL_EXECNAME		= 1 << 6,
49562306a36Sopenharmony_ci	HIST_FIELD_FL_SYSCALL		= 1 << 7,
49662306a36Sopenharmony_ci	HIST_FIELD_FL_STACKTRACE	= 1 << 8,
49762306a36Sopenharmony_ci	HIST_FIELD_FL_LOG2		= 1 << 9,
49862306a36Sopenharmony_ci	HIST_FIELD_FL_TIMESTAMP		= 1 << 10,
49962306a36Sopenharmony_ci	HIST_FIELD_FL_TIMESTAMP_USECS	= 1 << 11,
50062306a36Sopenharmony_ci	HIST_FIELD_FL_VAR		= 1 << 12,
50162306a36Sopenharmony_ci	HIST_FIELD_FL_EXPR		= 1 << 13,
50262306a36Sopenharmony_ci	HIST_FIELD_FL_VAR_REF		= 1 << 14,
50362306a36Sopenharmony_ci	HIST_FIELD_FL_CPU		= 1 << 15,
50462306a36Sopenharmony_ci	HIST_FIELD_FL_ALIAS		= 1 << 16,
50562306a36Sopenharmony_ci	HIST_FIELD_FL_BUCKET		= 1 << 17,
50662306a36Sopenharmony_ci	HIST_FIELD_FL_CONST		= 1 << 18,
50762306a36Sopenharmony_ci	HIST_FIELD_FL_PERCENT		= 1 << 19,
50862306a36Sopenharmony_ci	HIST_FIELD_FL_GRAPH		= 1 << 20,
50962306a36Sopenharmony_ci};
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistruct var_defs {
51262306a36Sopenharmony_ci	unsigned int	n_vars;
51362306a36Sopenharmony_ci	char		*name[TRACING_MAP_VARS_MAX];
51462306a36Sopenharmony_ci	char		*expr[TRACING_MAP_VARS_MAX];
51562306a36Sopenharmony_ci};
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistruct hist_trigger_attrs {
51862306a36Sopenharmony_ci	char		*keys_str;
51962306a36Sopenharmony_ci	char		*vals_str;
52062306a36Sopenharmony_ci	char		*sort_key_str;
52162306a36Sopenharmony_ci	char		*name;
52262306a36Sopenharmony_ci	char		*clock;
52362306a36Sopenharmony_ci	bool		pause;
52462306a36Sopenharmony_ci	bool		cont;
52562306a36Sopenharmony_ci	bool		clear;
52662306a36Sopenharmony_ci	bool		ts_in_usecs;
52762306a36Sopenharmony_ci	bool		no_hitcount;
52862306a36Sopenharmony_ci	unsigned int	map_bits;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	char		*assignment_str[TRACING_MAP_VARS_MAX];
53162306a36Sopenharmony_ci	unsigned int	n_assignments;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	char		*action_str[HIST_ACTIONS_MAX];
53462306a36Sopenharmony_ci	unsigned int	n_actions;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	struct var_defs	var_defs;
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistruct field_var {
54062306a36Sopenharmony_ci	struct hist_field	*var;
54162306a36Sopenharmony_ci	struct hist_field	*val;
54262306a36Sopenharmony_ci};
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistruct field_var_hist {
54562306a36Sopenharmony_ci	struct hist_trigger_data	*hist_data;
54662306a36Sopenharmony_ci	char				*cmd;
54762306a36Sopenharmony_ci};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistruct hist_trigger_data {
55062306a36Sopenharmony_ci	struct hist_field               *fields[HIST_FIELDS_MAX];
55162306a36Sopenharmony_ci	unsigned int			n_vals;
55262306a36Sopenharmony_ci	unsigned int			n_keys;
55362306a36Sopenharmony_ci	unsigned int			n_fields;
55462306a36Sopenharmony_ci	unsigned int			n_vars;
55562306a36Sopenharmony_ci	unsigned int			n_var_str;
55662306a36Sopenharmony_ci	unsigned int			key_size;
55762306a36Sopenharmony_ci	struct tracing_map_sort_key	sort_keys[TRACING_MAP_SORT_KEYS_MAX];
55862306a36Sopenharmony_ci	unsigned int			n_sort_keys;
55962306a36Sopenharmony_ci	struct trace_event_file		*event_file;
56062306a36Sopenharmony_ci	struct hist_trigger_attrs	*attrs;
56162306a36Sopenharmony_ci	struct tracing_map		*map;
56262306a36Sopenharmony_ci	bool				enable_timestamps;
56362306a36Sopenharmony_ci	bool				remove;
56462306a36Sopenharmony_ci	struct hist_field               *var_refs[TRACING_MAP_VARS_MAX];
56562306a36Sopenharmony_ci	unsigned int			n_var_refs;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	struct action_data		*actions[HIST_ACTIONS_MAX];
56862306a36Sopenharmony_ci	unsigned int			n_actions;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	struct field_var		*field_vars[SYNTH_FIELDS_MAX];
57162306a36Sopenharmony_ci	unsigned int			n_field_vars;
57262306a36Sopenharmony_ci	unsigned int			n_field_var_str;
57362306a36Sopenharmony_ci	struct field_var_hist		*field_var_hists[SYNTH_FIELDS_MAX];
57462306a36Sopenharmony_ci	unsigned int			n_field_var_hists;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	struct field_var		*save_vars[SYNTH_FIELDS_MAX];
57762306a36Sopenharmony_ci	unsigned int			n_save_vars;
57862306a36Sopenharmony_ci	unsigned int			n_save_var_str;
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistruct action_data;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_citypedef void (*action_fn_t) (struct hist_trigger_data *hist_data,
58462306a36Sopenharmony_ci			     struct tracing_map_elt *elt,
58562306a36Sopenharmony_ci			     struct trace_buffer *buffer, void *rec,
58662306a36Sopenharmony_ci			     struct ring_buffer_event *rbe, void *key,
58762306a36Sopenharmony_ci			     struct action_data *data, u64 *var_ref_vals);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_citypedef bool (*check_track_val_fn_t) (u64 track_val, u64 var_val);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cienum handler_id {
59262306a36Sopenharmony_ci	HANDLER_ONMATCH = 1,
59362306a36Sopenharmony_ci	HANDLER_ONMAX,
59462306a36Sopenharmony_ci	HANDLER_ONCHANGE,
59562306a36Sopenharmony_ci};
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cienum action_id {
59862306a36Sopenharmony_ci	ACTION_SAVE = 1,
59962306a36Sopenharmony_ci	ACTION_TRACE,
60062306a36Sopenharmony_ci	ACTION_SNAPSHOT,
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistruct action_data {
60462306a36Sopenharmony_ci	enum handler_id		handler;
60562306a36Sopenharmony_ci	enum action_id		action;
60662306a36Sopenharmony_ci	char			*action_name;
60762306a36Sopenharmony_ci	action_fn_t		fn;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	unsigned int		n_params;
61062306a36Sopenharmony_ci	char			*params[SYNTH_FIELDS_MAX];
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/*
61362306a36Sopenharmony_ci	 * When a histogram trigger is hit, the values of any
61462306a36Sopenharmony_ci	 * references to variables, including variables being passed
61562306a36Sopenharmony_ci	 * as parameters to synthetic events, are collected into a
61662306a36Sopenharmony_ci	 * var_ref_vals array.  This var_ref_idx array is an array of
61762306a36Sopenharmony_ci	 * indices into the var_ref_vals array, one for each synthetic
61862306a36Sopenharmony_ci	 * event param, and is passed to the synthetic event
61962306a36Sopenharmony_ci	 * invocation.
62062306a36Sopenharmony_ci	 */
62162306a36Sopenharmony_ci	unsigned int		var_ref_idx[SYNTH_FIELDS_MAX];
62262306a36Sopenharmony_ci	struct synth_event	*synth_event;
62362306a36Sopenharmony_ci	bool			use_trace_keyword;
62462306a36Sopenharmony_ci	char			*synth_event_name;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	union {
62762306a36Sopenharmony_ci		struct {
62862306a36Sopenharmony_ci			char			*event;
62962306a36Sopenharmony_ci			char			*event_system;
63062306a36Sopenharmony_ci		} match_data;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		struct {
63362306a36Sopenharmony_ci			/*
63462306a36Sopenharmony_ci			 * var_str contains the $-unstripped variable
63562306a36Sopenharmony_ci			 * name referenced by var_ref, and used when
63662306a36Sopenharmony_ci			 * printing the action.  Because var_ref
63762306a36Sopenharmony_ci			 * creation is deferred to create_actions(),
63862306a36Sopenharmony_ci			 * we need a per-action way to save it until
63962306a36Sopenharmony_ci			 * then, thus var_str.
64062306a36Sopenharmony_ci			 */
64162306a36Sopenharmony_ci			char			*var_str;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci			/*
64462306a36Sopenharmony_ci			 * var_ref refers to the variable being
64562306a36Sopenharmony_ci			 * tracked e.g onmax($var).
64662306a36Sopenharmony_ci			 */
64762306a36Sopenharmony_ci			struct hist_field	*var_ref;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci			/*
65062306a36Sopenharmony_ci			 * track_var contains the 'invisible' tracking
65162306a36Sopenharmony_ci			 * variable created to keep the current
65262306a36Sopenharmony_ci			 * e.g. max value.
65362306a36Sopenharmony_ci			 */
65462306a36Sopenharmony_ci			struct hist_field	*track_var;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci			check_track_val_fn_t	check_val;
65762306a36Sopenharmony_ci			action_fn_t		save_data;
65862306a36Sopenharmony_ci		} track_data;
65962306a36Sopenharmony_ci	};
66062306a36Sopenharmony_ci};
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistruct track_data {
66362306a36Sopenharmony_ci	u64				track_val;
66462306a36Sopenharmony_ci	bool				updated;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	unsigned int			key_len;
66762306a36Sopenharmony_ci	void				*key;
66862306a36Sopenharmony_ci	struct tracing_map_elt		elt;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	struct action_data		*action_data;
67162306a36Sopenharmony_ci	struct hist_trigger_data	*hist_data;
67262306a36Sopenharmony_ci};
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistruct hist_elt_data {
67562306a36Sopenharmony_ci	char *comm;
67662306a36Sopenharmony_ci	u64 *var_ref_vals;
67762306a36Sopenharmony_ci	char **field_var_str;
67862306a36Sopenharmony_ci	int n_field_var_str;
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistruct snapshot_context {
68262306a36Sopenharmony_ci	struct tracing_map_elt	*elt;
68362306a36Sopenharmony_ci	void			*key;
68462306a36Sopenharmony_ci};
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci/*
68762306a36Sopenharmony_ci * Returns the specific division function to use if the divisor
68862306a36Sopenharmony_ci * is constant. This avoids extra branches when the trigger is hit.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_cistatic enum hist_field_fn hist_field_get_div_fn(struct hist_field *divisor)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	u64 div = divisor->constant;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (!(div & (div - 1)))
69562306a36Sopenharmony_ci		return HIST_FIELD_FN_DIV_POWER2;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* If the divisor is too large, do a regular division */
69862306a36Sopenharmony_ci	if (div > (1 << HIST_DIV_SHIFT))
69962306a36Sopenharmony_ci		return HIST_FIELD_FN_DIV_NOT_POWER2;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	divisor->div_multiplier = div64_u64((u64)(1 << HIST_DIV_SHIFT), div);
70262306a36Sopenharmony_ci	return HIST_FIELD_FN_DIV_MULT_SHIFT;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void track_data_free(struct track_data *track_data)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct hist_elt_data *elt_data;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (!track_data)
71062306a36Sopenharmony_ci		return;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	kfree(track_data->key);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	elt_data = track_data->elt.private_data;
71562306a36Sopenharmony_ci	if (elt_data) {
71662306a36Sopenharmony_ci		kfree(elt_data->comm);
71762306a36Sopenharmony_ci		kfree(elt_data);
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	kfree(track_data);
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic struct track_data *track_data_alloc(unsigned int key_len,
72462306a36Sopenharmony_ci					   struct action_data *action_data,
72562306a36Sopenharmony_ci					   struct hist_trigger_data *hist_data)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct track_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
72862306a36Sopenharmony_ci	struct hist_elt_data *elt_data;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (!data)
73162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	data->key = kzalloc(key_len, GFP_KERNEL);
73462306a36Sopenharmony_ci	if (!data->key) {
73562306a36Sopenharmony_ci		track_data_free(data);
73662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	data->key_len = key_len;
74062306a36Sopenharmony_ci	data->action_data = action_data;
74162306a36Sopenharmony_ci	data->hist_data = hist_data;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL);
74462306a36Sopenharmony_ci	if (!elt_data) {
74562306a36Sopenharmony_ci		track_data_free(data);
74662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	data->elt.private_data = elt_data;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	elt_data->comm = kzalloc(TASK_COMM_LEN, GFP_KERNEL);
75262306a36Sopenharmony_ci	if (!elt_data->comm) {
75362306a36Sopenharmony_ci		track_data_free(data);
75462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return data;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci#define HIST_PREFIX "hist:"
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic char *last_cmd;
76362306a36Sopenharmony_cistatic char last_cmd_loc[MAX_FILTER_STR_VAL];
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic int errpos(char *str)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	if (!str || !last_cmd)
76862306a36Sopenharmony_ci		return 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return err_pos(last_cmd, str);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void last_cmd_set(struct trace_event_file *file, char *str)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	const char *system = NULL, *name = NULL;
77662306a36Sopenharmony_ci	struct trace_event_call *call;
77762306a36Sopenharmony_ci	int len;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (!str)
78062306a36Sopenharmony_ci		return;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* sizeof() contains the nul byte */
78362306a36Sopenharmony_ci	len = sizeof(HIST_PREFIX) + strlen(str);
78462306a36Sopenharmony_ci	kfree(last_cmd);
78562306a36Sopenharmony_ci	last_cmd = kzalloc(len, GFP_KERNEL);
78662306a36Sopenharmony_ci	if (!last_cmd)
78762306a36Sopenharmony_ci		return;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	strcpy(last_cmd, HIST_PREFIX);
79062306a36Sopenharmony_ci	/* Again, sizeof() contains the nul byte */
79162306a36Sopenharmony_ci	len -= sizeof(HIST_PREFIX);
79262306a36Sopenharmony_ci	strncat(last_cmd, str, len);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (file) {
79562306a36Sopenharmony_ci		call = file->event_call;
79662306a36Sopenharmony_ci		system = call->class->system;
79762306a36Sopenharmony_ci		if (system) {
79862306a36Sopenharmony_ci			name = trace_event_name(call);
79962306a36Sopenharmony_ci			if (!name)
80062306a36Sopenharmony_ci				system = NULL;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (system)
80562306a36Sopenharmony_ci		snprintf(last_cmd_loc, MAX_FILTER_STR_VAL, HIST_PREFIX "%s:%s", system, name);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic void hist_err(struct trace_array *tr, u8 err_type, u16 err_pos)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	if (!last_cmd)
81162306a36Sopenharmony_ci		return;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	tracing_log_err(tr, last_cmd_loc, last_cmd, err_text,
81462306a36Sopenharmony_ci			err_type, err_pos);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic void hist_err_clear(void)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	if (last_cmd)
82062306a36Sopenharmony_ci		last_cmd[0] = '\0';
82162306a36Sopenharmony_ci	last_cmd_loc[0] = '\0';
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_citypedef void (*synth_probe_func_t) (void *__data, u64 *var_ref_vals,
82562306a36Sopenharmony_ci				    unsigned int *var_ref_idx);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic inline void trace_synth(struct synth_event *event, u64 *var_ref_vals,
82862306a36Sopenharmony_ci			       unsigned int *var_ref_idx)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct tracepoint *tp = event->tp;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (unlikely(atomic_read(&tp->key.enabled) > 0)) {
83362306a36Sopenharmony_ci		struct tracepoint_func *probe_func_ptr;
83462306a36Sopenharmony_ci		synth_probe_func_t probe_func;
83562306a36Sopenharmony_ci		void *__data;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		if (!(cpu_online(raw_smp_processor_id())))
83862306a36Sopenharmony_ci			return;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		probe_func_ptr = rcu_dereference_sched((tp)->funcs);
84162306a36Sopenharmony_ci		if (probe_func_ptr) {
84262306a36Sopenharmony_ci			do {
84362306a36Sopenharmony_ci				probe_func = probe_func_ptr->func;
84462306a36Sopenharmony_ci				__data = probe_func_ptr->data;
84562306a36Sopenharmony_ci				probe_func(__data, var_ref_vals, var_ref_idx);
84662306a36Sopenharmony_ci			} while ((++probe_func_ptr)->func);
84762306a36Sopenharmony_ci		}
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic void action_trace(struct hist_trigger_data *hist_data,
85262306a36Sopenharmony_ci			 struct tracing_map_elt *elt,
85362306a36Sopenharmony_ci			 struct trace_buffer *buffer, void *rec,
85462306a36Sopenharmony_ci			 struct ring_buffer_event *rbe, void *key,
85562306a36Sopenharmony_ci			 struct action_data *data, u64 *var_ref_vals)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	struct synth_event *event = data->synth_event;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	trace_synth(event, var_ref_vals, data->var_ref_idx);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistruct hist_var_data {
86362306a36Sopenharmony_ci	struct list_head list;
86462306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic u64 hist_field_timestamp(struct hist_field *hist_field,
86862306a36Sopenharmony_ci				struct tracing_map_elt *elt,
86962306a36Sopenharmony_ci				struct trace_buffer *buffer,
87062306a36Sopenharmony_ci				struct ring_buffer_event *rbe,
87162306a36Sopenharmony_ci				void *event)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = hist_field->hist_data;
87462306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	u64 ts = ring_buffer_event_time_stamp(buffer, rbe);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (hist_data->attrs->ts_in_usecs && trace_clock_in_ns(tr))
87962306a36Sopenharmony_ci		ts = ns2usecs(ts);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	return ts;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic u64 hist_field_cpu(struct hist_field *hist_field,
88562306a36Sopenharmony_ci			  struct tracing_map_elt *elt,
88662306a36Sopenharmony_ci			  struct trace_buffer *buffer,
88762306a36Sopenharmony_ci			  struct ring_buffer_event *rbe,
88862306a36Sopenharmony_ci			  void *event)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	int cpu = smp_processor_id();
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	return cpu;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/**
89662306a36Sopenharmony_ci * check_field_for_var_ref - Check if a VAR_REF field references a variable
89762306a36Sopenharmony_ci * @hist_field: The VAR_REF field to check
89862306a36Sopenharmony_ci * @var_data: The hist trigger that owns the variable
89962306a36Sopenharmony_ci * @var_idx: The trigger variable identifier
90062306a36Sopenharmony_ci *
90162306a36Sopenharmony_ci * Check the given VAR_REF field to see whether or not it references
90262306a36Sopenharmony_ci * the given variable associated with the given trigger.
90362306a36Sopenharmony_ci *
90462306a36Sopenharmony_ci * Return: The VAR_REF field if it does reference the variable, NULL if not
90562306a36Sopenharmony_ci */
90662306a36Sopenharmony_cistatic struct hist_field *
90762306a36Sopenharmony_cicheck_field_for_var_ref(struct hist_field *hist_field,
90862306a36Sopenharmony_ci			struct hist_trigger_data *var_data,
90962306a36Sopenharmony_ci			unsigned int var_idx)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	WARN_ON(!(hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF));
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (hist_field && hist_field->var.idx == var_idx &&
91462306a36Sopenharmony_ci	    hist_field->var.hist_data == var_data)
91562306a36Sopenharmony_ci		return hist_field;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	return NULL;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci/**
92162306a36Sopenharmony_ci * find_var_ref - Check if a trigger has a reference to a trigger variable
92262306a36Sopenharmony_ci * @hist_data: The hist trigger that might have a reference to the variable
92362306a36Sopenharmony_ci * @var_data: The hist trigger that owns the variable
92462306a36Sopenharmony_ci * @var_idx: The trigger variable identifier
92562306a36Sopenharmony_ci *
92662306a36Sopenharmony_ci * Check the list of var_refs[] on the first hist trigger to see
92762306a36Sopenharmony_ci * whether any of them are references to the variable on the second
92862306a36Sopenharmony_ci * trigger.
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * Return: The VAR_REF field referencing the variable if so, NULL if not
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_cistatic struct hist_field *find_var_ref(struct hist_trigger_data *hist_data,
93362306a36Sopenharmony_ci				       struct hist_trigger_data *var_data,
93462306a36Sopenharmony_ci				       unsigned int var_idx)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct hist_field *hist_field;
93762306a36Sopenharmony_ci	unsigned int i;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_var_refs; i++) {
94062306a36Sopenharmony_ci		hist_field = hist_data->var_refs[i];
94162306a36Sopenharmony_ci		if (check_field_for_var_ref(hist_field, var_data, var_idx))
94262306a36Sopenharmony_ci			return hist_field;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return NULL;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci/**
94962306a36Sopenharmony_ci * find_any_var_ref - Check if there is a reference to a given trigger variable
95062306a36Sopenharmony_ci * @hist_data: The hist trigger
95162306a36Sopenharmony_ci * @var_idx: The trigger variable identifier
95262306a36Sopenharmony_ci *
95362306a36Sopenharmony_ci * Check to see whether the given variable is currently referenced by
95462306a36Sopenharmony_ci * any other trigger.
95562306a36Sopenharmony_ci *
95662306a36Sopenharmony_ci * The trigger the variable is defined on is explicitly excluded - the
95762306a36Sopenharmony_ci * assumption being that a self-reference doesn't prevent a trigger
95862306a36Sopenharmony_ci * from being removed.
95962306a36Sopenharmony_ci *
96062306a36Sopenharmony_ci * Return: The VAR_REF field referencing the variable if so, NULL if not
96162306a36Sopenharmony_ci */
96262306a36Sopenharmony_cistatic struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data,
96362306a36Sopenharmony_ci					   unsigned int var_idx)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
96662306a36Sopenharmony_ci	struct hist_field *found = NULL;
96762306a36Sopenharmony_ci	struct hist_var_data *var_data;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	list_for_each_entry(var_data, &tr->hist_vars, list) {
97062306a36Sopenharmony_ci		if (var_data->hist_data == hist_data)
97162306a36Sopenharmony_ci			continue;
97262306a36Sopenharmony_ci		found = find_var_ref(var_data->hist_data, hist_data, var_idx);
97362306a36Sopenharmony_ci		if (found)
97462306a36Sopenharmony_ci			break;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return found;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/**
98162306a36Sopenharmony_ci * check_var_refs - Check if there is a reference to any of trigger's variables
98262306a36Sopenharmony_ci * @hist_data: The hist trigger
98362306a36Sopenharmony_ci *
98462306a36Sopenharmony_ci * A trigger can define one or more variables.  If any one of them is
98562306a36Sopenharmony_ci * currently referenced by any other trigger, this function will
98662306a36Sopenharmony_ci * determine that.
98762306a36Sopenharmony_ci *
98862306a36Sopenharmony_ci * Typically used to determine whether or not a trigger can be removed
98962306a36Sopenharmony_ci * - if there are any references to a trigger's variables, it cannot.
99062306a36Sopenharmony_ci *
99162306a36Sopenharmony_ci * Return: True if there is a reference to any of trigger's variables
99262306a36Sopenharmony_ci */
99362306a36Sopenharmony_cistatic bool check_var_refs(struct hist_trigger_data *hist_data)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct hist_field *field;
99662306a36Sopenharmony_ci	bool found = false;
99762306a36Sopenharmony_ci	int i;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	for_each_hist_field(i, hist_data) {
100062306a36Sopenharmony_ci		field = hist_data->fields[i];
100162306a36Sopenharmony_ci		if (field && field->flags & HIST_FIELD_FL_VAR) {
100262306a36Sopenharmony_ci			if (find_any_var_ref(hist_data, field->var.idx)) {
100362306a36Sopenharmony_ci				found = true;
100462306a36Sopenharmony_ci				break;
100562306a36Sopenharmony_ci			}
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return found;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
101562306a36Sopenharmony_ci	struct hist_var_data *var_data, *found = NULL;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	list_for_each_entry(var_data, &tr->hist_vars, list) {
101862306a36Sopenharmony_ci		if (var_data->hist_data == hist_data) {
101962306a36Sopenharmony_ci			found = var_data;
102062306a36Sopenharmony_ci			break;
102162306a36Sopenharmony_ci		}
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return found;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic bool field_has_hist_vars(struct hist_field *hist_field,
102862306a36Sopenharmony_ci				unsigned int level)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	int i;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (level > 3)
103362306a36Sopenharmony_ci		return false;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (!hist_field)
103662306a36Sopenharmony_ci		return false;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (hist_field->flags & HIST_FIELD_FL_VAR ||
103962306a36Sopenharmony_ci	    hist_field->flags & HIST_FIELD_FL_VAR_REF)
104062306a36Sopenharmony_ci		return true;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) {
104362306a36Sopenharmony_ci		struct hist_field *operand;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		operand = hist_field->operands[i];
104662306a36Sopenharmony_ci		if (field_has_hist_vars(operand, level + 1))
104762306a36Sopenharmony_ci			return true;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return false;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic bool has_hist_vars(struct hist_trigger_data *hist_data)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct hist_field *hist_field;
105662306a36Sopenharmony_ci	int i;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	for_each_hist_field(i, hist_data) {
105962306a36Sopenharmony_ci		hist_field = hist_data->fields[i];
106062306a36Sopenharmony_ci		if (field_has_hist_vars(hist_field, 0))
106162306a36Sopenharmony_ci			return true;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	return false;
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic int save_hist_vars(struct hist_trigger_data *hist_data)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
107062306a36Sopenharmony_ci	struct hist_var_data *var_data;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	var_data = find_hist_vars(hist_data);
107362306a36Sopenharmony_ci	if (var_data)
107462306a36Sopenharmony_ci		return 0;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (tracing_check_open_get_tr(tr))
107762306a36Sopenharmony_ci		return -ENODEV;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	var_data = kzalloc(sizeof(*var_data), GFP_KERNEL);
108062306a36Sopenharmony_ci	if (!var_data) {
108162306a36Sopenharmony_ci		trace_array_put(tr);
108262306a36Sopenharmony_ci		return -ENOMEM;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	var_data->hist_data = hist_data;
108662306a36Sopenharmony_ci	list_add(&var_data->list, &tr->hist_vars);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return 0;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic void remove_hist_vars(struct hist_trigger_data *hist_data)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
109462306a36Sopenharmony_ci	struct hist_var_data *var_data;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	var_data = find_hist_vars(hist_data);
109762306a36Sopenharmony_ci	if (!var_data)
109862306a36Sopenharmony_ci		return;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	if (WARN_ON(check_var_refs(hist_data)))
110162306a36Sopenharmony_ci		return;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	list_del(&var_data->list);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	kfree(var_data);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	trace_array_put(tr);
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
111162306a36Sopenharmony_ci					 const char *var_name)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct hist_field *hist_field, *found = NULL;
111462306a36Sopenharmony_ci	int i;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	for_each_hist_field(i, hist_data) {
111762306a36Sopenharmony_ci		hist_field = hist_data->fields[i];
111862306a36Sopenharmony_ci		if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR &&
111962306a36Sopenharmony_ci		    strcmp(hist_field->var.name, var_name) == 0) {
112062306a36Sopenharmony_ci			found = hist_field;
112162306a36Sopenharmony_ci			break;
112262306a36Sopenharmony_ci		}
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	return found;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic struct hist_field *find_var(struct hist_trigger_data *hist_data,
112962306a36Sopenharmony_ci				   struct trace_event_file *file,
113062306a36Sopenharmony_ci				   const char *var_name)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct hist_trigger_data *test_data;
113362306a36Sopenharmony_ci	struct event_trigger_data *test;
113462306a36Sopenharmony_ci	struct hist_field *hist_field;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	hist_field = find_var_field(hist_data, var_name);
113962306a36Sopenharmony_ci	if (hist_field)
114062306a36Sopenharmony_ci		return hist_field;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
114362306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
114462306a36Sopenharmony_ci			test_data = test->private_data;
114562306a36Sopenharmony_ci			hist_field = find_var_field(test_data, var_name);
114662306a36Sopenharmony_ci			if (hist_field)
114762306a36Sopenharmony_ci				return hist_field;
114862306a36Sopenharmony_ci		}
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return NULL;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic struct trace_event_file *find_var_file(struct trace_array *tr,
115562306a36Sopenharmony_ci					      char *system,
115662306a36Sopenharmony_ci					      char *event_name,
115762306a36Sopenharmony_ci					      char *var_name)
115862306a36Sopenharmony_ci{
115962306a36Sopenharmony_ci	struct hist_trigger_data *var_hist_data;
116062306a36Sopenharmony_ci	struct hist_var_data *var_data;
116162306a36Sopenharmony_ci	struct trace_event_file *file, *found = NULL;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (system)
116462306a36Sopenharmony_ci		return find_event_file(tr, system, event_name);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	list_for_each_entry(var_data, &tr->hist_vars, list) {
116762306a36Sopenharmony_ci		var_hist_data = var_data->hist_data;
116862306a36Sopenharmony_ci		file = var_hist_data->event_file;
116962306a36Sopenharmony_ci		if (file == found)
117062306a36Sopenharmony_ci			continue;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		if (find_var_field(var_hist_data, var_name)) {
117362306a36Sopenharmony_ci			if (found) {
117462306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_VAR_NOT_UNIQUE, errpos(var_name));
117562306a36Sopenharmony_ci				return NULL;
117662306a36Sopenharmony_ci			}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci			found = file;
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	return found;
118362306a36Sopenharmony_ci}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cistatic struct hist_field *find_file_var(struct trace_event_file *file,
118662306a36Sopenharmony_ci					const char *var_name)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct hist_trigger_data *test_data;
118962306a36Sopenharmony_ci	struct event_trigger_data *test;
119062306a36Sopenharmony_ci	struct hist_field *hist_field;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
119562306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
119662306a36Sopenharmony_ci			test_data = test->private_data;
119762306a36Sopenharmony_ci			hist_field = find_var_field(test_data, var_name);
119862306a36Sopenharmony_ci			if (hist_field)
119962306a36Sopenharmony_ci				return hist_field;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	return NULL;
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_cistatic struct hist_field *
120762306a36Sopenharmony_cifind_match_var(struct hist_trigger_data *hist_data, char *var_name)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
121062306a36Sopenharmony_ci	struct hist_field *hist_field, *found = NULL;
121162306a36Sopenharmony_ci	struct trace_event_file *file;
121262306a36Sopenharmony_ci	unsigned int i;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
121562306a36Sopenharmony_ci		struct action_data *data = hist_data->actions[i];
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		if (data->handler == HANDLER_ONMATCH) {
121862306a36Sopenharmony_ci			char *system = data->match_data.event_system;
121962306a36Sopenharmony_ci			char *event_name = data->match_data.event;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci			file = find_var_file(tr, system, event_name, var_name);
122262306a36Sopenharmony_ci			if (!file)
122362306a36Sopenharmony_ci				continue;
122462306a36Sopenharmony_ci			hist_field = find_file_var(file, var_name);
122562306a36Sopenharmony_ci			if (hist_field) {
122662306a36Sopenharmony_ci				if (found) {
122762306a36Sopenharmony_ci					hist_err(tr, HIST_ERR_VAR_NOT_UNIQUE,
122862306a36Sopenharmony_ci						 errpos(var_name));
122962306a36Sopenharmony_ci					return ERR_PTR(-EINVAL);
123062306a36Sopenharmony_ci				}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci				found = hist_field;
123362306a36Sopenharmony_ci			}
123462306a36Sopenharmony_ci		}
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci	return found;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic struct hist_field *find_event_var(struct hist_trigger_data *hist_data,
124062306a36Sopenharmony_ci					 char *system,
124162306a36Sopenharmony_ci					 char *event_name,
124262306a36Sopenharmony_ci					 char *var_name)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
124562306a36Sopenharmony_ci	struct hist_field *hist_field = NULL;
124662306a36Sopenharmony_ci	struct trace_event_file *file;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!system || !event_name) {
124962306a36Sopenharmony_ci		hist_field = find_match_var(hist_data, var_name);
125062306a36Sopenharmony_ci		if (IS_ERR(hist_field))
125162306a36Sopenharmony_ci			return NULL;
125262306a36Sopenharmony_ci		if (hist_field)
125362306a36Sopenharmony_ci			return hist_field;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	file = find_var_file(tr, system, event_name, var_name);
125762306a36Sopenharmony_ci	if (!file)
125862306a36Sopenharmony_ci		return NULL;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	hist_field = find_file_var(file, var_name);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return hist_field;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic u64 hist_field_var_ref(struct hist_field *hist_field,
126662306a36Sopenharmony_ci			      struct tracing_map_elt *elt,
126762306a36Sopenharmony_ci			      struct trace_buffer *buffer,
126862306a36Sopenharmony_ci			      struct ring_buffer_event *rbe,
126962306a36Sopenharmony_ci			      void *event)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct hist_elt_data *elt_data;
127262306a36Sopenharmony_ci	u64 var_val = 0;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (WARN_ON_ONCE(!elt))
127562306a36Sopenharmony_ci		return var_val;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	elt_data = elt->private_data;
127862306a36Sopenharmony_ci	var_val = elt_data->var_ref_vals[hist_field->var_ref_idx];
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return var_val;
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key,
128462306a36Sopenharmony_ci			     u64 *var_ref_vals, bool self)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	struct hist_trigger_data *var_data;
128762306a36Sopenharmony_ci	struct tracing_map_elt *var_elt;
128862306a36Sopenharmony_ci	struct hist_field *hist_field;
128962306a36Sopenharmony_ci	unsigned int i, var_idx;
129062306a36Sopenharmony_ci	bool resolved = true;
129162306a36Sopenharmony_ci	u64 var_val = 0;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_var_refs; i++) {
129462306a36Sopenharmony_ci		hist_field = hist_data->var_refs[i];
129562306a36Sopenharmony_ci		var_idx = hist_field->var.idx;
129662306a36Sopenharmony_ci		var_data = hist_field->var.hist_data;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		if (var_data == NULL) {
129962306a36Sopenharmony_ci			resolved = false;
130062306a36Sopenharmony_ci			break;
130162306a36Sopenharmony_ci		}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		if ((self && var_data != hist_data) ||
130462306a36Sopenharmony_ci		    (!self && var_data == hist_data))
130562306a36Sopenharmony_ci			continue;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		var_elt = tracing_map_lookup(var_data->map, key);
130862306a36Sopenharmony_ci		if (!var_elt) {
130962306a36Sopenharmony_ci			resolved = false;
131062306a36Sopenharmony_ci			break;
131162306a36Sopenharmony_ci		}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		if (!tracing_map_var_set(var_elt, var_idx)) {
131462306a36Sopenharmony_ci			resolved = false;
131562306a36Sopenharmony_ci			break;
131662306a36Sopenharmony_ci		}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		if (self || !hist_field->read_once)
131962306a36Sopenharmony_ci			var_val = tracing_map_read_var(var_elt, var_idx);
132062306a36Sopenharmony_ci		else
132162306a36Sopenharmony_ci			var_val = tracing_map_read_var_once(var_elt, var_idx);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		var_ref_vals[i] = var_val;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	return resolved;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic const char *hist_field_name(struct hist_field *field,
133062306a36Sopenharmony_ci				   unsigned int level)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	const char *field_name = "";
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	if (WARN_ON_ONCE(!field))
133562306a36Sopenharmony_ci		return field_name;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (level > 1)
133862306a36Sopenharmony_ci		return field_name;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	if (field->field)
134162306a36Sopenharmony_ci		field_name = field->field->name;
134262306a36Sopenharmony_ci	else if (field->flags & HIST_FIELD_FL_LOG2 ||
134362306a36Sopenharmony_ci		 field->flags & HIST_FIELD_FL_ALIAS ||
134462306a36Sopenharmony_ci		 field->flags & HIST_FIELD_FL_BUCKET)
134562306a36Sopenharmony_ci		field_name = hist_field_name(field->operands[0], ++level);
134662306a36Sopenharmony_ci	else if (field->flags & HIST_FIELD_FL_CPU)
134762306a36Sopenharmony_ci		field_name = "common_cpu";
134862306a36Sopenharmony_ci	else if (field->flags & HIST_FIELD_FL_EXPR ||
134962306a36Sopenharmony_ci		 field->flags & HIST_FIELD_FL_VAR_REF) {
135062306a36Sopenharmony_ci		if (field->system) {
135162306a36Sopenharmony_ci			static char full_name[MAX_FILTER_STR_VAL];
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci			strcat(full_name, field->system);
135462306a36Sopenharmony_ci			strcat(full_name, ".");
135562306a36Sopenharmony_ci			strcat(full_name, field->event_name);
135662306a36Sopenharmony_ci			strcat(full_name, ".");
135762306a36Sopenharmony_ci			strcat(full_name, field->name);
135862306a36Sopenharmony_ci			field_name = full_name;
135962306a36Sopenharmony_ci		} else
136062306a36Sopenharmony_ci			field_name = field->name;
136162306a36Sopenharmony_ci	} else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
136262306a36Sopenharmony_ci		field_name = "common_timestamp";
136362306a36Sopenharmony_ci	else if (field->flags & HIST_FIELD_FL_STACKTRACE) {
136462306a36Sopenharmony_ci		if (field->field)
136562306a36Sopenharmony_ci			field_name = field->field->name;
136662306a36Sopenharmony_ci		else
136762306a36Sopenharmony_ci			field_name = "common_stacktrace";
136862306a36Sopenharmony_ci	} else if (field->flags & HIST_FIELD_FL_HITCOUNT)
136962306a36Sopenharmony_ci		field_name = "hitcount";
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	if (field_name == NULL)
137262306a36Sopenharmony_ci		field_name = "";
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	return field_name;
137562306a36Sopenharmony_ci}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_cistatic enum hist_field_fn select_value_fn(int field_size, int field_is_signed)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	switch (field_size) {
138062306a36Sopenharmony_ci	case 8:
138162306a36Sopenharmony_ci		if (field_is_signed)
138262306a36Sopenharmony_ci			return HIST_FIELD_FN_S64;
138362306a36Sopenharmony_ci		else
138462306a36Sopenharmony_ci			return HIST_FIELD_FN_U64;
138562306a36Sopenharmony_ci	case 4:
138662306a36Sopenharmony_ci		if (field_is_signed)
138762306a36Sopenharmony_ci			return HIST_FIELD_FN_S32;
138862306a36Sopenharmony_ci		else
138962306a36Sopenharmony_ci			return HIST_FIELD_FN_U32;
139062306a36Sopenharmony_ci	case 2:
139162306a36Sopenharmony_ci		if (field_is_signed)
139262306a36Sopenharmony_ci			return HIST_FIELD_FN_S16;
139362306a36Sopenharmony_ci		else
139462306a36Sopenharmony_ci			return HIST_FIELD_FN_U16;
139562306a36Sopenharmony_ci	case 1:
139662306a36Sopenharmony_ci		if (field_is_signed)
139762306a36Sopenharmony_ci			return HIST_FIELD_FN_S8;
139862306a36Sopenharmony_ci		else
139962306a36Sopenharmony_ci			return HIST_FIELD_FN_U8;
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	return HIST_FIELD_FN_NOP;
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic int parse_map_size(char *str)
140662306a36Sopenharmony_ci{
140762306a36Sopenharmony_ci	unsigned long size, map_bits;
140862306a36Sopenharmony_ci	int ret;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	ret = kstrtoul(str, 0, &size);
141162306a36Sopenharmony_ci	if (ret)
141262306a36Sopenharmony_ci		goto out;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	map_bits = ilog2(roundup_pow_of_two(size));
141562306a36Sopenharmony_ci	if (map_bits < TRACING_MAP_BITS_MIN ||
141662306a36Sopenharmony_ci	    map_bits > TRACING_MAP_BITS_MAX)
141762306a36Sopenharmony_ci		ret = -EINVAL;
141862306a36Sopenharmony_ci	else
141962306a36Sopenharmony_ci		ret = map_bits;
142062306a36Sopenharmony_ci out:
142162306a36Sopenharmony_ci	return ret;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	unsigned int i;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	if (!attrs)
142962306a36Sopenharmony_ci		return;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	for (i = 0; i < attrs->n_assignments; i++)
143262306a36Sopenharmony_ci		kfree(attrs->assignment_str[i]);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	for (i = 0; i < attrs->n_actions; i++)
143562306a36Sopenharmony_ci		kfree(attrs->action_str[i]);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	kfree(attrs->name);
143862306a36Sopenharmony_ci	kfree(attrs->sort_key_str);
143962306a36Sopenharmony_ci	kfree(attrs->keys_str);
144062306a36Sopenharmony_ci	kfree(attrs->vals_str);
144162306a36Sopenharmony_ci	kfree(attrs->clock);
144262306a36Sopenharmony_ci	kfree(attrs);
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_cistatic int parse_action(char *str, struct hist_trigger_attrs *attrs)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	int ret = -EINVAL;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (attrs->n_actions >= HIST_ACTIONS_MAX)
145062306a36Sopenharmony_ci		return ret;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if ((str_has_prefix(str, "onmatch(")) ||
145362306a36Sopenharmony_ci	    (str_has_prefix(str, "onmax(")) ||
145462306a36Sopenharmony_ci	    (str_has_prefix(str, "onchange("))) {
145562306a36Sopenharmony_ci		attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL);
145662306a36Sopenharmony_ci		if (!attrs->action_str[attrs->n_actions]) {
145762306a36Sopenharmony_ci			ret = -ENOMEM;
145862306a36Sopenharmony_ci			return ret;
145962306a36Sopenharmony_ci		}
146062306a36Sopenharmony_ci		attrs->n_actions++;
146162306a36Sopenharmony_ci		ret = 0;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci	return ret;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic int parse_assignment(struct trace_array *tr,
146762306a36Sopenharmony_ci			    char *str, struct hist_trigger_attrs *attrs)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	int len, ret = 0;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if ((len = str_has_prefix(str, "key=")) ||
147262306a36Sopenharmony_ci	    (len = str_has_prefix(str, "keys="))) {
147362306a36Sopenharmony_ci		attrs->keys_str = kstrdup(str + len, GFP_KERNEL);
147462306a36Sopenharmony_ci		if (!attrs->keys_str) {
147562306a36Sopenharmony_ci			ret = -ENOMEM;
147662306a36Sopenharmony_ci			goto out;
147762306a36Sopenharmony_ci		}
147862306a36Sopenharmony_ci	} else if ((len = str_has_prefix(str, "val=")) ||
147962306a36Sopenharmony_ci		   (len = str_has_prefix(str, "vals=")) ||
148062306a36Sopenharmony_ci		   (len = str_has_prefix(str, "values="))) {
148162306a36Sopenharmony_ci		attrs->vals_str = kstrdup(str + len, GFP_KERNEL);
148262306a36Sopenharmony_ci		if (!attrs->vals_str) {
148362306a36Sopenharmony_ci			ret = -ENOMEM;
148462306a36Sopenharmony_ci			goto out;
148562306a36Sopenharmony_ci		}
148662306a36Sopenharmony_ci	} else if ((len = str_has_prefix(str, "sort="))) {
148762306a36Sopenharmony_ci		attrs->sort_key_str = kstrdup(str + len, GFP_KERNEL);
148862306a36Sopenharmony_ci		if (!attrs->sort_key_str) {
148962306a36Sopenharmony_ci			ret = -ENOMEM;
149062306a36Sopenharmony_ci			goto out;
149162306a36Sopenharmony_ci		}
149262306a36Sopenharmony_ci	} else if (str_has_prefix(str, "name=")) {
149362306a36Sopenharmony_ci		attrs->name = kstrdup(str, GFP_KERNEL);
149462306a36Sopenharmony_ci		if (!attrs->name) {
149562306a36Sopenharmony_ci			ret = -ENOMEM;
149662306a36Sopenharmony_ci			goto out;
149762306a36Sopenharmony_ci		}
149862306a36Sopenharmony_ci	} else if ((len = str_has_prefix(str, "clock="))) {
149962306a36Sopenharmony_ci		str += len;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci		str = strstrip(str);
150262306a36Sopenharmony_ci		attrs->clock = kstrdup(str, GFP_KERNEL);
150362306a36Sopenharmony_ci		if (!attrs->clock) {
150462306a36Sopenharmony_ci			ret = -ENOMEM;
150562306a36Sopenharmony_ci			goto out;
150662306a36Sopenharmony_ci		}
150762306a36Sopenharmony_ci	} else if ((len = str_has_prefix(str, "size="))) {
150862306a36Sopenharmony_ci		int map_bits = parse_map_size(str + len);
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci		if (map_bits < 0) {
151162306a36Sopenharmony_ci			ret = map_bits;
151262306a36Sopenharmony_ci			goto out;
151362306a36Sopenharmony_ci		}
151462306a36Sopenharmony_ci		attrs->map_bits = map_bits;
151562306a36Sopenharmony_ci	} else {
151662306a36Sopenharmony_ci		char *assignment;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		if (attrs->n_assignments == TRACING_MAP_VARS_MAX) {
151962306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_TOO_MANY_VARS, errpos(str));
152062306a36Sopenharmony_ci			ret = -EINVAL;
152162306a36Sopenharmony_ci			goto out;
152262306a36Sopenharmony_ci		}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci		assignment = kstrdup(str, GFP_KERNEL);
152562306a36Sopenharmony_ci		if (!assignment) {
152662306a36Sopenharmony_ci			ret = -ENOMEM;
152762306a36Sopenharmony_ci			goto out;
152862306a36Sopenharmony_ci		}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		attrs->assignment_str[attrs->n_assignments++] = assignment;
153162306a36Sopenharmony_ci	}
153262306a36Sopenharmony_ci out:
153362306a36Sopenharmony_ci	return ret;
153462306a36Sopenharmony_ci}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_cistatic struct hist_trigger_attrs *
153762306a36Sopenharmony_ciparse_hist_trigger_attrs(struct trace_array *tr, char *trigger_str)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	struct hist_trigger_attrs *attrs;
154062306a36Sopenharmony_ci	int ret = 0;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
154362306a36Sopenharmony_ci	if (!attrs)
154462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	while (trigger_str) {
154762306a36Sopenharmony_ci		char *str = strsep(&trigger_str, ":");
154862306a36Sopenharmony_ci		char *rhs;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		rhs = strchr(str, '=');
155162306a36Sopenharmony_ci		if (rhs) {
155262306a36Sopenharmony_ci			if (!strlen(++rhs)) {
155362306a36Sopenharmony_ci				ret = -EINVAL;
155462306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_EMPTY_ASSIGNMENT, errpos(str));
155562306a36Sopenharmony_ci				goto free;
155662306a36Sopenharmony_ci			}
155762306a36Sopenharmony_ci			ret = parse_assignment(tr, str, attrs);
155862306a36Sopenharmony_ci			if (ret)
155962306a36Sopenharmony_ci				goto free;
156062306a36Sopenharmony_ci		} else if (strcmp(str, "nohitcount") == 0 ||
156162306a36Sopenharmony_ci			   strcmp(str, "NOHC") == 0)
156262306a36Sopenharmony_ci			attrs->no_hitcount = true;
156362306a36Sopenharmony_ci		else if (strcmp(str, "pause") == 0)
156462306a36Sopenharmony_ci			attrs->pause = true;
156562306a36Sopenharmony_ci		else if ((strcmp(str, "cont") == 0) ||
156662306a36Sopenharmony_ci			 (strcmp(str, "continue") == 0))
156762306a36Sopenharmony_ci			attrs->cont = true;
156862306a36Sopenharmony_ci		else if (strcmp(str, "clear") == 0)
156962306a36Sopenharmony_ci			attrs->clear = true;
157062306a36Sopenharmony_ci		else {
157162306a36Sopenharmony_ci			ret = parse_action(str, attrs);
157262306a36Sopenharmony_ci			if (ret)
157362306a36Sopenharmony_ci				goto free;
157462306a36Sopenharmony_ci		}
157562306a36Sopenharmony_ci	}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	if (!attrs->keys_str) {
157862306a36Sopenharmony_ci		ret = -EINVAL;
157962306a36Sopenharmony_ci		goto free;
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	if (!attrs->clock) {
158362306a36Sopenharmony_ci		attrs->clock = kstrdup("global", GFP_KERNEL);
158462306a36Sopenharmony_ci		if (!attrs->clock) {
158562306a36Sopenharmony_ci			ret = -ENOMEM;
158662306a36Sopenharmony_ci			goto free;
158762306a36Sopenharmony_ci		}
158862306a36Sopenharmony_ci	}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	return attrs;
159162306a36Sopenharmony_ci free:
159262306a36Sopenharmony_ci	destroy_hist_trigger_attrs(attrs);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	return ERR_PTR(ret);
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic inline void save_comm(char *comm, struct task_struct *task)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	if (!task->pid) {
160062306a36Sopenharmony_ci		strcpy(comm, "<idle>");
160162306a36Sopenharmony_ci		return;
160262306a36Sopenharmony_ci	}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (WARN_ON_ONCE(task->pid < 0)) {
160562306a36Sopenharmony_ci		strcpy(comm, "<XXX>");
160662306a36Sopenharmony_ci		return;
160762306a36Sopenharmony_ci	}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	strncpy(comm, task->comm, TASK_COMM_LEN);
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_cistatic void hist_elt_data_free(struct hist_elt_data *elt_data)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	unsigned int i;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	for (i = 0; i < elt_data->n_field_var_str; i++)
161762306a36Sopenharmony_ci		kfree(elt_data->field_var_str[i]);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	kfree(elt_data->field_var_str);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	kfree(elt_data->comm);
162262306a36Sopenharmony_ci	kfree(elt_data);
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic void hist_trigger_elt_data_free(struct tracing_map_elt *elt)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	struct hist_elt_data *elt_data = elt->private_data;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	hist_elt_data_free(elt_data);
163062306a36Sopenharmony_ci}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_cistatic int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = elt->map->private_data;
163562306a36Sopenharmony_ci	unsigned int size = TASK_COMM_LEN;
163662306a36Sopenharmony_ci	struct hist_elt_data *elt_data;
163762306a36Sopenharmony_ci	struct hist_field *hist_field;
163862306a36Sopenharmony_ci	unsigned int i, n_str;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL);
164162306a36Sopenharmony_ci	if (!elt_data)
164262306a36Sopenharmony_ci		return -ENOMEM;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	for_each_hist_field(i, hist_data) {
164562306a36Sopenharmony_ci		hist_field = hist_data->fields[i];
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci		if (hist_field->flags & HIST_FIELD_FL_EXECNAME) {
164862306a36Sopenharmony_ci			elt_data->comm = kzalloc(size, GFP_KERNEL);
164962306a36Sopenharmony_ci			if (!elt_data->comm) {
165062306a36Sopenharmony_ci				kfree(elt_data);
165162306a36Sopenharmony_ci				return -ENOMEM;
165262306a36Sopenharmony_ci			}
165362306a36Sopenharmony_ci			break;
165462306a36Sopenharmony_ci		}
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	n_str = hist_data->n_field_var_str + hist_data->n_save_var_str +
165862306a36Sopenharmony_ci		hist_data->n_var_str;
165962306a36Sopenharmony_ci	if (n_str > SYNTH_FIELDS_MAX) {
166062306a36Sopenharmony_ci		hist_elt_data_free(elt_data);
166162306a36Sopenharmony_ci		return -EINVAL;
166262306a36Sopenharmony_ci	}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	BUILD_BUG_ON(STR_VAR_LEN_MAX & (sizeof(u64) - 1));
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	size = STR_VAR_LEN_MAX;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	elt_data->field_var_str = kcalloc(n_str, sizeof(char *), GFP_KERNEL);
166962306a36Sopenharmony_ci	if (!elt_data->field_var_str) {
167062306a36Sopenharmony_ci		hist_elt_data_free(elt_data);
167162306a36Sopenharmony_ci		return -EINVAL;
167262306a36Sopenharmony_ci	}
167362306a36Sopenharmony_ci	elt_data->n_field_var_str = n_str;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	for (i = 0; i < n_str; i++) {
167662306a36Sopenharmony_ci		elt_data->field_var_str[i] = kzalloc(size, GFP_KERNEL);
167762306a36Sopenharmony_ci		if (!elt_data->field_var_str[i]) {
167862306a36Sopenharmony_ci			hist_elt_data_free(elt_data);
167962306a36Sopenharmony_ci			return -ENOMEM;
168062306a36Sopenharmony_ci		}
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	elt->private_data = elt_data;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return 0;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic void hist_trigger_elt_data_init(struct tracing_map_elt *elt)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	struct hist_elt_data *elt_data = elt->private_data;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	if (elt_data->comm)
169362306a36Sopenharmony_ci		save_comm(elt_data->comm, current);
169462306a36Sopenharmony_ci}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cistatic const struct tracing_map_ops hist_trigger_elt_data_ops = {
169762306a36Sopenharmony_ci	.elt_alloc	= hist_trigger_elt_data_alloc,
169862306a36Sopenharmony_ci	.elt_free	= hist_trigger_elt_data_free,
169962306a36Sopenharmony_ci	.elt_init	= hist_trigger_elt_data_init,
170062306a36Sopenharmony_ci};
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_cistatic const char *get_hist_field_flags(struct hist_field *hist_field)
170362306a36Sopenharmony_ci{
170462306a36Sopenharmony_ci	const char *flags_str = NULL;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	if (hist_field->flags & HIST_FIELD_FL_HEX)
170762306a36Sopenharmony_ci		flags_str = "hex";
170862306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_SYM)
170962306a36Sopenharmony_ci		flags_str = "sym";
171062306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET)
171162306a36Sopenharmony_ci		flags_str = "sym-offset";
171262306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_EXECNAME)
171362306a36Sopenharmony_ci		flags_str = "execname";
171462306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_SYSCALL)
171562306a36Sopenharmony_ci		flags_str = "syscall";
171662306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_LOG2)
171762306a36Sopenharmony_ci		flags_str = "log2";
171862306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_BUCKET)
171962306a36Sopenharmony_ci		flags_str = "buckets";
172062306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS)
172162306a36Sopenharmony_ci		flags_str = "usecs";
172262306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_PERCENT)
172362306a36Sopenharmony_ci		flags_str = "percent";
172462306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_GRAPH)
172562306a36Sopenharmony_ci		flags_str = "graph";
172662306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_STACKTRACE)
172762306a36Sopenharmony_ci		flags_str = "stacktrace";
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	return flags_str;
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic void expr_field_str(struct hist_field *field, char *expr)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	if (field->flags & HIST_FIELD_FL_VAR_REF)
173562306a36Sopenharmony_ci		strcat(expr, "$");
173662306a36Sopenharmony_ci	else if (field->flags & HIST_FIELD_FL_CONST) {
173762306a36Sopenharmony_ci		char str[HIST_CONST_DIGITS_MAX];
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci		snprintf(str, HIST_CONST_DIGITS_MAX, "%llu", field->constant);
174062306a36Sopenharmony_ci		strcat(expr, str);
174162306a36Sopenharmony_ci	}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	strcat(expr, hist_field_name(field, 0));
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	if (field->flags && !(field->flags & HIST_FIELD_FL_VAR_REF)) {
174662306a36Sopenharmony_ci		const char *flags_str = get_hist_field_flags(field);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci		if (flags_str) {
174962306a36Sopenharmony_ci			strcat(expr, ".");
175062306a36Sopenharmony_ci			strcat(expr, flags_str);
175162306a36Sopenharmony_ci		}
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_cistatic char *expr_str(struct hist_field *field, unsigned int level)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	char *expr;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (level > 1)
176062306a36Sopenharmony_ci		return NULL;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
176362306a36Sopenharmony_ci	if (!expr)
176462306a36Sopenharmony_ci		return NULL;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	if (!field->operands[0]) {
176762306a36Sopenharmony_ci		expr_field_str(field, expr);
176862306a36Sopenharmony_ci		return expr;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (field->operator == FIELD_OP_UNARY_MINUS) {
177262306a36Sopenharmony_ci		char *subexpr;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci		strcat(expr, "-(");
177562306a36Sopenharmony_ci		subexpr = expr_str(field->operands[0], ++level);
177662306a36Sopenharmony_ci		if (!subexpr) {
177762306a36Sopenharmony_ci			kfree(expr);
177862306a36Sopenharmony_ci			return NULL;
177962306a36Sopenharmony_ci		}
178062306a36Sopenharmony_ci		strcat(expr, subexpr);
178162306a36Sopenharmony_ci		strcat(expr, ")");
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci		kfree(subexpr);
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci		return expr;
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	expr_field_str(field->operands[0], expr);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	switch (field->operator) {
179162306a36Sopenharmony_ci	case FIELD_OP_MINUS:
179262306a36Sopenharmony_ci		strcat(expr, "-");
179362306a36Sopenharmony_ci		break;
179462306a36Sopenharmony_ci	case FIELD_OP_PLUS:
179562306a36Sopenharmony_ci		strcat(expr, "+");
179662306a36Sopenharmony_ci		break;
179762306a36Sopenharmony_ci	case FIELD_OP_DIV:
179862306a36Sopenharmony_ci		strcat(expr, "/");
179962306a36Sopenharmony_ci		break;
180062306a36Sopenharmony_ci	case FIELD_OP_MULT:
180162306a36Sopenharmony_ci		strcat(expr, "*");
180262306a36Sopenharmony_ci		break;
180362306a36Sopenharmony_ci	default:
180462306a36Sopenharmony_ci		kfree(expr);
180562306a36Sopenharmony_ci		return NULL;
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	expr_field_str(field->operands[1], expr);
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	return expr;
181162306a36Sopenharmony_ci}
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci/*
181462306a36Sopenharmony_ci * If field_op != FIELD_OP_NONE, *sep points to the root operator
181562306a36Sopenharmony_ci * of the expression tree to be evaluated.
181662306a36Sopenharmony_ci */
181762306a36Sopenharmony_cistatic int contains_operator(char *str, char **sep)
181862306a36Sopenharmony_ci{
181962306a36Sopenharmony_ci	enum field_op_id field_op = FIELD_OP_NONE;
182062306a36Sopenharmony_ci	char *minus_op, *plus_op, *div_op, *mult_op;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	/*
182462306a36Sopenharmony_ci	 * Report the last occurrence of the operators first, so that the
182562306a36Sopenharmony_ci	 * expression is evaluated left to right. This is important since
182662306a36Sopenharmony_ci	 * subtraction and division are not associative.
182762306a36Sopenharmony_ci	 *
182862306a36Sopenharmony_ci	 *	e.g
182962306a36Sopenharmony_ci	 *		64/8/4/2 is 1, i.e 64/8/4/2 = ((64/8)/4)/2
183062306a36Sopenharmony_ci	 *		14-7-5-2 is 0, i.e 14-7-5-2 = ((14-7)-5)-2
183162306a36Sopenharmony_ci	 */
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	/*
183462306a36Sopenharmony_ci	 * First, find lower precedence addition and subtraction
183562306a36Sopenharmony_ci	 * since the expression will be evaluated recursively.
183662306a36Sopenharmony_ci	 */
183762306a36Sopenharmony_ci	minus_op = strrchr(str, '-');
183862306a36Sopenharmony_ci	if (minus_op) {
183962306a36Sopenharmony_ci		/*
184062306a36Sopenharmony_ci		 * Unary minus is not supported in sub-expressions. If
184162306a36Sopenharmony_ci		 * present, it is always the next root operator.
184262306a36Sopenharmony_ci		 */
184362306a36Sopenharmony_ci		if (minus_op == str) {
184462306a36Sopenharmony_ci			field_op = FIELD_OP_UNARY_MINUS;
184562306a36Sopenharmony_ci			goto out;
184662306a36Sopenharmony_ci		}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci		field_op = FIELD_OP_MINUS;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	plus_op = strrchr(str, '+');
185262306a36Sopenharmony_ci	if (plus_op || minus_op) {
185362306a36Sopenharmony_ci		/*
185462306a36Sopenharmony_ci		 * For operators of the same precedence use to rightmost as the
185562306a36Sopenharmony_ci		 * root, so that the expression is evaluated left to right.
185662306a36Sopenharmony_ci		 */
185762306a36Sopenharmony_ci		if (plus_op > minus_op)
185862306a36Sopenharmony_ci			field_op = FIELD_OP_PLUS;
185962306a36Sopenharmony_ci		goto out;
186062306a36Sopenharmony_ci	}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	/*
186362306a36Sopenharmony_ci	 * Multiplication and division have higher precedence than addition and
186462306a36Sopenharmony_ci	 * subtraction.
186562306a36Sopenharmony_ci	 */
186662306a36Sopenharmony_ci	div_op = strrchr(str, '/');
186762306a36Sopenharmony_ci	if (div_op)
186862306a36Sopenharmony_ci		field_op = FIELD_OP_DIV;
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	mult_op = strrchr(str, '*');
187162306a36Sopenharmony_ci	/*
187262306a36Sopenharmony_ci	 * For operators of the same precedence use to rightmost as the
187362306a36Sopenharmony_ci	 * root, so that the expression is evaluated left to right.
187462306a36Sopenharmony_ci	 */
187562306a36Sopenharmony_ci	if (mult_op > div_op)
187662306a36Sopenharmony_ci		field_op = FIELD_OP_MULT;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ciout:
187962306a36Sopenharmony_ci	if (sep) {
188062306a36Sopenharmony_ci		switch (field_op) {
188162306a36Sopenharmony_ci		case FIELD_OP_UNARY_MINUS:
188262306a36Sopenharmony_ci		case FIELD_OP_MINUS:
188362306a36Sopenharmony_ci			*sep = minus_op;
188462306a36Sopenharmony_ci			break;
188562306a36Sopenharmony_ci		case FIELD_OP_PLUS:
188662306a36Sopenharmony_ci			*sep = plus_op;
188762306a36Sopenharmony_ci			break;
188862306a36Sopenharmony_ci		case FIELD_OP_DIV:
188962306a36Sopenharmony_ci			*sep = div_op;
189062306a36Sopenharmony_ci			break;
189162306a36Sopenharmony_ci		case FIELD_OP_MULT:
189262306a36Sopenharmony_ci			*sep = mult_op;
189362306a36Sopenharmony_ci			break;
189462306a36Sopenharmony_ci		case FIELD_OP_NONE:
189562306a36Sopenharmony_ci		default:
189662306a36Sopenharmony_ci			*sep = NULL;
189762306a36Sopenharmony_ci			break;
189862306a36Sopenharmony_ci		}
189962306a36Sopenharmony_ci	}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	return field_op;
190262306a36Sopenharmony_ci}
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_cistatic void get_hist_field(struct hist_field *hist_field)
190562306a36Sopenharmony_ci{
190662306a36Sopenharmony_ci	hist_field->ref++;
190762306a36Sopenharmony_ci}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cistatic void __destroy_hist_field(struct hist_field *hist_field)
191062306a36Sopenharmony_ci{
191162306a36Sopenharmony_ci	if (--hist_field->ref > 1)
191262306a36Sopenharmony_ci		return;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	kfree(hist_field->var.name);
191562306a36Sopenharmony_ci	kfree(hist_field->name);
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	/* Can likely be a const */
191862306a36Sopenharmony_ci	kfree_const(hist_field->type);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	kfree(hist_field->system);
192162306a36Sopenharmony_ci	kfree(hist_field->event_name);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	kfree(hist_field);
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_cistatic void destroy_hist_field(struct hist_field *hist_field,
192762306a36Sopenharmony_ci			       unsigned int level)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	unsigned int i;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	if (level > 3)
193262306a36Sopenharmony_ci		return;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (!hist_field)
193562306a36Sopenharmony_ci		return;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	if (hist_field->flags & HIST_FIELD_FL_VAR_REF)
193862306a36Sopenharmony_ci		return; /* var refs will be destroyed separately */
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++)
194162306a36Sopenharmony_ci		destroy_hist_field(hist_field->operands[i], level + 1);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	__destroy_hist_field(hist_field);
194462306a36Sopenharmony_ci}
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_cistatic struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
194762306a36Sopenharmony_ci					    struct ftrace_event_field *field,
194862306a36Sopenharmony_ci					    unsigned long flags,
194962306a36Sopenharmony_ci					    char *var_name)
195062306a36Sopenharmony_ci{
195162306a36Sopenharmony_ci	struct hist_field *hist_field;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	if (field && is_function_field(field))
195462306a36Sopenharmony_ci		return NULL;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL);
195762306a36Sopenharmony_ci	if (!hist_field)
195862306a36Sopenharmony_ci		return NULL;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	hist_field->ref = 1;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	hist_field->hist_data = hist_data;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_EXPR || flags & HIST_FIELD_FL_ALIAS)
196562306a36Sopenharmony_ci		goto out; /* caller will populate */
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_VAR_REF) {
196862306a36Sopenharmony_ci		hist_field->fn_num = HIST_FIELD_FN_VAR_REF;
196962306a36Sopenharmony_ci		goto out;
197062306a36Sopenharmony_ci	}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_HITCOUNT) {
197362306a36Sopenharmony_ci		hist_field->fn_num = HIST_FIELD_FN_COUNTER;
197462306a36Sopenharmony_ci		hist_field->size = sizeof(u64);
197562306a36Sopenharmony_ci		hist_field->type = "u64";
197662306a36Sopenharmony_ci		goto out;
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_CONST) {
198062306a36Sopenharmony_ci		hist_field->fn_num = HIST_FIELD_FN_CONST;
198162306a36Sopenharmony_ci		hist_field->size = sizeof(u64);
198262306a36Sopenharmony_ci		hist_field->type = kstrdup("u64", GFP_KERNEL);
198362306a36Sopenharmony_ci		if (!hist_field->type)
198462306a36Sopenharmony_ci			goto free;
198562306a36Sopenharmony_ci		goto out;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_STACKTRACE) {
198962306a36Sopenharmony_ci		if (field)
199062306a36Sopenharmony_ci			hist_field->fn_num = HIST_FIELD_FN_STACK;
199162306a36Sopenharmony_ci		else
199262306a36Sopenharmony_ci			hist_field->fn_num = HIST_FIELD_FN_NOP;
199362306a36Sopenharmony_ci		hist_field->size = HIST_STACKTRACE_SIZE;
199462306a36Sopenharmony_ci		hist_field->type = kstrdup_const("unsigned long[]", GFP_KERNEL);
199562306a36Sopenharmony_ci		if (!hist_field->type)
199662306a36Sopenharmony_ci			goto free;
199762306a36Sopenharmony_ci		goto out;
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	if (flags & (HIST_FIELD_FL_LOG2 | HIST_FIELD_FL_BUCKET)) {
200162306a36Sopenharmony_ci		unsigned long fl = flags & ~(HIST_FIELD_FL_LOG2 | HIST_FIELD_FL_BUCKET);
200262306a36Sopenharmony_ci		hist_field->fn_num = flags & HIST_FIELD_FL_LOG2 ? HIST_FIELD_FN_LOG2 :
200362306a36Sopenharmony_ci			HIST_FIELD_FN_BUCKET;
200462306a36Sopenharmony_ci		hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL);
200562306a36Sopenharmony_ci		if (!hist_field->operands[0])
200662306a36Sopenharmony_ci			goto free;
200762306a36Sopenharmony_ci		hist_field->size = hist_field->operands[0]->size;
200862306a36Sopenharmony_ci		hist_field->type = kstrdup_const(hist_field->operands[0]->type, GFP_KERNEL);
200962306a36Sopenharmony_ci		if (!hist_field->type)
201062306a36Sopenharmony_ci			goto free;
201162306a36Sopenharmony_ci		goto out;
201262306a36Sopenharmony_ci	}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_TIMESTAMP) {
201562306a36Sopenharmony_ci		hist_field->fn_num = HIST_FIELD_FN_TIMESTAMP;
201662306a36Sopenharmony_ci		hist_field->size = sizeof(u64);
201762306a36Sopenharmony_ci		hist_field->type = "u64";
201862306a36Sopenharmony_ci		goto out;
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_CPU) {
202262306a36Sopenharmony_ci		hist_field->fn_num = HIST_FIELD_FN_CPU;
202362306a36Sopenharmony_ci		hist_field->size = sizeof(int);
202462306a36Sopenharmony_ci		hist_field->type = "unsigned int";
202562306a36Sopenharmony_ci		goto out;
202662306a36Sopenharmony_ci	}
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	if (WARN_ON_ONCE(!field))
202962306a36Sopenharmony_ci		goto out;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	/* Pointers to strings are just pointers and dangerous to dereference */
203262306a36Sopenharmony_ci	if (is_string_field(field) &&
203362306a36Sopenharmony_ci	    (field->filter_type != FILTER_PTR_STRING)) {
203462306a36Sopenharmony_ci		flags |= HIST_FIELD_FL_STRING;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci		hist_field->size = MAX_FILTER_STR_VAL;
203762306a36Sopenharmony_ci		hist_field->type = kstrdup_const(field->type, GFP_KERNEL);
203862306a36Sopenharmony_ci		if (!hist_field->type)
203962306a36Sopenharmony_ci			goto free;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci		if (field->filter_type == FILTER_STATIC_STRING) {
204262306a36Sopenharmony_ci			hist_field->fn_num = HIST_FIELD_FN_STRING;
204362306a36Sopenharmony_ci			hist_field->size = field->size;
204462306a36Sopenharmony_ci		} else if (field->filter_type == FILTER_DYN_STRING) {
204562306a36Sopenharmony_ci			hist_field->fn_num = HIST_FIELD_FN_DYNSTRING;
204662306a36Sopenharmony_ci		} else if (field->filter_type == FILTER_RDYN_STRING)
204762306a36Sopenharmony_ci			hist_field->fn_num = HIST_FIELD_FN_RELDYNSTRING;
204862306a36Sopenharmony_ci		else
204962306a36Sopenharmony_ci			hist_field->fn_num = HIST_FIELD_FN_PSTRING;
205062306a36Sopenharmony_ci	} else {
205162306a36Sopenharmony_ci		hist_field->size = field->size;
205262306a36Sopenharmony_ci		hist_field->is_signed = field->is_signed;
205362306a36Sopenharmony_ci		hist_field->type = kstrdup_const(field->type, GFP_KERNEL);
205462306a36Sopenharmony_ci		if (!hist_field->type)
205562306a36Sopenharmony_ci			goto free;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci		hist_field->fn_num = select_value_fn(field->size,
205862306a36Sopenharmony_ci						     field->is_signed);
205962306a36Sopenharmony_ci		if (hist_field->fn_num == HIST_FIELD_FN_NOP) {
206062306a36Sopenharmony_ci			destroy_hist_field(hist_field, 0);
206162306a36Sopenharmony_ci			return NULL;
206262306a36Sopenharmony_ci		}
206362306a36Sopenharmony_ci	}
206462306a36Sopenharmony_ci out:
206562306a36Sopenharmony_ci	hist_field->field = field;
206662306a36Sopenharmony_ci	hist_field->flags = flags;
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	if (var_name) {
206962306a36Sopenharmony_ci		hist_field->var.name = kstrdup(var_name, GFP_KERNEL);
207062306a36Sopenharmony_ci		if (!hist_field->var.name)
207162306a36Sopenharmony_ci			goto free;
207262306a36Sopenharmony_ci	}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	return hist_field;
207562306a36Sopenharmony_ci free:
207662306a36Sopenharmony_ci	destroy_hist_field(hist_field, 0);
207762306a36Sopenharmony_ci	return NULL;
207862306a36Sopenharmony_ci}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_cistatic void destroy_hist_fields(struct hist_trigger_data *hist_data)
208162306a36Sopenharmony_ci{
208262306a36Sopenharmony_ci	unsigned int i;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	for (i = 0; i < HIST_FIELDS_MAX; i++) {
208562306a36Sopenharmony_ci		if (hist_data->fields[i]) {
208662306a36Sopenharmony_ci			destroy_hist_field(hist_data->fields[i], 0);
208762306a36Sopenharmony_ci			hist_data->fields[i] = NULL;
208862306a36Sopenharmony_ci		}
208962306a36Sopenharmony_ci	}
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_var_refs; i++) {
209262306a36Sopenharmony_ci		WARN_ON(!(hist_data->var_refs[i]->flags & HIST_FIELD_FL_VAR_REF));
209362306a36Sopenharmony_ci		__destroy_hist_field(hist_data->var_refs[i]);
209462306a36Sopenharmony_ci		hist_data->var_refs[i] = NULL;
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_cistatic int init_var_ref(struct hist_field *ref_field,
209962306a36Sopenharmony_ci			struct hist_field *var_field,
210062306a36Sopenharmony_ci			char *system, char *event_name)
210162306a36Sopenharmony_ci{
210262306a36Sopenharmony_ci	int err = 0;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	ref_field->var.idx = var_field->var.idx;
210562306a36Sopenharmony_ci	ref_field->var.hist_data = var_field->hist_data;
210662306a36Sopenharmony_ci	ref_field->size = var_field->size;
210762306a36Sopenharmony_ci	ref_field->is_signed = var_field->is_signed;
210862306a36Sopenharmony_ci	ref_field->flags |= var_field->flags &
210962306a36Sopenharmony_ci		(HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	if (system) {
211262306a36Sopenharmony_ci		ref_field->system = kstrdup(system, GFP_KERNEL);
211362306a36Sopenharmony_ci		if (!ref_field->system)
211462306a36Sopenharmony_ci			return -ENOMEM;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	if (event_name) {
211862306a36Sopenharmony_ci		ref_field->event_name = kstrdup(event_name, GFP_KERNEL);
211962306a36Sopenharmony_ci		if (!ref_field->event_name) {
212062306a36Sopenharmony_ci			err = -ENOMEM;
212162306a36Sopenharmony_ci			goto free;
212262306a36Sopenharmony_ci		}
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	if (var_field->var.name) {
212662306a36Sopenharmony_ci		ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL);
212762306a36Sopenharmony_ci		if (!ref_field->name) {
212862306a36Sopenharmony_ci			err = -ENOMEM;
212962306a36Sopenharmony_ci			goto free;
213062306a36Sopenharmony_ci		}
213162306a36Sopenharmony_ci	} else if (var_field->name) {
213262306a36Sopenharmony_ci		ref_field->name = kstrdup(var_field->name, GFP_KERNEL);
213362306a36Sopenharmony_ci		if (!ref_field->name) {
213462306a36Sopenharmony_ci			err = -ENOMEM;
213562306a36Sopenharmony_ci			goto free;
213662306a36Sopenharmony_ci		}
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	ref_field->type = kstrdup_const(var_field->type, GFP_KERNEL);
214062306a36Sopenharmony_ci	if (!ref_field->type) {
214162306a36Sopenharmony_ci		err = -ENOMEM;
214262306a36Sopenharmony_ci		goto free;
214362306a36Sopenharmony_ci	}
214462306a36Sopenharmony_ci out:
214562306a36Sopenharmony_ci	return err;
214662306a36Sopenharmony_ci free:
214762306a36Sopenharmony_ci	kfree(ref_field->system);
214862306a36Sopenharmony_ci	ref_field->system = NULL;
214962306a36Sopenharmony_ci	kfree(ref_field->event_name);
215062306a36Sopenharmony_ci	ref_field->event_name = NULL;
215162306a36Sopenharmony_ci	kfree(ref_field->name);
215262306a36Sopenharmony_ci	ref_field->name = NULL;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	goto out;
215562306a36Sopenharmony_ci}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_cistatic int find_var_ref_idx(struct hist_trigger_data *hist_data,
215862306a36Sopenharmony_ci			    struct hist_field *var_field)
215962306a36Sopenharmony_ci{
216062306a36Sopenharmony_ci	struct hist_field *ref_field;
216162306a36Sopenharmony_ci	int i;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_var_refs; i++) {
216462306a36Sopenharmony_ci		ref_field = hist_data->var_refs[i];
216562306a36Sopenharmony_ci		if (ref_field->var.idx == var_field->var.idx &&
216662306a36Sopenharmony_ci		    ref_field->var.hist_data == var_field->hist_data)
216762306a36Sopenharmony_ci			return i;
216862306a36Sopenharmony_ci	}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	return -ENOENT;
217162306a36Sopenharmony_ci}
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci/**
217462306a36Sopenharmony_ci * create_var_ref - Create a variable reference and attach it to trigger
217562306a36Sopenharmony_ci * @hist_data: The trigger that will be referencing the variable
217662306a36Sopenharmony_ci * @var_field: The VAR field to create a reference to
217762306a36Sopenharmony_ci * @system: The optional system string
217862306a36Sopenharmony_ci * @event_name: The optional event_name string
217962306a36Sopenharmony_ci *
218062306a36Sopenharmony_ci * Given a variable hist_field, create a VAR_REF hist_field that
218162306a36Sopenharmony_ci * represents a reference to it.
218262306a36Sopenharmony_ci *
218362306a36Sopenharmony_ci * This function also adds the reference to the trigger that
218462306a36Sopenharmony_ci * now references the variable.
218562306a36Sopenharmony_ci *
218662306a36Sopenharmony_ci * Return: The VAR_REF field if successful, NULL if not
218762306a36Sopenharmony_ci */
218862306a36Sopenharmony_cistatic struct hist_field *create_var_ref(struct hist_trigger_data *hist_data,
218962306a36Sopenharmony_ci					 struct hist_field *var_field,
219062306a36Sopenharmony_ci					 char *system, char *event_name)
219162306a36Sopenharmony_ci{
219262306a36Sopenharmony_ci	unsigned long flags = HIST_FIELD_FL_VAR_REF;
219362306a36Sopenharmony_ci	struct hist_field *ref_field;
219462306a36Sopenharmony_ci	int i;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	/* Check if the variable already exists */
219762306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_var_refs; i++) {
219862306a36Sopenharmony_ci		ref_field = hist_data->var_refs[i];
219962306a36Sopenharmony_ci		if (ref_field->var.idx == var_field->var.idx &&
220062306a36Sopenharmony_ci		    ref_field->var.hist_data == var_field->hist_data) {
220162306a36Sopenharmony_ci			get_hist_field(ref_field);
220262306a36Sopenharmony_ci			return ref_field;
220362306a36Sopenharmony_ci		}
220462306a36Sopenharmony_ci	}
220562306a36Sopenharmony_ci	/* Sanity check to avoid out-of-bound write on 'hist_data->var_refs' */
220662306a36Sopenharmony_ci	if (hist_data->n_var_refs >= TRACING_MAP_VARS_MAX)
220762306a36Sopenharmony_ci		return NULL;
220862306a36Sopenharmony_ci	ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL);
220962306a36Sopenharmony_ci	if (ref_field) {
221062306a36Sopenharmony_ci		if (init_var_ref(ref_field, var_field, system, event_name)) {
221162306a36Sopenharmony_ci			destroy_hist_field(ref_field, 0);
221262306a36Sopenharmony_ci			return NULL;
221362306a36Sopenharmony_ci		}
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci		hist_data->var_refs[hist_data->n_var_refs] = ref_field;
221662306a36Sopenharmony_ci		ref_field->var_ref_idx = hist_data->n_var_refs++;
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	return ref_field;
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_cistatic bool is_var_ref(char *var_name)
222362306a36Sopenharmony_ci{
222462306a36Sopenharmony_ci	if (!var_name || strlen(var_name) < 2 || var_name[0] != '$')
222562306a36Sopenharmony_ci		return false;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return true;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_cistatic char *field_name_from_var(struct hist_trigger_data *hist_data,
223162306a36Sopenharmony_ci				 char *var_name)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	char *name, *field;
223462306a36Sopenharmony_ci	unsigned int i;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) {
223762306a36Sopenharmony_ci		name = hist_data->attrs->var_defs.name[i];
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci		if (strcmp(var_name, name) == 0) {
224062306a36Sopenharmony_ci			field = hist_data->attrs->var_defs.expr[i];
224162306a36Sopenharmony_ci			if (contains_operator(field, NULL) || is_var_ref(field))
224262306a36Sopenharmony_ci				continue;
224362306a36Sopenharmony_ci			return field;
224462306a36Sopenharmony_ci		}
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	return NULL;
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_cistatic char *local_field_var_ref(struct hist_trigger_data *hist_data,
225162306a36Sopenharmony_ci				 char *system, char *event_name,
225262306a36Sopenharmony_ci				 char *var_name)
225362306a36Sopenharmony_ci{
225462306a36Sopenharmony_ci	struct trace_event_call *call;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	if (system && event_name) {
225762306a36Sopenharmony_ci		call = hist_data->event_file->event_call;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci		if (strcmp(system, call->class->system) != 0)
226062306a36Sopenharmony_ci			return NULL;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci		if (strcmp(event_name, trace_event_name(call)) != 0)
226362306a36Sopenharmony_ci			return NULL;
226462306a36Sopenharmony_ci	}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	if (!!system != !!event_name)
226762306a36Sopenharmony_ci		return NULL;
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	if (!is_var_ref(var_name))
227062306a36Sopenharmony_ci		return NULL;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	var_name++;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	return field_name_from_var(hist_data, var_name);
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_cistatic struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
227862306a36Sopenharmony_ci					char *system, char *event_name,
227962306a36Sopenharmony_ci					char *var_name)
228062306a36Sopenharmony_ci{
228162306a36Sopenharmony_ci	struct hist_field *var_field = NULL, *ref_field = NULL;
228262306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	if (!is_var_ref(var_name))
228562306a36Sopenharmony_ci		return NULL;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	var_name++;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	var_field = find_event_var(hist_data, system, event_name, var_name);
229062306a36Sopenharmony_ci	if (var_field)
229162306a36Sopenharmony_ci		ref_field = create_var_ref(hist_data, var_field,
229262306a36Sopenharmony_ci					   system, event_name);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	if (!ref_field)
229562306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_VAR_NOT_FOUND, errpos(var_name));
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	return ref_field;
229862306a36Sopenharmony_ci}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_cistatic struct ftrace_event_field *
230162306a36Sopenharmony_ciparse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
230262306a36Sopenharmony_ci	    char *field_str, unsigned long *flags, unsigned long *buckets)
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	struct ftrace_event_field *field = NULL;
230562306a36Sopenharmony_ci	char *field_name, *modifier, *str;
230662306a36Sopenharmony_ci	struct trace_array *tr = file->tr;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	modifier = str = kstrdup(field_str, GFP_KERNEL);
230962306a36Sopenharmony_ci	if (!modifier)
231062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	field_name = strsep(&modifier, ".");
231362306a36Sopenharmony_ci	if (modifier) {
231462306a36Sopenharmony_ci		if (strcmp(modifier, "hex") == 0)
231562306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_HEX;
231662306a36Sopenharmony_ci		else if (strcmp(modifier, "sym") == 0)
231762306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_SYM;
231862306a36Sopenharmony_ci		/*
231962306a36Sopenharmony_ci		 * 'sym-offset' occurrences in the trigger string are modified
232062306a36Sopenharmony_ci		 * to 'symXoffset' to simplify arithmetic expression parsing.
232162306a36Sopenharmony_ci		 */
232262306a36Sopenharmony_ci		else if (strcmp(modifier, "symXoffset") == 0)
232362306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_SYM_OFFSET;
232462306a36Sopenharmony_ci		else if ((strcmp(modifier, "execname") == 0) &&
232562306a36Sopenharmony_ci			 (strcmp(field_name, "common_pid") == 0))
232662306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_EXECNAME;
232762306a36Sopenharmony_ci		else if (strcmp(modifier, "syscall") == 0)
232862306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_SYSCALL;
232962306a36Sopenharmony_ci		else if (strcmp(modifier, "stacktrace") == 0)
233062306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_STACKTRACE;
233162306a36Sopenharmony_ci		else if (strcmp(modifier, "log2") == 0)
233262306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_LOG2;
233362306a36Sopenharmony_ci		else if (strcmp(modifier, "usecs") == 0)
233462306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
233562306a36Sopenharmony_ci		else if (strncmp(modifier, "bucket", 6) == 0) {
233662306a36Sopenharmony_ci			int ret;
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci			modifier += 6;
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci			if (*modifier == 's')
234162306a36Sopenharmony_ci				modifier++;
234262306a36Sopenharmony_ci			if (*modifier != '=')
234362306a36Sopenharmony_ci				goto error;
234462306a36Sopenharmony_ci			modifier++;
234562306a36Sopenharmony_ci			ret = kstrtoul(modifier, 0, buckets);
234662306a36Sopenharmony_ci			if (ret || !(*buckets))
234762306a36Sopenharmony_ci				goto error;
234862306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_BUCKET;
234962306a36Sopenharmony_ci		} else if (strncmp(modifier, "percent", 7) == 0) {
235062306a36Sopenharmony_ci			if (*flags & (HIST_FIELD_FL_VAR | HIST_FIELD_FL_KEY))
235162306a36Sopenharmony_ci				goto error;
235262306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_PERCENT;
235362306a36Sopenharmony_ci		} else if (strncmp(modifier, "graph", 5) == 0) {
235462306a36Sopenharmony_ci			if (*flags & (HIST_FIELD_FL_VAR | HIST_FIELD_FL_KEY))
235562306a36Sopenharmony_ci				goto error;
235662306a36Sopenharmony_ci			*flags |= HIST_FIELD_FL_GRAPH;
235762306a36Sopenharmony_ci		} else {
235862306a36Sopenharmony_ci error:
235962306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(modifier));
236062306a36Sopenharmony_ci			field = ERR_PTR(-EINVAL);
236162306a36Sopenharmony_ci			goto out;
236262306a36Sopenharmony_ci		}
236362306a36Sopenharmony_ci	}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	if (strcmp(field_name, "common_timestamp") == 0) {
236662306a36Sopenharmony_ci		*flags |= HIST_FIELD_FL_TIMESTAMP;
236762306a36Sopenharmony_ci		hist_data->enable_timestamps = true;
236862306a36Sopenharmony_ci		if (*flags & HIST_FIELD_FL_TIMESTAMP_USECS)
236962306a36Sopenharmony_ci			hist_data->attrs->ts_in_usecs = true;
237062306a36Sopenharmony_ci	} else if (strcmp(field_name, "common_stacktrace") == 0) {
237162306a36Sopenharmony_ci		*flags |= HIST_FIELD_FL_STACKTRACE;
237262306a36Sopenharmony_ci	} else if (strcmp(field_name, "common_cpu") == 0)
237362306a36Sopenharmony_ci		*flags |= HIST_FIELD_FL_CPU;
237462306a36Sopenharmony_ci	else if (strcmp(field_name, "hitcount") == 0)
237562306a36Sopenharmony_ci		*flags |= HIST_FIELD_FL_HITCOUNT;
237662306a36Sopenharmony_ci	else {
237762306a36Sopenharmony_ci		field = trace_find_event_field(file->event_call, field_name);
237862306a36Sopenharmony_ci		if (!field || !field->size) {
237962306a36Sopenharmony_ci			/*
238062306a36Sopenharmony_ci			 * For backward compatibility, if field_name
238162306a36Sopenharmony_ci			 * was "cpu" or "stacktrace", then we treat this
238262306a36Sopenharmony_ci			 * the same as common_cpu and common_stacktrace
238362306a36Sopenharmony_ci			 * respectively. This also works for "CPU", and
238462306a36Sopenharmony_ci			 * "STACKTRACE".
238562306a36Sopenharmony_ci			 */
238662306a36Sopenharmony_ci			if (field && field->filter_type == FILTER_CPU) {
238762306a36Sopenharmony_ci				*flags |= HIST_FIELD_FL_CPU;
238862306a36Sopenharmony_ci			} else if (field && field->filter_type == FILTER_STACKTRACE) {
238962306a36Sopenharmony_ci				*flags |= HIST_FIELD_FL_STACKTRACE;
239062306a36Sopenharmony_ci			} else {
239162306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_FIELD_NOT_FOUND,
239262306a36Sopenharmony_ci					 errpos(field_name));
239362306a36Sopenharmony_ci				field = ERR_PTR(-EINVAL);
239462306a36Sopenharmony_ci				goto out;
239562306a36Sopenharmony_ci			}
239662306a36Sopenharmony_ci		}
239762306a36Sopenharmony_ci	}
239862306a36Sopenharmony_ci out:
239962306a36Sopenharmony_ci	kfree(str);
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	return field;
240262306a36Sopenharmony_ci}
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_cistatic struct hist_field *create_alias(struct hist_trigger_data *hist_data,
240562306a36Sopenharmony_ci				       struct hist_field *var_ref,
240662306a36Sopenharmony_ci				       char *var_name)
240762306a36Sopenharmony_ci{
240862306a36Sopenharmony_ci	struct hist_field *alias = NULL;
240962306a36Sopenharmony_ci	unsigned long flags = HIST_FIELD_FL_ALIAS | HIST_FIELD_FL_VAR;
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	alias = create_hist_field(hist_data, NULL, flags, var_name);
241262306a36Sopenharmony_ci	if (!alias)
241362306a36Sopenharmony_ci		return NULL;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	alias->fn_num = var_ref->fn_num;
241662306a36Sopenharmony_ci	alias->operands[0] = var_ref;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	if (init_var_ref(alias, var_ref, var_ref->system, var_ref->event_name)) {
241962306a36Sopenharmony_ci		destroy_hist_field(alias, 0);
242062306a36Sopenharmony_ci		return NULL;
242162306a36Sopenharmony_ci	}
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	alias->var_ref_idx = var_ref->var_ref_idx;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	return alias;
242662306a36Sopenharmony_ci}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_cistatic struct hist_field *parse_const(struct hist_trigger_data *hist_data,
242962306a36Sopenharmony_ci				      char *str, char *var_name,
243062306a36Sopenharmony_ci				      unsigned long *flags)
243162306a36Sopenharmony_ci{
243262306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
243362306a36Sopenharmony_ci	struct hist_field *field = NULL;
243462306a36Sopenharmony_ci	u64 constant;
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	if (kstrtoull(str, 0, &constant)) {
243762306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_EXPECT_NUMBER, errpos(str));
243862306a36Sopenharmony_ci		return NULL;
243962306a36Sopenharmony_ci	}
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	*flags |= HIST_FIELD_FL_CONST;
244262306a36Sopenharmony_ci	field = create_hist_field(hist_data, NULL, *flags, var_name);
244362306a36Sopenharmony_ci	if (!field)
244462306a36Sopenharmony_ci		return NULL;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	field->constant = constant;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	return field;
244962306a36Sopenharmony_ci}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_cistatic struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
245262306a36Sopenharmony_ci				     struct trace_event_file *file, char *str,
245362306a36Sopenharmony_ci				     unsigned long *flags, char *var_name)
245462306a36Sopenharmony_ci{
245562306a36Sopenharmony_ci	char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str;
245662306a36Sopenharmony_ci	struct ftrace_event_field *field = NULL;
245762306a36Sopenharmony_ci	struct hist_field *hist_field = NULL;
245862306a36Sopenharmony_ci	unsigned long buckets = 0;
245962306a36Sopenharmony_ci	int ret = 0;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	if (isdigit(str[0])) {
246262306a36Sopenharmony_ci		hist_field = parse_const(hist_data, str, var_name, flags);
246362306a36Sopenharmony_ci		if (!hist_field) {
246462306a36Sopenharmony_ci			ret = -EINVAL;
246562306a36Sopenharmony_ci			goto out;
246662306a36Sopenharmony_ci		}
246762306a36Sopenharmony_ci		return hist_field;
246862306a36Sopenharmony_ci	}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	s = strchr(str, '.');
247162306a36Sopenharmony_ci	if (s) {
247262306a36Sopenharmony_ci		s = strchr(++s, '.');
247362306a36Sopenharmony_ci		if (s) {
247462306a36Sopenharmony_ci			ref_system = strsep(&str, ".");
247562306a36Sopenharmony_ci			if (!str) {
247662306a36Sopenharmony_ci				ret = -EINVAL;
247762306a36Sopenharmony_ci				goto out;
247862306a36Sopenharmony_ci			}
247962306a36Sopenharmony_ci			ref_event = strsep(&str, ".");
248062306a36Sopenharmony_ci			if (!str) {
248162306a36Sopenharmony_ci				ret = -EINVAL;
248262306a36Sopenharmony_ci				goto out;
248362306a36Sopenharmony_ci			}
248462306a36Sopenharmony_ci			ref_var = str;
248562306a36Sopenharmony_ci		}
248662306a36Sopenharmony_ci	}
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var);
248962306a36Sopenharmony_ci	if (!s) {
249062306a36Sopenharmony_ci		hist_field = parse_var_ref(hist_data, ref_system,
249162306a36Sopenharmony_ci					   ref_event, ref_var);
249262306a36Sopenharmony_ci		if (hist_field) {
249362306a36Sopenharmony_ci			if (var_name) {
249462306a36Sopenharmony_ci				hist_field = create_alias(hist_data, hist_field, var_name);
249562306a36Sopenharmony_ci				if (!hist_field) {
249662306a36Sopenharmony_ci					ret = -ENOMEM;
249762306a36Sopenharmony_ci					goto out;
249862306a36Sopenharmony_ci				}
249962306a36Sopenharmony_ci			}
250062306a36Sopenharmony_ci			return hist_field;
250162306a36Sopenharmony_ci		}
250262306a36Sopenharmony_ci	} else
250362306a36Sopenharmony_ci		str = s;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	field = parse_field(hist_data, file, str, flags, &buckets);
250662306a36Sopenharmony_ci	if (IS_ERR(field)) {
250762306a36Sopenharmony_ci		ret = PTR_ERR(field);
250862306a36Sopenharmony_ci		goto out;
250962306a36Sopenharmony_ci	}
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	hist_field = create_hist_field(hist_data, field, *flags, var_name);
251262306a36Sopenharmony_ci	if (!hist_field) {
251362306a36Sopenharmony_ci		ret = -ENOMEM;
251462306a36Sopenharmony_ci		goto out;
251562306a36Sopenharmony_ci	}
251662306a36Sopenharmony_ci	hist_field->buckets = buckets;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	return hist_field;
251962306a36Sopenharmony_ci out:
252062306a36Sopenharmony_ci	return ERR_PTR(ret);
252162306a36Sopenharmony_ci}
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_cistatic struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
252462306a36Sopenharmony_ci				     struct trace_event_file *file,
252562306a36Sopenharmony_ci				     char *str, unsigned long flags,
252662306a36Sopenharmony_ci				     char *var_name, unsigned int *n_subexprs);
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_cistatic struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
252962306a36Sopenharmony_ci				      struct trace_event_file *file,
253062306a36Sopenharmony_ci				      char *str, unsigned long flags,
253162306a36Sopenharmony_ci				      char *var_name, unsigned int *n_subexprs)
253262306a36Sopenharmony_ci{
253362306a36Sopenharmony_ci	struct hist_field *operand1, *expr = NULL;
253462306a36Sopenharmony_ci	unsigned long operand_flags;
253562306a36Sopenharmony_ci	int ret = 0;
253662306a36Sopenharmony_ci	char *s;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	/* Unary minus operator, increment n_subexprs */
253962306a36Sopenharmony_ci	++*n_subexprs;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	/* we support only -(xxx) i.e. explicit parens required */
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	if (*n_subexprs > 3) {
254462306a36Sopenharmony_ci		hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
254562306a36Sopenharmony_ci		ret = -EINVAL;
254662306a36Sopenharmony_ci		goto free;
254762306a36Sopenharmony_ci	}
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	str++; /* skip leading '-' */
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	s = strchr(str, '(');
255262306a36Sopenharmony_ci	if (s)
255362306a36Sopenharmony_ci		str++;
255462306a36Sopenharmony_ci	else {
255562306a36Sopenharmony_ci		ret = -EINVAL;
255662306a36Sopenharmony_ci		goto free;
255762306a36Sopenharmony_ci	}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	s = strrchr(str, ')');
256062306a36Sopenharmony_ci	if (s) {
256162306a36Sopenharmony_ci		 /* unary minus not supported in sub-expressions */
256262306a36Sopenharmony_ci		if (*(s+1) != '\0') {
256362306a36Sopenharmony_ci			hist_err(file->tr, HIST_ERR_UNARY_MINUS_SUBEXPR,
256462306a36Sopenharmony_ci				 errpos(str));
256562306a36Sopenharmony_ci			ret = -EINVAL;
256662306a36Sopenharmony_ci			goto free;
256762306a36Sopenharmony_ci		}
256862306a36Sopenharmony_ci		*s = '\0';
256962306a36Sopenharmony_ci	}
257062306a36Sopenharmony_ci	else {
257162306a36Sopenharmony_ci		ret = -EINVAL; /* no closing ')' */
257262306a36Sopenharmony_ci		goto free;
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	flags |= HIST_FIELD_FL_EXPR;
257662306a36Sopenharmony_ci	expr = create_hist_field(hist_data, NULL, flags, var_name);
257762306a36Sopenharmony_ci	if (!expr) {
257862306a36Sopenharmony_ci		ret = -ENOMEM;
257962306a36Sopenharmony_ci		goto free;
258062306a36Sopenharmony_ci	}
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	operand_flags = 0;
258362306a36Sopenharmony_ci	operand1 = parse_expr(hist_data, file, str, operand_flags, NULL, n_subexprs);
258462306a36Sopenharmony_ci	if (IS_ERR(operand1)) {
258562306a36Sopenharmony_ci		ret = PTR_ERR(operand1);
258662306a36Sopenharmony_ci		goto free;
258762306a36Sopenharmony_ci	}
258862306a36Sopenharmony_ci	if (operand1->flags & HIST_FIELD_FL_STRING) {
258962306a36Sopenharmony_ci		/* String type can not be the operand of unary operator. */
259062306a36Sopenharmony_ci		hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(str));
259162306a36Sopenharmony_ci		destroy_hist_field(operand1, 0);
259262306a36Sopenharmony_ci		ret = -EINVAL;
259362306a36Sopenharmony_ci		goto free;
259462306a36Sopenharmony_ci	}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	expr->flags |= operand1->flags &
259762306a36Sopenharmony_ci		(HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
259862306a36Sopenharmony_ci	expr->fn_num = HIST_FIELD_FN_UMINUS;
259962306a36Sopenharmony_ci	expr->operands[0] = operand1;
260062306a36Sopenharmony_ci	expr->size = operand1->size;
260162306a36Sopenharmony_ci	expr->is_signed = operand1->is_signed;
260262306a36Sopenharmony_ci	expr->operator = FIELD_OP_UNARY_MINUS;
260362306a36Sopenharmony_ci	expr->name = expr_str(expr, 0);
260462306a36Sopenharmony_ci	expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
260562306a36Sopenharmony_ci	if (!expr->type) {
260662306a36Sopenharmony_ci		ret = -ENOMEM;
260762306a36Sopenharmony_ci		goto free;
260862306a36Sopenharmony_ci	}
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	return expr;
261162306a36Sopenharmony_ci free:
261262306a36Sopenharmony_ci	destroy_hist_field(expr, 0);
261362306a36Sopenharmony_ci	return ERR_PTR(ret);
261462306a36Sopenharmony_ci}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci/*
261762306a36Sopenharmony_ci * If the operands are var refs, return pointers the
261862306a36Sopenharmony_ci * variable(s) referenced in var1 and var2, else NULL.
261962306a36Sopenharmony_ci */
262062306a36Sopenharmony_cistatic int check_expr_operands(struct trace_array *tr,
262162306a36Sopenharmony_ci			       struct hist_field *operand1,
262262306a36Sopenharmony_ci			       struct hist_field *operand2,
262362306a36Sopenharmony_ci			       struct hist_field **var1,
262462306a36Sopenharmony_ci			       struct hist_field **var2)
262562306a36Sopenharmony_ci{
262662306a36Sopenharmony_ci	unsigned long operand1_flags = operand1->flags;
262762306a36Sopenharmony_ci	unsigned long operand2_flags = operand2->flags;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	if ((operand1_flags & HIST_FIELD_FL_VAR_REF) ||
263062306a36Sopenharmony_ci	    (operand1_flags & HIST_FIELD_FL_ALIAS)) {
263162306a36Sopenharmony_ci		struct hist_field *var;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci		var = find_var_field(operand1->var.hist_data, operand1->name);
263462306a36Sopenharmony_ci		if (!var)
263562306a36Sopenharmony_ci			return -EINVAL;
263662306a36Sopenharmony_ci		operand1_flags = var->flags;
263762306a36Sopenharmony_ci		*var1 = var;
263862306a36Sopenharmony_ci	}
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	if ((operand2_flags & HIST_FIELD_FL_VAR_REF) ||
264162306a36Sopenharmony_ci	    (operand2_flags & HIST_FIELD_FL_ALIAS)) {
264262306a36Sopenharmony_ci		struct hist_field *var;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci		var = find_var_field(operand2->var.hist_data, operand2->name);
264562306a36Sopenharmony_ci		if (!var)
264662306a36Sopenharmony_ci			return -EINVAL;
264762306a36Sopenharmony_ci		operand2_flags = var->flags;
264862306a36Sopenharmony_ci		*var2 = var;
264962306a36Sopenharmony_ci	}
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) !=
265262306a36Sopenharmony_ci	    (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) {
265362306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_TIMESTAMP_MISMATCH, 0);
265462306a36Sopenharmony_ci		return -EINVAL;
265562306a36Sopenharmony_ci	}
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	return 0;
265862306a36Sopenharmony_ci}
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_cistatic struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
266162306a36Sopenharmony_ci				     struct trace_event_file *file,
266262306a36Sopenharmony_ci				     char *str, unsigned long flags,
266362306a36Sopenharmony_ci				     char *var_name, unsigned int *n_subexprs)
266462306a36Sopenharmony_ci{
266562306a36Sopenharmony_ci	struct hist_field *operand1 = NULL, *operand2 = NULL, *expr = NULL;
266662306a36Sopenharmony_ci	struct hist_field *var1 = NULL, *var2 = NULL;
266762306a36Sopenharmony_ci	unsigned long operand_flags, operand2_flags;
266862306a36Sopenharmony_ci	int field_op, ret = -EINVAL;
266962306a36Sopenharmony_ci	char *sep, *operand1_str;
267062306a36Sopenharmony_ci	enum hist_field_fn op_fn;
267162306a36Sopenharmony_ci	bool combine_consts;
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	if (*n_subexprs > 3) {
267462306a36Sopenharmony_ci		hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
267562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
267662306a36Sopenharmony_ci	}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	field_op = contains_operator(str, &sep);
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	if (field_op == FIELD_OP_NONE)
268162306a36Sopenharmony_ci		return parse_atom(hist_data, file, str, &flags, var_name);
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci	if (field_op == FIELD_OP_UNARY_MINUS)
268462306a36Sopenharmony_ci		return parse_unary(hist_data, file, str, flags, var_name, n_subexprs);
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci	/* Binary operator found, increment n_subexprs */
268762306a36Sopenharmony_ci	++*n_subexprs;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	/* Split the expression string at the root operator */
269062306a36Sopenharmony_ci	if (!sep)
269162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	*sep = '\0';
269462306a36Sopenharmony_ci	operand1_str = str;
269562306a36Sopenharmony_ci	str = sep+1;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	/* Binary operator requires both operands */
269862306a36Sopenharmony_ci	if (*operand1_str == '\0' || *str == '\0')
269962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	operand_flags = 0;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	/* LHS of string is an expression e.g. a+b in a+b+c */
270462306a36Sopenharmony_ci	operand1 = parse_expr(hist_data, file, operand1_str, operand_flags, NULL, n_subexprs);
270562306a36Sopenharmony_ci	if (IS_ERR(operand1))
270662306a36Sopenharmony_ci		return ERR_CAST(operand1);
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	if (operand1->flags & HIST_FIELD_FL_STRING) {
270962306a36Sopenharmony_ci		hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(operand1_str));
271062306a36Sopenharmony_ci		ret = -EINVAL;
271162306a36Sopenharmony_ci		goto free_op1;
271262306a36Sopenharmony_ci	}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	/* RHS of string is another expression e.g. c in a+b+c */
271562306a36Sopenharmony_ci	operand_flags = 0;
271662306a36Sopenharmony_ci	operand2 = parse_expr(hist_data, file, str, operand_flags, NULL, n_subexprs);
271762306a36Sopenharmony_ci	if (IS_ERR(operand2)) {
271862306a36Sopenharmony_ci		ret = PTR_ERR(operand2);
271962306a36Sopenharmony_ci		goto free_op1;
272062306a36Sopenharmony_ci	}
272162306a36Sopenharmony_ci	if (operand2->flags & HIST_FIELD_FL_STRING) {
272262306a36Sopenharmony_ci		hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(str));
272362306a36Sopenharmony_ci		ret = -EINVAL;
272462306a36Sopenharmony_ci		goto free_operands;
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	switch (field_op) {
272862306a36Sopenharmony_ci	case FIELD_OP_MINUS:
272962306a36Sopenharmony_ci		op_fn = HIST_FIELD_FN_MINUS;
273062306a36Sopenharmony_ci		break;
273162306a36Sopenharmony_ci	case FIELD_OP_PLUS:
273262306a36Sopenharmony_ci		op_fn = HIST_FIELD_FN_PLUS;
273362306a36Sopenharmony_ci		break;
273462306a36Sopenharmony_ci	case FIELD_OP_DIV:
273562306a36Sopenharmony_ci		op_fn = HIST_FIELD_FN_DIV;
273662306a36Sopenharmony_ci		break;
273762306a36Sopenharmony_ci	case FIELD_OP_MULT:
273862306a36Sopenharmony_ci		op_fn = HIST_FIELD_FN_MULT;
273962306a36Sopenharmony_ci		break;
274062306a36Sopenharmony_ci	default:
274162306a36Sopenharmony_ci		ret = -EINVAL;
274262306a36Sopenharmony_ci		goto free_operands;
274362306a36Sopenharmony_ci	}
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	ret = check_expr_operands(file->tr, operand1, operand2, &var1, &var2);
274662306a36Sopenharmony_ci	if (ret)
274762306a36Sopenharmony_ci		goto free_operands;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	operand_flags = var1 ? var1->flags : operand1->flags;
275062306a36Sopenharmony_ci	operand2_flags = var2 ? var2->flags : operand2->flags;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	/*
275362306a36Sopenharmony_ci	 * If both operands are constant, the expression can be
275462306a36Sopenharmony_ci	 * collapsed to a single constant.
275562306a36Sopenharmony_ci	 */
275662306a36Sopenharmony_ci	combine_consts = operand_flags & operand2_flags & HIST_FIELD_FL_CONST;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	flags |= combine_consts ? HIST_FIELD_FL_CONST : HIST_FIELD_FL_EXPR;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	flags |= operand1->flags &
276162306a36Sopenharmony_ci		(HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_ci	expr = create_hist_field(hist_data, NULL, flags, var_name);
276462306a36Sopenharmony_ci	if (!expr) {
276562306a36Sopenharmony_ci		ret = -ENOMEM;
276662306a36Sopenharmony_ci		goto free_operands;
276762306a36Sopenharmony_ci	}
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	operand1->read_once = true;
277062306a36Sopenharmony_ci	operand2->read_once = true;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	/* The operands are now owned and free'd by 'expr' */
277362306a36Sopenharmony_ci	expr->operands[0] = operand1;
277462306a36Sopenharmony_ci	expr->operands[1] = operand2;
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	if (field_op == FIELD_OP_DIV &&
277762306a36Sopenharmony_ci			operand2_flags & HIST_FIELD_FL_CONST) {
277862306a36Sopenharmony_ci		u64 divisor = var2 ? var2->constant : operand2->constant;
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci		if (!divisor) {
278162306a36Sopenharmony_ci			hist_err(file->tr, HIST_ERR_DIVISION_BY_ZERO, errpos(str));
278262306a36Sopenharmony_ci			ret = -EDOM;
278362306a36Sopenharmony_ci			goto free_expr;
278462306a36Sopenharmony_ci		}
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci		/*
278762306a36Sopenharmony_ci		 * Copy the divisor here so we don't have to look it up
278862306a36Sopenharmony_ci		 * later if this is a var ref
278962306a36Sopenharmony_ci		 */
279062306a36Sopenharmony_ci		operand2->constant = divisor;
279162306a36Sopenharmony_ci		op_fn = hist_field_get_div_fn(operand2);
279262306a36Sopenharmony_ci	}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	expr->fn_num = op_fn;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	if (combine_consts) {
279762306a36Sopenharmony_ci		if (var1)
279862306a36Sopenharmony_ci			expr->operands[0] = var1;
279962306a36Sopenharmony_ci		if (var2)
280062306a36Sopenharmony_ci			expr->operands[1] = var2;
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci		expr->constant = hist_fn_call(expr, NULL, NULL, NULL, NULL);
280362306a36Sopenharmony_ci		expr->fn_num = HIST_FIELD_FN_CONST;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci		expr->operands[0] = NULL;
280662306a36Sopenharmony_ci		expr->operands[1] = NULL;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci		/*
280962306a36Sopenharmony_ci		 * var refs won't be destroyed immediately
281062306a36Sopenharmony_ci		 * See: destroy_hist_field()
281162306a36Sopenharmony_ci		 */
281262306a36Sopenharmony_ci		destroy_hist_field(operand2, 0);
281362306a36Sopenharmony_ci		destroy_hist_field(operand1, 0);
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci		expr->name = expr_str(expr, 0);
281662306a36Sopenharmony_ci	} else {
281762306a36Sopenharmony_ci		/* The operand sizes should be the same, so just pick one */
281862306a36Sopenharmony_ci		expr->size = operand1->size;
281962306a36Sopenharmony_ci		expr->is_signed = operand1->is_signed;
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci		expr->operator = field_op;
282262306a36Sopenharmony_ci		expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
282362306a36Sopenharmony_ci		if (!expr->type) {
282462306a36Sopenharmony_ci			ret = -ENOMEM;
282562306a36Sopenharmony_ci			goto free_expr;
282662306a36Sopenharmony_ci		}
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci		expr->name = expr_str(expr, 0);
282962306a36Sopenharmony_ci	}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci	return expr;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_cifree_operands:
283462306a36Sopenharmony_ci	destroy_hist_field(operand2, 0);
283562306a36Sopenharmony_cifree_op1:
283662306a36Sopenharmony_ci	destroy_hist_field(operand1, 0);
283762306a36Sopenharmony_ci	return ERR_PTR(ret);
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_cifree_expr:
284062306a36Sopenharmony_ci	destroy_hist_field(expr, 0);
284162306a36Sopenharmony_ci	return ERR_PTR(ret);
284262306a36Sopenharmony_ci}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_cistatic char *find_trigger_filter(struct hist_trigger_data *hist_data,
284562306a36Sopenharmony_ci				 struct trace_event_file *file)
284662306a36Sopenharmony_ci{
284762306a36Sopenharmony_ci	struct event_trigger_data *test;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
285262306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
285362306a36Sopenharmony_ci			if (test->private_data == hist_data)
285462306a36Sopenharmony_ci				return test->filter_str;
285562306a36Sopenharmony_ci		}
285662306a36Sopenharmony_ci	}
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	return NULL;
285962306a36Sopenharmony_ci}
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_cistatic struct event_command trigger_hist_cmd;
286262306a36Sopenharmony_cistatic int event_hist_trigger_parse(struct event_command *cmd_ops,
286362306a36Sopenharmony_ci				    struct trace_event_file *file,
286462306a36Sopenharmony_ci				    char *glob, char *cmd,
286562306a36Sopenharmony_ci				    char *param_and_filter);
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_cistatic bool compatible_keys(struct hist_trigger_data *target_hist_data,
286862306a36Sopenharmony_ci			    struct hist_trigger_data *hist_data,
286962306a36Sopenharmony_ci			    unsigned int n_keys)
287062306a36Sopenharmony_ci{
287162306a36Sopenharmony_ci	struct hist_field *target_hist_field, *hist_field;
287262306a36Sopenharmony_ci	unsigned int n, i, j;
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	if (hist_data->n_fields - hist_data->n_vals != n_keys)
287562306a36Sopenharmony_ci		return false;
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci	i = hist_data->n_vals;
287862306a36Sopenharmony_ci	j = target_hist_data->n_vals;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	for (n = 0; n < n_keys; n++) {
288162306a36Sopenharmony_ci		hist_field = hist_data->fields[i + n];
288262306a36Sopenharmony_ci		target_hist_field = target_hist_data->fields[j + n];
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci		if (strcmp(hist_field->type, target_hist_field->type) != 0)
288562306a36Sopenharmony_ci			return false;
288662306a36Sopenharmony_ci		if (hist_field->size != target_hist_field->size)
288762306a36Sopenharmony_ci			return false;
288862306a36Sopenharmony_ci		if (hist_field->is_signed != target_hist_field->is_signed)
288962306a36Sopenharmony_ci			return false;
289062306a36Sopenharmony_ci	}
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	return true;
289362306a36Sopenharmony_ci}
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_cistatic struct hist_trigger_data *
289662306a36Sopenharmony_cifind_compatible_hist(struct hist_trigger_data *target_hist_data,
289762306a36Sopenharmony_ci		     struct trace_event_file *file)
289862306a36Sopenharmony_ci{
289962306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
290062306a36Sopenharmony_ci	struct event_trigger_data *test;
290162306a36Sopenharmony_ci	unsigned int n_keys;
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci	n_keys = target_hist_data->n_fields - target_hist_data->n_vals;
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
290862306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
290962306a36Sopenharmony_ci			hist_data = test->private_data;
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci			if (compatible_keys(target_hist_data, hist_data, n_keys))
291262306a36Sopenharmony_ci				return hist_data;
291362306a36Sopenharmony_ci		}
291462306a36Sopenharmony_ci	}
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	return NULL;
291762306a36Sopenharmony_ci}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_cistatic struct trace_event_file *event_file(struct trace_array *tr,
292062306a36Sopenharmony_ci					   char *system, char *event_name)
292162306a36Sopenharmony_ci{
292262306a36Sopenharmony_ci	struct trace_event_file *file;
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	file = __find_event_file(tr, system, event_name);
292562306a36Sopenharmony_ci	if (!file)
292662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	return file;
292962306a36Sopenharmony_ci}
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_cistatic struct hist_field *
293262306a36Sopenharmony_cifind_synthetic_field_var(struct hist_trigger_data *target_hist_data,
293362306a36Sopenharmony_ci			 char *system, char *event_name, char *field_name)
293462306a36Sopenharmony_ci{
293562306a36Sopenharmony_ci	struct hist_field *event_var;
293662306a36Sopenharmony_ci	char *synthetic_name;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	synthetic_name = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
293962306a36Sopenharmony_ci	if (!synthetic_name)
294062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	strcpy(synthetic_name, "synthetic_");
294362306a36Sopenharmony_ci	strcat(synthetic_name, field_name);
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_ci	event_var = find_event_var(target_hist_data, system, event_name, synthetic_name);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	kfree(synthetic_name);
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_ci	return event_var;
295062306a36Sopenharmony_ci}
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci/**
295362306a36Sopenharmony_ci * create_field_var_hist - Automatically create a histogram and var for a field
295462306a36Sopenharmony_ci * @target_hist_data: The target hist trigger
295562306a36Sopenharmony_ci * @subsys_name: Optional subsystem name
295662306a36Sopenharmony_ci * @event_name: Optional event name
295762306a36Sopenharmony_ci * @field_name: The name of the field (and the resulting variable)
295862306a36Sopenharmony_ci *
295962306a36Sopenharmony_ci * Hist trigger actions fetch data from variables, not directly from
296062306a36Sopenharmony_ci * events.  However, for convenience, users are allowed to directly
296162306a36Sopenharmony_ci * specify an event field in an action, which will be automatically
296262306a36Sopenharmony_ci * converted into a variable on their behalf.
296362306a36Sopenharmony_ci *
296462306a36Sopenharmony_ci * If a user specifies a field on an event that isn't the event the
296562306a36Sopenharmony_ci * histogram currently being defined (the target event histogram), the
296662306a36Sopenharmony_ci * only way that can be accomplished is if a new hist trigger is
296762306a36Sopenharmony_ci * created and the field variable defined on that.
296862306a36Sopenharmony_ci *
296962306a36Sopenharmony_ci * This function creates a new histogram compatible with the target
297062306a36Sopenharmony_ci * event (meaning a histogram with the same key as the target
297162306a36Sopenharmony_ci * histogram), and creates a variable for the specified field, but
297262306a36Sopenharmony_ci * with 'synthetic_' prepended to the variable name in order to avoid
297362306a36Sopenharmony_ci * collision with normal field variables.
297462306a36Sopenharmony_ci *
297562306a36Sopenharmony_ci * Return: The variable created for the field.
297662306a36Sopenharmony_ci */
297762306a36Sopenharmony_cistatic struct hist_field *
297862306a36Sopenharmony_cicreate_field_var_hist(struct hist_trigger_data *target_hist_data,
297962306a36Sopenharmony_ci		      char *subsys_name, char *event_name, char *field_name)
298062306a36Sopenharmony_ci{
298162306a36Sopenharmony_ci	struct trace_array *tr = target_hist_data->event_file->tr;
298262306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
298362306a36Sopenharmony_ci	unsigned int i, n, first = true;
298462306a36Sopenharmony_ci	struct field_var_hist *var_hist;
298562306a36Sopenharmony_ci	struct trace_event_file *file;
298662306a36Sopenharmony_ci	struct hist_field *key_field;
298762306a36Sopenharmony_ci	struct hist_field *event_var;
298862306a36Sopenharmony_ci	char *saved_filter;
298962306a36Sopenharmony_ci	char *cmd;
299062306a36Sopenharmony_ci	int ret;
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) {
299362306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_TOO_MANY_FIELD_VARS, errpos(field_name));
299462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
299562306a36Sopenharmony_ci	}
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	file = event_file(tr, subsys_name, event_name);
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	if (IS_ERR(file)) {
300062306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_EVENT_FILE_NOT_FOUND, errpos(field_name));
300162306a36Sopenharmony_ci		ret = PTR_ERR(file);
300262306a36Sopenharmony_ci		return ERR_PTR(ret);
300362306a36Sopenharmony_ci	}
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	/*
300662306a36Sopenharmony_ci	 * Look for a histogram compatible with target.  We'll use the
300762306a36Sopenharmony_ci	 * found histogram specification to create a new matching
300862306a36Sopenharmony_ci	 * histogram with our variable on it.  target_hist_data is not
300962306a36Sopenharmony_ci	 * yet a registered histogram so we can't use that.
301062306a36Sopenharmony_ci	 */
301162306a36Sopenharmony_ci	hist_data = find_compatible_hist(target_hist_data, file);
301262306a36Sopenharmony_ci	if (!hist_data) {
301362306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_HIST_NOT_FOUND, errpos(field_name));
301462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
301562306a36Sopenharmony_ci	}
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	/* See if a synthetic field variable has already been created */
301862306a36Sopenharmony_ci	event_var = find_synthetic_field_var(target_hist_data, subsys_name,
301962306a36Sopenharmony_ci					     event_name, field_name);
302062306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(event_var))
302162306a36Sopenharmony_ci		return event_var;
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci	var_hist = kzalloc(sizeof(*var_hist), GFP_KERNEL);
302462306a36Sopenharmony_ci	if (!var_hist)
302562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	cmd = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
302862306a36Sopenharmony_ci	if (!cmd) {
302962306a36Sopenharmony_ci		kfree(var_hist);
303062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
303162306a36Sopenharmony_ci	}
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	/* Use the same keys as the compatible histogram */
303462306a36Sopenharmony_ci	strcat(cmd, "keys=");
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci	for_each_hist_key_field(i, hist_data) {
303762306a36Sopenharmony_ci		key_field = hist_data->fields[i];
303862306a36Sopenharmony_ci		if (!first)
303962306a36Sopenharmony_ci			strcat(cmd, ",");
304062306a36Sopenharmony_ci		strcat(cmd, key_field->field->name);
304162306a36Sopenharmony_ci		first = false;
304262306a36Sopenharmony_ci	}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci	/* Create the synthetic field variable specification */
304562306a36Sopenharmony_ci	strcat(cmd, ":synthetic_");
304662306a36Sopenharmony_ci	strcat(cmd, field_name);
304762306a36Sopenharmony_ci	strcat(cmd, "=");
304862306a36Sopenharmony_ci	strcat(cmd, field_name);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	/* Use the same filter as the compatible histogram */
305162306a36Sopenharmony_ci	saved_filter = find_trigger_filter(hist_data, file);
305262306a36Sopenharmony_ci	if (saved_filter) {
305362306a36Sopenharmony_ci		strcat(cmd, " if ");
305462306a36Sopenharmony_ci		strcat(cmd, saved_filter);
305562306a36Sopenharmony_ci	}
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci	var_hist->cmd = kstrdup(cmd, GFP_KERNEL);
305862306a36Sopenharmony_ci	if (!var_hist->cmd) {
305962306a36Sopenharmony_ci		kfree(cmd);
306062306a36Sopenharmony_ci		kfree(var_hist);
306162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
306262306a36Sopenharmony_ci	}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	/* Save the compatible histogram information */
306562306a36Sopenharmony_ci	var_hist->hist_data = hist_data;
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	/* Create the new histogram with our variable */
306862306a36Sopenharmony_ci	ret = event_hist_trigger_parse(&trigger_hist_cmd, file,
306962306a36Sopenharmony_ci				       "", "hist", cmd);
307062306a36Sopenharmony_ci	if (ret) {
307162306a36Sopenharmony_ci		kfree(cmd);
307262306a36Sopenharmony_ci		kfree(var_hist->cmd);
307362306a36Sopenharmony_ci		kfree(var_hist);
307462306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_HIST_CREATE_FAIL, errpos(field_name));
307562306a36Sopenharmony_ci		return ERR_PTR(ret);
307662306a36Sopenharmony_ci	}
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	kfree(cmd);
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	/* If we can't find the variable, something went wrong */
308162306a36Sopenharmony_ci	event_var = find_synthetic_field_var(target_hist_data, subsys_name,
308262306a36Sopenharmony_ci					     event_name, field_name);
308362306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(event_var)) {
308462306a36Sopenharmony_ci		kfree(var_hist->cmd);
308562306a36Sopenharmony_ci		kfree(var_hist);
308662306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_SYNTH_VAR_NOT_FOUND, errpos(field_name));
308762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
308862306a36Sopenharmony_ci	}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	n = target_hist_data->n_field_var_hists;
309162306a36Sopenharmony_ci	target_hist_data->field_var_hists[n] = var_hist;
309262306a36Sopenharmony_ci	target_hist_data->n_field_var_hists++;
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_ci	return event_var;
309562306a36Sopenharmony_ci}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_cistatic struct hist_field *
309862306a36Sopenharmony_cifind_target_event_var(struct hist_trigger_data *hist_data,
309962306a36Sopenharmony_ci		      char *subsys_name, char *event_name, char *var_name)
310062306a36Sopenharmony_ci{
310162306a36Sopenharmony_ci	struct trace_event_file *file = hist_data->event_file;
310262306a36Sopenharmony_ci	struct hist_field *hist_field = NULL;
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	if (subsys_name) {
310562306a36Sopenharmony_ci		struct trace_event_call *call;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci		if (!event_name)
310862306a36Sopenharmony_ci			return NULL;
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci		call = file->event_call;
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci		if (strcmp(subsys_name, call->class->system) != 0)
311362306a36Sopenharmony_ci			return NULL;
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci		if (strcmp(event_name, trace_event_name(call)) != 0)
311662306a36Sopenharmony_ci			return NULL;
311762306a36Sopenharmony_ci	}
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	hist_field = find_var_field(hist_data, var_name);
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	return hist_field;
312262306a36Sopenharmony_ci}
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_cistatic inline void __update_field_vars(struct tracing_map_elt *elt,
312562306a36Sopenharmony_ci				       struct trace_buffer *buffer,
312662306a36Sopenharmony_ci				       struct ring_buffer_event *rbe,
312762306a36Sopenharmony_ci				       void *rec,
312862306a36Sopenharmony_ci				       struct field_var **field_vars,
312962306a36Sopenharmony_ci				       unsigned int n_field_vars,
313062306a36Sopenharmony_ci				       unsigned int field_var_str_start)
313162306a36Sopenharmony_ci{
313262306a36Sopenharmony_ci	struct hist_elt_data *elt_data = elt->private_data;
313362306a36Sopenharmony_ci	unsigned int i, j, var_idx;
313462306a36Sopenharmony_ci	u64 var_val;
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	/* Make sure stacktrace can fit in the string variable length */
313762306a36Sopenharmony_ci	BUILD_BUG_ON((HIST_STACKTRACE_DEPTH + 1) * sizeof(long) >= STR_VAR_LEN_MAX);
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	for (i = 0, j = field_var_str_start; i < n_field_vars; i++) {
314062306a36Sopenharmony_ci		struct field_var *field_var = field_vars[i];
314162306a36Sopenharmony_ci		struct hist_field *var = field_var->var;
314262306a36Sopenharmony_ci		struct hist_field *val = field_var->val;
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci		var_val = hist_fn_call(val, elt, buffer, rbe, rec);
314562306a36Sopenharmony_ci		var_idx = var->var.idx;
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci		if (val->flags & (HIST_FIELD_FL_STRING |
314862306a36Sopenharmony_ci				  HIST_FIELD_FL_STACKTRACE)) {
314962306a36Sopenharmony_ci			char *str = elt_data->field_var_str[j++];
315062306a36Sopenharmony_ci			char *val_str = (char *)(uintptr_t)var_val;
315162306a36Sopenharmony_ci			unsigned int size;
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci			if (val->flags & HIST_FIELD_FL_STRING) {
315462306a36Sopenharmony_ci				size = min(val->size, STR_VAR_LEN_MAX);
315562306a36Sopenharmony_ci				strscpy(str, val_str, size);
315662306a36Sopenharmony_ci			} else {
315762306a36Sopenharmony_ci				char *stack_start = str + sizeof(unsigned long);
315862306a36Sopenharmony_ci				int e;
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci				e = stack_trace_save((void *)stack_start,
316162306a36Sopenharmony_ci						     HIST_STACKTRACE_DEPTH,
316262306a36Sopenharmony_ci						     HIST_STACKTRACE_SKIP);
316362306a36Sopenharmony_ci				if (e < HIST_STACKTRACE_DEPTH - 1)
316462306a36Sopenharmony_ci					((unsigned long *)stack_start)[e] = 0;
316562306a36Sopenharmony_ci				*((unsigned long *)str) = e;
316662306a36Sopenharmony_ci			}
316762306a36Sopenharmony_ci			var_val = (u64)(uintptr_t)str;
316862306a36Sopenharmony_ci		}
316962306a36Sopenharmony_ci		tracing_map_set_var(elt, var_idx, var_val);
317062306a36Sopenharmony_ci	}
317162306a36Sopenharmony_ci}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_cistatic void update_field_vars(struct hist_trigger_data *hist_data,
317462306a36Sopenharmony_ci			      struct tracing_map_elt *elt,
317562306a36Sopenharmony_ci			      struct trace_buffer *buffer,
317662306a36Sopenharmony_ci			      struct ring_buffer_event *rbe,
317762306a36Sopenharmony_ci			      void *rec)
317862306a36Sopenharmony_ci{
317962306a36Sopenharmony_ci	__update_field_vars(elt, buffer, rbe, rec, hist_data->field_vars,
318062306a36Sopenharmony_ci			    hist_data->n_field_vars, 0);
318162306a36Sopenharmony_ci}
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_cistatic void save_track_data_vars(struct hist_trigger_data *hist_data,
318462306a36Sopenharmony_ci				 struct tracing_map_elt *elt,
318562306a36Sopenharmony_ci				 struct trace_buffer *buffer,  void *rec,
318662306a36Sopenharmony_ci				 struct ring_buffer_event *rbe, void *key,
318762306a36Sopenharmony_ci				 struct action_data *data, u64 *var_ref_vals)
318862306a36Sopenharmony_ci{
318962306a36Sopenharmony_ci	__update_field_vars(elt, buffer, rbe, rec, hist_data->save_vars,
319062306a36Sopenharmony_ci			    hist_data->n_save_vars, hist_data->n_field_var_str);
319162306a36Sopenharmony_ci}
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_cistatic struct hist_field *create_var(struct hist_trigger_data *hist_data,
319462306a36Sopenharmony_ci				     struct trace_event_file *file,
319562306a36Sopenharmony_ci				     char *name, int size, const char *type)
319662306a36Sopenharmony_ci{
319762306a36Sopenharmony_ci	struct hist_field *var;
319862306a36Sopenharmony_ci	int idx;
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	if (find_var(hist_data, file, name) && !hist_data->remove) {
320162306a36Sopenharmony_ci		var = ERR_PTR(-EINVAL);
320262306a36Sopenharmony_ci		goto out;
320362306a36Sopenharmony_ci	}
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci	var = kzalloc(sizeof(struct hist_field), GFP_KERNEL);
320662306a36Sopenharmony_ci	if (!var) {
320762306a36Sopenharmony_ci		var = ERR_PTR(-ENOMEM);
320862306a36Sopenharmony_ci		goto out;
320962306a36Sopenharmony_ci	}
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	idx = tracing_map_add_var(hist_data->map);
321262306a36Sopenharmony_ci	if (idx < 0) {
321362306a36Sopenharmony_ci		kfree(var);
321462306a36Sopenharmony_ci		var = ERR_PTR(-EINVAL);
321562306a36Sopenharmony_ci		goto out;
321662306a36Sopenharmony_ci	}
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	var->ref = 1;
321962306a36Sopenharmony_ci	var->flags = HIST_FIELD_FL_VAR;
322062306a36Sopenharmony_ci	var->var.idx = idx;
322162306a36Sopenharmony_ci	var->var.hist_data = var->hist_data = hist_data;
322262306a36Sopenharmony_ci	var->size = size;
322362306a36Sopenharmony_ci	var->var.name = kstrdup(name, GFP_KERNEL);
322462306a36Sopenharmony_ci	var->type = kstrdup_const(type, GFP_KERNEL);
322562306a36Sopenharmony_ci	if (!var->var.name || !var->type) {
322662306a36Sopenharmony_ci		kfree_const(var->type);
322762306a36Sopenharmony_ci		kfree(var->var.name);
322862306a36Sopenharmony_ci		kfree(var);
322962306a36Sopenharmony_ci		var = ERR_PTR(-ENOMEM);
323062306a36Sopenharmony_ci	}
323162306a36Sopenharmony_ci out:
323262306a36Sopenharmony_ci	return var;
323362306a36Sopenharmony_ci}
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_cistatic struct field_var *create_field_var(struct hist_trigger_data *hist_data,
323662306a36Sopenharmony_ci					  struct trace_event_file *file,
323762306a36Sopenharmony_ci					  char *field_name)
323862306a36Sopenharmony_ci{
323962306a36Sopenharmony_ci	struct hist_field *val = NULL, *var = NULL;
324062306a36Sopenharmony_ci	unsigned long flags = HIST_FIELD_FL_VAR;
324162306a36Sopenharmony_ci	struct trace_array *tr = file->tr;
324262306a36Sopenharmony_ci	struct field_var *field_var;
324362306a36Sopenharmony_ci	int ret = 0;
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) {
324662306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_TOO_MANY_FIELD_VARS, errpos(field_name));
324762306a36Sopenharmony_ci		ret = -EINVAL;
324862306a36Sopenharmony_ci		goto err;
324962306a36Sopenharmony_ci	}
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	val = parse_atom(hist_data, file, field_name, &flags, NULL);
325262306a36Sopenharmony_ci	if (IS_ERR(val)) {
325362306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_FIELD_VAR_PARSE_FAIL, errpos(field_name));
325462306a36Sopenharmony_ci		ret = PTR_ERR(val);
325562306a36Sopenharmony_ci		goto err;
325662306a36Sopenharmony_ci	}
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	var = create_var(hist_data, file, field_name, val->size, val->type);
325962306a36Sopenharmony_ci	if (IS_ERR(var)) {
326062306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_VAR_CREATE_FIND_FAIL, errpos(field_name));
326162306a36Sopenharmony_ci		kfree(val);
326262306a36Sopenharmony_ci		ret = PTR_ERR(var);
326362306a36Sopenharmony_ci		goto err;
326462306a36Sopenharmony_ci	}
326562306a36Sopenharmony_ci
326662306a36Sopenharmony_ci	field_var = kzalloc(sizeof(struct field_var), GFP_KERNEL);
326762306a36Sopenharmony_ci	if (!field_var) {
326862306a36Sopenharmony_ci		kfree(val);
326962306a36Sopenharmony_ci		kfree(var);
327062306a36Sopenharmony_ci		ret =  -ENOMEM;
327162306a36Sopenharmony_ci		goto err;
327262306a36Sopenharmony_ci	}
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	field_var->var = var;
327562306a36Sopenharmony_ci	field_var->val = val;
327662306a36Sopenharmony_ci out:
327762306a36Sopenharmony_ci	return field_var;
327862306a36Sopenharmony_ci err:
327962306a36Sopenharmony_ci	field_var = ERR_PTR(ret);
328062306a36Sopenharmony_ci	goto out;
328162306a36Sopenharmony_ci}
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci/**
328462306a36Sopenharmony_ci * create_target_field_var - Automatically create a variable for a field
328562306a36Sopenharmony_ci * @target_hist_data: The target hist trigger
328662306a36Sopenharmony_ci * @subsys_name: Optional subsystem name
328762306a36Sopenharmony_ci * @event_name: Optional event name
328862306a36Sopenharmony_ci * @var_name: The name of the field (and the resulting variable)
328962306a36Sopenharmony_ci *
329062306a36Sopenharmony_ci * Hist trigger actions fetch data from variables, not directly from
329162306a36Sopenharmony_ci * events.  However, for convenience, users are allowed to directly
329262306a36Sopenharmony_ci * specify an event field in an action, which will be automatically
329362306a36Sopenharmony_ci * converted into a variable on their behalf.
329462306a36Sopenharmony_ci *
329562306a36Sopenharmony_ci * This function creates a field variable with the name var_name on
329662306a36Sopenharmony_ci * the hist trigger currently being defined on the target event.  If
329762306a36Sopenharmony_ci * subsys_name and event_name are specified, this function simply
329862306a36Sopenharmony_ci * verifies that they do in fact match the target event subsystem and
329962306a36Sopenharmony_ci * event name.
330062306a36Sopenharmony_ci *
330162306a36Sopenharmony_ci * Return: The variable created for the field.
330262306a36Sopenharmony_ci */
330362306a36Sopenharmony_cistatic struct field_var *
330462306a36Sopenharmony_cicreate_target_field_var(struct hist_trigger_data *target_hist_data,
330562306a36Sopenharmony_ci			char *subsys_name, char *event_name, char *var_name)
330662306a36Sopenharmony_ci{
330762306a36Sopenharmony_ci	struct trace_event_file *file = target_hist_data->event_file;
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	if (subsys_name) {
331062306a36Sopenharmony_ci		struct trace_event_call *call;
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci		if (!event_name)
331362306a36Sopenharmony_ci			return NULL;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci		call = file->event_call;
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci		if (strcmp(subsys_name, call->class->system) != 0)
331862306a36Sopenharmony_ci			return NULL;
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci		if (strcmp(event_name, trace_event_name(call)) != 0)
332162306a36Sopenharmony_ci			return NULL;
332262306a36Sopenharmony_ci	}
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ci	return create_field_var(target_hist_data, file, var_name);
332562306a36Sopenharmony_ci}
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_cistatic bool check_track_val_max(u64 track_val, u64 var_val)
332862306a36Sopenharmony_ci{
332962306a36Sopenharmony_ci	if (var_val <= track_val)
333062306a36Sopenharmony_ci		return false;
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci	return true;
333362306a36Sopenharmony_ci}
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_cistatic bool check_track_val_changed(u64 track_val, u64 var_val)
333662306a36Sopenharmony_ci{
333762306a36Sopenharmony_ci	if (var_val == track_val)
333862306a36Sopenharmony_ci		return false;
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	return true;
334162306a36Sopenharmony_ci}
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_cistatic u64 get_track_val(struct hist_trigger_data *hist_data,
334462306a36Sopenharmony_ci			 struct tracing_map_elt *elt,
334562306a36Sopenharmony_ci			 struct action_data *data)
334662306a36Sopenharmony_ci{
334762306a36Sopenharmony_ci	unsigned int track_var_idx = data->track_data.track_var->var.idx;
334862306a36Sopenharmony_ci	u64 track_val;
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	track_val = tracing_map_read_var(elt, track_var_idx);
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	return track_val;
335362306a36Sopenharmony_ci}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_cistatic void save_track_val(struct hist_trigger_data *hist_data,
335662306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
335762306a36Sopenharmony_ci			   struct action_data *data, u64 var_val)
335862306a36Sopenharmony_ci{
335962306a36Sopenharmony_ci	unsigned int track_var_idx = data->track_data.track_var->var.idx;
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	tracing_map_set_var(elt, track_var_idx, var_val);
336262306a36Sopenharmony_ci}
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_cistatic void save_track_data(struct hist_trigger_data *hist_data,
336562306a36Sopenharmony_ci			    struct tracing_map_elt *elt,
336662306a36Sopenharmony_ci			    struct trace_buffer *buffer, void *rec,
336762306a36Sopenharmony_ci			    struct ring_buffer_event *rbe, void *key,
336862306a36Sopenharmony_ci			    struct action_data *data, u64 *var_ref_vals)
336962306a36Sopenharmony_ci{
337062306a36Sopenharmony_ci	if (data->track_data.save_data)
337162306a36Sopenharmony_ci		data->track_data.save_data(hist_data, elt, buffer, rec, rbe,
337262306a36Sopenharmony_ci					   key, data, var_ref_vals);
337362306a36Sopenharmony_ci}
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_cistatic bool check_track_val(struct tracing_map_elt *elt,
337662306a36Sopenharmony_ci			    struct action_data *data,
337762306a36Sopenharmony_ci			    u64 var_val)
337862306a36Sopenharmony_ci{
337962306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
338062306a36Sopenharmony_ci	u64 track_val;
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	hist_data = data->track_data.track_var->hist_data;
338362306a36Sopenharmony_ci	track_val = get_track_val(hist_data, elt, data);
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci	return data->track_data.check_val(track_val, var_val);
338662306a36Sopenharmony_ci}
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ci#ifdef CONFIG_TRACER_SNAPSHOT
338962306a36Sopenharmony_cistatic bool cond_snapshot_update(struct trace_array *tr, void *cond_data)
339062306a36Sopenharmony_ci{
339162306a36Sopenharmony_ci	/* called with tr->max_lock held */
339262306a36Sopenharmony_ci	struct track_data *track_data = tr->cond_snapshot->cond_data;
339362306a36Sopenharmony_ci	struct hist_elt_data *elt_data, *track_elt_data;
339462306a36Sopenharmony_ci	struct snapshot_context *context = cond_data;
339562306a36Sopenharmony_ci	struct action_data *action;
339662306a36Sopenharmony_ci	u64 track_val;
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci	if (!track_data)
339962306a36Sopenharmony_ci		return false;
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci	action = track_data->action_data;
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci	track_val = get_track_val(track_data->hist_data, context->elt,
340462306a36Sopenharmony_ci				  track_data->action_data);
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_ci	if (!action->track_data.check_val(track_data->track_val, track_val))
340762306a36Sopenharmony_ci		return false;
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_ci	track_data->track_val = track_val;
341062306a36Sopenharmony_ci	memcpy(track_data->key, context->key, track_data->key_len);
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci	elt_data = context->elt->private_data;
341362306a36Sopenharmony_ci	track_elt_data = track_data->elt.private_data;
341462306a36Sopenharmony_ci	if (elt_data->comm)
341562306a36Sopenharmony_ci		strncpy(track_elt_data->comm, elt_data->comm, TASK_COMM_LEN);
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	track_data->updated = true;
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	return true;
342062306a36Sopenharmony_ci}
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_cistatic void save_track_data_snapshot(struct hist_trigger_data *hist_data,
342362306a36Sopenharmony_ci				     struct tracing_map_elt *elt,
342462306a36Sopenharmony_ci				     struct trace_buffer *buffer, void *rec,
342562306a36Sopenharmony_ci				     struct ring_buffer_event *rbe, void *key,
342662306a36Sopenharmony_ci				     struct action_data *data,
342762306a36Sopenharmony_ci				     u64 *var_ref_vals)
342862306a36Sopenharmony_ci{
342962306a36Sopenharmony_ci	struct trace_event_file *file = hist_data->event_file;
343062306a36Sopenharmony_ci	struct snapshot_context context;
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_ci	context.elt = elt;
343362306a36Sopenharmony_ci	context.key = key;
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci	tracing_snapshot_cond(file->tr, &context);
343662306a36Sopenharmony_ci}
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_cistatic void hist_trigger_print_key(struct seq_file *m,
343962306a36Sopenharmony_ci				   struct hist_trigger_data *hist_data,
344062306a36Sopenharmony_ci				   void *key,
344162306a36Sopenharmony_ci				   struct tracing_map_elt *elt);
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_cistatic struct action_data *snapshot_action(struct hist_trigger_data *hist_data)
344462306a36Sopenharmony_ci{
344562306a36Sopenharmony_ci	unsigned int i;
344662306a36Sopenharmony_ci
344762306a36Sopenharmony_ci	if (!hist_data->n_actions)
344862306a36Sopenharmony_ci		return NULL;
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
345162306a36Sopenharmony_ci		struct action_data *data = hist_data->actions[i];
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_ci		if (data->action == ACTION_SNAPSHOT)
345462306a36Sopenharmony_ci			return data;
345562306a36Sopenharmony_ci	}
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	return NULL;
345862306a36Sopenharmony_ci}
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_cistatic void track_data_snapshot_print(struct seq_file *m,
346162306a36Sopenharmony_ci				      struct hist_trigger_data *hist_data)
346262306a36Sopenharmony_ci{
346362306a36Sopenharmony_ci	struct trace_event_file *file = hist_data->event_file;
346462306a36Sopenharmony_ci	struct track_data *track_data;
346562306a36Sopenharmony_ci	struct action_data *action;
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_ci	track_data = tracing_cond_snapshot_data(file->tr);
346862306a36Sopenharmony_ci	if (!track_data)
346962306a36Sopenharmony_ci		return;
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	if (!track_data->updated)
347262306a36Sopenharmony_ci		return;
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_ci	action = snapshot_action(hist_data);
347562306a36Sopenharmony_ci	if (!action)
347662306a36Sopenharmony_ci		return;
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	seq_puts(m, "\nSnapshot taken (see tracing/snapshot).  Details:\n");
347962306a36Sopenharmony_ci	seq_printf(m, "\ttriggering value { %s(%s) }: %10llu",
348062306a36Sopenharmony_ci		   action->handler == HANDLER_ONMAX ? "onmax" : "onchange",
348162306a36Sopenharmony_ci		   action->track_data.var_str, track_data->track_val);
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci	seq_puts(m, "\ttriggered by event with key: ");
348462306a36Sopenharmony_ci	hist_trigger_print_key(m, hist_data, track_data->key, &track_data->elt);
348562306a36Sopenharmony_ci	seq_putc(m, '\n');
348662306a36Sopenharmony_ci}
348762306a36Sopenharmony_ci#else
348862306a36Sopenharmony_cistatic bool cond_snapshot_update(struct trace_array *tr, void *cond_data)
348962306a36Sopenharmony_ci{
349062306a36Sopenharmony_ci	return false;
349162306a36Sopenharmony_ci}
349262306a36Sopenharmony_cistatic void save_track_data_snapshot(struct hist_trigger_data *hist_data,
349362306a36Sopenharmony_ci				     struct tracing_map_elt *elt,
349462306a36Sopenharmony_ci				     struct trace_buffer *buffer, void *rec,
349562306a36Sopenharmony_ci				     struct ring_buffer_event *rbe, void *key,
349662306a36Sopenharmony_ci				     struct action_data *data,
349762306a36Sopenharmony_ci				     u64 *var_ref_vals) {}
349862306a36Sopenharmony_cistatic void track_data_snapshot_print(struct seq_file *m,
349962306a36Sopenharmony_ci				      struct hist_trigger_data *hist_data) {}
350062306a36Sopenharmony_ci#endif /* CONFIG_TRACER_SNAPSHOT */
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_cistatic void track_data_print(struct seq_file *m,
350362306a36Sopenharmony_ci			     struct hist_trigger_data *hist_data,
350462306a36Sopenharmony_ci			     struct tracing_map_elt *elt,
350562306a36Sopenharmony_ci			     struct action_data *data)
350662306a36Sopenharmony_ci{
350762306a36Sopenharmony_ci	u64 track_val = get_track_val(hist_data, elt, data);
350862306a36Sopenharmony_ci	unsigned int i, save_var_idx;
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	if (data->handler == HANDLER_ONMAX)
351162306a36Sopenharmony_ci		seq_printf(m, "\n\tmax: %10llu", track_val);
351262306a36Sopenharmony_ci	else if (data->handler == HANDLER_ONCHANGE)
351362306a36Sopenharmony_ci		seq_printf(m, "\n\tchanged: %10llu", track_val);
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	if (data->action == ACTION_SNAPSHOT)
351662306a36Sopenharmony_ci		return;
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_save_vars; i++) {
351962306a36Sopenharmony_ci		struct hist_field *save_val = hist_data->save_vars[i]->val;
352062306a36Sopenharmony_ci		struct hist_field *save_var = hist_data->save_vars[i]->var;
352162306a36Sopenharmony_ci		u64 val;
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci		save_var_idx = save_var->var.idx;
352462306a36Sopenharmony_ci
352562306a36Sopenharmony_ci		val = tracing_map_read_var(elt, save_var_idx);
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci		if (save_val->flags & HIST_FIELD_FL_STRING) {
352862306a36Sopenharmony_ci			seq_printf(m, "  %s: %-32s", save_var->var.name,
352962306a36Sopenharmony_ci				   (char *)(uintptr_t)(val));
353062306a36Sopenharmony_ci		} else
353162306a36Sopenharmony_ci			seq_printf(m, "  %s: %10llu", save_var->var.name, val);
353262306a36Sopenharmony_ci	}
353362306a36Sopenharmony_ci}
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_cistatic void ontrack_action(struct hist_trigger_data *hist_data,
353662306a36Sopenharmony_ci			   struct tracing_map_elt *elt,
353762306a36Sopenharmony_ci			   struct trace_buffer *buffer, void *rec,
353862306a36Sopenharmony_ci			   struct ring_buffer_event *rbe, void *key,
353962306a36Sopenharmony_ci			   struct action_data *data, u64 *var_ref_vals)
354062306a36Sopenharmony_ci{
354162306a36Sopenharmony_ci	u64 var_val = var_ref_vals[data->track_data.var_ref->var_ref_idx];
354262306a36Sopenharmony_ci
354362306a36Sopenharmony_ci	if (check_track_val(elt, data, var_val)) {
354462306a36Sopenharmony_ci		save_track_val(hist_data, elt, data, var_val);
354562306a36Sopenharmony_ci		save_track_data(hist_data, elt, buffer, rec, rbe,
354662306a36Sopenharmony_ci				key, data, var_ref_vals);
354762306a36Sopenharmony_ci	}
354862306a36Sopenharmony_ci}
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_cistatic void action_data_destroy(struct action_data *data)
355162306a36Sopenharmony_ci{
355262306a36Sopenharmony_ci	unsigned int i;
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	kfree(data->action_name);
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	for (i = 0; i < data->n_params; i++)
355962306a36Sopenharmony_ci		kfree(data->params[i]);
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	if (data->synth_event)
356262306a36Sopenharmony_ci		data->synth_event->ref--;
356362306a36Sopenharmony_ci
356462306a36Sopenharmony_ci	kfree(data->synth_event_name);
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ci	kfree(data);
356762306a36Sopenharmony_ci}
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_cistatic void track_data_destroy(struct hist_trigger_data *hist_data,
357062306a36Sopenharmony_ci			       struct action_data *data)
357162306a36Sopenharmony_ci{
357262306a36Sopenharmony_ci	struct trace_event_file *file = hist_data->event_file;
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	destroy_hist_field(data->track_data.track_var, 0);
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci	if (data->action == ACTION_SNAPSHOT) {
357762306a36Sopenharmony_ci		struct track_data *track_data;
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci		track_data = tracing_cond_snapshot_data(file->tr);
358062306a36Sopenharmony_ci		if (track_data && track_data->hist_data == hist_data) {
358162306a36Sopenharmony_ci			tracing_snapshot_cond_disable(file->tr);
358262306a36Sopenharmony_ci			track_data_free(track_data);
358362306a36Sopenharmony_ci		}
358462306a36Sopenharmony_ci	}
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci	kfree(data->track_data.var_str);
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci	action_data_destroy(data);
358962306a36Sopenharmony_ci}
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_cistatic int action_create(struct hist_trigger_data *hist_data,
359262306a36Sopenharmony_ci			 struct action_data *data);
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_cistatic int track_data_create(struct hist_trigger_data *hist_data,
359562306a36Sopenharmony_ci			     struct action_data *data)
359662306a36Sopenharmony_ci{
359762306a36Sopenharmony_ci	struct hist_field *var_field, *ref_field, *track_var = NULL;
359862306a36Sopenharmony_ci	struct trace_event_file *file = hist_data->event_file;
359962306a36Sopenharmony_ci	struct trace_array *tr = file->tr;
360062306a36Sopenharmony_ci	char *track_data_var_str;
360162306a36Sopenharmony_ci	int ret = 0;
360262306a36Sopenharmony_ci
360362306a36Sopenharmony_ci	track_data_var_str = data->track_data.var_str;
360462306a36Sopenharmony_ci	if (track_data_var_str[0] != '$') {
360562306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_ONX_NOT_VAR, errpos(track_data_var_str));
360662306a36Sopenharmony_ci		return -EINVAL;
360762306a36Sopenharmony_ci	}
360862306a36Sopenharmony_ci	track_data_var_str++;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	var_field = find_target_event_var(hist_data, NULL, NULL, track_data_var_str);
361162306a36Sopenharmony_ci	if (!var_field) {
361262306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_ONX_VAR_NOT_FOUND, errpos(track_data_var_str));
361362306a36Sopenharmony_ci		return -EINVAL;
361462306a36Sopenharmony_ci	}
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_ci	ref_field = create_var_ref(hist_data, var_field, NULL, NULL);
361762306a36Sopenharmony_ci	if (!ref_field)
361862306a36Sopenharmony_ci		return -ENOMEM;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	data->track_data.var_ref = ref_field;
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	if (data->handler == HANDLER_ONMAX)
362362306a36Sopenharmony_ci		track_var = create_var(hist_data, file, "__max", sizeof(u64), "u64");
362462306a36Sopenharmony_ci	if (IS_ERR(track_var)) {
362562306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_ONX_VAR_CREATE_FAIL, 0);
362662306a36Sopenharmony_ci		ret = PTR_ERR(track_var);
362762306a36Sopenharmony_ci		goto out;
362862306a36Sopenharmony_ci	}
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	if (data->handler == HANDLER_ONCHANGE)
363162306a36Sopenharmony_ci		track_var = create_var(hist_data, file, "__change", sizeof(u64), "u64");
363262306a36Sopenharmony_ci	if (IS_ERR(track_var)) {
363362306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_ONX_VAR_CREATE_FAIL, 0);
363462306a36Sopenharmony_ci		ret = PTR_ERR(track_var);
363562306a36Sopenharmony_ci		goto out;
363662306a36Sopenharmony_ci	}
363762306a36Sopenharmony_ci	data->track_data.track_var = track_var;
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci	ret = action_create(hist_data, data);
364062306a36Sopenharmony_ci out:
364162306a36Sopenharmony_ci	return ret;
364262306a36Sopenharmony_ci}
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_cistatic int parse_action_params(struct trace_array *tr, char *params,
364562306a36Sopenharmony_ci			       struct action_data *data)
364662306a36Sopenharmony_ci{
364762306a36Sopenharmony_ci	char *param, *saved_param;
364862306a36Sopenharmony_ci	bool first_param = true;
364962306a36Sopenharmony_ci	int ret = 0;
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	while (params) {
365262306a36Sopenharmony_ci		if (data->n_params >= SYNTH_FIELDS_MAX) {
365362306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_TOO_MANY_PARAMS, 0);
365462306a36Sopenharmony_ci			ret = -EINVAL;
365562306a36Sopenharmony_ci			goto out;
365662306a36Sopenharmony_ci		}
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci		param = strsep(&params, ",");
365962306a36Sopenharmony_ci		if (!param) {
366062306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_PARAM_NOT_FOUND, 0);
366162306a36Sopenharmony_ci			ret = -EINVAL;
366262306a36Sopenharmony_ci			goto out;
366362306a36Sopenharmony_ci		}
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci		param = strstrip(param);
366662306a36Sopenharmony_ci		if (strlen(param) < 2) {
366762306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_INVALID_PARAM, errpos(param));
366862306a36Sopenharmony_ci			ret = -EINVAL;
366962306a36Sopenharmony_ci			goto out;
367062306a36Sopenharmony_ci		}
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci		saved_param = kstrdup(param, GFP_KERNEL);
367362306a36Sopenharmony_ci		if (!saved_param) {
367462306a36Sopenharmony_ci			ret = -ENOMEM;
367562306a36Sopenharmony_ci			goto out;
367662306a36Sopenharmony_ci		}
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_ci		if (first_param && data->use_trace_keyword) {
367962306a36Sopenharmony_ci			data->synth_event_name = saved_param;
368062306a36Sopenharmony_ci			first_param = false;
368162306a36Sopenharmony_ci			continue;
368262306a36Sopenharmony_ci		}
368362306a36Sopenharmony_ci		first_param = false;
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci		data->params[data->n_params++] = saved_param;
368662306a36Sopenharmony_ci	}
368762306a36Sopenharmony_ci out:
368862306a36Sopenharmony_ci	return ret;
368962306a36Sopenharmony_ci}
369062306a36Sopenharmony_ci
369162306a36Sopenharmony_cistatic int action_parse(struct trace_array *tr, char *str, struct action_data *data,
369262306a36Sopenharmony_ci			enum handler_id handler)
369362306a36Sopenharmony_ci{
369462306a36Sopenharmony_ci	char *action_name;
369562306a36Sopenharmony_ci	int ret = 0;
369662306a36Sopenharmony_ci
369762306a36Sopenharmony_ci	strsep(&str, ".");
369862306a36Sopenharmony_ci	if (!str) {
369962306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_ACTION_NOT_FOUND, 0);
370062306a36Sopenharmony_ci		ret = -EINVAL;
370162306a36Sopenharmony_ci		goto out;
370262306a36Sopenharmony_ci	}
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	action_name = strsep(&str, "(");
370562306a36Sopenharmony_ci	if (!action_name || !str) {
370662306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_ACTION_NOT_FOUND, 0);
370762306a36Sopenharmony_ci		ret = -EINVAL;
370862306a36Sopenharmony_ci		goto out;
370962306a36Sopenharmony_ci	}
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_ci	if (str_has_prefix(action_name, "save")) {
371262306a36Sopenharmony_ci		char *params = strsep(&str, ")");
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci		if (!params) {
371562306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_NO_SAVE_PARAMS, 0);
371662306a36Sopenharmony_ci			ret = -EINVAL;
371762306a36Sopenharmony_ci			goto out;
371862306a36Sopenharmony_ci		}
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci		ret = parse_action_params(tr, params, data);
372162306a36Sopenharmony_ci		if (ret)
372262306a36Sopenharmony_ci			goto out;
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_ci		if (handler == HANDLER_ONMAX)
372562306a36Sopenharmony_ci			data->track_data.check_val = check_track_val_max;
372662306a36Sopenharmony_ci		else if (handler == HANDLER_ONCHANGE)
372762306a36Sopenharmony_ci			data->track_data.check_val = check_track_val_changed;
372862306a36Sopenharmony_ci		else {
372962306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_ACTION_MISMATCH, errpos(action_name));
373062306a36Sopenharmony_ci			ret = -EINVAL;
373162306a36Sopenharmony_ci			goto out;
373262306a36Sopenharmony_ci		}
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci		data->track_data.save_data = save_track_data_vars;
373562306a36Sopenharmony_ci		data->fn = ontrack_action;
373662306a36Sopenharmony_ci		data->action = ACTION_SAVE;
373762306a36Sopenharmony_ci	} else if (str_has_prefix(action_name, "snapshot")) {
373862306a36Sopenharmony_ci		char *params = strsep(&str, ")");
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_ci		if (!str) {
374162306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_NO_CLOSING_PAREN, errpos(params));
374262306a36Sopenharmony_ci			ret = -EINVAL;
374362306a36Sopenharmony_ci			goto out;
374462306a36Sopenharmony_ci		}
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci		if (handler == HANDLER_ONMAX)
374762306a36Sopenharmony_ci			data->track_data.check_val = check_track_val_max;
374862306a36Sopenharmony_ci		else if (handler == HANDLER_ONCHANGE)
374962306a36Sopenharmony_ci			data->track_data.check_val = check_track_val_changed;
375062306a36Sopenharmony_ci		else {
375162306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_ACTION_MISMATCH, errpos(action_name));
375262306a36Sopenharmony_ci			ret = -EINVAL;
375362306a36Sopenharmony_ci			goto out;
375462306a36Sopenharmony_ci		}
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci		data->track_data.save_data = save_track_data_snapshot;
375762306a36Sopenharmony_ci		data->fn = ontrack_action;
375862306a36Sopenharmony_ci		data->action = ACTION_SNAPSHOT;
375962306a36Sopenharmony_ci	} else {
376062306a36Sopenharmony_ci		char *params = strsep(&str, ")");
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci		if (str_has_prefix(action_name, "trace"))
376362306a36Sopenharmony_ci			data->use_trace_keyword = true;
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci		if (params) {
376662306a36Sopenharmony_ci			ret = parse_action_params(tr, params, data);
376762306a36Sopenharmony_ci			if (ret)
376862306a36Sopenharmony_ci				goto out;
376962306a36Sopenharmony_ci		}
377062306a36Sopenharmony_ci
377162306a36Sopenharmony_ci		if (handler == HANDLER_ONMAX)
377262306a36Sopenharmony_ci			data->track_data.check_val = check_track_val_max;
377362306a36Sopenharmony_ci		else if (handler == HANDLER_ONCHANGE)
377462306a36Sopenharmony_ci			data->track_data.check_val = check_track_val_changed;
377562306a36Sopenharmony_ci
377662306a36Sopenharmony_ci		if (handler != HANDLER_ONMATCH) {
377762306a36Sopenharmony_ci			data->track_data.save_data = action_trace;
377862306a36Sopenharmony_ci			data->fn = ontrack_action;
377962306a36Sopenharmony_ci		} else
378062306a36Sopenharmony_ci			data->fn = action_trace;
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci		data->action = ACTION_TRACE;
378362306a36Sopenharmony_ci	}
378462306a36Sopenharmony_ci
378562306a36Sopenharmony_ci	data->action_name = kstrdup(action_name, GFP_KERNEL);
378662306a36Sopenharmony_ci	if (!data->action_name) {
378762306a36Sopenharmony_ci		ret = -ENOMEM;
378862306a36Sopenharmony_ci		goto out;
378962306a36Sopenharmony_ci	}
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci	data->handler = handler;
379262306a36Sopenharmony_ci out:
379362306a36Sopenharmony_ci	return ret;
379462306a36Sopenharmony_ci}
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_cistatic struct action_data *track_data_parse(struct hist_trigger_data *hist_data,
379762306a36Sopenharmony_ci					    char *str, enum handler_id handler)
379862306a36Sopenharmony_ci{
379962306a36Sopenharmony_ci	struct action_data *data;
380062306a36Sopenharmony_ci	int ret = -EINVAL;
380162306a36Sopenharmony_ci	char *var_str;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
380462306a36Sopenharmony_ci	if (!data)
380562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci	var_str = strsep(&str, ")");
380862306a36Sopenharmony_ci	if (!var_str || !str) {
380962306a36Sopenharmony_ci		ret = -EINVAL;
381062306a36Sopenharmony_ci		goto free;
381162306a36Sopenharmony_ci	}
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	data->track_data.var_str = kstrdup(var_str, GFP_KERNEL);
381462306a36Sopenharmony_ci	if (!data->track_data.var_str) {
381562306a36Sopenharmony_ci		ret = -ENOMEM;
381662306a36Sopenharmony_ci		goto free;
381762306a36Sopenharmony_ci	}
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ci	ret = action_parse(hist_data->event_file->tr, str, data, handler);
382062306a36Sopenharmony_ci	if (ret)
382162306a36Sopenharmony_ci		goto free;
382262306a36Sopenharmony_ci out:
382362306a36Sopenharmony_ci	return data;
382462306a36Sopenharmony_ci free:
382562306a36Sopenharmony_ci	track_data_destroy(hist_data, data);
382662306a36Sopenharmony_ci	data = ERR_PTR(ret);
382762306a36Sopenharmony_ci	goto out;
382862306a36Sopenharmony_ci}
382962306a36Sopenharmony_ci
383062306a36Sopenharmony_cistatic void onmatch_destroy(struct action_data *data)
383162306a36Sopenharmony_ci{
383262306a36Sopenharmony_ci	kfree(data->match_data.event);
383362306a36Sopenharmony_ci	kfree(data->match_data.event_system);
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci	action_data_destroy(data);
383662306a36Sopenharmony_ci}
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_cistatic void destroy_field_var(struct field_var *field_var)
383962306a36Sopenharmony_ci{
384062306a36Sopenharmony_ci	if (!field_var)
384162306a36Sopenharmony_ci		return;
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	destroy_hist_field(field_var->var, 0);
384462306a36Sopenharmony_ci	destroy_hist_field(field_var->val, 0);
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_ci	kfree(field_var);
384762306a36Sopenharmony_ci}
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_cistatic void destroy_field_vars(struct hist_trigger_data *hist_data)
385062306a36Sopenharmony_ci{
385162306a36Sopenharmony_ci	unsigned int i;
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_field_vars; i++)
385462306a36Sopenharmony_ci		destroy_field_var(hist_data->field_vars[i]);
385562306a36Sopenharmony_ci
385662306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_save_vars; i++)
385762306a36Sopenharmony_ci		destroy_field_var(hist_data->save_vars[i]);
385862306a36Sopenharmony_ci}
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_cistatic void save_field_var(struct hist_trigger_data *hist_data,
386162306a36Sopenharmony_ci			   struct field_var *field_var)
386262306a36Sopenharmony_ci{
386362306a36Sopenharmony_ci	hist_data->field_vars[hist_data->n_field_vars++] = field_var;
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci	/* Stack traces are saved in the string storage too */
386662306a36Sopenharmony_ci	if (field_var->val->flags & (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
386762306a36Sopenharmony_ci		hist_data->n_field_var_str++;
386862306a36Sopenharmony_ci}
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci
387162306a36Sopenharmony_cistatic int check_synth_field(struct synth_event *event,
387262306a36Sopenharmony_ci			     struct hist_field *hist_field,
387362306a36Sopenharmony_ci			     unsigned int field_pos)
387462306a36Sopenharmony_ci{
387562306a36Sopenharmony_ci	struct synth_field *field;
387662306a36Sopenharmony_ci
387762306a36Sopenharmony_ci	if (field_pos >= event->n_fields)
387862306a36Sopenharmony_ci		return -EINVAL;
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	field = event->fields[field_pos];
388162306a36Sopenharmony_ci
388262306a36Sopenharmony_ci	/*
388362306a36Sopenharmony_ci	 * A dynamic string synth field can accept static or
388462306a36Sopenharmony_ci	 * dynamic. A static string synth field can only accept a
388562306a36Sopenharmony_ci	 * same-sized static string, which is checked for later.
388662306a36Sopenharmony_ci	 */
388762306a36Sopenharmony_ci	if (strstr(hist_field->type, "char[") && field->is_string
388862306a36Sopenharmony_ci	    && field->is_dynamic)
388962306a36Sopenharmony_ci		return 0;
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	if (strstr(hist_field->type, "long[") && field->is_stack)
389262306a36Sopenharmony_ci		return 0;
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	if (strcmp(field->type, hist_field->type) != 0) {
389562306a36Sopenharmony_ci		if (field->size != hist_field->size ||
389662306a36Sopenharmony_ci		    (!field->is_string && field->is_signed != hist_field->is_signed))
389762306a36Sopenharmony_ci			return -EINVAL;
389862306a36Sopenharmony_ci	}
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	return 0;
390162306a36Sopenharmony_ci}
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_cistatic struct hist_field *
390462306a36Sopenharmony_citrace_action_find_var(struct hist_trigger_data *hist_data,
390562306a36Sopenharmony_ci		      struct action_data *data,
390662306a36Sopenharmony_ci		      char *system, char *event, char *var)
390762306a36Sopenharmony_ci{
390862306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
390962306a36Sopenharmony_ci	struct hist_field *hist_field;
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	var++; /* skip '$' */
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci	hist_field = find_target_event_var(hist_data, system, event, var);
391462306a36Sopenharmony_ci	if (!hist_field) {
391562306a36Sopenharmony_ci		if (!system && data->handler == HANDLER_ONMATCH) {
391662306a36Sopenharmony_ci			system = data->match_data.event_system;
391762306a36Sopenharmony_ci			event = data->match_data.event;
391862306a36Sopenharmony_ci		}
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ci		hist_field = find_event_var(hist_data, system, event, var);
392162306a36Sopenharmony_ci	}
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	if (!hist_field)
392462306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_PARAM_NOT_FOUND, errpos(var));
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	return hist_field;
392762306a36Sopenharmony_ci}
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_cistatic struct hist_field *
393062306a36Sopenharmony_citrace_action_create_field_var(struct hist_trigger_data *hist_data,
393162306a36Sopenharmony_ci			      struct action_data *data, char *system,
393262306a36Sopenharmony_ci			      char *event, char *var)
393362306a36Sopenharmony_ci{
393462306a36Sopenharmony_ci	struct hist_field *hist_field = NULL;
393562306a36Sopenharmony_ci	struct field_var *field_var;
393662306a36Sopenharmony_ci
393762306a36Sopenharmony_ci	/*
393862306a36Sopenharmony_ci	 * First try to create a field var on the target event (the
393962306a36Sopenharmony_ci	 * currently being defined).  This will create a variable for
394062306a36Sopenharmony_ci	 * unqualified fields on the target event, or if qualified,
394162306a36Sopenharmony_ci	 * target fields that have qualified names matching the target.
394262306a36Sopenharmony_ci	 */
394362306a36Sopenharmony_ci	field_var = create_target_field_var(hist_data, system, event, var);
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci	if (field_var && !IS_ERR(field_var)) {
394662306a36Sopenharmony_ci		save_field_var(hist_data, field_var);
394762306a36Sopenharmony_ci		hist_field = field_var->var;
394862306a36Sopenharmony_ci	} else {
394962306a36Sopenharmony_ci		field_var = NULL;
395062306a36Sopenharmony_ci		/*
395162306a36Sopenharmony_ci		 * If no explicit system.event is specified, default to
395262306a36Sopenharmony_ci		 * looking for fields on the onmatch(system.event.xxx)
395362306a36Sopenharmony_ci		 * event.
395462306a36Sopenharmony_ci		 */
395562306a36Sopenharmony_ci		if (!system && data->handler == HANDLER_ONMATCH) {
395662306a36Sopenharmony_ci			system = data->match_data.event_system;
395762306a36Sopenharmony_ci			event = data->match_data.event;
395862306a36Sopenharmony_ci		}
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_ci		if (!event)
396162306a36Sopenharmony_ci			goto free;
396262306a36Sopenharmony_ci		/*
396362306a36Sopenharmony_ci		 * At this point, we're looking at a field on another
396462306a36Sopenharmony_ci		 * event.  Because we can't modify a hist trigger on
396562306a36Sopenharmony_ci		 * another event to add a variable for a field, we need
396662306a36Sopenharmony_ci		 * to create a new trigger on that event and create the
396762306a36Sopenharmony_ci		 * variable at the same time.
396862306a36Sopenharmony_ci		 */
396962306a36Sopenharmony_ci		hist_field = create_field_var_hist(hist_data, system, event, var);
397062306a36Sopenharmony_ci		if (IS_ERR(hist_field))
397162306a36Sopenharmony_ci			goto free;
397262306a36Sopenharmony_ci	}
397362306a36Sopenharmony_ci out:
397462306a36Sopenharmony_ci	return hist_field;
397562306a36Sopenharmony_ci free:
397662306a36Sopenharmony_ci	destroy_field_var(field_var);
397762306a36Sopenharmony_ci	hist_field = NULL;
397862306a36Sopenharmony_ci	goto out;
397962306a36Sopenharmony_ci}
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_cistatic int trace_action_create(struct hist_trigger_data *hist_data,
398262306a36Sopenharmony_ci			       struct action_data *data)
398362306a36Sopenharmony_ci{
398462306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
398562306a36Sopenharmony_ci	char *event_name, *param, *system = NULL;
398662306a36Sopenharmony_ci	struct hist_field *hist_field, *var_ref;
398762306a36Sopenharmony_ci	unsigned int i;
398862306a36Sopenharmony_ci	unsigned int field_pos = 0;
398962306a36Sopenharmony_ci	struct synth_event *event;
399062306a36Sopenharmony_ci	char *synth_event_name;
399162306a36Sopenharmony_ci	int var_ref_idx, ret = 0;
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_ci	/* Sanity check to avoid out-of-bound write on 'data->var_ref_idx' */
399662306a36Sopenharmony_ci	if (data->n_params > SYNTH_FIELDS_MAX)
399762306a36Sopenharmony_ci		return -EINVAL;
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_ci	if (data->use_trace_keyword)
400062306a36Sopenharmony_ci		synth_event_name = data->synth_event_name;
400162306a36Sopenharmony_ci	else
400262306a36Sopenharmony_ci		synth_event_name = data->action_name;
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_ci	event = find_synth_event(synth_event_name);
400562306a36Sopenharmony_ci	if (!event) {
400662306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_SYNTH_EVENT_NOT_FOUND, errpos(synth_event_name));
400762306a36Sopenharmony_ci		return -EINVAL;
400862306a36Sopenharmony_ci	}
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	event->ref++;
401162306a36Sopenharmony_ci
401262306a36Sopenharmony_ci	for (i = 0; i < data->n_params; i++) {
401362306a36Sopenharmony_ci		char *p;
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci		p = param = kstrdup(data->params[i], GFP_KERNEL);
401662306a36Sopenharmony_ci		if (!param) {
401762306a36Sopenharmony_ci			ret = -ENOMEM;
401862306a36Sopenharmony_ci			goto err;
401962306a36Sopenharmony_ci		}
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ci		system = strsep(&param, ".");
402262306a36Sopenharmony_ci		if (!param) {
402362306a36Sopenharmony_ci			param = (char *)system;
402462306a36Sopenharmony_ci			system = event_name = NULL;
402562306a36Sopenharmony_ci		} else {
402662306a36Sopenharmony_ci			event_name = strsep(&param, ".");
402762306a36Sopenharmony_ci			if (!param) {
402862306a36Sopenharmony_ci				kfree(p);
402962306a36Sopenharmony_ci				ret = -EINVAL;
403062306a36Sopenharmony_ci				goto err;
403162306a36Sopenharmony_ci			}
403262306a36Sopenharmony_ci		}
403362306a36Sopenharmony_ci
403462306a36Sopenharmony_ci		if (param[0] == '$')
403562306a36Sopenharmony_ci			hist_field = trace_action_find_var(hist_data, data,
403662306a36Sopenharmony_ci							   system, event_name,
403762306a36Sopenharmony_ci							   param);
403862306a36Sopenharmony_ci		else
403962306a36Sopenharmony_ci			hist_field = trace_action_create_field_var(hist_data,
404062306a36Sopenharmony_ci								   data,
404162306a36Sopenharmony_ci								   system,
404262306a36Sopenharmony_ci								   event_name,
404362306a36Sopenharmony_ci								   param);
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci		if (!hist_field) {
404662306a36Sopenharmony_ci			kfree(p);
404762306a36Sopenharmony_ci			ret = -EINVAL;
404862306a36Sopenharmony_ci			goto err;
404962306a36Sopenharmony_ci		}
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci		if (check_synth_field(event, hist_field, field_pos) == 0) {
405262306a36Sopenharmony_ci			var_ref = create_var_ref(hist_data, hist_field,
405362306a36Sopenharmony_ci						 system, event_name);
405462306a36Sopenharmony_ci			if (!var_ref) {
405562306a36Sopenharmony_ci				kfree(p);
405662306a36Sopenharmony_ci				ret = -ENOMEM;
405762306a36Sopenharmony_ci				goto err;
405862306a36Sopenharmony_ci			}
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci			var_ref_idx = find_var_ref_idx(hist_data, var_ref);
406162306a36Sopenharmony_ci			if (WARN_ON(var_ref_idx < 0)) {
406262306a36Sopenharmony_ci				kfree(p);
406362306a36Sopenharmony_ci				ret = var_ref_idx;
406462306a36Sopenharmony_ci				goto err;
406562306a36Sopenharmony_ci			}
406662306a36Sopenharmony_ci
406762306a36Sopenharmony_ci			data->var_ref_idx[i] = var_ref_idx;
406862306a36Sopenharmony_ci
406962306a36Sopenharmony_ci			field_pos++;
407062306a36Sopenharmony_ci			kfree(p);
407162306a36Sopenharmony_ci			continue;
407262306a36Sopenharmony_ci		}
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_SYNTH_TYPE_MISMATCH, errpos(param));
407562306a36Sopenharmony_ci		kfree(p);
407662306a36Sopenharmony_ci		ret = -EINVAL;
407762306a36Sopenharmony_ci		goto err;
407862306a36Sopenharmony_ci	}
407962306a36Sopenharmony_ci
408062306a36Sopenharmony_ci	if (field_pos != event->n_fields) {
408162306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_SYNTH_COUNT_MISMATCH, errpos(event->name));
408262306a36Sopenharmony_ci		ret = -EINVAL;
408362306a36Sopenharmony_ci		goto err;
408462306a36Sopenharmony_ci	}
408562306a36Sopenharmony_ci
408662306a36Sopenharmony_ci	data->synth_event = event;
408762306a36Sopenharmony_ci out:
408862306a36Sopenharmony_ci	return ret;
408962306a36Sopenharmony_ci err:
409062306a36Sopenharmony_ci	event->ref--;
409162306a36Sopenharmony_ci
409262306a36Sopenharmony_ci	goto out;
409362306a36Sopenharmony_ci}
409462306a36Sopenharmony_ci
409562306a36Sopenharmony_cistatic int action_create(struct hist_trigger_data *hist_data,
409662306a36Sopenharmony_ci			 struct action_data *data)
409762306a36Sopenharmony_ci{
409862306a36Sopenharmony_ci	struct trace_event_file *file = hist_data->event_file;
409962306a36Sopenharmony_ci	struct trace_array *tr = file->tr;
410062306a36Sopenharmony_ci	struct track_data *track_data;
410162306a36Sopenharmony_ci	struct field_var *field_var;
410262306a36Sopenharmony_ci	unsigned int i;
410362306a36Sopenharmony_ci	char *param;
410462306a36Sopenharmony_ci	int ret = 0;
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci	if (data->action == ACTION_TRACE)
410762306a36Sopenharmony_ci		return trace_action_create(hist_data, data);
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci	if (data->action == ACTION_SNAPSHOT) {
411062306a36Sopenharmony_ci		track_data = track_data_alloc(hist_data->key_size, data, hist_data);
411162306a36Sopenharmony_ci		if (IS_ERR(track_data)) {
411262306a36Sopenharmony_ci			ret = PTR_ERR(track_data);
411362306a36Sopenharmony_ci			goto out;
411462306a36Sopenharmony_ci		}
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci		ret = tracing_snapshot_cond_enable(file->tr, track_data,
411762306a36Sopenharmony_ci						   cond_snapshot_update);
411862306a36Sopenharmony_ci		if (ret)
411962306a36Sopenharmony_ci			track_data_free(track_data);
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci		goto out;
412262306a36Sopenharmony_ci	}
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_ci	if (data->action == ACTION_SAVE) {
412562306a36Sopenharmony_ci		if (hist_data->n_save_vars) {
412662306a36Sopenharmony_ci			ret = -EEXIST;
412762306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_TOO_MANY_SAVE_ACTIONS, 0);
412862306a36Sopenharmony_ci			goto out;
412962306a36Sopenharmony_ci		}
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci		for (i = 0; i < data->n_params; i++) {
413262306a36Sopenharmony_ci			param = kstrdup(data->params[i], GFP_KERNEL);
413362306a36Sopenharmony_ci			if (!param) {
413462306a36Sopenharmony_ci				ret = -ENOMEM;
413562306a36Sopenharmony_ci				goto out;
413662306a36Sopenharmony_ci			}
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ci			field_var = create_target_field_var(hist_data, NULL, NULL, param);
413962306a36Sopenharmony_ci			if (IS_ERR(field_var)) {
414062306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_FIELD_VAR_CREATE_FAIL,
414162306a36Sopenharmony_ci					 errpos(param));
414262306a36Sopenharmony_ci				ret = PTR_ERR(field_var);
414362306a36Sopenharmony_ci				kfree(param);
414462306a36Sopenharmony_ci				goto out;
414562306a36Sopenharmony_ci			}
414662306a36Sopenharmony_ci
414762306a36Sopenharmony_ci			hist_data->save_vars[hist_data->n_save_vars++] = field_var;
414862306a36Sopenharmony_ci			if (field_var->val->flags &
414962306a36Sopenharmony_ci			    (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
415062306a36Sopenharmony_ci				hist_data->n_save_var_str++;
415162306a36Sopenharmony_ci			kfree(param);
415262306a36Sopenharmony_ci		}
415362306a36Sopenharmony_ci	}
415462306a36Sopenharmony_ci out:
415562306a36Sopenharmony_ci	return ret;
415662306a36Sopenharmony_ci}
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_cistatic int onmatch_create(struct hist_trigger_data *hist_data,
415962306a36Sopenharmony_ci			  struct action_data *data)
416062306a36Sopenharmony_ci{
416162306a36Sopenharmony_ci	return action_create(hist_data, data);
416262306a36Sopenharmony_ci}
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_cistatic struct action_data *onmatch_parse(struct trace_array *tr, char *str)
416562306a36Sopenharmony_ci{
416662306a36Sopenharmony_ci	char *match_event, *match_event_system;
416762306a36Sopenharmony_ci	struct action_data *data;
416862306a36Sopenharmony_ci	int ret = -EINVAL;
416962306a36Sopenharmony_ci
417062306a36Sopenharmony_ci	data = kzalloc(sizeof(*data), GFP_KERNEL);
417162306a36Sopenharmony_ci	if (!data)
417262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci	match_event = strsep(&str, ")");
417562306a36Sopenharmony_ci	if (!match_event || !str) {
417662306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_NO_CLOSING_PAREN, errpos(match_event));
417762306a36Sopenharmony_ci		goto free;
417862306a36Sopenharmony_ci	}
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	match_event_system = strsep(&match_event, ".");
418162306a36Sopenharmony_ci	if (!match_event) {
418262306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_SUBSYS_NOT_FOUND, errpos(match_event_system));
418362306a36Sopenharmony_ci		goto free;
418462306a36Sopenharmony_ci	}
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	if (IS_ERR(event_file(tr, match_event_system, match_event))) {
418762306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_INVALID_SUBSYS_EVENT, errpos(match_event));
418862306a36Sopenharmony_ci		goto free;
418962306a36Sopenharmony_ci	}
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_ci	data->match_data.event = kstrdup(match_event, GFP_KERNEL);
419262306a36Sopenharmony_ci	if (!data->match_data.event) {
419362306a36Sopenharmony_ci		ret = -ENOMEM;
419462306a36Sopenharmony_ci		goto free;
419562306a36Sopenharmony_ci	}
419662306a36Sopenharmony_ci
419762306a36Sopenharmony_ci	data->match_data.event_system = kstrdup(match_event_system, GFP_KERNEL);
419862306a36Sopenharmony_ci	if (!data->match_data.event_system) {
419962306a36Sopenharmony_ci		ret = -ENOMEM;
420062306a36Sopenharmony_ci		goto free;
420162306a36Sopenharmony_ci	}
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	ret = action_parse(tr, str, data, HANDLER_ONMATCH);
420462306a36Sopenharmony_ci	if (ret)
420562306a36Sopenharmony_ci		goto free;
420662306a36Sopenharmony_ci out:
420762306a36Sopenharmony_ci	return data;
420862306a36Sopenharmony_ci free:
420962306a36Sopenharmony_ci	onmatch_destroy(data);
421062306a36Sopenharmony_ci	data = ERR_PTR(ret);
421162306a36Sopenharmony_ci	goto out;
421262306a36Sopenharmony_ci}
421362306a36Sopenharmony_ci
421462306a36Sopenharmony_cistatic int create_hitcount_val(struct hist_trigger_data *hist_data)
421562306a36Sopenharmony_ci{
421662306a36Sopenharmony_ci	hist_data->fields[HITCOUNT_IDX] =
421762306a36Sopenharmony_ci		create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT, NULL);
421862306a36Sopenharmony_ci	if (!hist_data->fields[HITCOUNT_IDX])
421962306a36Sopenharmony_ci		return -ENOMEM;
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_ci	hist_data->n_vals++;
422262306a36Sopenharmony_ci	hist_data->n_fields++;
422362306a36Sopenharmony_ci
422462306a36Sopenharmony_ci	if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX))
422562306a36Sopenharmony_ci		return -EINVAL;
422662306a36Sopenharmony_ci
422762306a36Sopenharmony_ci	return 0;
422862306a36Sopenharmony_ci}
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_cistatic int __create_val_field(struct hist_trigger_data *hist_data,
423162306a36Sopenharmony_ci			      unsigned int val_idx,
423262306a36Sopenharmony_ci			      struct trace_event_file *file,
423362306a36Sopenharmony_ci			      char *var_name, char *field_str,
423462306a36Sopenharmony_ci			      unsigned long flags)
423562306a36Sopenharmony_ci{
423662306a36Sopenharmony_ci	struct hist_field *hist_field;
423762306a36Sopenharmony_ci	int ret = 0, n_subexprs = 0;
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci	hist_field = parse_expr(hist_data, file, field_str, flags, var_name, &n_subexprs);
424062306a36Sopenharmony_ci	if (IS_ERR(hist_field)) {
424162306a36Sopenharmony_ci		ret = PTR_ERR(hist_field);
424262306a36Sopenharmony_ci		goto out;
424362306a36Sopenharmony_ci	}
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	/* values and variables should not have some modifiers */
424662306a36Sopenharmony_ci	if (hist_field->flags & HIST_FIELD_FL_VAR) {
424762306a36Sopenharmony_ci		/* Variable */
424862306a36Sopenharmony_ci		if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT |
424962306a36Sopenharmony_ci					 HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2))
425062306a36Sopenharmony_ci			goto err;
425162306a36Sopenharmony_ci	} else {
425262306a36Sopenharmony_ci		/* Value */
425362306a36Sopenharmony_ci		if (hist_field->flags & (HIST_FIELD_FL_GRAPH | HIST_FIELD_FL_PERCENT |
425462306a36Sopenharmony_ci					 HIST_FIELD_FL_BUCKET | HIST_FIELD_FL_LOG2 |
425562306a36Sopenharmony_ci					 HIST_FIELD_FL_SYM | HIST_FIELD_FL_SYM_OFFSET |
425662306a36Sopenharmony_ci					 HIST_FIELD_FL_SYSCALL | HIST_FIELD_FL_STACKTRACE))
425762306a36Sopenharmony_ci			goto err;
425862306a36Sopenharmony_ci	}
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	hist_data->fields[val_idx] = hist_field;
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	++hist_data->n_vals;
426362306a36Sopenharmony_ci	++hist_data->n_fields;
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_ci	if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX))
426662306a36Sopenharmony_ci		ret = -EINVAL;
426762306a36Sopenharmony_ci out:
426862306a36Sopenharmony_ci	return ret;
426962306a36Sopenharmony_ci err:
427062306a36Sopenharmony_ci	hist_err(file->tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(field_str));
427162306a36Sopenharmony_ci	return -EINVAL;
427262306a36Sopenharmony_ci}
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_cistatic int create_val_field(struct hist_trigger_data *hist_data,
427562306a36Sopenharmony_ci			    unsigned int val_idx,
427662306a36Sopenharmony_ci			    struct trace_event_file *file,
427762306a36Sopenharmony_ci			    char *field_str)
427862306a36Sopenharmony_ci{
427962306a36Sopenharmony_ci	if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX))
428062306a36Sopenharmony_ci		return -EINVAL;
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	return __create_val_field(hist_data, val_idx, file, NULL, field_str, 0);
428362306a36Sopenharmony_ci}
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_cistatic const char no_comm[] = "(no comm)";
428662306a36Sopenharmony_ci
428762306a36Sopenharmony_cistatic u64 hist_field_execname(struct hist_field *hist_field,
428862306a36Sopenharmony_ci			       struct tracing_map_elt *elt,
428962306a36Sopenharmony_ci			       struct trace_buffer *buffer,
429062306a36Sopenharmony_ci			       struct ring_buffer_event *rbe,
429162306a36Sopenharmony_ci			       void *event)
429262306a36Sopenharmony_ci{
429362306a36Sopenharmony_ci	struct hist_elt_data *elt_data;
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_ci	if (WARN_ON_ONCE(!elt))
429662306a36Sopenharmony_ci		return (u64)(unsigned long)no_comm;
429762306a36Sopenharmony_ci
429862306a36Sopenharmony_ci	elt_data = elt->private_data;
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_ci	if (WARN_ON_ONCE(!elt_data->comm))
430162306a36Sopenharmony_ci		return (u64)(unsigned long)no_comm;
430262306a36Sopenharmony_ci
430362306a36Sopenharmony_ci	return (u64)(unsigned long)(elt_data->comm);
430462306a36Sopenharmony_ci}
430562306a36Sopenharmony_ci
430662306a36Sopenharmony_cistatic u64 hist_field_stack(struct hist_field *hist_field,
430762306a36Sopenharmony_ci			    struct tracing_map_elt *elt,
430862306a36Sopenharmony_ci			    struct trace_buffer *buffer,
430962306a36Sopenharmony_ci			    struct ring_buffer_event *rbe,
431062306a36Sopenharmony_ci			    void *event)
431162306a36Sopenharmony_ci{
431262306a36Sopenharmony_ci	u32 str_item = *(u32 *)(event + hist_field->field->offset);
431362306a36Sopenharmony_ci	int str_loc = str_item & 0xffff;
431462306a36Sopenharmony_ci	char *addr = (char *)(event + str_loc);
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	return (u64)(unsigned long)addr;
431762306a36Sopenharmony_ci}
431862306a36Sopenharmony_ci
431962306a36Sopenharmony_cistatic u64 hist_fn_call(struct hist_field *hist_field,
432062306a36Sopenharmony_ci			struct tracing_map_elt *elt,
432162306a36Sopenharmony_ci			struct trace_buffer *buffer,
432262306a36Sopenharmony_ci			struct ring_buffer_event *rbe,
432362306a36Sopenharmony_ci			void *event)
432462306a36Sopenharmony_ci{
432562306a36Sopenharmony_ci	switch (hist_field->fn_num) {
432662306a36Sopenharmony_ci	case HIST_FIELD_FN_VAR_REF:
432762306a36Sopenharmony_ci		return hist_field_var_ref(hist_field, elt, buffer, rbe, event);
432862306a36Sopenharmony_ci	case HIST_FIELD_FN_COUNTER:
432962306a36Sopenharmony_ci		return hist_field_counter(hist_field, elt, buffer, rbe, event);
433062306a36Sopenharmony_ci	case HIST_FIELD_FN_CONST:
433162306a36Sopenharmony_ci		return hist_field_const(hist_field, elt, buffer, rbe, event);
433262306a36Sopenharmony_ci	case HIST_FIELD_FN_LOG2:
433362306a36Sopenharmony_ci		return hist_field_log2(hist_field, elt, buffer, rbe, event);
433462306a36Sopenharmony_ci	case HIST_FIELD_FN_BUCKET:
433562306a36Sopenharmony_ci		return hist_field_bucket(hist_field, elt, buffer, rbe, event);
433662306a36Sopenharmony_ci	case HIST_FIELD_FN_TIMESTAMP:
433762306a36Sopenharmony_ci		return hist_field_timestamp(hist_field, elt, buffer, rbe, event);
433862306a36Sopenharmony_ci	case HIST_FIELD_FN_CPU:
433962306a36Sopenharmony_ci		return hist_field_cpu(hist_field, elt, buffer, rbe, event);
434062306a36Sopenharmony_ci	case HIST_FIELD_FN_STRING:
434162306a36Sopenharmony_ci		return hist_field_string(hist_field, elt, buffer, rbe, event);
434262306a36Sopenharmony_ci	case HIST_FIELD_FN_DYNSTRING:
434362306a36Sopenharmony_ci		return hist_field_dynstring(hist_field, elt, buffer, rbe, event);
434462306a36Sopenharmony_ci	case HIST_FIELD_FN_RELDYNSTRING:
434562306a36Sopenharmony_ci		return hist_field_reldynstring(hist_field, elt, buffer, rbe, event);
434662306a36Sopenharmony_ci	case HIST_FIELD_FN_PSTRING:
434762306a36Sopenharmony_ci		return hist_field_pstring(hist_field, elt, buffer, rbe, event);
434862306a36Sopenharmony_ci	case HIST_FIELD_FN_S64:
434962306a36Sopenharmony_ci		return hist_field_s64(hist_field, elt, buffer, rbe, event);
435062306a36Sopenharmony_ci	case HIST_FIELD_FN_U64:
435162306a36Sopenharmony_ci		return hist_field_u64(hist_field, elt, buffer, rbe, event);
435262306a36Sopenharmony_ci	case HIST_FIELD_FN_S32:
435362306a36Sopenharmony_ci		return hist_field_s32(hist_field, elt, buffer, rbe, event);
435462306a36Sopenharmony_ci	case HIST_FIELD_FN_U32:
435562306a36Sopenharmony_ci		return hist_field_u32(hist_field, elt, buffer, rbe, event);
435662306a36Sopenharmony_ci	case HIST_FIELD_FN_S16:
435762306a36Sopenharmony_ci		return hist_field_s16(hist_field, elt, buffer, rbe, event);
435862306a36Sopenharmony_ci	case HIST_FIELD_FN_U16:
435962306a36Sopenharmony_ci		return hist_field_u16(hist_field, elt, buffer, rbe, event);
436062306a36Sopenharmony_ci	case HIST_FIELD_FN_S8:
436162306a36Sopenharmony_ci		return hist_field_s8(hist_field, elt, buffer, rbe, event);
436262306a36Sopenharmony_ci	case HIST_FIELD_FN_U8:
436362306a36Sopenharmony_ci		return hist_field_u8(hist_field, elt, buffer, rbe, event);
436462306a36Sopenharmony_ci	case HIST_FIELD_FN_UMINUS:
436562306a36Sopenharmony_ci		return hist_field_unary_minus(hist_field, elt, buffer, rbe, event);
436662306a36Sopenharmony_ci	case HIST_FIELD_FN_MINUS:
436762306a36Sopenharmony_ci		return hist_field_minus(hist_field, elt, buffer, rbe, event);
436862306a36Sopenharmony_ci	case HIST_FIELD_FN_PLUS:
436962306a36Sopenharmony_ci		return hist_field_plus(hist_field, elt, buffer, rbe, event);
437062306a36Sopenharmony_ci	case HIST_FIELD_FN_DIV:
437162306a36Sopenharmony_ci		return hist_field_div(hist_field, elt, buffer, rbe, event);
437262306a36Sopenharmony_ci	case HIST_FIELD_FN_MULT:
437362306a36Sopenharmony_ci		return hist_field_mult(hist_field, elt, buffer, rbe, event);
437462306a36Sopenharmony_ci	case HIST_FIELD_FN_DIV_POWER2:
437562306a36Sopenharmony_ci		return div_by_power_of_two(hist_field, elt, buffer, rbe, event);
437662306a36Sopenharmony_ci	case HIST_FIELD_FN_DIV_NOT_POWER2:
437762306a36Sopenharmony_ci		return div_by_not_power_of_two(hist_field, elt, buffer, rbe, event);
437862306a36Sopenharmony_ci	case HIST_FIELD_FN_DIV_MULT_SHIFT:
437962306a36Sopenharmony_ci		return div_by_mult_and_shift(hist_field, elt, buffer, rbe, event);
438062306a36Sopenharmony_ci	case HIST_FIELD_FN_EXECNAME:
438162306a36Sopenharmony_ci		return hist_field_execname(hist_field, elt, buffer, rbe, event);
438262306a36Sopenharmony_ci	case HIST_FIELD_FN_STACK:
438362306a36Sopenharmony_ci		return hist_field_stack(hist_field, elt, buffer, rbe, event);
438462306a36Sopenharmony_ci	default:
438562306a36Sopenharmony_ci		return 0;
438662306a36Sopenharmony_ci	}
438762306a36Sopenharmony_ci}
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_ci/* Convert a var that points to common_pid.execname to a string */
439062306a36Sopenharmony_cistatic void update_var_execname(struct hist_field *hist_field)
439162306a36Sopenharmony_ci{
439262306a36Sopenharmony_ci	hist_field->flags = HIST_FIELD_FL_STRING | HIST_FIELD_FL_VAR |
439362306a36Sopenharmony_ci		HIST_FIELD_FL_EXECNAME;
439462306a36Sopenharmony_ci	hist_field->size = MAX_FILTER_STR_VAL;
439562306a36Sopenharmony_ci	hist_field->is_signed = 0;
439662306a36Sopenharmony_ci
439762306a36Sopenharmony_ci	kfree_const(hist_field->type);
439862306a36Sopenharmony_ci	hist_field->type = "char[]";
439962306a36Sopenharmony_ci
440062306a36Sopenharmony_ci	hist_field->fn_num = HIST_FIELD_FN_EXECNAME;
440162306a36Sopenharmony_ci}
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_cistatic int create_var_field(struct hist_trigger_data *hist_data,
440462306a36Sopenharmony_ci			    unsigned int val_idx,
440562306a36Sopenharmony_ci			    struct trace_event_file *file,
440662306a36Sopenharmony_ci			    char *var_name, char *expr_str)
440762306a36Sopenharmony_ci{
440862306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
440962306a36Sopenharmony_ci	unsigned long flags = 0;
441062306a36Sopenharmony_ci	int ret;
441162306a36Sopenharmony_ci
441262306a36Sopenharmony_ci	if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX))
441362306a36Sopenharmony_ci		return -EINVAL;
441462306a36Sopenharmony_ci
441562306a36Sopenharmony_ci	if (find_var(hist_data, file, var_name) && !hist_data->remove) {
441662306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_DUPLICATE_VAR, errpos(var_name));
441762306a36Sopenharmony_ci		return -EINVAL;
441862306a36Sopenharmony_ci	}
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci	flags |= HIST_FIELD_FL_VAR;
442162306a36Sopenharmony_ci	hist_data->n_vars++;
442262306a36Sopenharmony_ci	if (WARN_ON(hist_data->n_vars > TRACING_MAP_VARS_MAX))
442362306a36Sopenharmony_ci		return -EINVAL;
442462306a36Sopenharmony_ci
442562306a36Sopenharmony_ci	ret = __create_val_field(hist_data, val_idx, file, var_name, expr_str, flags);
442662306a36Sopenharmony_ci
442762306a36Sopenharmony_ci	if (!ret && hist_data->fields[val_idx]->flags & HIST_FIELD_FL_EXECNAME)
442862306a36Sopenharmony_ci		update_var_execname(hist_data->fields[val_idx]);
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	if (!ret && hist_data->fields[val_idx]->flags &
443162306a36Sopenharmony_ci	    (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE))
443262306a36Sopenharmony_ci		hist_data->fields[val_idx]->var_str_idx = hist_data->n_var_str++;
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_ci	return ret;
443562306a36Sopenharmony_ci}
443662306a36Sopenharmony_ci
443762306a36Sopenharmony_cistatic int create_val_fields(struct hist_trigger_data *hist_data,
443862306a36Sopenharmony_ci			     struct trace_event_file *file)
443962306a36Sopenharmony_ci{
444062306a36Sopenharmony_ci	unsigned int i, j = 1, n_hitcount = 0;
444162306a36Sopenharmony_ci	char *fields_str, *field_str;
444262306a36Sopenharmony_ci	int ret;
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci	ret = create_hitcount_val(hist_data);
444562306a36Sopenharmony_ci	if (ret)
444662306a36Sopenharmony_ci		goto out;
444762306a36Sopenharmony_ci
444862306a36Sopenharmony_ci	fields_str = hist_data->attrs->vals_str;
444962306a36Sopenharmony_ci	if (!fields_str)
445062306a36Sopenharmony_ci		goto out;
445162306a36Sopenharmony_ci
445262306a36Sopenharmony_ci	for (i = 0, j = 1; i < TRACING_MAP_VALS_MAX &&
445362306a36Sopenharmony_ci		     j < TRACING_MAP_VALS_MAX; i++) {
445462306a36Sopenharmony_ci		field_str = strsep(&fields_str, ",");
445562306a36Sopenharmony_ci		if (!field_str)
445662306a36Sopenharmony_ci			break;
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_ci		if (strcmp(field_str, "hitcount") == 0) {
445962306a36Sopenharmony_ci			if (!n_hitcount++)
446062306a36Sopenharmony_ci				continue;
446162306a36Sopenharmony_ci		}
446262306a36Sopenharmony_ci
446362306a36Sopenharmony_ci		ret = create_val_field(hist_data, j++, file, field_str);
446462306a36Sopenharmony_ci		if (ret)
446562306a36Sopenharmony_ci			goto out;
446662306a36Sopenharmony_ci	}
446762306a36Sopenharmony_ci
446862306a36Sopenharmony_ci	if (fields_str && (strcmp(fields_str, "hitcount") != 0))
446962306a36Sopenharmony_ci		ret = -EINVAL;
447062306a36Sopenharmony_ci out:
447162306a36Sopenharmony_ci	/* There is only raw hitcount but nohitcount suppresses it. */
447262306a36Sopenharmony_ci	if (j == 1 && hist_data->attrs->no_hitcount) {
447362306a36Sopenharmony_ci		hist_err(hist_data->event_file->tr, HIST_ERR_NEED_NOHC_VAL, 0);
447462306a36Sopenharmony_ci		ret = -ENOENT;
447562306a36Sopenharmony_ci	}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci	return ret;
447862306a36Sopenharmony_ci}
447962306a36Sopenharmony_ci
448062306a36Sopenharmony_cistatic int create_key_field(struct hist_trigger_data *hist_data,
448162306a36Sopenharmony_ci			    unsigned int key_idx,
448262306a36Sopenharmony_ci			    unsigned int key_offset,
448362306a36Sopenharmony_ci			    struct trace_event_file *file,
448462306a36Sopenharmony_ci			    char *field_str)
448562306a36Sopenharmony_ci{
448662306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
448762306a36Sopenharmony_ci	struct hist_field *hist_field = NULL;
448862306a36Sopenharmony_ci	unsigned long flags = 0;
448962306a36Sopenharmony_ci	unsigned int key_size;
449062306a36Sopenharmony_ci	int ret = 0, n_subexprs = 0;
449162306a36Sopenharmony_ci
449262306a36Sopenharmony_ci	if (WARN_ON(key_idx >= HIST_FIELDS_MAX))
449362306a36Sopenharmony_ci		return -EINVAL;
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_ci	flags |= HIST_FIELD_FL_KEY;
449662306a36Sopenharmony_ci
449762306a36Sopenharmony_ci	if (strcmp(field_str, "stacktrace") == 0) {
449862306a36Sopenharmony_ci		flags |= HIST_FIELD_FL_STACKTRACE;
449962306a36Sopenharmony_ci		key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH;
450062306a36Sopenharmony_ci		hist_field = create_hist_field(hist_data, NULL, flags, NULL);
450162306a36Sopenharmony_ci	} else {
450262306a36Sopenharmony_ci		hist_field = parse_expr(hist_data, file, field_str, flags,
450362306a36Sopenharmony_ci					NULL, &n_subexprs);
450462306a36Sopenharmony_ci		if (IS_ERR(hist_field)) {
450562306a36Sopenharmony_ci			ret = PTR_ERR(hist_field);
450662306a36Sopenharmony_ci			goto out;
450762306a36Sopenharmony_ci		}
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci		if (field_has_hist_vars(hist_field, 0))	{
451062306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_INVALID_REF_KEY, errpos(field_str));
451162306a36Sopenharmony_ci			destroy_hist_field(hist_field, 0);
451262306a36Sopenharmony_ci			ret = -EINVAL;
451362306a36Sopenharmony_ci			goto out;
451462306a36Sopenharmony_ci		}
451562306a36Sopenharmony_ci
451662306a36Sopenharmony_ci		key_size = hist_field->size;
451762306a36Sopenharmony_ci	}
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci	hist_data->fields[key_idx] = hist_field;
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci	key_size = ALIGN(key_size, sizeof(u64));
452262306a36Sopenharmony_ci	hist_data->fields[key_idx]->size = key_size;
452362306a36Sopenharmony_ci	hist_data->fields[key_idx]->offset = key_offset;
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci	hist_data->key_size += key_size;
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_ci	if (hist_data->key_size > HIST_KEY_SIZE_MAX) {
452862306a36Sopenharmony_ci		ret = -EINVAL;
452962306a36Sopenharmony_ci		goto out;
453062306a36Sopenharmony_ci	}
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci	hist_data->n_keys++;
453362306a36Sopenharmony_ci	hist_data->n_fields++;
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci	if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX))
453662306a36Sopenharmony_ci		return -EINVAL;
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci	ret = key_size;
453962306a36Sopenharmony_ci out:
454062306a36Sopenharmony_ci	return ret;
454162306a36Sopenharmony_ci}
454262306a36Sopenharmony_ci
454362306a36Sopenharmony_cistatic int create_key_fields(struct hist_trigger_data *hist_data,
454462306a36Sopenharmony_ci			     struct trace_event_file *file)
454562306a36Sopenharmony_ci{
454662306a36Sopenharmony_ci	unsigned int i, key_offset = 0, n_vals = hist_data->n_vals;
454762306a36Sopenharmony_ci	char *fields_str, *field_str;
454862306a36Sopenharmony_ci	int ret = -EINVAL;
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_ci	fields_str = hist_data->attrs->keys_str;
455162306a36Sopenharmony_ci	if (!fields_str)
455262306a36Sopenharmony_ci		goto out;
455362306a36Sopenharmony_ci
455462306a36Sopenharmony_ci	for (i = n_vals; i < n_vals + TRACING_MAP_KEYS_MAX; i++) {
455562306a36Sopenharmony_ci		field_str = strsep(&fields_str, ",");
455662306a36Sopenharmony_ci		if (!field_str)
455762306a36Sopenharmony_ci			break;
455862306a36Sopenharmony_ci		ret = create_key_field(hist_data, i, key_offset,
455962306a36Sopenharmony_ci				       file, field_str);
456062306a36Sopenharmony_ci		if (ret < 0)
456162306a36Sopenharmony_ci			goto out;
456262306a36Sopenharmony_ci		key_offset += ret;
456362306a36Sopenharmony_ci	}
456462306a36Sopenharmony_ci	if (fields_str) {
456562306a36Sopenharmony_ci		ret = -EINVAL;
456662306a36Sopenharmony_ci		goto out;
456762306a36Sopenharmony_ci	}
456862306a36Sopenharmony_ci	ret = 0;
456962306a36Sopenharmony_ci out:
457062306a36Sopenharmony_ci	return ret;
457162306a36Sopenharmony_ci}
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_cistatic int create_var_fields(struct hist_trigger_data *hist_data,
457462306a36Sopenharmony_ci			     struct trace_event_file *file)
457562306a36Sopenharmony_ci{
457662306a36Sopenharmony_ci	unsigned int i, j = hist_data->n_vals;
457762306a36Sopenharmony_ci	int ret = 0;
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_ci	unsigned int n_vars = hist_data->attrs->var_defs.n_vars;
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci	for (i = 0; i < n_vars; i++) {
458262306a36Sopenharmony_ci		char *var_name = hist_data->attrs->var_defs.name[i];
458362306a36Sopenharmony_ci		char *expr = hist_data->attrs->var_defs.expr[i];
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci		ret = create_var_field(hist_data, j++, file, var_name, expr);
458662306a36Sopenharmony_ci		if (ret)
458762306a36Sopenharmony_ci			goto out;
458862306a36Sopenharmony_ci	}
458962306a36Sopenharmony_ci out:
459062306a36Sopenharmony_ci	return ret;
459162306a36Sopenharmony_ci}
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_cistatic void free_var_defs(struct hist_trigger_data *hist_data)
459462306a36Sopenharmony_ci{
459562306a36Sopenharmony_ci	unsigned int i;
459662306a36Sopenharmony_ci
459762306a36Sopenharmony_ci	for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) {
459862306a36Sopenharmony_ci		kfree(hist_data->attrs->var_defs.name[i]);
459962306a36Sopenharmony_ci		kfree(hist_data->attrs->var_defs.expr[i]);
460062306a36Sopenharmony_ci	}
460162306a36Sopenharmony_ci
460262306a36Sopenharmony_ci	hist_data->attrs->var_defs.n_vars = 0;
460362306a36Sopenharmony_ci}
460462306a36Sopenharmony_ci
460562306a36Sopenharmony_cistatic int parse_var_defs(struct hist_trigger_data *hist_data)
460662306a36Sopenharmony_ci{
460762306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
460862306a36Sopenharmony_ci	char *s, *str, *var_name, *field_str;
460962306a36Sopenharmony_ci	unsigned int i, j, n_vars = 0;
461062306a36Sopenharmony_ci	int ret = 0;
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci	for (i = 0; i < hist_data->attrs->n_assignments; i++) {
461362306a36Sopenharmony_ci		str = hist_data->attrs->assignment_str[i];
461462306a36Sopenharmony_ci		for (j = 0; j < TRACING_MAP_VARS_MAX; j++) {
461562306a36Sopenharmony_ci			field_str = strsep(&str, ",");
461662306a36Sopenharmony_ci			if (!field_str)
461762306a36Sopenharmony_ci				break;
461862306a36Sopenharmony_ci
461962306a36Sopenharmony_ci			var_name = strsep(&field_str, "=");
462062306a36Sopenharmony_ci			if (!var_name || !field_str) {
462162306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_MALFORMED_ASSIGNMENT,
462262306a36Sopenharmony_ci					 errpos(var_name));
462362306a36Sopenharmony_ci				ret = -EINVAL;
462462306a36Sopenharmony_ci				goto free;
462562306a36Sopenharmony_ci			}
462662306a36Sopenharmony_ci
462762306a36Sopenharmony_ci			if (n_vars == TRACING_MAP_VARS_MAX) {
462862306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_TOO_MANY_VARS, errpos(var_name));
462962306a36Sopenharmony_ci				ret = -EINVAL;
463062306a36Sopenharmony_ci				goto free;
463162306a36Sopenharmony_ci			}
463262306a36Sopenharmony_ci
463362306a36Sopenharmony_ci			s = kstrdup(var_name, GFP_KERNEL);
463462306a36Sopenharmony_ci			if (!s) {
463562306a36Sopenharmony_ci				ret = -ENOMEM;
463662306a36Sopenharmony_ci				goto free;
463762306a36Sopenharmony_ci			}
463862306a36Sopenharmony_ci			hist_data->attrs->var_defs.name[n_vars] = s;
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci			s = kstrdup(field_str, GFP_KERNEL);
464162306a36Sopenharmony_ci			if (!s) {
464262306a36Sopenharmony_ci				kfree(hist_data->attrs->var_defs.name[n_vars]);
464362306a36Sopenharmony_ci				hist_data->attrs->var_defs.name[n_vars] = NULL;
464462306a36Sopenharmony_ci				ret = -ENOMEM;
464562306a36Sopenharmony_ci				goto free;
464662306a36Sopenharmony_ci			}
464762306a36Sopenharmony_ci			hist_data->attrs->var_defs.expr[n_vars++] = s;
464862306a36Sopenharmony_ci
464962306a36Sopenharmony_ci			hist_data->attrs->var_defs.n_vars = n_vars;
465062306a36Sopenharmony_ci		}
465162306a36Sopenharmony_ci	}
465262306a36Sopenharmony_ci
465362306a36Sopenharmony_ci	return ret;
465462306a36Sopenharmony_ci free:
465562306a36Sopenharmony_ci	free_var_defs(hist_data);
465662306a36Sopenharmony_ci
465762306a36Sopenharmony_ci	return ret;
465862306a36Sopenharmony_ci}
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_cistatic int create_hist_fields(struct hist_trigger_data *hist_data,
466162306a36Sopenharmony_ci			      struct trace_event_file *file)
466262306a36Sopenharmony_ci{
466362306a36Sopenharmony_ci	int ret;
466462306a36Sopenharmony_ci
466562306a36Sopenharmony_ci	ret = parse_var_defs(hist_data);
466662306a36Sopenharmony_ci	if (ret)
466762306a36Sopenharmony_ci		return ret;
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci	ret = create_val_fields(hist_data, file);
467062306a36Sopenharmony_ci	if (ret)
467162306a36Sopenharmony_ci		goto out;
467262306a36Sopenharmony_ci
467362306a36Sopenharmony_ci	ret = create_var_fields(hist_data, file);
467462306a36Sopenharmony_ci	if (ret)
467562306a36Sopenharmony_ci		goto out;
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci	ret = create_key_fields(hist_data, file);
467862306a36Sopenharmony_ci
467962306a36Sopenharmony_ci out:
468062306a36Sopenharmony_ci	free_var_defs(hist_data);
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	return ret;
468362306a36Sopenharmony_ci}
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_cistatic int is_descending(struct trace_array *tr, const char *str)
468662306a36Sopenharmony_ci{
468762306a36Sopenharmony_ci	if (!str)
468862306a36Sopenharmony_ci		return 0;
468962306a36Sopenharmony_ci
469062306a36Sopenharmony_ci	if (strcmp(str, "descending") == 0)
469162306a36Sopenharmony_ci		return 1;
469262306a36Sopenharmony_ci
469362306a36Sopenharmony_ci	if (strcmp(str, "ascending") == 0)
469462306a36Sopenharmony_ci		return 0;
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci	hist_err(tr, HIST_ERR_INVALID_SORT_MODIFIER, errpos((char *)str));
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci	return -EINVAL;
469962306a36Sopenharmony_ci}
470062306a36Sopenharmony_ci
470162306a36Sopenharmony_cistatic int create_sort_keys(struct hist_trigger_data *hist_data)
470262306a36Sopenharmony_ci{
470362306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
470462306a36Sopenharmony_ci	char *fields_str = hist_data->attrs->sort_key_str;
470562306a36Sopenharmony_ci	struct tracing_map_sort_key *sort_key;
470662306a36Sopenharmony_ci	int descending, ret = 0;
470762306a36Sopenharmony_ci	unsigned int i, j, k;
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci	hist_data->n_sort_keys = 1; /* we always have at least one, hitcount */
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_ci	if (!fields_str)
471262306a36Sopenharmony_ci		goto out;
471362306a36Sopenharmony_ci
471462306a36Sopenharmony_ci	for (i = 0; i < TRACING_MAP_SORT_KEYS_MAX; i++) {
471562306a36Sopenharmony_ci		struct hist_field *hist_field;
471662306a36Sopenharmony_ci		char *field_str, *field_name;
471762306a36Sopenharmony_ci		const char *test_name;
471862306a36Sopenharmony_ci
471962306a36Sopenharmony_ci		sort_key = &hist_data->sort_keys[i];
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_ci		field_str = strsep(&fields_str, ",");
472262306a36Sopenharmony_ci		if (!field_str)
472362306a36Sopenharmony_ci			break;
472462306a36Sopenharmony_ci
472562306a36Sopenharmony_ci		if (!*field_str) {
472662306a36Sopenharmony_ci			ret = -EINVAL;
472762306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_EMPTY_SORT_FIELD, errpos("sort="));
472862306a36Sopenharmony_ci			break;
472962306a36Sopenharmony_ci		}
473062306a36Sopenharmony_ci
473162306a36Sopenharmony_ci		if ((i == TRACING_MAP_SORT_KEYS_MAX - 1) && fields_str) {
473262306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_TOO_MANY_SORT_FIELDS, errpos("sort="));
473362306a36Sopenharmony_ci			ret = -EINVAL;
473462306a36Sopenharmony_ci			break;
473562306a36Sopenharmony_ci		}
473662306a36Sopenharmony_ci
473762306a36Sopenharmony_ci		field_name = strsep(&field_str, ".");
473862306a36Sopenharmony_ci		if (!field_name || !*field_name) {
473962306a36Sopenharmony_ci			ret = -EINVAL;
474062306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_EMPTY_SORT_FIELD, errpos("sort="));
474162306a36Sopenharmony_ci			break;
474262306a36Sopenharmony_ci		}
474362306a36Sopenharmony_ci
474462306a36Sopenharmony_ci		if (strcmp(field_name, "hitcount") == 0) {
474562306a36Sopenharmony_ci			descending = is_descending(tr, field_str);
474662306a36Sopenharmony_ci			if (descending < 0) {
474762306a36Sopenharmony_ci				ret = descending;
474862306a36Sopenharmony_ci				break;
474962306a36Sopenharmony_ci			}
475062306a36Sopenharmony_ci			sort_key->descending = descending;
475162306a36Sopenharmony_ci			continue;
475262306a36Sopenharmony_ci		}
475362306a36Sopenharmony_ci
475462306a36Sopenharmony_ci		for (j = 1, k = 1; j < hist_data->n_fields; j++) {
475562306a36Sopenharmony_ci			unsigned int idx;
475662306a36Sopenharmony_ci
475762306a36Sopenharmony_ci			hist_field = hist_data->fields[j];
475862306a36Sopenharmony_ci			if (hist_field->flags & HIST_FIELD_FL_VAR)
475962306a36Sopenharmony_ci				continue;
476062306a36Sopenharmony_ci
476162306a36Sopenharmony_ci			idx = k++;
476262306a36Sopenharmony_ci
476362306a36Sopenharmony_ci			test_name = hist_field_name(hist_field, 0);
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci			if (strcmp(field_name, test_name) == 0) {
476662306a36Sopenharmony_ci				sort_key->field_idx = idx;
476762306a36Sopenharmony_ci				descending = is_descending(tr, field_str);
476862306a36Sopenharmony_ci				if (descending < 0) {
476962306a36Sopenharmony_ci					ret = descending;
477062306a36Sopenharmony_ci					goto out;
477162306a36Sopenharmony_ci				}
477262306a36Sopenharmony_ci				sort_key->descending = descending;
477362306a36Sopenharmony_ci				break;
477462306a36Sopenharmony_ci			}
477562306a36Sopenharmony_ci		}
477662306a36Sopenharmony_ci		if (j == hist_data->n_fields) {
477762306a36Sopenharmony_ci			ret = -EINVAL;
477862306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_INVALID_SORT_FIELD, errpos(field_name));
477962306a36Sopenharmony_ci			break;
478062306a36Sopenharmony_ci		}
478162306a36Sopenharmony_ci	}
478262306a36Sopenharmony_ci
478362306a36Sopenharmony_ci	hist_data->n_sort_keys = i;
478462306a36Sopenharmony_ci out:
478562306a36Sopenharmony_ci	return ret;
478662306a36Sopenharmony_ci}
478762306a36Sopenharmony_ci
478862306a36Sopenharmony_cistatic void destroy_actions(struct hist_trigger_data *hist_data)
478962306a36Sopenharmony_ci{
479062306a36Sopenharmony_ci	unsigned int i;
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
479362306a36Sopenharmony_ci		struct action_data *data = hist_data->actions[i];
479462306a36Sopenharmony_ci
479562306a36Sopenharmony_ci		if (data->handler == HANDLER_ONMATCH)
479662306a36Sopenharmony_ci			onmatch_destroy(data);
479762306a36Sopenharmony_ci		else if (data->handler == HANDLER_ONMAX ||
479862306a36Sopenharmony_ci			 data->handler == HANDLER_ONCHANGE)
479962306a36Sopenharmony_ci			track_data_destroy(hist_data, data);
480062306a36Sopenharmony_ci		else
480162306a36Sopenharmony_ci			kfree(data);
480262306a36Sopenharmony_ci	}
480362306a36Sopenharmony_ci}
480462306a36Sopenharmony_ci
480562306a36Sopenharmony_cistatic int parse_actions(struct hist_trigger_data *hist_data)
480662306a36Sopenharmony_ci{
480762306a36Sopenharmony_ci	struct trace_array *tr = hist_data->event_file->tr;
480862306a36Sopenharmony_ci	struct action_data *data;
480962306a36Sopenharmony_ci	unsigned int i;
481062306a36Sopenharmony_ci	int ret = 0;
481162306a36Sopenharmony_ci	char *str;
481262306a36Sopenharmony_ci	int len;
481362306a36Sopenharmony_ci
481462306a36Sopenharmony_ci	for (i = 0; i < hist_data->attrs->n_actions; i++) {
481562306a36Sopenharmony_ci		str = hist_data->attrs->action_str[i];
481662306a36Sopenharmony_ci
481762306a36Sopenharmony_ci		if ((len = str_has_prefix(str, "onmatch("))) {
481862306a36Sopenharmony_ci			char *action_str = str + len;
481962306a36Sopenharmony_ci
482062306a36Sopenharmony_ci			data = onmatch_parse(tr, action_str);
482162306a36Sopenharmony_ci			if (IS_ERR(data)) {
482262306a36Sopenharmony_ci				ret = PTR_ERR(data);
482362306a36Sopenharmony_ci				break;
482462306a36Sopenharmony_ci			}
482562306a36Sopenharmony_ci		} else if ((len = str_has_prefix(str, "onmax("))) {
482662306a36Sopenharmony_ci			char *action_str = str + len;
482762306a36Sopenharmony_ci
482862306a36Sopenharmony_ci			data = track_data_parse(hist_data, action_str,
482962306a36Sopenharmony_ci						HANDLER_ONMAX);
483062306a36Sopenharmony_ci			if (IS_ERR(data)) {
483162306a36Sopenharmony_ci				ret = PTR_ERR(data);
483262306a36Sopenharmony_ci				break;
483362306a36Sopenharmony_ci			}
483462306a36Sopenharmony_ci		} else if ((len = str_has_prefix(str, "onchange("))) {
483562306a36Sopenharmony_ci			char *action_str = str + len;
483662306a36Sopenharmony_ci
483762306a36Sopenharmony_ci			data = track_data_parse(hist_data, action_str,
483862306a36Sopenharmony_ci						HANDLER_ONCHANGE);
483962306a36Sopenharmony_ci			if (IS_ERR(data)) {
484062306a36Sopenharmony_ci				ret = PTR_ERR(data);
484162306a36Sopenharmony_ci				break;
484262306a36Sopenharmony_ci			}
484362306a36Sopenharmony_ci		} else {
484462306a36Sopenharmony_ci			ret = -EINVAL;
484562306a36Sopenharmony_ci			break;
484662306a36Sopenharmony_ci		}
484762306a36Sopenharmony_ci
484862306a36Sopenharmony_ci		hist_data->actions[hist_data->n_actions++] = data;
484962306a36Sopenharmony_ci	}
485062306a36Sopenharmony_ci
485162306a36Sopenharmony_ci	return ret;
485262306a36Sopenharmony_ci}
485362306a36Sopenharmony_ci
485462306a36Sopenharmony_cistatic int create_actions(struct hist_trigger_data *hist_data)
485562306a36Sopenharmony_ci{
485662306a36Sopenharmony_ci	struct action_data *data;
485762306a36Sopenharmony_ci	unsigned int i;
485862306a36Sopenharmony_ci	int ret = 0;
485962306a36Sopenharmony_ci
486062306a36Sopenharmony_ci	for (i = 0; i < hist_data->attrs->n_actions; i++) {
486162306a36Sopenharmony_ci		data = hist_data->actions[i];
486262306a36Sopenharmony_ci
486362306a36Sopenharmony_ci		if (data->handler == HANDLER_ONMATCH) {
486462306a36Sopenharmony_ci			ret = onmatch_create(hist_data, data);
486562306a36Sopenharmony_ci			if (ret)
486662306a36Sopenharmony_ci				break;
486762306a36Sopenharmony_ci		} else if (data->handler == HANDLER_ONMAX ||
486862306a36Sopenharmony_ci			   data->handler == HANDLER_ONCHANGE) {
486962306a36Sopenharmony_ci			ret = track_data_create(hist_data, data);
487062306a36Sopenharmony_ci			if (ret)
487162306a36Sopenharmony_ci				break;
487262306a36Sopenharmony_ci		} else {
487362306a36Sopenharmony_ci			ret = -EINVAL;
487462306a36Sopenharmony_ci			break;
487562306a36Sopenharmony_ci		}
487662306a36Sopenharmony_ci	}
487762306a36Sopenharmony_ci
487862306a36Sopenharmony_ci	return ret;
487962306a36Sopenharmony_ci}
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_cistatic void print_actions(struct seq_file *m,
488262306a36Sopenharmony_ci			  struct hist_trigger_data *hist_data,
488362306a36Sopenharmony_ci			  struct tracing_map_elt *elt)
488462306a36Sopenharmony_ci{
488562306a36Sopenharmony_ci	unsigned int i;
488662306a36Sopenharmony_ci
488762306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
488862306a36Sopenharmony_ci		struct action_data *data = hist_data->actions[i];
488962306a36Sopenharmony_ci
489062306a36Sopenharmony_ci		if (data->action == ACTION_SNAPSHOT)
489162306a36Sopenharmony_ci			continue;
489262306a36Sopenharmony_ci
489362306a36Sopenharmony_ci		if (data->handler == HANDLER_ONMAX ||
489462306a36Sopenharmony_ci		    data->handler == HANDLER_ONCHANGE)
489562306a36Sopenharmony_ci			track_data_print(m, hist_data, elt, data);
489662306a36Sopenharmony_ci	}
489762306a36Sopenharmony_ci}
489862306a36Sopenharmony_ci
489962306a36Sopenharmony_cistatic void print_action_spec(struct seq_file *m,
490062306a36Sopenharmony_ci			      struct hist_trigger_data *hist_data,
490162306a36Sopenharmony_ci			      struct action_data *data)
490262306a36Sopenharmony_ci{
490362306a36Sopenharmony_ci	unsigned int i;
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_ci	if (data->action == ACTION_SAVE) {
490662306a36Sopenharmony_ci		for (i = 0; i < hist_data->n_save_vars; i++) {
490762306a36Sopenharmony_ci			seq_printf(m, "%s", hist_data->save_vars[i]->var->var.name);
490862306a36Sopenharmony_ci			if (i < hist_data->n_save_vars - 1)
490962306a36Sopenharmony_ci				seq_puts(m, ",");
491062306a36Sopenharmony_ci		}
491162306a36Sopenharmony_ci	} else if (data->action == ACTION_TRACE) {
491262306a36Sopenharmony_ci		if (data->use_trace_keyword)
491362306a36Sopenharmony_ci			seq_printf(m, "%s", data->synth_event_name);
491462306a36Sopenharmony_ci		for (i = 0; i < data->n_params; i++) {
491562306a36Sopenharmony_ci			if (i || data->use_trace_keyword)
491662306a36Sopenharmony_ci				seq_puts(m, ",");
491762306a36Sopenharmony_ci			seq_printf(m, "%s", data->params[i]);
491862306a36Sopenharmony_ci		}
491962306a36Sopenharmony_ci	}
492062306a36Sopenharmony_ci}
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_cistatic void print_track_data_spec(struct seq_file *m,
492362306a36Sopenharmony_ci				  struct hist_trigger_data *hist_data,
492462306a36Sopenharmony_ci				  struct action_data *data)
492562306a36Sopenharmony_ci{
492662306a36Sopenharmony_ci	if (data->handler == HANDLER_ONMAX)
492762306a36Sopenharmony_ci		seq_puts(m, ":onmax(");
492862306a36Sopenharmony_ci	else if (data->handler == HANDLER_ONCHANGE)
492962306a36Sopenharmony_ci		seq_puts(m, ":onchange(");
493062306a36Sopenharmony_ci	seq_printf(m, "%s", data->track_data.var_str);
493162306a36Sopenharmony_ci	seq_printf(m, ").%s(", data->action_name);
493262306a36Sopenharmony_ci
493362306a36Sopenharmony_ci	print_action_spec(m, hist_data, data);
493462306a36Sopenharmony_ci
493562306a36Sopenharmony_ci	seq_puts(m, ")");
493662306a36Sopenharmony_ci}
493762306a36Sopenharmony_ci
493862306a36Sopenharmony_cistatic void print_onmatch_spec(struct seq_file *m,
493962306a36Sopenharmony_ci			       struct hist_trigger_data *hist_data,
494062306a36Sopenharmony_ci			       struct action_data *data)
494162306a36Sopenharmony_ci{
494262306a36Sopenharmony_ci	seq_printf(m, ":onmatch(%s.%s).", data->match_data.event_system,
494362306a36Sopenharmony_ci		   data->match_data.event);
494462306a36Sopenharmony_ci
494562306a36Sopenharmony_ci	seq_printf(m, "%s(", data->action_name);
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_ci	print_action_spec(m, hist_data, data);
494862306a36Sopenharmony_ci
494962306a36Sopenharmony_ci	seq_puts(m, ")");
495062306a36Sopenharmony_ci}
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_cistatic bool actions_match(struct hist_trigger_data *hist_data,
495362306a36Sopenharmony_ci			  struct hist_trigger_data *hist_data_test)
495462306a36Sopenharmony_ci{
495562306a36Sopenharmony_ci	unsigned int i, j;
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	if (hist_data->n_actions != hist_data_test->n_actions)
495862306a36Sopenharmony_ci		return false;
495962306a36Sopenharmony_ci
496062306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
496162306a36Sopenharmony_ci		struct action_data *data = hist_data->actions[i];
496262306a36Sopenharmony_ci		struct action_data *data_test = hist_data_test->actions[i];
496362306a36Sopenharmony_ci		char *action_name, *action_name_test;
496462306a36Sopenharmony_ci
496562306a36Sopenharmony_ci		if (data->handler != data_test->handler)
496662306a36Sopenharmony_ci			return false;
496762306a36Sopenharmony_ci		if (data->action != data_test->action)
496862306a36Sopenharmony_ci			return false;
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci		if (data->n_params != data_test->n_params)
497162306a36Sopenharmony_ci			return false;
497262306a36Sopenharmony_ci
497362306a36Sopenharmony_ci		for (j = 0; j < data->n_params; j++) {
497462306a36Sopenharmony_ci			if (strcmp(data->params[j], data_test->params[j]) != 0)
497562306a36Sopenharmony_ci				return false;
497662306a36Sopenharmony_ci		}
497762306a36Sopenharmony_ci
497862306a36Sopenharmony_ci		if (data->use_trace_keyword)
497962306a36Sopenharmony_ci			action_name = data->synth_event_name;
498062306a36Sopenharmony_ci		else
498162306a36Sopenharmony_ci			action_name = data->action_name;
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci		if (data_test->use_trace_keyword)
498462306a36Sopenharmony_ci			action_name_test = data_test->synth_event_name;
498562306a36Sopenharmony_ci		else
498662306a36Sopenharmony_ci			action_name_test = data_test->action_name;
498762306a36Sopenharmony_ci
498862306a36Sopenharmony_ci		if (strcmp(action_name, action_name_test) != 0)
498962306a36Sopenharmony_ci			return false;
499062306a36Sopenharmony_ci
499162306a36Sopenharmony_ci		if (data->handler == HANDLER_ONMATCH) {
499262306a36Sopenharmony_ci			if (strcmp(data->match_data.event_system,
499362306a36Sopenharmony_ci				   data_test->match_data.event_system) != 0)
499462306a36Sopenharmony_ci				return false;
499562306a36Sopenharmony_ci			if (strcmp(data->match_data.event,
499662306a36Sopenharmony_ci				   data_test->match_data.event) != 0)
499762306a36Sopenharmony_ci				return false;
499862306a36Sopenharmony_ci		} else if (data->handler == HANDLER_ONMAX ||
499962306a36Sopenharmony_ci			   data->handler == HANDLER_ONCHANGE) {
500062306a36Sopenharmony_ci			if (strcmp(data->track_data.var_str,
500162306a36Sopenharmony_ci				   data_test->track_data.var_str) != 0)
500262306a36Sopenharmony_ci				return false;
500362306a36Sopenharmony_ci		}
500462306a36Sopenharmony_ci	}
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_ci	return true;
500762306a36Sopenharmony_ci}
500862306a36Sopenharmony_ci
500962306a36Sopenharmony_ci
501062306a36Sopenharmony_cistatic void print_actions_spec(struct seq_file *m,
501162306a36Sopenharmony_ci			       struct hist_trigger_data *hist_data)
501262306a36Sopenharmony_ci{
501362306a36Sopenharmony_ci	unsigned int i;
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
501662306a36Sopenharmony_ci		struct action_data *data = hist_data->actions[i];
501762306a36Sopenharmony_ci
501862306a36Sopenharmony_ci		if (data->handler == HANDLER_ONMATCH)
501962306a36Sopenharmony_ci			print_onmatch_spec(m, hist_data, data);
502062306a36Sopenharmony_ci		else if (data->handler == HANDLER_ONMAX ||
502162306a36Sopenharmony_ci			 data->handler == HANDLER_ONCHANGE)
502262306a36Sopenharmony_ci			print_track_data_spec(m, hist_data, data);
502362306a36Sopenharmony_ci	}
502462306a36Sopenharmony_ci}
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_cistatic void destroy_field_var_hists(struct hist_trigger_data *hist_data)
502762306a36Sopenharmony_ci{
502862306a36Sopenharmony_ci	unsigned int i;
502962306a36Sopenharmony_ci
503062306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_field_var_hists; i++) {
503162306a36Sopenharmony_ci		kfree(hist_data->field_var_hists[i]->cmd);
503262306a36Sopenharmony_ci		kfree(hist_data->field_var_hists[i]);
503362306a36Sopenharmony_ci	}
503462306a36Sopenharmony_ci}
503562306a36Sopenharmony_ci
503662306a36Sopenharmony_cistatic void destroy_hist_data(struct hist_trigger_data *hist_data)
503762306a36Sopenharmony_ci{
503862306a36Sopenharmony_ci	if (!hist_data)
503962306a36Sopenharmony_ci		return;
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ci	destroy_hist_trigger_attrs(hist_data->attrs);
504262306a36Sopenharmony_ci	destroy_hist_fields(hist_data);
504362306a36Sopenharmony_ci	tracing_map_destroy(hist_data->map);
504462306a36Sopenharmony_ci
504562306a36Sopenharmony_ci	destroy_actions(hist_data);
504662306a36Sopenharmony_ci	destroy_field_vars(hist_data);
504762306a36Sopenharmony_ci	destroy_field_var_hists(hist_data);
504862306a36Sopenharmony_ci
504962306a36Sopenharmony_ci	kfree(hist_data);
505062306a36Sopenharmony_ci}
505162306a36Sopenharmony_ci
505262306a36Sopenharmony_cistatic int create_tracing_map_fields(struct hist_trigger_data *hist_data)
505362306a36Sopenharmony_ci{
505462306a36Sopenharmony_ci	struct tracing_map *map = hist_data->map;
505562306a36Sopenharmony_ci	struct ftrace_event_field *field;
505662306a36Sopenharmony_ci	struct hist_field *hist_field;
505762306a36Sopenharmony_ci	int i, idx = 0;
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_ci	for_each_hist_field(i, hist_data) {
506062306a36Sopenharmony_ci		hist_field = hist_data->fields[i];
506162306a36Sopenharmony_ci		if (hist_field->flags & HIST_FIELD_FL_KEY) {
506262306a36Sopenharmony_ci			tracing_map_cmp_fn_t cmp_fn;
506362306a36Sopenharmony_ci
506462306a36Sopenharmony_ci			field = hist_field->field;
506562306a36Sopenharmony_ci
506662306a36Sopenharmony_ci			if (hist_field->flags & HIST_FIELD_FL_STACKTRACE)
506762306a36Sopenharmony_ci				cmp_fn = tracing_map_cmp_none;
506862306a36Sopenharmony_ci			else if (!field || hist_field->flags & HIST_FIELD_FL_CPU)
506962306a36Sopenharmony_ci				cmp_fn = tracing_map_cmp_num(hist_field->size,
507062306a36Sopenharmony_ci							     hist_field->is_signed);
507162306a36Sopenharmony_ci			else if (is_string_field(field))
507262306a36Sopenharmony_ci				cmp_fn = tracing_map_cmp_string;
507362306a36Sopenharmony_ci			else
507462306a36Sopenharmony_ci				cmp_fn = tracing_map_cmp_num(field->size,
507562306a36Sopenharmony_ci							     field->is_signed);
507662306a36Sopenharmony_ci			idx = tracing_map_add_key_field(map,
507762306a36Sopenharmony_ci							hist_field->offset,
507862306a36Sopenharmony_ci							cmp_fn);
507962306a36Sopenharmony_ci		} else if (!(hist_field->flags & HIST_FIELD_FL_VAR))
508062306a36Sopenharmony_ci			idx = tracing_map_add_sum_field(map);
508162306a36Sopenharmony_ci
508262306a36Sopenharmony_ci		if (idx < 0)
508362306a36Sopenharmony_ci			return idx;
508462306a36Sopenharmony_ci
508562306a36Sopenharmony_ci		if (hist_field->flags & HIST_FIELD_FL_VAR) {
508662306a36Sopenharmony_ci			idx = tracing_map_add_var(map);
508762306a36Sopenharmony_ci			if (idx < 0)
508862306a36Sopenharmony_ci				return idx;
508962306a36Sopenharmony_ci			hist_field->var.idx = idx;
509062306a36Sopenharmony_ci			hist_field->var.hist_data = hist_data;
509162306a36Sopenharmony_ci		}
509262306a36Sopenharmony_ci	}
509362306a36Sopenharmony_ci
509462306a36Sopenharmony_ci	return 0;
509562306a36Sopenharmony_ci}
509662306a36Sopenharmony_ci
509762306a36Sopenharmony_cistatic struct hist_trigger_data *
509862306a36Sopenharmony_cicreate_hist_data(unsigned int map_bits,
509962306a36Sopenharmony_ci		 struct hist_trigger_attrs *attrs,
510062306a36Sopenharmony_ci		 struct trace_event_file *file,
510162306a36Sopenharmony_ci		 bool remove)
510262306a36Sopenharmony_ci{
510362306a36Sopenharmony_ci	const struct tracing_map_ops *map_ops = NULL;
510462306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
510562306a36Sopenharmony_ci	int ret = 0;
510662306a36Sopenharmony_ci
510762306a36Sopenharmony_ci	hist_data = kzalloc(sizeof(*hist_data), GFP_KERNEL);
510862306a36Sopenharmony_ci	if (!hist_data)
510962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
511062306a36Sopenharmony_ci
511162306a36Sopenharmony_ci	hist_data->attrs = attrs;
511262306a36Sopenharmony_ci	hist_data->remove = remove;
511362306a36Sopenharmony_ci	hist_data->event_file = file;
511462306a36Sopenharmony_ci
511562306a36Sopenharmony_ci	ret = parse_actions(hist_data);
511662306a36Sopenharmony_ci	if (ret)
511762306a36Sopenharmony_ci		goto free;
511862306a36Sopenharmony_ci
511962306a36Sopenharmony_ci	ret = create_hist_fields(hist_data, file);
512062306a36Sopenharmony_ci	if (ret)
512162306a36Sopenharmony_ci		goto free;
512262306a36Sopenharmony_ci
512362306a36Sopenharmony_ci	ret = create_sort_keys(hist_data);
512462306a36Sopenharmony_ci	if (ret)
512562306a36Sopenharmony_ci		goto free;
512662306a36Sopenharmony_ci
512762306a36Sopenharmony_ci	map_ops = &hist_trigger_elt_data_ops;
512862306a36Sopenharmony_ci
512962306a36Sopenharmony_ci	hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
513062306a36Sopenharmony_ci					    map_ops, hist_data);
513162306a36Sopenharmony_ci	if (IS_ERR(hist_data->map)) {
513262306a36Sopenharmony_ci		ret = PTR_ERR(hist_data->map);
513362306a36Sopenharmony_ci		hist_data->map = NULL;
513462306a36Sopenharmony_ci		goto free;
513562306a36Sopenharmony_ci	}
513662306a36Sopenharmony_ci
513762306a36Sopenharmony_ci	ret = create_tracing_map_fields(hist_data);
513862306a36Sopenharmony_ci	if (ret)
513962306a36Sopenharmony_ci		goto free;
514062306a36Sopenharmony_ci out:
514162306a36Sopenharmony_ci	return hist_data;
514262306a36Sopenharmony_ci free:
514362306a36Sopenharmony_ci	hist_data->attrs = NULL;
514462306a36Sopenharmony_ci
514562306a36Sopenharmony_ci	destroy_hist_data(hist_data);
514662306a36Sopenharmony_ci
514762306a36Sopenharmony_ci	hist_data = ERR_PTR(ret);
514862306a36Sopenharmony_ci
514962306a36Sopenharmony_ci	goto out;
515062306a36Sopenharmony_ci}
515162306a36Sopenharmony_ci
515262306a36Sopenharmony_cistatic void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
515362306a36Sopenharmony_ci				    struct tracing_map_elt *elt,
515462306a36Sopenharmony_ci				    struct trace_buffer *buffer, void *rec,
515562306a36Sopenharmony_ci				    struct ring_buffer_event *rbe,
515662306a36Sopenharmony_ci				    u64 *var_ref_vals)
515762306a36Sopenharmony_ci{
515862306a36Sopenharmony_ci	struct hist_elt_data *elt_data;
515962306a36Sopenharmony_ci	struct hist_field *hist_field;
516062306a36Sopenharmony_ci	unsigned int i, var_idx;
516162306a36Sopenharmony_ci	u64 hist_val;
516262306a36Sopenharmony_ci
516362306a36Sopenharmony_ci	elt_data = elt->private_data;
516462306a36Sopenharmony_ci	elt_data->var_ref_vals = var_ref_vals;
516562306a36Sopenharmony_ci
516662306a36Sopenharmony_ci	for_each_hist_val_field(i, hist_data) {
516762306a36Sopenharmony_ci		hist_field = hist_data->fields[i];
516862306a36Sopenharmony_ci		hist_val = hist_fn_call(hist_field, elt, buffer, rbe, rec);
516962306a36Sopenharmony_ci		if (hist_field->flags & HIST_FIELD_FL_VAR) {
517062306a36Sopenharmony_ci			var_idx = hist_field->var.idx;
517162306a36Sopenharmony_ci
517262306a36Sopenharmony_ci			if (hist_field->flags &
517362306a36Sopenharmony_ci			    (HIST_FIELD_FL_STRING | HIST_FIELD_FL_STACKTRACE)) {
517462306a36Sopenharmony_ci				unsigned int str_start, var_str_idx, idx;
517562306a36Sopenharmony_ci				char *str, *val_str;
517662306a36Sopenharmony_ci				unsigned int size;
517762306a36Sopenharmony_ci
517862306a36Sopenharmony_ci				str_start = hist_data->n_field_var_str +
517962306a36Sopenharmony_ci					hist_data->n_save_var_str;
518062306a36Sopenharmony_ci				var_str_idx = hist_field->var_str_idx;
518162306a36Sopenharmony_ci				idx = str_start + var_str_idx;
518262306a36Sopenharmony_ci
518362306a36Sopenharmony_ci				str = elt_data->field_var_str[idx];
518462306a36Sopenharmony_ci				val_str = (char *)(uintptr_t)hist_val;
518562306a36Sopenharmony_ci
518662306a36Sopenharmony_ci				if (hist_field->flags & HIST_FIELD_FL_STRING) {
518762306a36Sopenharmony_ci					size = min(hist_field->size, STR_VAR_LEN_MAX);
518862306a36Sopenharmony_ci					strscpy(str, val_str, size);
518962306a36Sopenharmony_ci				} else {
519062306a36Sopenharmony_ci					char *stack_start = str + sizeof(unsigned long);
519162306a36Sopenharmony_ci					int e;
519262306a36Sopenharmony_ci
519362306a36Sopenharmony_ci					e = stack_trace_save((void *)stack_start,
519462306a36Sopenharmony_ci							     HIST_STACKTRACE_DEPTH,
519562306a36Sopenharmony_ci							     HIST_STACKTRACE_SKIP);
519662306a36Sopenharmony_ci					if (e < HIST_STACKTRACE_DEPTH - 1)
519762306a36Sopenharmony_ci						((unsigned long *)stack_start)[e] = 0;
519862306a36Sopenharmony_ci					*((unsigned long *)str) = e;
519962306a36Sopenharmony_ci				}
520062306a36Sopenharmony_ci				hist_val = (u64)(uintptr_t)str;
520162306a36Sopenharmony_ci			}
520262306a36Sopenharmony_ci			tracing_map_set_var(elt, var_idx, hist_val);
520362306a36Sopenharmony_ci			continue;
520462306a36Sopenharmony_ci		}
520562306a36Sopenharmony_ci		tracing_map_update_sum(elt, i, hist_val);
520662306a36Sopenharmony_ci	}
520762306a36Sopenharmony_ci
520862306a36Sopenharmony_ci	for_each_hist_key_field(i, hist_data) {
520962306a36Sopenharmony_ci		hist_field = hist_data->fields[i];
521062306a36Sopenharmony_ci		if (hist_field->flags & HIST_FIELD_FL_VAR) {
521162306a36Sopenharmony_ci			hist_val = hist_fn_call(hist_field, elt, buffer, rbe, rec);
521262306a36Sopenharmony_ci			var_idx = hist_field->var.idx;
521362306a36Sopenharmony_ci			tracing_map_set_var(elt, var_idx, hist_val);
521462306a36Sopenharmony_ci		}
521562306a36Sopenharmony_ci	}
521662306a36Sopenharmony_ci
521762306a36Sopenharmony_ci	update_field_vars(hist_data, elt, buffer, rbe, rec);
521862306a36Sopenharmony_ci}
521962306a36Sopenharmony_ci
522062306a36Sopenharmony_cistatic inline void add_to_key(char *compound_key, void *key,
522162306a36Sopenharmony_ci			      struct hist_field *key_field, void *rec)
522262306a36Sopenharmony_ci{
522362306a36Sopenharmony_ci	size_t size = key_field->size;
522462306a36Sopenharmony_ci
522562306a36Sopenharmony_ci	if (key_field->flags & HIST_FIELD_FL_STRING) {
522662306a36Sopenharmony_ci		struct ftrace_event_field *field;
522762306a36Sopenharmony_ci
522862306a36Sopenharmony_ci		field = key_field->field;
522962306a36Sopenharmony_ci		if (field->filter_type == FILTER_DYN_STRING ||
523062306a36Sopenharmony_ci		    field->filter_type == FILTER_RDYN_STRING)
523162306a36Sopenharmony_ci			size = *(u32 *)(rec + field->offset) >> 16;
523262306a36Sopenharmony_ci		else if (field->filter_type == FILTER_STATIC_STRING)
523362306a36Sopenharmony_ci			size = field->size;
523462306a36Sopenharmony_ci
523562306a36Sopenharmony_ci		/* ensure NULL-termination */
523662306a36Sopenharmony_ci		if (size > key_field->size - 1)
523762306a36Sopenharmony_ci			size = key_field->size - 1;
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci		strncpy(compound_key + key_field->offset, (char *)key, size);
524062306a36Sopenharmony_ci	} else
524162306a36Sopenharmony_ci		memcpy(compound_key + key_field->offset, key, size);
524262306a36Sopenharmony_ci}
524362306a36Sopenharmony_ci
524462306a36Sopenharmony_cistatic void
524562306a36Sopenharmony_cihist_trigger_actions(struct hist_trigger_data *hist_data,
524662306a36Sopenharmony_ci		     struct tracing_map_elt *elt,
524762306a36Sopenharmony_ci		     struct trace_buffer *buffer, void *rec,
524862306a36Sopenharmony_ci		     struct ring_buffer_event *rbe, void *key,
524962306a36Sopenharmony_ci		     u64 *var_ref_vals)
525062306a36Sopenharmony_ci{
525162306a36Sopenharmony_ci	struct action_data *data;
525262306a36Sopenharmony_ci	unsigned int i;
525362306a36Sopenharmony_ci
525462306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
525562306a36Sopenharmony_ci		data = hist_data->actions[i];
525662306a36Sopenharmony_ci		data->fn(hist_data, elt, buffer, rec, rbe, key, data, var_ref_vals);
525762306a36Sopenharmony_ci	}
525862306a36Sopenharmony_ci}
525962306a36Sopenharmony_ci
526062306a36Sopenharmony_cistatic void event_hist_trigger(struct event_trigger_data *data,
526162306a36Sopenharmony_ci			       struct trace_buffer *buffer, void *rec,
526262306a36Sopenharmony_ci			       struct ring_buffer_event *rbe)
526362306a36Sopenharmony_ci{
526462306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
526562306a36Sopenharmony_ci	bool use_compound_key = (hist_data->n_keys > 1);
526662306a36Sopenharmony_ci	unsigned long entries[HIST_STACKTRACE_DEPTH];
526762306a36Sopenharmony_ci	u64 var_ref_vals[TRACING_MAP_VARS_MAX];
526862306a36Sopenharmony_ci	char compound_key[HIST_KEY_SIZE_MAX];
526962306a36Sopenharmony_ci	struct tracing_map_elt *elt = NULL;
527062306a36Sopenharmony_ci	struct hist_field *key_field;
527162306a36Sopenharmony_ci	u64 field_contents;
527262306a36Sopenharmony_ci	void *key = NULL;
527362306a36Sopenharmony_ci	unsigned int i;
527462306a36Sopenharmony_ci
527562306a36Sopenharmony_ci	if (unlikely(!rbe))
527662306a36Sopenharmony_ci		return;
527762306a36Sopenharmony_ci
527862306a36Sopenharmony_ci	memset(compound_key, 0, hist_data->key_size);
527962306a36Sopenharmony_ci
528062306a36Sopenharmony_ci	for_each_hist_key_field(i, hist_data) {
528162306a36Sopenharmony_ci		key_field = hist_data->fields[i];
528262306a36Sopenharmony_ci
528362306a36Sopenharmony_ci		if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
528462306a36Sopenharmony_ci			memset(entries, 0, HIST_STACKTRACE_SIZE);
528562306a36Sopenharmony_ci			if (key_field->field) {
528662306a36Sopenharmony_ci				unsigned long *stack, n_entries;
528762306a36Sopenharmony_ci
528862306a36Sopenharmony_ci				field_contents = hist_fn_call(key_field, elt, buffer, rbe, rec);
528962306a36Sopenharmony_ci				stack = (unsigned long *)(long)field_contents;
529062306a36Sopenharmony_ci				n_entries = *stack;
529162306a36Sopenharmony_ci				memcpy(entries, ++stack, n_entries * sizeof(unsigned long));
529262306a36Sopenharmony_ci			} else {
529362306a36Sopenharmony_ci				stack_trace_save(entries, HIST_STACKTRACE_DEPTH,
529462306a36Sopenharmony_ci						 HIST_STACKTRACE_SKIP);
529562306a36Sopenharmony_ci			}
529662306a36Sopenharmony_ci			key = entries;
529762306a36Sopenharmony_ci		} else {
529862306a36Sopenharmony_ci			field_contents = hist_fn_call(key_field, elt, buffer, rbe, rec);
529962306a36Sopenharmony_ci			if (key_field->flags & HIST_FIELD_FL_STRING) {
530062306a36Sopenharmony_ci				key = (void *)(unsigned long)field_contents;
530162306a36Sopenharmony_ci				use_compound_key = true;
530262306a36Sopenharmony_ci			} else
530362306a36Sopenharmony_ci				key = (void *)&field_contents;
530462306a36Sopenharmony_ci		}
530562306a36Sopenharmony_ci
530662306a36Sopenharmony_ci		if (use_compound_key)
530762306a36Sopenharmony_ci			add_to_key(compound_key, key, key_field, rec);
530862306a36Sopenharmony_ci	}
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_ci	if (use_compound_key)
531162306a36Sopenharmony_ci		key = compound_key;
531262306a36Sopenharmony_ci
531362306a36Sopenharmony_ci	if (hist_data->n_var_refs &&
531462306a36Sopenharmony_ci	    !resolve_var_refs(hist_data, key, var_ref_vals, false))
531562306a36Sopenharmony_ci		return;
531662306a36Sopenharmony_ci
531762306a36Sopenharmony_ci	elt = tracing_map_insert(hist_data->map, key);
531862306a36Sopenharmony_ci	if (!elt)
531962306a36Sopenharmony_ci		return;
532062306a36Sopenharmony_ci
532162306a36Sopenharmony_ci	hist_trigger_elt_update(hist_data, elt, buffer, rec, rbe, var_ref_vals);
532262306a36Sopenharmony_ci
532362306a36Sopenharmony_ci	if (resolve_var_refs(hist_data, key, var_ref_vals, true))
532462306a36Sopenharmony_ci		hist_trigger_actions(hist_data, elt, buffer, rec, rbe, key, var_ref_vals);
532562306a36Sopenharmony_ci}
532662306a36Sopenharmony_ci
532762306a36Sopenharmony_cistatic void hist_trigger_stacktrace_print(struct seq_file *m,
532862306a36Sopenharmony_ci					  unsigned long *stacktrace_entries,
532962306a36Sopenharmony_ci					  unsigned int max_entries)
533062306a36Sopenharmony_ci{
533162306a36Sopenharmony_ci	unsigned int spaces = 8;
533262306a36Sopenharmony_ci	unsigned int i;
533362306a36Sopenharmony_ci
533462306a36Sopenharmony_ci	for (i = 0; i < max_entries; i++) {
533562306a36Sopenharmony_ci		if (!stacktrace_entries[i])
533662306a36Sopenharmony_ci			return;
533762306a36Sopenharmony_ci
533862306a36Sopenharmony_ci		seq_printf(m, "%*c", 1 + spaces, ' ');
533962306a36Sopenharmony_ci		seq_printf(m, "%pS\n", (void*)stacktrace_entries[i]);
534062306a36Sopenharmony_ci	}
534162306a36Sopenharmony_ci}
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_cistatic void hist_trigger_print_key(struct seq_file *m,
534462306a36Sopenharmony_ci				   struct hist_trigger_data *hist_data,
534562306a36Sopenharmony_ci				   void *key,
534662306a36Sopenharmony_ci				   struct tracing_map_elt *elt)
534762306a36Sopenharmony_ci{
534862306a36Sopenharmony_ci	struct hist_field *key_field;
534962306a36Sopenharmony_ci	bool multiline = false;
535062306a36Sopenharmony_ci	const char *field_name;
535162306a36Sopenharmony_ci	unsigned int i;
535262306a36Sopenharmony_ci	u64 uval;
535362306a36Sopenharmony_ci
535462306a36Sopenharmony_ci	seq_puts(m, "{ ");
535562306a36Sopenharmony_ci
535662306a36Sopenharmony_ci	for_each_hist_key_field(i, hist_data) {
535762306a36Sopenharmony_ci		key_field = hist_data->fields[i];
535862306a36Sopenharmony_ci
535962306a36Sopenharmony_ci		if (i > hist_data->n_vals)
536062306a36Sopenharmony_ci			seq_puts(m, ", ");
536162306a36Sopenharmony_ci
536262306a36Sopenharmony_ci		field_name = hist_field_name(key_field, 0);
536362306a36Sopenharmony_ci
536462306a36Sopenharmony_ci		if (key_field->flags & HIST_FIELD_FL_HEX) {
536562306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
536662306a36Sopenharmony_ci			seq_printf(m, "%s: %llx", field_name, uval);
536762306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_SYM) {
536862306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
536962306a36Sopenharmony_ci			seq_printf(m, "%s: [%llx] %-45ps", field_name,
537062306a36Sopenharmony_ci				   uval, (void *)(uintptr_t)uval);
537162306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_SYM_OFFSET) {
537262306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
537362306a36Sopenharmony_ci			seq_printf(m, "%s: [%llx] %-55pS", field_name,
537462306a36Sopenharmony_ci				   uval, (void *)(uintptr_t)uval);
537562306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
537662306a36Sopenharmony_ci			struct hist_elt_data *elt_data = elt->private_data;
537762306a36Sopenharmony_ci			char *comm;
537862306a36Sopenharmony_ci
537962306a36Sopenharmony_ci			if (WARN_ON_ONCE(!elt_data))
538062306a36Sopenharmony_ci				return;
538162306a36Sopenharmony_ci
538262306a36Sopenharmony_ci			comm = elt_data->comm;
538362306a36Sopenharmony_ci
538462306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
538562306a36Sopenharmony_ci			seq_printf(m, "%s: %-16s[%10llu]", field_name,
538662306a36Sopenharmony_ci				   comm, uval);
538762306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_SYSCALL) {
538862306a36Sopenharmony_ci			const char *syscall_name;
538962306a36Sopenharmony_ci
539062306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
539162306a36Sopenharmony_ci			syscall_name = get_syscall_name(uval);
539262306a36Sopenharmony_ci			if (!syscall_name)
539362306a36Sopenharmony_ci				syscall_name = "unknown_syscall";
539462306a36Sopenharmony_ci
539562306a36Sopenharmony_ci			seq_printf(m, "%s: %-30s[%3llu]", field_name,
539662306a36Sopenharmony_ci				   syscall_name, uval);
539762306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
539862306a36Sopenharmony_ci			if (key_field->field)
539962306a36Sopenharmony_ci				seq_printf(m, "%s.stacktrace", key_field->field->name);
540062306a36Sopenharmony_ci			else
540162306a36Sopenharmony_ci				seq_puts(m, "common_stacktrace:\n");
540262306a36Sopenharmony_ci			hist_trigger_stacktrace_print(m,
540362306a36Sopenharmony_ci						      key + key_field->offset,
540462306a36Sopenharmony_ci						      HIST_STACKTRACE_DEPTH);
540562306a36Sopenharmony_ci			multiline = true;
540662306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_LOG2) {
540762306a36Sopenharmony_ci			seq_printf(m, "%s: ~ 2^%-2llu", field_name,
540862306a36Sopenharmony_ci				   *(u64 *)(key + key_field->offset));
540962306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_BUCKET) {
541062306a36Sopenharmony_ci			unsigned long buckets = key_field->buckets;
541162306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
541262306a36Sopenharmony_ci			seq_printf(m, "%s: ~ %llu-%llu", field_name,
541362306a36Sopenharmony_ci				   uval, uval + buckets -1);
541462306a36Sopenharmony_ci		} else if (key_field->flags & HIST_FIELD_FL_STRING) {
541562306a36Sopenharmony_ci			seq_printf(m, "%s: %-50s", field_name,
541662306a36Sopenharmony_ci				   (char *)(key + key_field->offset));
541762306a36Sopenharmony_ci		} else {
541862306a36Sopenharmony_ci			uval = *(u64 *)(key + key_field->offset);
541962306a36Sopenharmony_ci			seq_printf(m, "%s: %10llu", field_name, uval);
542062306a36Sopenharmony_ci		}
542162306a36Sopenharmony_ci	}
542262306a36Sopenharmony_ci
542362306a36Sopenharmony_ci	if (!multiline)
542462306a36Sopenharmony_ci		seq_puts(m, " ");
542562306a36Sopenharmony_ci
542662306a36Sopenharmony_ci	seq_puts(m, "}");
542762306a36Sopenharmony_ci}
542862306a36Sopenharmony_ci
542962306a36Sopenharmony_ci/* Get the 100 times of the percentage of @val in @total */
543062306a36Sopenharmony_cistatic inline unsigned int __get_percentage(u64 val, u64 total)
543162306a36Sopenharmony_ci{
543262306a36Sopenharmony_ci	if (!total)
543362306a36Sopenharmony_ci		goto div0;
543462306a36Sopenharmony_ci
543562306a36Sopenharmony_ci	if (val < (U64_MAX / 10000))
543662306a36Sopenharmony_ci		return (unsigned int)div64_ul(val * 10000, total);
543762306a36Sopenharmony_ci
543862306a36Sopenharmony_ci	total = div64_u64(total, 10000);
543962306a36Sopenharmony_ci	if (!total)
544062306a36Sopenharmony_ci		goto div0;
544162306a36Sopenharmony_ci
544262306a36Sopenharmony_ci	return (unsigned int)div64_ul(val, total);
544362306a36Sopenharmony_cidiv0:
544462306a36Sopenharmony_ci	return val ? UINT_MAX : 0;
544562306a36Sopenharmony_ci}
544662306a36Sopenharmony_ci
544762306a36Sopenharmony_ci#define BAR_CHAR '#'
544862306a36Sopenharmony_ci
544962306a36Sopenharmony_cistatic inline const char *__fill_bar_str(char *buf, int size, u64 val, u64 max)
545062306a36Sopenharmony_ci{
545162306a36Sopenharmony_ci	unsigned int len = __get_percentage(val, max);
545262306a36Sopenharmony_ci	int i;
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_ci	if (len == UINT_MAX) {
545562306a36Sopenharmony_ci		snprintf(buf, size, "[ERROR]");
545662306a36Sopenharmony_ci		return buf;
545762306a36Sopenharmony_ci	}
545862306a36Sopenharmony_ci
545962306a36Sopenharmony_ci	len = len * size / 10000;
546062306a36Sopenharmony_ci	for (i = 0; i < len && i < size; i++)
546162306a36Sopenharmony_ci		buf[i] = BAR_CHAR;
546262306a36Sopenharmony_ci	while (i < size)
546362306a36Sopenharmony_ci		buf[i++] = ' ';
546462306a36Sopenharmony_ci	buf[size] = '\0';
546562306a36Sopenharmony_ci
546662306a36Sopenharmony_ci	return buf;
546762306a36Sopenharmony_ci}
546862306a36Sopenharmony_ci
546962306a36Sopenharmony_cistruct hist_val_stat {
547062306a36Sopenharmony_ci	u64 max;
547162306a36Sopenharmony_ci	u64 total;
547262306a36Sopenharmony_ci};
547362306a36Sopenharmony_ci
547462306a36Sopenharmony_cistatic void hist_trigger_print_val(struct seq_file *m, unsigned int idx,
547562306a36Sopenharmony_ci				   const char *field_name, unsigned long flags,
547662306a36Sopenharmony_ci				   struct hist_val_stat *stats,
547762306a36Sopenharmony_ci				   struct tracing_map_elt *elt)
547862306a36Sopenharmony_ci{
547962306a36Sopenharmony_ci	u64 val = tracing_map_read_sum(elt, idx);
548062306a36Sopenharmony_ci	unsigned int pc;
548162306a36Sopenharmony_ci	char bar[21];
548262306a36Sopenharmony_ci
548362306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_PERCENT) {
548462306a36Sopenharmony_ci		pc = __get_percentage(val, stats[idx].total);
548562306a36Sopenharmony_ci		if (pc == UINT_MAX)
548662306a36Sopenharmony_ci			seq_printf(m, " %s (%%):[ERROR]", field_name);
548762306a36Sopenharmony_ci		else
548862306a36Sopenharmony_ci			seq_printf(m, " %s (%%): %3u.%02u", field_name,
548962306a36Sopenharmony_ci					pc / 100, pc % 100);
549062306a36Sopenharmony_ci	} else if (flags & HIST_FIELD_FL_GRAPH) {
549162306a36Sopenharmony_ci		seq_printf(m, " %s: %20s", field_name,
549262306a36Sopenharmony_ci			   __fill_bar_str(bar, 20, val, stats[idx].max));
549362306a36Sopenharmony_ci	} else if (flags & HIST_FIELD_FL_HEX) {
549462306a36Sopenharmony_ci		seq_printf(m, " %s: %10llx", field_name, val);
549562306a36Sopenharmony_ci	} else {
549662306a36Sopenharmony_ci		seq_printf(m, " %s: %10llu", field_name, val);
549762306a36Sopenharmony_ci	}
549862306a36Sopenharmony_ci}
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_cistatic void hist_trigger_entry_print(struct seq_file *m,
550162306a36Sopenharmony_ci				     struct hist_trigger_data *hist_data,
550262306a36Sopenharmony_ci				     struct hist_val_stat *stats,
550362306a36Sopenharmony_ci				     void *key,
550462306a36Sopenharmony_ci				     struct tracing_map_elt *elt)
550562306a36Sopenharmony_ci{
550662306a36Sopenharmony_ci	const char *field_name;
550762306a36Sopenharmony_ci	unsigned int i = HITCOUNT_IDX;
550862306a36Sopenharmony_ci	unsigned long flags;
550962306a36Sopenharmony_ci
551062306a36Sopenharmony_ci	hist_trigger_print_key(m, hist_data, key, elt);
551162306a36Sopenharmony_ci
551262306a36Sopenharmony_ci	/* At first, show the raw hitcount if !nohitcount */
551362306a36Sopenharmony_ci	if (!hist_data->attrs->no_hitcount)
551462306a36Sopenharmony_ci		hist_trigger_print_val(m, i, "hitcount", 0, stats, elt);
551562306a36Sopenharmony_ci
551662306a36Sopenharmony_ci	for (i = 1; i < hist_data->n_vals; i++) {
551762306a36Sopenharmony_ci		field_name = hist_field_name(hist_data->fields[i], 0);
551862306a36Sopenharmony_ci		flags = hist_data->fields[i]->flags;
551962306a36Sopenharmony_ci		if (flags & HIST_FIELD_FL_VAR || flags & HIST_FIELD_FL_EXPR)
552062306a36Sopenharmony_ci			continue;
552162306a36Sopenharmony_ci
552262306a36Sopenharmony_ci		seq_puts(m, " ");
552362306a36Sopenharmony_ci		hist_trigger_print_val(m, i, field_name, flags, stats, elt);
552462306a36Sopenharmony_ci	}
552562306a36Sopenharmony_ci
552662306a36Sopenharmony_ci	print_actions(m, hist_data, elt);
552762306a36Sopenharmony_ci
552862306a36Sopenharmony_ci	seq_puts(m, "\n");
552962306a36Sopenharmony_ci}
553062306a36Sopenharmony_ci
553162306a36Sopenharmony_cistatic int print_entries(struct seq_file *m,
553262306a36Sopenharmony_ci			 struct hist_trigger_data *hist_data)
553362306a36Sopenharmony_ci{
553462306a36Sopenharmony_ci	struct tracing_map_sort_entry **sort_entries = NULL;
553562306a36Sopenharmony_ci	struct tracing_map *map = hist_data->map;
553662306a36Sopenharmony_ci	int i, j, n_entries;
553762306a36Sopenharmony_ci	struct hist_val_stat *stats = NULL;
553862306a36Sopenharmony_ci	u64 val;
553962306a36Sopenharmony_ci
554062306a36Sopenharmony_ci	n_entries = tracing_map_sort_entries(map, hist_data->sort_keys,
554162306a36Sopenharmony_ci					     hist_data->n_sort_keys,
554262306a36Sopenharmony_ci					     &sort_entries);
554362306a36Sopenharmony_ci	if (n_entries < 0)
554462306a36Sopenharmony_ci		return n_entries;
554562306a36Sopenharmony_ci
554662306a36Sopenharmony_ci	/* Calculate the max and the total for each field if needed. */
554762306a36Sopenharmony_ci	for (j = 0; j < hist_data->n_vals; j++) {
554862306a36Sopenharmony_ci		if (!(hist_data->fields[j]->flags &
554962306a36Sopenharmony_ci			(HIST_FIELD_FL_PERCENT | HIST_FIELD_FL_GRAPH)))
555062306a36Sopenharmony_ci			continue;
555162306a36Sopenharmony_ci		if (!stats) {
555262306a36Sopenharmony_ci			stats = kcalloc(hist_data->n_vals, sizeof(*stats),
555362306a36Sopenharmony_ci				       GFP_KERNEL);
555462306a36Sopenharmony_ci			if (!stats) {
555562306a36Sopenharmony_ci				n_entries = -ENOMEM;
555662306a36Sopenharmony_ci				goto out;
555762306a36Sopenharmony_ci			}
555862306a36Sopenharmony_ci		}
555962306a36Sopenharmony_ci		for (i = 0; i < n_entries; i++) {
556062306a36Sopenharmony_ci			val = tracing_map_read_sum(sort_entries[i]->elt, j);
556162306a36Sopenharmony_ci			stats[j].total += val;
556262306a36Sopenharmony_ci			if (stats[j].max < val)
556362306a36Sopenharmony_ci				stats[j].max = val;
556462306a36Sopenharmony_ci		}
556562306a36Sopenharmony_ci	}
556662306a36Sopenharmony_ci
556762306a36Sopenharmony_ci	for (i = 0; i < n_entries; i++)
556862306a36Sopenharmony_ci		hist_trigger_entry_print(m, hist_data, stats,
556962306a36Sopenharmony_ci					 sort_entries[i]->key,
557062306a36Sopenharmony_ci					 sort_entries[i]->elt);
557162306a36Sopenharmony_ci
557262306a36Sopenharmony_ci	kfree(stats);
557362306a36Sopenharmony_ciout:
557462306a36Sopenharmony_ci	tracing_map_destroy_sort_entries(sort_entries, n_entries);
557562306a36Sopenharmony_ci
557662306a36Sopenharmony_ci	return n_entries;
557762306a36Sopenharmony_ci}
557862306a36Sopenharmony_ci
557962306a36Sopenharmony_cistatic void hist_trigger_show(struct seq_file *m,
558062306a36Sopenharmony_ci			      struct event_trigger_data *data, int n)
558162306a36Sopenharmony_ci{
558262306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
558362306a36Sopenharmony_ci	int n_entries;
558462306a36Sopenharmony_ci
558562306a36Sopenharmony_ci	if (n > 0)
558662306a36Sopenharmony_ci		seq_puts(m, "\n\n");
558762306a36Sopenharmony_ci
558862306a36Sopenharmony_ci	seq_puts(m, "# event histogram\n#\n# trigger info: ");
558962306a36Sopenharmony_ci	data->ops->print(m, data);
559062306a36Sopenharmony_ci	seq_puts(m, "#\n\n");
559162306a36Sopenharmony_ci
559262306a36Sopenharmony_ci	hist_data = data->private_data;
559362306a36Sopenharmony_ci	n_entries = print_entries(m, hist_data);
559462306a36Sopenharmony_ci	if (n_entries < 0)
559562306a36Sopenharmony_ci		n_entries = 0;
559662306a36Sopenharmony_ci
559762306a36Sopenharmony_ci	track_data_snapshot_print(m, hist_data);
559862306a36Sopenharmony_ci
559962306a36Sopenharmony_ci	seq_printf(m, "\nTotals:\n    Hits: %llu\n    Entries: %u\n    Dropped: %llu\n",
560062306a36Sopenharmony_ci		   (u64)atomic64_read(&hist_data->map->hits),
560162306a36Sopenharmony_ci		   n_entries, (u64)atomic64_read(&hist_data->map->drops));
560262306a36Sopenharmony_ci}
560362306a36Sopenharmony_ci
560462306a36Sopenharmony_cistatic int hist_show(struct seq_file *m, void *v)
560562306a36Sopenharmony_ci{
560662306a36Sopenharmony_ci	struct event_trigger_data *data;
560762306a36Sopenharmony_ci	struct trace_event_file *event_file;
560862306a36Sopenharmony_ci	int n = 0, ret = 0;
560962306a36Sopenharmony_ci
561062306a36Sopenharmony_ci	mutex_lock(&event_mutex);
561162306a36Sopenharmony_ci
561262306a36Sopenharmony_ci	event_file = event_file_data(m->private);
561362306a36Sopenharmony_ci	if (unlikely(!event_file)) {
561462306a36Sopenharmony_ci		ret = -ENODEV;
561562306a36Sopenharmony_ci		goto out_unlock;
561662306a36Sopenharmony_ci	}
561762306a36Sopenharmony_ci
561862306a36Sopenharmony_ci	list_for_each_entry(data, &event_file->triggers, list) {
561962306a36Sopenharmony_ci		if (data->cmd_ops->trigger_type == ETT_EVENT_HIST)
562062306a36Sopenharmony_ci			hist_trigger_show(m, data, n++);
562162306a36Sopenharmony_ci	}
562262306a36Sopenharmony_ci
562362306a36Sopenharmony_ci out_unlock:
562462306a36Sopenharmony_ci	mutex_unlock(&event_mutex);
562562306a36Sopenharmony_ci
562662306a36Sopenharmony_ci	return ret;
562762306a36Sopenharmony_ci}
562862306a36Sopenharmony_ci
562962306a36Sopenharmony_cistatic int event_hist_open(struct inode *inode, struct file *file)
563062306a36Sopenharmony_ci{
563162306a36Sopenharmony_ci	int ret;
563262306a36Sopenharmony_ci
563362306a36Sopenharmony_ci	ret = tracing_open_file_tr(inode, file);
563462306a36Sopenharmony_ci	if (ret)
563562306a36Sopenharmony_ci		return ret;
563662306a36Sopenharmony_ci
563762306a36Sopenharmony_ci	/* Clear private_data to avoid warning in single_open() */
563862306a36Sopenharmony_ci	file->private_data = NULL;
563962306a36Sopenharmony_ci	return single_open(file, hist_show, file);
564062306a36Sopenharmony_ci}
564162306a36Sopenharmony_ci
564262306a36Sopenharmony_ciconst struct file_operations event_hist_fops = {
564362306a36Sopenharmony_ci	.open = event_hist_open,
564462306a36Sopenharmony_ci	.read = seq_read,
564562306a36Sopenharmony_ci	.llseek = seq_lseek,
564662306a36Sopenharmony_ci	.release = tracing_single_release_file_tr,
564762306a36Sopenharmony_ci};
564862306a36Sopenharmony_ci
564962306a36Sopenharmony_ci#ifdef CONFIG_HIST_TRIGGERS_DEBUG
565062306a36Sopenharmony_cistatic void hist_field_debug_show_flags(struct seq_file *m,
565162306a36Sopenharmony_ci					unsigned long flags)
565262306a36Sopenharmony_ci{
565362306a36Sopenharmony_ci	seq_puts(m, "      flags:\n");
565462306a36Sopenharmony_ci
565562306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_KEY)
565662306a36Sopenharmony_ci		seq_puts(m, "        HIST_FIELD_FL_KEY\n");
565762306a36Sopenharmony_ci	else if (flags & HIST_FIELD_FL_HITCOUNT)
565862306a36Sopenharmony_ci		seq_puts(m, "        VAL: HIST_FIELD_FL_HITCOUNT\n");
565962306a36Sopenharmony_ci	else if (flags & HIST_FIELD_FL_VAR)
566062306a36Sopenharmony_ci		seq_puts(m, "        HIST_FIELD_FL_VAR\n");
566162306a36Sopenharmony_ci	else if (flags & HIST_FIELD_FL_VAR_REF)
566262306a36Sopenharmony_ci		seq_puts(m, "        HIST_FIELD_FL_VAR_REF\n");
566362306a36Sopenharmony_ci	else
566462306a36Sopenharmony_ci		seq_puts(m, "        VAL: normal u64 value\n");
566562306a36Sopenharmony_ci
566662306a36Sopenharmony_ci	if (flags & HIST_FIELD_FL_ALIAS)
566762306a36Sopenharmony_ci		seq_puts(m, "        HIST_FIELD_FL_ALIAS\n");
566862306a36Sopenharmony_ci	else if (flags & HIST_FIELD_FL_CONST)
566962306a36Sopenharmony_ci		seq_puts(m, "        HIST_FIELD_FL_CONST\n");
567062306a36Sopenharmony_ci}
567162306a36Sopenharmony_ci
567262306a36Sopenharmony_cistatic int hist_field_debug_show(struct seq_file *m,
567362306a36Sopenharmony_ci				 struct hist_field *field, unsigned long flags)
567462306a36Sopenharmony_ci{
567562306a36Sopenharmony_ci	if ((field->flags & flags) != flags) {
567662306a36Sopenharmony_ci		seq_printf(m, "ERROR: bad flags - %lx\n", flags);
567762306a36Sopenharmony_ci		return -EINVAL;
567862306a36Sopenharmony_ci	}
567962306a36Sopenharmony_ci
568062306a36Sopenharmony_ci	hist_field_debug_show_flags(m, field->flags);
568162306a36Sopenharmony_ci	if (field->field)
568262306a36Sopenharmony_ci		seq_printf(m, "      ftrace_event_field name: %s\n",
568362306a36Sopenharmony_ci			   field->field->name);
568462306a36Sopenharmony_ci
568562306a36Sopenharmony_ci	if (field->flags & HIST_FIELD_FL_VAR) {
568662306a36Sopenharmony_ci		seq_printf(m, "      var.name: %s\n", field->var.name);
568762306a36Sopenharmony_ci		seq_printf(m, "      var.idx (into tracing_map_elt.vars[]): %u\n",
568862306a36Sopenharmony_ci			   field->var.idx);
568962306a36Sopenharmony_ci	}
569062306a36Sopenharmony_ci
569162306a36Sopenharmony_ci	if (field->flags & HIST_FIELD_FL_CONST)
569262306a36Sopenharmony_ci		seq_printf(m, "      constant: %llu\n", field->constant);
569362306a36Sopenharmony_ci
569462306a36Sopenharmony_ci	if (field->flags & HIST_FIELD_FL_ALIAS)
569562306a36Sopenharmony_ci		seq_printf(m, "      var_ref_idx (into hist_data->var_refs[]): %u\n",
569662306a36Sopenharmony_ci			   field->var_ref_idx);
569762306a36Sopenharmony_ci
569862306a36Sopenharmony_ci	if (field->flags & HIST_FIELD_FL_VAR_REF) {
569962306a36Sopenharmony_ci		seq_printf(m, "      name: %s\n", field->name);
570062306a36Sopenharmony_ci		seq_printf(m, "      var.idx (into tracing_map_elt.vars[]): %u\n",
570162306a36Sopenharmony_ci			   field->var.idx);
570262306a36Sopenharmony_ci		seq_printf(m, "      var.hist_data: %p\n", field->var.hist_data);
570362306a36Sopenharmony_ci		seq_printf(m, "      var_ref_idx (into hist_data->var_refs[]): %u\n",
570462306a36Sopenharmony_ci			   field->var_ref_idx);
570562306a36Sopenharmony_ci		if (field->system)
570662306a36Sopenharmony_ci			seq_printf(m, "      system: %s\n", field->system);
570762306a36Sopenharmony_ci		if (field->event_name)
570862306a36Sopenharmony_ci			seq_printf(m, "      event_name: %s\n", field->event_name);
570962306a36Sopenharmony_ci	}
571062306a36Sopenharmony_ci
571162306a36Sopenharmony_ci	seq_printf(m, "      type: %s\n", field->type);
571262306a36Sopenharmony_ci	seq_printf(m, "      size: %u\n", field->size);
571362306a36Sopenharmony_ci	seq_printf(m, "      is_signed: %u\n", field->is_signed);
571462306a36Sopenharmony_ci
571562306a36Sopenharmony_ci	return 0;
571662306a36Sopenharmony_ci}
571762306a36Sopenharmony_ci
571862306a36Sopenharmony_cistatic int field_var_debug_show(struct seq_file *m,
571962306a36Sopenharmony_ci				struct field_var *field_var, unsigned int i,
572062306a36Sopenharmony_ci				bool save_vars)
572162306a36Sopenharmony_ci{
572262306a36Sopenharmony_ci	const char *vars_name = save_vars ? "save_vars" : "field_vars";
572362306a36Sopenharmony_ci	struct hist_field *field;
572462306a36Sopenharmony_ci	int ret = 0;
572562306a36Sopenharmony_ci
572662306a36Sopenharmony_ci	seq_printf(m, "\n    hist_data->%s[%d]:\n", vars_name, i);
572762306a36Sopenharmony_ci
572862306a36Sopenharmony_ci	field = field_var->var;
572962306a36Sopenharmony_ci
573062306a36Sopenharmony_ci	seq_printf(m, "\n      %s[%d].var:\n", vars_name, i);
573162306a36Sopenharmony_ci
573262306a36Sopenharmony_ci	hist_field_debug_show_flags(m, field->flags);
573362306a36Sopenharmony_ci	seq_printf(m, "      var.name: %s\n", field->var.name);
573462306a36Sopenharmony_ci	seq_printf(m, "      var.idx (into tracing_map_elt.vars[]): %u\n",
573562306a36Sopenharmony_ci		   field->var.idx);
573662306a36Sopenharmony_ci
573762306a36Sopenharmony_ci	field = field_var->val;
573862306a36Sopenharmony_ci
573962306a36Sopenharmony_ci	seq_printf(m, "\n      %s[%d].val:\n", vars_name, i);
574062306a36Sopenharmony_ci	if (field->field)
574162306a36Sopenharmony_ci		seq_printf(m, "      ftrace_event_field name: %s\n",
574262306a36Sopenharmony_ci			   field->field->name);
574362306a36Sopenharmony_ci	else {
574462306a36Sopenharmony_ci		ret = -EINVAL;
574562306a36Sopenharmony_ci		goto out;
574662306a36Sopenharmony_ci	}
574762306a36Sopenharmony_ci
574862306a36Sopenharmony_ci	seq_printf(m, "      type: %s\n", field->type);
574962306a36Sopenharmony_ci	seq_printf(m, "      size: %u\n", field->size);
575062306a36Sopenharmony_ci	seq_printf(m, "      is_signed: %u\n", field->is_signed);
575162306a36Sopenharmony_ciout:
575262306a36Sopenharmony_ci	return ret;
575362306a36Sopenharmony_ci}
575462306a36Sopenharmony_ci
575562306a36Sopenharmony_cistatic int hist_action_debug_show(struct seq_file *m,
575662306a36Sopenharmony_ci				  struct action_data *data, int i)
575762306a36Sopenharmony_ci{
575862306a36Sopenharmony_ci	int ret = 0;
575962306a36Sopenharmony_ci
576062306a36Sopenharmony_ci	if (data->handler == HANDLER_ONMAX ||
576162306a36Sopenharmony_ci	    data->handler == HANDLER_ONCHANGE) {
576262306a36Sopenharmony_ci		seq_printf(m, "\n    hist_data->actions[%d].track_data.var_ref:\n", i);
576362306a36Sopenharmony_ci		ret = hist_field_debug_show(m, data->track_data.var_ref,
576462306a36Sopenharmony_ci					    HIST_FIELD_FL_VAR_REF);
576562306a36Sopenharmony_ci		if (ret)
576662306a36Sopenharmony_ci			goto out;
576762306a36Sopenharmony_ci
576862306a36Sopenharmony_ci		seq_printf(m, "\n    hist_data->actions[%d].track_data.track_var:\n", i);
576962306a36Sopenharmony_ci		ret = hist_field_debug_show(m, data->track_data.track_var,
577062306a36Sopenharmony_ci					    HIST_FIELD_FL_VAR);
577162306a36Sopenharmony_ci		if (ret)
577262306a36Sopenharmony_ci			goto out;
577362306a36Sopenharmony_ci	}
577462306a36Sopenharmony_ci
577562306a36Sopenharmony_ci	if (data->handler == HANDLER_ONMATCH) {
577662306a36Sopenharmony_ci		seq_printf(m, "\n    hist_data->actions[%d].match_data.event_system: %s\n",
577762306a36Sopenharmony_ci			   i, data->match_data.event_system);
577862306a36Sopenharmony_ci		seq_printf(m, "    hist_data->actions[%d].match_data.event: %s\n",
577962306a36Sopenharmony_ci			   i, data->match_data.event);
578062306a36Sopenharmony_ci	}
578162306a36Sopenharmony_ciout:
578262306a36Sopenharmony_ci	return ret;
578362306a36Sopenharmony_ci}
578462306a36Sopenharmony_ci
578562306a36Sopenharmony_cistatic int hist_actions_debug_show(struct seq_file *m,
578662306a36Sopenharmony_ci				   struct hist_trigger_data *hist_data)
578762306a36Sopenharmony_ci{
578862306a36Sopenharmony_ci	int i, ret = 0;
578962306a36Sopenharmony_ci
579062306a36Sopenharmony_ci	if (hist_data->n_actions)
579162306a36Sopenharmony_ci		seq_puts(m, "\n  action tracking variables (for onmax()/onchange()/onmatch()):\n");
579262306a36Sopenharmony_ci
579362306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_actions; i++) {
579462306a36Sopenharmony_ci		struct action_data *action = hist_data->actions[i];
579562306a36Sopenharmony_ci
579662306a36Sopenharmony_ci		ret = hist_action_debug_show(m, action, i);
579762306a36Sopenharmony_ci		if (ret)
579862306a36Sopenharmony_ci			goto out;
579962306a36Sopenharmony_ci	}
580062306a36Sopenharmony_ci
580162306a36Sopenharmony_ci	if (hist_data->n_save_vars)
580262306a36Sopenharmony_ci		seq_puts(m, "\n  save action variables (save() params):\n");
580362306a36Sopenharmony_ci
580462306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_save_vars; i++) {
580562306a36Sopenharmony_ci		ret = field_var_debug_show(m, hist_data->save_vars[i], i, true);
580662306a36Sopenharmony_ci		if (ret)
580762306a36Sopenharmony_ci			goto out;
580862306a36Sopenharmony_ci	}
580962306a36Sopenharmony_ciout:
581062306a36Sopenharmony_ci	return ret;
581162306a36Sopenharmony_ci}
581262306a36Sopenharmony_ci
581362306a36Sopenharmony_cistatic void hist_trigger_debug_show(struct seq_file *m,
581462306a36Sopenharmony_ci				    struct event_trigger_data *data, int n)
581562306a36Sopenharmony_ci{
581662306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
581762306a36Sopenharmony_ci	int i, ret;
581862306a36Sopenharmony_ci
581962306a36Sopenharmony_ci	if (n > 0)
582062306a36Sopenharmony_ci		seq_puts(m, "\n\n");
582162306a36Sopenharmony_ci
582262306a36Sopenharmony_ci	seq_puts(m, "# event histogram\n#\n# trigger info: ");
582362306a36Sopenharmony_ci	data->ops->print(m, data);
582462306a36Sopenharmony_ci	seq_puts(m, "#\n\n");
582562306a36Sopenharmony_ci
582662306a36Sopenharmony_ci	hist_data = data->private_data;
582762306a36Sopenharmony_ci
582862306a36Sopenharmony_ci	seq_printf(m, "hist_data: %p\n\n", hist_data);
582962306a36Sopenharmony_ci	seq_printf(m, "  n_vals: %u\n", hist_data->n_vals);
583062306a36Sopenharmony_ci	seq_printf(m, "  n_keys: %u\n", hist_data->n_keys);
583162306a36Sopenharmony_ci	seq_printf(m, "  n_fields: %u\n", hist_data->n_fields);
583262306a36Sopenharmony_ci
583362306a36Sopenharmony_ci	seq_puts(m, "\n  val fields:\n\n");
583462306a36Sopenharmony_ci
583562306a36Sopenharmony_ci	seq_puts(m, "    hist_data->fields[0]:\n");
583662306a36Sopenharmony_ci	ret = hist_field_debug_show(m, hist_data->fields[0],
583762306a36Sopenharmony_ci				    HIST_FIELD_FL_HITCOUNT);
583862306a36Sopenharmony_ci	if (ret)
583962306a36Sopenharmony_ci		return;
584062306a36Sopenharmony_ci
584162306a36Sopenharmony_ci	for (i = 1; i < hist_data->n_vals; i++) {
584262306a36Sopenharmony_ci		seq_printf(m, "\n    hist_data->fields[%d]:\n", i);
584362306a36Sopenharmony_ci		ret = hist_field_debug_show(m, hist_data->fields[i], 0);
584462306a36Sopenharmony_ci		if (ret)
584562306a36Sopenharmony_ci			return;
584662306a36Sopenharmony_ci	}
584762306a36Sopenharmony_ci
584862306a36Sopenharmony_ci	seq_puts(m, "\n  key fields:\n");
584962306a36Sopenharmony_ci
585062306a36Sopenharmony_ci	for (i = hist_data->n_vals; i < hist_data->n_fields; i++) {
585162306a36Sopenharmony_ci		seq_printf(m, "\n    hist_data->fields[%d]:\n", i);
585262306a36Sopenharmony_ci		ret = hist_field_debug_show(m, hist_data->fields[i],
585362306a36Sopenharmony_ci					    HIST_FIELD_FL_KEY);
585462306a36Sopenharmony_ci		if (ret)
585562306a36Sopenharmony_ci			return;
585662306a36Sopenharmony_ci	}
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci	if (hist_data->n_var_refs)
585962306a36Sopenharmony_ci		seq_puts(m, "\n  variable reference fields:\n");
586062306a36Sopenharmony_ci
586162306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_var_refs; i++) {
586262306a36Sopenharmony_ci		seq_printf(m, "\n    hist_data->var_refs[%d]:\n", i);
586362306a36Sopenharmony_ci		ret = hist_field_debug_show(m, hist_data->var_refs[i],
586462306a36Sopenharmony_ci					    HIST_FIELD_FL_VAR_REF);
586562306a36Sopenharmony_ci		if (ret)
586662306a36Sopenharmony_ci			return;
586762306a36Sopenharmony_ci	}
586862306a36Sopenharmony_ci
586962306a36Sopenharmony_ci	if (hist_data->n_field_vars)
587062306a36Sopenharmony_ci		seq_puts(m, "\n  field variables:\n");
587162306a36Sopenharmony_ci
587262306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_field_vars; i++) {
587362306a36Sopenharmony_ci		ret = field_var_debug_show(m, hist_data->field_vars[i], i, false);
587462306a36Sopenharmony_ci		if (ret)
587562306a36Sopenharmony_ci			return;
587662306a36Sopenharmony_ci	}
587762306a36Sopenharmony_ci
587862306a36Sopenharmony_ci	ret = hist_actions_debug_show(m, hist_data);
587962306a36Sopenharmony_ci	if (ret)
588062306a36Sopenharmony_ci		return;
588162306a36Sopenharmony_ci}
588262306a36Sopenharmony_ci
588362306a36Sopenharmony_cistatic int hist_debug_show(struct seq_file *m, void *v)
588462306a36Sopenharmony_ci{
588562306a36Sopenharmony_ci	struct event_trigger_data *data;
588662306a36Sopenharmony_ci	struct trace_event_file *event_file;
588762306a36Sopenharmony_ci	int n = 0, ret = 0;
588862306a36Sopenharmony_ci
588962306a36Sopenharmony_ci	mutex_lock(&event_mutex);
589062306a36Sopenharmony_ci
589162306a36Sopenharmony_ci	event_file = event_file_data(m->private);
589262306a36Sopenharmony_ci	if (unlikely(!event_file)) {
589362306a36Sopenharmony_ci		ret = -ENODEV;
589462306a36Sopenharmony_ci		goto out_unlock;
589562306a36Sopenharmony_ci	}
589662306a36Sopenharmony_ci
589762306a36Sopenharmony_ci	list_for_each_entry(data, &event_file->triggers, list) {
589862306a36Sopenharmony_ci		if (data->cmd_ops->trigger_type == ETT_EVENT_HIST)
589962306a36Sopenharmony_ci			hist_trigger_debug_show(m, data, n++);
590062306a36Sopenharmony_ci	}
590162306a36Sopenharmony_ci
590262306a36Sopenharmony_ci out_unlock:
590362306a36Sopenharmony_ci	mutex_unlock(&event_mutex);
590462306a36Sopenharmony_ci
590562306a36Sopenharmony_ci	return ret;
590662306a36Sopenharmony_ci}
590762306a36Sopenharmony_ci
590862306a36Sopenharmony_cistatic int event_hist_debug_open(struct inode *inode, struct file *file)
590962306a36Sopenharmony_ci{
591062306a36Sopenharmony_ci	int ret;
591162306a36Sopenharmony_ci
591262306a36Sopenharmony_ci	ret = tracing_open_file_tr(inode, file);
591362306a36Sopenharmony_ci	if (ret)
591462306a36Sopenharmony_ci		return ret;
591562306a36Sopenharmony_ci
591662306a36Sopenharmony_ci	/* Clear private_data to avoid warning in single_open() */
591762306a36Sopenharmony_ci	file->private_data = NULL;
591862306a36Sopenharmony_ci	return single_open(file, hist_debug_show, file);
591962306a36Sopenharmony_ci}
592062306a36Sopenharmony_ci
592162306a36Sopenharmony_ciconst struct file_operations event_hist_debug_fops = {
592262306a36Sopenharmony_ci	.open = event_hist_debug_open,
592362306a36Sopenharmony_ci	.read = seq_read,
592462306a36Sopenharmony_ci	.llseek = seq_lseek,
592562306a36Sopenharmony_ci	.release = tracing_single_release_file_tr,
592662306a36Sopenharmony_ci};
592762306a36Sopenharmony_ci#endif
592862306a36Sopenharmony_ci
592962306a36Sopenharmony_cistatic void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
593062306a36Sopenharmony_ci{
593162306a36Sopenharmony_ci	const char *field_name = hist_field_name(hist_field, 0);
593262306a36Sopenharmony_ci
593362306a36Sopenharmony_ci	if (hist_field->var.name)
593462306a36Sopenharmony_ci		seq_printf(m, "%s=", hist_field->var.name);
593562306a36Sopenharmony_ci
593662306a36Sopenharmony_ci	if (hist_field->flags & HIST_FIELD_FL_CPU)
593762306a36Sopenharmony_ci		seq_puts(m, "common_cpu");
593862306a36Sopenharmony_ci	else if (hist_field->flags & HIST_FIELD_FL_CONST)
593962306a36Sopenharmony_ci		seq_printf(m, "%llu", hist_field->constant);
594062306a36Sopenharmony_ci	else if (field_name) {
594162306a36Sopenharmony_ci		if (hist_field->flags & HIST_FIELD_FL_VAR_REF ||
594262306a36Sopenharmony_ci		    hist_field->flags & HIST_FIELD_FL_ALIAS)
594362306a36Sopenharmony_ci			seq_putc(m, '$');
594462306a36Sopenharmony_ci		seq_printf(m, "%s", field_name);
594562306a36Sopenharmony_ci	} else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP)
594662306a36Sopenharmony_ci		seq_puts(m, "common_timestamp");
594762306a36Sopenharmony_ci
594862306a36Sopenharmony_ci	if (hist_field->flags) {
594962306a36Sopenharmony_ci		if (!(hist_field->flags & HIST_FIELD_FL_VAR_REF) &&
595062306a36Sopenharmony_ci		    !(hist_field->flags & HIST_FIELD_FL_EXPR) &&
595162306a36Sopenharmony_ci		    !(hist_field->flags & HIST_FIELD_FL_STACKTRACE)) {
595262306a36Sopenharmony_ci			const char *flags = get_hist_field_flags(hist_field);
595362306a36Sopenharmony_ci
595462306a36Sopenharmony_ci			if (flags)
595562306a36Sopenharmony_ci				seq_printf(m, ".%s", flags);
595662306a36Sopenharmony_ci		}
595762306a36Sopenharmony_ci	}
595862306a36Sopenharmony_ci	if (hist_field->buckets)
595962306a36Sopenharmony_ci		seq_printf(m, "=%ld", hist_field->buckets);
596062306a36Sopenharmony_ci}
596162306a36Sopenharmony_ci
596262306a36Sopenharmony_cistatic int event_hist_trigger_print(struct seq_file *m,
596362306a36Sopenharmony_ci				    struct event_trigger_data *data)
596462306a36Sopenharmony_ci{
596562306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
596662306a36Sopenharmony_ci	struct hist_field *field;
596762306a36Sopenharmony_ci	bool have_var = false;
596862306a36Sopenharmony_ci	bool show_val = false;
596962306a36Sopenharmony_ci	unsigned int i;
597062306a36Sopenharmony_ci
597162306a36Sopenharmony_ci	seq_puts(m, HIST_PREFIX);
597262306a36Sopenharmony_ci
597362306a36Sopenharmony_ci	if (data->name)
597462306a36Sopenharmony_ci		seq_printf(m, "%s:", data->name);
597562306a36Sopenharmony_ci
597662306a36Sopenharmony_ci	seq_puts(m, "keys=");
597762306a36Sopenharmony_ci
597862306a36Sopenharmony_ci	for_each_hist_key_field(i, hist_data) {
597962306a36Sopenharmony_ci		field = hist_data->fields[i];
598062306a36Sopenharmony_ci
598162306a36Sopenharmony_ci		if (i > hist_data->n_vals)
598262306a36Sopenharmony_ci			seq_puts(m, ",");
598362306a36Sopenharmony_ci
598462306a36Sopenharmony_ci		if (field->flags & HIST_FIELD_FL_STACKTRACE) {
598562306a36Sopenharmony_ci			if (field->field)
598662306a36Sopenharmony_ci				seq_printf(m, "%s.stacktrace", field->field->name);
598762306a36Sopenharmony_ci			else
598862306a36Sopenharmony_ci				seq_puts(m, "common_stacktrace");
598962306a36Sopenharmony_ci		} else
599062306a36Sopenharmony_ci			hist_field_print(m, field);
599162306a36Sopenharmony_ci	}
599262306a36Sopenharmony_ci
599362306a36Sopenharmony_ci	seq_puts(m, ":vals=");
599462306a36Sopenharmony_ci
599562306a36Sopenharmony_ci	for_each_hist_val_field(i, hist_data) {
599662306a36Sopenharmony_ci		field = hist_data->fields[i];
599762306a36Sopenharmony_ci		if (field->flags & HIST_FIELD_FL_VAR) {
599862306a36Sopenharmony_ci			have_var = true;
599962306a36Sopenharmony_ci			continue;
600062306a36Sopenharmony_ci		}
600162306a36Sopenharmony_ci
600262306a36Sopenharmony_ci		if (i == HITCOUNT_IDX) {
600362306a36Sopenharmony_ci			if (hist_data->attrs->no_hitcount)
600462306a36Sopenharmony_ci				continue;
600562306a36Sopenharmony_ci			seq_puts(m, "hitcount");
600662306a36Sopenharmony_ci		} else {
600762306a36Sopenharmony_ci			if (show_val)
600862306a36Sopenharmony_ci				seq_puts(m, ",");
600962306a36Sopenharmony_ci			hist_field_print(m, field);
601062306a36Sopenharmony_ci		}
601162306a36Sopenharmony_ci		show_val = true;
601262306a36Sopenharmony_ci	}
601362306a36Sopenharmony_ci
601462306a36Sopenharmony_ci	if (have_var) {
601562306a36Sopenharmony_ci		unsigned int n = 0;
601662306a36Sopenharmony_ci
601762306a36Sopenharmony_ci		seq_puts(m, ":");
601862306a36Sopenharmony_ci
601962306a36Sopenharmony_ci		for_each_hist_val_field(i, hist_data) {
602062306a36Sopenharmony_ci			field = hist_data->fields[i];
602162306a36Sopenharmony_ci
602262306a36Sopenharmony_ci			if (field->flags & HIST_FIELD_FL_VAR) {
602362306a36Sopenharmony_ci				if (n++)
602462306a36Sopenharmony_ci					seq_puts(m, ",");
602562306a36Sopenharmony_ci				hist_field_print(m, field);
602662306a36Sopenharmony_ci			}
602762306a36Sopenharmony_ci		}
602862306a36Sopenharmony_ci	}
602962306a36Sopenharmony_ci
603062306a36Sopenharmony_ci	seq_puts(m, ":sort=");
603162306a36Sopenharmony_ci
603262306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_sort_keys; i++) {
603362306a36Sopenharmony_ci		struct tracing_map_sort_key *sort_key;
603462306a36Sopenharmony_ci		unsigned int idx, first_key_idx;
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_ci		/* skip VAR vals */
603762306a36Sopenharmony_ci		first_key_idx = hist_data->n_vals - hist_data->n_vars;
603862306a36Sopenharmony_ci
603962306a36Sopenharmony_ci		sort_key = &hist_data->sort_keys[i];
604062306a36Sopenharmony_ci		idx = sort_key->field_idx;
604162306a36Sopenharmony_ci
604262306a36Sopenharmony_ci		if (WARN_ON(idx >= HIST_FIELDS_MAX))
604362306a36Sopenharmony_ci			return -EINVAL;
604462306a36Sopenharmony_ci
604562306a36Sopenharmony_ci		if (i > 0)
604662306a36Sopenharmony_ci			seq_puts(m, ",");
604762306a36Sopenharmony_ci
604862306a36Sopenharmony_ci		if (idx == HITCOUNT_IDX)
604962306a36Sopenharmony_ci			seq_puts(m, "hitcount");
605062306a36Sopenharmony_ci		else {
605162306a36Sopenharmony_ci			if (idx >= first_key_idx)
605262306a36Sopenharmony_ci				idx += hist_data->n_vars;
605362306a36Sopenharmony_ci			hist_field_print(m, hist_data->fields[idx]);
605462306a36Sopenharmony_ci		}
605562306a36Sopenharmony_ci
605662306a36Sopenharmony_ci		if (sort_key->descending)
605762306a36Sopenharmony_ci			seq_puts(m, ".descending");
605862306a36Sopenharmony_ci	}
605962306a36Sopenharmony_ci	seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
606062306a36Sopenharmony_ci	if (hist_data->enable_timestamps)
606162306a36Sopenharmony_ci		seq_printf(m, ":clock=%s", hist_data->attrs->clock);
606262306a36Sopenharmony_ci	if (hist_data->attrs->no_hitcount)
606362306a36Sopenharmony_ci		seq_puts(m, ":nohitcount");
606462306a36Sopenharmony_ci
606562306a36Sopenharmony_ci	print_actions_spec(m, hist_data);
606662306a36Sopenharmony_ci
606762306a36Sopenharmony_ci	if (data->filter_str)
606862306a36Sopenharmony_ci		seq_printf(m, " if %s", data->filter_str);
606962306a36Sopenharmony_ci
607062306a36Sopenharmony_ci	if (data->paused)
607162306a36Sopenharmony_ci		seq_puts(m, " [paused]");
607262306a36Sopenharmony_ci	else
607362306a36Sopenharmony_ci		seq_puts(m, " [active]");
607462306a36Sopenharmony_ci
607562306a36Sopenharmony_ci	seq_putc(m, '\n');
607662306a36Sopenharmony_ci
607762306a36Sopenharmony_ci	return 0;
607862306a36Sopenharmony_ci}
607962306a36Sopenharmony_ci
608062306a36Sopenharmony_cistatic int event_hist_trigger_init(struct event_trigger_data *data)
608162306a36Sopenharmony_ci{
608262306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
608362306a36Sopenharmony_ci
608462306a36Sopenharmony_ci	if (!data->ref && hist_data->attrs->name)
608562306a36Sopenharmony_ci		save_named_trigger(hist_data->attrs->name, data);
608662306a36Sopenharmony_ci
608762306a36Sopenharmony_ci	data->ref++;
608862306a36Sopenharmony_ci
608962306a36Sopenharmony_ci	return 0;
609062306a36Sopenharmony_ci}
609162306a36Sopenharmony_ci
609262306a36Sopenharmony_cistatic void unregister_field_var_hists(struct hist_trigger_data *hist_data)
609362306a36Sopenharmony_ci{
609462306a36Sopenharmony_ci	struct trace_event_file *file;
609562306a36Sopenharmony_ci	unsigned int i;
609662306a36Sopenharmony_ci	char *cmd;
609762306a36Sopenharmony_ci	int ret;
609862306a36Sopenharmony_ci
609962306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_field_var_hists; i++) {
610062306a36Sopenharmony_ci		file = hist_data->field_var_hists[i]->hist_data->event_file;
610162306a36Sopenharmony_ci		cmd = hist_data->field_var_hists[i]->cmd;
610262306a36Sopenharmony_ci		ret = event_hist_trigger_parse(&trigger_hist_cmd, file,
610362306a36Sopenharmony_ci					       "!hist", "hist", cmd);
610462306a36Sopenharmony_ci		WARN_ON_ONCE(ret < 0);
610562306a36Sopenharmony_ci	}
610662306a36Sopenharmony_ci}
610762306a36Sopenharmony_ci
610862306a36Sopenharmony_cistatic void event_hist_trigger_free(struct event_trigger_data *data)
610962306a36Sopenharmony_ci{
611062306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
611162306a36Sopenharmony_ci
611262306a36Sopenharmony_ci	if (WARN_ON_ONCE(data->ref <= 0))
611362306a36Sopenharmony_ci		return;
611462306a36Sopenharmony_ci
611562306a36Sopenharmony_ci	data->ref--;
611662306a36Sopenharmony_ci	if (!data->ref) {
611762306a36Sopenharmony_ci		if (data->name)
611862306a36Sopenharmony_ci			del_named_trigger(data);
611962306a36Sopenharmony_ci
612062306a36Sopenharmony_ci		trigger_data_free(data);
612162306a36Sopenharmony_ci
612262306a36Sopenharmony_ci		remove_hist_vars(hist_data);
612362306a36Sopenharmony_ci
612462306a36Sopenharmony_ci		unregister_field_var_hists(hist_data);
612562306a36Sopenharmony_ci
612662306a36Sopenharmony_ci		destroy_hist_data(hist_data);
612762306a36Sopenharmony_ci	}
612862306a36Sopenharmony_ci}
612962306a36Sopenharmony_ci
613062306a36Sopenharmony_cistatic struct event_trigger_ops event_hist_trigger_ops = {
613162306a36Sopenharmony_ci	.trigger		= event_hist_trigger,
613262306a36Sopenharmony_ci	.print			= event_hist_trigger_print,
613362306a36Sopenharmony_ci	.init			= event_hist_trigger_init,
613462306a36Sopenharmony_ci	.free			= event_hist_trigger_free,
613562306a36Sopenharmony_ci};
613662306a36Sopenharmony_ci
613762306a36Sopenharmony_cistatic int event_hist_trigger_named_init(struct event_trigger_data *data)
613862306a36Sopenharmony_ci{
613962306a36Sopenharmony_ci	data->ref++;
614062306a36Sopenharmony_ci
614162306a36Sopenharmony_ci	save_named_trigger(data->named_data->name, data);
614262306a36Sopenharmony_ci
614362306a36Sopenharmony_ci	event_hist_trigger_init(data->named_data);
614462306a36Sopenharmony_ci
614562306a36Sopenharmony_ci	return 0;
614662306a36Sopenharmony_ci}
614762306a36Sopenharmony_ci
614862306a36Sopenharmony_cistatic void event_hist_trigger_named_free(struct event_trigger_data *data)
614962306a36Sopenharmony_ci{
615062306a36Sopenharmony_ci	if (WARN_ON_ONCE(data->ref <= 0))
615162306a36Sopenharmony_ci		return;
615262306a36Sopenharmony_ci
615362306a36Sopenharmony_ci	event_hist_trigger_free(data->named_data);
615462306a36Sopenharmony_ci
615562306a36Sopenharmony_ci	data->ref--;
615662306a36Sopenharmony_ci	if (!data->ref) {
615762306a36Sopenharmony_ci		del_named_trigger(data);
615862306a36Sopenharmony_ci		trigger_data_free(data);
615962306a36Sopenharmony_ci	}
616062306a36Sopenharmony_ci}
616162306a36Sopenharmony_ci
616262306a36Sopenharmony_cistatic struct event_trigger_ops event_hist_trigger_named_ops = {
616362306a36Sopenharmony_ci	.trigger		= event_hist_trigger,
616462306a36Sopenharmony_ci	.print			= event_hist_trigger_print,
616562306a36Sopenharmony_ci	.init			= event_hist_trigger_named_init,
616662306a36Sopenharmony_ci	.free			= event_hist_trigger_named_free,
616762306a36Sopenharmony_ci};
616862306a36Sopenharmony_ci
616962306a36Sopenharmony_cistatic struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
617062306a36Sopenharmony_ci							    char *param)
617162306a36Sopenharmony_ci{
617262306a36Sopenharmony_ci	return &event_hist_trigger_ops;
617362306a36Sopenharmony_ci}
617462306a36Sopenharmony_ci
617562306a36Sopenharmony_cistatic void hist_clear(struct event_trigger_data *data)
617662306a36Sopenharmony_ci{
617762306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
617862306a36Sopenharmony_ci
617962306a36Sopenharmony_ci	if (data->name)
618062306a36Sopenharmony_ci		pause_named_trigger(data);
618162306a36Sopenharmony_ci
618262306a36Sopenharmony_ci	tracepoint_synchronize_unregister();
618362306a36Sopenharmony_ci
618462306a36Sopenharmony_ci	tracing_map_clear(hist_data->map);
618562306a36Sopenharmony_ci
618662306a36Sopenharmony_ci	if (data->name)
618762306a36Sopenharmony_ci		unpause_named_trigger(data);
618862306a36Sopenharmony_ci}
618962306a36Sopenharmony_ci
619062306a36Sopenharmony_cistatic bool compatible_field(struct ftrace_event_field *field,
619162306a36Sopenharmony_ci			     struct ftrace_event_field *test_field)
619262306a36Sopenharmony_ci{
619362306a36Sopenharmony_ci	if (field == test_field)
619462306a36Sopenharmony_ci		return true;
619562306a36Sopenharmony_ci	if (field == NULL || test_field == NULL)
619662306a36Sopenharmony_ci		return false;
619762306a36Sopenharmony_ci	if (strcmp(field->name, test_field->name) != 0)
619862306a36Sopenharmony_ci		return false;
619962306a36Sopenharmony_ci	if (strcmp(field->type, test_field->type) != 0)
620062306a36Sopenharmony_ci		return false;
620162306a36Sopenharmony_ci	if (field->size != test_field->size)
620262306a36Sopenharmony_ci		return false;
620362306a36Sopenharmony_ci	if (field->is_signed != test_field->is_signed)
620462306a36Sopenharmony_ci		return false;
620562306a36Sopenharmony_ci
620662306a36Sopenharmony_ci	return true;
620762306a36Sopenharmony_ci}
620862306a36Sopenharmony_ci
620962306a36Sopenharmony_cistatic bool hist_trigger_match(struct event_trigger_data *data,
621062306a36Sopenharmony_ci			       struct event_trigger_data *data_test,
621162306a36Sopenharmony_ci			       struct event_trigger_data *named_data,
621262306a36Sopenharmony_ci			       bool ignore_filter)
621362306a36Sopenharmony_ci{
621462306a36Sopenharmony_ci	struct tracing_map_sort_key *sort_key, *sort_key_test;
621562306a36Sopenharmony_ci	struct hist_trigger_data *hist_data, *hist_data_test;
621662306a36Sopenharmony_ci	struct hist_field *key_field, *key_field_test;
621762306a36Sopenharmony_ci	unsigned int i;
621862306a36Sopenharmony_ci
621962306a36Sopenharmony_ci	if (named_data && (named_data != data_test) &&
622062306a36Sopenharmony_ci	    (named_data != data_test->named_data))
622162306a36Sopenharmony_ci		return false;
622262306a36Sopenharmony_ci
622362306a36Sopenharmony_ci	if (!named_data && is_named_trigger(data_test))
622462306a36Sopenharmony_ci		return false;
622562306a36Sopenharmony_ci
622662306a36Sopenharmony_ci	hist_data = data->private_data;
622762306a36Sopenharmony_ci	hist_data_test = data_test->private_data;
622862306a36Sopenharmony_ci
622962306a36Sopenharmony_ci	if (hist_data->n_vals != hist_data_test->n_vals ||
623062306a36Sopenharmony_ci	    hist_data->n_fields != hist_data_test->n_fields ||
623162306a36Sopenharmony_ci	    hist_data->n_sort_keys != hist_data_test->n_sort_keys)
623262306a36Sopenharmony_ci		return false;
623362306a36Sopenharmony_ci
623462306a36Sopenharmony_ci	if (!ignore_filter) {
623562306a36Sopenharmony_ci		if ((data->filter_str && !data_test->filter_str) ||
623662306a36Sopenharmony_ci		   (!data->filter_str && data_test->filter_str))
623762306a36Sopenharmony_ci			return false;
623862306a36Sopenharmony_ci	}
623962306a36Sopenharmony_ci
624062306a36Sopenharmony_ci	for_each_hist_field(i, hist_data) {
624162306a36Sopenharmony_ci		key_field = hist_data->fields[i];
624262306a36Sopenharmony_ci		key_field_test = hist_data_test->fields[i];
624362306a36Sopenharmony_ci
624462306a36Sopenharmony_ci		if (key_field->flags != key_field_test->flags)
624562306a36Sopenharmony_ci			return false;
624662306a36Sopenharmony_ci		if (!compatible_field(key_field->field, key_field_test->field))
624762306a36Sopenharmony_ci			return false;
624862306a36Sopenharmony_ci		if (key_field->offset != key_field_test->offset)
624962306a36Sopenharmony_ci			return false;
625062306a36Sopenharmony_ci		if (key_field->size != key_field_test->size)
625162306a36Sopenharmony_ci			return false;
625262306a36Sopenharmony_ci		if (key_field->is_signed != key_field_test->is_signed)
625362306a36Sopenharmony_ci			return false;
625462306a36Sopenharmony_ci		if (!!key_field->var.name != !!key_field_test->var.name)
625562306a36Sopenharmony_ci			return false;
625662306a36Sopenharmony_ci		if (key_field->var.name &&
625762306a36Sopenharmony_ci		    strcmp(key_field->var.name, key_field_test->var.name) != 0)
625862306a36Sopenharmony_ci			return false;
625962306a36Sopenharmony_ci	}
626062306a36Sopenharmony_ci
626162306a36Sopenharmony_ci	for (i = 0; i < hist_data->n_sort_keys; i++) {
626262306a36Sopenharmony_ci		sort_key = &hist_data->sort_keys[i];
626362306a36Sopenharmony_ci		sort_key_test = &hist_data_test->sort_keys[i];
626462306a36Sopenharmony_ci
626562306a36Sopenharmony_ci		if (sort_key->field_idx != sort_key_test->field_idx ||
626662306a36Sopenharmony_ci		    sort_key->descending != sort_key_test->descending)
626762306a36Sopenharmony_ci			return false;
626862306a36Sopenharmony_ci	}
626962306a36Sopenharmony_ci
627062306a36Sopenharmony_ci	if (!ignore_filter && data->filter_str &&
627162306a36Sopenharmony_ci	    (strcmp(data->filter_str, data_test->filter_str) != 0))
627262306a36Sopenharmony_ci		return false;
627362306a36Sopenharmony_ci
627462306a36Sopenharmony_ci	if (!actions_match(hist_data, hist_data_test))
627562306a36Sopenharmony_ci		return false;
627662306a36Sopenharmony_ci
627762306a36Sopenharmony_ci	return true;
627862306a36Sopenharmony_ci}
627962306a36Sopenharmony_ci
628062306a36Sopenharmony_cistatic bool existing_hist_update_only(char *glob,
628162306a36Sopenharmony_ci				      struct event_trigger_data *data,
628262306a36Sopenharmony_ci				      struct trace_event_file *file)
628362306a36Sopenharmony_ci{
628462306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
628562306a36Sopenharmony_ci	struct event_trigger_data *test, *named_data = NULL;
628662306a36Sopenharmony_ci	bool updated = false;
628762306a36Sopenharmony_ci
628862306a36Sopenharmony_ci	if (!hist_data->attrs->pause && !hist_data->attrs->cont &&
628962306a36Sopenharmony_ci	    !hist_data->attrs->clear)
629062306a36Sopenharmony_ci		goto out;
629162306a36Sopenharmony_ci
629262306a36Sopenharmony_ci	if (hist_data->attrs->name) {
629362306a36Sopenharmony_ci		named_data = find_named_trigger(hist_data->attrs->name);
629462306a36Sopenharmony_ci		if (named_data) {
629562306a36Sopenharmony_ci			if (!hist_trigger_match(data, named_data, named_data,
629662306a36Sopenharmony_ci						true))
629762306a36Sopenharmony_ci				goto out;
629862306a36Sopenharmony_ci		}
629962306a36Sopenharmony_ci	}
630062306a36Sopenharmony_ci
630162306a36Sopenharmony_ci	if (hist_data->attrs->name && !named_data)
630262306a36Sopenharmony_ci		goto out;
630362306a36Sopenharmony_ci
630462306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
630562306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
630662306a36Sopenharmony_ci			if (!hist_trigger_match(data, test, named_data, false))
630762306a36Sopenharmony_ci				continue;
630862306a36Sopenharmony_ci			if (hist_data->attrs->pause)
630962306a36Sopenharmony_ci				test->paused = true;
631062306a36Sopenharmony_ci			else if (hist_data->attrs->cont)
631162306a36Sopenharmony_ci				test->paused = false;
631262306a36Sopenharmony_ci			else if (hist_data->attrs->clear)
631362306a36Sopenharmony_ci				hist_clear(test);
631462306a36Sopenharmony_ci			updated = true;
631562306a36Sopenharmony_ci			goto out;
631662306a36Sopenharmony_ci		}
631762306a36Sopenharmony_ci	}
631862306a36Sopenharmony_ci out:
631962306a36Sopenharmony_ci	return updated;
632062306a36Sopenharmony_ci}
632162306a36Sopenharmony_ci
632262306a36Sopenharmony_cistatic int hist_register_trigger(char *glob,
632362306a36Sopenharmony_ci				 struct event_trigger_data *data,
632462306a36Sopenharmony_ci				 struct trace_event_file *file)
632562306a36Sopenharmony_ci{
632662306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
632762306a36Sopenharmony_ci	struct event_trigger_data *test, *named_data = NULL;
632862306a36Sopenharmony_ci	struct trace_array *tr = file->tr;
632962306a36Sopenharmony_ci	int ret = 0;
633062306a36Sopenharmony_ci
633162306a36Sopenharmony_ci	if (hist_data->attrs->name) {
633262306a36Sopenharmony_ci		named_data = find_named_trigger(hist_data->attrs->name);
633362306a36Sopenharmony_ci		if (named_data) {
633462306a36Sopenharmony_ci			if (!hist_trigger_match(data, named_data, named_data,
633562306a36Sopenharmony_ci						true)) {
633662306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_NAMED_MISMATCH, errpos(hist_data->attrs->name));
633762306a36Sopenharmony_ci				ret = -EINVAL;
633862306a36Sopenharmony_ci				goto out;
633962306a36Sopenharmony_ci			}
634062306a36Sopenharmony_ci		}
634162306a36Sopenharmony_ci	}
634262306a36Sopenharmony_ci
634362306a36Sopenharmony_ci	if (hist_data->attrs->name && !named_data)
634462306a36Sopenharmony_ci		goto new;
634562306a36Sopenharmony_ci
634662306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
634762306a36Sopenharmony_ci
634862306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
634962306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
635062306a36Sopenharmony_ci			if (hist_trigger_match(data, test, named_data, false)) {
635162306a36Sopenharmony_ci				hist_err(tr, HIST_ERR_TRIGGER_EEXIST, 0);
635262306a36Sopenharmony_ci				ret = -EEXIST;
635362306a36Sopenharmony_ci				goto out;
635462306a36Sopenharmony_ci			}
635562306a36Sopenharmony_ci		}
635662306a36Sopenharmony_ci	}
635762306a36Sopenharmony_ci new:
635862306a36Sopenharmony_ci	if (hist_data->attrs->cont || hist_data->attrs->clear) {
635962306a36Sopenharmony_ci		hist_err(tr, HIST_ERR_TRIGGER_ENOENT_CLEAR, 0);
636062306a36Sopenharmony_ci		ret = -ENOENT;
636162306a36Sopenharmony_ci		goto out;
636262306a36Sopenharmony_ci	}
636362306a36Sopenharmony_ci
636462306a36Sopenharmony_ci	if (hist_data->attrs->pause)
636562306a36Sopenharmony_ci		data->paused = true;
636662306a36Sopenharmony_ci
636762306a36Sopenharmony_ci	if (named_data) {
636862306a36Sopenharmony_ci		data->private_data = named_data->private_data;
636962306a36Sopenharmony_ci		set_named_trigger_data(data, named_data);
637062306a36Sopenharmony_ci		data->ops = &event_hist_trigger_named_ops;
637162306a36Sopenharmony_ci	}
637262306a36Sopenharmony_ci
637362306a36Sopenharmony_ci	if (data->ops->init) {
637462306a36Sopenharmony_ci		ret = data->ops->init(data);
637562306a36Sopenharmony_ci		if (ret < 0)
637662306a36Sopenharmony_ci			goto out;
637762306a36Sopenharmony_ci	}
637862306a36Sopenharmony_ci
637962306a36Sopenharmony_ci	if (hist_data->enable_timestamps) {
638062306a36Sopenharmony_ci		char *clock = hist_data->attrs->clock;
638162306a36Sopenharmony_ci
638262306a36Sopenharmony_ci		ret = tracing_set_clock(file->tr, hist_data->attrs->clock);
638362306a36Sopenharmony_ci		if (ret) {
638462306a36Sopenharmony_ci			hist_err(tr, HIST_ERR_SET_CLOCK_FAIL, errpos(clock));
638562306a36Sopenharmony_ci			goto out;
638662306a36Sopenharmony_ci		}
638762306a36Sopenharmony_ci
638862306a36Sopenharmony_ci		tracing_set_filter_buffering(file->tr, true);
638962306a36Sopenharmony_ci	}
639062306a36Sopenharmony_ci
639162306a36Sopenharmony_ci	if (named_data)
639262306a36Sopenharmony_ci		destroy_hist_data(hist_data);
639362306a36Sopenharmony_ci out:
639462306a36Sopenharmony_ci	return ret;
639562306a36Sopenharmony_ci}
639662306a36Sopenharmony_ci
639762306a36Sopenharmony_cistatic int hist_trigger_enable(struct event_trigger_data *data,
639862306a36Sopenharmony_ci			       struct trace_event_file *file)
639962306a36Sopenharmony_ci{
640062306a36Sopenharmony_ci	int ret = 0;
640162306a36Sopenharmony_ci
640262306a36Sopenharmony_ci	list_add_tail_rcu(&data->list, &file->triggers);
640362306a36Sopenharmony_ci
640462306a36Sopenharmony_ci	update_cond_flag(file);
640562306a36Sopenharmony_ci
640662306a36Sopenharmony_ci	if (trace_event_trigger_enable_disable(file, 1) < 0) {
640762306a36Sopenharmony_ci		list_del_rcu(&data->list);
640862306a36Sopenharmony_ci		update_cond_flag(file);
640962306a36Sopenharmony_ci		ret--;
641062306a36Sopenharmony_ci	}
641162306a36Sopenharmony_ci
641262306a36Sopenharmony_ci	return ret;
641362306a36Sopenharmony_ci}
641462306a36Sopenharmony_ci
641562306a36Sopenharmony_cistatic bool have_hist_trigger_match(struct event_trigger_data *data,
641662306a36Sopenharmony_ci				    struct trace_event_file *file)
641762306a36Sopenharmony_ci{
641862306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
641962306a36Sopenharmony_ci	struct event_trigger_data *test, *named_data = NULL;
642062306a36Sopenharmony_ci	bool match = false;
642162306a36Sopenharmony_ci
642262306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
642362306a36Sopenharmony_ci
642462306a36Sopenharmony_ci	if (hist_data->attrs->name)
642562306a36Sopenharmony_ci		named_data = find_named_trigger(hist_data->attrs->name);
642662306a36Sopenharmony_ci
642762306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
642862306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
642962306a36Sopenharmony_ci			if (hist_trigger_match(data, test, named_data, false)) {
643062306a36Sopenharmony_ci				match = true;
643162306a36Sopenharmony_ci				break;
643262306a36Sopenharmony_ci			}
643362306a36Sopenharmony_ci		}
643462306a36Sopenharmony_ci	}
643562306a36Sopenharmony_ci
643662306a36Sopenharmony_ci	return match;
643762306a36Sopenharmony_ci}
643862306a36Sopenharmony_ci
643962306a36Sopenharmony_cistatic bool hist_trigger_check_refs(struct event_trigger_data *data,
644062306a36Sopenharmony_ci				    struct trace_event_file *file)
644162306a36Sopenharmony_ci{
644262306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
644362306a36Sopenharmony_ci	struct event_trigger_data *test, *named_data = NULL;
644462306a36Sopenharmony_ci
644562306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
644662306a36Sopenharmony_ci
644762306a36Sopenharmony_ci	if (hist_data->attrs->name)
644862306a36Sopenharmony_ci		named_data = find_named_trigger(hist_data->attrs->name);
644962306a36Sopenharmony_ci
645062306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
645162306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
645262306a36Sopenharmony_ci			if (!hist_trigger_match(data, test, named_data, false))
645362306a36Sopenharmony_ci				continue;
645462306a36Sopenharmony_ci			hist_data = test->private_data;
645562306a36Sopenharmony_ci			if (check_var_refs(hist_data))
645662306a36Sopenharmony_ci				return true;
645762306a36Sopenharmony_ci			break;
645862306a36Sopenharmony_ci		}
645962306a36Sopenharmony_ci	}
646062306a36Sopenharmony_ci
646162306a36Sopenharmony_ci	return false;
646262306a36Sopenharmony_ci}
646362306a36Sopenharmony_ci
646462306a36Sopenharmony_cistatic void hist_unregister_trigger(char *glob,
646562306a36Sopenharmony_ci				    struct event_trigger_data *data,
646662306a36Sopenharmony_ci				    struct trace_event_file *file)
646762306a36Sopenharmony_ci{
646862306a36Sopenharmony_ci	struct event_trigger_data *test = NULL, *iter, *named_data = NULL;
646962306a36Sopenharmony_ci	struct hist_trigger_data *hist_data = data->private_data;
647062306a36Sopenharmony_ci
647162306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
647262306a36Sopenharmony_ci
647362306a36Sopenharmony_ci	if (hist_data->attrs->name)
647462306a36Sopenharmony_ci		named_data = find_named_trigger(hist_data->attrs->name);
647562306a36Sopenharmony_ci
647662306a36Sopenharmony_ci	list_for_each_entry(iter, &file->triggers, list) {
647762306a36Sopenharmony_ci		if (iter->cmd_ops->trigger_type == ETT_EVENT_HIST) {
647862306a36Sopenharmony_ci			if (!hist_trigger_match(data, iter, named_data, false))
647962306a36Sopenharmony_ci				continue;
648062306a36Sopenharmony_ci			test = iter;
648162306a36Sopenharmony_ci			list_del_rcu(&test->list);
648262306a36Sopenharmony_ci			trace_event_trigger_enable_disable(file, 0);
648362306a36Sopenharmony_ci			update_cond_flag(file);
648462306a36Sopenharmony_ci			break;
648562306a36Sopenharmony_ci		}
648662306a36Sopenharmony_ci	}
648762306a36Sopenharmony_ci
648862306a36Sopenharmony_ci	if (test && test->ops->free)
648962306a36Sopenharmony_ci		test->ops->free(test);
649062306a36Sopenharmony_ci
649162306a36Sopenharmony_ci	if (hist_data->enable_timestamps) {
649262306a36Sopenharmony_ci		if (!hist_data->remove || test)
649362306a36Sopenharmony_ci			tracing_set_filter_buffering(file->tr, false);
649462306a36Sopenharmony_ci	}
649562306a36Sopenharmony_ci}
649662306a36Sopenharmony_ci
649762306a36Sopenharmony_cistatic bool hist_file_check_refs(struct trace_event_file *file)
649862306a36Sopenharmony_ci{
649962306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
650062306a36Sopenharmony_ci	struct event_trigger_data *test;
650162306a36Sopenharmony_ci
650262306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
650362306a36Sopenharmony_ci
650462306a36Sopenharmony_ci	list_for_each_entry(test, &file->triggers, list) {
650562306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
650662306a36Sopenharmony_ci			hist_data = test->private_data;
650762306a36Sopenharmony_ci			if (check_var_refs(hist_data))
650862306a36Sopenharmony_ci				return true;
650962306a36Sopenharmony_ci		}
651062306a36Sopenharmony_ci	}
651162306a36Sopenharmony_ci
651262306a36Sopenharmony_ci	return false;
651362306a36Sopenharmony_ci}
651462306a36Sopenharmony_ci
651562306a36Sopenharmony_cistatic void hist_unreg_all(struct trace_event_file *file)
651662306a36Sopenharmony_ci{
651762306a36Sopenharmony_ci	struct event_trigger_data *test, *n;
651862306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
651962306a36Sopenharmony_ci	struct synth_event *se;
652062306a36Sopenharmony_ci	const char *se_name;
652162306a36Sopenharmony_ci
652262306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
652362306a36Sopenharmony_ci
652462306a36Sopenharmony_ci	if (hist_file_check_refs(file))
652562306a36Sopenharmony_ci		return;
652662306a36Sopenharmony_ci
652762306a36Sopenharmony_ci	list_for_each_entry_safe(test, n, &file->triggers, list) {
652862306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
652962306a36Sopenharmony_ci			hist_data = test->private_data;
653062306a36Sopenharmony_ci			list_del_rcu(&test->list);
653162306a36Sopenharmony_ci			trace_event_trigger_enable_disable(file, 0);
653262306a36Sopenharmony_ci
653362306a36Sopenharmony_ci			se_name = trace_event_name(file->event_call);
653462306a36Sopenharmony_ci			se = find_synth_event(se_name);
653562306a36Sopenharmony_ci			if (se)
653662306a36Sopenharmony_ci				se->ref--;
653762306a36Sopenharmony_ci
653862306a36Sopenharmony_ci			update_cond_flag(file);
653962306a36Sopenharmony_ci			if (hist_data->enable_timestamps)
654062306a36Sopenharmony_ci				tracing_set_filter_buffering(file->tr, false);
654162306a36Sopenharmony_ci			if (test->ops->free)
654262306a36Sopenharmony_ci				test->ops->free(test);
654362306a36Sopenharmony_ci		}
654462306a36Sopenharmony_ci	}
654562306a36Sopenharmony_ci}
654662306a36Sopenharmony_ci
654762306a36Sopenharmony_cistatic int event_hist_trigger_parse(struct event_command *cmd_ops,
654862306a36Sopenharmony_ci				    struct trace_event_file *file,
654962306a36Sopenharmony_ci				    char *glob, char *cmd,
655062306a36Sopenharmony_ci				    char *param_and_filter)
655162306a36Sopenharmony_ci{
655262306a36Sopenharmony_ci	unsigned int hist_trigger_bits = TRACING_MAP_BITS_DEFAULT;
655362306a36Sopenharmony_ci	struct event_trigger_data *trigger_data;
655462306a36Sopenharmony_ci	struct hist_trigger_attrs *attrs;
655562306a36Sopenharmony_ci	struct hist_trigger_data *hist_data;
655662306a36Sopenharmony_ci	char *param, *filter, *p, *start;
655762306a36Sopenharmony_ci	struct synth_event *se;
655862306a36Sopenharmony_ci	const char *se_name;
655962306a36Sopenharmony_ci	bool remove;
656062306a36Sopenharmony_ci	int ret = 0;
656162306a36Sopenharmony_ci
656262306a36Sopenharmony_ci	lockdep_assert_held(&event_mutex);
656362306a36Sopenharmony_ci
656462306a36Sopenharmony_ci	if (WARN_ON(!glob))
656562306a36Sopenharmony_ci		return -EINVAL;
656662306a36Sopenharmony_ci
656762306a36Sopenharmony_ci	if (glob[0]) {
656862306a36Sopenharmony_ci		hist_err_clear();
656962306a36Sopenharmony_ci		last_cmd_set(file, param_and_filter);
657062306a36Sopenharmony_ci	}
657162306a36Sopenharmony_ci
657262306a36Sopenharmony_ci	remove = event_trigger_check_remove(glob);
657362306a36Sopenharmony_ci
657462306a36Sopenharmony_ci	if (event_trigger_empty_param(param_and_filter))
657562306a36Sopenharmony_ci		return -EINVAL;
657662306a36Sopenharmony_ci
657762306a36Sopenharmony_ci	/*
657862306a36Sopenharmony_ci	 * separate the trigger from the filter (k:v [if filter])
657962306a36Sopenharmony_ci	 * allowing for whitespace in the trigger
658062306a36Sopenharmony_ci	 */
658162306a36Sopenharmony_ci	p = param = param_and_filter;
658262306a36Sopenharmony_ci	do {
658362306a36Sopenharmony_ci		p = strstr(p, "if");
658462306a36Sopenharmony_ci		if (!p)
658562306a36Sopenharmony_ci			break;
658662306a36Sopenharmony_ci		if (p == param_and_filter)
658762306a36Sopenharmony_ci			return -EINVAL;
658862306a36Sopenharmony_ci		if (*(p - 1) != ' ' && *(p - 1) != '\t') {
658962306a36Sopenharmony_ci			p++;
659062306a36Sopenharmony_ci			continue;
659162306a36Sopenharmony_ci		}
659262306a36Sopenharmony_ci		if (p >= param_and_filter + strlen(param_and_filter) - (sizeof("if") - 1) - 1)
659362306a36Sopenharmony_ci			return -EINVAL;
659462306a36Sopenharmony_ci		if (*(p + sizeof("if") - 1) != ' ' && *(p + sizeof("if") - 1) != '\t') {
659562306a36Sopenharmony_ci			p++;
659662306a36Sopenharmony_ci			continue;
659762306a36Sopenharmony_ci		}
659862306a36Sopenharmony_ci		break;
659962306a36Sopenharmony_ci	} while (1);
660062306a36Sopenharmony_ci
660162306a36Sopenharmony_ci	if (!p)
660262306a36Sopenharmony_ci		filter = NULL;
660362306a36Sopenharmony_ci	else {
660462306a36Sopenharmony_ci		*(p - 1) = '\0';
660562306a36Sopenharmony_ci		filter = strstrip(p);
660662306a36Sopenharmony_ci		param = strstrip(param);
660762306a36Sopenharmony_ci	}
660862306a36Sopenharmony_ci
660962306a36Sopenharmony_ci	/*
661062306a36Sopenharmony_ci	 * To simplify arithmetic expression parsing, replace occurrences of
661162306a36Sopenharmony_ci	 * '.sym-offset' modifier with '.symXoffset'
661262306a36Sopenharmony_ci	 */
661362306a36Sopenharmony_ci	start = strstr(param, ".sym-offset");
661462306a36Sopenharmony_ci	while (start) {
661562306a36Sopenharmony_ci		*(start + 4) = 'X';
661662306a36Sopenharmony_ci		start = strstr(start + 11, ".sym-offset");
661762306a36Sopenharmony_ci	}
661862306a36Sopenharmony_ci
661962306a36Sopenharmony_ci	attrs = parse_hist_trigger_attrs(file->tr, param);
662062306a36Sopenharmony_ci	if (IS_ERR(attrs))
662162306a36Sopenharmony_ci		return PTR_ERR(attrs);
662262306a36Sopenharmony_ci
662362306a36Sopenharmony_ci	if (attrs->map_bits)
662462306a36Sopenharmony_ci		hist_trigger_bits = attrs->map_bits;
662562306a36Sopenharmony_ci
662662306a36Sopenharmony_ci	hist_data = create_hist_data(hist_trigger_bits, attrs, file, remove);
662762306a36Sopenharmony_ci	if (IS_ERR(hist_data)) {
662862306a36Sopenharmony_ci		destroy_hist_trigger_attrs(attrs);
662962306a36Sopenharmony_ci		return PTR_ERR(hist_data);
663062306a36Sopenharmony_ci	}
663162306a36Sopenharmony_ci
663262306a36Sopenharmony_ci	trigger_data = event_trigger_alloc(cmd_ops, cmd, param, hist_data);
663362306a36Sopenharmony_ci	if (!trigger_data) {
663462306a36Sopenharmony_ci		ret = -ENOMEM;
663562306a36Sopenharmony_ci		goto out_free;
663662306a36Sopenharmony_ci	}
663762306a36Sopenharmony_ci
663862306a36Sopenharmony_ci	ret = event_trigger_set_filter(cmd_ops, file, filter, trigger_data);
663962306a36Sopenharmony_ci	if (ret < 0)
664062306a36Sopenharmony_ci		goto out_free;
664162306a36Sopenharmony_ci
664262306a36Sopenharmony_ci	if (remove) {
664362306a36Sopenharmony_ci		if (!have_hist_trigger_match(trigger_data, file))
664462306a36Sopenharmony_ci			goto out_free;
664562306a36Sopenharmony_ci
664662306a36Sopenharmony_ci		if (hist_trigger_check_refs(trigger_data, file)) {
664762306a36Sopenharmony_ci			ret = -EBUSY;
664862306a36Sopenharmony_ci			goto out_free;
664962306a36Sopenharmony_ci		}
665062306a36Sopenharmony_ci
665162306a36Sopenharmony_ci		event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
665262306a36Sopenharmony_ci		se_name = trace_event_name(file->event_call);
665362306a36Sopenharmony_ci		se = find_synth_event(se_name);
665462306a36Sopenharmony_ci		if (se)
665562306a36Sopenharmony_ci			se->ref--;
665662306a36Sopenharmony_ci		ret = 0;
665762306a36Sopenharmony_ci		goto out_free;
665862306a36Sopenharmony_ci	}
665962306a36Sopenharmony_ci
666062306a36Sopenharmony_ci	if (existing_hist_update_only(glob, trigger_data, file))
666162306a36Sopenharmony_ci		goto out_free;
666262306a36Sopenharmony_ci
666362306a36Sopenharmony_ci	ret = event_trigger_register(cmd_ops, file, glob, trigger_data);
666462306a36Sopenharmony_ci	if (ret < 0)
666562306a36Sopenharmony_ci		goto out_free;
666662306a36Sopenharmony_ci
666762306a36Sopenharmony_ci	if (get_named_trigger_data(trigger_data))
666862306a36Sopenharmony_ci		goto enable;
666962306a36Sopenharmony_ci
667062306a36Sopenharmony_ci	ret = create_actions(hist_data);
667162306a36Sopenharmony_ci	if (ret)
667262306a36Sopenharmony_ci		goto out_unreg;
667362306a36Sopenharmony_ci
667462306a36Sopenharmony_ci	if (has_hist_vars(hist_data) || hist_data->n_var_refs) {
667562306a36Sopenharmony_ci		ret = save_hist_vars(hist_data);
667662306a36Sopenharmony_ci		if (ret)
667762306a36Sopenharmony_ci			goto out_unreg;
667862306a36Sopenharmony_ci	}
667962306a36Sopenharmony_ci
668062306a36Sopenharmony_ci	ret = tracing_map_init(hist_data->map);
668162306a36Sopenharmony_ci	if (ret)
668262306a36Sopenharmony_ci		goto out_unreg;
668362306a36Sopenharmony_cienable:
668462306a36Sopenharmony_ci	ret = hist_trigger_enable(trigger_data, file);
668562306a36Sopenharmony_ci	if (ret)
668662306a36Sopenharmony_ci		goto out_unreg;
668762306a36Sopenharmony_ci
668862306a36Sopenharmony_ci	se_name = trace_event_name(file->event_call);
668962306a36Sopenharmony_ci	se = find_synth_event(se_name);
669062306a36Sopenharmony_ci	if (se)
669162306a36Sopenharmony_ci		se->ref++;
669262306a36Sopenharmony_ci out:
669362306a36Sopenharmony_ci	if (ret == 0 && glob[0])
669462306a36Sopenharmony_ci		hist_err_clear();
669562306a36Sopenharmony_ci
669662306a36Sopenharmony_ci	return ret;
669762306a36Sopenharmony_ci out_unreg:
669862306a36Sopenharmony_ci	event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
669962306a36Sopenharmony_ci out_free:
670062306a36Sopenharmony_ci	event_trigger_reset_filter(cmd_ops, trigger_data);
670162306a36Sopenharmony_ci
670262306a36Sopenharmony_ci	remove_hist_vars(hist_data);
670362306a36Sopenharmony_ci
670462306a36Sopenharmony_ci	kfree(trigger_data);
670562306a36Sopenharmony_ci
670662306a36Sopenharmony_ci	destroy_hist_data(hist_data);
670762306a36Sopenharmony_ci	goto out;
670862306a36Sopenharmony_ci}
670962306a36Sopenharmony_ci
671062306a36Sopenharmony_cistatic struct event_command trigger_hist_cmd = {
671162306a36Sopenharmony_ci	.name			= "hist",
671262306a36Sopenharmony_ci	.trigger_type		= ETT_EVENT_HIST,
671362306a36Sopenharmony_ci	.flags			= EVENT_CMD_FL_NEEDS_REC,
671462306a36Sopenharmony_ci	.parse			= event_hist_trigger_parse,
671562306a36Sopenharmony_ci	.reg			= hist_register_trigger,
671662306a36Sopenharmony_ci	.unreg			= hist_unregister_trigger,
671762306a36Sopenharmony_ci	.unreg_all		= hist_unreg_all,
671862306a36Sopenharmony_ci	.get_trigger_ops	= event_hist_get_trigger_ops,
671962306a36Sopenharmony_ci	.set_filter		= set_trigger_filter,
672062306a36Sopenharmony_ci};
672162306a36Sopenharmony_ci
672262306a36Sopenharmony_ci__init int register_trigger_hist_cmd(void)
672362306a36Sopenharmony_ci{
672462306a36Sopenharmony_ci	int ret;
672562306a36Sopenharmony_ci
672662306a36Sopenharmony_ci	ret = register_event_command(&trigger_hist_cmd);
672762306a36Sopenharmony_ci	WARN_ON(ret < 0);
672862306a36Sopenharmony_ci
672962306a36Sopenharmony_ci	return ret;
673062306a36Sopenharmony_ci}
673162306a36Sopenharmony_ci
673262306a36Sopenharmony_cistatic void
673362306a36Sopenharmony_cihist_enable_trigger(struct event_trigger_data *data,
673462306a36Sopenharmony_ci		    struct trace_buffer *buffer,  void *rec,
673562306a36Sopenharmony_ci		    struct ring_buffer_event *event)
673662306a36Sopenharmony_ci{
673762306a36Sopenharmony_ci	struct enable_trigger_data *enable_data = data->private_data;
673862306a36Sopenharmony_ci	struct event_trigger_data *test;
673962306a36Sopenharmony_ci
674062306a36Sopenharmony_ci	list_for_each_entry_rcu(test, &enable_data->file->triggers, list,
674162306a36Sopenharmony_ci				lockdep_is_held(&event_mutex)) {
674262306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
674362306a36Sopenharmony_ci			if (enable_data->enable)
674462306a36Sopenharmony_ci				test->paused = false;
674562306a36Sopenharmony_ci			else
674662306a36Sopenharmony_ci				test->paused = true;
674762306a36Sopenharmony_ci		}
674862306a36Sopenharmony_ci	}
674962306a36Sopenharmony_ci}
675062306a36Sopenharmony_ci
675162306a36Sopenharmony_cistatic void
675262306a36Sopenharmony_cihist_enable_count_trigger(struct event_trigger_data *data,
675362306a36Sopenharmony_ci			  struct trace_buffer *buffer,  void *rec,
675462306a36Sopenharmony_ci			  struct ring_buffer_event *event)
675562306a36Sopenharmony_ci{
675662306a36Sopenharmony_ci	if (!data->count)
675762306a36Sopenharmony_ci		return;
675862306a36Sopenharmony_ci
675962306a36Sopenharmony_ci	if (data->count != -1)
676062306a36Sopenharmony_ci		(data->count)--;
676162306a36Sopenharmony_ci
676262306a36Sopenharmony_ci	hist_enable_trigger(data, buffer, rec, event);
676362306a36Sopenharmony_ci}
676462306a36Sopenharmony_ci
676562306a36Sopenharmony_cistatic struct event_trigger_ops hist_enable_trigger_ops = {
676662306a36Sopenharmony_ci	.trigger		= hist_enable_trigger,
676762306a36Sopenharmony_ci	.print			= event_enable_trigger_print,
676862306a36Sopenharmony_ci	.init			= event_trigger_init,
676962306a36Sopenharmony_ci	.free			= event_enable_trigger_free,
677062306a36Sopenharmony_ci};
677162306a36Sopenharmony_ci
677262306a36Sopenharmony_cistatic struct event_trigger_ops hist_enable_count_trigger_ops = {
677362306a36Sopenharmony_ci	.trigger		= hist_enable_count_trigger,
677462306a36Sopenharmony_ci	.print			= event_enable_trigger_print,
677562306a36Sopenharmony_ci	.init			= event_trigger_init,
677662306a36Sopenharmony_ci	.free			= event_enable_trigger_free,
677762306a36Sopenharmony_ci};
677862306a36Sopenharmony_ci
677962306a36Sopenharmony_cistatic struct event_trigger_ops hist_disable_trigger_ops = {
678062306a36Sopenharmony_ci	.trigger		= hist_enable_trigger,
678162306a36Sopenharmony_ci	.print			= event_enable_trigger_print,
678262306a36Sopenharmony_ci	.init			= event_trigger_init,
678362306a36Sopenharmony_ci	.free			= event_enable_trigger_free,
678462306a36Sopenharmony_ci};
678562306a36Sopenharmony_ci
678662306a36Sopenharmony_cistatic struct event_trigger_ops hist_disable_count_trigger_ops = {
678762306a36Sopenharmony_ci	.trigger		= hist_enable_count_trigger,
678862306a36Sopenharmony_ci	.print			= event_enable_trigger_print,
678962306a36Sopenharmony_ci	.init			= event_trigger_init,
679062306a36Sopenharmony_ci	.free			= event_enable_trigger_free,
679162306a36Sopenharmony_ci};
679262306a36Sopenharmony_ci
679362306a36Sopenharmony_cistatic struct event_trigger_ops *
679462306a36Sopenharmony_cihist_enable_get_trigger_ops(char *cmd, char *param)
679562306a36Sopenharmony_ci{
679662306a36Sopenharmony_ci	struct event_trigger_ops *ops;
679762306a36Sopenharmony_ci	bool enable;
679862306a36Sopenharmony_ci
679962306a36Sopenharmony_ci	enable = (strcmp(cmd, ENABLE_HIST_STR) == 0);
680062306a36Sopenharmony_ci
680162306a36Sopenharmony_ci	if (enable)
680262306a36Sopenharmony_ci		ops = param ? &hist_enable_count_trigger_ops :
680362306a36Sopenharmony_ci			&hist_enable_trigger_ops;
680462306a36Sopenharmony_ci	else
680562306a36Sopenharmony_ci		ops = param ? &hist_disable_count_trigger_ops :
680662306a36Sopenharmony_ci			&hist_disable_trigger_ops;
680762306a36Sopenharmony_ci
680862306a36Sopenharmony_ci	return ops;
680962306a36Sopenharmony_ci}
681062306a36Sopenharmony_ci
681162306a36Sopenharmony_cistatic void hist_enable_unreg_all(struct trace_event_file *file)
681262306a36Sopenharmony_ci{
681362306a36Sopenharmony_ci	struct event_trigger_data *test, *n;
681462306a36Sopenharmony_ci
681562306a36Sopenharmony_ci	list_for_each_entry_safe(test, n, &file->triggers, list) {
681662306a36Sopenharmony_ci		if (test->cmd_ops->trigger_type == ETT_HIST_ENABLE) {
681762306a36Sopenharmony_ci			list_del_rcu(&test->list);
681862306a36Sopenharmony_ci			update_cond_flag(file);
681962306a36Sopenharmony_ci			trace_event_trigger_enable_disable(file, 0);
682062306a36Sopenharmony_ci			if (test->ops->free)
682162306a36Sopenharmony_ci				test->ops->free(test);
682262306a36Sopenharmony_ci		}
682362306a36Sopenharmony_ci	}
682462306a36Sopenharmony_ci}
682562306a36Sopenharmony_ci
682662306a36Sopenharmony_cistatic struct event_command trigger_hist_enable_cmd = {
682762306a36Sopenharmony_ci	.name			= ENABLE_HIST_STR,
682862306a36Sopenharmony_ci	.trigger_type		= ETT_HIST_ENABLE,
682962306a36Sopenharmony_ci	.parse			= event_enable_trigger_parse,
683062306a36Sopenharmony_ci	.reg			= event_enable_register_trigger,
683162306a36Sopenharmony_ci	.unreg			= event_enable_unregister_trigger,
683262306a36Sopenharmony_ci	.unreg_all		= hist_enable_unreg_all,
683362306a36Sopenharmony_ci	.get_trigger_ops	= hist_enable_get_trigger_ops,
683462306a36Sopenharmony_ci	.set_filter		= set_trigger_filter,
683562306a36Sopenharmony_ci};
683662306a36Sopenharmony_ci
683762306a36Sopenharmony_cistatic struct event_command trigger_hist_disable_cmd = {
683862306a36Sopenharmony_ci	.name			= DISABLE_HIST_STR,
683962306a36Sopenharmony_ci	.trigger_type		= ETT_HIST_ENABLE,
684062306a36Sopenharmony_ci	.parse			= event_enable_trigger_parse,
684162306a36Sopenharmony_ci	.reg			= event_enable_register_trigger,
684262306a36Sopenharmony_ci	.unreg			= event_enable_unregister_trigger,
684362306a36Sopenharmony_ci	.unreg_all		= hist_enable_unreg_all,
684462306a36Sopenharmony_ci	.get_trigger_ops	= hist_enable_get_trigger_ops,
684562306a36Sopenharmony_ci	.set_filter		= set_trigger_filter,
684662306a36Sopenharmony_ci};
684762306a36Sopenharmony_ci
684862306a36Sopenharmony_cistatic __init void unregister_trigger_hist_enable_disable_cmds(void)
684962306a36Sopenharmony_ci{
685062306a36Sopenharmony_ci	unregister_event_command(&trigger_hist_enable_cmd);
685162306a36Sopenharmony_ci	unregister_event_command(&trigger_hist_disable_cmd);
685262306a36Sopenharmony_ci}
685362306a36Sopenharmony_ci
685462306a36Sopenharmony_ci__init int register_trigger_hist_enable_disable_cmds(void)
685562306a36Sopenharmony_ci{
685662306a36Sopenharmony_ci	int ret;
685762306a36Sopenharmony_ci
685862306a36Sopenharmony_ci	ret = register_event_command(&trigger_hist_enable_cmd);
685962306a36Sopenharmony_ci	if (WARN_ON(ret < 0))
686062306a36Sopenharmony_ci		return ret;
686162306a36Sopenharmony_ci	ret = register_event_command(&trigger_hist_disable_cmd);
686262306a36Sopenharmony_ci	if (WARN_ON(ret < 0))
686362306a36Sopenharmony_ci		unregister_trigger_hist_enable_disable_cmds();
686462306a36Sopenharmony_ci
686562306a36Sopenharmony_ci	return ret;
686662306a36Sopenharmony_ci}
6867