Merge tag 'for-4.21/block-20190102' of git://git.kernel.dk/linux-block
[linux] / kernel / trace / trace_events_hist.c
index eb908ef..449d90c 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "tracing_map.h"
 #include "trace.h"
+#include "trace_dynevent.h"
 
 #define SYNTH_SYSTEM           "synthetic"
 #define SYNTH_FIELDS_MAX       16
@@ -39,6 +40,16 @@ enum field_op_id {
        FIELD_OP_UNARY_MINUS,
 };
 
+/*
+ * A hist_var (histogram variable) contains variable information for
+ * hist_fields having the HIST_FIELD_FL_VAR or HIST_FIELD_FL_VAR_REF
+ * flag set.  A hist_var has a variable name e.g. ts0, and is
+ * associated with a given histogram trigger, as specified by
+ * hist_data.  The hist_var idx is the unique index assigned to the
+ * variable by the hist trigger's tracing_map.  The idx is what is
+ * used to set a variable's value and, by a variable reference, to
+ * retrieve it.
+ */
 struct hist_var {
        char                            *name;
        struct hist_trigger_data        *hist_data;
@@ -55,12 +66,29 @@ struct hist_field {
        const char                      *type;
        struct hist_field               *operands[HIST_FIELD_OPERANDS_MAX];
        struct hist_trigger_data        *hist_data;
+
+       /*
+        * Variable fields contain variable-specific info in var.
+        */
        struct hist_var                 var;
        enum field_op_id                operator;
        char                            *system;
        char                            *event_name;
+
+       /*
+        * The name field is used for EXPR and VAR_REF fields.  VAR
+        * fields contain the variable name in var.name.
+        */
        char                            *name;
-       unsigned int                    var_idx;
+
+       /*
+        * When a histogram trigger is hit, if it has any references
+        * to variables, the values of those variables are collected
+        * into a var_ref_vals array by resolve_var_refs().  The
+        * current value of each variable is read from the tracing_map
+        * using the hist field's hist_var.idx and entered into the
+        * var_ref_idx entry i.e. var_ref_vals[var_ref_idx].
+        */
        unsigned int                    var_ref_idx;
        bool                            read_once;
 };
@@ -279,8 +307,6 @@ struct hist_trigger_data {
        struct action_data              *actions[HIST_ACTIONS_MAX];
        unsigned int                    n_actions;
 
-       struct hist_field               *synth_var_refs[SYNTH_FIELDS_MAX];
-       unsigned int                    n_synth_var_refs;
        struct field_var                *field_vars[SYNTH_FIELDS_MAX];
        unsigned int                    n_field_vars;
        unsigned int                    n_field_var_str;
@@ -292,6 +318,21 @@ struct hist_trigger_data {
        unsigned int                    n_max_var_str;
 };
 
+static int synth_event_create(int argc, const char **argv);
+static int synth_event_show(struct seq_file *m, struct dyn_event *ev);
+static int synth_event_release(struct dyn_event *ev);
+static bool synth_event_is_busy(struct dyn_event *ev);
+static bool synth_event_match(const char *system, const char *event,
+                             struct dyn_event *ev);
+
+static struct dyn_event_operations synth_event_ops = {
+       .create = synth_event_create,
+       .show = synth_event_show,
+       .is_busy = synth_event_is_busy,
+       .free = synth_event_release,
+       .match = synth_event_match,
+};
+
 struct synth_field {
        char *type;
        char *name;
@@ -301,7 +342,7 @@ struct synth_field {
 };
 
 struct synth_event {
-       struct list_head                        list;
+       struct dyn_event                        devent;
        int                                     ref;
        char                                    *name;
        struct synth_field                      **fields;
@@ -312,6 +353,32 @@ struct synth_event {
        struct tracepoint                       *tp;
 };
 
+static bool is_synth_event(struct dyn_event *ev)
+{
+       return ev->ops == &synth_event_ops;
+}
+
+static struct synth_event *to_synth_event(struct dyn_event *ev)
+{
+       return container_of(ev, struct synth_event, devent);
+}
+
+static bool synth_event_is_busy(struct dyn_event *ev)
+{
+       struct synth_event *event = to_synth_event(ev);
+
+       return event->ref != 0;
+}
+
+static bool synth_event_match(const char *system, const char *event,
+                             struct dyn_event *ev)
+{
+       struct synth_event *sev = to_synth_event(ev);
+
+       return strcmp(sev->name, event) == 0 &&
+               (!system || strcmp(system, SYNTH_SYSTEM) == 0);
+}
+
 struct action_data;
 
 typedef void (*action_fn_t) (struct hist_trigger_data *hist_data,
@@ -326,6 +393,14 @@ struct action_data {
 
        union {
                struct {
+                       /*
+                        * When a histogram trigger is hit, the values of any
+                        * references to variables, including variables being passed
+                        * as parameters to synthetic events, are collected into a
+                        * var_ref_vals array.  This var_ref_idx is the index of the
+                        * first param in the array to be passed to the synthetic
+                        * event invocation.
+                        */
                        unsigned int            var_ref_idx;
                        char                    *match_event;
                        char                    *match_event_system;
@@ -402,9 +477,6 @@ static bool have_hist_err(void)
        return false;
 }
 
-static LIST_HEAD(synth_event_list);
-static DEFINE_MUTEX(synth_event_mutex);
-
 struct synth_trace_event {
        struct trace_entry      ent;
        u64                     fields[];
@@ -446,7 +518,7 @@ static int synth_event_define_fields(struct trace_event_call *call)
 
 static bool synth_field_signed(char *type)
 {
-       if (strncmp(type, "u", 1) == 0)
+       if (str_has_prefix(type, "u"))
                return false;
 
        return true;
@@ -469,7 +541,7 @@ static int synth_field_string_size(char *type)
        start = strstr(type, "char[");
        if (start == NULL)
                return -EINVAL;
-       start += strlen("char[");
+       start += sizeof("char[") - 1;
 
        end = strchr(type, ']');
        if (!end || end < start)
@@ -738,14 +810,12 @@ static void free_synth_field(struct synth_field *field)
        kfree(field);
 }
 
-static struct synth_field *parse_synth_field(int argc, char **argv,
+static struct synth_field *parse_synth_field(int argc, const char **argv,
                                             int *consumed)
 {
        struct synth_field *field;
-       const char *prefix = NULL;
-       char *field_type = argv[0], *field_name;
+       const char *prefix = NULL, *field_type = argv[0], *field_name, *array;
        int len, ret = 0;
-       char *array;
 
        if (field_type[0] == ';')
                field_type++;
@@ -762,20 +832,31 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
                *consumed = 2;
        }
 
-       len = strlen(field_name);
-       if (field_name[len - 1] == ';')
-               field_name[len - 1] = '\0';
-
        field = kzalloc(sizeof(*field), GFP_KERNEL);
        if (!field)
                return ERR_PTR(-ENOMEM);
 
-       len = strlen(field_type) + 1;
+       len = strlen(field_name);
        array = strchr(field_name, '[');
+       if (array)
+               len -= strlen(array);
+       else if (field_name[len - 1] == ';')
+               len--;
+
+       field->name = kmemdup_nul(field_name, len, GFP_KERNEL);
+       if (!field->name) {
+               ret = -ENOMEM;
+               goto free;
+       }
+
+       if (field_type[0] == ';')
+               field_type++;
+       len = strlen(field_type) + 1;
        if (array)
                len += strlen(array);
        if (prefix)
                len += strlen(prefix);
+
        field->type = kzalloc(len, GFP_KERNEL);
        if (!field->type) {
                ret = -ENOMEM;
@@ -786,7 +867,8 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
        strcat(field->type, field_type);
        if (array) {
                strcat(field->type, array);
-               *array = '\0';
+               if (field->type[len - 1] == ';')
+                       field->type[len - 1] = '\0';
        }
 
        field->size = synth_field_size(field->type);
@@ -800,11 +882,6 @@ static struct synth_field *parse_synth_field(int argc, char **argv,
 
        field->is_signed = synth_field_signed(field->type);
 
-       field->name = kstrdup(field_name, GFP_KERNEL);
-       if (!field->name) {
-               ret = -ENOMEM;
-               goto free;
-       }
  out:
        return field;
  free:
@@ -868,9 +945,13 @@ static inline void trace_synth(struct synth_event *event, u64 *var_ref_vals,
 
 static struct synth_event *find_synth_event(const char *name)
 {
+       struct dyn_event *pos;
        struct synth_event *event;
 
-       list_for_each_entry(event, &synth_event_list, list) {
+       for_each_dyn_event(pos) {
+               if (!is_synth_event(pos))
+                       continue;
+               event = to_synth_event(pos);
                if (strcmp(event->name, name) == 0)
                        return event;
        }
@@ -959,7 +1040,7 @@ static void free_synth_event(struct synth_event *event)
        kfree(event);
 }
 
-static struct synth_event *alloc_synth_event(char *event_name, int n_fields,
+static struct synth_event *alloc_synth_event(const char *name, int n_fields,
                                             struct synth_field **fields)
 {
        struct synth_event *event;
@@ -971,7 +1052,7 @@ static struct synth_event *alloc_synth_event(char *event_name, int n_fields,
                goto out;
        }
 
-       event->name = kstrdup(event_name, GFP_KERNEL);
+       event->name = kstrdup(name, GFP_KERNEL);
        if (!event->name) {
                kfree(event);
                event = ERR_PTR(-ENOMEM);
@@ -985,6 +1066,8 @@ static struct synth_event *alloc_synth_event(char *event_name, int n_fields,
                goto out;
        }
 
+       dyn_event_init(&event->devent, &synth_event_ops);
+
        for (i = 0; i < n_fields; i++)
                event->fields[i] = fields[i];
 
@@ -1008,29 +1091,11 @@ struct hist_var_data {
        struct hist_trigger_data *hist_data;
 };
 
-static void add_or_delete_synth_event(struct synth_event *event, int delete)
-{
-       if (delete)
-               free_synth_event(event);
-       else {
-               mutex_lock(&synth_event_mutex);
-               if (!find_synth_event(event->name))
-                       list_add(&event->list, &synth_event_list);
-               else
-                       free_synth_event(event);
-               mutex_unlock(&synth_event_mutex);
-       }
-}
-
-static int create_synth_event(int argc, char **argv)
+static int __create_synth_event(int argc, const char *name, const char **argv)
 {
        struct synth_field *field, *fields[SYNTH_FIELDS_MAX];
        struct synth_event *event = NULL;
-       bool delete_event = false;
        int i, consumed = 0, n_fields = 0, ret = 0;
-       char *name;
-
-       mutex_lock(&synth_event_mutex);
 
        /*
         * Argument syntax:
@@ -1038,42 +1103,19 @@ static int create_synth_event(int argc, char **argv)
         *  - Remove synthetic event: !<event_name> field[;field] ...
         *      where 'field' = type field_name
         */
-       if (argc < 1) {
-               ret = -EINVAL;
-               goto out;
-       }
 
-       name = argv[0];
-       if (name[0] == '!') {
-               delete_event = true;
-               name++;
-       }
+       if (name[0] == '\0' || argc < 1)
+               return -EINVAL;
+
+       mutex_lock(&event_mutex);
 
        event = find_synth_event(name);
        if (event) {
-               if (delete_event) {
-                       if (event->ref) {
-                               event = NULL;
-                               ret = -EBUSY;
-                               goto out;
-                       }
-                       list_del(&event->list);
-                       goto out;
-               }
-               event = NULL;
                ret = -EEXIST;
                goto out;
-       } else if (delete_event) {
-               ret = -ENOENT;
-               goto out;
-       }
-
-       if (argc < 2) {
-               ret = -EINVAL;
-               goto out;
        }
 
-       for (i = 1; i < argc - 1; i++) {
+       for (i = 0; i < argc - 1; i++) {
                if (strcmp(argv[i], ";") == 0)
                        continue;
                if (n_fields == SYNTH_FIELDS_MAX) {
@@ -1101,83 +1143,91 @@ static int create_synth_event(int argc, char **argv)
                event = NULL;
                goto err;
        }
+       ret = register_synth_event(event);
+       if (!ret)
+               dyn_event_add(&event->devent);
+       else
+               free_synth_event(event);
  out:
-       mutex_unlock(&synth_event_mutex);
-
-       if (event) {
-               if (delete_event) {
-                       ret = unregister_synth_event(event);
-                       add_or_delete_synth_event(event, !ret);
-               } else {
-                       ret = register_synth_event(event);
-                       add_or_delete_synth_event(event, ret);
-               }
-       }
+       mutex_unlock(&event_mutex);
 
        return ret;
  err:
-       mutex_unlock(&synth_event_mutex);
-
        for (i = 0; i < n_fields; i++)
                free_synth_field(fields[i]);
-       free_synth_event(event);
 
-       return ret;
+       goto out;
 }
 
-static int release_all_synth_events(void)
+static int create_or_delete_synth_event(int argc, char **argv)
 {
-       struct list_head release_events;
-       struct synth_event *event, *e;
-       int ret = 0;
-
-       INIT_LIST_HEAD(&release_events);
-
-       mutex_lock(&synth_event_mutex);
+       const char *name = argv[0];
+       struct synth_event *event = NULL;
+       int ret;
 
-       list_for_each_entry(event, &synth_event_list, list) {
-               if (event->ref) {
-                       mutex_unlock(&synth_event_mutex);
-                       return -EBUSY;
-               }
+       /* trace_run_command() ensures argc != 0 */
+       if (name[0] == '!') {
+               mutex_lock(&event_mutex);
+               event = find_synth_event(name + 1);
+               if (event) {
+                       if (event->ref)
+                               ret = -EBUSY;
+                       else {
+                               ret = unregister_synth_event(event);
+                               if (!ret) {
+                                       dyn_event_remove(&event->devent);
+                                       free_synth_event(event);
+                               }
+                       }
+               } else
+                       ret = -ENOENT;
+               mutex_unlock(&event_mutex);
+               return ret;
        }
 
-       list_splice_init(&event->list, &release_events);
+       ret = __create_synth_event(argc - 1, name, (const char **)argv + 1);
+       return ret == -ECANCELED ? -EINVAL : ret;
+}
 
-       mutex_unlock(&synth_event_mutex);
+static int synth_event_create(int argc, const char **argv)
+{
+       const char *name = argv[0];
+       int len;
 
-       list_for_each_entry_safe(event, e, &release_events, list) {
-               list_del(&event->list);
+       if (name[0] != 's' || name[1] != ':')
+               return -ECANCELED;
+       name += 2;
 
-               ret = unregister_synth_event(event);
-               add_or_delete_synth_event(event, !ret);
+       /* This interface accepts group name prefix */
+       if (strchr(name, '/')) {
+               len = sizeof(SYNTH_SYSTEM "/") - 1;
+               if (strncmp(name, SYNTH_SYSTEM "/", len))
+                       return -EINVAL;
+               name += len;
        }
-
-       return ret;
+       return __create_synth_event(argc - 1, name, argv + 1);
 }
 
-
-static void *synth_events_seq_start(struct seq_file *m, loff_t *pos)
+static int synth_event_release(struct dyn_event *ev)
 {
-       mutex_lock(&synth_event_mutex);
+       struct synth_event *event = to_synth_event(ev);
+       int ret;
 
-       return seq_list_start(&synth_event_list, *pos);
-}
+       if (event->ref)
+               return -EBUSY;
 
-static void *synth_events_seq_next(struct seq_file *m, void *v, loff_t *pos)
-{
-       return seq_list_next(v, &synth_event_list, pos);
-}
+       ret = unregister_synth_event(event);
+       if (ret)
+               return ret;
 
-static void synth_events_seq_stop(struct seq_file *m, void *v)
-{
-       mutex_unlock(&synth_event_mutex);
+       dyn_event_remove(ev);
+       free_synth_event(event);
+       return 0;
 }
 
-static int synth_events_seq_show(struct seq_file *m, void *v)
+static int __synth_event_show(struct seq_file *m, struct synth_event *event)
 {
        struct synth_field *field;
-       struct synth_event *event = v;
        unsigned int i;
 
        seq_printf(m, "%s\t", event->name);
@@ -1195,11 +1245,30 @@ static int synth_events_seq_show(struct seq_file *m, void *v)
        return 0;
 }
 
+static int synth_event_show(struct seq_file *m, struct dyn_event *ev)
+{
+       struct synth_event *event = to_synth_event(ev);
+
+       seq_printf(m, "s:%s/", event->class.system);
+
+       return __synth_event_show(m, event);
+}
+
+static int synth_events_seq_show(struct seq_file *m, void *v)
+{
+       struct dyn_event *ev = v;
+
+       if (!is_synth_event(ev))
+               return 0;
+
+       return __synth_event_show(m, to_synth_event(ev));
+}
+
 static const struct seq_operations synth_events_seq_op = {
-       .start  = synth_events_seq_start,
-       .next   = synth_events_seq_next,
-       .stop   = synth_events_seq_stop,
-       .show   = synth_events_seq_show
+       .start  = dyn_event_seq_start,
+       .next   = dyn_event_seq_next,
+       .stop   = dyn_event_seq_stop,
+       .show   = synth_events_seq_show,
 };
 
 static int synth_events_open(struct inode *inode, struct file *file)
@@ -1207,7 +1276,7 @@ static int synth_events_open(struct inode *inode, struct file *file)
        int ret;
 
        if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
-               ret = release_all_synth_events();
+               ret = dyn_events_release_all(&synth_event_ops);
                if (ret < 0)
                        return ret;
        }
@@ -1220,7 +1289,7 @@ static ssize_t synth_events_write(struct file *file,
                                  size_t count, loff_t *ppos)
 {
        return trace_parse_run_command(file, buffer, count, ppos,
-                                      create_synth_event);
+                                      create_or_delete_synth_event);
 }
 
 static const struct file_operations synth_events_fops = {
@@ -1257,82 +1326,73 @@ static u64 hist_field_cpu(struct hist_field *hist_field,
        return cpu;
 }
 
+/**
+ * check_field_for_var_ref - Check if a VAR_REF field references a variable
+ * @hist_field: The VAR_REF field to check
+ * @var_data: The hist trigger that owns the variable
+ * @var_idx: The trigger variable identifier
+ *
+ * Check the given VAR_REF field to see whether or not it references
+ * the given variable associated with the given trigger.
+ *
+ * Return: The VAR_REF field if it does reference the variable, NULL if not
+ */
 static struct hist_field *
 check_field_for_var_ref(struct hist_field *hist_field,
                        struct hist_trigger_data *var_data,
                        unsigned int var_idx)
 {
-       struct hist_field *found = NULL;
-
-       if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF) {
-               if (hist_field->var.idx == var_idx &&
-                   hist_field->var.hist_data == var_data) {
-                       found = hist_field;
-               }
-       }
+       WARN_ON(!(hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF));
 
-       return found;
-}
-
-static struct hist_field *
-check_field_for_var_refs(struct hist_trigger_data *hist_data,
-                        struct hist_field *hist_field,
-                        struct hist_trigger_data *var_data,
-                        unsigned int var_idx,
-                        unsigned int level)
-{
-       struct hist_field *found = NULL;
-       unsigned int i;
-
-       if (level > 3)
-               return found;
-
-       if (!hist_field)
-               return found;
-
-       found = check_field_for_var_ref(hist_field, var_data, var_idx);
-       if (found)
-               return found;
-
-       for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) {
-               struct hist_field *operand;
-
-               operand = hist_field->operands[i];
-               found = check_field_for_var_refs(hist_data, operand, var_data,
-                                                var_idx, level + 1);
-               if (found)
-                       return found;
-       }
+       if (hist_field && hist_field->var.idx == var_idx &&
+           hist_field->var.hist_data == var_data)
+               return hist_field;
 
-       return found;
+       return NULL;
 }
 
+/**
+ * find_var_ref - Check if a trigger has a reference to a trigger variable
+ * @hist_data: The hist trigger that might have a reference to the variable
+ * @var_data: The hist trigger that owns the variable
+ * @var_idx: The trigger variable identifier
+ *
+ * Check the list of var_refs[] on the first hist trigger to see
+ * whether any of them are references to the variable on the second
+ * trigger.
+ *
+ * Return: The VAR_REF field referencing the variable if so, NULL if not
+ */
 static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data,
                                       struct hist_trigger_data *var_data,
                                       unsigned int var_idx)
 {
-       struct hist_field *hist_field, *found = NULL;
+       struct hist_field *hist_field;
        unsigned int i;
 
-       for_each_hist_field(i, hist_data) {
-               hist_field = hist_data->fields[i];
-               found = check_field_for_var_refs(hist_data, hist_field,
-                                                var_data, var_idx, 0);
-               if (found)
-                       return found;
-       }
-
-       for (i = 0; i < hist_data->n_synth_var_refs; i++) {
-               hist_field = hist_data->synth_var_refs[i];
-               found = check_field_for_var_refs(hist_data, hist_field,
-                                                var_data, var_idx, 0);
-               if (found)
-                       return found;
+       for (i = 0; i < hist_data->n_var_refs; i++) {
+               hist_field = hist_data->var_refs[i];
+               if (check_field_for_var_ref(hist_field, var_data, var_idx))
+                       return hist_field;
        }
 
-       return found;
+       return NULL;
 }
 
+/**
+ * find_any_var_ref - Check if there is a reference to a given trigger variable
+ * @hist_data: The hist trigger
+ * @var_idx: The trigger variable identifier
+ *
+ * Check to see whether the given variable is currently referenced by
+ * any other trigger.
+ *
+ * The trigger the variable is defined on is explicitly excluded - the
+ * assumption being that a self-reference doesn't prevent a trigger
+ * from being removed.
+ *
+ * Return: The VAR_REF field referencing the variable if so, NULL if not
+ */
 static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data,
                                           unsigned int var_idx)
 {
@@ -1351,6 +1411,19 @@ static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data,
        return found;
 }
 
+/**
+ * check_var_refs - Check if there is a reference to any of trigger's variables
+ * @hist_data: The hist trigger
+ *
+ * A trigger can define one or more variables.  If any one of them is
+ * currently referenced by any other trigger, this function will
+ * determine that.
+
+ * Typically used to determine whether or not a trigger can be removed
+ * - if there are any references to a trigger's variables, it cannot.
+ *
+ * Return: True if there is a reference to any of trigger's variables
+ */
 static bool check_var_refs(struct hist_trigger_data *hist_data)
 {
        struct hist_field *field;
@@ -1808,8 +1881,8 @@ static int parse_action(char *str, struct hist_trigger_attrs *attrs)
        if (attrs->n_actions >= HIST_ACTIONS_MAX)
                return ret;
 
-       if ((strncmp(str, "onmatch(", strlen("onmatch(")) == 0) ||
-           (strncmp(str, "onmax(", strlen("onmax(")) == 0)) {
+       if ((str_has_prefix(str, "onmatch(")) ||
+           (str_has_prefix(str, "onmax("))) {
                attrs->action_str[attrs->n_actions] = kstrdup(str, GFP_KERNEL);
                if (!attrs->action_str[attrs->n_actions]) {
                        ret = -ENOMEM;
@@ -1826,34 +1899,34 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
 {
        int ret = 0;
 
-       if ((strncmp(str, "key=", strlen("key=")) == 0) ||
-           (strncmp(str, "keys=", strlen("keys=")) == 0)) {
+       if ((str_has_prefix(str, "key=")) ||
+           (str_has_prefix(str, "keys="))) {
                attrs->keys_str = kstrdup(str, GFP_KERNEL);
                if (!attrs->keys_str) {
                        ret = -ENOMEM;
                        goto out;
                }
-       } else if ((strncmp(str, "val=", strlen("val=")) == 0) ||
-                (strncmp(str, "vals=", strlen("vals=")) == 0) ||
-                (strncmp(str, "values=", strlen("values=")) == 0)) {
+       } else if ((str_has_prefix(str, "val=")) ||
+                  (str_has_prefix(str, "vals=")) ||
+                  (str_has_prefix(str, "values="))) {
                attrs->vals_str = kstrdup(str, GFP_KERNEL);
                if (!attrs->vals_str) {
                        ret = -ENOMEM;
                        goto out;
                }
-       } else if (strncmp(str, "sort=", strlen("sort=")) == 0) {
+       } else if (str_has_prefix(str, "sort=")) {
                attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
                if (!attrs->sort_key_str) {
                        ret = -ENOMEM;
                        goto out;
                }
-       } else if (strncmp(str, "name=", strlen("name=")) == 0) {
+       } else if (str_has_prefix(str, "name=")) {
                attrs->name = kstrdup(str, GFP_KERNEL);
                if (!attrs->name) {
                        ret = -ENOMEM;
                        goto out;
                }
-       } else if (strncmp(str, "clock=", strlen("clock=")) == 0) {
+       } else if (str_has_prefix(str, "clock=")) {
                strsep(&str, "=");
                if (!str) {
                        ret = -EINVAL;
@@ -1866,7 +1939,7 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
                        ret = -ENOMEM;
                        goto out;
                }
-       } else if (strncmp(str, "size=", strlen("size=")) == 0) {
+       } else if (str_has_prefix(str, "size=")) {
                int map_bits = parse_map_size(str);
 
                if (map_bits < 0) {
@@ -2151,6 +2224,15 @@ static int contains_operator(char *str)
        return field_op;
 }
 
+static void __destroy_hist_field(struct hist_field *hist_field)
+{
+       kfree(hist_field->var.name);
+       kfree(hist_field->name);
+       kfree(hist_field->type);
+
+       kfree(hist_field);
+}
+
 static void destroy_hist_field(struct hist_field *hist_field,
                               unsigned int level)
 {
@@ -2162,14 +2244,13 @@ static void destroy_hist_field(struct hist_field *hist_field,
        if (!hist_field)
                return;
 
+       if (hist_field->flags & HIST_FIELD_FL_VAR_REF)
+               return; /* var refs will be destroyed separately */
+
        for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++)
                destroy_hist_field(hist_field->operands[i], level + 1);
 
-       kfree(hist_field->var.name);
-       kfree(hist_field->name);
-       kfree(hist_field->type);
-
-       kfree(hist_field);
+       __destroy_hist_field(hist_field);
 }
 
 static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
@@ -2296,6 +2377,12 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
                        hist_data->fields[i] = NULL;
                }
        }
+
+       for (i = 0; i < hist_data->n_var_refs; i++) {
+               WARN_ON(!(hist_data->var_refs[i]->flags & HIST_FIELD_FL_VAR_REF));
+               __destroy_hist_field(hist_data->var_refs[i]);
+               hist_data->var_refs[i] = NULL;
+       }
 }
 
 static int init_var_ref(struct hist_field *ref_field,
@@ -2354,7 +2441,23 @@ static int init_var_ref(struct hist_field *ref_field,
        goto out;
 }
 
-static struct hist_field *create_var_ref(struct hist_field *var_field,
+/**
+ * create_var_ref - Create a variable reference and attach it to trigger
+ * @hist_data: The trigger that will be referencing the variable
+ * @var_field: The VAR field to create a reference to
+ * @system: The optional system string
+ * @event_name: The optional event_name string
+ *
+ * Given a variable hist_field, create a VAR_REF hist_field that
+ * represents a reference to it.
+ *
+ * This function also adds the reference to the trigger that
+ * now references the variable.
+ *
+ * Return: The VAR_REF field if successful, NULL if not
+ */
+static struct hist_field *create_var_ref(struct hist_trigger_data *hist_data,
+                                        struct hist_field *var_field,
                                         char *system, char *event_name)
 {
        unsigned long flags = HIST_FIELD_FL_VAR_REF;
@@ -2366,6 +2469,9 @@ static struct hist_field *create_var_ref(struct hist_field *var_field,
                        destroy_hist_field(ref_field, 0);
                        return NULL;
                }
+
+               hist_data->var_refs[hist_data->n_var_refs] = ref_field;
+               ref_field->var_ref_idx = hist_data->n_var_refs++;
        }
 
        return ref_field;
@@ -2439,7 +2545,8 @@ static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
 
        var_field = find_event_var(hist_data, system, event_name, var_name);
        if (var_field)
-               ref_field = create_var_ref(var_field, system, event_name);
+               ref_field = create_var_ref(hist_data, var_field,
+                                          system, event_name);
 
        if (!ref_field)
                hist_err_event("Couldn't find variable: $",
@@ -2557,8 +2664,6 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
        if (!s) {
                hist_field = parse_var_ref(hist_data, ref_system, ref_event, ref_var);
                if (hist_field) {
-                       hist_data->var_refs[hist_data->n_var_refs] = hist_field;
-                       hist_field->var_ref_idx = hist_data->n_var_refs++;
                        if (var_name) {
                                hist_field = create_alias(hist_data, hist_field, var_name);
                                if (!hist_field) {
@@ -3332,7 +3437,6 @@ static int onmax_create(struct hist_trigger_data *hist_data,
        unsigned int var_ref_idx = hist_data->n_var_refs;
        struct field_var *field_var;
        char *onmax_var_str, *param;
-       unsigned long flags;
        unsigned int i;
        int ret = 0;
 
@@ -3349,18 +3453,10 @@ static int onmax_create(struct hist_trigger_data *hist_data,
                return -EINVAL;
        }
 
-       flags = HIST_FIELD_FL_VAR_REF;
-       ref_field = create_hist_field(hist_data, NULL, flags, NULL);
+       ref_field = create_var_ref(hist_data, var_field, NULL, NULL);
        if (!ref_field)
                return -ENOMEM;
 
-       if (init_var_ref(ref_field, var_field, NULL, NULL)) {
-               destroy_hist_field(ref_field, 0);
-               ret = -ENOMEM;
-               goto out;
-       }
-       hist_data->var_refs[hist_data->n_var_refs] = ref_field;
-       ref_field->var_ref_idx = hist_data->n_var_refs++;
        data->onmax.var = ref_field;
 
        data->fn = onmax_save;
@@ -3462,7 +3558,7 @@ static struct action_data *onmax_parse(char *str)
        if (!onmax_fn_name || !str)
                goto free;
 
-       if (strncmp(onmax_fn_name, "save", strlen("save")) == 0) {
+       if (str_has_prefix(onmax_fn_name, "save")) {
                char *params = strsep(&str, ")");
 
                if (!params) {
@@ -3493,7 +3589,7 @@ static void onmatch_destroy(struct action_data *data)
 {
        unsigned int i;
 
-       mutex_lock(&synth_event_mutex);
+       lockdep_assert_held(&event_mutex);
 
        kfree(data->onmatch.match_event);
        kfree(data->onmatch.match_event_system);
@@ -3506,8 +3602,6 @@ static void onmatch_destroy(struct action_data *data)
                data->onmatch.synth_event->ref--;
 
        kfree(data);
-
-       mutex_unlock(&synth_event_mutex);
 }
 
 static void destroy_field_var(struct field_var *field_var)
@@ -3539,23 +3633,6 @@ static void save_field_var(struct hist_trigger_data *hist_data,
 }
 
 
-static void destroy_synth_var_refs(struct hist_trigger_data *hist_data)
-{
-       unsigned int i;
-
-       for (i = 0; i < hist_data->n_synth_var_refs; i++)
-               destroy_hist_field(hist_data->synth_var_refs[i], 0);
-}
-
-static void save_synth_var_ref(struct hist_trigger_data *hist_data,
-                        struct hist_field *var_ref)
-{
-       hist_data->synth_var_refs[hist_data->n_synth_var_refs++] = var_ref;
-
-       hist_data->var_refs[hist_data->n_var_refs] = var_ref;
-       var_ref->var_ref_idx = hist_data->n_var_refs++;
-}
-
 static int check_synth_field(struct synth_event *event,
                             struct hist_field *hist_field,
                             unsigned int field_pos)
@@ -3658,15 +3735,14 @@ static int onmatch_create(struct hist_trigger_data *hist_data,
        struct synth_event *event;
        int ret = 0;
 
-       mutex_lock(&synth_event_mutex);
+       lockdep_assert_held(&event_mutex);
+
        event = find_synth_event(data->onmatch.synth_event_name);
        if (!event) {
                hist_err("onmatch: Couldn't find synthetic event: ", data->onmatch.synth_event_name);
-               mutex_unlock(&synth_event_mutex);
                return -EINVAL;
        }
        event->ref++;
-       mutex_unlock(&synth_event_mutex);
 
        var_ref_idx = hist_data->n_var_refs;
 
@@ -3708,14 +3784,14 @@ static int onmatch_create(struct hist_trigger_data *hist_data,
                }
 
                if (check_synth_field(event, hist_field, field_pos) == 0) {
-                       var_ref = create_var_ref(hist_field, system, event_name);
+                       var_ref = create_var_ref(hist_data, hist_field,
+                                                system, event_name);
                        if (!var_ref) {
                                kfree(p);
                                ret = -ENOMEM;
                                goto err;
                        }
 
-                       save_synth_var_ref(hist_data, var_ref);
                        field_pos++;
                        kfree(p);
                        continue;
@@ -3740,9 +3816,7 @@ static int onmatch_create(struct hist_trigger_data *hist_data,
  out:
        return ret;
  err:
-       mutex_lock(&synth_event_mutex);
        event->ref--;
-       mutex_unlock(&synth_event_mutex);
 
        goto out;
 }
@@ -4268,12 +4342,13 @@ static int parse_actions(struct hist_trigger_data *hist_data)
        unsigned int i;
        int ret = 0;
        char *str;
+       int len;
 
        for (i = 0; i < hist_data->attrs->n_actions; i++) {
                str = hist_data->attrs->action_str[i];
 
-               if (strncmp(str, "onmatch(", strlen("onmatch(")) == 0) {
-                       char *action_str = str + strlen("onmatch(");
+               if ((len = str_has_prefix(str, "onmatch("))) {
+                       char *action_str = str + len;
 
                        data = onmatch_parse(tr, action_str);
                        if (IS_ERR(data)) {
@@ -4281,8 +4356,8 @@ static int parse_actions(struct hist_trigger_data *hist_data)
                                break;
                        }
                        data->fn = action_trace;
-               } else if (strncmp(str, "onmax(", strlen("onmax(")) == 0) {
-                       char *action_str = str + strlen("onmax(");
+               } else if ((len = str_has_prefix(str, "onmax("))) {
+                       char *action_str = str + len;
 
                        data = onmax_parse(action_str);
                        if (IS_ERR(data)) {
@@ -4461,7 +4536,6 @@ static void destroy_hist_data(struct hist_trigger_data *hist_data)
        destroy_actions(hist_data);
        destroy_field_vars(hist_data);
        destroy_field_var_hists(hist_data);
-       destroy_synth_var_refs(hist_data);
 
        kfree(hist_data);
 }
@@ -5450,6 +5524,8 @@ static void hist_unreg_all(struct trace_event_file *file)
        struct synth_event *se;
        const char *se_name;
 
+       lockdep_assert_held(&event_mutex);
+
        if (hist_file_check_refs(file))
                return;
 
@@ -5459,12 +5535,10 @@ static void hist_unreg_all(struct trace_event_file *file)
                        list_del_rcu(&test->list);
                        trace_event_trigger_enable_disable(file, 0);
 
-                       mutex_lock(&synth_event_mutex);
                        se_name = trace_event_name(file->event_call);
                        se = find_synth_event(se_name);
                        if (se)
                                se->ref--;
-                       mutex_unlock(&synth_event_mutex);
 
                        update_cond_flag(file);
                        if (hist_data->enable_timestamps)
@@ -5490,6 +5564,8 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
        char *trigger, *p;
        int ret = 0;
 
+       lockdep_assert_held(&event_mutex);
+
        if (glob && strlen(glob)) {
                last_cmd_set(param);
                hist_err_clear();
@@ -5516,9 +5592,9 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
                        p++;
                        continue;
                }
-               if (p >= param + strlen(param) - strlen("if") - 1)
+               if (p >= param + strlen(param) - (sizeof("if") - 1) - 1)
                        return -EINVAL;
-               if (*(p + strlen("if")) != ' ' && *(p + strlen("if")) != '\t') {
+               if (*(p + sizeof("if") - 1) != ' ' && *(p + sizeof("if") - 1) != '\t') {
                        p++;
                        continue;
                }
@@ -5580,14 +5656,10 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
                }
 
                cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
-
-               mutex_lock(&synth_event_mutex);
                se_name = trace_event_name(file->event_call);
                se = find_synth_event(se_name);
                if (se)
                        se->ref--;
-               mutex_unlock(&synth_event_mutex);
-
                ret = 0;
                goto out_free;
        }
@@ -5623,13 +5695,10 @@ enable:
        if (ret)
                goto out_unreg;
 
-       mutex_lock(&synth_event_mutex);
        se_name = trace_event_name(file->event_call);
        se = find_synth_event(se_name);
        if (se)
                se->ref++;
-       mutex_unlock(&synth_event_mutex);
-
        /* Just return zero, not the number of registered triggers */
        ret = 0;
  out:
@@ -5812,6 +5881,12 @@ static __init int trace_events_hist_init(void)
        struct dentry *d_tracer;
        int err = 0;
 
+       err = dyn_event_register(&synth_event_ops);
+       if (err) {
+               pr_warn("Could not register synth_event_ops\n");
+               return err;
+       }
+
        d_tracer = tracing_init_dentry();
        if (IS_ERR(d_tracer)) {
                err = PTR_ERR(d_tracer);