Skip to content

Commit c2489bb

Browse files
Zheng Yejianrostedt
authored andcommitted
tracing: Introduce pipe_cpumask to avoid race on trace_pipes
There is race issue when concurrently splice_read main trace_pipe and per_cpu trace_pipes which will result in data read out being different from what actually writen. As suggested by Steven: > I believe we should add a ref count to trace_pipe and the per_cpu > trace_pipes, where if they are opened, nothing else can read it. > > Opening trace_pipe locks all per_cpu ref counts, if any of them are > open, then the trace_pipe open will fail (and releases any ref counts > it had taken). > > Opening a per_cpu trace_pipe will up the ref count for just that > CPU buffer. This will allow multiple tasks to read different per_cpu > trace_pipe files, but will prevent the main trace_pipe file from > being opened. But because we only need to know whether per_cpu trace_pipe is open or not, using a cpumask instead of using ref count may be easier. After this patch, users will find that: - Main trace_pipe can be opened by only one user, and if it is opened, all per_cpu trace_pipes cannot be opened; - Per_cpu trace_pipes can be opened by multiple users, but each per_cpu trace_pipe can only be opened by one user. And if one of them is opened, main trace_pipe cannot be opened. Link: https://lore.kernel.org/linux-trace-kernel/20230818022645.1948314-1-zhengyejian1@huawei.com Suggested-by: Steven Rostedt (Google) <rostedt@goodmis.org> Signed-off-by: Zheng Yejian <zhengyejian1@huawei.com> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent eecb91b commit c2489bb

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

kernel/trace/trace.c

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6718,24 +6718,53 @@ tracing_max_lat_write(struct file *filp, const char __user *ubuf,
67186718

67196719
#endif
67206720

6721+
static int open_pipe_on_cpu(struct trace_array *tr, int cpu)
6722+
{
6723+
if (cpu == RING_BUFFER_ALL_CPUS) {
6724+
if (cpumask_empty(tr->pipe_cpumask)) {
6725+
cpumask_setall(tr->pipe_cpumask);
6726+
return 0;
6727+
}
6728+
} else if (!cpumask_test_cpu(cpu, tr->pipe_cpumask)) {
6729+
cpumask_set_cpu(cpu, tr->pipe_cpumask);
6730+
return 0;
6731+
}
6732+
return -EBUSY;
6733+
}
6734+
6735+
static void close_pipe_on_cpu(struct trace_array *tr, int cpu)
6736+
{
6737+
if (cpu == RING_BUFFER_ALL_CPUS) {
6738+
WARN_ON(!cpumask_full(tr->pipe_cpumask));
6739+
cpumask_clear(tr->pipe_cpumask);
6740+
} else {
6741+
WARN_ON(!cpumask_test_cpu(cpu, tr->pipe_cpumask));
6742+
cpumask_clear_cpu(cpu, tr->pipe_cpumask);
6743+
}
6744+
}
6745+
67216746
static int tracing_open_pipe(struct inode *inode, struct file *filp)
67226747
{
67236748
struct trace_array *tr = inode->i_private;
67246749
struct trace_iterator *iter;
6750+
int cpu;
67256751
int ret;
67266752

67276753
ret = tracing_check_open_get_tr(tr);
67286754
if (ret)
67296755
return ret;
67306756

67316757
mutex_lock(&trace_types_lock);
6758+
cpu = tracing_get_cpu(inode);
6759+
ret = open_pipe_on_cpu(tr, cpu);
6760+
if (ret)
6761+
goto fail_pipe_on_cpu;
67326762

67336763
/* create a buffer to store the information to pass to userspace */
67346764
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
67356765
if (!iter) {
67366766
ret = -ENOMEM;
6737-
__trace_array_put(tr);
6738-
goto out;
6767+
goto fail_alloc_iter;
67396768
}
67406769

67416770
trace_seq_init(&iter->seq);
@@ -6758,7 +6787,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
67586787

67596788
iter->tr = tr;
67606789
iter->array_buffer = &tr->array_buffer;
6761-
iter->cpu_file = tracing_get_cpu(inode);
6790+
iter->cpu_file = cpu;
67626791
mutex_init(&iter->mutex);
67636792
filp->private_data = iter;
67646793

@@ -6768,12 +6797,15 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
67686797
nonseekable_open(inode, filp);
67696798

67706799
tr->trace_ref++;
6771-
out:
6800+
67726801
mutex_unlock(&trace_types_lock);
67736802
return ret;
67746803

67756804
fail:
67766805
kfree(iter);
6806+
fail_alloc_iter:
6807+
close_pipe_on_cpu(tr, cpu);
6808+
fail_pipe_on_cpu:
67776809
__trace_array_put(tr);
67786810
mutex_unlock(&trace_types_lock);
67796811
return ret;
@@ -6790,7 +6822,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)
67906822

67916823
if (iter->trace->pipe_close)
67926824
iter->trace->pipe_close(iter);
6793-
6825+
close_pipe_on_cpu(tr, iter->cpu_file);
67946826
mutex_unlock(&trace_types_lock);
67956827

67966828
free_cpumask_var(iter->started);
@@ -9454,6 +9486,9 @@ static struct trace_array *trace_array_create(const char *name)
94549486
if (!alloc_cpumask_var(&tr->tracing_cpumask, GFP_KERNEL))
94559487
goto out_free_tr;
94569488

9489+
if (!alloc_cpumask_var(&tr->pipe_cpumask, GFP_KERNEL))
9490+
goto out_free_tr;
9491+
94579492
tr->trace_flags = global_trace.trace_flags & ~ZEROED_TRACE_FLAGS;
94589493

94599494
cpumask_copy(tr->tracing_cpumask, cpu_all_mask);
@@ -9495,6 +9530,7 @@ static struct trace_array *trace_array_create(const char *name)
94959530
out_free_tr:
94969531
ftrace_free_ftrace_ops(tr);
94979532
free_trace_buffers(tr);
9533+
free_cpumask_var(tr->pipe_cpumask);
94989534
free_cpumask_var(tr->tracing_cpumask);
94999535
kfree(tr->name);
95009536
kfree(tr);
@@ -9597,6 +9633,7 @@ static int __remove_instance(struct trace_array *tr)
95979633
}
95989634
kfree(tr->topts);
95999635

9636+
free_cpumask_var(tr->pipe_cpumask);
96009637
free_cpumask_var(tr->tracing_cpumask);
96019638
kfree(tr->name);
96029639
kfree(tr);
@@ -10394,12 +10431,14 @@ __init static int tracer_alloc_buffers(void)
1039410431
if (trace_create_savedcmd() < 0)
1039510432
goto out_free_temp_buffer;
1039610433

10434+
if (!alloc_cpumask_var(&global_trace.pipe_cpumask, GFP_KERNEL))
10435+
goto out_free_savedcmd;
10436+
1039710437
/* TODO: make the number of buffers hot pluggable with CPUS */
1039810438
if (allocate_trace_buffers(&global_trace, ring_buf_size) < 0) {
1039910439
MEM_FAIL(1, "tracer: failed to allocate ring buffer!\n");
10400-
goto out_free_savedcmd;
10440+
goto out_free_pipe_cpumask;
1040110441
}
10402-
1040310442
if (global_trace.buffer_disabled)
1040410443
tracing_off();
1040510444

@@ -10452,6 +10491,8 @@ __init static int tracer_alloc_buffers(void)
1045210491

1045310492
return 0;
1045410493

10494+
out_free_pipe_cpumask:
10495+
free_cpumask_var(global_trace.pipe_cpumask);
1045510496
out_free_savedcmd:
1045610497
free_saved_cmdlines_buffer(savedcmd);
1045710498
out_free_temp_buffer:

kernel/trace/trace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ struct trace_array {
377377
struct list_head events;
378378
struct trace_event_file *trace_marker_file;
379379
cpumask_var_t tracing_cpumask; /* only trace on set CPUs */
380+
/* one per_cpu trace_pipe can be opened by only one user */
381+
cpumask_var_t pipe_cpumask;
380382
int ref;
381383
int trace_ref;
382384
#ifdef CONFIG_FUNCTION_TRACER

0 commit comments

Comments
 (0)