| From 7b85af63034818e43aee6c1d7bf1c7c6796a9073 Mon Sep 17 00:00:00 2001 |
| From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org> |
| Date: Mon, 1 Jul 2013 23:34:22 -0400 |
| Subject: tracing: Get trace_array ref counts when accessing trace files |
| |
| From: "Steven Rostedt (Red Hat)" <rostedt@goodmis.org> |
| |
| commit 7b85af63034818e43aee6c1d7bf1c7c6796a9073 upstream. |
| |
| When a trace file is opened that may access a trace array, it must |
| increment its ref count to prevent it from being deleted. |
| |
| Reported-by: Alexander Lam <azl@google.com> |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| kernel/trace/trace.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++---- |
| 1 file changed, 112 insertions(+), 9 deletions(-) |
| |
| --- a/kernel/trace/trace.c |
| +++ b/kernel/trace/trace.c |
| @@ -2902,6 +2902,43 @@ int tracing_open_generic(struct inode *i |
| return 0; |
| } |
| |
| +/* |
| + * Open and update trace_array ref count. |
| + * Must have the current trace_array passed to it. |
| + */ |
| +int tracing_open_generic_tr(struct inode *inode, struct file *filp) |
| +{ |
| + struct trace_array *tr = inode->i_private; |
| + |
| + if (tracing_disabled) |
| + return -ENODEV; |
| + |
| + if (trace_array_get(tr) < 0) |
| + return -ENODEV; |
| + |
| + filp->private_data = inode->i_private; |
| + |
| + return 0; |
| + |
| +} |
| + |
| +int tracing_open_generic_tc(struct inode *inode, struct file *filp) |
| +{ |
| + struct trace_cpu *tc = inode->i_private; |
| + struct trace_array *tr = tc->tr; |
| + |
| + if (tracing_disabled) |
| + return -ENODEV; |
| + |
| + if (trace_array_get(tr) < 0) |
| + return -ENODEV; |
| + |
| + filp->private_data = inode->i_private; |
| + |
| + return 0; |
| + |
| +} |
| + |
| static int tracing_release(struct inode *inode, struct file *file) |
| { |
| struct seq_file *m = file->private_data; |
| @@ -2945,6 +2982,32 @@ static int tracing_release(struct inode |
| return 0; |
| } |
| |
| +static int tracing_release_generic_tr(struct inode *inode, struct file *file) |
| +{ |
| + struct trace_array *tr = inode->i_private; |
| + |
| + trace_array_put(tr); |
| + return 0; |
| +} |
| + |
| +static int tracing_release_generic_tc(struct inode *inode, struct file *file) |
| +{ |
| + struct trace_cpu *tc = inode->i_private; |
| + struct trace_array *tr = tc->tr; |
| + |
| + trace_array_put(tr); |
| + return 0; |
| +} |
| + |
| +static int tracing_single_release_tr(struct inode *inode, struct file *file) |
| +{ |
| + struct trace_array *tr = inode->i_private; |
| + |
| + trace_array_put(tr); |
| + |
| + return single_release(inode, file); |
| +} |
| + |
| static int tracing_open(struct inode *inode, struct file *file) |
| { |
| struct trace_cpu *tc = inode->i_private; |
| @@ -3331,9 +3394,14 @@ tracing_trace_options_write(struct file |
| |
| static int tracing_trace_options_open(struct inode *inode, struct file *file) |
| { |
| + struct trace_array *tr = inode->i_private; |
| + |
| if (tracing_disabled) |
| return -ENODEV; |
| |
| + if (trace_array_get(tr) < 0) |
| + return -ENODEV; |
| + |
| return single_open(file, tracing_trace_options_show, inode->i_private); |
| } |
| |
| @@ -3341,7 +3409,7 @@ static const struct file_operations trac |
| .open = tracing_trace_options_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| - .release = single_release, |
| + .release = tracing_single_release_tr, |
| .write = tracing_trace_options_write, |
| }; |
| |
| @@ -3829,6 +3897,9 @@ static int tracing_open_pipe(struct inod |
| if (tracing_disabled) |
| return -ENODEV; |
| |
| + if (trace_array_get(tr) < 0) |
| + return -ENODEV; |
| + |
| mutex_lock(&trace_types_lock); |
| |
| /* create a buffer to store the information to pass to userspace */ |
| @@ -3881,6 +3952,7 @@ out: |
| fail: |
| kfree(iter->trace); |
| kfree(iter); |
| + __trace_array_put(tr); |
| mutex_unlock(&trace_types_lock); |
| return ret; |
| } |
| @@ -3888,6 +3960,8 @@ fail: |
| static int tracing_release_pipe(struct inode *inode, struct file *file) |
| { |
| struct trace_iterator *iter = file->private_data; |
| + struct trace_cpu *tc = inode->i_private; |
| + struct trace_array *tr = tc->tr; |
| |
| mutex_lock(&trace_types_lock); |
| |
| @@ -3901,6 +3975,8 @@ static int tracing_release_pipe(struct i |
| kfree(iter->trace); |
| kfree(iter); |
| |
| + trace_array_put(tr); |
| + |
| return 0; |
| } |
| |
| @@ -4358,6 +4434,8 @@ tracing_free_buffer_release(struct inode |
| /* resize the ring buffer to 0 */ |
| tracing_resize_ring_buffer(tr, 0, RING_BUFFER_ALL_CPUS); |
| |
| + trace_array_put(tr); |
| + |
| return 0; |
| } |
| |
| @@ -4534,10 +4612,20 @@ static ssize_t tracing_clock_write(struc |
| |
| static int tracing_clock_open(struct inode *inode, struct file *file) |
| { |
| + struct trace_array *tr = inode->i_private; |
| + int ret; |
| + |
| if (tracing_disabled) |
| return -ENODEV; |
| |
| - return single_open(file, tracing_clock_show, inode->i_private); |
| + if (trace_array_get(tr)) |
| + return -ENODEV; |
| + |
| + ret = single_open(file, tracing_clock_show, inode->i_private); |
| + if (ret < 0) |
| + trace_array_put(tr); |
| + |
| + return ret; |
| } |
| |
| struct ftrace_buffer_info { |
| @@ -4733,34 +4821,38 @@ static const struct file_operations trac |
| }; |
| |
| static const struct file_operations tracing_entries_fops = { |
| - .open = tracing_open_generic, |
| + .open = tracing_open_generic_tc, |
| .read = tracing_entries_read, |
| .write = tracing_entries_write, |
| .llseek = generic_file_llseek, |
| + .release = tracing_release_generic_tc, |
| }; |
| |
| static const struct file_operations tracing_total_entries_fops = { |
| - .open = tracing_open_generic, |
| + .open = tracing_open_generic_tr, |
| .read = tracing_total_entries_read, |
| .llseek = generic_file_llseek, |
| + .release = tracing_release_generic_tr, |
| }; |
| |
| static const struct file_operations tracing_free_buffer_fops = { |
| + .open = tracing_open_generic_tr, |
| .write = tracing_free_buffer_write, |
| .release = tracing_free_buffer_release, |
| }; |
| |
| static const struct file_operations tracing_mark_fops = { |
| - .open = tracing_open_generic, |
| + .open = tracing_open_generic_tr, |
| .write = tracing_mark_write, |
| .llseek = generic_file_llseek, |
| + .release = tracing_release_generic_tr, |
| }; |
| |
| static const struct file_operations trace_clock_fops = { |
| .open = tracing_clock_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| - .release = single_release, |
| + .release = tracing_single_release_tr, |
| .write = tracing_clock_write, |
| }; |
| |
| @@ -4788,13 +4880,19 @@ static int tracing_buffers_open(struct i |
| struct trace_cpu *tc = inode->i_private; |
| struct trace_array *tr = tc->tr; |
| struct ftrace_buffer_info *info; |
| + int ret; |
| |
| if (tracing_disabled) |
| return -ENODEV; |
| |
| + if (trace_array_get(tr) < 0) |
| + return -ENODEV; |
| + |
| info = kzalloc(sizeof(*info), GFP_KERNEL); |
| - if (!info) |
| + if (!info) { |
| + trace_array_put(tr); |
| return -ENOMEM; |
| + } |
| |
| mutex_lock(&trace_types_lock); |
| |
| @@ -4812,7 +4910,11 @@ static int tracing_buffers_open(struct i |
| |
| mutex_unlock(&trace_types_lock); |
| |
| - return nonseekable_open(inode, filp); |
| + ret = nonseekable_open(inode, filp); |
| + if (ret < 0) |
| + trace_array_put(tr); |
| + |
| + return ret; |
| } |
| |
| static unsigned int |
| @@ -5707,9 +5809,10 @@ rb_simple_write(struct file *filp, const |
| } |
| |
| static const struct file_operations rb_simple_fops = { |
| - .open = tracing_open_generic, |
| + .open = tracing_open_generic_tr, |
| .read = rb_simple_read, |
| .write = rb_simple_write, |
| + .release = tracing_release_generic_tr, |
| .llseek = default_llseek, |
| }; |
| |