Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-04 Thread Petr Mladek
On Tue 2020-03-03 16:42:07, John Ogness wrote:
> On 2020-03-03, Petr Mladek  wrote:
> >> diff --git a/kernel/printk/printk_ringbuffer.c 
> >> b/kernel/printk/printk_ringbuffer.c
> >> new file mode 100644
> >> index ..796257f226ee
> >> --- /dev/null
> >> +++ b/kernel/printk/printk_ringbuffer.c
> >> +/*
> >> + * Read the record @id and verify that it is committed and has the 
> >> sequence
> >> + * number @seq. On success, 0 is returned.
> >> + *
> >> + * Error return values:
> >> + * -EINVAL: A committed record @seq does not exist.
> >> + * -ENOENT: The record @seq exists, but its data is not available. 
> >> This is a
> >> + *  valid record, so readers should continue with the next 
> >> seq.
> >> + */
> >> +static int desc_read_committed(struct prb_desc_ring *desc_ring,
> >> + unsigned long id, u64 seq,
> >> + struct prb_desc *desc)
> >> +{
> >>>
> @id _is_ very important because that is how descriptors are
> read. desc_read() takes @id as an argument and it is @id that identifies
> the descriptor. @seq is only meta-data within a descriptor. The only
> reason @seq is even checked is because of possible ABA issues with @id
> on 32-bit systems.

I think that the different view is because I look at this API
from the reader API side. It is called the following way:

prb_read_valid(, seq, )
  _prb_read_valid( , &seq, )
prb_read( , *seq, )
# id is read from address defined by seq
rdesc = dr->descs[seq & MASK];
id = rdesc->state_var && MASK_ID;

desc_read_commited( , id, seq, )
  desc_read( , id, )
# desc is the same as rdesc above because
# seq & MASK == id & MASK
desc = dr->descs[id & MASK];

Note that prb_read_valid() and prb_read() are addressed by seq.

It would be perfectly fine to pass only seq to desc_read_committed()
and read id from inside.

The name desc_read_committed() suggests that the important condition
is that the descriptor is in the committed state. It is not obvious
that seq is important as well.

>From my POV, it will be more clear to pass only seq and rename the
function to desc_read_by_seq() or so:

  + seq is enough for addressing
  + function returns true only when the stored seq matches
  + the stored seq is valid only when the state is committed
or reusable


Please, do not reply to this mail. Either take the idea or keep
the code as is. I could live with it. And it is not important
enough to spend more time on it. I just wanted to explain my view.
But it is obviously just a personal preference.

Best Regards,
Petr

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-04 Thread Petr Mladek
On Mon 2020-03-02 14:43:41, John Ogness wrote:
> On 2020-03-02, Petr Mladek  wrote:
>  diff --git a/kernel/printk/printk_ringbuffer.c 
>  b/kernel/printk/printk_ringbuffer.c
>  new file mode 100644
>  index ..796257f226ee
>  --- /dev/null
>  +++ b/kernel/printk/printk_ringbuffer.c
>  +/*
>  + * Read the record @id and verify that it is committed and has the 
>  sequence
>  + * number @seq. On success, 0 is returned.
>  + *
>  + * Error return values:
>  + * -EINVAL: A committed record @seq does not exist.
>  + * -ENOENT: The record @seq exists, but its data is not available. This 
>  is a
>  + *  valid record, so readers should continue with the next seq.
>  + */
>  +static int desc_read_committed(struct prb_desc_ring *desc_ring,
>  +   unsigned long id, u64 seq,
>  +   struct prb_desc *desc)
>  +{
> >
> > static enum desc_state
> > desc_read_by_seq(struct prb_desc_ring *desc_ring,
> >  u64 seq, struct prb_desc *desc)
> > {
> > struct prb_desc *rdesc = to_desc(desc_ring, seq);
> > atomic_long_t *state_var = &rdesc->state_var;
> > id = DESC_ID(atomic_long_read(state_var));
> 
> I think it is error-prone to re-read @state_var here. It is lockless
> shared data. desc_read_committed() is called twice in prb_read() and it
> is expected that both calls are using the same @id.
> 
> > enum desc_state d_state;
> >
> > d_state = desc_read(desc_ring, id, desc);
> > if (d_state == desc_miss ||
> > d_state == desc_reserved ||
> > desc->info.seq != seq)
> > return -EINVAL;
> >
> > if (d_state == desc_reusable)
> > return -ENOENT;
> 
> I can use this refactoring.
> 
> >
> > if (d_state != desc_committed)
> > return -EINVAL;
> 
> I suppose you meant to remove this check and leave in the @blk_lpos
> check instead. If we're trying to minimize lines of code, the @blk_lpos
> check could be combined with the "== desc_reusable" check as well.

I am an idiot. I missed that the check "d_state != desc_committed"
will return -EINVAL also when desc_miss or desc_reserved.

I was too concentrated by the fact that desc->info.seq was checked
first even though it might contain garbage.

Also it did not help me much the note about blk_lpos. I did not
see how it was related to this code.

To sum up. The original code worked fine. But I would prefer my variant
that has more lines but it is somehow cleaner.

Best Regards,
Petr

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-03 Thread John Ogness
On 2020-03-03, Petr Mladek  wrote:
>> diff --git a/kernel/printk/printk_ringbuffer.c 
>> b/kernel/printk/printk_ringbuffer.c
>> new file mode 100644
>> index ..796257f226ee
>> --- /dev/null
>> +++ b/kernel/printk/printk_ringbuffer.c
>> +/*
>> + * Read the record @id and verify that it is committed and has the 
>> sequence
>> + * number @seq. On success, 0 is returned.
>> + *
>> + * Error return values:
>> + * -EINVAL: A committed record @seq does not exist.
>> + * -ENOENT: The record @seq exists, but its data is not available. This 
>> is a
>> + *  valid record, so readers should continue with the next seq.
>> + */
>> +static int desc_read_committed(struct prb_desc_ring *desc_ring,
>> +   unsigned long id, u64 seq,
>> +   struct prb_desc *desc)
>> +{
>>>
>>> OK, what about having desc_read_by_seq() instead?
>> 
>> Well, it isn't actually "reading by seq". @seq is there for
>> additional verification. Yes, prb_read() is deriving @id from
>> @seq. But it only does this once and uses that value for both calls.
>
> I do not want to nitpick about words. If I get it properly,
> the "id" is not important here. Any "id" is fine as long as
> "seq" matches. Reading "id" once is just an optimization.

Your statement is incorrect. We are not nitpicking about words. I am
trying to clarify what you are misunderstanding.

@id _is_ very important because that is how descriptors are
read. desc_read() takes @id as an argument and it is @id that identifies
the descriptor. @seq is only meta-data within a descriptor. The only
reason @seq is even checked is because of possible ABA issues with @id
on 32-bit systems.

> I do not resist on the change. It was just an idea how to
> avoid confusion. I was confused more than once. But I might
> be the only one. The more strightforward code looked more
> important to me than the optimization.

I am sorry for the confusion. In preparation for v2 I have changed the
function description to:

/*
 * Get a copy of a specified descriptor and verify that the record is
 * committed and has the sequence number @seq. @seq is checked because
 * of possible ABA issues with @id on 32-bit systems. On success, 0 is
 * returned.
 *
 * Error return values:
 * -EINVAL: A committed record @seq does not exist.
 * -ENOENT: The record @seq exists, but its data is not available. This is a
 *  valid record, so readers should continue with the next seq.
 */

This is using the same language as the description of desc_read() so
that is it is hopefully clear that desc_read_committed() is an extended
version of desc_read().

>>> Also there is a bug in current desc_read_commited().
>>> desc->info.seq might contain a garbage when d_state is desc_miss
>>> or desc_reserved.
>> 
>> It is not a bug. In both of those cases, -EINVAL is the correct return
>> value.
>
> No, it is a bug. If info is not read and contains garbage then the
> following check may pass by chance:
>
>   if (desc->info.seq != seq)
>   return -EINVAL;
>
> Then the function would return 0 even when desc_read() returned
> desc_miss or desc_reserved.

0 cannot be returned. The state is checked. Please let us stop this
bug/non-bug discussion. It is distracting us from clarifying this
function and refactoring it to simplify understanding.

>>> I would change it to:
>>>
>>> static enum desc_state
>>> desc_read_by_seq(struct prb_desc_ring *desc_ring,
>>>  u64 seq, struct prb_desc *desc)
>>> {
>>> struct prb_desc *rdesc = to_desc(desc_ring, seq);
>>> atomic_long_t *state_var = &rdesc->state_var;
>>> id = DESC_ID(atomic_long_read(state_var));
>> 
>> I think it is error-prone to re-read @state_var here. It is lockless
>> shared data. desc_read_committed() is called twice in prb_read() and
>> it is expected that both calls are using the same @id.
>
> It is not error prone. If "id" changes then "seq" will not match.

@id is set during prb_reserve(). @seq (being mere meta-data) is set
_afterwards_. Your proposed multiple-deriving of @id from @seq would
work because the _state checks_ would catch it, not because @seq would
necessarily change.

But that logic is backwards. @seq is not what is important here. It is
only meta-data. On 64-bit systems the @seq checks could be safely
removed.

You may want to refer back to your private email [0] from last November
where you asked me to move this code out of prb_read() and into a helper
function. That may clarify what we are talking about (although I hope
the new function description is clear enough).

John Ogness

[0] private: 20191122122724.n6wlummg3ap56...@pathway.suse.cz

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-03 Thread Petr Mladek
On Mon 2020-03-02 14:43:41, John Ogness wrote:
> On 2020-03-02, Petr Mladek  wrote:
>  diff --git a/kernel/printk/printk_ringbuffer.c 
>  b/kernel/printk/printk_ringbuffer.c
>  new file mode 100644
>  index ..796257f226ee
>  --- /dev/null
>  +++ b/kernel/printk/printk_ringbuffer.c
>  +/*
>  + * Read the record @id and verify that it is committed and has the 
>  sequence
>  + * number @seq. On success, 0 is returned.
>  + *
>  + * Error return values:
>  + * -EINVAL: A committed record @seq does not exist.
>  + * -ENOENT: The record @seq exists, but its data is not available. This 
>  is a
>  + *  valid record, so readers should continue with the next seq.
>  + */
>  +static int desc_read_committed(struct prb_desc_ring *desc_ring,
>  +   unsigned long id, u64 seq,
>  +   struct prb_desc *desc)
>  +{
> >
> > OK, what about having desc_read_by_seq() instead?
> 
> Well, it isn't actually "reading by seq". @seq is there for additional
> verification. Yes, prb_read() is deriving @id from @seq. But it only
> does this once and uses that value for both calls.

I do not want to nitpick about words. If I get it properly,
the "id" is not important here. Any "id" is fine as long as
"seq" matches. Reading "id" once is just an optimization.

I do not resist on the change. It was just an idea how to
avoid confusion. I was confused more than once. But I might
be the only one. The more strightforward code looked more
important to me than the optimization.


> > Also there is a bug in current desc_read_commited().
> > desc->info.seq might contain a garbage when d_state is desc_miss
> > or desc_reserved.
> 
> It is not a bug. In both of those cases, -EINVAL is the correct return
> value.

No, it is a bug. If info is not read and contains garbage then the
following check may pass by chance:

if (desc->info.seq != seq)
return -EINVAL;

Then the function would return 0 even when desc_read() returned
desc_miss or desc_reserved.


> > I would change it to:
> >
> > static enum desc_state
> > desc_read_by_seq(struct prb_desc_ring *desc_ring,
> >  u64 seq, struct prb_desc *desc)
> > {
> > struct prb_desc *rdesc = to_desc(desc_ring, seq);
> > atomic_long_t *state_var = &rdesc->state_var;
> > id = DESC_ID(atomic_long_read(state_var));
> 
> I think it is error-prone to re-read @state_var here. It is lockless
> shared data. desc_read_committed() is called twice in prb_read() and it
> is expected that both calls are using the same @id.

It is not error prone. If "id" changes then "seq" will not match.

> > enum desc_state d_state;
> >
> > d_state = desc_read(desc_ring, id, desc);
> > if (d_state == desc_miss ||
> > d_state == desc_reserved ||
> > desc->info.seq != seq)
> > return -EINVAL;
> >
> > if (d_state == desc_reusable)
> > return -ENOENT;
> 
> I can use this refactoring.

Yes please, "else" is not needed.

> >
> > if (d_state != desc_committed)
> > return -EINVAL;
> 
> I suppose you meant to remove this check and leave in the @blk_lpos
> check instead.

Good catch, this check is superfluous.

> If we're trying to minimize lines of code, the @blk_lpos
> check could be combined with the "== desc_reusable" check as well.

Minimizing the lines of code was not my primary goal. I was just
confused by the function name. Also the fact that "seq" was the
important thing was well hidden.

Best Regards,
Petr

PS: I dived into the barriers and got lost. I hope that I will
be able to send something sensible in the end ;-)

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-02 Thread John Ogness
On 2020-03-02, Petr Mladek  wrote:
 diff --git a/kernel/printk/printk_ringbuffer.c 
 b/kernel/printk/printk_ringbuffer.c
 new file mode 100644
 index ..796257f226ee
 --- /dev/null
 +++ b/kernel/printk/printk_ringbuffer.c
 +/*
 + * Read the record @id and verify that it is committed and has the 
 sequence
 + * number @seq. On success, 0 is returned.
 + *
 + * Error return values:
 + * -EINVAL: A committed record @seq does not exist.
 + * -ENOENT: The record @seq exists, but its data is not available. This 
 is a
 + *  valid record, so readers should continue with the next seq.
 + */
 +static int desc_read_committed(struct prb_desc_ring *desc_ring,
 + unsigned long id, u64 seq,
 + struct prb_desc *desc)
 +{
>
> OK, what about having desc_read_by_seq() instead?

Well, it isn't actually "reading by seq". @seq is there for additional
verification. Yes, prb_read() is deriving @id from @seq. But it only
does this once and uses that value for both calls.

> Also there is a bug in current desc_read_commited().
> desc->info.seq might contain a garbage when d_state is desc_miss
> or desc_reserved.

It is not a bug. In both of those cases, -EINVAL is the correct return
value.

> I would change it to:
>
> static enum desc_state
> desc_read_by_seq(struct prb_desc_ring *desc_ring,
>u64 seq, struct prb_desc *desc)
> {
>   struct prb_desc *rdesc = to_desc(desc_ring, seq);
>   atomic_long_t *state_var = &rdesc->state_var;
>   id = DESC_ID(atomic_long_read(state_var));

I think it is error-prone to re-read @state_var here. It is lockless
shared data. desc_read_committed() is called twice in prb_read() and it
is expected that both calls are using the same @id.

>   enum desc_state d_state;
>
>   d_state = desc_read(desc_ring, id, desc);
>   if (d_state == desc_miss ||
>   d_state == desc_reserved ||
>   desc->info.seq != seq)
>   return -EINVAL;
>
>   if (d_state == desc_reusable)
>   return -ENOENT;

I can use this refactoring.

>
>   if (d_state != desc_committed)
>   return -EINVAL;

I suppose you meant to remove this check and leave in the @blk_lpos
check instead. If we're trying to minimize lines of code, the @blk_lpos
check could be combined with the "== desc_reusable" check as well.

>
>   return 0;
> }

Thanks.

John Ogness

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-02 Thread Petr Mladek
On Mon 2020-03-02 11:38:42, John Ogness wrote:
> On 2020-02-21, Petr Mladek  wrote:
> >> diff --git a/kernel/printk/printk_ringbuffer.c 
> >> b/kernel/printk/printk_ringbuffer.c
> >> new file mode 100644
> >> index ..796257f226ee
> >> --- /dev/null
> >> +++ b/kernel/printk/printk_ringbuffer.c
> >> +/*
> >> + * Read the record @id and verify that it is committed and has the 
> >> sequence
> >> + * number @seq. On success, 0 is returned.
> >> + *
> >> + * Error return values:
> >> + * -EINVAL: A committed record @seq does not exist.
> >> + * -ENOENT: The record @seq exists, but its data is not available. This 
> >> is a
> >> + *  valid record, so readers should continue with the next seq.
> >> + */
> >> +static int desc_read_committed(struct prb_desc_ring *desc_ring,
> >> + unsigned long id, u64 seq,
> >> + struct prb_desc *desc)
> >> +{
> >
> > I was few times confused whether this function reads the descriptor
> > a safe way or not.
> >
> > Please, rename it to make it clear that does only a check.
> > For example, check_state_commited().
> 
> This function _does_ read. It is a helper function of prb_read() to
> _read_ the descriptor. It is an extended version of desc_read() that
> also performs various checks that the descriptor is committed.

I see.

> I will update the function description to be more similar to desc_read()
> so that it is obvious that it is "getting a copy of a specified
> descriptor".

OK, what about having desc_read_by_seq() instead?

Also there is a bug in current desc_read_commited().
desc->info.seq might contain a garbage when d_state is desc_miss
or desc_reserved.

I would change it to:

static enum desc_state
desc_read_by_seq(struct prb_desc_ring *desc_ring,
 u64 seq, struct prb_desc *desc)
{
struct prb_desc *rdesc = to_desc(desc_ring, seq);
atomic_long_t *state_var = &rdesc->state_var;
id = DESC_ID(atomic_long_read(state_var));
enum desc_state d_state;

d_state = desc_read(desc_ring, id, desc);
if (d_state == desc_miss ||
d_state == desc_reserved ||
desc->info.seq != seq)
return -EINVAL;

if (d_state == desc_reusable)
return -ENOENT;

if (d_state != desc_committed)
return -EINVAL;

return 0;
}

Best Regards,
Petr

PS: I am going to dive into the barriers again to answer the last
letter about them.

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-02 Thread Joe Perches
On Mon, 2020-03-02 at 11:38 +0100, John Ogness wrote:
> On 2020-02-21, Petr Mladek  wrote:
> > > diff --git a/kernel/printk/printk_ringbuffer.c 
> > > b/kernel/printk/printk_ringbuffer.c
[]
> > > +static struct prb_data_block *to_block(struct prb_data_ring *data_ring,
> > > +unsigned long begin_lpos)
> > > +{
> > > + char *data = &data_ring->data[DATA_INDEX(data_ring, begin_lpos)];
> > > +
> > > + return (struct prb_data_block *)data;
> > 
> > Nit: Please, use "blk" instead of "data". I was slightly confused
> > because "data" is also one member of struct prb_data_block.
> 
> OK.

trivia:

Perhaps use void * instead of char * and a direct return
and avoid the naming altogether.

static struct prb_data_block *to_block(struct prb_data_ring *data_ring, 
   unsigned long begin_lpos)
{
return (void *)&data_ring->data[DATA_INDEX(data_ring, begin_lpos)];
}


___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


Re: misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-03-02 Thread John Ogness
On 2020-02-21, Petr Mladek  wrote:
>> diff --git a/kernel/printk/printk_ringbuffer.c 
>> b/kernel/printk/printk_ringbuffer.c
>> new file mode 100644
>> index ..796257f226ee
>> --- /dev/null
>> +++ b/kernel/printk/printk_ringbuffer.c
>> +static struct prb_data_block *to_block(struct prb_data_ring *data_ring,
>> +   unsigned long begin_lpos)
>> +{
>> +char *data = &data_ring->data[DATA_INDEX(data_ring, begin_lpos)];
>> +
>> +return (struct prb_data_block *)data;
>
> Nit: Please, use "blk" instead of "data". I was slightly confused
> because "data" is also one member of struct prb_data_block.

OK.

>> +/* The possible responses of a descriptor state-query. */
>> +enum desc_state {
>> +desc_miss,  /* ID mismatch */
>> +desc_reserved,  /* reserved, but still in use by writer */
>> +desc_committed, /* committed, writer is done */
>> +desc_reusable,  /* free, not used by any writer */
>
> s/not used/not yet used/

OK.

>> +EXPORT_SYMBOL(prb_reserve);
>
> Please, do not export symbols if there are no plans to actually
> use them from modules. It will be easier to rework the code
> in the future. Nobody would need to worry about external
> users.
>
> Please, do so everywhere in the patchset.

You are correct.

The reason I exported them is that I could run my test module. But since
the test module will not be part of the kernel source, I'll just hack
the exports in when doing my testing.

>> +static char *get_data(struct prb_data_ring *data_ring,
>> +  struct prb_data_blk_lpos *blk_lpos,
>> +  unsigned long *data_size)
>> +{
>> +struct prb_data_block *db;
>> +
>> +/* Data-less data block description. */
>> +if (blk_lpos->begin == INVALID_LPOS &&
>> +blk_lpos->next == INVALID_LPOS) {
>> +return NULL;
>
> Nit: There is no need for "else" after return. checkpatch.pl usually
> complains about it ;-)

OK.

>> +/*
>> + * Read the record @id and verify that it is committed and has the sequence
>> + * number @seq. On success, 0 is returned.
>> + *
>> + * Error return values:
>> + * -EINVAL: A committed record @seq does not exist.
>> + * -ENOENT: The record @seq exists, but its data is not available. This is a
>> + *  valid record, so readers should continue with the next seq.
>> + */
>> +static int desc_read_committed(struct prb_desc_ring *desc_ring,
>> +   unsigned long id, u64 seq,
>> +   struct prb_desc *desc)
>> +{
>
> I was few times confused whether this function reads the descriptor
> a safe way or not.
>
> Please, rename it to make it clear that does only a check.
> For example, check_state_commited().

This function _does_ read. It is a helper function of prb_read() to
_read_ the descriptor. It is an extended version of desc_read() that
also performs various checks that the descriptor is committed.

I will update the function description to be more similar to desc_read()
so that it is obvious that it is "getting a copy of a specified
descriptor".

John Ogness

___
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec


misc nits Re: [PATCH 1/2] printk: add lockless buffer

2020-02-21 Thread Petr Mladek
Hi,

there are few more small things that catched my eyes during review.
They are from the nits deparment.

On Tue 2020-01-28 17:25:47, John Ogness wrote:
> Introduce a multi-reader multi-writer lockless ringbuffer for storing
> the kernel log messages. Readers and writers may use their API from
> any context (including scheduler and NMI). This ringbuffer will make
> it possible to decouple printk() callers from any context, locking,
> or console constraints. It also makes it possible for readers to have
> full access to the ringbuffer contents at any time and context (for
> example from any panic situation).
> 
> diff --git a/kernel/printk/printk_ringbuffer.c 
> b/kernel/printk/printk_ringbuffer.c
> new file mode 100644
> index ..796257f226ee
> --- /dev/null
> +++ b/kernel/printk/printk_ringbuffer.c
> +/**
> + * DOC: printk_ringbuffer overview

I really like the overview.

> +/* A data block: maps to the raw data within the data ring. */
> +struct prb_data_block {
> + unsigned long   id;
> + chardata[0];
> +};
> +
> +
> +static struct prb_data_block *to_block(struct prb_data_ring *data_ring,
> +unsigned long begin_lpos)
> +{
> + char *data = &data_ring->data[DATA_INDEX(data_ring, begin_lpos)];
> +
> + return (struct prb_data_block *)data;

Nit: Please, use "blk" instead of "data". I was slightly confused
because "data" is also one member of struct prb_data_block.


> +/* The possible responses of a descriptor state-query. */
> +enum desc_state {
> + desc_miss,  /* ID mismatch */
> + desc_reserved,  /* reserved, but still in use by writer */
> + desc_committed, /* committed, writer is done */
> + desc_reusable,  /* free, not used by any writer */

s/not used/not yet used/

> +};

[...]

> +EXPORT_SYMBOL(prb_reserve);

Please, do not export symbols if there are no plans to actually
use them from modules. It will be easier to rework the code
in the future. Nobody would need to worry about external
users.

Please, do so everywhere in the patchset.

> +/*
> + * Given @blk_lpos, return a pointer to the raw data from the data block
> + * and calculate the size of the data part. A NULL pointer is returned
> + * if @blk_lpos specifies values that could never be legal.
> + *
> + * This function (used by readers) performs strict validation on the lpos
> + * values to possibly detect bugs in the writer code. A WARN_ON_ONCE() is
> + * triggered if an internal error is detected.
> + */
> +static char *get_data(struct prb_data_ring *data_ring,
> +   struct prb_data_blk_lpos *blk_lpos,
> +   unsigned long *data_size)
> +{
> + struct prb_data_block *db;
> +
> + /* Data-less data block description. */
> + if (blk_lpos->begin == INVALID_LPOS &&
> + blk_lpos->next == INVALID_LPOS) {
> + return NULL;

Nit: There is no need for "else" after return. checkpatch.pl usually
complains about it ;-)

> +
> + /* Regular data block: @begin less than @next and in same wrap. */
> + } else if (DATA_WRAPS(data_ring, blk_lpos->begin) ==
> +DATA_WRAPS(data_ring, blk_lpos->next) &&
> +blk_lpos->begin < blk_lpos->next) {
> + db = to_block(data_ring, blk_lpos->begin);
> + *data_size = blk_lpos->next - blk_lpos->begin;
> +
> + /* Wrapping data block: @begin is one wrap behind @next. */
> + } else if (DATA_WRAPS(data_ring,
> +   blk_lpos->begin + DATA_SIZE(data_ring)) ==
> +DATA_WRAPS(data_ring, blk_lpos->next)) {
> + db = to_block(data_ring, 0);
> + *data_size = DATA_INDEX(data_ring, blk_lpos->next);
> +
> + /* Illegal block description. */
> + } else {
> + WARN_ON_ONCE(1);
> + return NULL;
> + }
> +
> + /* A valid data block will always be aligned to the ID size. */
> + if (WARN_ON_ONCE(blk_lpos->begin !=
> +  ALIGN(blk_lpos->begin, sizeof(db->id))) ||
> + WARN_ON_ONCE(blk_lpos->next !=
> +  ALIGN(blk_lpos->next, sizeof(db->id {
> + return NULL;
> + }
> +
> + /* A valid data block will always have at least an ID. */
> + if (WARN_ON_ONCE(*data_size < sizeof(db->id)))
> + return NULL;
> +
> + /* Subtract descriptor ID space from size to reflect data size. */
> + *data_size -= sizeof(db->id);
> +
> + return &db->data[0];
> +}
> +
> +/*
> + * Read the record @id and verify that it is committed and has the sequence
> + * number @seq. On success, 0 is returned.
> + *
> + * Error return values:
> + * -EINVAL: A committed record @seq does not exist.
> + * -ENOENT: The record @seq exists, but its data is not available. This is a
> + *  valid record, so readers should continue with the next seq.
> + */
> +static int desc_read_committed(struct prb_desc_ring *desc_ring,
> +unsigned long id