Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[firefly-linux-kernel-4.4.55.git] / kernel / trace / trace_events_filter.c
index 97daa8cf958df8e73c9fa8090f89cbaf4e63893e..2468f56dc5db2d1403df5692f1d9e0da912d6a1c 100644 (file)
@@ -637,10 +637,18 @@ static void append_filter_err(struct filter_parse_state *ps,
        free_page((unsigned long) buf);
 }
 
+static inline struct event_filter *event_filter(struct ftrace_event_file *file)
+{
+       if (file->event_call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               return file->event_call->filter;
+       else
+               return file->filter;
+}
+
 /* caller must hold event_mutex */
-void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s)
+void print_event_filter(struct ftrace_event_file *file, struct trace_seq *s)
 {
-       struct event_filter *filter = call->filter;
+       struct event_filter *filter = event_filter(file);
 
        if (filter && filter->filter_string)
                trace_seq_printf(s, "%s\n", filter->filter_string);
@@ -766,11 +774,21 @@ static void __free_preds(struct event_filter *filter)
        filter->n_preds = 0;
 }
 
-static void filter_disable(struct ftrace_event_call *call)
+static void call_filter_disable(struct ftrace_event_call *call)
 {
        call->flags &= ~TRACE_EVENT_FL_FILTERED;
 }
 
+static void filter_disable(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call_filter_disable(call);
+       else
+               file->flags &= ~FTRACE_EVENT_FL_FILTERED;
+}
+
 static void __free_filter(struct event_filter *filter)
 {
        if (!filter)
@@ -781,16 +799,30 @@ static void __free_filter(struct event_filter *filter)
        kfree(filter);
 }
 
+void destroy_call_preds(struct ftrace_event_call *call)
+{
+       __free_filter(call->filter);
+       call->filter = NULL;
+}
+
+static void destroy_file_preds(struct ftrace_event_file *file)
+{
+       __free_filter(file->filter);
+       file->filter = NULL;
+}
+
 /*
- * Called when destroying the ftrace_event_call.
- * The call is being freed, so we do not need to worry about
- * the call being currently used. This is for module code removing
+ * Called when destroying the ftrace_event_file.
+ * The file is being freed, so we do not need to worry about
+ * the file being currently used. This is for module code removing
  * the tracepoints from within it.
  */
-void destroy_preds(struct ftrace_event_call *call)
+void destroy_preds(struct ftrace_event_file *file)
 {
-       __free_filter(call->filter);
-       call->filter = NULL;
+       if (file->event_call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               destroy_call_preds(file->event_call);
+       else
+               destroy_file_preds(file);
 }
 
 static struct event_filter *__alloc_filter(void)
@@ -825,28 +857,56 @@ static int __alloc_preds(struct event_filter *filter, int n_preds)
        return 0;
 }
 
-static void filter_free_subsystem_preds(struct event_subsystem *system)
+static inline void __remove_filter(struct ftrace_event_file *file)
 {
+       struct ftrace_event_call *call = file->event_call;
+
+       filter_disable(file);
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               remove_filter_string(call->filter);
+       else
+               remove_filter_string(file->filter);
+}
+
+static void filter_free_subsystem_preds(struct event_subsystem *system,
+                                       struct trace_array *tr)
+{
+       struct ftrace_event_file *file;
        struct ftrace_event_call *call;
 
-       list_for_each_entry(call, &ftrace_events, list) {
+       list_for_each_entry(file, &tr->events, list) {
+               call = file->event_call;
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
-               filter_disable(call);
-               remove_filter_string(call->filter);
+               __remove_filter(file);
        }
 }
 
-static void filter_free_subsystem_filters(struct event_subsystem *system)
+static inline void __free_subsystem_filter(struct ftrace_event_file *file)
 {
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER) {
+               __free_filter(call->filter);
+               call->filter = NULL;
+       } else {
+               __free_filter(file->filter);
+               file->filter = NULL;
+       }
+}
+
+static void filter_free_subsystem_filters(struct event_subsystem *system,
+                                         struct trace_array *tr)
+{
+       struct ftrace_event_file *file;
        struct ftrace_event_call *call;
 
-       list_for_each_entry(call, &ftrace_events, list) {
+       list_for_each_entry(file, &tr->events, list) {
+               call = file->event_call;
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
-               __free_filter(call->filter);
-               call->filter = NULL;
+               __free_subsystem_filter(file);
        }
 }
 
@@ -1617,15 +1677,85 @@ fail:
        return err;
 }
 
+static inline void event_set_filtered_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call->flags |= TRACE_EVENT_FL_FILTERED;
+       else
+               file->flags |= FTRACE_EVENT_FL_FILTERED;
+}
+
+static inline void event_set_filter(struct ftrace_event_file *file,
+                                   struct event_filter *filter)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               rcu_assign_pointer(call->filter, filter);
+       else
+               rcu_assign_pointer(file->filter, filter);
+}
+
+static inline void event_clear_filter(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               RCU_INIT_POINTER(call->filter, NULL);
+       else
+               RCU_INIT_POINTER(file->filter, NULL);
+}
+
+static inline void
+event_set_no_set_filter_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call->flags |= TRACE_EVENT_FL_NO_SET_FILTER;
+       else
+               file->flags |= FTRACE_EVENT_FL_NO_SET_FILTER;
+}
+
+static inline void
+event_clear_no_set_filter_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (call->flags & TRACE_EVENT_FL_USE_CALL_FILTER)
+               call->flags &= ~TRACE_EVENT_FL_NO_SET_FILTER;
+       else
+               file->flags &= ~FTRACE_EVENT_FL_NO_SET_FILTER;
+}
+
+static inline bool
+event_no_set_filter_flag(struct ftrace_event_file *file)
+{
+       struct ftrace_event_call *call = file->event_call;
+
+       if (file->flags & FTRACE_EVENT_FL_NO_SET_FILTER)
+               return true;
+
+       if ((call->flags & TRACE_EVENT_FL_USE_CALL_FILTER) &&
+           (call->flags & TRACE_EVENT_FL_NO_SET_FILTER))
+               return true;
+
+       return false;
+}
+
 struct filter_list {
        struct list_head        list;
        struct event_filter     *filter;
 };
 
 static int replace_system_preds(struct event_subsystem *system,
+                               struct trace_array *tr,
                                struct filter_parse_state *ps,
                                char *filter_string)
 {
+       struct ftrace_event_file *file;
        struct ftrace_event_call *call;
        struct filter_list *filter_item;
        struct filter_list *tmp;
@@ -1633,8 +1763,8 @@ static int replace_system_preds(struct event_subsystem *system,
        bool fail = true;
        int err;
 
-       list_for_each_entry(call, &ftrace_events, list) {
-
+       list_for_each_entry(file, &tr->events, list) {
+               call = file->event_call;
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
@@ -1644,18 +1774,20 @@ static int replace_system_preds(struct event_subsystem *system,
                 */
                err = replace_preds(call, NULL, ps, filter_string, true);
                if (err)
-                       call->flags |= TRACE_EVENT_FL_NO_SET_FILTER;
+                       event_set_no_set_filter_flag(file);
                else
-                       call->flags &= ~TRACE_EVENT_FL_NO_SET_FILTER;
+                       event_clear_no_set_filter_flag(file);
        }
 
-       list_for_each_entry(call, &ftrace_events, list) {
+       list_for_each_entry(file, &tr->events, list) {
                struct event_filter *filter;
 
+               call = file->event_call;
+
                if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
-               if (call->flags & TRACE_EVENT_FL_NO_SET_FILTER)
+               if (event_no_set_filter_flag(file))
                        continue;
 
                filter_item = kzalloc(sizeof(*filter_item), GFP_KERNEL);
@@ -1676,17 +1808,17 @@ static int replace_system_preds(struct event_subsystem *system,
 
                err = replace_preds(call, filter, ps, filter_string, false);
                if (err) {
-                       filter_disable(call);
+                       filter_disable(file);
                        parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0);
                        append_filter_err(ps, filter);
                } else
-                       call->flags |= TRACE_EVENT_FL_FILTERED;
+                       event_set_filtered_flag(file);
                /*
                 * Regardless of if this returned an error, we still
                 * replace the filter for the call.
                 */
-               filter = call->filter;
-               rcu_assign_pointer(call->filter, filter_item->filter);
+               filter = event_filter(file);
+               event_set_filter(file, filter_item->filter);
                filter_item->filter = filter;
 
                fail = false;
@@ -1816,6 +1948,7 @@ static int create_filter(struct ftrace_event_call *call,
  * and always remembers @filter_str.
  */
 static int create_system_filter(struct event_subsystem *system,
+                               struct trace_array *tr,
                                char *filter_str, struct event_filter **filterp)
 {
        struct event_filter *filter = NULL;
@@ -1824,7 +1957,7 @@ static int create_system_filter(struct event_subsystem *system,
 
        err = create_filter_start(filter_str, true, &ps, &filter);
        if (!err) {
-               err = replace_system_preds(system, ps, filter_str);
+               err = replace_system_preds(system, tr, ps, filter_str);
                if (!err) {
                        /* System filters just show a default message */
                        kfree(filter->filter_string);
@@ -1840,20 +1973,25 @@ static int create_system_filter(struct event_subsystem *system,
 }
 
 /* caller must hold event_mutex */
-int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
+int apply_event_filter(struct ftrace_event_file *file, char *filter_string)
 {
+       struct ftrace_event_call *call = file->event_call;
        struct event_filter *filter;
        int err;
 
        if (!strcmp(strstrip(filter_string), "0")) {
-               filter_disable(call);
-               filter = call->filter;
+               filter_disable(file);
+               filter = event_filter(file);
+
                if (!filter)
                        return 0;
-               RCU_INIT_POINTER(call->filter, NULL);
+
+               event_clear_filter(file);
+
                /* Make sure the filter is not being used */
                synchronize_sched();
                __free_filter(filter);
+
                return 0;
        }
 
@@ -1866,14 +2004,15 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
         * string
         */
        if (filter) {
-               struct event_filter *tmp = call->filter;
+               struct event_filter *tmp;
 
+               tmp = event_filter(file);
                if (!err)
-                       call->flags |= TRACE_EVENT_FL_FILTERED;
+                       event_set_filtered_flag(file);
                else
-                       filter_disable(call);
+                       filter_disable(file);
 
-               rcu_assign_pointer(call->filter, filter);
+               event_set_filter(file, filter);
 
                if (tmp) {
                        /* Make sure the call is done with the filter */
@@ -1889,6 +2028,7 @@ int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir,
                                 char *filter_string)
 {
        struct event_subsystem *system = dir->subsystem;
+       struct trace_array *tr = dir->tr;
        struct event_filter *filter;
        int err = 0;
 
@@ -1901,18 +2041,18 @@ int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir,
        }
 
        if (!strcmp(strstrip(filter_string), "0")) {
-               filter_free_subsystem_preds(system);
+               filter_free_subsystem_preds(system, tr);
                remove_filter_string(system->filter);
                filter = system->filter;
                system->filter = NULL;
                /* Ensure all filters are no longer used */
                synchronize_sched();
-               filter_free_subsystem_filters(system);
+               filter_free_subsystem_filters(system, tr);
                __free_filter(filter);
                goto out_unlock;
        }
 
-       err = create_system_filter(system, filter_string, &filter);
+       err = create_system_filter(system, tr, filter_string, &filter);
        if (filter) {
                /*
                 * No event actually uses the system filter