Re: [PATCH v2] ring-buffer: Handle a reader taking the new head in rb_handle_head_page()
From: Vincent Donnefort
Date: Wed Nov 11 2026 - 08:46:18 EST
On Tue, Nov 10, 2026 at 02:52:02AM -0500, Steven Rostedt wrote:
> On Mon, 9 Nov 2026 09:21:32 +0000
> Vincent Donnefort <vdonnefort@xxxxxxxxxx> wrote:
>
> > > The issue I think you hit was that we have two HEAD pages, when interrupts
> > > come in. That is, we can get a state of:
> > >
> > > --> subbuf --UPDATE--> subbuf --> subbuf --HEAD--> subbuf -->
> >
> > How we can end-up in this situation? The cmpxch will only swap HEAD for UPDATE.
>
> The cpu_buffer->head_page is only updated by the reader, when it did its last read.
>
> Let's look at it one at a time:
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf ----> subbuf --HEAD--> subbuf ----> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------+
>
> Then the writer moves the HEAD page around the buffer while there are
> no reads happening
>
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf ----> subbuf ----> subbuf --HEAD--> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------+
>
> cpu_buffer->head_page
> | tail
> v v
> +--> subbuf ----> subbuf ----> subbuf ----> subbuf --HEAD--> subbuf --+
> | |
> +---------------------------------------------------------------------+
>
> cpu_buffer->head_page
> | tail
> v v
> +-HEAD-> subbuf ----> subbuf ----> subbuf ----> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------+
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf --HEAD--> subbuf ----> subbuf ----> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------+
>
> Now, I'll break up the update part:
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf --UPDATE--> subbuf ----> subbuf ----> subbuf ----> subbuf --+
> | |
> +-----------------------------------------------------------------------+
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf --UPDATE--> subbuf --HEAD--> subbuf ----> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------------+
>
> An interrupt comes in and moves the head even more.
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf --UPDATE--> subbuf ----> subbuf --HEAD--> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------------+
>
> Now the reader decides to read again and starts between the UPDATE and the HEAD
>
>
> cpu_buffer->head_page
> tail |
> v v
> +--> subbuf --UPDATE--> subbuf ----> subbuf --HEAD--> subbuf ----> subbuf --+
> | |
> +---------------------------------------------------------------------------+
>
> It finds a page with the HEAD pointer set but not the UPDATE pointer and does a swap!
>
>
> reader_page -----+
> |
> cpu_buffer->head_page |
> tail | |
> v v v
> +--> subbuf --UPDATE--> subbuf ----> subbuf ----> subbuf --HEAD--> subbuf --+
> | |
> +---------------------------------------------------------------------------+
>
> Now the commit finishes and moves forward and will even hit a blank
> page but the last write is now on the reader page. This breaks the
> entire integrity of the ring buffer.
>
> Does this make sense?
>
> -- Steve
Yes, now I spent more time on it, I believe it makes sense. I looked at what we
could do and I'm trying to use a temporary RB_PAGE_HEAD | RB_PAGE_UPDATE. This
link flag is then only converted to RB_PAGE_HEAD by the outer writer. I believe
this would fix the race entirely... However that means the reader spins as long
as the buffer writing is interrupted, which I admit is far from ideal.
--
Vincent