--- /dev/null
+/*
+ * trace_events_hist - trace event hist triggers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2015 Tom Zanussi <tom.zanussi@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/stacktrace.h>
+
+#include "tracing_map.h"
+#include "trace.h"
+
+struct hist_field;
+
+typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event);
+
+struct hist_field {
+       struct ftrace_event_field       *field;
+       unsigned long                   flags;
+       hist_field_fn_t                 fn;
+       unsigned int                    size;
+};
+
+static u64 hist_field_counter(struct hist_field *field, void *event)
+{
+       return 1;
+}
+
+static u64 hist_field_string(struct hist_field *hist_field, void *event)
+{
+       char *addr = (char *)(event + hist_field->field->offset);
+
+       return (u64)(unsigned long)addr;
+}
+
+#define DEFINE_HIST_FIELD_FN(type)                                     \
+static u64 hist_field_##type(struct hist_field *hist_field, void *event)\
+{                                                                      \
+       type *addr = (type *)(event + hist_field->field->offset);       \
+                                                                       \
+       return (u64)*addr;                                              \
+}
+
+DEFINE_HIST_FIELD_FN(s64);
+DEFINE_HIST_FIELD_FN(u64);
+DEFINE_HIST_FIELD_FN(s32);
+DEFINE_HIST_FIELD_FN(u32);
+DEFINE_HIST_FIELD_FN(s16);
+DEFINE_HIST_FIELD_FN(u16);
+DEFINE_HIST_FIELD_FN(s8);
+DEFINE_HIST_FIELD_FN(u8);
+
+#define for_each_hist_field(i, hist_data)      \
+       for ((i) = 0; (i) < (hist_data)->n_fields; (i)++)
+
+#define for_each_hist_val_field(i, hist_data)  \
+       for ((i) = 0; (i) < (hist_data)->n_vals; (i)++)
+
+#define for_each_hist_key_field(i, hist_data)  \
+       for ((i) = (hist_data)->n_vals; (i) < (hist_data)->n_fields; (i)++)
+
+#define HITCOUNT_IDX           0
+#define HIST_KEY_MAX           1
+#define HIST_KEY_SIZE_MAX      MAX_FILTER_STR_VAL
+
+enum hist_field_flags {
+       HIST_FIELD_FL_HITCOUNT  = 1,
+       HIST_FIELD_FL_KEY       = 2,
+       HIST_FIELD_FL_STRING    = 4,
+};
+
+struct hist_trigger_attrs {
+       char            *keys_str;
+       unsigned int    map_bits;
+};
+
+struct hist_trigger_data {
+       struct hist_field               *fields[TRACING_MAP_FIELDS_MAX];
+       unsigned int                    n_vals;
+       unsigned int                    n_keys;
+       unsigned int                    n_fields;
+       unsigned int                    key_size;
+       struct tracing_map_sort_key     sort_keys[TRACING_MAP_SORT_KEYS_MAX];
+       unsigned int                    n_sort_keys;
+       struct trace_event_file         *event_file;
+       struct hist_trigger_attrs       *attrs;
+       struct tracing_map              *map;
+};
+
+static hist_field_fn_t select_value_fn(int field_size, int field_is_signed)
+{
+       hist_field_fn_t fn = NULL;
+
+       switch (field_size) {
+       case 8:
+               if (field_is_signed)
+                       fn = hist_field_s64;
+               else
+                       fn = hist_field_u64;
+               break;
+       case 4:
+               if (field_is_signed)
+                       fn = hist_field_s32;
+               else
+                       fn = hist_field_u32;
+               break;
+       case 2:
+               if (field_is_signed)
+                       fn = hist_field_s16;
+               else
+                       fn = hist_field_u16;
+               break;
+       case 1:
+               if (field_is_signed)
+                       fn = hist_field_s8;
+               else
+                       fn = hist_field_u8;
+               break;
+       }
+
+       return fn;
+}
+
+static int parse_map_size(char *str)
+{
+       unsigned long size, map_bits;
+       int ret;
+
+       strsep(&str, "=");
+       if (!str) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = kstrtoul(str, 0, &size);
+       if (ret)
+               goto out;
+
+       map_bits = ilog2(roundup_pow_of_two(size));
+       if (map_bits < TRACING_MAP_BITS_MIN ||
+           map_bits > TRACING_MAP_BITS_MAX)
+               ret = -EINVAL;
+       else
+               ret = map_bits;
+ out:
+       return ret;
+}
+
+static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
+{
+       if (!attrs)
+               return;
+
+       kfree(attrs->keys_str);
+       kfree(attrs);
+}
+
+static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
+{
+       struct hist_trigger_attrs *attrs;
+       int ret = 0;
+
+       attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
+       if (!attrs)
+               return ERR_PTR(-ENOMEM);
+
+       while (trigger_str) {
+               char *str = strsep(&trigger_str, ":");
+
+               if ((strncmp(str, "key=", strlen("key=")) == 0) ||
+                   (strncmp(str, "keys=", strlen("keys=")) == 0))
+                       attrs->keys_str = kstrdup(str, GFP_KERNEL);
+               else if (strncmp(str, "size=", strlen("size=")) == 0) {
+                       int map_bits = parse_map_size(str);
+
+                       if (map_bits < 0) {
+                               ret = map_bits;
+                               goto free;
+                       }
+                       attrs->map_bits = map_bits;
+               } else {
+                       ret = -EINVAL;
+                       goto free;
+               }
+       }
+
+       if (!attrs->keys_str) {
+               ret = -EINVAL;
+               goto free;
+       }
+
+       return attrs;
+ free:
+       destroy_hist_trigger_attrs(attrs);
+
+       return ERR_PTR(ret);
+}
+
+static void destroy_hist_field(struct hist_field *hist_field)
+{
+       kfree(hist_field);
+}
+
+static struct hist_field *create_hist_field(struct ftrace_event_field *field,
+                                           unsigned long flags)
+{
+       struct hist_field *hist_field;
+
+       if (field && is_function_field(field))
+               return NULL;
+
+       hist_field = kzalloc(sizeof(struct hist_field), GFP_KERNEL);
+       if (!hist_field)
+               return NULL;
+
+       if (flags & HIST_FIELD_FL_HITCOUNT) {
+               hist_field->fn = hist_field_counter;
+               goto out;
+       }
+
+       if (is_string_field(field)) {
+               flags |= HIST_FIELD_FL_STRING;
+               hist_field->fn = hist_field_string;
+       } else {
+               hist_field->fn = select_value_fn(field->size,
+                                                field->is_signed);
+               if (!hist_field->fn) {
+                       destroy_hist_field(hist_field);
+                       return NULL;
+               }
+       }
+ out:
+       hist_field->field = field;
+       hist_field->flags = flags;
+
+       return hist_field;
+}
+
+static void destroy_hist_fields(struct hist_trigger_data *hist_data)
+{
+       unsigned int i;
+
+       for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) {
+               if (hist_data->fields[i]) {
+                       destroy_hist_field(hist_data->fields[i]);
+                       hist_data->fields[i] = NULL;
+               }
+       }
+}
+
+static int create_hitcount_val(struct hist_trigger_data *hist_data)
+{
+       hist_data->fields[HITCOUNT_IDX] =
+               create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT);
+       if (!hist_data->fields[HITCOUNT_IDX])
+               return -ENOMEM;
+
+       hist_data->n_vals++;
+
+       if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int create_val_fields(struct hist_trigger_data *hist_data,
+                            struct trace_event_file *file)
+{
+       int ret;
+
+       ret = create_hitcount_val(hist_data);
+
+       return ret;
+}
+
+static int create_key_field(struct hist_trigger_data *hist_data,
+                           unsigned int key_idx,
+                           struct trace_event_file *file,
+                           char *field_str)
+{
+       struct ftrace_event_field *field = NULL;
+       unsigned long flags = 0;
+       unsigned int key_size;
+       int ret = 0;
+
+       if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX))
+               return -EINVAL;
+
+       flags |= HIST_FIELD_FL_KEY;
+
+       field = trace_find_event_field(file->event_call, field_str);
+       if (!field) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       key_size = field->size;
+
+       hist_data->fields[key_idx] = create_hist_field(field, flags);
+       if (!hist_data->fields[key_idx]) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       key_size = ALIGN(key_size, sizeof(u64));
+       hist_data->fields[key_idx]->size = key_size;
+       hist_data->key_size = key_size;
+       if (hist_data->key_size > HIST_KEY_SIZE_MAX) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       hist_data->n_keys++;
+
+       if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX))
+               return -EINVAL;
+
+       ret = key_size;
+ out:
+       return ret;
+}
+
+static int create_key_fields(struct hist_trigger_data *hist_data,
+                            struct trace_event_file *file)
+{
+       unsigned int i, n_vals = hist_data->n_vals;
+       char *fields_str, *field_str;
+       int ret = -EINVAL;
+
+       fields_str = hist_data->attrs->keys_str;
+       if (!fields_str)
+               goto out;
+
+       strsep(&fields_str, "=");
+       if (!fields_str)
+               goto out;
+
+       for (i = n_vals; i < n_vals + HIST_KEY_MAX; i++) {
+               field_str = strsep(&fields_str, ",");
+               if (!field_str)
+                       break;
+               ret = create_key_field(hist_data, i, file, field_str);
+               if (ret < 0)
+                       goto out;
+       }
+       if (fields_str) {
+               ret = -EINVAL;
+               goto out;
+       }
+       ret = 0;
+ out:
+       return ret;
+}
+
+static int create_hist_fields(struct hist_trigger_data *hist_data,
+                             struct trace_event_file *file)
+{
+       int ret;
+
+       ret = create_val_fields(hist_data, file);
+       if (ret)
+               goto out;
+
+       ret = create_key_fields(hist_data, file);
+       if (ret)
+               goto out;
+
+       hist_data->n_fields = hist_data->n_vals + hist_data->n_keys;
+ out:
+       return ret;
+}
+
+static int create_sort_keys(struct hist_trigger_data *hist_data)
+{
+       int ret = 0;
+
+       hist_data->n_sort_keys = 1; /* sort_keys[0] is always hitcount */
+
+       return ret;
+}
+
+static void destroy_hist_data(struct hist_trigger_data *hist_data)
+{
+       destroy_hist_trigger_attrs(hist_data->attrs);
+       destroy_hist_fields(hist_data);
+       tracing_map_destroy(hist_data->map);
+       kfree(hist_data);
+}
+
+static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
+{
+       struct tracing_map *map = hist_data->map;
+       struct ftrace_event_field *field;
+       struct hist_field *hist_field;
+       unsigned int i, idx;
+
+       for_each_hist_field(i, hist_data) {
+               hist_field = hist_data->fields[i];
+               if (hist_field->flags & HIST_FIELD_FL_KEY) {
+                       tracing_map_cmp_fn_t cmp_fn;
+
+                       field = hist_field->field;
+
+                       if (is_string_field(field))
+                               cmp_fn = tracing_map_cmp_string;
+                       else
+                               cmp_fn = tracing_map_cmp_num(field->size,
+                                                            field->is_signed);
+                       idx = tracing_map_add_key_field(map, 0, cmp_fn);
+               } else
+                       idx = tracing_map_add_sum_field(map);
+
+               if (idx < 0)
+                       return idx;
+       }
+
+       return 0;
+}
+
+static struct hist_trigger_data *
+create_hist_data(unsigned int map_bits,
+                struct hist_trigger_attrs *attrs,
+                struct trace_event_file *file)
+{
+       struct hist_trigger_data *hist_data;
+       int ret = 0;
+
+       hist_data = kzalloc(sizeof(*hist_data), GFP_KERNEL);
+       if (!hist_data)
+               return ERR_PTR(-ENOMEM);
+
+       hist_data->attrs = attrs;
+
+       ret = create_hist_fields(hist_data, file);
+       if (ret)
+               goto free;
+
+       ret = create_sort_keys(hist_data);
+       if (ret)
+               goto free;
+
+       hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
+                                           NULL, hist_data);
+       if (IS_ERR(hist_data->map)) {
+               ret = PTR_ERR(hist_data->map);
+               hist_data->map = NULL;
+               goto free;
+       }
+
+       ret = create_tracing_map_fields(hist_data);
+       if (ret)
+               goto free;
+
+       ret = tracing_map_init(hist_data->map);
+       if (ret)
+               goto free;
+
+       hist_data->event_file = file;
+ out:
+       return hist_data;
+ free:
+       hist_data->attrs = NULL;
+
+       destroy_hist_data(hist_data);
+
+       hist_data = ERR_PTR(ret);
+
+       goto out;
+}
+
+static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
+                                   struct tracing_map_elt *elt,
+                                   void *rec)
+{
+       struct hist_field *hist_field;
+       unsigned int i;
+       u64 hist_val;
+
+       for_each_hist_val_field(i, hist_data) {
+               hist_field = hist_data->fields[i];
+               hist_val = hist_field->fn(hist_field, rec);
+               tracing_map_update_sum(elt, i, hist_val);
+       }
+}
+
+static void event_hist_trigger(struct event_trigger_data *data, void *rec)
+{
+       struct hist_trigger_data *hist_data = data->private_data;
+       struct hist_field *key_field;
+       struct tracing_map_elt *elt;
+       u64 field_contents;
+       void *key = NULL;
+       unsigned int i;
+
+       for_each_hist_key_field(i, hist_data) {
+               key_field = hist_data->fields[i];
+
+               field_contents = key_field->fn(key_field, rec);
+               if (key_field->flags & HIST_FIELD_FL_STRING)
+                       key = (void *)(unsigned long)field_contents;
+               else
+                       key = (void *)&field_contents;
+       }
+
+       elt = tracing_map_insert(hist_data->map, key);
+       if (elt)
+               hist_trigger_elt_update(hist_data, elt, rec);
+}
+
+static void
+hist_trigger_entry_print(struct seq_file *m,
+                        struct hist_trigger_data *hist_data, void *key,
+                        struct tracing_map_elt *elt)
+{
+       struct hist_field *key_field;
+       unsigned int i;
+       u64 uval;
+
+       seq_puts(m, "{ ");
+
+       for_each_hist_key_field(i, hist_data) {
+               key_field = hist_data->fields[i];
+
+               if (i > hist_data->n_vals)
+                       seq_puts(m, ", ");
+
+               if (key_field->flags & HIST_FIELD_FL_STRING) {
+                       seq_printf(m, "%s: %-50s", key_field->field->name,
+                                  (char *)key);
+               } else {
+                       uval = *(u64 *)key;
+                       seq_printf(m, "%s: %10llu",
+                                  key_field->field->name, uval);
+               }
+       }
+
+       seq_puts(m, " }");
+
+       seq_printf(m, " hitcount: %10llu",
+                  tracing_map_read_sum(elt, HITCOUNT_IDX));
+
+       seq_puts(m, "\n");
+}
+
+static int print_entries(struct seq_file *m,
+                        struct hist_trigger_data *hist_data)
+{
+       struct tracing_map_sort_entry **sort_entries = NULL;
+       struct tracing_map *map = hist_data->map;
+       unsigned int i, n_entries;
+
+       n_entries = tracing_map_sort_entries(map, hist_data->sort_keys,
+                                            hist_data->n_sort_keys,
+                                            &sort_entries);
+       if (n_entries < 0)
+               return n_entries;
+
+       for (i = 0; i < n_entries; i++)
+               hist_trigger_entry_print(m, hist_data,
+                                        sort_entries[i]->key,
+                                        sort_entries[i]->elt);
+
+       tracing_map_destroy_sort_entries(sort_entries, n_entries);
+
+       return n_entries;
+}
+
+static int hist_show(struct seq_file *m, void *v)
+{
+       struct event_trigger_data *test, *data = NULL;
+       struct trace_event_file *event_file;
+       struct hist_trigger_data *hist_data;
+       int n_entries, ret = 0;
+
+       mutex_lock(&event_mutex);
+
+       event_file = event_file_data(m->private);
+       if (unlikely(!event_file)) {
+               ret = -ENODEV;
+               goto out_unlock;
+       }
+
+       list_for_each_entry_rcu(test, &event_file->triggers, list) {
+               if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+                       data = test;
+                       break;
+               }
+       }
+       if (!data)
+               goto out_unlock;
+
+       seq_puts(m, "# event histogram\n#\n# trigger info: ");
+       data->ops->print(m, data->ops, data);
+       seq_puts(m, "\n");
+
+       hist_data = data->private_data;
+       n_entries = print_entries(m, hist_data);
+       if (n_entries < 0) {
+               ret = n_entries;
+               n_entries = 0;
+       }
+
+       seq_printf(m, "\nTotals:\n    Hits: %llu\n    Entries: %u\n    Dropped: %llu\n",
+                  (u64)atomic64_read(&hist_data->map->hits),
+                  n_entries, (u64)atomic64_read(&hist_data->map->drops));
+ out_unlock:
+       mutex_unlock(&event_mutex);
+
+       return ret;
+}
+
+static int event_hist_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, hist_show, file);
+}
+
+const struct file_operations event_hist_fops = {
+       .open = event_hist_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
+{
+       seq_printf(m, "%s", hist_field->field->name);
+}
+
+static int event_hist_trigger_print(struct seq_file *m,
+                                   struct event_trigger_ops *ops,
+                                   struct event_trigger_data *data)
+{
+       struct hist_trigger_data *hist_data = data->private_data;
+       struct hist_field *key_field;
+       unsigned int i;
+
+       seq_puts(m, "hist:keys=");
+
+       for_each_hist_key_field(i, hist_data) {
+               key_field = hist_data->fields[i];
+
+               if (i > hist_data->n_vals)
+                       seq_puts(m, ",");
+
+               hist_field_print(m, key_field);
+       }
+
+       seq_puts(m, ":vals=");
+       seq_puts(m, "hitcount");
+
+       seq_puts(m, ":sort=");
+       seq_puts(m, "hitcount");
+
+       seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
+
+       if (data->filter_str)
+               seq_printf(m, " if %s", data->filter_str);
+
+       seq_puts(m, " [active]");
+
+       seq_putc(m, '\n');
+
+       return 0;
+}
+
+static void event_hist_trigger_free(struct event_trigger_ops *ops,
+                                   struct event_trigger_data *data)
+{
+       struct hist_trigger_data *hist_data = data->private_data;
+
+       if (WARN_ON_ONCE(data->ref <= 0))
+               return;
+
+       data->ref--;
+       if (!data->ref) {
+               trigger_data_free(data);
+               destroy_hist_data(hist_data);
+       }
+}
+
+static struct event_trigger_ops event_hist_trigger_ops = {
+       .func                   = event_hist_trigger,
+       .print                  = event_hist_trigger_print,
+       .init                   = event_trigger_init,
+       .free                   = event_hist_trigger_free,
+};
+
+static struct event_trigger_ops *event_hist_get_trigger_ops(char *cmd,
+                                                           char *param)
+{
+       return &event_hist_trigger_ops;
+}
+
+static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
+                                struct event_trigger_data *data,
+                                struct trace_event_file *file)
+{
+       struct event_trigger_data *test;
+       int ret = 0;
+
+       list_for_each_entry_rcu(test, &file->triggers, list) {
+               if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+                       ret = -EEXIST;
+                       goto out;
+               }
+       }
+
+       if (data->ops->init) {
+               ret = data->ops->init(data->ops, data);
+               if (ret < 0)
+                       goto out;
+       }
+
+       list_add_rcu(&data->list, &file->triggers);
+       ret++;
+
+       update_cond_flag(file);
+       if (trace_event_trigger_enable_disable(file, 1) < 0) {
+               list_del_rcu(&data->list);
+               update_cond_flag(file);
+               ret--;
+       }
+ out:
+       return ret;
+}
+
+static int event_hist_trigger_func(struct event_command *cmd_ops,
+                                  struct trace_event_file *file,
+                                  char *glob, char *cmd, char *param)
+{
+       unsigned int hist_trigger_bits = TRACING_MAP_BITS_DEFAULT;
+       struct event_trigger_data *trigger_data;
+       struct hist_trigger_attrs *attrs;
+       struct event_trigger_ops *trigger_ops;
+       struct hist_trigger_data *hist_data;
+       char *trigger;
+       int ret = 0;
+
+       if (!param)
+               return -EINVAL;
+
+       /* separate the trigger from the filter (k:v [if filter]) */
+       trigger = strsep(¶m, " \t");
+       if (!trigger)
+               return -EINVAL;
+
+       attrs = parse_hist_trigger_attrs(trigger);
+       if (IS_ERR(attrs))
+               return PTR_ERR(attrs);
+
+       if (attrs->map_bits)
+               hist_trigger_bits = attrs->map_bits;
+
+       hist_data = create_hist_data(hist_trigger_bits, attrs, file);
+       if (IS_ERR(hist_data)) {
+               destroy_hist_trigger_attrs(attrs);
+               return PTR_ERR(hist_data);
+       }
+
+       trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);
+
+       ret = -ENOMEM;
+       trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL);
+       if (!trigger_data)
+               goto out_free;
+
+       trigger_data->count = -1;
+       trigger_data->ops = trigger_ops;
+       trigger_data->cmd_ops = cmd_ops;
+
+       INIT_LIST_HEAD(&trigger_data->list);
+       RCU_INIT_POINTER(trigger_data->filter, NULL);
+
+       trigger_data->private_data = hist_data;
+
+       if (glob[0] == '!') {
+               cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
+               ret = 0;
+               goto out_free;
+       }
+
+       if (!param) /* if param is non-empty, it's supposed to be a filter */
+               goto out_reg;
+
+       if (!cmd_ops->set_filter)
+               goto out_reg;
+
+       ret = cmd_ops->set_filter(param, trigger_data, file);
+       if (ret < 0)
+               goto out_free;
+ out_reg:
+       ret = cmd_ops->reg(glob, trigger_ops, trigger_data, file);
+       /*
+        * The above returns on success the # of triggers registered,
+        * but if it didn't register any it returns zero.  Consider no
+        * triggers registered a failure too.
+        */
+       if (!ret) {
+               ret = -ENOENT;
+               goto out_free;
+       } else if (ret < 0)
+               goto out_free;
+       /* Just return zero, not the number of registered triggers */
+       ret = 0;
+ out:
+       return ret;
+ out_free:
+       if (cmd_ops->set_filter)
+               cmd_ops->set_filter(NULL, trigger_data, NULL);
+
+       kfree(trigger_data);
+
+       destroy_hist_data(hist_data);
+       goto out;
+}
+
+static struct event_command trigger_hist_cmd = {
+       .name                   = "hist",
+       .trigger_type           = ETT_EVENT_HIST,
+       .flags                  = EVENT_CMD_FL_NEEDS_REC,
+       .func                   = event_hist_trigger_func,
+       .reg                    = hist_register_trigger,
+       .unreg                  = unregister_trigger,
+       .get_trigger_ops        = event_hist_get_trigger_ops,
+       .set_filter             = set_trigger_filter,
+};
+
+__init int register_trigger_hist_cmd(void)
+{
+       int ret;
+
+       ret = register_event_command(&trigger_hist_cmd);
+       WARN_ON(ret < 0);
+
+       return ret;
+}