Skip to content

Commit 8145f1c

Browse files
committed
ring-buffer: Fix full_waiters_pending in poll
If a reader of the ring buffer is doing a poll, and waiting for the ring buffer to hit a specific watermark, there could be a case where it gets into an infinite ping-pong loop. The poll code has: rbwork->full_waiters_pending = true; if (!cpu_buffer->shortest_full || cpu_buffer->shortest_full > full) cpu_buffer->shortest_full = full; The writer will see full_waiters_pending and check if the ring buffer is filled over the percentage of the shortest_full value. If it is, it calls an irq_work to wake up all the waiters. But the code could get into a circular loop: CPU 0 CPU 1 ----- ----- [ Poll ] [ shortest_full = 0 ] rbwork->full_waiters_pending = true; if (rbwork->full_waiters_pending && [ buffer percent ] > shortest_full) { rbwork->wakeup_full = true; [ queue_irqwork ] cpu_buffer->shortest_full = full; [ IRQ work ] if (rbwork->wakeup_full) { cpu_buffer->shortest_full = 0; wakeup poll waiters; [woken] if ([ buffer percent ] > full) break; rbwork->full_waiters_pending = true; if (rbwork->full_waiters_pending && [ buffer percent ] > shortest_full) { rbwork->wakeup_full = true; [ queue_irqwork ] cpu_buffer->shortest_full = full; [ IRQ work ] if (rbwork->wakeup_full) { cpu_buffer->shortest_full = 0; wakeup poll waiters; [woken] [ Wash, rinse, repeat! ] In the poll, the shortest_full needs to be set before the full_pending_waiters, as once that is set, the writer will compare the current shortest_full (which is incorrect) to decide to call the irq_work, which will reset the shortest_full (expecting the readers to update it). Also move the setting of full_waiters_pending after the check if the ring buffer has the required percentage filled. There's no reason to tell the writer to wake up waiters if there are no waiters. Link: https://lore.kernel.org/linux-trace-kernel/[email protected] Cc: [email protected] Cc: Mark Rutland <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Andrew Morton <[email protected]> Fixes: 42fb0a1 ("tracing/ring-buffer: Have polling block on watermark") Reviewed-by: Masami Hiramatsu (Google) <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 761d947 commit 8145f1c

File tree

1 file changed

+20
-7
lines changed

1 file changed

+20
-7
lines changed

kernel/trace/ring_buffer.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -965,16 +965,32 @@ __poll_t ring_buffer_poll_wait(struct trace_buffer *buffer, int cpu,
965965
poll_wait(filp, &rbwork->full_waiters, poll_table);
966966

967967
raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
968-
rbwork->full_waiters_pending = true;
969968
if (!cpu_buffer->shortest_full ||
970969
cpu_buffer->shortest_full > full)
971970
cpu_buffer->shortest_full = full;
972971
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
973-
} else {
974-
poll_wait(filp, &rbwork->waiters, poll_table);
975-
rbwork->waiters_pending = true;
972+
if (full_hit(buffer, cpu, full))
973+
return EPOLLIN | EPOLLRDNORM;
974+
/*
975+
* Only allow full_waiters_pending update to be seen after
976+
* the shortest_full is set. If the writer sees the
977+
* full_waiters_pending flag set, it will compare the
978+
* amount in the ring buffer to shortest_full. If the amount
979+
* in the ring buffer is greater than the shortest_full
980+
* percent, it will call the irq_work handler to wake up
981+
* this list. The irq_handler will reset shortest_full
982+
* back to zero. That's done under the reader_lock, but
983+
* the below smp_mb() makes sure that the update to
984+
* full_waiters_pending doesn't leak up into the above.
985+
*/
986+
smp_mb();
987+
rbwork->full_waiters_pending = true;
988+
return 0;
976989
}
977990

991+
poll_wait(filp, &rbwork->waiters, poll_table);
992+
rbwork->waiters_pending = true;
993+
978994
/*
979995
* There's a tight race between setting the waiters_pending and
980996
* checking if the ring buffer is empty. Once the waiters_pending bit
@@ -990,9 +1006,6 @@ __poll_t ring_buffer_poll_wait(struct trace_buffer *buffer, int cpu,
9901006
*/
9911007
smp_mb();
9921008

993-
if (full)
994-
return full_hit(buffer, cpu, full) ? EPOLLIN | EPOLLRDNORM : 0;
995-
9961009
if ((cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer)) ||
9971010
(cpu != RING_BUFFER_ALL_CPUS && !ring_buffer_empty_cpu(buffer, cpu)))
9981011
return EPOLLIN | EPOLLRDNORM;

0 commit comments

Comments
 (0)