Re: [PATCH v2 5/7][next] printk: ringbuffer: add finalization/extension support

From: Petr Mladek
Date: Thu Aug 27 2020 - 11:17:23 EST


On Thu 2020-08-27 12:04:58, John Ogness wrote:
> On 2020-08-26, Petr Mladek <pmladek@xxxxxxxx> wrote:
> >> This series makes a very naive assumption that the previous
> >> descriptor is either in the reserved or committed queried states. The
> >> fact is, it can be in any of the 4 queried states. Adding support for
> >> finalization of all the states then gets quite complex, since any
> >> state transition (cmpxchg) may have to deal with an unexpected FINAL
> >> flag.
> >
> > It has to be done in two steps to avoid race:
> >
> > prb_commit()
> >
> > + set PRB_COMMIT_MASK
> > + check if it is still the last descriptor in the array
> > + set PRB_FINAL_MASK when it is not the last descriptor
> >
> > It should work because prb_reserve() finalizes the previous
> > descriptor after the new one is reserved. As a result:
> >
> > + prb_reserve() should either see PRB_COMMIT_MASK in the previous
> > descriptor and be able to finalize it.
> >
> > + or prb_commit() will see that the head moved and it is not
> > longer the last reserved one.
>
> I do not like the idea of relying on descriptors to finalize
> themselves. I worry that there might be some hole there. Failing to
> finalize basically disables printk, so that is pretty serious.
>
> Below is a patch against this series that adds support for finalizing
> all 4 queried states. It passes all my tests. Note that the code handles
> 2 corner cases:
>
> 1. When seq is 0, there is no previous descriptor to finalize. This
> exception is important because we don't want to finalize the -1
> placeholder. Otherwise, upon the first wrap, a descriptor will be
> prematurely finalized.
>
> 2. When a previous descriptor is being reserved for the first time, it
> might have a state_var value of 0 because the writer is still in
> prb_reserve() and has not set the initial value yet. I added
> considerable comments on this special case.
>
> I am comfortable with adding this new code, although it clearly adds
> complexity.
>
> John Ogness
>
> diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c
> index 90d48973ac9e..1ed1e9eb930f 100644
> --- a/kernel/printk/printk_ringbuffer.c
> +++ b/kernel/printk/printk_ringbuffer.c
> @@ -967,10 +976,25 @@ static bool desc_reserve(struct printk_ringbuffer *rb, unsigned long *id_out)
> * any other changes. A write memory barrier is sufficient for this.
> * This pairs with desc_read:D.
> */
> - if (!atomic_long_try_cmpxchg(&desc->state_var, &prev_state_val,
> - id | 0)) { /* LMM(desc_reserve:F) */
> - WARN_ON_ONCE(1);
> - return false;
> + if (is_final)
> + state_val = id | 0 | DESC_FINAL_MASK;
> + else
> + state_val = id | 0;
> + if (atomic_long_cmpxchg(&desc->state_var, prev_state_val,
> + state_val) != prev_state_val) { /* LMM(desc_reserve:F) */
> + /*
> + * This reusable descriptor must have been finalized already.
> + * Retry with a reusable+final expected value.
> + */
> + prev_state_val |= DESC_FINAL_MASK;
> + state_val |= DESC_FINAL_MASK;
> +
> + if (!atomic_long_try_cmpxchg(&desc->state_var, &prev_state_val,
> + state_val)) { /* LMM(desc_reserve:FIXME) */
> +
> + WARN_ON_ONCE(1);
> + return false;

IMHO, this need to be done in a cycle until is succeeds. The previous
descriptor might get reopened or commited several times in the mean time.

I have played with the code from the original patchset in parallel
and came with alreanative solution that uses DESC_FINAL_MASK
as I originally thought. See below.

I still have to shake my head around both approaches.

Anwyay, here is my code against the original patchset: