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>
next prev parent 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 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).