Sometimes, write to buffer_size_kb can be permanently failure if we change the cpu_online_mask between two for_each_online_buffer_cpu loops in function ring_buffer_reset_online_cpus. The number of increasing and decreasing on cpu_buffer->resize_disable may be inconsistent, leading the resize_disabled in some CPUs becoming none zero after ring_buffer_reset_online_cpus return. This issue can be reproduced by "echo 0 > trace" and hotplug cpu at the same time. After reproducing succeess, we can find out the attempt to write to buffer_size_kb node failure every time. This patch prevent the inconsistent increasing and decreasing on cpu_buffer->resize_disabled by copying the cpu_online_mask at the beginning of the function. But I wonder if there's any side-effect of this patch, since the behavior changed, if we turn on a cpu between the two loops, reset_disabled_cpu_buffer() of that cpu won't be run as before, meaning the cpu_buffer on that cpu just awake will not be cleaned up. Cc: stable@xxxxxxxxxxxxxxx Signed-off-by: Tze-nan Wu <Tze-nan.Wu@xxxxxxxxxxxx> --- kernel/trace/ring_buffer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 76a2d91eecad..468f46bba71e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -288,9 +288,6 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data); #define for_each_buffer_cpu(buffer, cpu) \ for_each_cpu(cpu, buffer->cpumask) -#define for_each_online_buffer_cpu(buffer, cpu) \ - for_each_cpu_and(cpu, buffer->cpumask, cpu_online_mask) - #define TS_SHIFT 27 #define TS_MASK ((1ULL << TS_SHIFT) - 1) #define TS_DELTA_TEST (~TS_MASK) @@ -5353,12 +5350,15 @@ EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu); void ring_buffer_reset_online_cpus(struct trace_buffer *buffer) { struct ring_buffer_per_cpu *cpu_buffer; + cpumask_var_t reset_online_mask; int cpu; /* prevent another thread from changing buffer sizes */ mutex_lock(&buffer->mutex); - for_each_online_buffer_cpu(buffer, cpu) { + cpumask_copy(reset_online_mask, cpu_online_mask); + + for_each_cpu_and(cpu, buffer->cpumask, reset_online_mask) { cpu_buffer = buffer->buffers[cpu]; atomic_inc(&cpu_buffer->resize_disabled); @@ -5368,7 +5368,7 @@ void ring_buffer_reset_online_cpus(struct trace_buffer *buffer) /* Make sure all commits have finished */ synchronize_rcu(); - for_each_online_buffer_cpu(buffer, cpu) { + for_each_cpu_and(cpu, buffer->cpumask, reset_online_mask) { cpu_buffer = buffer->buffers[cpu]; reset_disabled_cpu_buffer(cpu_buffer); -- 2.18.0