]> Pileus Git - ~andy/linux/blobdiff - kernel/trace/trace_events.c
tracing: Use direct field, type and system names
[~andy/linux] / kernel / trace / trace_events.c
index 439955239baec0f2d4c24368b6962d702d067916..63b4bdf8459383b5f5617cc2f0a71c4164922163 100644 (file)
@@ -36,6 +36,11 @@ EXPORT_SYMBOL_GPL(event_storage);
 LIST_HEAD(ftrace_events);
 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)                       \
        list_for_each_entry(tr, &ftrace_trace_arrays, list) {   \
@@ -63,17 +68,12 @@ static int __trace_define_field(struct list_head *head, const char *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 +89,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;
 }
@@ -141,9 +139,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);
        }
 }
 
@@ -281,7 +277,6 @@ static void __put_system(struct event_subsystem *system)
                kfree(filter->filter_string);
                kfree(filter);
        }
-       kfree(system->name);
        kfree(system);
 }
 
@@ -1197,10 +1192,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 +1205,6 @@ create_new_subsystem(const char *name)
        return system;
 
  out_free:
-       kfree(system->name);
        kfree(system);
        return NULL;
 }
@@ -1383,7 +1374,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 +1453,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 +1464,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);
@@ -1709,6 +1722,70 @@ __trace_add_event_dirs(struct trace_array *tr)
        }
 }
 
+/*
+ * 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)
@@ -1749,7 +1826,9 @@ static __init int setup_trace_event(char *str)
 }
 __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 +1841,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,11 +1859,92 @@ 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_mutex);
        __trace_add_event_dirs(tr);
+       up_write(&trace_event_mutex);
+
+ 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_mutex);
+       __trace_early_add_event_dirs(tr);
+       up_write(&trace_event_mutex);
+
+ 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_mutex);
+       __trace_remove_event_dirs(tr);
+       debugfs_remove_recursive(tr->event_dir);
+       up_write(&trace_event_mutex);
+
+       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;
+}
+
 static __init int event_trace_enable(void)
 {
        struct trace_array *tr = top_trace_array();
@@ -1799,6 +1961,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, ",");
 
@@ -1839,7 +2009,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 +2019,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);