}
 }
 
+struct filter_list {
+       struct list_head        list;
+       struct event_filter     *filter;
+};
+
+struct filter_head {
+       struct list_head        list;
+       struct rcu_head         rcu;
+};
+
+
+static void free_filter_list(struct rcu_head *rhp)
+{
+       struct filter_head *filter_list = container_of(rhp, struct filter_head, rcu);
+       struct filter_list *filter_item, *tmp;
+
+       list_for_each_entry_safe(filter_item, tmp, &filter_list->list, list) {
+               __free_filter(filter_item->filter);
+               list_del(&filter_item->list);
+               kfree(filter_item);
+       }
+       kfree(filter_list);
+}
+
+static void free_filter_list_tasks(struct rcu_head *rhp)
+{
+       call_rcu(rhp, free_filter_list);
+}
+
+/*
+ * The tracepoint_synchronize_unregister() is a double rcu call.
+ * It calls synchronize_rcu_tasks_trace() followed by synchronize_rcu().
+ * Instead of waiting for it, simply call these via the call_rcu*()
+ * variants.
+ */
+static void delay_free_filter(struct filter_head *head)
+{
+       call_rcu_tasks_trace(&head->rcu, free_filter_list_tasks);
+}
+
+static void try_delay_free_filter(struct event_filter *filter)
+{
+       struct filter_head *head;
+       struct filter_list *item;
+
+       head = kmalloc(sizeof(*head), GFP_KERNEL);
+       if (!head)
+               goto free_now;
+
+       INIT_LIST_HEAD(&head->list);
+
+       item = kmalloc(sizeof(*item), GFP_KERNEL);
+       if (!item) {
+               kfree(head);
+               goto free_now;
+       }
+
+       item->filter = filter;
+       list_add_tail(&item->list, &head->list);
+       delay_free_filter(head);
+       return;
+
+ free_now:
+       /* Make sure the filter is not being used */
+       tracepoint_synchronize_unregister();
+       __free_filter(filter);
+}
+
 static inline void __free_subsystem_filter(struct trace_event_file *file)
 {
        __free_filter(file->filter);
        file->filter = NULL;
 }
 
+static inline void event_set_filter(struct trace_event_file *file,
+                                   struct event_filter *filter)
+{
+       rcu_assign_pointer(file->filter, filter);
+}
+
+static inline void event_clear_filter(struct trace_event_file *file)
+{
+       RCU_INIT_POINTER(file->filter, NULL);
+}
+
 static void filter_free_subsystem_filters(struct trace_subsystem_dir *dir,
-                                         struct trace_array *tr)
+                                         struct trace_array *tr,
+                                         struct event_filter *filter)
 {
        struct trace_event_file *file;
+       struct filter_head *head;
+       struct filter_list *item;
+
+       head = kmalloc(sizeof(*head), GFP_KERNEL);
+       if (!head)
+               goto free_now;
+
+       INIT_LIST_HEAD(&head->list);
+
+       item = kmalloc(sizeof(*item), GFP_KERNEL);
+       if (!item) {
+               kfree(head);
+               goto free_now;
+       }
+
+       item->filter = filter;
+       list_add_tail(&item->list, &head->list);
 
        list_for_each_entry(file, &tr->events, list) {
                if (file->system != dir)
                        continue;
-               __free_subsystem_filter(file);
+               item = kmalloc(sizeof(*item), GFP_KERNEL);
+               if (!item)
+                       goto free_now;
+               item->filter = event_filter(file);
+               list_add_tail(&item->list, &head->list);
+               event_clear_filter(file);
        }
+
+       delay_free_filter(head);
+       return;
+ free_now:
+       tracepoint_synchronize_unregister();
+
+       if (head)
+               free_filter_list(&head->rcu);
+
+       list_for_each_entry(file, &tr->events, list) {
+               if (file->system != dir || !file->filter)
+                       continue;
+               __free_filter(file->filter);
+       }
+       __free_filter(filter);
 }
 
 int filter_assign_type(const char *type)
                trace_buffered_event_enable();
 }
 
-static inline void event_set_filter(struct trace_event_file *file,
-                                   struct event_filter *filter)
-{
-       rcu_assign_pointer(file->filter, filter);
-}
-
-static inline void event_clear_filter(struct trace_event_file *file)
-{
-       RCU_INIT_POINTER(file->filter, NULL);
-}
-
-struct filter_list {
-       struct list_head        list;
-       struct event_filter     *filter;
-};
-
 static int process_system_preds(struct trace_subsystem_dir *dir,
                                struct trace_array *tr,
                                struct filter_parse_error *pe,
        struct trace_event_file *file;
        struct filter_list *filter_item;
        struct event_filter *filter = NULL;
-       struct filter_list *tmp;
-       LIST_HEAD(filter_list);
+       struct filter_head *filter_list;
        bool fail = true;
        int err;
 
+       filter_list = kmalloc(sizeof(*filter_list), GFP_KERNEL);
+       if (!filter_list)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&filter_list->list);
+
        list_for_each_entry(file, &tr->events, list) {
 
                if (file->system != dir)
                if (!filter_item)
                        goto fail_mem;
 
-               list_add_tail(&filter_item->list, &filter_list);
+               list_add_tail(&filter_item->list, &filter_list->list);
                /*
                 * Regardless of if this returned an error, we still
                 * replace the filter for the call.
         * Do a synchronize_rcu() and to ensure all calls are
         * done with them before we free them.
         */
-       tracepoint_synchronize_unregister();
-       list_for_each_entry_safe(filter_item, tmp, &filter_list, list) {
-               __free_filter(filter_item->filter);
-               list_del(&filter_item->list);
-               kfree(filter_item);
-       }
+       delay_free_filter(filter_list);
        return 0;
  fail:
        /* No call succeeded */
-       list_for_each_entry_safe(filter_item, tmp, &filter_list, list) {
-               list_del(&filter_item->list);
-               kfree(filter_item);
-       }
+       free_filter_list(&filter_list->rcu);
        parse_error(pe, FILT_ERR_BAD_SUBSYS_FILTER, 0);
        return -EINVAL;
  fail_mem:
        __free_filter(filter);
+
        /* If any call succeeded, we still need to sync */
        if (!fail)
-               tracepoint_synchronize_unregister();
-       list_for_each_entry_safe(filter_item, tmp, &filter_list, list) {
-               __free_filter(filter_item->filter);
-               list_del(&filter_item->list);
-               kfree(filter_item);
-       }
+               delay_free_filter(filter_list);
+       else
+               free_filter_list(&filter_list->rcu);
+
        return -ENOMEM;
 }
 
 
                event_clear_filter(file);
 
-               /* Make sure the filter is not being used */
-               tracepoint_synchronize_unregister();
-               __free_filter(filter);
+               try_delay_free_filter(filter);
 
                return 0;
        }
 
                event_set_filter(file, filter);
 
-               if (tmp) {
-                       /* Make sure the call is done with the filter */
-                       tracepoint_synchronize_unregister();
-                       __free_filter(tmp);
-               }
+               if (tmp)
+                       try_delay_free_filter(tmp);
        }
 
        return err;
                filter = system->filter;
                system->filter = NULL;
                /* Ensure all filters are no longer used */
-               tracepoint_synchronize_unregister();
-               filter_free_subsystem_filters(dir, tr);
-               __free_filter(filter);
+               filter_free_subsystem_filters(dir, tr, filter);
                return 0;
        }