From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B9DF4339398 for ; Thu, 20 Aug 2026 10:40:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=90.155.92.199 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787222433; cv=none; b=YRpz2fM9ky8mrEDKUBOU1i+e9fxztH3OEla6xXkxAVjwCc+FAbMSmvyMWIH7XEB1hEg+sW5a/eZHUNwemALTHv+sBMGGuHFXHVZAd2Izc28lX3PBM57h37mQc7ejIBWTqVPmScAunqrRuV7/1EOXGO5z3IRSoBVY49vNJmeB33c= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787222433; c=relaxed/simple; bh=aAwsu9r7XDI4vMkijLaBO97I+nqEUyPslAXiHzCRLpE=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=tiJxDEOswuzue/eba5eysX7KPoPFvyp9jmP46mFQfnmIyaOLgGbg16P/1/SyXoc/meJjMZUSIczdmruWauimhwmMtNuRo7qV17fIWq12jIdlnG/TF7UfDYFuvZc270kNou/Yw0habSZ6EsQJnPcC9BZbHVYQZvDK5EfpJawQMSw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=infradead.org; spf=pass smtp.mailfrom=infradead.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b=FWJlc4EU; arc=none smtp.client-ip=90.155.92.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=infradead.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=infradead.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b="FWJlc4EU" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=In-Reply-To:Content-Type:MIME-Version: References:Message-ID:Subject:Cc:To:From:Date:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=ZW7iYOTBPAnOLyW9znOcBQZshF4/8mjkKtXh0ggRg3k=; b=FWJlc4EUjQyT/UIQ5c+O4xUa0x ojLEOO5jlxxA94vNcKSVtbQ7pEzBMedWTWOYgDziOjzhs2xBwPrs1N2DxJ7c83Yyjq0aMhcN59l8q nbAm32QQlojcxKNOSdSVhI5eDssZbpvZyKPDtiDckYwsFUpQO7EtdzS6+Wx3KC8ZcEW1KIKoxEm4B uJOL+c0O6XwWn9eG79RpPZFoLROeofSchB2nRW1SVXRxmK3qxxQPt/uF3gewtxRD3UbhwQX9rHq9l gjR3SBrkXWuy3Png33l+EYFxFdiaJ3PqaY4jdUQqHVrhQU5ekIdg1B5FXjlsHOSJ9a+DbjqdOIgOL gwPLlL8Q==; Received: from 77-249-17-252.cable.dynamic.v4.ziggo.nl ([77.249.17.252] helo=noisy.programming.kicks-ass.net) by desiato.infradead.org with esmtpsa (Exim 4.99.2 #2 (Red Hat Linux)) id 1wx0C5-000000031qY-0ZlI; Thu, 20 Aug 2026 10:40:21 +0000 Received: by noisy.programming.kicks-ass.net (Postfix, from userid 1000) id 674EF3006FA; Thu, 20 Aug 2026 12:40:15 +0200 (CEST) Date: Thu, 20 Aug 2026 12:40:15 +0200 From: Peter Zijlstra To: Chris Mason Cc: tglx@kernel.org, linux-kernel@vger.kernel.org, Paul McKenney Subject: Re: [PATCH] futex: sample poll cookie after publishing new hash Message-ID: <20260820104015.GE687043@noisy.programming.kicks-ass.net> References: <20260818011036.1138213-1-mason@kernel.org> <20260818011036.1138213-2-mason@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 > --- > 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 >