tracing/filter: Do not allow infix to exceed end of string
[pandora-kernel.git] / kernel / trace / trace_events_filter.c
index 256764e..bfeb725 100644 (file)
@@ -381,6 +381,63 @@ get_pred_parent(struct filter_pred *pred, struct filter_pred *preds,
        return pred;
 }
 
+enum walk_return {
+       WALK_PRED_ABORT,
+       WALK_PRED_PARENT,
+       WALK_PRED_DEFAULT,
+};
+
+typedef int (*filter_pred_walkcb_t) (enum move_type move,
+                                    struct filter_pred *pred,
+                                    int *err, void *data);
+
+static int walk_pred_tree(struct filter_pred *preds,
+                         struct filter_pred *root,
+                         filter_pred_walkcb_t cb, void *data)
+{
+       struct filter_pred *pred = root;
+       enum move_type move = MOVE_DOWN;
+       int done = 0;
+
+       if  (!preds)
+               return -EINVAL;
+
+       do {
+               int err = 0, ret;
+
+               ret = cb(move, pred, &err, data);
+               if (ret == WALK_PRED_ABORT)
+                       return err;
+               if (ret == WALK_PRED_PARENT)
+                       goto get_parent;
+
+               switch (move) {
+               case MOVE_DOWN:
+                       if (pred->left != FILTER_PRED_INVALID) {
+                               pred = &preds[pred->left];
+                               continue;
+                       }
+                       goto get_parent;
+               case MOVE_UP_FROM_LEFT:
+                       pred = &preds[pred->right];
+                       move = MOVE_DOWN;
+                       continue;
+               case MOVE_UP_FROM_RIGHT:
+ get_parent:
+                       if (pred == root)
+                               break;
+                       pred = get_pred_parent(pred, preds,
+                                              pred->parent,
+                                              &move);
+                       continue;
+               }
+               done = 1;
+       } while (!done);
+
+       /* We are fine. */
+       return 0;
+}
+
 /*
  * A series of AND or ORs where found together. Instead of
  * climbing up and down the tree branches, an array of the
@@ -410,99 +467,91 @@ static int process_ops(struct filter_pred *preds,
 
        for (i = 0; i < op->val; i++) {
                pred = &preds[op->ops[i]];
-               match = pred->fn(pred, rec);
+               if (!WARN_ON_ONCE(!pred->fn))
+                       match = pred->fn(pred, rec);
                if (!!match == type)
                        return match;
        }
        return match;
 }
 
+struct filter_match_preds_data {
+       struct filter_pred *preds;
+       int match;
+       void *rec;
+};
+
+static int filter_match_preds_cb(enum move_type move, struct filter_pred *pred,
+                                int *err, void *data)
+{
+       struct filter_match_preds_data *d = data;
+
+       *err = 0;
+       switch (move) {
+       case MOVE_DOWN:
+               /* only AND and OR have children */
+               if (pred->left != FILTER_PRED_INVALID) {
+                       /* If ops is set, then it was folded. */
+                       if (!pred->ops)
+                               return WALK_PRED_DEFAULT;
+                       /* We can treat folded ops as a leaf node */
+                       d->match = process_ops(d->preds, pred, d->rec);
+               } else {
+                       if (!WARN_ON_ONCE(!pred->fn))
+                               d->match = pred->fn(pred, d->rec);
+               }
+
+               return WALK_PRED_PARENT;
+       case MOVE_UP_FROM_LEFT:
+               /*
+                * Check for short circuits.
+                *
+                * Optimization: !!match == (pred->op == OP_OR)
+                *   is the same as:
+                * if ((match && pred->op == OP_OR) ||
+                *     (!match && pred->op == OP_AND))
+                */
+               if (!!d->match == (pred->op == OP_OR))
+                       return WALK_PRED_PARENT;
+               break;
+       case MOVE_UP_FROM_RIGHT:
+               break;
+       }
+
+       return WALK_PRED_DEFAULT;
+}
+
 /* return 1 if event matches, 0 otherwise (discard) */
 int filter_match_preds(struct event_filter *filter, void *rec)
 {
-       int match = -1;
-       enum move_type move = MOVE_DOWN;
        struct filter_pred *preds;
-       struct filter_pred *pred;
        struct filter_pred *root;
-       int n_preds;
-       int done = 0;
+       struct filter_match_preds_data data = {
+               /* match is currently meaningless */
+               .match = -1,
+               .rec   = rec,
+       };
+       int n_preds, ret;
 
        /* no filter is considered a match */
        if (!filter)
                return 1;
 
        n_preds = filter->n_preds;
-
        if (!n_preds)
                return 1;
 
        /*
         * n_preds, root and filter->preds are protect with preemption disabled.
         */
-       preds = rcu_dereference_sched(filter->preds);
        root = rcu_dereference_sched(filter->root);
        if (!root)
                return 1;
 
-       pred = root;
-
-       /* match is currently meaningless */
-       match = -1;
-
-       do {
-               switch (move) {
-               case MOVE_DOWN:
-                       /* only AND and OR have children */
-                       if (pred->left != FILTER_PRED_INVALID) {
-                               /* If ops is set, then it was folded. */
-                               if (!pred->ops) {
-                                       /* keep going to down the left side */
-                                       pred = &preds[pred->left];
-                                       continue;
-                               }
-                               /* We can treat folded ops as a leaf node */
-                               match = process_ops(preds, pred, rec);
-                       } else
-                               match = pred->fn(pred, rec);
-                       /* If this pred is the only pred */
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               case MOVE_UP_FROM_LEFT:
-                       /*
-                        * Check for short circuits.
-                        *
-                        * Optimization: !!match == (pred->op == OP_OR)
-                        *   is the same as:
-                        * if ((match && pred->op == OP_OR) ||
-                        *     (!match && pred->op == OP_AND))
-                        */
-                       if (!!match == (pred->op == OP_OR)) {
-                               if (pred == root)
-                                       break;
-                               pred = get_pred_parent(pred, preds,
-                                                      pred->parent, &move);
-                               continue;
-                       }
-                       /* now go down the right side of the tree. */
-                       pred = &preds[pred->right];
-                       move = MOVE_DOWN;
-                       continue;
-               case MOVE_UP_FROM_RIGHT:
-                       /* We finished this equation. */
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               }
-               done = 1;
-       } while (!done);
-
-       return match;
+       data.preds = preds = rcu_dereference_sched(filter->preds);
+       ret = walk_pred_tree(preds, root, filter_match_preds_cb, &data);
+       WARN_ON(ret);
+       return data.match;
 }
 EXPORT_SYMBOL_GPL(filter_match_preds);
 
@@ -628,22 +677,6 @@ find_event_field(struct ftrace_event_call *call, char *name)
        return __find_event_field(head, name);
 }
 
-static void filter_free_pred(struct filter_pred *pred)
-{
-       if (!pred)
-               return;
-
-       kfree(pred->field_name);
-       kfree(pred);
-}
-
-static void filter_clear_pred(struct filter_pred *pred)
-{
-       kfree(pred->field_name);
-       pred->field_name = NULL;
-       pred->regex.len = 0;
-}
-
 static int __alloc_pred_stack(struct pred_stack *stack, int n_preds)
 {
        stack->preds = kzalloc(sizeof(*stack->preds)*(n_preds + 1), GFP_KERNEL);
@@ -689,20 +722,13 @@ __pop_pred_stack(struct pred_stack *stack)
 static int filter_set_pred(struct event_filter *filter,
                           int idx,
                           struct pred_stack *stack,
-                          struct filter_pred *src,
-                          filter_pred_fn_t fn)
+                          struct filter_pred *src)
 {
        struct filter_pred *dest = &filter->preds[idx];
        struct filter_pred *left;
        struct filter_pred *right;
 
        *dest = *src;
-       if (src->field_name) {
-               dest->field_name = kstrdup(src->field_name, GFP_KERNEL);
-               if (!dest->field_name)
-                       return -ENOMEM;
-       }
-       dest->fn = fn;
        dest->index = idx;
 
        if (dest->op == OP_OR || dest->op == OP_AND) {
@@ -746,8 +772,8 @@ static void __free_preds(struct event_filter *filter)
        int i;
 
        if (filter->preds) {
-               for (i = 0; i < filter->a_preds; i++)
-                       kfree(filter->preds[i].field_name);
+               for (i = 0; i < filter->n_preds; i++)
+                       kfree(filter->preds[i].ops);
                kfree(filter->preds);
                filter->preds = NULL;
        }
@@ -840,23 +866,19 @@ static void filter_free_subsystem_filters(struct event_subsystem *system)
        }
 }
 
-static int filter_add_pred_fn(struct filter_parse_state *ps,
-                             struct ftrace_event_call *call,
-                             struct event_filter *filter,
-                             struct filter_pred *pred,
-                             struct pred_stack *stack,
-                             filter_pred_fn_t fn)
+static int filter_add_pred(struct filter_parse_state *ps,
+                          struct event_filter *filter,
+                          struct filter_pred *pred,
+                          struct pred_stack *stack)
 {
-       int idx, err;
+       int err;
 
        if (WARN_ON(filter->n_preds == filter->a_preds)) {
                parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0);
                return -ENOSPC;
        }
 
-       idx = filter->n_preds;
-       filter_clear_pred(&filter->preds[idx]);
-       err = filter_set_pred(filter, idx, stack, pred, fn);
+       err = filter_set_pred(filter, filter->n_preds, stack, pred);
        if (err)
                return err;
 
@@ -937,31 +959,15 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size,
        return fn;
 }
 
-static int filter_add_pred(struct filter_parse_state *ps,
-                          struct ftrace_event_call *call,
-                          struct event_filter *filter,
-                          struct filter_pred *pred,
-                          struct pred_stack *stack,
-                          bool dry_run)
+static int init_pred(struct filter_parse_state *ps,
+                    struct ftrace_event_field *field,
+                    struct filter_pred *pred)
+
 {
-       struct ftrace_event_field *field;
-       filter_pred_fn_t fn;
+       filter_pred_fn_t fn = filter_pred_none;
        unsigned long long val;
        int ret;
 
-       fn = pred->fn = filter_pred_none;
-
-       if (pred->op == OP_AND)
-               goto add_pred_fn;
-       else if (pred->op == OP_OR)
-               goto add_pred_fn;
-
-       field = find_event_field(call, pred->field_name);
-       if (!field) {
-               parse_error(ps, FILT_ERR_FIELD_NOT_FOUND, 0);
-               return -EINVAL;
-       }
-
        pred->offset = field->offset;
 
        if (!is_legal_op(field, pred->op)) {
@@ -1001,9 +1007,7 @@ static int filter_add_pred(struct filter_parse_state *ps,
        if (pred->op == OP_NE)
                pred->not = 1;
 
-add_pred_fn:
-       if (!dry_run)
-               return filter_add_pred_fn(ps, call, filter, pred, stack, fn);
+       pred->fn = fn;
        return 0;
 }
 
@@ -1023,6 +1027,9 @@ static void parse_init(struct filter_parse_state *ps,
 
 static char infix_next(struct filter_parse_state *ps)
 {
+       if (!ps->infix.cnt)
+               return 0;
+
        ps->infix.cnt--;
 
        return ps->infix.string[ps->infix.tail++];
@@ -1038,6 +1045,9 @@ static char infix_peek(struct filter_parse_state *ps)
 
 static void infix_advance(struct filter_parse_state *ps)
 {
+       if (!ps->infix.cnt)
+               return;
+
        ps->infix.cnt--;
        ps->infix.tail++;
 }
@@ -1302,58 +1312,64 @@ parse_operand:
        return 0;
 }
 
-static struct filter_pred *create_pred(int op, char *operand1, char *operand2)
+static struct filter_pred *create_pred(struct filter_parse_state *ps,
+                                      struct ftrace_event_call *call,
+                                      int op, char *operand1, char *operand2)
 {
-       struct filter_pred *pred;
+       struct ftrace_event_field *field;
+       static struct filter_pred pred;
 
-       pred = kzalloc(sizeof(*pred), GFP_KERNEL);
-       if (!pred)
-               return NULL;
+       memset(&pred, 0, sizeof(pred));
+       pred.op = op;
+
+       if (op == OP_AND || op == OP_OR)
+               return &pred;
 
-       pred->field_name = kstrdup(operand1, GFP_KERNEL);
-       if (!pred->field_name) {
-               kfree(pred);
+       if (!operand1 || !operand2) {
+               parse_error(ps, FILT_ERR_MISSING_FIELD, 0);
                return NULL;
        }
 
-       strcpy(pred->regex.pattern, operand2);
-       pred->regex.len = strlen(pred->regex.pattern);
-
-       pred->op = op;
-
-       return pred;
-}
-
-static struct filter_pred *create_logical_pred(int op)
-{
-       struct filter_pred *pred;
-
-       pred = kzalloc(sizeof(*pred), GFP_KERNEL);
-       if (!pred)
+       field = find_event_field(call, operand1);
+       if (!field) {
+               parse_error(ps, FILT_ERR_FIELD_NOT_FOUND, 0);
                return NULL;
+       }
 
-       pred->op = op;
+       strcpy(pred.regex.pattern, operand2);
+       pred.regex.len = strlen(pred.regex.pattern);
 
-       return pred;
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+       pred.field = field;
+#endif
+       return init_pred(ps, field, &pred) ? NULL : &pred;
 }
 
 static int check_preds(struct filter_parse_state *ps)
 {
        int n_normal_preds = 0, n_logical_preds = 0;
        struct postfix_elt *elt;
+       int cnt = 0;
 
        list_for_each_entry(elt, &ps->postfix, list) {
-               if (elt->op == OP_NONE)
+               if (elt->op == OP_NONE) {
+                       cnt++;
                        continue;
+               }
 
                if (elt->op == OP_AND || elt->op == OP_OR) {
                        n_logical_preds++;
+                       cnt--;
                        continue;
                }
+               cnt--;
                n_normal_preds++;
+               /* all ops should have operands */
+               if (cnt < 0)
+                       break;
        }
 
-       if (!n_normal_preds || n_logical_preds >= n_normal_preds) {
+       if (cnt != 1 || !n_normal_preds || n_logical_preds >= n_normal_preds) {
                parse_error(ps, FILT_ERR_INVALID_FILTER, 0);
                return -EINVAL;
        }
@@ -1375,6 +1391,23 @@ static int count_preds(struct filter_parse_state *ps)
        return n_preds;
 }
 
+struct check_pred_data {
+       int count;
+       int max;
+};
+
+static int check_pred_tree_cb(enum move_type move, struct filter_pred *pred,
+                             int *err, void *data)
+{
+       struct check_pred_data *d = data;
+
+       if (WARN_ON(d->count++ > d->max)) {
+               *err = -EINVAL;
+               return WALK_PRED_ABORT;
+       }
+       return WALK_PRED_DEFAULT;
+}
+
 /*
  * The tree is walked at filtering of an event. If the tree is not correctly
  * built, it may cause an infinite loop. Check here that the tree does
@@ -1383,107 +1416,76 @@ static int count_preds(struct filter_parse_state *ps)
 static int check_pred_tree(struct event_filter *filter,
                           struct filter_pred *root)
 {
-       struct filter_pred *preds;
-       struct filter_pred *pred;
-       enum move_type move = MOVE_DOWN;
-       int count = 0;
-       int done = 0;
-       int max;
+       struct check_pred_data data = {
+               /*
+                * The max that we can hit a node is three times.
+                * Once going down, once coming up from left, and
+                * once coming up from right. This is more than enough
+                * since leafs are only hit a single time.
+                */
+               .max   = 3 * filter->n_preds,
+               .count = 0,
+       };
 
-       /*
-        * The max that we can hit a node is three times.
-        * Once going down, once coming up from left, and
-        * once coming up from right. This is more than enough
-        * since leafs are only hit a single time.
-        */
-       max = 3 * filter->n_preds;
+       return walk_pred_tree(filter->preds, root,
+                             check_pred_tree_cb, &data);
+}
 
-       preds = filter->preds;
-       if  (!preds)
-               return -EINVAL;
-       pred = root;
+static int count_leafs_cb(enum move_type move, struct filter_pred *pred,
+                         int *err, void *data)
+{
+       int *count = data;
 
-       do {
-               if (WARN_ON(count++ > max))
-                       return -EINVAL;
+       if ((move == MOVE_DOWN) &&
+           (pred->left == FILTER_PRED_INVALID))
+               (*count)++;
 
-               switch (move) {
-               case MOVE_DOWN:
-                       if (pred->left != FILTER_PRED_INVALID) {
-                               pred = &preds[pred->left];
-                               continue;
-                       }
-                       /* A leaf at the root is just a leaf in the tree */
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               case MOVE_UP_FROM_LEFT:
-                       pred = &preds[pred->right];
-                       move = MOVE_DOWN;
-                       continue;
-               case MOVE_UP_FROM_RIGHT:
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               }
-               done = 1;
-       } while (!done);
-
-       /* We are fine. */
-       return 0;
+       return WALK_PRED_DEFAULT;
 }
 
 static int count_leafs(struct filter_pred *preds, struct filter_pred *root)
 {
-       struct filter_pred *pred;
-       enum move_type move = MOVE_DOWN;
-       int count = 0;
-       int done = 0;
+       int count = 0, ret;
 
-       pred = root;
+       ret = walk_pred_tree(preds, root, count_leafs_cb, &count);
+       WARN_ON(ret);
+       return count;
+}
 
-       do {
-               switch (move) {
-               case MOVE_DOWN:
-                       if (pred->left != FILTER_PRED_INVALID) {
-                               pred = &preds[pred->left];
-                               continue;
-                       }
-                       /* A leaf at the root is just a leaf in the tree */
-                       if (pred == root)
-                               return 1;
-                       count++;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               case MOVE_UP_FROM_LEFT:
-                       pred = &preds[pred->right];
-                       move = MOVE_DOWN;
-                       continue;
-               case MOVE_UP_FROM_RIGHT:
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               }
-               done = 1;
-       } while (!done);
+struct fold_pred_data {
+       struct filter_pred *root;
+       int count;
+       int children;
+};
 
-       return count;
+static int fold_pred_cb(enum move_type move, struct filter_pred *pred,
+                       int *err, void *data)
+{
+       struct fold_pred_data *d = data;
+       struct filter_pred *root = d->root;
+
+       if (move != MOVE_DOWN)
+               return WALK_PRED_DEFAULT;
+       if (pred->left != FILTER_PRED_INVALID)
+               return WALK_PRED_DEFAULT;
+
+       if (WARN_ON(d->count == d->children)) {
+               *err = -EINVAL;
+               return WALK_PRED_ABORT;
+       }
+
+       pred->index &= ~FILTER_PRED_FOLD;
+       root->ops[d->count++] = pred->index;
+       return WALK_PRED_DEFAULT;
 }
 
 static int fold_pred(struct filter_pred *preds, struct filter_pred *root)
 {
-       struct filter_pred *pred;
-       enum move_type move = MOVE_DOWN;
-       int count = 0;
+       struct fold_pred_data data = {
+               .root  = root,
+               .count = 0,
+       };
        int children;
-       int done = 0;
 
        /* No need to keep the fold flag */
        root->index &= ~FILTER_PRED_FOLD;
@@ -1501,37 +1503,26 @@ static int fold_pred(struct filter_pred *preds, struct filter_pred *root)
                return -ENOMEM;
 
        root->val = children;
+       data.children = children;
+       return walk_pred_tree(preds, root, fold_pred_cb, &data);
+}
 
-       pred = root;
-       do {
-               switch (move) {
-               case MOVE_DOWN:
-                       if (pred->left != FILTER_PRED_INVALID) {
-                               pred = &preds[pred->left];
-                               continue;
-                       }
-                       if (WARN_ON(count == children))
-                               return -EINVAL;
-                       pred->index &= ~FILTER_PRED_FOLD;
-                       root->ops[count++] = pred->index;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               case MOVE_UP_FROM_LEFT:
-                       pred = &preds[pred->right];
-                       move = MOVE_DOWN;
-                       continue;
-               case MOVE_UP_FROM_RIGHT:
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               }
-               done = 1;
-       } while (!done);
+static int fold_pred_tree_cb(enum move_type move, struct filter_pred *pred,
+                            int *err, void *data)
+{
+       struct filter_pred *preds = data;
 
-       return 0;
+       if (move != MOVE_DOWN)
+               return WALK_PRED_DEFAULT;
+       if (!(pred->index & FILTER_PRED_FOLD))
+               return WALK_PRED_DEFAULT;
+
+       *err = fold_pred(preds, pred);
+       if (*err)
+               return WALK_PRED_ABORT;
+
+       /* eveyrhing below is folded, continue with parent */
+       return WALK_PRED_PARENT;
 }
 
 /*
@@ -1542,51 +1533,8 @@ static int fold_pred(struct filter_pred *preds, struct filter_pred *root)
 static int fold_pred_tree(struct event_filter *filter,
                           struct filter_pred *root)
 {
-       struct filter_pred *preds;
-       struct filter_pred *pred;
-       enum move_type move = MOVE_DOWN;
-       int done = 0;
-       int err;
-
-       preds = filter->preds;
-       if  (!preds)
-               return -EINVAL;
-       pred = root;
-
-       do {
-               switch (move) {
-               case MOVE_DOWN:
-                       if (pred->index & FILTER_PRED_FOLD) {
-                               err = fold_pred(preds, pred);
-                               if (err)
-                                       return err;
-                               /* Folded nodes are like leafs */
-                       } else if (pred->left != FILTER_PRED_INVALID) {
-                               pred = &preds[pred->left];
-                               continue;
-                       }
-
-                       /* A leaf at the root is just a leaf in the tree */
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               case MOVE_UP_FROM_LEFT:
-                       pred = &preds[pred->right];
-                       move = MOVE_DOWN;
-                       continue;
-               case MOVE_UP_FROM_RIGHT:
-                       if (pred == root)
-                               break;
-                       pred = get_pred_parent(pred, preds,
-                                              pred->parent, &move);
-                       continue;
-               }
-               done = 1;
-       } while (!done);
-
-       return 0;
+       return walk_pred_tree(filter->preds, root, fold_pred_tree_cb,
+                             filter->preds);
 }
 
 static int replace_preds(struct ftrace_event_call *call,
@@ -1643,27 +1591,17 @@ static int replace_preds(struct ftrace_event_call *call,
                        goto fail;
                }
 
-               if (elt->op == OP_AND || elt->op == OP_OR) {
-                       pred = create_logical_pred(elt->op);
-                       goto add_pred;
-               }
-
-               if (!operand1 || !operand2) {
-                       parse_error(ps, FILT_ERR_MISSING_FIELD, 0);
+               pred = create_pred(ps, call, elt->op, operand1, operand2);
+               if (!pred) {
                        err = -EINVAL;
                        goto fail;
                }
 
-               pred = create_pred(elt->op, operand1, operand2);
-add_pred:
-               if (!pred) {
-                       err = -ENOMEM;
-                       goto fail;
+               if (!dry_run) {
+                       err = filter_add_pred(ps, filter, pred, &stack);
+                       if (err)
+                               goto fail;
                }
-               err = filter_add_pred(ps, call, filter, pred, &stack, dry_run);
-               filter_free_pred(pred);
-               if (err)
-                       goto fail;
 
                operand1 = operand2 = NULL;
        }
@@ -1729,7 +1667,9 @@ static int replace_system_preds(struct event_subsystem *system,
                 */
                err = replace_preds(call, NULL, ps, filter_string, true);
                if (err)
-                       goto fail;
+                       call->flags |= TRACE_EVENT_FL_NO_SET_FILTER;
+               else
+                       call->flags &= ~TRACE_EVENT_FL_NO_SET_FILTER;
        }
 
        list_for_each_entry(call, &ftrace_events, list) {
@@ -1738,6 +1678,9 @@ static int replace_system_preds(struct event_subsystem *system,
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
+               if (call->flags & TRACE_EVENT_FL_NO_SET_FILTER)
+                       continue;
+
                filter_item = kzalloc(sizeof(*filter_item), GFP_KERNEL);
                if (!filter_item)
                        goto fail_mem;
@@ -1766,7 +1709,7 @@ static int replace_system_preds(struct event_subsystem *system,
                 * replace the filter for the call.
                 */
                filter = call->filter;
-               call->filter = filter_item->filter;
+               rcu_assign_pointer(call->filter, filter_item->filter);
                filter_item->filter = filter;
 
                fail = false;
@@ -1821,7 +1764,7 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
                filter = call->filter;
                if (!filter)
                        goto out_unlock;
-               call->filter = NULL;
+               RCU_INIT_POINTER(call->filter, NULL);
                /* Make sure the filter is not being used */
                synchronize_sched();
                __free_filter(filter);
@@ -1862,7 +1805,7 @@ out:
         * string
         */
        tmp = call->filter;
-       call->filter = filter;
+       rcu_assign_pointer(call->filter, filter);
        if (tmp) {
                /* Make sure the call is done with the filter */
                synchronize_sched();
@@ -1958,17 +1901,14 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id,
        int err;
        struct event_filter *filter;
        struct filter_parse_state *ps;
-       struct ftrace_event_call *call = NULL;
+       struct ftrace_event_call *call;
 
        mutex_lock(&event_mutex);
 
-       list_for_each_entry(call, &ftrace_events, list) {
-               if (call->event.type == event_id)
-                       break;
-       }
+       call = event->tp_event;
 
        err = -EINVAL;
-       if (&call->list == &ftrace_events)
+       if (!call)
                goto out_unlock;
 
        err = -EEXIST;
@@ -2012,3 +1952,215 @@ out_unlock:
 
 #endif /* CONFIG_PERF_EVENTS */
 
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace_events_filter_test.h"
+
+static int test_get_filter(char *filter_str, struct ftrace_event_call *call,
+                          struct event_filter **pfilter)
+{
+       struct event_filter *filter;
+       struct filter_parse_state *ps;
+       int err = -ENOMEM;
+
+       filter = __alloc_filter();
+       if (!filter)
+               goto out;
+
+       ps = kzalloc(sizeof(*ps), GFP_KERNEL);
+       if (!ps)
+               goto free_filter;
+
+       parse_init(ps, filter_ops, filter_str);
+       err = filter_parse(ps);
+       if (err)
+               goto free_ps;
+
+       err = replace_preds(call, filter, ps, filter_str, false);
+       if (!err)
+               *pfilter = filter;
+
+ free_ps:
+       filter_opstack_clear(ps);
+       postfix_clear(ps);
+       kfree(ps);
+
+ free_filter:
+       if (err)
+               __free_filter(filter);
+
+ out:
+       return err;
+}
+
+#define DATA_REC(m, va, vb, vc, vd, ve, vf, vg, vh, nvisit) \
+{ \
+       .filter = FILTER, \
+       .rec    = { .a = va, .b = vb, .c = vc, .d = vd, \
+                   .e = ve, .f = vf, .g = vg, .h = vh }, \
+       .match  = m, \
+       .not_visited = nvisit, \
+}
+#define YES 1
+#define NO  0
+
+static struct test_filter_data_t {
+       char *filter;
+       struct ftrace_raw_ftrace_test_filter rec;
+       int match;
+       char *not_visited;
+} test_filter_data[] = {
+#define FILTER "a == 1 && b == 1 && c == 1 && d == 1 && " \
+              "e == 1 && f == 1 && g == 1 && h == 1"
+       DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, ""),
+       DATA_REC(NO,  0, 1, 1, 1, 1, 1, 1, 1, "bcdefgh"),
+       DATA_REC(NO,  1, 1, 1, 1, 1, 1, 1, 0, ""),
+#undef FILTER
+#define FILTER "a == 1 || b == 1 || c == 1 || d == 1 || " \
+              "e == 1 || f == 1 || g == 1 || h == 1"
+       DATA_REC(NO,  0, 0, 0, 0, 0, 0, 0, 0, ""),
+       DATA_REC(YES, 0, 0, 0, 0, 0, 0, 0, 1, ""),
+       DATA_REC(YES, 1, 0, 0, 0, 0, 0, 0, 0, "bcdefgh"),
+#undef FILTER
+#define FILTER "(a == 1 || b == 1) && (c == 1 || d == 1) && " \
+              "(e == 1 || f == 1) && (g == 1 || h == 1)"
+       DATA_REC(NO,  0, 0, 1, 1, 1, 1, 1, 1, "dfh"),
+       DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""),
+       DATA_REC(YES, 1, 0, 1, 0, 0, 1, 0, 1, "bd"),
+       DATA_REC(NO,  1, 0, 1, 0, 0, 1, 0, 0, "bd"),
+#undef FILTER
+#define FILTER "(a == 1 && b == 1) || (c == 1 && d == 1) || " \
+              "(e == 1 && f == 1) || (g == 1 && h == 1)"
+       DATA_REC(YES, 1, 0, 1, 1, 1, 1, 1, 1, "efgh"),
+       DATA_REC(YES, 0, 0, 0, 0, 0, 0, 1, 1, ""),
+       DATA_REC(NO,  0, 0, 0, 0, 0, 0, 0, 1, ""),
+#undef FILTER
+#define FILTER "(a == 1 && b == 1) && (c == 1 && d == 1) && " \
+              "(e == 1 && f == 1) || (g == 1 && h == 1)"
+       DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 0, "gh"),
+       DATA_REC(NO,  0, 0, 0, 0, 0, 0, 0, 1, ""),
+       DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, ""),
+#undef FILTER
+#define FILTER "((a == 1 || b == 1) || (c == 1 || d == 1) || " \
+              "(e == 1 || f == 1)) && (g == 1 || h == 1)"
+       DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 1, "bcdef"),
+       DATA_REC(NO,  0, 0, 0, 0, 0, 0, 0, 0, ""),
+       DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, "h"),
+#undef FILTER
+#define FILTER "((((((((a == 1) && (b == 1)) || (c == 1)) && (d == 1)) || " \
+              "(e == 1)) && (f == 1)) || (g == 1)) && (h == 1))"
+       DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "ceg"),
+       DATA_REC(NO,  0, 1, 0, 1, 0, 1, 0, 1, ""),
+       DATA_REC(NO,  1, 0, 1, 0, 1, 0, 1, 0, ""),
+#undef FILTER
+#define FILTER "((((((((a == 1) || (b == 1)) && (c == 1)) || (d == 1)) && " \
+              "(e == 1)) || (f == 1)) && (g == 1)) || (h == 1))"
+       DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "bdfh"),
+       DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""),
+       DATA_REC(YES, 1, 0, 1, 0, 1, 0, 1, 0, "bdfh"),
+};
+
+#undef DATA_REC
+#undef FILTER
+#undef YES
+#undef NO
+
+#define DATA_CNT (sizeof(test_filter_data)/sizeof(struct test_filter_data_t))
+
+static int test_pred_visited;
+
+static int test_pred_visited_fn(struct filter_pred *pred, void *event)
+{
+       struct ftrace_event_field *field = pred->field;
+
+       test_pred_visited = 1;
+       printk(KERN_INFO "\npred visited %s\n", field->name);
+       return 1;
+}
+
+static int test_walk_pred_cb(enum move_type move, struct filter_pred *pred,
+                            int *err, void *data)
+{
+       char *fields = data;
+
+       if ((move == MOVE_DOWN) &&
+           (pred->left == FILTER_PRED_INVALID)) {
+               struct ftrace_event_field *field = pred->field;
+
+               if (!field) {
+                       WARN(1, "all leafs should have field defined");
+                       return WALK_PRED_DEFAULT;
+               }
+               if (!strchr(fields, *field->name))
+                       return WALK_PRED_DEFAULT;
+
+               WARN_ON(!pred->fn);
+               pred->fn = test_pred_visited_fn;
+       }
+       return WALK_PRED_DEFAULT;
+}
+
+static __init int ftrace_test_event_filter(void)
+{
+       int i;
+
+       printk(KERN_INFO "Testing ftrace filter: ");
+
+       for (i = 0; i < DATA_CNT; i++) {
+               struct event_filter *filter = NULL;
+               struct test_filter_data_t *d = &test_filter_data[i];
+               int err;
+
+               err = test_get_filter(d->filter, &event_ftrace_test_filter,
+                                     &filter);
+               if (err) {
+                       printk(KERN_INFO
+                              "Failed to get filter for '%s', err %d\n",
+                              d->filter, err);
+                       break;
+               }
+
+               /*
+                * The preemption disabling is not really needed for self
+                * tests, but the rcu dereference will complain without it.
+                */
+               preempt_disable();
+               if (*d->not_visited)
+                       walk_pred_tree(filter->preds, filter->root,
+                                      test_walk_pred_cb,
+                                      d->not_visited);
+
+               test_pred_visited = 0;
+               err = filter_match_preds(filter, &d->rec);
+               preempt_enable();
+
+               __free_filter(filter);
+
+               if (test_pred_visited) {
+                       printk(KERN_INFO
+                              "Failed, unwanted pred visited for filter %s\n",
+                              d->filter);
+                       break;
+               }
+
+               if (err != d->match) {
+                       printk(KERN_INFO
+                              "Failed to match filter '%s', expected %d\n",
+                              d->filter, d->match);
+                       break;
+               }
+       }
+
+       if (i == DATA_CNT)
+               printk(KERN_CONT "OK\n");
+
+       return 0;
+}
+
+late_initcall(ftrace_test_event_filter);
+
+#endif /* CONFIG_FTRACE_STARTUP_TEST */