On 1/9/26 10:06, Simon Schippers wrote:
> On 1/9/26 09:31, Michael S. Tsirkin wrote:
>> On Fri, Jan 09, 2026 at 08:35:31AM +0100, Simon Schippers wrote:
>>> On 1/9/26 08:22, Michael S. Tsirkin wrote:
>>>> On Wed, Jan 07, 2026 at 10:04:41PM +0100, Simon Schippers wrote:
>>>>> This proposed function checks whether __ptr_ring_zero_tail() was invoked
>>>>> within the last n calls to __ptr_ring_consume(), which indicates that new
>>>>> free space was created. Since __ptr_ring_zero_tail() moves the tail to
>>>>> the head - and no other function modifies either the head or the tail,
>>>>> aside from the wrap-around case described below - detecting such a
>>>>> movement is sufficient to detect the invocation of
>>>>> __ptr_ring_zero_tail().
>>>>>
>>>>> The implementation detects this movement by checking whether the tail is
>>>>> at most n positions behind the head. If this condition holds, the shift
>>>>> of the tail to its current position must have occurred within the last n
>>>>> calls to __ptr_ring_consume(), indicating that __ptr_ring_zero_tail() was
>>>>> invoked and that new free space was created.
>>>>>
>>>>> This logic also correctly handles the wrap-around case in which
>>>>> __ptr_ring_zero_tail() is invoked and the head and the tail are reset
>>>>> to 0. Since this reset likewise moves the tail to the head, the same
>>>>> detection logic applies.
>>>>>
>>>>> Co-developed-by: Tim Gebauer <[email protected]>
>>>>> Signed-off-by: Tim Gebauer <[email protected]>
>>>>> Signed-off-by: Simon Schippers <[email protected]>
>>>>> ---
>>>>> include/linux/ptr_ring.h | 13 +++++++++++++
>>>>> 1 file changed, 13 insertions(+)
>>>>>
>>>>> diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h
>>>>> index a5a3fa4916d3..7cdae6d1d400 100644
>>>>> --- a/include/linux/ptr_ring.h
>>>>> +++ b/include/linux/ptr_ring.h
>>>>> @@ -438,6 +438,19 @@ static inline int ptr_ring_consume_batched_bh(struct
>>>>> ptr_ring *r,
>>>>> return ret;
>>>>> }
>>>>>
>>>>> +/* Returns true if the consume of the last n elements has created space
>>>>> + * in the ring buffer (i.e., a new element can be produced).
>>>>> + *
>>>>> + * Note: Because of batching, a successful call to __ptr_ring_consume() /
>>>>> + * __ptr_ring_consume_batched() does not guarantee that the next call to
>>>>> + * __ptr_ring_produce() will succeed.
>>>>
>>>>
>>>> I think the issue is it does not say what is the actual guarantee.
>>>>
>>>> Another issue is that the "Note" really should be more prominent,
>>>> it really is part of explaining what the functions does.
>>>>
>>>> Hmm. Maybe we should tell it how many entries have been consumed and
>>>> get back an indication of how much space this created?
>>>>
>>>> fundamentally
>>>> n - (r->consumer_head - r->consumer_tail)?
>>>
>>> No, that is wrong from my POV.
>>>
>>> It always creates the same amount of space which is the batch size or
>>> multiple batch sizes (or something less in the wrap-around case). That is
>>> of course only if __ptr_ring_zero_tail() was executed at least once,
>>> else it creates zero space.
>>
>> exactly, and caller does not know, and now he wants to know so
>> we add an API for him to find out?
>>
>> I feel the fact it's a binary (batch or 0) is an implementation
>> detail better hidden from user.
>
> I agree, and I now understood your logic :)
>
> So it should be:
>
> static inline int __ptr_ring_consume_created_space(struct ptr_ring *r,
> int n)
> {
> return max(n - (r->consumer_head - r->consumer_tail), 0);
> }
>
> Right?
BTW:
No, that's still not correct. It misses the elements between the tail and
head that existed before the consume operation (called pre_consume_gap in
the code below).
After thinking about it a bit more, the best solution I came up with is:
static inline int __ptr_ring_consume_created_space(struct ptr_ring *r,
int n)
{
int pre_consume_gap = (r->head - n) % r->size % r->batch;
return n - (r->head - r->tail) + pre_consume_gap;
}
Here, (r->head - n) represents the head position before the consume, but
it may be negative. The first modulo normalizes it to a positive value in
the range [0, size). Applying the modulo batch to the pre-consume head
position then yields the number of elements that were between the tail
and head before the consume.
With this approach, we no longer need max(..., 0), because if
n < (r->head - r->tail), the + pre_consume_gap term cancels it out.
Is this solution viable in terms of performance regarding the modulo
operations?
>
>>
>>
>>
>>>>
>>>>
>>>> does the below sound good maybe?
>>>>
>>>> /* Returns the amound of space (number of new elements that can be
>>>> * produced) that calls to ptr_ring_consume created.
>>>> *
>>>> * Getting n entries from calls to ptr_ring_consume() /
>>>> * ptr_ring_consume_batched() does *not* guarantee that the next n calls to
>>>> * ptr_ring_produce() will succeed.
>>>> *
>>>> * Use this function after consuming n entries to get a hint about
>>>> * how much space was actually created.
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>> + */
>>>>> +static inline bool __ptr_ring_consume_created_space(struct ptr_ring *r,
>>>>> + int n)
>>>>> +{
>>>>> + return r->consumer_head - r->consumer_tail < n;
>>>>> +}
>>>>> +
>>>>> /* Cast to structure type and call a function without discarding from
>>>>> FIFO.
>>>>> * Function must return a value.
>>>>> * Callers must take consumer_lock.
>>>>> --
>>>>> 2.43.0
>>>>
>>