aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2013-07-23 11:25:54 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-29 12:47:32 -0400
commite2d4dbe02175e54616fe52c4e9c367199f8ecdaa (patch)
tree6e23d7e01e1df9a4e635e3f288ca722bf8310d6b /kernel
parente899e7e4f83ecd606e85d355410d724ec76542cb (diff)
tracing: Introduce trace_create_cpu_file() and tracing_get_cpu()
commit 649e9c70da6bfbeb563193a35d3424a5aa7c0d38 upstream. Every "file_operations" used by tracing_init_debugfs_percpu is buggy. f_op->open/etc does: 1. struct trace_cpu *tc = inode->i_private; struct trace_array *tr = tc->tr; 2. trace_array_get(tr) or fail; 3. do_something(tc); But tc (and tr) can be already freed before trace_array_get() is called. And it doesn't matter whether this file is per-cpu or it was created by init_tracer_debugfs(), free_percpu() or kfree() are equally bad. Note that even 1. is not safe, the freed memory can be unmapped. But even if it was safe trace_array_get() can wrongly succeed if we also race with the next new_instance_create() which can re-allocate the same tr, or tc was overwritten and ->tr points to the valid tr. In this case 3. uses the freed/reused memory. Add the new trivial helper, trace_create_cpu_file() which simply calls trace_create_file() and encodes "cpu" in "struct inode". Another helper, tracing_get_cpu() will be used to read cpu_nr-or-RING_BUFFER_ALL_CPUS. The patch abuses ->i_cdev to encode the number, it is never used unless the file is S_ISCHR(). But we could use something else, say, i_bytes or even ->d_fsdata. In any case this hack is hidden inside these 2 helpers, it would be trivial to change them if needed. This patch only changes tracing_init_debugfs_percpu() to use the new trace_create_cpu_file(), the next patches will change file_operations. Note: tracing_get_cpu(inode) is always safe but you can't trust the result unless trace_array_get() was called, without trace_types_lock which acts as a barrier it can wrongly return RING_BUFFER_ALL_CPUS. Link: http://lkml.kernel.org/r/20130723152554.GA23710@redhat.com Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/trace/trace.c50
1 files changed, 36 insertions, 14 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 06a5bcef373f..f064dfb973fe 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -2834,6 +2834,17 @@ static int s_show(struct seq_file *m, void *v)
2834 return 0; 2834 return 0;
2835} 2835}
2836 2836
2837/*
2838 * Should be used after trace_array_get(), trace_types_lock
2839 * ensures that i_cdev was already initialized.
2840 */
2841static inline int tracing_get_cpu(struct inode *inode)
2842{
2843 if (inode->i_cdev) /* See trace_create_cpu_file() */
2844 return (long)inode->i_cdev - 1;
2845 return RING_BUFFER_ALL_CPUS;
2846}
2847
2837static const struct seq_operations tracer_seq_ops = { 2848static const struct seq_operations tracer_seq_ops = {
2838 .start = s_start, 2849 .start = s_start,
2839 .next = s_next, 2850 .next = s_next,
@@ -5521,6 +5532,17 @@ static struct dentry *tracing_dentry_percpu(struct trace_array *tr, int cpu)
5521 return tr->percpu_dir; 5532 return tr->percpu_dir;
5522} 5533}
5523 5534
5535static struct dentry *
5536trace_create_cpu_file(const char *name, umode_t mode, struct dentry *parent,
5537 void *data, long cpu, const struct file_operations *fops)
5538{
5539 struct dentry *ret = trace_create_file(name, mode, parent, data, fops);
5540
5541 if (ret) /* See tracing_get_cpu() */
5542 ret->d_inode->i_cdev = (void *)(cpu + 1);
5543 return ret;
5544}
5545
5524static void 5546static void
5525tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) 5547tracing_init_debugfs_percpu(struct trace_array *tr, long cpu)
5526{ 5548{
@@ -5540,28 +5562,28 @@ tracing_init_debugfs_percpu(struct trace_array *tr, long cpu)
5540 } 5562 }
5541 5563
5542 /* per cpu trace_pipe */ 5564 /* per cpu trace_pipe */
5543 trace_create_file("trace_pipe", 0444, d_cpu, 5565 trace_create_cpu_file("trace_pipe", 0444, d_cpu,
5544 (void *)&data->trace_cpu, &tracing_pipe_fops); 5566 &data->trace_cpu, cpu, &tracing_pipe_fops);
5545 5567
5546 /* per cpu trace */ 5568 /* per cpu trace */
5547 trace_create_file("trace", 0644, d_cpu, 5569 trace_create_cpu_file("trace", 0644, d_cpu,
5548 (void *)&data->trace_cpu, &tracing_fops); 5570 &data->trace_cpu, cpu, &tracing_fops);
5549 5571
5550 trace_create_file("trace_pipe_raw", 0444, d_cpu, 5572 trace_create_cpu_file("trace_pipe_raw", 0444, d_cpu,
5551 (void *)&data->trace_cpu, &tracing_buffers_fops); 5573 &data->trace_cpu, cpu, &tracing_buffers_fops);
5552 5574
5553 trace_create_file("stats", 0444, d_cpu, 5575 trace_create_cpu_file("stats", 0444, d_cpu,
5554 (void *)&data->trace_cpu, &tracing_stats_fops); 5576 &data->trace_cpu, cpu, &tracing_stats_fops);
5555 5577
5556 trace_create_file("buffer_size_kb", 0444, d_cpu, 5578 trace_create_cpu_file("buffer_size_kb", 0444, d_cpu,
5557 (void *)&data->trace_cpu, &tracing_entries_fops); 5579 &data->trace_cpu, cpu, &tracing_entries_fops);
5558 5580
5559#ifdef CONFIG_TRACER_SNAPSHOT 5581#ifdef CONFIG_TRACER_SNAPSHOT
5560 trace_create_file("snapshot", 0644, d_cpu, 5582 trace_create_cpu_file("snapshot", 0644, d_cpu,
5561 (void *)&data->trace_cpu, &snapshot_fops); 5583 &data->trace_cpu, cpu, &snapshot_fops);
5562 5584
5563 trace_create_file("snapshot_raw", 0444, d_cpu, 5585 trace_create_cpu_file("snapshot_raw", 0444, d_cpu,
5564 (void *)&data->trace_cpu, &snapshot_raw_fops); 5586 &data->trace_cpu, cpu, &snapshot_raw_fops);
5565#endif 5587#endif
5566} 5588}
5567 5589