The implementation is inspired by ptr_ring_empty.
Co-developed-by: Tim Gebauer <tim.gebauer@tu-dortmund.de>
Signed-off-by: Tim Gebauer <tim.gebauer@tu-dortmund.de>
Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de>
---
include/linux/ptr_ring.h | 71 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h
index 551329220e4f..6b8cfaecf478 100644
--- a/include/linux/ptr_ring.h
+++ b/include/linux/ptr_ring.h
@@ -243,6 +243,77 @@ static inline bool ptr_ring_empty_bh(struct ptr_ring *r)
return ret;
}
+/*
+ * Check if a spare capacity of cnt is available without taking any locks.
+ *
+ * If cnt==0 or cnt > r->size it acts the same as __ptr_ring_empty.
+ *
+ * The same requirements apply as described for __ptr_ring_empty.
+ */
+static inline bool __ptr_ring_spare(struct ptr_ring *r, int cnt)
+{
+ int size = r->size;
+ int to_check;
+
+ if (unlikely(!size || cnt < 0))
+ return true;
+
+ if (cnt > size)
+ cnt = 0;
+
+ to_check = READ_ONCE(r->consumer_head) - cnt;
+
+ if (to_check < 0)
+ to_check += size;
+
+ return !r->queue[to_check];
+}
+
+static inline bool ptr_ring_spare(struct ptr_ring *r, int cnt)
+{
+ bool ret;
+
+ spin_lock(&r->consumer_lock);
+ ret = __ptr_ring_spare(r, cnt);
+ spin_unlock(&r->consumer_lock);
+
+ return ret;
+}
+
+static inline bool ptr_ring_spare_irq(struct ptr_ring *r, int cnt)
+{
+ bool ret;
+
+ spin_lock_irq(&r->consumer_lock);
+ ret = __ptr_ring_spare(r, cnt);
+ spin_unlock_irq(&r->consumer_lock);
+
+ return ret;
+}
+
+static inline bool ptr_ring_spare_any(struct ptr_ring *r, int cnt)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&r->consumer_lock, flags);
+ ret = __ptr_ring_spare(r, cnt);
+ spin_unlock_irqrestore(&r->consumer_lock, flags);
+
+ return ret;
+}
+
+static inline bool ptr_ring_spare_bh(struct ptr_ring *r, int cnt)
+{
+ bool ret;
+
+ spin_lock_bh(&r->consumer_lock);
+ ret = __ptr_ring_spare(r, cnt);
+ spin_unlock_bh(&r->consumer_lock);
+
+ return ret;
+}
+
/* Must only be called after __ptr_ring_peek returned !NULL */
static inline void __ptr_ring_discard_one(struct ptr_ring *r)
{
--
2.43.0
On Tue, Sep 02, 2025 at 10:09:54AM +0200, Simon Schippers wrote: > The implementation is inspired by ptr_ring_empty. > > Co-developed-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> > Signed-off-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> > Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de> > --- > include/linux/ptr_ring.h | 71 ++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 71 insertions(+) > > diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h > index 551329220e4f..6b8cfaecf478 100644 > --- a/include/linux/ptr_ring.h > +++ b/include/linux/ptr_ring.h > @@ -243,6 +243,77 @@ static inline bool ptr_ring_empty_bh(struct ptr_ring *r) > return ret; > } > > +/* > + * Check if a spare capacity of cnt is available without taking any locks. Not sure what "spare" means here. I think you mean Check if the ring has enough space to produce a given number of entries. > + * > + * If cnt==0 or cnt > r->size it acts the same as __ptr_ring_empty. Logically, cnt = 0 should always be true, cnt > size should always be false then? Why do you want it to act as __ptr_ring_empty? > + * > + * The same requirements apply as described for __ptr_ring_empty. Which is: * However, if some other CPU consumes ring entries at the same time, the value * returned is not guaranteed to be correct. but it's not right here yes? consuming entries will just add more space ... Also: * In this case - to avoid incorrectly detecting the ring * as empty - the CPU consuming the ring entries is responsible * for either consuming all ring entries until the ring is empty, * or synchronizing with some other CPU and causing it to * re-test __ptr_ring_empty and/or consume the ring enteries * after the synchronization point. how would you apply this here? > + */ > +static inline bool __ptr_ring_spare(struct ptr_ring *r, int cnt) > +{ > + int size = r->size; > + int to_check; > + > + if (unlikely(!size || cnt < 0)) > + return true; > + > + if (cnt > size) > + cnt = 0; > + > + to_check = READ_ONCE(r->consumer_head) - cnt; > + > + if (to_check < 0) > + to_check += size; > + > + return !r->queue[to_check]; > +} > + I will have to look at how this is used to understand if it's correct. But I think we need better documentation. > +static inline bool ptr_ring_spare(struct ptr_ring *r, int cnt) > +{ > + bool ret; > + > + spin_lock(&r->consumer_lock); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock(&r->consumer_lock); > + > + return ret; I don't understand why you take the consumer lock here. If a producer is running it will make the value wrong, if consumer is running it will just create more space. > +} > + > +static inline bool ptr_ring_spare_irq(struct ptr_ring *r, int cnt) > +{ > + bool ret; > + > + spin_lock_irq(&r->consumer_lock); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock_irq(&r->consumer_lock); > + > + return ret; > +} > + > +static inline bool ptr_ring_spare_any(struct ptr_ring *r, int cnt) > +{ > + unsigned long flags; > + bool ret; > + > + spin_lock_irqsave(&r->consumer_lock, flags); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock_irqrestore(&r->consumer_lock, flags); > + > + return ret; > +} > + > +static inline bool ptr_ring_spare_bh(struct ptr_ring *r, int cnt) > +{ > + bool ret; > + > + spin_lock_bh(&r->consumer_lock); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock_bh(&r->consumer_lock); > + > + return ret; > +} > + > /* Must only be called after __ptr_ring_peek returned !NULL */ > static inline void __ptr_ring_discard_one(struct ptr_ring *r) > { > -- > 2.43.0
Michael S. Tsirkin wrote: > On Tue, Sep 02, 2025 at 10:09:54AM +0200, Simon Schippers wrote: >> The implementation is inspired by ptr_ring_empty. >> >> Co-developed-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> >> Signed-off-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> >> Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de> >> --- >> include/linux/ptr_ring.h | 71 ++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 71 insertions(+) >> >> diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h >> index 551329220e4f..6b8cfaecf478 100644 >> --- a/include/linux/ptr_ring.h >> +++ b/include/linux/ptr_ring.h >> @@ -243,6 +243,77 @@ static inline bool ptr_ring_empty_bh(struct ptr_ring *r) >> return ret; >> } >> >> +/* >> + * Check if a spare capacity of cnt is available without taking any locks. > > Not sure what "spare" means here. I think you mean > > Check if the ring has enough space to produce a given > number of entries. > >> + * >> + * If cnt==0 or cnt > r->size it acts the same as __ptr_ring_empty. > > Logically, cnt = 0 should always be true, cnt > size should always be > false then? > > Why do you want it to act as __ptr_ring_empty? > > >> + * >> + * The same requirements apply as described for __ptr_ring_empty. > > > Which is: > > * However, if some other CPU consumes ring entries at the same time, the value > * returned is not guaranteed to be correct. > > > but it's not right here yes? consuming entries will just add more > space ... > > Also: > * In this case - to avoid incorrectly detecting the ring > * as empty - the CPU consuming the ring entries is responsible > * for either consuming all ring entries until the ring is empty, > * or synchronizing with some other CPU and causing it to > * re-test __ptr_ring_empty and/or consume the ring enteries > * after the synchronization point. > > how would you apply this here? > > >> + */ >> +static inline bool __ptr_ring_spare(struct ptr_ring *r, int cnt) >> +{ >> + int size = r->size; >> + int to_check; >> + >> + if (unlikely(!size || cnt < 0)) >> + return true; >> + >> + if (cnt > size) >> + cnt = 0; >> + >> + to_check = READ_ONCE(r->consumer_head) - cnt; >> + >> + if (to_check < 0) >> + to_check += size; >> + >> + return !r->queue[to_check]; >> +} >> + > > I will have to look at how this is used to understand if it's > correct. But I think we need better documentation. > > >> +static inline bool ptr_ring_spare(struct ptr_ring *r, int cnt) >> +{ >> + bool ret; >> + >> + spin_lock(&r->consumer_lock); >> + ret = __ptr_ring_spare(r, cnt); >> + spin_unlock(&r->consumer_lock); >> + >> + return ret; > > > I don't understand why you take the consumer lock here. > If a producer is running it will make the value wrong, > if consumer is running it will just create more space. > > I agree, I messed up the ptr_ring helper. Your proposed approach is way superior and I will use that one instead. The idea behind the cnt was to have an option if the producer may produce multiple entries like tap_handle_frame with GSO. But of course this should be in a different patch since I will not cover tap_handle_frame, which is used by ipvtap and macvtap, in this patch series.
Simon Schippers wrote: > The implementation is inspired by ptr_ring_empty. > > Co-developed-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> > Signed-off-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> > Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de> > --- > include/linux/ptr_ring.h | 71 ++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 71 insertions(+) > > diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h > index 551329220e4f..6b8cfaecf478 100644 > --- a/include/linux/ptr_ring.h > +++ b/include/linux/ptr_ring.h > @@ -243,6 +243,77 @@ static inline bool ptr_ring_empty_bh(struct ptr_ring *r) > return ret; > } > > +/* > + * Check if a spare capacity of cnt is available without taking any locks. > + * > + * If cnt==0 or cnt > r->size it acts the same as __ptr_ring_empty. cnt >= r->size? > + * > + * The same requirements apply as described for __ptr_ring_empty. > + */ > +static inline bool __ptr_ring_spare(struct ptr_ring *r, int cnt) > +{ > + int size = r->size; > + int to_check; > + > + if (unlikely(!size || cnt < 0)) > + return true; Does !size ever happen. Also no need for preconditions for trivial errors that never happen, like passing negative values. Or prefer an unsigned type. > + > + if (cnt > size) > + cnt = 0; > + > + to_check = READ_ONCE(r->consumer_head) - cnt; > + > + if (to_check < 0) > + to_check += size; > + > + return !r->queue[to_check]; > +} > + > +static inline bool ptr_ring_spare(struct ptr_ring *r, int cnt) > +{ > + bool ret; > + > + spin_lock(&r->consumer_lock); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock(&r->consumer_lock); > + > + return ret; > +} > + > +static inline bool ptr_ring_spare_irq(struct ptr_ring *r, int cnt) > +{ > + bool ret; > + > + spin_lock_irq(&r->consumer_lock); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock_irq(&r->consumer_lock); > + > + return ret; > +} > + > +static inline bool ptr_ring_spare_any(struct ptr_ring *r, int cnt) > +{ > + unsigned long flags; > + bool ret; > + > + spin_lock_irqsave(&r->consumer_lock, flags); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock_irqrestore(&r->consumer_lock, flags); > + > + return ret; > +} > + > +static inline bool ptr_ring_spare_bh(struct ptr_ring *r, int cnt) > +{ > + bool ret; > + > + spin_lock_bh(&r->consumer_lock); > + ret = __ptr_ring_spare(r, cnt); > + spin_unlock_bh(&r->consumer_lock); > + > + return ret; > +} Please only introduce the variants actually used.
On Wed, Sep 3, 2025 at 5:13 AM Willem de Bruijn <willemdebruijn.kernel@gmail.com> wrote: > > Simon Schippers wrote: > > The implementation is inspired by ptr_ring_empty. > > > > Co-developed-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> > > Signed-off-by: Tim Gebauer <tim.gebauer@tu-dortmund.de> > > Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de> > > --- > > include/linux/ptr_ring.h | 71 ++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 71 insertions(+) > > > > diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h > > index 551329220e4f..6b8cfaecf478 100644 > > --- a/include/linux/ptr_ring.h > > +++ b/include/linux/ptr_ring.h > > @@ -243,6 +243,77 @@ static inline bool ptr_ring_empty_bh(struct ptr_ring *r) > > return ret; > > } > > > > +/* > > + * Check if a spare capacity of cnt is available without taking any locks. > > + * > > + * If cnt==0 or cnt > r->size it acts the same as __ptr_ring_empty. > > cnt >= r->size? > > > + * > > + * The same requirements apply as described for __ptr_ring_empty. > > + */ > > +static inline bool __ptr_ring_spare(struct ptr_ring *r, int cnt) > > +{ > > + int size = r->size; > > + int to_check; > > + > > + if (unlikely(!size || cnt < 0)) > > + return true; > > Does !size ever happen. Yes, see 982fb490c298 ("ptr_ring: support zero length ring"). The reason is tun reuse dev->tx_queue_len for ptr_ring size. > Also no need for preconditions for trivial > errors that never happen, like passing negative values. Or prefer > an unsigned type. +1. Thanks
© 2016 - 2025 Red Hat, Inc.