KVM: MMU: invalidate and flush on spte small->large page size change
[safe/jmp/linux-2.6] / kernel / trace / trace_events_filter.c
index b9aae72..57bb1bb 100644 (file)
  * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
  */
 
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/ctype.h>
 #include <linux/mutex.h>
+#include <linux/perf_event.h>
+#include <linux/slab.h>
 
 #include "trace.h"
 #include "trace_output.h"
@@ -31,6 +31,7 @@ enum filter_op_ids
 {
        OP_OR,
        OP_AND,
+       OP_GLOB,
        OP_NE,
        OP_EQ,
        OP_LT,
@@ -48,16 +49,17 @@ struct filter_op {
 };
 
 static struct filter_op filter_ops[] = {
-       { OP_OR, "||", 1 },
-       { OP_AND, "&&", 2 },
-       { OP_NE, "!=", 4 },
-       { OP_EQ, "==", 4 },
-       { OP_LT, "<", 5 },
-       { OP_LE, "<=", 5 },
-       { OP_GT, ">", 5 },
-       { OP_GE, ">=", 5 },
-       { OP_NONE, "OP_NONE", 0 },
-       { OP_OPEN_PAREN, "(", 0 },
+       { OP_OR,        "||",           1 },
+       { OP_AND,       "&&",           2 },
+       { OP_GLOB,      "~",            4 },
+       { OP_NE,        "!=",           4 },
+       { OP_EQ,        "==",           4 },
+       { OP_LT,        "<",            5 },
+       { OP_LE,        "<=",           5 },
+       { OP_GT,        ">",            5 },
+       { OP_GE,        ">=",           5 },
+       { OP_NONE,      "OP_NONE",      0 },
+       { OP_OPEN_PAREN, "(",           0 },
 };
 
 enum {
@@ -121,6 +123,47 @@ struct filter_parse_state {
        } operand;
 };
 
+#define DEFINE_COMPARISON_PRED(type)                                   \
+static int filter_pred_##type(struct filter_pred *pred, void *event,   \
+                             int val1, int val2)                       \
+{                                                                      \
+       type *addr = (type *)(event + pred->offset);                    \
+       type val = (type)pred->val;                                     \
+       int match = 0;                                                  \
+                                                                       \
+       switch (pred->op) {                                             \
+       case OP_LT:                                                     \
+               match = (*addr < val);                                  \
+               break;                                                  \
+       case OP_LE:                                                     \
+               match = (*addr <= val);                                 \
+               break;                                                  \
+       case OP_GT:                                                     \
+               match = (*addr > val);                                  \
+               break;                                                  \
+       case OP_GE:                                                     \
+               match = (*addr >= val);                                 \
+               break;                                                  \
+       default:                                                        \
+               break;                                                  \
+       }                                                               \
+                                                                       \
+       return match;                                                   \
+}
+
+#define DEFINE_EQUALITY_PRED(size)                                     \
+static int filter_pred_##size(struct filter_pred *pred, void *event,   \
+                             int val1, int val2)                       \
+{                                                                      \
+       u##size *addr = (u##size *)(event + pred->offset);              \
+       u##size val = (u##size)pred->val;                               \
+       int match;                                                      \
+                                                                       \
+       match = (val == *addr) ^ pred->not;                             \
+                                                                       \
+       return match;                                                   \
+}
+
 DEFINE_COMPARISON_PRED(s64);
 DEFINE_COMPARISON_PRED(u64);
 DEFINE_COMPARISON_PRED(s32);
@@ -156,9 +199,24 @@ static int filter_pred_string(struct filter_pred *pred, void *event,
        char *addr = (char *)(event + pred->offset);
        int cmp, match;
 
-       cmp = strncmp(addr, pred->str_val, pred->str_len);
+       cmp = pred->regex.match(addr, &pred->regex, pred->regex.field_len);
+
+       match = cmp ^ pred->not;
+
+       return match;
+}
+
+/* Filter predicate for char * pointers */
+static int filter_pred_pchar(struct filter_pred *pred, void *event,
+                            int val1, int val2)
+{
+       char **addr = (char **)(event + pred->offset);
+       int cmp, match;
+       int len = strlen(*addr) + 1;    /* including tailing '\0' */
+
+       cmp = pred->regex.match(*addr, &pred->regex, len);
 
-       match = (!cmp) ^ pred->not;
+       match = cmp ^ pred->not;
 
        return match;
 }
@@ -176,13 +234,15 @@ static int filter_pred_string(struct filter_pred *pred, void *event,
 static int filter_pred_strloc(struct filter_pred *pred, void *event,
                              int val1, int val2)
 {
-       unsigned short str_loc = *(unsigned short *)(event + pred->offset);
+       u32 str_item = *(u32 *)(event + pred->offset);
+       int str_loc = str_item & 0xffff;
+       int str_len = str_item >> 16;
        char *addr = (char *)(event + str_loc);
        int cmp, match;
 
-       cmp = strncmp(addr, pred->str_val, pred->str_len);
+       cmp = pred->regex.match(addr, &pred->regex, str_len);
 
-       match = (!cmp) ^ pred->not;
+       match = cmp ^ pred->not;
 
        return match;
 }
@@ -193,10 +253,133 @@ static int filter_pred_none(struct filter_pred *pred, void *event,
        return 0;
 }
 
+/*
+ * regex_match_foo - Basic regex callbacks
+ *
+ * @str: the string to be searched
+ * @r:   the regex structure containing the pattern string
+ * @len: the length of the string to be searched (including '\0')
+ *
+ * Note:
+ * - @str might not be NULL-terminated if it's of type DYN_STRING
+ *   or STATIC_STRING
+ */
+
+static int regex_match_full(char *str, struct regex *r, int len)
+{
+       if (strncmp(str, r->pattern, len) == 0)
+               return 1;
+       return 0;
+}
+
+static int regex_match_front(char *str, struct regex *r, int len)
+{
+       if (strncmp(str, r->pattern, r->len) == 0)
+               return 1;
+       return 0;
+}
+
+static int regex_match_middle(char *str, struct regex *r, int len)
+{
+       if (strnstr(str, r->pattern, len))
+               return 1;
+       return 0;
+}
+
+static int regex_match_end(char *str, struct regex *r, int len)
+{
+       int strlen = len - 1;
+
+       if (strlen >= r->len &&
+           memcmp(str + strlen - r->len, r->pattern, r->len) == 0)
+               return 1;
+       return 0;
+}
+
+/**
+ * filter_parse_regex - parse a basic regex
+ * @buff:   the raw regex
+ * @len:    length of the regex
+ * @search: will point to the beginning of the string to compare
+ * @not:    tell whether the match will have to be inverted
+ *
+ * This passes in a buffer containing a regex and this function will
+ * set search to point to the search part of the buffer and
+ * return the type of search it is (see enum above).
+ * This does modify buff.
+ *
+ * Returns enum type.
+ *  search returns the pointer to use for comparison.
+ *  not returns 1 if buff started with a '!'
+ *     0 otherwise.
+ */
+enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not)
+{
+       int type = MATCH_FULL;
+       int i;
+
+       if (buff[0] == '!') {
+               *not = 1;
+               buff++;
+               len--;
+       } else
+               *not = 0;
+
+       *search = buff;
+
+       for (i = 0; i < len; i++) {
+               if (buff[i] == '*') {
+                       if (!i) {
+                               *search = buff + 1;
+                               type = MATCH_END_ONLY;
+                       } else {
+                               if (type == MATCH_END_ONLY)
+                                       type = MATCH_MIDDLE_ONLY;
+                               else
+                                       type = MATCH_FRONT_ONLY;
+                               buff[i] = 0;
+                               break;
+                       }
+               }
+       }
+
+       return type;
+}
+
+static void filter_build_regex(struct filter_pred *pred)
+{
+       struct regex *r = &pred->regex;
+       char *search;
+       enum regex_type type = MATCH_FULL;
+       int not = 0;
+
+       if (pred->op == OP_GLOB) {
+               type = filter_parse_regex(r->pattern, r->len, &search, &not);
+               r->len = strlen(search);
+               memmove(r->pattern, search, r->len+1);
+       }
+
+       switch (type) {
+       case MATCH_FULL:
+               r->match = regex_match_full;
+               break;
+       case MATCH_FRONT_ONLY:
+               r->match = regex_match_front;
+               break;
+       case MATCH_MIDDLE_ONLY:
+               r->match = regex_match_middle;
+               break;
+       case MATCH_END_ONLY:
+               r->match = regex_match_end;
+               break;
+       }
+
+       pred->not ^= not;
+}
+
 /* return 1 if event matches, 0 otherwise (discard) */
-int filter_match_preds(struct ftrace_event_call *call, void *rec)
+int filter_match_preds(struct event_filter *filter, void *rec)
 {
-       struct event_filter *filter = call->filter;
        int match, top = 0, val1 = 0, val2 = 0;
        int stack[MAX_FILTER_PRED];
        struct filter_pred *pred;
@@ -293,7 +476,7 @@ void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
        struct event_filter *filter = call->filter;
 
        mutex_lock(&event_mutex);
-       if (filter->filter_string)
+       if (filter && filter->filter_string)
                trace_seq_printf(s, "%s\n", filter->filter_string);
        else
                trace_seq_printf(s, "none\n");
@@ -306,7 +489,7 @@ void print_subsystem_event_filter(struct event_subsystem *system,
        struct event_filter *filter = system->filter;
 
        mutex_lock(&event_mutex);
-       if (filter->filter_string)
+       if (filter && filter->filter_string)
                trace_seq_printf(s, "%s\n", filter->filter_string);
        else
                trace_seq_printf(s, "none\n");
@@ -317,8 +500,10 @@ static struct ftrace_event_field *
 find_event_field(struct ftrace_event_call *call, char *name)
 {
        struct ftrace_event_field *field;
+       struct list_head *head;
 
-       list_for_each_entry(field, &call->fields, link) {
+       head = trace_get_fields(call);
+       list_for_each_entry(field, head, link) {
                if (!strcmp(field->name, name))
                        return field;
        }
@@ -339,7 +524,7 @@ static void filter_clear_pred(struct filter_pred *pred)
 {
        kfree(pred->field_name);
        pred->field_name = NULL;
-       pred->str_len = 0;
+       pred->regex.len = 0;
 }
 
 static int filter_set_pred(struct filter_pred *dest,
@@ -362,18 +547,20 @@ static void filter_disable_preds(struct ftrace_event_call *call)
        struct event_filter *filter = call->filter;
        int i;
 
-       call->filter_active = 0;
+       call->flags &= ~TRACE_EVENT_FL_FILTERED;
        filter->n_preds = 0;
 
        for (i = 0; i < MAX_FILTER_PRED; i++)
                filter->preds[i]->fn = filter_pred_none;
 }
 
-void destroy_preds(struct ftrace_event_call *call)
+static void __free_preds(struct event_filter *filter)
 {
-       struct event_filter *filter = call->filter;
        int i;
 
+       if (!filter)
+               return;
+
        for (i = 0; i < MAX_FILTER_PRED; i++) {
                if (filter->preds[i])
                        filter_free_pred(filter->preds[i]);
@@ -381,20 +568,25 @@ void destroy_preds(struct ftrace_event_call *call)
        kfree(filter->preds);
        kfree(filter->filter_string);
        kfree(filter);
+}
+
+void destroy_preds(struct ftrace_event_call *call)
+{
+       __free_preds(call->filter);
        call->filter = NULL;
+       call->flags &= ~TRACE_EVENT_FL_FILTERED;
 }
 
-int init_preds(struct ftrace_event_call *call)
+static struct event_filter *__alloc_preds(void)
 {
        struct event_filter *filter;
        struct filter_pred *pred;
        int i;
 
-       filter = call->filter = kzalloc(sizeof(*filter), GFP_KERNEL);
-       if (!call->filter)
-               return -ENOMEM;
+       filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+       if (!filter)
+               return ERR_PTR(-ENOMEM);
 
-       call->filter_active = 0;
        filter->n_preds = 0;
 
        filter->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), GFP_KERNEL);
@@ -409,36 +601,68 @@ int init_preds(struct ftrace_event_call *call)
                filter->preds[i] = pred;
        }
 
-       return 0;
+       return filter;
 
 oom:
-       destroy_preds(call);
+       __free_preds(filter);
+       return ERR_PTR(-ENOMEM);
+}
+
+static int init_preds(struct ftrace_event_call *call)
+{
+       if (call->filter)
+               return 0;
 
-       return -ENOMEM;
+       call->flags &= ~TRACE_EVENT_FL_FILTERED;
+       call->filter = __alloc_preds();
+       if (IS_ERR(call->filter))
+               return PTR_ERR(call->filter);
+
+       return 0;
+}
+
+static int init_subsystem_preds(struct event_subsystem *system)
+{
+       struct ftrace_event_call *call;
+       int err;
+
+       list_for_each_entry(call, &ftrace_events, list) {
+               if (!call->class || !call->class->define_fields)
+                       continue;
+
+               if (strcmp(call->class->system, system->name) != 0)
+                       continue;
+
+               err = init_preds(call);
+               if (err)
+                       return err;
+       }
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(init_preds);
 
 static void filter_free_subsystem_preds(struct event_subsystem *system)
 {
        struct ftrace_event_call *call;
 
        list_for_each_entry(call, &ftrace_events, list) {
-               if (!call->define_fields)
+               if (!call->class || !call->class->define_fields)
                        continue;
 
-               if (!strcmp(call->system, system->name)) {
-                       filter_disable_preds(call);
-                       remove_filter_string(call->filter);
-               }
+               if (strcmp(call->class->system, system->name) != 0)
+                       continue;
+
+               filter_disable_preds(call);
+               remove_filter_string(call->filter);
        }
 }
 
 static int filter_add_pred_fn(struct filter_parse_state *ps,
                              struct ftrace_event_call *call,
+                             struct event_filter *filter,
                              struct filter_pred *pred,
                              filter_pred_fn_t fn)
 {
-       struct event_filter *filter = call->filter;
        int idx, err;
 
        if (filter->n_preds == MAX_FILTER_PRED) {
@@ -453,17 +677,11 @@ static int filter_add_pred_fn(struct filter_parse_state *ps,
                return err;
 
        filter->n_preds++;
-       call->filter_active = 1;
 
        return 0;
 }
 
-enum {
-       FILTER_STATIC_STRING = 1,
-       FILTER_DYN_STRING
-};
-
-static int is_string_field(const char *type)
+int filter_assign_type(const char *type)
 {
        if (strstr(type, "__data_loc") && strstr(type, "char"))
                return FILTER_DYN_STRING;
@@ -471,12 +689,22 @@ static int is_string_field(const char *type)
        if (strchr(type, '[') && strstr(type, "char"))
                return FILTER_STATIC_STRING;
 
-       return 0;
+       return FILTER_OTHER;
+}
+
+static bool is_string_field(struct ftrace_event_field *field)
+{
+       return field->filter_type == FILTER_DYN_STRING ||
+              field->filter_type == FILTER_STATIC_STRING ||
+              field->filter_type == FILTER_PTR_STRING;
 }
 
 static int is_legal_op(struct ftrace_event_field *field, int op)
 {
-       if (is_string_field(field->type) && (op != OP_EQ && op != OP_NE))
+       if (is_string_field(field) &&
+           (op != OP_EQ && op != OP_NE && op != OP_GLOB))
+               return 0;
+       if (!is_string_field(field) && op == OP_GLOB)
                return 0;
 
        return 1;
@@ -527,22 +755,25 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size,
 
 static int filter_add_pred(struct filter_parse_state *ps,
                           struct ftrace_event_call *call,
-                          struct filter_pred *pred)
+                          struct event_filter *filter,
+                          struct filter_pred *pred,
+                          bool dry_run)
 {
        struct ftrace_event_field *field;
        filter_pred_fn_t fn;
        unsigned long long val;
-       int string_type;
        int ret;
 
        pred->fn = filter_pred_none;
 
        if (pred->op == OP_AND) {
                pred->pop_n = 2;
-               return filter_add_pred_fn(ps, call, pred, filter_pred_and);
+               fn = filter_pred_and;
+               goto add_pred_fn;
        } else if (pred->op == OP_OR) {
                pred->pop_n = 2;
-               return filter_add_pred_fn(ps, call, pred, filter_pred_or);
+               fn = filter_pred_or;
+               goto add_pred_fn;
        }
 
        field = find_event_field(call, pred->field_name);
@@ -558,66 +789,42 @@ static int filter_add_pred(struct filter_parse_state *ps,
                return -EINVAL;
        }
 
-       string_type = is_string_field(field->type);
-       if (string_type) {
-               if (string_type == FILTER_STATIC_STRING)
+       if (is_string_field(field)) {
+               filter_build_regex(pred);
+
+               if (field->filter_type == FILTER_STATIC_STRING) {
                        fn = filter_pred_string;
-               else
+                       pred->regex.field_len = field->size;
+               } else if (field->filter_type == FILTER_DYN_STRING)
                        fn = filter_pred_strloc;
-               pred->str_len = field->size;
-               if (pred->op == OP_NE)
-                       pred->not = 1;
-               return filter_add_pred_fn(ps, call, pred, fn);
+               else
+                       fn = filter_pred_pchar;
        } else {
                if (field->is_signed)
-                       ret = strict_strtoll(pred->str_val, 0, &val);
+                       ret = strict_strtoll(pred->regex.pattern, 0, &val);
                else
-                       ret = strict_strtoull(pred->str_val, 0, &val);
+                       ret = strict_strtoull(pred->regex.pattern, 0, &val);
                if (ret) {
                        parse_error(ps, FILT_ERR_ILLEGAL_INTVAL, 0);
                        return -EINVAL;
                }
                pred->val = val;
-       }
 
-       fn = select_comparison_fn(pred->op, field->size, field->is_signed);
-       if (!fn) {
-               parse_error(ps, FILT_ERR_INVALID_OP, 0);
-               return -EINVAL;
+               fn = select_comparison_fn(pred->op, field->size,
+                                         field->is_signed);
+               if (!fn) {
+                       parse_error(ps, FILT_ERR_INVALID_OP, 0);
+                       return -EINVAL;
+               }
        }
 
        if (pred->op == OP_NE)
                pred->not = 1;
 
-       return filter_add_pred_fn(ps, call, pred, fn);
-}
-
-static int filter_add_subsystem_pred(struct filter_parse_state *ps,
-                                    struct event_subsystem *system,
-                                    struct filter_pred *pred,
-                                    char *filter_string)
-{
-       struct ftrace_event_call *call;
-       int err = 0;
-
-       list_for_each_entry(call, &ftrace_events, list) {
-
-               if (!call->define_fields)
-                       continue;
-
-               if (strcmp(call->system, system->name))
-                       continue;
-
-               err = filter_add_pred(ps, call, pred);
-               if (err) {
-                       filter_free_subsystem_preds(system);
-                       parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
-                       goto out;
-               }
-               replace_filter_string(call->filter, filter_string);
-       }
-out:
-       return err;
+add_pred_fn:
+       if (!dry_run)
+               return filter_add_pred_fn(ps, call, filter, pred, fn);
+       return 0;
 }
 
 static void parse_init(struct filter_parse_state *ps,
@@ -817,8 +1024,9 @@ static void postfix_clear(struct filter_parse_state *ps)
 
        while (!list_empty(&ps->postfix)) {
                elt = list_first_entry(&ps->postfix, struct postfix_elt, list);
-               kfree(elt->operand);
                list_del(&elt->list);
+               kfree(elt->operand);
+               kfree(elt);
        }
 }
 
@@ -928,8 +1136,8 @@ static struct filter_pred *create_pred(int op, char *operand1, char *operand2)
                return NULL;
        }
 
-       strcpy(pred->str_val, operand2);
-       pred->str_len = strlen(operand2);
+       strcpy(pred->regex.pattern, operand2);
+       pred->regex.len = strlen(pred->regex.pattern);
 
        pred->op = op;
 
@@ -973,15 +1181,17 @@ static int check_preds(struct filter_parse_state *ps)
        return 0;
 }
 
-static int replace_preds(struct event_subsystem *system,
-                        struct ftrace_event_call *call,
+static int replace_preds(struct ftrace_event_call *call,
+                        struct event_filter *filter,
                         struct filter_parse_state *ps,
-                        char *filter_string)
+                        char *filter_string,
+                        bool dry_run)
 {
        char *operand1 = NULL, *operand2 = NULL;
        struct filter_pred *pred;
        struct postfix_elt *elt;
        int err;
+       int n_preds = 0;
 
        err = check_preds(ps);
        if (err)
@@ -1000,19 +1210,14 @@ static int replace_preds(struct event_subsystem *system,
                        continue;
                }
 
+               if (n_preds++ == MAX_FILTER_PRED) {
+                       parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0);
+                       return -ENOSPC;
+               }
+
                if (elt->op == OP_AND || elt->op == OP_OR) {
                        pred = create_logical_pred(elt->op);
-                       if (call)
-                               err = filter_add_pred(ps, call, pred);
-                       else
-                               err = filter_add_subsystem_pred(ps, system,
-                                                       pred, filter_string);
-                       filter_free_pred(pred);
-                       if (err)
-                               return err;
-
-                       operand1 = operand2 = NULL;
-                       continue;
+                       goto add_pred;
                }
 
                if (!operand1 || !operand2) {
@@ -1021,11 +1226,10 @@ static int replace_preds(struct event_subsystem *system,
                }
 
                pred = create_pred(elt->op, operand1, operand2);
-               if (call)
-                       err = filter_add_pred(ps, call, pred);
-               else
-                       err = filter_add_subsystem_pred(ps, system, pred,
-                                                       filter_string);
+add_pred:
+               if (!pred)
+                       return -ENOMEM;
+               err = filter_add_pred(ps, call, filter, pred, dry_run);
                filter_free_pred(pred);
                if (err)
                        return err;
@@ -1036,19 +1240,62 @@ static int replace_preds(struct event_subsystem *system,
        return 0;
 }
 
-int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
+static int replace_system_preds(struct event_subsystem *system,
+                               struct filter_parse_state *ps,
+                               char *filter_string)
 {
+       struct ftrace_event_call *call;
+       bool fail = true;
        int err;
 
+       list_for_each_entry(call, &ftrace_events, list) {
+               struct event_filter *filter = call->filter;
+
+               if (!call->class || !call->class->define_fields)
+                       continue;
+
+               if (strcmp(call->class->system, system->name) != 0)
+                       continue;
+
+               /* try to see if the filter can be applied */
+               err = replace_preds(call, filter, ps, filter_string, true);
+               if (err)
+                       continue;
+
+               /* really apply the filter */
+               filter_disable_preds(call);
+               err = replace_preds(call, filter, ps, filter_string, false);
+               if (err)
+                       filter_disable_preds(call);
+               else {
+                       call->flags |= TRACE_EVENT_FL_FILTERED;
+                       replace_filter_string(filter, filter_string);
+               }
+               fail = false;
+       }
+
+       if (fail) {
+               parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
+{
+       int err;
        struct filter_parse_state *ps;
 
        mutex_lock(&event_mutex);
 
+       err = init_preds(call);
+       if (err)
+               goto out_unlock;
+
        if (!strcmp(strstrip(filter_string), "0")) {
                filter_disable_preds(call);
                remove_filter_string(call->filter);
-               mutex_unlock(&event_mutex);
-               return 0;
+               goto out_unlock;
        }
 
        err = -ENOMEM;
@@ -1066,10 +1313,11 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
                goto out;
        }
 
-       err = replace_preds(NULL, call, ps, filter_string);
+       err = replace_preds(call, call->filter, ps, filter_string, false);
        if (err)
                append_filter_err(ps, call->filter);
-
+       else
+               call->flags |= TRACE_EVENT_FL_FILTERED;
 out:
        filter_opstack_clear(ps);
        postfix_clear(ps);
@@ -1084,16 +1332,18 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
                                 char *filter_string)
 {
        int err;
-
        struct filter_parse_state *ps;
 
        mutex_lock(&event_mutex);
 
+       err = init_subsystem_preds(system);
+       if (err)
+               goto out_unlock;
+
        if (!strcmp(strstrip(filter_string), "0")) {
                filter_free_subsystem_preds(system);
                remove_filter_string(system->filter);
-               mutex_unlock(&event_mutex);
-               return 0;
+               goto out_unlock;
        }
 
        err = -ENOMEM;
@@ -1101,7 +1351,6 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
        if (!ps)
                goto out_unlock;
 
-       filter_free_subsystem_preds(system);
        replace_filter_string(system->filter, filter_string);
 
        parse_init(ps, filter_ops, filter_string);
@@ -1111,7 +1360,7 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
                goto out;
        }
 
-       err = replace_preds(system, NULL, ps, filter_string);
+       err = replace_system_preds(system, ps, filter_string);
        if (err)
                append_filter_err(ps, system->filter);
 
@@ -1125,3 +1374,73 @@ out_unlock:
        return err;
 }
 
+#ifdef CONFIG_PERF_EVENTS
+
+void ftrace_profile_free_filter(struct perf_event *event)
+{
+       struct event_filter *filter = event->filter;
+
+       event->filter = NULL;
+       __free_preds(filter);
+}
+
+int ftrace_profile_set_filter(struct perf_event *event, int event_id,
+                             char *filter_str)
+{
+       int err;
+       struct event_filter *filter;
+       struct filter_parse_state *ps;
+       struct ftrace_event_call *call = NULL;
+
+       mutex_lock(&event_mutex);
+
+       list_for_each_entry(call, &ftrace_events, list) {
+               if (call->event.type == event_id)
+                       break;
+       }
+
+       err = -EINVAL;
+       if (&call->list == &ftrace_events)
+               goto out_unlock;
+
+       err = -EEXIST;
+       if (event->filter)
+               goto out_unlock;
+
+       filter = __alloc_preds();
+       if (IS_ERR(filter)) {
+               err = PTR_ERR(filter);
+               goto out_unlock;
+       }
+
+       err = -ENOMEM;
+       ps = kzalloc(sizeof(*ps), GFP_KERNEL);
+       if (!ps)
+               goto free_preds;
+
+       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)
+               event->filter = filter;
+
+free_ps:
+       filter_opstack_clear(ps);
+       postfix_clear(ps);
+       kfree(ps);
+
+free_preds:
+       if (err)
+               __free_preds(filter);
+
+out_unlock:
+       mutex_unlock(&event_mutex);
+
+       return err;
+}
+
+#endif /* CONFIG_PERF_EVENTS */
+