|
@@ -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) {
|
|
@@ -743,11 +769,7 @@ static int filter_set_pred(struct event_filter *filter,
|
|
|
|
|
|
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);
|
|
|
kfree(filter->preds);
|
|
|
filter->preds = NULL;
|
|
|
}
|
|
@@ -840,23 +862,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 +955,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 +1003,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;
|
|
|
}
|
|
|
|
|
@@ -1302,39 +1302,37 @@ 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;
|
|
|
|
|
|
- pred->field_name = kstrdup(operand1, GFP_KERNEL);
|
|
|
- if (!pred->field_name) {
|
|
|
- kfree(pred);
|
|
|
+ if (op == OP_AND || op == OP_OR)
|
|
|
+ return &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)
|
|
@@ -1375,6 +1373,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 +1398,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;
|
|
|
-
|
|
|
- /*
|
|
|
- * 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;
|
|
|
+ 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,
|
|
|
+ };
|
|
|
|
|
|
- preds = filter->preds;
|
|
|
- if (!preds)
|
|
|
- return -EINVAL;
|
|
|
- pred = root;
|
|
|
+ return walk_pred_tree(filter->preds, root,
|
|
|
+ check_pred_tree_cb, &data);
|
|
|
+}
|
|
|
|
|
|
- do {
|
|
|
- if (WARN_ON(count++ > max))
|
|
|
- return -EINVAL;
|
|
|
+static int count_leafs_cb(enum move_type move, struct filter_pred *pred,
|
|
|
+ int *err, void *data)
|
|
|
+{
|
|
|
+ int *count = data;
|
|
|
|
|
|
- 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);
|
|
|
+ if ((move == MOVE_DOWN) &&
|
|
|
+ (pred->left == FILTER_PRED_INVALID))
|
|
|
+ (*count)++;
|
|
|
|
|
|
- /* 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 +1485,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 +1515,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 +1573,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;
|
|
|
}
|
|
@@ -1958,17 +1878,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 +1929,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 */
|