]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ring-buffer: Enforce read ordering of trace_buffer cpumask and buffers
authorVincent Donnefort <vdonnefort@google.com>
Wed, 1 Apr 2026 05:36:59 +0000 (06:36 +0100)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Thu, 2 Apr 2026 17:19:09 +0000 (13:19 -0400)
On CPU hotplug, if it is the first time a trace_buffer sees a CPU, a
ring_buffer_per_cpu will be allocated and its corresponding bit toggled
in the cpumask. Many readers check this cpumask to know if they can
safely read the ring_buffer_per_cpu but they are doing so without memory
ordering and may observe the cpumask bit set while having NULL buffer
pointer.

Enforce the memory read ordering by sending an IPI to all online CPUs.
The hotplug path is a slow-path anyway and it saves us from adding read
barriers in numerous call sites.

Link: https://patch.msgid.link/20260401053659.3458961-1-vdonnefort@google.com
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
Suggested-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
kernel/trace/ring_buffer.c

index 8b6c39bba56d3c6800d694d01eefcb99792582c7..2caa5d3d0ae984194db8f778ec04c3e3af579b8e 100644 (file)
@@ -7722,6 +7722,12 @@ out:
        return 0;
 }
 
+static void rb_cpu_sync(void *data)
+{
+       /* Not really needed, but documents what is happening */
+       smp_rmb();
+}
+
 /*
  * We only allocate new buffers, never free them if the CPU goes down.
  * If we were to free the buffer, then the user would lose any trace that was in
@@ -7760,7 +7766,18 @@ int trace_rb_cpu_prepare(unsigned int cpu, struct hlist_node *node)
                     cpu);
                return -ENOMEM;
        }
-       smp_wmb();
+
+       /*
+        * Ensure trace_buffer readers observe the newly allocated
+        * ring_buffer_per_cpu before they check the cpumask. Instead of using a
+        * read barrier for all readers, send an IPI.
+        */
+       if (unlikely(system_state == SYSTEM_RUNNING)) {
+               on_each_cpu(rb_cpu_sync, NULL, 1);
+               /* Not really needed, but documents what is happening */
+               smp_wmb();
+       }
+
        cpumask_set_cpu(cpu, buffer->cpumask);
        return 0;
 }