Merge branch 'misc.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / kernel / trace / trace_events_hist.c
index 949ef09..a6061a6 100644 (file)
@@ -121,6 +121,7 @@ struct hist_field {
        unsigned int                    size;
        unsigned int                    offset;
        unsigned int                    is_signed;
+       unsigned long                   buckets;
        const char                      *type;
        struct hist_field               *operands[HIST_FIELD_OPERANDS_MAX];
        struct hist_trigger_data        *hist_data;
@@ -219,6 +220,27 @@ static u64 hist_field_log2(struct hist_field *hist_field,
        return (u64) ilog2(roundup_pow_of_two(val));
 }
 
+static u64 hist_field_bucket(struct hist_field *hist_field,
+                            struct tracing_map_elt *elt,
+                            struct trace_buffer *buffer,
+                            struct ring_buffer_event *rbe,
+                            void *event)
+{
+       struct hist_field *operand = hist_field->operands[0];
+       unsigned long buckets = hist_field->buckets;
+
+       u64 val = operand->fn(operand, elt, buffer, rbe, event);
+
+       if (WARN_ON_ONCE(!buckets))
+               return val;
+
+       if (val >= LONG_MAX)
+               val = div64_ul(val, buckets);
+       else
+               val = (u64)((unsigned long)val / buckets);
+       return val * buckets;
+}
+
 static u64 hist_field_plus(struct hist_field *hist_field,
                           struct tracing_map_elt *elt,
                           struct trace_buffer *buffer,
@@ -318,6 +340,7 @@ enum hist_field_flags {
        HIST_FIELD_FL_VAR_REF           = 1 << 14,
        HIST_FIELD_FL_CPU               = 1 << 15,
        HIST_FIELD_FL_ALIAS             = 1 << 16,
+       HIST_FIELD_FL_BUCKET            = 1 << 17,
 };
 
 struct var_defs {
@@ -485,7 +508,8 @@ struct track_data {
 struct hist_elt_data {
        char *comm;
        u64 *var_ref_vals;
-       char *field_var_str[SYNTH_FIELDS_MAX];
+       char **field_var_str;
+       int n_field_var_str;
 };
 
 struct snapshot_context {
@@ -1109,7 +1133,8 @@ static const char *hist_field_name(struct hist_field *field,
        if (field->field)
                field_name = field->field->name;
        else if (field->flags & HIST_FIELD_FL_LOG2 ||
-                field->flags & HIST_FIELD_FL_ALIAS)
+                field->flags & HIST_FIELD_FL_ALIAS ||
+                field->flags & HIST_FIELD_FL_BUCKET)
                field_name = hist_field_name(field->operands[0], ++level);
        else if (field->flags & HIST_FIELD_FL_CPU)
                field_name = "common_cpu";
@@ -1377,9 +1402,11 @@ static void hist_elt_data_free(struct hist_elt_data *elt_data)
 {
        unsigned int i;
 
-       for (i = 0; i < SYNTH_FIELDS_MAX; i++)
+       for (i = 0; i < elt_data->n_field_var_str; i++)
                kfree(elt_data->field_var_str[i]);
 
+       kfree(elt_data->field_var_str);
+
        kfree(elt_data->comm);
        kfree(elt_data);
 }
@@ -1396,17 +1423,17 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt)
        struct hist_trigger_data *hist_data = elt->map->private_data;
        unsigned int size = TASK_COMM_LEN;
        struct hist_elt_data *elt_data;
-       struct hist_field *key_field;
+       struct hist_field *hist_field;
        unsigned int i, n_str;
 
        elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL);
        if (!elt_data)
                return -ENOMEM;
 
-       for_each_hist_key_field(i, hist_data) {
-               key_field = hist_data->fields[i];
+       for_each_hist_field(i, hist_data) {
+               hist_field = hist_data->fields[i];
 
-               if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
+               if (hist_field->flags & HIST_FIELD_FL_EXECNAME) {
                        elt_data->comm = kzalloc(size, GFP_KERNEL);
                        if (!elt_data->comm) {
                                kfree(elt_data);
@@ -1427,6 +1454,13 @@ static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt)
 
        size = STR_VAR_LEN_MAX;
 
+       elt_data->field_var_str = kcalloc(n_str, sizeof(char *), GFP_KERNEL);
+       if (!elt_data->field_var_str) {
+               hist_elt_data_free(elt_data);
+               return -EINVAL;
+       }
+       elt_data->n_field_var_str = n_str;
+
        for (i = 0; i < n_str; i++) {
                elt_data->field_var_str[i] = kzalloc(size, GFP_KERNEL);
                if (!elt_data->field_var_str[i]) {
@@ -1470,6 +1504,8 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
                flags_str = "syscall";
        else if (hist_field->flags & HIST_FIELD_FL_LOG2)
                flags_str = "log2";
+       else if (hist_field->flags & HIST_FIELD_FL_BUCKET)
+               flags_str = "buckets";
        else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS)
                flags_str = "usecs";
 
@@ -1590,7 +1626,9 @@ static void __destroy_hist_field(struct hist_field *hist_field)
 
        kfree(hist_field->var.name);
        kfree(hist_field->name);
-       kfree(hist_field->type);
+
+       /* Can likely be a const */
+       kfree_const(hist_field->type);
 
        kfree(hist_field->system);
        kfree(hist_field->event_name);
@@ -1647,9 +1685,7 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
        if (flags & HIST_FIELD_FL_HITCOUNT) {
                hist_field->fn = hist_field_counter;
                hist_field->size = sizeof(u64);
-               hist_field->type = kstrdup("u64", GFP_KERNEL);
-               if (!hist_field->type)
-                       goto free;
+               hist_field->type = "u64";
                goto out;
        }
 
@@ -1658,12 +1694,13 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
                goto out;
        }
 
-       if (flags & HIST_FIELD_FL_LOG2) {
-               unsigned long fl = flags & ~HIST_FIELD_FL_LOG2;
-               hist_field->fn = hist_field_log2;
+       if (flags & (HIST_FIELD_FL_LOG2 | HIST_FIELD_FL_BUCKET)) {
+               unsigned long fl = flags & ~(HIST_FIELD_FL_LOG2 | HIST_FIELD_FL_BUCKET);
+               hist_field->fn = flags & HIST_FIELD_FL_LOG2 ? hist_field_log2 :
+                       hist_field_bucket;
                hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL);
                hist_field->size = hist_field->operands[0]->size;
-               hist_field->type = kstrdup(hist_field->operands[0]->type, GFP_KERNEL);
+               hist_field->type = kstrdup_const(hist_field->operands[0]->type, GFP_KERNEL);
                if (!hist_field->type)
                        goto free;
                goto out;
@@ -1672,18 +1709,14 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
        if (flags & HIST_FIELD_FL_TIMESTAMP) {
                hist_field->fn = hist_field_timestamp;
                hist_field->size = sizeof(u64);
-               hist_field->type = kstrdup("u64", GFP_KERNEL);
-               if (!hist_field->type)
-                       goto free;
+               hist_field->type = "u64";
                goto out;
        }
 
        if (flags & HIST_FIELD_FL_CPU) {
                hist_field->fn = hist_field_cpu;
                hist_field->size = sizeof(int);
-               hist_field->type = kstrdup("unsigned int", GFP_KERNEL);
-               if (!hist_field->type)
-                       goto free;
+               hist_field->type = "unsigned int";
                goto out;
        }
 
@@ -1696,7 +1729,7 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
                flags |= HIST_FIELD_FL_STRING;
 
                hist_field->size = MAX_FILTER_STR_VAL;
-               hist_field->type = kstrdup(field->type, GFP_KERNEL);
+               hist_field->type = kstrdup_const(field->type, GFP_KERNEL);
                if (!hist_field->type)
                        goto free;
 
@@ -1709,7 +1742,7 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
        } else {
                hist_field->size = field->size;
                hist_field->is_signed = field->is_signed;
-               hist_field->type = kstrdup(field->type, GFP_KERNEL);
+               hist_field->type = kstrdup_const(field->type, GFP_KERNEL);
                if (!hist_field->type)
                        goto free;
 
@@ -1795,7 +1828,7 @@ static int init_var_ref(struct hist_field *ref_field,
                }
        }
 
-       ref_field->type = kstrdup(var_field->type, GFP_KERNEL);
+       ref_field->type = kstrdup_const(var_field->type, GFP_KERNEL);
        if (!ref_field->type) {
                err = -ENOMEM;
                goto free;
@@ -1953,7 +1986,7 @@ static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
 
 static struct ftrace_event_field *
 parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
-           char *field_str, unsigned long *flags)
+           char *field_str, unsigned long *flags, unsigned long *buckets)
 {
        struct ftrace_event_field *field = NULL;
        char *field_name, *modifier, *str;
@@ -1980,7 +2013,22 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
                        *flags |= HIST_FIELD_FL_LOG2;
                else if (strcmp(modifier, "usecs") == 0)
                        *flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
-               else {
+               else if (strncmp(modifier, "bucket", 6) == 0) {
+                       int ret;
+
+                       modifier += 6;
+
+                       if (*modifier == 's')
+                               modifier++;
+                       if (*modifier != '=')
+                               goto error;
+                       modifier++;
+                       ret = kstrtoul(modifier, 0, buckets);
+                       if (ret || !(*buckets))
+                               goto error;
+                       *flags |= HIST_FIELD_FL_BUCKET;
+               } else {
+ error:
                        hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(modifier));
                        field = ERR_PTR(-EINVAL);
                        goto out;
@@ -2049,6 +2097,7 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
        char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str;
        struct ftrace_event_field *field = NULL;
        struct hist_field *hist_field = NULL;
+       unsigned long buckets = 0;
        int ret = 0;
 
        s = strchr(str, '.');
@@ -2086,7 +2135,7 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
        } else
                str = s;
 
-       field = parse_field(hist_data, file, str, flags);
+       field = parse_field(hist_data, file, str, flags, &buckets);
        if (IS_ERR(field)) {
                ret = PTR_ERR(field);
                goto out;
@@ -2097,6 +2146,7 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
                ret = -ENOMEM;
                goto out;
        }
+       hist_field->buckets = buckets;
 
        return hist_field;
  out:
@@ -2171,7 +2221,7 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
        expr->operands[0] = operand1;
        expr->operator = FIELD_OP_UNARY_MINUS;
        expr->name = expr_str(expr, 0);
-       expr->type = kstrdup(operand1->type, GFP_KERNEL);
+       expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
        if (!expr->type) {
                ret = -ENOMEM;
                goto free;
@@ -2311,7 +2361,7 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
 
        expr->operator = field_op;
        expr->name = expr_str(expr, 0);
-       expr->type = kstrdup(operand1->type, GFP_KERNEL);
+       expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
        if (!expr->type) {
                ret = -ENOMEM;
                goto free;
@@ -2699,10 +2749,10 @@ static struct hist_field *create_var(struct hist_trigger_data *hist_data,
        var->var.hist_data = var->hist_data = hist_data;
        var->size = size;
        var->var.name = kstrdup(name, GFP_KERNEL);
-       var->type = kstrdup(type, GFP_KERNEL);
+       var->type = kstrdup_const(type, GFP_KERNEL);
        if (!var->var.name || !var->type) {
+               kfree_const(var->type);
                kfree(var->var.name);
-               kfree(var->type);
                kfree(var);
                var = ERR_PTR(-ENOMEM);
        }
@@ -3430,6 +3480,8 @@ trace_action_create_field_var(struct hist_trigger_data *hist_data,
                        event = data->match_data.event;
                }
 
+               if (!event)
+                       goto free;
                /*
                 * At this point, we're looking at a field on another
                 * event.  Because we can't modify a hist trigger on
@@ -3729,6 +3781,41 @@ static int create_val_field(struct hist_trigger_data *hist_data,
        return __create_val_field(hist_data, val_idx, file, NULL, field_str, 0);
 }
 
+static const char *no_comm = "(no comm)";
+
+static u64 hist_field_execname(struct hist_field *hist_field,
+                              struct tracing_map_elt *elt,
+                              struct trace_buffer *buffer,
+                              struct ring_buffer_event *rbe,
+                              void *event)
+{
+       struct hist_elt_data *elt_data;
+
+       if (WARN_ON_ONCE(!elt))
+               return (u64)(unsigned long)no_comm;
+
+       elt_data = elt->private_data;
+
+       if (WARN_ON_ONCE(!elt_data->comm))
+               return (u64)(unsigned long)no_comm;
+
+       return (u64)(unsigned long)(elt_data->comm);
+}
+
+/* Convert a var that points to common_pid.execname to a string */
+static void update_var_execname(struct hist_field *hist_field)
+{
+       hist_field->flags = HIST_FIELD_FL_STRING | HIST_FIELD_FL_VAR |
+               HIST_FIELD_FL_EXECNAME;
+       hist_field->size = MAX_FILTER_STR_VAL;
+       hist_field->is_signed = 0;
+
+       kfree_const(hist_field->type);
+       hist_field->type = "char[]";
+
+       hist_field->fn = hist_field_execname;
+}
+
 static int create_var_field(struct hist_trigger_data *hist_data,
                            unsigned int val_idx,
                            struct trace_event_file *file,
@@ -3753,6 +3840,9 @@ static int create_var_field(struct hist_trigger_data *hist_data,
 
        ret = __create_val_field(hist_data, val_idx, file, var_name, expr_str, flags);
 
+       if (!ret && hist_data->fields[val_idx]->flags & HIST_FIELD_FL_EXECNAME)
+               update_var_execname(hist_data->fields[val_idx]);
+
        if (!ret && hist_data->fields[val_idx]->flags & HIST_FIELD_FL_STRING)
                hist_data->fields[val_idx]->var_str_idx = hist_data->n_var_str++;
 
@@ -4696,6 +4786,11 @@ static void hist_trigger_print_key(struct seq_file *m,
                } else if (key_field->flags & HIST_FIELD_FL_LOG2) {
                        seq_printf(m, "%s: ~ 2^%-2llu", field_name,
                                   *(u64 *)(key + key_field->offset));
+               } else if (key_field->flags & HIST_FIELD_FL_BUCKET) {
+                       unsigned long buckets = key_field->buckets;
+                       uval = *(u64 *)(key + key_field->offset);
+                       seq_printf(m, "%s: ~ %llu-%llu", field_name,
+                                  uval, uval + buckets -1);
                } else if (key_field->flags & HIST_FIELD_FL_STRING) {
                        seq_printf(m, "%s: %-50s", field_name,
                                   (char *)(key + key_field->offset));
@@ -5135,6 +5230,8 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
                                seq_printf(m, ".%s", flags);
                }
        }
+       if (hist_field->buckets)
+               seq_printf(m, "=%ld", hist_field->buckets);
 }
 
 static int event_hist_trigger_print(struct seq_file *m,