struct hist_trigger_data        *hist_data;
        struct hist_var                 var;
        enum field_op_id                operator;
+       char                            *system;
+       char                            *event_name;
        char                            *name;
+       unsigned int                    var_idx;
+       unsigned int                    var_ref_idx;
+       bool                            read_once;
 };
 
 static u64 hist_field_none(struct hist_field *field,
        HIST_FIELD_FL_TIMESTAMP_USECS   = 1 << 11,
        HIST_FIELD_FL_VAR               = 1 << 12,
        HIST_FIELD_FL_EXPR              = 1 << 13,
+       HIST_FIELD_FL_VAR_REF           = 1 << 14,
 };
 
 struct var_defs {
        struct tracing_map              *map;
        bool                            enable_timestamps;
        bool                            remove;
+       struct hist_field               *var_refs[TRACING_MAP_VARS_MAX];
+       unsigned int                    n_var_refs;
 };
 
 static u64 hist_field_timestamp(struct hist_field *hist_field,
        return ts;
 }
 
+struct hist_var_data {
+       struct list_head list;
+       struct hist_trigger_data *hist_data;
+};
+
+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;
+               }
+       }
+
+       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;
+       }
+
+       return found;
+}
+
+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;
+       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;
+       }
+
+       return found;
+}
+
+static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data,
+                                          unsigned int var_idx)
+{
+       struct trace_array *tr = hist_data->event_file->tr;
+       struct hist_field *found = NULL;
+       struct hist_var_data *var_data;
+
+       list_for_each_entry(var_data, &tr->hist_vars, list) {
+               if (var_data->hist_data == hist_data)
+                       continue;
+               found = find_var_ref(var_data->hist_data, hist_data, var_idx);
+               if (found)
+                       break;
+       }
+
+       return found;
+}
+
+static bool check_var_refs(struct hist_trigger_data *hist_data)
+{
+       struct hist_field *field;
+       bool found = false;
+       int i;
+
+       for_each_hist_field(i, hist_data) {
+               field = hist_data->fields[i];
+               if (field && field->flags & HIST_FIELD_FL_VAR) {
+                       if (find_any_var_ref(hist_data, field->var.idx)) {
+                               found = true;
+                               break;
+                       }
+               }
+       }
+
+       return found;
+}
+
+static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data)
+{
+       struct trace_array *tr = hist_data->event_file->tr;
+       struct hist_var_data *var_data, *found = NULL;
+
+       list_for_each_entry(var_data, &tr->hist_vars, list) {
+               if (var_data->hist_data == hist_data) {
+                       found = var_data;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+static bool field_has_hist_vars(struct hist_field *hist_field,
+                               unsigned int level)
+{
+       int i;
+
+       if (level > 3)
+               return false;
+
+       if (!hist_field)
+               return false;
+
+       if (hist_field->flags & HIST_FIELD_FL_VAR ||
+           hist_field->flags & HIST_FIELD_FL_VAR_REF)
+               return true;
+
+       for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) {
+               struct hist_field *operand;
+
+               operand = hist_field->operands[i];
+               if (field_has_hist_vars(operand, level + 1))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool has_hist_vars(struct hist_trigger_data *hist_data)
+{
+       struct hist_field *hist_field;
+       int i;
+
+       for_each_hist_field(i, hist_data) {
+               hist_field = hist_data->fields[i];
+               if (field_has_hist_vars(hist_field, 0))
+                       return true;
+       }
+
+       return false;
+}
+
+static int save_hist_vars(struct hist_trigger_data *hist_data)
+{
+       struct trace_array *tr = hist_data->event_file->tr;
+       struct hist_var_data *var_data;
+
+       var_data = find_hist_vars(hist_data);
+       if (var_data)
+               return 0;
+
+       if (trace_array_get(tr) < 0)
+               return -ENODEV;
+
+       var_data = kzalloc(sizeof(*var_data), GFP_KERNEL);
+       if (!var_data) {
+               trace_array_put(tr);
+               return -ENOMEM;
+       }
+
+       var_data->hist_data = hist_data;
+       list_add(&var_data->list, &tr->hist_vars);
+
+       return 0;
+}
+
+static void remove_hist_vars(struct hist_trigger_data *hist_data)
+{
+       struct trace_array *tr = hist_data->event_file->tr;
+       struct hist_var_data *var_data;
+
+       var_data = find_hist_vars(hist_data);
+       if (!var_data)
+               return;
+
+       if (WARN_ON(check_var_refs(hist_data)))
+               return;
+
+       list_del(&var_data->list);
+
+       kfree(var_data);
+
+       trace_array_put(tr);
+}
+
 static struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
                                         const char *var_name)
 {
        return NULL;
 }
 
+static struct trace_event_file *find_var_file(struct trace_array *tr,
+                                             char *system,
+                                             char *event_name,
+                                             char *var_name)
+{
+       struct hist_trigger_data *var_hist_data;
+       struct hist_var_data *var_data;
+       struct trace_event_file *file, *found = NULL;
+
+       if (system)
+               return find_event_file(tr, system, event_name);
+
+       list_for_each_entry(var_data, &tr->hist_vars, list) {
+               var_hist_data = var_data->hist_data;
+               file = var_hist_data->event_file;
+               if (file == found)
+                       continue;
+
+               if (find_var_field(var_hist_data, var_name)) {
+                       if (found)
+                               return NULL;
+
+                       found = file;
+               }
+       }
+
+       return found;
+}
+
+static struct hist_field *find_file_var(struct trace_event_file *file,
+                                       const char *var_name)
+{
+       struct hist_trigger_data *test_data;
+       struct event_trigger_data *test;
+       struct hist_field *hist_field;
+
+       list_for_each_entry_rcu(test, &file->triggers, list) {
+               if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+                       test_data = test->private_data;
+                       hist_field = find_var_field(test_data, var_name);
+                       if (hist_field)
+                               return hist_field;
+               }
+       }
+
+       return NULL;
+}
+
+static struct hist_field *find_event_var(struct hist_trigger_data *hist_data,
+                                        char *system,
+                                        char *event_name,
+                                        char *var_name)
+{
+       struct trace_array *tr = hist_data->event_file->tr;
+       struct hist_field *hist_field = NULL;
+       struct trace_event_file *file;
+
+       file = find_var_file(tr, system, event_name, var_name);
+       if (!file)
+               return NULL;
+
+       hist_field = find_file_var(file, var_name);
+
+       return hist_field;
+}
+
 struct hist_elt_data {
        char *comm;
+       u64 *var_ref_vals;
 };
 
+static u64 hist_field_var_ref(struct hist_field *hist_field,
+                             struct tracing_map_elt *elt,
+                             struct ring_buffer_event *rbe,
+                             void *event)
+{
+       struct hist_elt_data *elt_data;
+       u64 var_val = 0;
+
+       elt_data = elt->private_data;
+       var_val = elt_data->var_ref_vals[hist_field->var_ref_idx];
+
+       return var_val;
+}
+
+static bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key,
+                            u64 *var_ref_vals, bool self)
+{
+       struct hist_trigger_data *var_data;
+       struct tracing_map_elt *var_elt;
+       struct hist_field *hist_field;
+       unsigned int i, var_idx;
+       bool resolved = true;
+       u64 var_val = 0;
+
+       for (i = 0; i < hist_data->n_var_refs; i++) {
+               hist_field = hist_data->var_refs[i];
+               var_idx = hist_field->var.idx;
+               var_data = hist_field->var.hist_data;
+
+               if (var_data == NULL) {
+                       resolved = false;
+                       break;
+               }
+
+               if ((self && var_data != hist_data) ||
+                   (!self && var_data == hist_data))
+                       continue;
+
+               var_elt = tracing_map_lookup(var_data->map, key);
+               if (!var_elt) {
+                       resolved = false;
+                       break;
+               }
+
+               if (!tracing_map_var_set(var_elt, var_idx)) {
+                       resolved = false;
+                       break;
+               }
+
+               if (self || !hist_field->read_once)
+                       var_val = tracing_map_read_var(var_elt, var_idx);
+               else
+                       var_val = tracing_map_read_var_once(var_elt, var_idx);
+
+               var_ref_vals[i] = var_val;
+       }
+
+       return resolved;
+}
+
 static const char *hist_field_name(struct hist_field *field,
                                   unsigned int level)
 {
                field_name = hist_field_name(field->operands[0], ++level);
        else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
                field_name = "common_timestamp";
-       else if (field->flags & HIST_FIELD_FL_EXPR)
-               field_name = field->name;
+       else if (field->flags & HIST_FIELD_FL_EXPR ||
+                field->flags & HIST_FIELD_FL_VAR_REF) {
+               if (field->system) {
+                       static char full_name[MAX_FILTER_STR_VAL];
+
+                       strcat(full_name, field->system);
+                       strcat(full_name, ".");
+                       strcat(full_name, field->event_name);
+                       strcat(full_name, ".");
+                       strcat(full_name, field->name);
+                       field_name = full_name;
+               } else
+                       field_name = field->name;
+       }
 
        if (field_name == NULL)
                field_name = "";
 
 static void expr_field_str(struct hist_field *field, char *expr)
 {
+       if (field->flags & HIST_FIELD_FL_VAR_REF)
+               strcat(expr, "$");
+
        strcat(expr, hist_field_name(field, 0));
 
        if (field->flags) {
        if (flags & HIST_FIELD_FL_EXPR)
                goto out; /* caller will populate */
 
+       if (flags & HIST_FIELD_FL_VAR_REF) {
+               hist_field->fn = hist_field_var_ref;
+               goto out;
+       }
+
        if (flags & HIST_FIELD_FL_HITCOUNT) {
                hist_field->fn = hist_field_counter;
                hist_field->size = sizeof(u64);
        }
 }
 
+static int init_var_ref(struct hist_field *ref_field,
+                       struct hist_field *var_field,
+                       char *system, char *event_name)
+{
+       int err = 0;
+
+       ref_field->var.idx = var_field->var.idx;
+       ref_field->var.hist_data = var_field->hist_data;
+       ref_field->size = var_field->size;
+       ref_field->is_signed = var_field->is_signed;
+       ref_field->flags |= var_field->flags &
+               (HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
+
+       if (system) {
+               ref_field->system = kstrdup(system, GFP_KERNEL);
+               if (!ref_field->system)
+                       return -ENOMEM;
+       }
+
+       if (event_name) {
+               ref_field->event_name = kstrdup(event_name, GFP_KERNEL);
+               if (!ref_field->event_name) {
+                       err = -ENOMEM;
+                       goto free;
+               }
+       }
+
+       ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL);
+       if (!ref_field->name) {
+               err = -ENOMEM;
+               goto free;
+       }
+
+       ref_field->type = kstrdup(var_field->type, GFP_KERNEL);
+       if (!ref_field->type) {
+               err = -ENOMEM;
+               goto free;
+       }
+ out:
+       return err;
+ free:
+       kfree(ref_field->system);
+       kfree(ref_field->event_name);
+       kfree(ref_field->name);
+
+       goto out;
+}
+
+static struct hist_field *create_var_ref(struct hist_field *var_field,
+                                        char *system, char *event_name)
+{
+       unsigned long flags = HIST_FIELD_FL_VAR_REF;
+       struct hist_field *ref_field;
+
+       ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL);
+       if (ref_field) {
+               if (init_var_ref(ref_field, var_field, system, event_name)) {
+                       destroy_hist_field(ref_field, 0);
+                       return NULL;
+               }
+       }
+
+       return ref_field;
+}
+
+static bool is_var_ref(char *var_name)
+{
+       if (!var_name || strlen(var_name) < 2 || var_name[0] != '$')
+               return false;
+
+       return true;
+}
+
+static char *field_name_from_var(struct hist_trigger_data *hist_data,
+                                char *var_name)
+{
+       char *name, *field;
+       unsigned int i;
+
+       for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) {
+               name = hist_data->attrs->var_defs.name[i];
+
+               if (strcmp(var_name, name) == 0) {
+                       field = hist_data->attrs->var_defs.expr[i];
+                       if (contains_operator(field) || is_var_ref(field))
+                               continue;
+                       return field;
+               }
+       }
+
+       return NULL;
+}
+
+static char *local_field_var_ref(struct hist_trigger_data *hist_data,
+                                char *system, char *event_name,
+                                char *var_name)
+{
+       struct trace_event_call *call;
+
+       if (system && event_name) {
+               call = hist_data->event_file->event_call;
+
+               if (strcmp(system, call->class->system) != 0)
+                       return NULL;
+
+               if (strcmp(event_name, trace_event_name(call)) != 0)
+                       return NULL;
+       }
+
+       if (!!system != !!event_name)
+               return NULL;
+
+       if (!is_var_ref(var_name))
+               return NULL;
+
+       var_name++;
+
+       return field_name_from_var(hist_data, var_name);
+}
+
+static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
+                                       char *system, char *event_name,
+                                       char *var_name)
+{
+       struct hist_field *var_field = NULL, *ref_field = NULL;
+
+       if (!is_var_ref(var_name))
+               return NULL;
+
+       var_name++;
+
+       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);
+
+       return ref_field;
+}
+
 static struct ftrace_event_field *
 parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
            char *field_str, unsigned long *flags)
                                     struct trace_event_file *file, char *str,
                                     unsigned long *flags, char *var_name)
 {
+       char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str;
        struct ftrace_event_field *field = NULL;
        struct hist_field *hist_field = NULL;
        int ret = 0;
 
+       s = strchr(str, '.');
+       if (s) {
+               s = strchr(++s, '.');
+               if (s) {
+                       ref_system = strsep(&str, ".");
+                       if (!str) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       ref_event = strsep(&str, ".");
+                       if (!str) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       ref_var = str;
+               }
+       }
+
+       s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var);
+       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++;
+                       return hist_field;
+               }
+       } else
+               str = s;
+
        field = parse_field(hist_data, file, str, flags);
        if (IS_ERR(field)) {
                ret = PTR_ERR(field);
                goto free;
        }
 
+       operand1->read_once = true;
+       operand2->read_once = true;
+
        expr->operands[0] = operand1;
        expr->operands[1] = operand2;
        expr->operator = field_op;
                        goto out;
                }
 
+               if (hist_field->flags & HIST_FIELD_FL_VAR_REF) {
+                       destroy_hist_field(hist_field, 0);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
                key_size = hist_field->size;
        }
 
 
        hist_data->attrs = attrs;
        hist_data->remove = remove;
+       hist_data->event_file = file;
 
        ret = create_hist_fields(hist_data, file);
        if (ret)
        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:
 
 static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
                                    struct tracing_map_elt *elt, void *rec,
-                                   struct ring_buffer_event *rbe)
+                                   struct ring_buffer_event *rbe,
+                                   u64 *var_ref_vals)
 {
+       struct hist_elt_data *elt_data;
        struct hist_field *hist_field;
        unsigned int i, var_idx;
        u64 hist_val;
 
+       elt_data = elt->private_data;
+       elt_data->var_ref_vals = var_ref_vals;
+
        for_each_hist_val_field(i, hist_data) {
                hist_field = hist_data->fields[i];
                hist_val = hist_field->fn(hist_field, elt, rbe, rec);
        struct hist_trigger_data *hist_data = data->private_data;
        bool use_compound_key = (hist_data->n_keys > 1);
        unsigned long entries[HIST_STACKTRACE_DEPTH];
+       u64 var_ref_vals[TRACING_MAP_VARS_MAX];
        char compound_key[HIST_KEY_SIZE_MAX];
        struct tracing_map_elt *elt = NULL;
        struct stack_trace stacktrace;
        if (use_compound_key)
                key = compound_key;
 
+       if (hist_data->n_var_refs &&
+           !resolve_var_refs(hist_data, key, var_ref_vals, false))
+               return;
+
        elt = tracing_map_insert(hist_data->map, key);
-       if (elt)
-               hist_trigger_elt_update(hist_data, elt, rec, rbe);
+       if (!elt)
+               return;
+
+       hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals);
 }
 
 static void hist_trigger_stacktrace_print(struct seq_file *m,
 
        if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP)
                seq_puts(m, "common_timestamp");
-       else if (field_name)
+       else if (field_name) {
+               if (hist_field->flags & HIST_FIELD_FL_VAR_REF)
+                       seq_putc(m, '$');
                seq_printf(m, "%s", field_name);
+       }
 
        if (hist_field->flags) {
                const char *flags_str = get_hist_field_flags(hist_field);
        if (!data->ref) {
                if (data->name)
                        del_named_trigger(data);
+
                trigger_data_free(data);
+
+               remove_hist_vars(hist_data);
+
                destroy_hist_data(hist_data);
        }
 }
                        goto out;
        }
 
-       list_add_rcu(&data->list, &file->triggers);
        ret++;
 
-       update_cond_flag(file);
-
        if (hist_data->enable_timestamps)
                tracing_set_time_stamp_abs(file->tr, true);
+ out:
+       return ret;
+}
+
+static int hist_trigger_enable(struct event_trigger_data *data,
+                              struct trace_event_file *file)
+{
+       int ret = 0;
+
+       list_add_tail_rcu(&data->list, &file->triggers);
+
+       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 bool hist_trigger_check_refs(struct event_trigger_data *data,
+                                   struct trace_event_file *file)
+{
+       struct hist_trigger_data *hist_data = data->private_data;
+       struct event_trigger_data *test, *named_data = NULL;
+
+       if (hist_data->attrs->name)
+               named_data = find_named_trigger(hist_data->attrs->name);
+
+       list_for_each_entry_rcu(test, &file->triggers, list) {
+               if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+                       if (!hist_trigger_match(data, test, named_data, false))
+                               continue;
+                       hist_data = test->private_data;
+                       if (check_var_refs(hist_data))
+                               return true;
+                       break;
+               }
+       }
+
+       return false;
+}
+
 static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
                                    struct event_trigger_data *data,
                                    struct trace_event_file *file)
        }
 }
 
+static bool hist_file_check_refs(struct trace_event_file *file)
+{
+       struct hist_trigger_data *hist_data;
+       struct event_trigger_data *test;
+
+       list_for_each_entry_rcu(test, &file->triggers, list) {
+               if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
+                       hist_data = test->private_data;
+                       if (check_var_refs(hist_data))
+                               return true;
+               }
+       }
+
+       return false;
+}
+
 static void hist_unreg_all(struct trace_event_file *file)
 {
        struct event_trigger_data *test, *n;
        struct hist_trigger_data *hist_data;
 
+       if (hist_file_check_refs(file))
+               return;
+
        list_for_each_entry_safe(test, n, &file->triggers, list) {
                if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
                        hist_data = test->private_data;
        }
 
        if (remove) {
+               if (hist_trigger_check_refs(trigger_data, file)) {
+                       ret = -EBUSY;
+                       goto out_free;
+               }
+
                cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
                ret = 0;
                goto out_free;
                goto out_free;
        } else if (ret < 0)
                goto out_free;
+
+       if (get_named_trigger_data(trigger_data))
+               goto enable;
+
+       if (has_hist_vars(hist_data))
+               save_hist_vars(hist_data);
+
+       ret = tracing_map_init(hist_data->map);
+       if (ret)
+               goto out_unreg;
+enable:
+       ret = hist_trigger_enable(trigger_data, file);
+       if (ret)
+               goto out_unreg;
+
        /* Just return zero, not the number of registered triggers */
        ret = 0;
  out:
        return ret;
+ out_unreg:
+       cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
  out_free:
        if (cmd_ops->set_filter)
                cmd_ops->set_filter(NULL, trigger_data, NULL);
 
+       remove_hist_vars(hist_data);
+
        kfree(trigger_data);
 
        destroy_hist_data(hist_data);