All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Morten Brørup" <mb@smartsharesystems.com>
To: "Honnappa Nagarahalli" <Honnappa.Nagarahalli@arm.com>,
	"Stephen Hemminger" <stephen@networkplumber.org>, <dev@dpdk.org>
Cc: "nd" <nd@arm.com>, "nd" <nd@arm.com>
Subject: RE: [RFC] rwlock: prevent readers from starving writers
Date: Sat, 9 Jul 2022 00:04:27 +0200	[thread overview]
Message-ID: <98CBD80474FA8B44BF855DF32C47DC35D871BB@smartserver.smartshare.dk> (raw)
In-Reply-To: <DBAPR08MB58144872160AC6D4235D1DC198829@DBAPR08MB5814.eurprd08.prod.outlook.com>

> From: Honnappa Nagarahalli [mailto:Honnappa.Nagarahalli@arm.com]
> Sent: Friday, 8 July 2022 21.22
> 
> <snip>
> >
> > The original reader/writer lock in DPDK can cause a stream of readers
> to
> > starve writers.
> >
> > The new version uses an additional bit to indicate that a writer is
> waiting and
> > which keeps readers from starving the writer.
> This addition makes sense.
> I am wondering if we should create a new lock. Is it possible that some
> applications are dependent on the current behavior?

Any reader risks having to wait a while for a writer to finish its work.

In my opinion, this implementation only increases the probability of that risk occurring, but it doesn't change the writer's impact on the readers. Therefore, I think this improved implementation can replace the old rwlock.

> 
> >
> > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> > ---
> > Would like this to be in 22.11, but needs some more review
> >
> >  lib/eal/include/generic/rte_rwlock.h | 93 ++++++++++++++++++--------
> --
> >  1 file changed, 61 insertions(+), 32 deletions(-)
> >
> > diff --git a/lib/eal/include/generic/rte_rwlock.h
> > b/lib/eal/include/generic/rte_rwlock.h
> > index da9bc3e9c0e2..725cd19ffb27 100644
> > --- a/lib/eal/include/generic/rte_rwlock.h
> > +++ b/lib/eal/include/generic/rte_rwlock.h
> > @@ -13,7 +13,7 @@
> >   * This file defines an API for read-write locks. The lock is used
> to
> >   * protect data that allows multiple readers in parallel, but only
> >   * one writer. All readers are blocked until the writer is finished
> > - * writing.
> > + * writing. This version will not starve writers.
> >   *
> >   */
> >
> > @@ -28,10 +28,17 @@ extern "C" {
> >  /**
> >   * The rte_rwlock_t type.
> >   *
> > - * cnt is -1 when write lock is held, and > 0 when read locks are
> held.
> > + * Readers increment the counter by RW_READ (4)
> > + * Writers set the RWLOCK_WRITE bit when lock is held
> > + *     and set the RWLOCK_WAIT bit while waiting.
> >   */
> > +
> > +#define RTE_RWLOCK_WAIT	 0x1	/* Writer is waiting */
> > +#define RTE_RWLOCK_WRITE 0x2	/* Writer has the lock */
> > +#define RTE_RWLOCK_READ	 0x4	/* Reader increment */
> > +
> >  typedef struct {
> > -	volatile int32_t cnt; /**< -1 when W lock held, > 0 when R locks
> held.
> > */
> > +	volatile int32_t cnt;

Not signed anymore, so consider uint32_t. Suggest also rename to cnt_state or similar, since it is not just a counter anymore.

> >  } rte_rwlock_t;
> >
> >  /**
> > @@ -61,17 +68,24 @@ static inline void
> >  rte_rwlock_read_lock(rte_rwlock_t *rwl)  {
> >  	int32_t x;
> > -	int success = 0;
> >
> > -	while (success == 0) {
> > +	while (1) {
> >  		x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
> >  		/* write lock is held */

Held -> Held or pending, not just held. Add question mark, or move inside the if block.

> > -		if (x < 0) {
> > +		if (x & (RTE_RWLOCK_WAIT | RTE_RWLOCK_WRITE)) {
> >  			rte_pause();
> >  			continue;
> >  		}
> > -		success = __atomic_compare_exchange_n(&rwl->cnt, &x, x
> > + 1, 1,
> > -					__ATOMIC_ACQUIRE,
> > __ATOMIC_RELAXED);
> > +
> > +		/* Try to get read lock */
> > +		x = __atomic_add_fetch(&rwl->cnt, RTE_RWLOCK_READ,
> > +				       __ATOMIC_ACQUIRE);
> > +		if (!(x & (RTE_RWLOCK_WAIT | RTE_RWLOCK_WRITE)))
> > +			return;
> > +
> > +		/* Undo */

Undo -> Unable, so release the read lock.

> > +		__atomic_fetch_sub(&rwl->cnt, RTE_RWLOCK_READ,
> > +				   __ATOMIC_RELEASE);
> >  	}
> >  }
> >
> > @@ -93,17 +107,23 @@ static inline int
> >  rte_rwlock_read_trylock(rte_rwlock_t *rwl)  {
> >  	int32_t x;
> > -	int success = 0;
> >
> > -	while (success == 0) {
> > -		x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
> > -		/* write lock is held */
> > -		if (x < 0)
> > -			return -EBUSY;
> > -		success = __atomic_compare_exchange_n(&rwl->cnt, &x, x
> > + 1, 1,
> > -					__ATOMIC_ACQUIRE,
> > __ATOMIC_RELAXED);
> > -	}
> > +	x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
> > +
> > +	/* write lock is held */

Same comment as above.

> > +	if (x & (RTE_RWLOCK_WAIT | RTE_RWLOCK_WRITE))
> > +		return -EBUSY;
> > +
> > +	/* Try to get read lock */
> > +	x = __atomic_add_fetch(&rwl->cnt, RTE_RWLOCK_READ,
> > +			       __ATOMIC_ACQUIRE);
> > +
> > +	if (x & (RTE_RWLOCK_WAIT | RTE_RWLOCK_WRITE)) {

Add a comment, e.g.: Unable, so release the read lock.

> > +		__atomic_fetch_sub(&rwl->cnt, RTE_RWLOCK_READ,
> > +				   __ATOMIC_RELEASE);
> >
> > +		return -EBUSY;
> > +	}
> >  	return 0;
> >  }
> >
> > @@ -116,7 +136,7 @@ rte_rwlock_read_trylock(rte_rwlock_t *rwl)
> static
> > inline void  rte_rwlock_read_unlock(rte_rwlock_t *rwl)  {
> > -	__atomic_fetch_sub(&rwl->cnt, 1, __ATOMIC_RELEASE);
> > +	__atomic_fetch_sub(&rwl->cnt, RTE_RWLOCK_READ,
> > __ATOMIC_RELEASE);
> >  }
> >
> >  /**
> > @@ -139,11 +159,12 @@ rte_rwlock_write_trylock(rte_rwlock_t *rwl)
> >  	int32_t x;
> >
> >  	x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
> > -	if (x != 0 || __atomic_compare_exchange_n(&rwl->cnt, &x, -1, 1,
> > -			      __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) == 0)
> > +	if (x < RTE_RWLOCK_WRITE &&
> > +	    __atomic_compare_exchange_n(&rwl->cnt, &x, x +
> > RTE_RWLOCK_WRITE,
> > +					1, __ATOMIC_ACQUIRE,
> > __ATOMIC_RELAXED))
> > +		return 0;
> > +	else
> >  		return -EBUSY;
> > -
> > -	return 0;
> >  }
> >
> >  /**
> > @@ -156,18 +177,26 @@ static inline void
> > rte_rwlock_write_lock(rte_rwlock_t *rwl)  {
> >  	int32_t x;
> > -	int success = 0;
> >
> > -	while (success == 0) {
> > +	while (1) {
> >  		x = __atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED);
> > -		/* a lock is held */
> > -		if (x != 0) {
> > -			rte_pause();
> > -			continue;
> > +
> > +		/* No readers or writers */

Add question mark, or move inside if block.

> > +		if (x < RTE_RWLOCK_WRITE) {
> > +			/* Turn off RTE_RWLOCK_WAIT, turn on
> > RTE_RWLOCK_WRITE */
> > +			if (__atomic_compare_exchange_n(&rwl->cnt, &x,
> > RTE_RWLOCK_WRITE, 1,
> > +
> > 	__ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > +				return;
> >  		}
> > -		success = __atomic_compare_exchange_n(&rwl->cnt, &x, -
> > 1, 1,
> > -					__ATOMIC_ACQUIRE,
> > __ATOMIC_RELAXED);
> > -	}
> > +
> > +		/* Turn on writer wait bit */
> > +		if (!(x & RTE_RWLOCK_WAIT))
> > +			__atomic_fetch_or(&rwl->cnt, RTE_RWLOCK_WAIT,
> > __ATOMIC_RELAXED);
> > +
> > +		/* Wait until can try to take the lock */
> > +		while (__atomic_load_n(&rwl->cnt, __ATOMIC_RELAXED) >
> > RTE_RWLOCK_WAIT)
> > +			rte_pause();
> > +    }
> >  }
> >
> >  /**
> > @@ -179,7 +208,7 @@ rte_rwlock_write_lock(rte_rwlock_t *rwl)  static
> > inline void  rte_rwlock_write_unlock(rte_rwlock_t *rwl)  {
> > -	__atomic_store_n(&rwl->cnt, 0, __ATOMIC_RELEASE);
> > +	__atomic_fetch_sub(&rwl->cnt, RTE_RWLOCK_WRITE,
> > __ATOMIC_RELEASE);
> >  }
> >
> >  /**
> > --
> > 2.35.1
> 

Always the creative mind, Stephen. :-)

You might consider adding/updating even more comments.

Acked-by: Morten Brørup <mb@smartsharesystems.com>


  reply	other threads:[~2022-07-08 22:04 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-07 20:12 [RFC] rwlock: prevent readers from starving writers Stephen Hemminger
2022-07-08 19:22 ` Honnappa Nagarahalli
2022-07-08 22:04   ` Morten Brørup [this message]
2022-07-09 16:22     ` Stephen Hemminger
2022-07-09 16:25     ` Stephen Hemminger
2022-07-19 20:27 ` [PATCH v2] " Stephen Hemminger
2022-07-19 21:52   ` Morten Brørup
2022-07-19 22:33     ` Stephen Hemminger
2022-07-20  6:48       ` Morten Brørup
2022-10-03 10:01   ` David Marchand

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=98CBD80474FA8B44BF855DF32C47DC35D871BB@smartserver.smartshare.dk \
    --to=mb@smartsharesystems.com \
    --cc=Honnappa.Nagarahalli@arm.com \
    --cc=dev@dpdk.org \
    --cc=nd@arm.com \
    --cc=stephen@networkplumber.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.