diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 7b99e36b8973..78022c1a125f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4308,7 +4308,7 @@ int tracing_update_buffers(void) struct trace_option_dentry; -static struct trace_option_dentry * +static void create_trace_option_files(struct trace_array *tr, struct tracer *tracer); /* @@ -4334,15 +4334,7 @@ static void add_tracer_options(struct trace_array *tr, struct tracer *t) if (!tr->dir) return; - /* Currently, only the top instance has options */ - if (!(tr->flags & TRACE_ARRAY_FL_GLOBAL)) - return; - - /* Ignore if they were already created */ - if (t->topts) - return; - - t->topts = create_trace_option_files(tr, t); + create_trace_option_files(tr, t); } static int tracing_set_tracer(struct trace_array *tr, const char *buf) @@ -6341,21 +6333,39 @@ create_trace_option_file(struct trace_array *tr, } -static struct trace_option_dentry * +static void create_trace_option_files(struct trace_array *tr, struct tracer *tracer) { struct trace_option_dentry *topts; + struct trace_options *tr_topts; struct tracer_flags *flags; struct tracer_opt *opts; int cnt; + int i; if (!tracer) - return NULL; + return; flags = tracer->flags; if (!flags || !flags->opts) - return NULL; + return; + + /* + * If this is an instance, only create flags for tracers + * the instance may have. + */ + if (!trace_ok_for_array(tracer, tr)) + return; + + for (i = 0; i < tr->nr_topts; i++) { + /* + * Check if these flags have already been added. + * Some tracers share flags. + */ + if (tr->topts[i].tracer->flags == tracer->flags) + return; + } opts = flags->opts; @@ -6364,7 +6374,19 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer) topts = kcalloc(cnt + 1, sizeof(*topts), GFP_KERNEL); if (!topts) - return NULL; + return; + + tr_topts = krealloc(tr->topts, sizeof(*tr->topts) * (tr->nr_topts + 1), + GFP_KERNEL); + if (!tr_topts) { + kfree(topts); + return; + } + + tr->topts = tr_topts; + tr->topts[tr->nr_topts].tracer = tracer; + tr->topts[tr->nr_topts].topts = topts; + tr->nr_topts++; for (cnt = 0; opts[cnt].name; cnt++) { create_trace_option_file(tr, &topts[cnt], flags, @@ -6373,8 +6395,6 @@ create_trace_option_files(struct trace_array *tr, struct tracer *tracer) "Failed to create trace option: %s", opts[cnt].name); } - - return topts; } static struct dentry * @@ -6552,6 +6572,21 @@ static void init_trace_flags_index(struct trace_array *tr) tr->trace_flags_index[i] = i; } +static void __update_tracer_options(struct trace_array *tr) +{ + struct tracer *t; + + for (t = trace_types; t; t = t->next) + add_tracer_options(tr, t); +} + +static void update_tracer_options(struct trace_array *tr) +{ + mutex_lock(&trace_types_lock); + __update_tracer_options(tr); + mutex_unlock(&trace_types_lock); +} + static int instance_mkdir(const char *name) { struct trace_array *tr; @@ -6605,6 +6640,7 @@ static int instance_mkdir(const char *name) init_tracer_tracefs(tr, tr->dir); init_trace_flags_index(tr); + __update_tracer_options(tr); list_add(&tr->list, &ftrace_trace_arrays); @@ -6630,6 +6666,7 @@ static int instance_rmdir(const char *name) struct trace_array *tr; int found = 0; int ret; + int i; mutex_lock(&trace_types_lock); @@ -6655,6 +6692,11 @@ static int instance_rmdir(const char *name) debugfs_remove_recursive(tr->dir); free_trace_buffers(tr); + for (i = 0; i < tr->nr_topts; i++) { + kfree(tr->topts[i].topts); + } + kfree(tr->topts); + kfree(tr->name); kfree(tr); @@ -6877,7 +6919,6 @@ static struct notifier_block trace_module_nb = { static __init int tracer_init_tracefs(void) { struct dentry *d_tracer; - struct tracer *t; trace_access_lock_init(); @@ -6914,10 +6955,7 @@ static __init int tracer_init_tracefs(void) create_trace_instances(d_tracer); - mutex_lock(&trace_types_lock); - for (t = trace_types; t; t = t->next) - add_tracer_options(&global_trace, t); - mutex_unlock(&trace_types_lock); + update_tracer_options(&global_trace); return 0; } diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 423cb48a1d6d..fb8a61c710ea 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -159,6 +159,7 @@ struct trace_array_cpu { }; struct tracer; +struct trace_option_dentry; struct trace_buffer { struct trace_array *tr; @@ -170,6 +171,11 @@ struct trace_buffer { #define TRACE_FLAGS_MAX_SIZE 32 +struct trace_options { + struct tracer *tracer; + struct trace_option_dentry *topts; +}; + /* * The trace array - an array of per-CPU trace arrays. This is the * highest level data structure that individual tracers deal with. @@ -218,6 +224,7 @@ struct trace_array { #endif int stop_count; int clock_id; + int nr_topts; struct tracer *current_trace; unsigned int trace_flags; unsigned char trace_flags_index[TRACE_FLAGS_MAX_SIZE]; @@ -227,6 +234,7 @@ struct trace_array { struct dentry *options; struct dentry *percpu_dir; struct dentry *event_dir; + struct trace_options *topts; struct list_head systems; struct list_head events; cpumask_var_t tracing_cpumask; /* only trace on set CPUs */ @@ -398,7 +406,6 @@ struct tracer { u32 mask, int set); struct tracer *next; struct tracer_flags *flags; - struct trace_option_dentry *topts; int enabled; int ref; bool print_max;