From: Frederic Weisbecker <frederic@kernel.org>
To: Uladzislau Rezki <urezki@gmail.com>
Cc: Joel Fernandes <joelagnelf@nvidia.com>,
"Paul E.McKenney" <paulmck@kernel.org>,
Vishal Chourasia <vishalc@linux.ibm.com>,
Shrikanth Hegde <sshegde@linux.ibm.com>,
Neeraj upadhyay <Neeraj.Upadhyay@amd.com>,
RCU <rcu@vger.kernel.org>, LKML <linux-kernel@vger.kernel.org>,
Samir M <samir@linux.ibm.com>
Subject: Re: [PATCH v2] rcu: Latch normal synchronize_rcu() path on flood
Date: Tue, 10 Mar 2026 15:11:21 +0100 [thread overview]
Message-ID: <abAmiX85PFNq2Ua-@localhost.localdomain> (raw)
In-Reply-To: <aaliA7sc4Nnu_i_C@milan>
Le Thu, Mar 05, 2026 at 11:59:15AM +0100, Uladzislau Rezki a écrit :
> On Tue, Mar 03, 2026 at 03:45:58PM -0500, Joel Fernandes wrote:
> > On Mon, 02 Mar 2026 11:04:04 +0100, Uladzislau Rezki (Sony) wrote:
> >
> > > * The latch is cleared only when the pending requests are fully
> > > drained(nr == 0);
> >
> > > +static void rcu_sr_normal_add_req(struct rcu_synchronize *rs)
> > > +{
> > > + long nr;
> > > +
> > > + llist_add((struct llist_node *) &rs->head, &rcu_state.srs_next);
> > > + nr = atomic_long_inc_return(&rcu_sr_normal_count);
> > > +
> > > + /* Latch: only when flooded and if unlatched. */
> > > + if (nr >= RCU_SR_NORMAL_LATCH_THR)
> > > + (void)atomic_cmpxchg(&rcu_sr_normal_latched, 0, 1);
> > > +}
> >
> > I think there is a stuck-latch race here. Once llist_add() places the
> > entry in srs_next, the GP kthread can pick it up and fire
> > rcu_sr_normal_complete() before the latching cmpxchg runs. If the last
> > in-flight completion drains count to zero in that window, the unlatch
> > cmpxchg(latched, 1, 0) fails (latched is still 0 at that moment), and
> > then the latching cmpxchg(latched, 0, 1) fires anyway — with count=0:
> >
> > CPU 0 (add_req, count just hit 64) GP kthread
> > ---------------------------------- ----------
> > llist_add() <-- entry now in srs_next
> > inc_return() --> nr = 64
> > [preempted]
> > rcu_sr_normal_complete() x64:
> > dec_return -> count: 64..1..0
> > count==0:
> > cmpxchg(latched, 1, 0)
> > --> FAILS (latched still 0)
> > [resumes]
> > cmpxchg(latched, 0, 1) --> latched = 1
> >
> > Final state: count=0, latched=1 --> STUCK LATCH
> >
> > All subsequent synchronize_rcu() callers see latched==1 and take the
> > fallback path (not counted). With no new SR-normal callers,
> > rcu_sr_normal_complete() is never reached again, so the unlatch
> > cmpxchg(latched, 1, 0) never fires. The latch is permanently stuck.
> >
> > This requires preemption for a full GP duration between llist_add() and
> > the cmpxchg, which is probably more likely on PREEMPT_RT or heavily loaded
> > systems.
> >
> > The fix: move the cmpxchg *before* llist_add(), so the entry is not
> > visible to the GP kthread until after the latch is already set.
> >
> > That should fix it, thoughts?
> >
> Yes and thank you!
>
> We can improve it even more by removing atomic_cmpxchg() in
> the rcu_sr_normal_add_req() function, because only one context
> sees the (nr == RCU_SR_NORMAL_LATCH_THR) condition:
>
> <snip>
> diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
> index 86dc88a70fd0..72b340940e11 100644
> --- a/kernel/rcu/tree.c
> +++ b/kernel/rcu/tree.c
> @@ -1640,7 +1640,7 @@ static struct workqueue_struct *sync_wq;
>
> /* Number of in-flight synchronize_rcu() calls queued on srs_next. */
> static atomic_long_t rcu_sr_normal_count;
> -static atomic_t rcu_sr_normal_latched;
> +static int rcu_sr_normal_latched; /* 0/1 */
>
> static void rcu_sr_normal_complete(struct llist_node *node)
> {
> @@ -1662,7 +1662,7 @@ static void rcu_sr_normal_complete(struct llist_node *node)
> * drained and if it has been latched.
> */
> if (nr == 0)
> - (void)atomic_cmpxchg(&rcu_sr_normal_latched, 1, 0);
> + (void)cmpxchg(&rcu_sr_normal_latched, 1, 0);
> }
>
> static void rcu_sr_normal_gp_cleanup_work(struct work_struct *work)
> @@ -1808,14 +1808,22 @@ static bool rcu_sr_normal_gp_init(void)
>
> static void rcu_sr_normal_add_req(struct rcu_synchronize *rs)
> {
> - long nr;
> + /*
> + * Increment before publish to avoid a complete
> + * vs enqueue race on latch.
> + */
> + long nr = atomic_long_inc_return(&rcu_sr_normal_count);
>
> - llist_add((struct llist_node *) &rs->head, &rcu_state.srs_next);
> - nr = atomic_long_inc_return(&rcu_sr_normal_count);
> + /*
> + * Latch on threshold crossing. (nr == RCU_SR_NORMAL_LATCH_THR)
> + * can be true only for one context, avoiding contention on the
> + * write path.
> + */
> + if (nr == RCU_SR_NORMAL_LATCH_THR)
> + WRITE_ONCE(rcu_sr_normal_latched, 1);
Isn't it still racy?
rcu_sr_normal_add_req rcu_sr_normal_complete
--------------------- ----------------------
nr = atomic_long_dec_return(&rcu_sr_normal_count);
// nr == 0
======= PREEMPTION =======
// 64 tasks doing synchronize_rcu()
rcu_sr_normal_add_req()
WRITE_ONCE(rcu_sr_normal_latched, 1);
cmpxchg(&rcu_sr_normal_latched, 1, 0);
Also more generally there is nothing that orders the WRITE_ONCE() with the
cmpxchg.
Is it possible to remove rcu_sr_normal_latched and simply deal with comparisons
between rcu_sr_normal_count and RCU_SR_NORMAL_LATCH_THR?
Thanks.
--
Frederic Weisbecker
SUSE Labs
next prev parent reply other threads:[~2026-03-10 14:11 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-02 10:04 [PATCH v2] rcu: Latch normal synchronize_rcu() path on flood Uladzislau Rezki (Sony)
2026-03-03 20:45 ` Joel Fernandes
2026-03-05 10:59 ` Uladzislau Rezki
2026-03-09 20:32 ` Joel Fernandes
2026-03-10 12:37 ` Uladzislau Rezki
2026-03-10 14:11 ` Frederic Weisbecker [this message]
2026-03-10 16:28 ` Uladzislau Rezki
2026-03-10 22:24 ` Frederic Weisbecker
2026-03-11 8:45 ` Uladzislau Rezki
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=abAmiX85PFNq2Ua-@localhost.localdomain \
--to=frederic@kernel.org \
--cc=Neeraj.Upadhyay@amd.com \
--cc=joelagnelf@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=paulmck@kernel.org \
--cc=rcu@vger.kernel.org \
--cc=samir@linux.ibm.com \
--cc=sshegde@linux.ibm.com \
--cc=urezki@gmail.com \
--cc=vishalc@linux.ibm.com \
/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.