ring-buffer: Select IRQ_WORK
[platform/adaptation/renesas_rcar/renesas_kernel.git] / kernel / trace / trace_events.c
index 4399552..53582e9 100644 (file)
@@ -34,7 +34,12 @@ char event_storage[EVENT_STORAGE_SIZE];
 EXPORT_SYMBOL_GPL(event_storage);
 
 LIST_HEAD(ftrace_events);
-LIST_HEAD(ftrace_common_fields);
+static LIST_HEAD(ftrace_common_fields);
+
+#define GFP_TRACE (GFP_KERNEL | __GFP_ZERO)
+
+static struct kmem_cache *field_cachep;
+static struct kmem_cache *file_cachep;
 
 /* Double loops, do not use break, only goto's work */
 #define do_for_each_event_file(tr, file)                       \
@@ -49,7 +54,7 @@ LIST_HEAD(ftrace_common_fields);
 #define while_for_each_event_file()            \
        }
 
-struct list_head *
+static struct list_head *
 trace_get_fields(struct ftrace_event_call *event_call)
 {
        if (!event_call->class->get_fields)
@@ -57,23 +62,45 @@ trace_get_fields(struct ftrace_event_call *event_call)
        return event_call->class->get_fields(event_call);
 }
 
+static struct ftrace_event_field *
+__find_event_field(struct list_head *head, char *name)
+{
+       struct ftrace_event_field *field;
+
+       list_for_each_entry(field, head, link) {
+               if (!strcmp(field->name, name))
+                       return field;
+       }
+
+       return NULL;
+}
+
+struct ftrace_event_field *
+trace_find_event_field(struct ftrace_event_call *call, char *name)
+{
+       struct ftrace_event_field *field;
+       struct list_head *head;
+
+       field = __find_event_field(&ftrace_common_fields, name);
+       if (field)
+               return field;
+
+       head = trace_get_fields(call);
+       return __find_event_field(head, name);
+}
+
 static int __trace_define_field(struct list_head *head, const char *type,
                                const char *name, int offset, int size,
                                int is_signed, int filter_type)
 {
        struct ftrace_event_field *field;
 
-       field = kzalloc(sizeof(*field), GFP_KERNEL);
+       field = kmem_cache_alloc(field_cachep, GFP_TRACE);
        if (!field)
                goto err;
 
-       field->name = kstrdup(name, GFP_KERNEL);
-       if (!field->name)
-               goto err;
-
-       field->type = kstrdup(type, GFP_KERNEL);
-       if (!field->type)
-               goto err;
+       field->name = name;
+       field->type = type;
 
        if (filter_type == FILTER_OTHER)
                field->filter_type = filter_assign_type(type);
@@ -89,9 +116,7 @@ static int __trace_define_field(struct list_head *head, const char *type,
        return 0;
 
 err:
-       if (field)
-               kfree(field->name);
-       kfree(field);
+       kmem_cache_free(field_cachep, field);
 
        return -ENOMEM;
 }
@@ -133,7 +158,7 @@ static int trace_define_common_fields(void)
        return ret;
 }
 
-void trace_destroy_fields(struct ftrace_event_call *call)
+static void trace_destroy_fields(struct ftrace_event_call *call)
 {
        struct ftrace_event_field *field, *next;
        struct list_head *head;
@@ -141,9 +166,7 @@ void trace_destroy_fields(struct ftrace_event_call *call)
        head = trace_get_fields(call);
        list_for_each_entry_safe(field, next, head, link) {
                list_del(&field->link);
-               kfree(field->type);
-               kfree(field->name);
-               kfree(field);
+               kmem_cache_free(field_cachep, field);
        }
 }
 
@@ -209,37 +232,77 @@ void trace_event_enable_cmd_record(bool enable)
 
                if (enable) {
                        tracing_start_cmdline_record();
-                       file->flags |= FTRACE_EVENT_FL_RECORDED_CMD;
+                       set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);
                } else {
                        tracing_stop_cmdline_record();
-                       file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD;
+                       clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);
                }
        } while_for_each_event_file();
        mutex_unlock(&event_mutex);
 }
 
-static int ftrace_event_enable_disable(struct ftrace_event_file *file,
-                                      int enable)
+static int __ftrace_event_enable_disable(struct ftrace_event_file *file,
+                                        int enable, int soft_disable)
 {
        struct ftrace_event_call *call = file->event_call;
        int ret = 0;
+       int disable;
 
        switch (enable) {
        case 0:
-               if (file->flags & FTRACE_EVENT_FL_ENABLED) {
-                       file->flags &= ~FTRACE_EVENT_FL_ENABLED;
+               /*
+                * When soft_disable is set and enable is cleared, we want
+                * to clear the SOFT_DISABLED flag but leave the event in the
+                * state that it was. That is, if the event was enabled and
+                * SOFT_DISABLED isn't set, then do nothing. But if SOFT_DISABLED
+                * is set we do not want the event to be enabled before we
+                * clear the bit.
+                *
+                * When soft_disable is not set but the SOFT_MODE flag is,
+                * we do nothing. Do not disable the tracepoint, otherwise
+                * "soft enable"s (clearing the SOFT_DISABLED bit) wont work.
+                */
+               if (soft_disable) {
+                       disable = file->flags & FTRACE_EVENT_FL_SOFT_DISABLED;
+                       clear_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags);
+               } else
+                       disable = !(file->flags & FTRACE_EVENT_FL_SOFT_MODE);
+
+               if (disable && (file->flags & FTRACE_EVENT_FL_ENABLED)) {
+                       clear_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags);
                        if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) {
                                tracing_stop_cmdline_record();
-                               file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD;
+                               clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);
                        }
                        call->class->reg(call, TRACE_REG_UNREGISTER, file);
                }
+               /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT */
+               if (file->flags & FTRACE_EVENT_FL_SOFT_MODE)
+                       set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);
                break;
        case 1:
+               /*
+                * When soft_disable is set and enable is set, we want to
+                * register the tracepoint for the event, but leave the event
+                * as is. That means, if the event was already enabled, we do
+                * nothing (but set SOFT_MODE). If the event is disabled, we
+                * set SOFT_DISABLED before enabling the event tracepoint, so
+                * it still seems to be disabled.
+                */
+               if (!soft_disable)
+                       clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);
+               else
+                       set_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags);
+
                if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) {
+
+                       /* Keep the event disabled, when going to SOFT_MODE. */
+                       if (soft_disable)
+                               set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags);
+
                        if (trace_flags & TRACE_ITER_RECORD_CMD) {
                                tracing_start_cmdline_record();
-                               file->flags |= FTRACE_EVENT_FL_RECORDED_CMD;
+                               set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags);
                        }
                        ret = call->class->reg(call, TRACE_REG_REGISTER, file);
                        if (ret) {
@@ -248,7 +311,10 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file,
                                        "%s\n", call->name);
                                break;
                        }
-                       file->flags |= FTRACE_EVENT_FL_ENABLED;
+                       set_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags);
+
+                       /* WAS_ENABLED gets set but never cleared. */
+                       call->flags |= TRACE_EVENT_FL_WAS_ENABLED;
                }
                break;
        }
@@ -256,6 +322,12 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file,
        return ret;
 }
 
+static int ftrace_event_enable_disable(struct ftrace_event_file *file,
+                                      int enable)
+{
+       return __ftrace_event_enable_disable(file, enable, 0);
+}
+
 static void ftrace_clear_events(struct trace_array *tr)
 {
        struct ftrace_event_file *file;
@@ -281,7 +353,6 @@ static void __put_system(struct event_subsystem *system)
                kfree(filter->filter_string);
                kfree(filter);
        }
-       kfree(system->name);
        kfree(system);
 }
 
@@ -549,12 +620,15 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
        struct ftrace_event_file *file = filp->private_data;
        char *buf;
 
-       if (file->flags & FTRACE_EVENT_FL_ENABLED)
-               buf = "1\n";
-       else
+       if (file->flags & FTRACE_EVENT_FL_ENABLED) {
+               if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED)
+                       buf = "0*\n";
+               else
+                       buf = "1\n";
+       } else
                buf = "0\n";
 
-       return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2);
+       return simple_read_from_buffer(ubuf, cnt, ppos, buf, strlen(buf));
 }
 
 static ssize_t
@@ -1197,10 +1271,7 @@ create_new_subsystem(const char *name)
                return NULL;
 
        system->ref_count = 1;
-       system->name = kstrdup(name, GFP_KERNEL);
-
-       if (!system->name)
-               goto out_free;
+       system->name = name;
 
        system->filter = NULL;
 
@@ -1213,7 +1284,6 @@ create_new_subsystem(const char *name)
        return system;
 
  out_free:
-       kfree(system->name);
        kfree(system);
        return NULL;
 }
@@ -1383,7 +1453,7 @@ static void remove_event_from_tracers(struct ftrace_event_call *call)
                list_del(&file->list);
                debugfs_remove_recursive(file->dir);
                remove_subsystem(file->system);
-               kfree(file);
+               kmem_cache_free(file_cachep, file);
 
                /*
                 * The do_for_each_event_file_safe() is
@@ -1462,7 +1532,7 @@ __trace_add_new_event(struct ftrace_event_call *call,
 {
        struct ftrace_event_file *file;
 
-       file = kzalloc(sizeof(*file), GFP_KERNEL);
+       file = kmem_cache_alloc(file_cachep, GFP_TRACE);
        if (!file)
                return -ENOMEM;
 
@@ -1473,6 +1543,28 @@ __trace_add_new_event(struct ftrace_event_call *call,
        return event_create_dir(tr->event_dir, file, id, enable, filter, format);
 }
 
+/*
+ * Just create a decriptor for early init. A descriptor is required
+ * for enabling events at boot. We want to enable events before
+ * the filesystem is initialized.
+ */
+static __init int
+__trace_early_add_new_event(struct ftrace_event_call *call,
+                           struct trace_array *tr)
+{
+       struct ftrace_event_file *file;
+
+       file = kmem_cache_alloc(file_cachep, GFP_TRACE);
+       if (!file)
+               return -ENOMEM;
+
+       file->event_call = call;
+       file->tr = tr;
+       list_add(&file->list, &tr->events);
+
+       return 0;
+}
+
 struct ftrace_module_file_ops;
 static void __add_event_to_tracers(struct ftrace_event_call *call,
                                   struct ftrace_module_file_ops *file_ops);
@@ -1492,7 +1584,7 @@ int trace_add_event_call(struct ftrace_event_call *call)
 }
 
 /*
- * Must be called under locking both of event_mutex and trace_event_mutex.
+ * Must be called under locking both of event_mutex and trace_event_sem.
  */
 static void __trace_remove_event_call(struct ftrace_event_call *call)
 {
@@ -1505,9 +1597,9 @@ static void __trace_remove_event_call(struct ftrace_event_call *call)
 void trace_remove_event_call(struct ftrace_event_call *call)
 {
        mutex_lock(&event_mutex);
-       down_write(&trace_event_mutex);
+       down_write(&trace_event_sem);
        __trace_remove_event_call(call);
-       up_write(&trace_event_mutex);
+       up_write(&trace_event_sem);
        mutex_unlock(&event_mutex);
 }
 
@@ -1533,9 +1625,18 @@ struct ftrace_module_file_ops {
        struct file_operations          filter;
 };
 
-static struct ftrace_module_file_ops *find_ftrace_file_ops(struct module *mod)
+static struct ftrace_module_file_ops *
+find_ftrace_file_ops(struct ftrace_module_file_ops *file_ops, struct module *mod)
 {
-       struct ftrace_module_file_ops *file_ops;
+       /*
+        * As event_calls are added in groups by module,
+        * when we find one file_ops, we don't need to search for
+        * each call in that module, as the rest should be the
+        * same. Only search for a new one if the last one did
+        * not match.
+        */
+       if (file_ops && mod == file_ops->mod)
+               return file_ops;
 
        list_for_each_entry(file_ops, &ftrace_module_file_list, list) {
                if (file_ops->mod == mod)
@@ -1604,12 +1705,13 @@ static void trace_module_remove_events(struct module *mod)
 {
        struct ftrace_module_file_ops *file_ops;
        struct ftrace_event_call *call, *p;
-       bool found = false;
+       bool clear_trace = false;
 
-       down_write(&trace_event_mutex);
+       down_write(&trace_event_sem);
        list_for_each_entry_safe(call, p, &ftrace_events, list) {
                if (call->mod == mod) {
-                       found = true;
+                       if (call->flags & TRACE_EVENT_FL_WAS_ENABLED)
+                               clear_trace = true;
                        __trace_remove_event_call(call);
                }
        }
@@ -1623,14 +1725,18 @@ static void trace_module_remove_events(struct module *mod)
                list_del(&file_ops->list);
                kfree(file_ops);
        }
+       up_write(&trace_event_sem);
 
        /*
         * It is safest to reset the ring buffer if the module being unloaded
-        * registered any events.
+        * registered any events that were used. The only worry is if
+        * a new module gets loaded, and takes on the same id as the events
+        * of this module. When printing out the buffer, traced events left
+        * over from this module may be passed to the new module events and
+        * unexpected results may occur.
         */
-       if (found)
-               tracing_reset_current_online_cpus();
-       up_write(&trace_event_mutex);
+       if (clear_trace)
+               tracing_reset_all_online_cpus();
 }
 
 static int trace_module_notify(struct notifier_block *self,
@@ -1651,16 +1757,35 @@ static int trace_module_notify(struct notifier_block *self,
 
        return 0;
 }
+
+static int
+__trace_add_new_mod_event(struct ftrace_event_call *call,
+                         struct trace_array *tr,
+                         struct ftrace_module_file_ops *file_ops)
+{
+       return __trace_add_new_event(call, tr,
+                                    &file_ops->id, &file_ops->enable,
+                                    &file_ops->filter, &file_ops->format);
+}
+
 #else
-static struct ftrace_module_file_ops *find_ftrace_file_ops(struct module *mod)
+static inline struct ftrace_module_file_ops *
+find_ftrace_file_ops(struct ftrace_module_file_ops *file_ops, struct module *mod)
 {
        return NULL;
 }
-static int trace_module_notify(struct notifier_block *self,
-                              unsigned long val, void *data)
+static inline int trace_module_notify(struct notifier_block *self,
+                                     unsigned long val, void *data)
 {
        return 0;
 }
+static inline int
+__trace_add_new_mod_event(struct ftrace_event_call *call,
+                         struct trace_array *tr,
+                         struct ftrace_module_file_ops *file_ops)
+{
+       return -ENODEV;
+}
 #endif /* CONFIG_MODULES */
 
 /* Create a new event directory structure for a trace directory. */
@@ -1679,20 +1804,11 @@ __trace_add_event_dirs(struct trace_array *tr)
                         * want the module to disappear when reading one
                         * of these files). The file_ops keep account of
                         * the module ref count.
-                        *
-                        * As event_calls are added in groups by module,
-                        * when we find one file_ops, we don't need to search for
-                        * each call in that module, as the rest should be the
-                        * same. Only search for a new one if the last one did
-                        * not match.
                         */
-                       if (!file_ops || call->mod != file_ops->mod)
-                               file_ops = find_ftrace_file_ops(call->mod);
+                       file_ops = find_ftrace_file_ops(file_ops, call->mod);
                        if (!file_ops)
                                continue; /* Warn? */
-                       ret = __trace_add_new_event(call, tr,
-                                       &file_ops->id, &file_ops->enable,
-                                       &file_ops->filter, &file_ops->format);
+                       ret = __trace_add_new_mod_event(call, tr, file_ops);
                        if (ret < 0)
                                pr_warning("Could not create directory for event %s\n",
                                           call->name);
@@ -1709,6 +1825,347 @@ __trace_add_event_dirs(struct trace_array *tr)
        }
 }
 
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+/* Avoid typos */
+#define ENABLE_EVENT_STR       "enable_event"
+#define DISABLE_EVENT_STR      "disable_event"
+
+struct event_probe_data {
+       struct ftrace_event_file        *file;
+       unsigned long                   count;
+       int                             ref;
+       bool                            enable;
+};
+
+static struct ftrace_event_file *
+find_event_file(struct trace_array *tr, const char *system,  const char *event)
+{
+       struct ftrace_event_file *file;
+       struct ftrace_event_call *call;
+
+       list_for_each_entry(file, &tr->events, list) {
+
+               call = file->event_call;
+
+               if (!call->name || !call->class || !call->class->reg)
+                       continue;
+
+               if (call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)
+                       continue;
+
+               if (strcmp(event, call->name) == 0 &&
+                   strcmp(system, call->class->system) == 0)
+                       return file;
+       }
+       return NULL;
+}
+
+static void
+event_enable_probe(unsigned long ip, unsigned long parent_ip, void **_data)
+{
+       struct event_probe_data **pdata = (struct event_probe_data **)_data;
+       struct event_probe_data *data = *pdata;
+
+       if (!data)
+               return;
+
+       if (data->enable)
+               clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &data->file->flags);
+       else
+               set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &data->file->flags);
+}
+
+static void
+event_enable_count_probe(unsigned long ip, unsigned long parent_ip, void **_data)
+{
+       struct event_probe_data **pdata = (struct event_probe_data **)_data;
+       struct event_probe_data *data = *pdata;
+
+       if (!data)
+               return;
+
+       if (!data->count)
+               return;
+
+       /* Skip if the event is in a state we want to switch to */
+       if (data->enable == !(data->file->flags & FTRACE_EVENT_FL_SOFT_DISABLED))
+               return;
+
+       if (data->count != -1)
+               (data->count)--;
+
+       event_enable_probe(ip, parent_ip, _data);
+}
+
+static int
+event_enable_print(struct seq_file *m, unsigned long ip,
+                     struct ftrace_probe_ops *ops, void *_data)
+{
+       struct event_probe_data *data = _data;
+
+       seq_printf(m, "%ps:", (void *)ip);
+
+       seq_printf(m, "%s:%s:%s",
+                  data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR,
+                  data->file->event_call->class->system,
+                  data->file->event_call->name);
+
+       if (data->count == -1)
+               seq_printf(m, ":unlimited\n");
+       else
+               seq_printf(m, ":count=%ld\n", data->count);
+
+       return 0;
+}
+
+static int
+event_enable_init(struct ftrace_probe_ops *ops, unsigned long ip,
+                 void **_data)
+{
+       struct event_probe_data **pdata = (struct event_probe_data **)_data;
+       struct event_probe_data *data = *pdata;
+
+       data->ref++;
+       return 0;
+}
+
+static void
+event_enable_free(struct ftrace_probe_ops *ops, unsigned long ip,
+                 void **_data)
+{
+       struct event_probe_data **pdata = (struct event_probe_data **)_data;
+       struct event_probe_data *data = *pdata;
+
+       if (WARN_ON_ONCE(data->ref <= 0))
+               return;
+
+       data->ref--;
+       if (!data->ref) {
+               /* Remove the SOFT_MODE flag */
+               __ftrace_event_enable_disable(data->file, 0, 1);
+               module_put(data->file->event_call->mod);
+               kfree(data);
+       }
+       *pdata = NULL;
+}
+
+static struct ftrace_probe_ops event_enable_probe_ops = {
+       .func                   = event_enable_probe,
+       .print                  = event_enable_print,
+       .init                   = event_enable_init,
+       .free                   = event_enable_free,
+};
+
+static struct ftrace_probe_ops event_enable_count_probe_ops = {
+       .func                   = event_enable_count_probe,
+       .print                  = event_enable_print,
+       .init                   = event_enable_init,
+       .free                   = event_enable_free,
+};
+
+static struct ftrace_probe_ops event_disable_probe_ops = {
+       .func                   = event_enable_probe,
+       .print                  = event_enable_print,
+       .init                   = event_enable_init,
+       .free                   = event_enable_free,
+};
+
+static struct ftrace_probe_ops event_disable_count_probe_ops = {
+       .func                   = event_enable_count_probe,
+       .print                  = event_enable_print,
+       .init                   = event_enable_init,
+       .free                   = event_enable_free,
+};
+
+static int
+event_enable_func(struct ftrace_hash *hash,
+                 char *glob, char *cmd, char *param, int enabled)
+{
+       struct trace_array *tr = top_trace_array();
+       struct ftrace_event_file *file;
+       struct ftrace_probe_ops *ops;
+       struct event_probe_data *data;
+       const char *system;
+       const char *event;
+       char *number;
+       bool enable;
+       int ret;
+
+       /* hash funcs only work with set_ftrace_filter */
+       if (!enabled)
+               return -EINVAL;
+
+       if (!param)
+               return -EINVAL;
+
+       system = strsep(&param, ":");
+       if (!param)
+               return -EINVAL;
+
+       event = strsep(&param, ":");
+
+       mutex_lock(&event_mutex);
+
+       ret = -EINVAL;
+       file = find_event_file(tr, system, event);
+       if (!file)
+               goto out;
+
+       enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
+
+       if (enable)
+               ops = param ? &event_enable_count_probe_ops : &event_enable_probe_ops;
+       else
+               ops = param ? &event_disable_count_probe_ops : &event_disable_probe_ops;
+
+       if (glob[0] == '!') {
+               unregister_ftrace_function_probe_func(glob+1, ops);
+               ret = 0;
+               goto out;
+       }
+
+       ret = -ENOMEM;
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto out;
+
+       data->enable = enable;
+       data->count = -1;
+       data->file = file;
+
+       if (!param)
+               goto out_reg;
+
+       number = strsep(&param, ":");
+
+       ret = -EINVAL;
+       if (!strlen(number))
+               goto out_free;
+
+       /*
+        * We use the callback data field (which is a pointer)
+        * as our counter.
+        */
+       ret = kstrtoul(number, 0, &data->count);
+       if (ret)
+               goto out_free;
+
+ out_reg:
+       /* Don't let event modules unload while probe registered */
+       ret = try_module_get(file->event_call->mod);
+       if (!ret)
+               goto out_free;
+
+       ret = __ftrace_event_enable_disable(file, 1, 1);
+       if (ret < 0)
+               goto out_put;
+       ret = register_ftrace_function_probe(glob, ops, data);
+       if (!ret)
+               goto out_disable;
+ out:
+       mutex_unlock(&event_mutex);
+       return ret;
+
+ out_disable:
+       __ftrace_event_enable_disable(file, 0, 1);
+ out_put:
+       module_put(file->event_call->mod);
+ out_free:
+       kfree(data);
+       goto out;
+}
+
+static struct ftrace_func_command event_enable_cmd = {
+       .name                   = ENABLE_EVENT_STR,
+       .func                   = event_enable_func,
+};
+
+static struct ftrace_func_command event_disable_cmd = {
+       .name                   = DISABLE_EVENT_STR,
+       .func                   = event_enable_func,
+};
+
+static __init int register_event_cmds(void)
+{
+       int ret;
+
+       ret = register_ftrace_command(&event_enable_cmd);
+       if (WARN_ON(ret < 0))
+               return ret;
+       ret = register_ftrace_command(&event_disable_cmd);
+       if (WARN_ON(ret < 0))
+               unregister_ftrace_command(&event_enable_cmd);
+       return ret;
+}
+#else
+static inline int register_event_cmds(void) { return 0; }
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+/*
+ * The top level array has already had its ftrace_event_file
+ * descriptors created in order to allow for early events to
+ * be recorded. This function is called after the debugfs has been
+ * initialized, and we now have to create the files associated
+ * to the events.
+ */
+static __init void
+__trace_early_add_event_dirs(struct trace_array *tr)
+{
+       struct ftrace_event_file *file;
+       int ret;
+
+
+       list_for_each_entry(file, &tr->events, list) {
+               ret = event_create_dir(tr->event_dir, file,
+                                      &ftrace_event_id_fops,
+                                      &ftrace_enable_fops,
+                                      &ftrace_event_filter_fops,
+                                      &ftrace_event_format_fops);
+               if (ret < 0)
+                       pr_warning("Could not create directory for event %s\n",
+                                  file->event_call->name);
+       }
+}
+
+/*
+ * For early boot up, the top trace array requires to have
+ * a list of events that can be enabled. This must be done before
+ * the filesystem is set up in order to allow events to be traced
+ * early.
+ */
+static __init void
+__trace_early_add_events(struct trace_array *tr)
+{
+       struct ftrace_event_call *call;
+       int ret;
+
+       list_for_each_entry(call, &ftrace_events, list) {
+               /* Early boot up should not have any modules loaded */
+               if (WARN_ON_ONCE(call->mod))
+                       continue;
+
+               ret = __trace_early_add_new_event(call, tr);
+               if (ret < 0)
+                       pr_warning("Could not create early event %s\n",
+                                  call->name);
+       }
+}
+
+/* Remove the event directory structure for a trace directory. */
+static void
+__trace_remove_event_dirs(struct trace_array *tr)
+{
+       struct ftrace_event_file *file, *next;
+
+       list_for_each_entry_safe(file, next, &tr->events, list) {
+               list_del(&file->list);
+               debugfs_remove_recursive(file->dir);
+               remove_subsystem(file->system);
+               kmem_cache_free(file_cachep, file);
+       }
+}
+
 static void
 __add_event_to_tracers(struct ftrace_event_call *call,
                       struct ftrace_module_file_ops *file_ops)
@@ -1717,9 +2174,7 @@ __add_event_to_tracers(struct ftrace_event_call *call,
 
        list_for_each_entry(tr, &ftrace_trace_arrays, list) {
                if (file_ops)
-                       __trace_add_new_event(call, tr,
-                                             &file_ops->id, &file_ops->enable,
-                                             &file_ops->filter, &file_ops->format);
+                       __trace_add_new_mod_event(call, tr, file_ops);
                else
                        __trace_add_new_event(call, tr,
                                              &ftrace_event_id_fops,
@@ -1742,14 +2197,16 @@ static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata;
 static __init int setup_trace_event(char *str)
 {
        strlcpy(bootup_event_buf, str, COMMAND_LINE_SIZE);
-       ring_buffer_expanded = 1;
-       tracing_selftest_disabled = 1;
+       ring_buffer_expanded = true;
+       tracing_selftest_disabled = true;
 
        return 1;
 }
 __setup("trace_event=", setup_trace_event);
 
-int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
+/* Expects to have event_mutex held when called */
+static int
+create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
 {
        struct dentry *d_events;
        struct dentry *entry;
@@ -1762,8 +2219,10 @@ int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
        }
 
        d_events = debugfs_create_dir("events", parent);
-       if (!d_events)
+       if (!d_events) {
                pr_warning("Could not create debugfs 'events' directory\n");
+               return -ENOMEM;
+       }
 
        /* ring buffer internal formats */
        trace_create_file("header_page", 0444, d_events,
@@ -1778,8 +2237,89 @@ int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
                          tr, &ftrace_tr_enable_fops);
 
        tr->event_dir = d_events;
+
+       return 0;
+}
+
+/**
+ * event_trace_add_tracer - add a instance of a trace_array to events
+ * @parent: The parent dentry to place the files/directories for events in
+ * @tr: The trace array associated with these events
+ *
+ * When a new instance is created, it needs to set up its events
+ * directory, as well as other files associated with events. It also
+ * creates the event hierachry in the @parent/events directory.
+ *
+ * Returns 0 on success.
+ */
+int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
+{
+       int ret;
+
+       mutex_lock(&event_mutex);
+
+       ret = create_event_toplevel_files(parent, tr);
+       if (ret)
+               goto out_unlock;
+
+       down_write(&trace_event_sem);
        __trace_add_event_dirs(tr);
+       up_write(&trace_event_sem);
+
+ out_unlock:
+       mutex_unlock(&event_mutex);
+
+       return ret;
+}
 
+/*
+ * The top trace array already had its file descriptors created.
+ * Now the files themselves need to be created.
+ */
+static __init int
+early_event_add_tracer(struct dentry *parent, struct trace_array *tr)
+{
+       int ret;
+
+       mutex_lock(&event_mutex);
+
+       ret = create_event_toplevel_files(parent, tr);
+       if (ret)
+               goto out_unlock;
+
+       down_write(&trace_event_sem);
+       __trace_early_add_event_dirs(tr);
+       up_write(&trace_event_sem);
+
+ out_unlock:
+       mutex_unlock(&event_mutex);
+
+       return ret;
+}
+
+int event_trace_del_tracer(struct trace_array *tr)
+{
+       /* Disable any running events */
+       __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0);
+
+       mutex_lock(&event_mutex);
+
+       down_write(&trace_event_sem);
+       __trace_remove_event_dirs(tr);
+       debugfs_remove_recursive(tr->event_dir);
+       up_write(&trace_event_sem);
+
+       tr->event_dir = NULL;
+
+       mutex_unlock(&event_mutex);
+
+       return 0;
+}
+
+static __init int event_trace_memsetup(void)
+{
+       field_cachep = KMEM_CACHE(ftrace_event_field, SLAB_PANIC);
+       file_cachep = KMEM_CACHE(ftrace_event_file, SLAB_PANIC);
        return 0;
 }
 
@@ -1799,6 +2339,14 @@ static __init int event_trace_enable(void)
                        list_add(&call->list, &ftrace_events);
        }
 
+       /*
+        * We need the top trace array to have a working set of trace
+        * points at early init, before the debug files and directories
+        * are created. Create the file entries now, and attach them
+        * to the actual file dentries later.
+        */
+       __trace_early_add_events(tr);
+
        while (true) {
                token = strsep(&buf, ",");
 
@@ -1814,6 +2362,8 @@ static __init int event_trace_enable(void)
 
        trace_printk_start_comm();
 
+       register_event_cmds();
+
        return 0;
 }
 
@@ -1839,7 +2389,7 @@ static __init int event_trace_init(void)
        if (trace_define_common_fields())
                pr_warning("tracing: Failed to allocate common fields");
 
-       ret = event_trace_add_tracer(d_tracer, tr);
+       ret = early_event_add_tracer(d_tracer, tr);
        if (ret)
                return ret;
 
@@ -1849,6 +2399,7 @@ static __init int event_trace_init(void)
 
        return 0;
 }
+early_initcall(event_trace_memsetup);
 core_initcall(event_trace_enable);
 fs_initcall(event_trace_init);