mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Peter Zijlstra <peterz@infradead.org>
To: Chris Mason <mason@kernel.org>
Cc: tglx@kernel.org, linux-kernel@vger.kernel.org,
	Paul McKenney <paulmck@kernel.org>
Subject: Re: [PATCH] futex: sample poll cookie after publishing new hash
Date: Thu, 20 Aug 2026 12:40:15 +0200	[thread overview]
Message-ID: <20260820104015.GE687043@noisy.programming.kicks-ass.net> (raw)
In-Reply-To: <20260818011036.1138213-2-mason@kernel.org>

On Mon, Aug 17, 2026 at 06:01:42PM -0700, Chris Mason wrote:
> futex_ref_drop() may only skip its grace period when one has already
> elapsed since the current private hash was published:
> 
>     kernel/futex/core.c:futex_ref_drop
>         if (poll_state_synchronize_rcu(mm->futex.phash.batches)) {
>                 /*
>                  * There was a grace-period, we can begin now.
>                  */
>                 __futex_ref_atomic_begin(fph);
>                 return;
>         }
> 
> The cookie it polls is sampled one statement before that publication:
> 
>     kernel/futex/core.c:__futex_pivot_hash
>         new->state = FR_PERCPU;
>         scoped_guard(rcu) {
>                 mmph->batches = get_state_synchronize_rcu();
>                 rcu_assign_pointer(mmph->hash, new);
>         }
>         kvfree_rcu(fph, rcu);
> 
> get_state_synchronize_rcu() anchors its guarantee at the snapshot, so
> the cookie is cleared by the first grace period that starts from there
> on, including one starting between the two stores which never waited
> for a reader that loaded the old hash after it began.
> 
>     CPU 0 (resize)                    CPU 1 (futex_hash)
>     ==============                    ==================
>     __futex_pivot_hash()
>       batches = get_state_...()
>                                       grace period starts
>                                       guard(rcu)
>                                       fph = old hash
>       rcu_assign_pointer(hash, new)
>       kvfree_rcu(old hash)
>     futex_hash_allocate()
>       futex_ref_drop(new hash)
>         poll_state_...() -> true
>         __futex_ref_atomic_begin()
>           atomic = LONG_MAX
>                                       futex_ref_get(old hash) -> true
>                                       spin_lock(&fph->queues[i].lock)
> 
> The reference count lives in the mm and has just been biased for the
> new generation, so the stalled reader pins and then locks the retired
> hash that is already queued for free, and its later put is charged
> against the live generation.
> 
> Fix by sampling the cookie after rcu_assign_pointer() publishes the
> new hash. Drop the surrounding scoped_guard(rcu) while at it: it only
> delayed completion of the prematurely anchored grace-period and serves
> no purpose once the cookie is sampled after publication. The writer
> side is serialized by mm->futex.phash.lock and neither
> rcu_assign_pointer() nor kvfree_rcu() requires a read-side section.

God, how I hate reading AI output :-(

Anyway, the thinking was that by holding rcu_read_lock(), the current
RCU-GP cannot change and the cookie and assignment are effectively
'atomic'.

Paul?

> Fixes: 56180dd20c19 ("futex: Use RCU-based per-CPU reference counting instead of rcuref_t")
> Assisted-by: kres:claude-opus-5
> Signed-off-by: Chris Mason <mason@kernel.org>
> ---
>  kernel/futex/core.c | 12 ++++++++----
>  1 file changed, 8 insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/futex/core.c b/kernel/futex/core.c
> index 128c5752f225..05619eb8329c 100644
> --- a/kernel/futex/core.c
> +++ b/kernel/futex/core.c
> @@ -209,10 +209,14 @@ static bool __futex_pivot_hash(struct mm_struct *mm, struct futex_private_hash *
>  		futex_rehash_private(fph, new);
>  	}
>  	new->state = FR_PERCPU;
> -	scoped_guard(rcu) {
> -		mmph->batches = get_state_synchronize_rcu();
> -		rcu_assign_pointer(mmph->hash, new);
> -	}
> +	rcu_assign_pointer(mmph->hash, new);
> +	/*
> +	 * Pairs with futex_ref_drop(): ->batches must be sampled at or after
> +	 * the rcu_assign_pointer() above, so any grace-period satisfying
> +	 * poll_state_synchronize_rcu() provably started once no reader could
> +	 * still load the retired fph. Both stores are done under mmph->lock.
> +	 */
> +	mmph->batches = get_state_synchronize_rcu();
>  	kvfree_rcu(fph, rcu);
>  	return true;
>  }
> -- 
> 2.53.0-Meta
> 

      reply	other threads:[~2026-08-20 10:40 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-18  1:01 [PATCH RFC] __futex_pivot_hash() race Chris Mason
2026-08-18  1:01 ` [PATCH] futex: sample poll cookie after publishing new hash Chris Mason
2026-08-20 10:40   ` Peter Zijlstra [this message]

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=20260820104015.GE687043@noisy.programming.kicks-ass.net \
    --to=peterz@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mason@kernel.org \
    --cc=paulmck@kernel.org \
    --cc=tglx@kernel.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

all inboxes | Powered by JetHome®