From: Andrea Parri <parri.andrea@gmail.com>
To: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
"Paul E. McKenney" <paulmck@kernel.org>
Cc: Andrea Parri <parri.andrea@gmail.com>,
Boqun Feng <boqun@kernel.org>, Gary Guo <gary@garyguo.net>,
Bradley Morgan <brads@mainlining.org>,
Kunwu Chan <kunwu.chan@gmail.com>,
rcu@vger.kernel.org, lkmm@lists.linux.dev,
linux-kernel@vger.kernel.org
Subject: [PATCH RFC -rcu/dev] hazptr: Record per-CPU slot owner after claiming the slot
Date: Mon, 19 Oct 2026 18:18:48 +0200 [thread overview]
Message-ID: <20261019161852.12783-1-parri.andrea@gmail.com> (raw)
An interrupt that uses a hazard pointer while the interrupted task is
in the acquire slow path can leave its own context recorded as the
owner of the per-CPU slot the task then publishes into. The next
context switch promotes the task's pointer with the interrupt's
context.
hazptr_get_free_percpu_slot() records the owner when it picks an empty
slot, but only Store B in __hazptr_try_acquire() claims the slot. An
interrupt in between picks the same slot, as the lowest free one:
Task on CPU 0 IRQ on CPU 0
------------- ------------
hazptr_acquire(ctx)
fast path fails
__hazptr_acquire(ctx)
picks item k (empty)
item k->ctx.ctx = ctx
hazptr_acquire(irq_ctx)
claims item k
item k->ctx.ctx = irq_ctx
hazptr_release(irq_ctx)
clears slot k
__hazptr_try_acquire()
Store B into slot k
(item k still names irq_ctx)
sleeps holding the pointer
hazptr_note_context_switch()
promotes item k with irq_ctx
The task gets there with k > 0 when it already holds an outer hazard
pointer in slot 0; no other task can, as its slot is promoted when it
is switched out.
The task gets there with k == 0 when the fast path leaves slot 0
empty: either its ptr_eq() check fails because an updater changed the
pointer, or the pointer is NULL and an updater publishes a non-NULL one
before the slow path loads it.
The promotion moves the task's pointer into irq_ctx's backup slot,
which nothing unchains, so hazptr_synchronize() on that pointer never
returns. The task's ctx->slot still names slot k, so its
hazptr_release() clears whatever another reader on that CPU has since
published there.
If the interrupt detaches instead, as hazptr_torture_acquire() does,
the promotion chains irq_ctx's backup slot a second time while it is
still linked. The overflow list then loops on that node, or follows
LIST_POISON1 once hazptr_unchain_backup_slot() removes it.
Record the owner after __hazptr_try_acquire() succeeds, as the fast
path in hazptr_acquire() does. This should be safe because an
interrupt that used the slot has released or detached it by the time
the task resumes, and only hazptr_note_context_switch() reads the
owner, which cannot run before the task enables preemption.
Fixes: 50b568234163 ("hazptr: Implement Hazard Pointers")
Assisted-by: LLM
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
Notes:
With this patch, hazptrtorture's NOPREEMPT scenario passes a 30-minute
run on x86_64 with 8 CPUs. PREEMPT runs clean for 30 minutes but can
hang at shutdown, in hazptrtorture's release of deferred hazard
pointers; that hang does not involve this patch, and is addressed by
"hazptrtorture: Release all deferred hazard pointers before stopping",
posted separately.
With that fix also applied, 30-minute runs pass for PREEMPT, for
NOPREEMPT with torture_type=hazptr, for PREEMPT with
torture_type=hazptr-stack, and for PREEMPT with CONFIG_HAZPTR_DEBUG=n.
PREEMPT and NOPREEMPT also pass without this patch: their readers hold
one hazard pointer each, so they reach the slow path only when the
ptr_eq() check fails or the pointer is NULL, which happens only before
the writer publishes its first element and after it stops.
A KUnit reproducer, with a test-only hook in __hazptr_try_acquire()
between the free-slot check and Store B, fails on 940793b926aa and
passes with this patch. I can post it if useful.
kernel/hazptr.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/kernel/hazptr.c b/kernel/hazptr.c
index 3ca73b56a5c25..b15b84ebd4ceb 100644
--- a/kernel/hazptr.c
+++ b/kernel/hazptr.c
@@ -54,19 +54,16 @@ unsigned int flip_list_phase(unsigned int phase)
}
static
-struct hazptr_slot *hazptr_get_free_percpu_slot(struct hazptr_ctx *ctx)
+struct hazptr_slot_item *hazptr_get_free_percpu_slot_item(void)
{
struct hazptr_percpu_slots *percpu_slots = this_cpu_ptr(&hazptr_percpu_slots);
unsigned int idx;
for (idx = 0; idx < NR_HAZPTR_PERCPU_SLOTS; idx++) {
struct hazptr_slot_item *item = &percpu_slots->items[idx];
- struct hazptr_slot *slot = &item->slot;
- if (!slot->addr) {
- item->ctx.ctx = ctx;
- return slot;
- }
+ if (!item->slot.addr)
+ return item;
}
/* All slots are in use. */
return NULL;
@@ -78,6 +75,7 @@ struct hazptr_slot *hazptr_get_free_percpu_slot(struct hazptr_ctx *ctx)
*/
void *__hazptr_acquire(struct hazptr_ctx *ctx, void * const *addr_p)
{
+ struct hazptr_slot_item *item;
struct hazptr_slot *slot;
void *addr;
@@ -85,10 +83,17 @@ void *__hazptr_acquire(struct hazptr_ctx *ctx, void * const *addr_p)
* In case we are called due to nested use of hazard pointers,
* try a slot protection with per-CPU slots.
*/
- slot = hazptr_get_free_percpu_slot(ctx);
- if (likely(slot)) {
+ item = hazptr_get_free_percpu_slot_item();
+ if (likely(item)) {
+ slot = &item->slot;
addr = __hazptr_try_acquire(ctx, addr_p, slot);
if (addr) {
+ /*
+ * Record the owner only once Store B has claimed the
+ * slot, as hazptr_acquire() does: an interrupt may
+ * have used this slot since it was chosen.
+ */
+ item->ctx.ctx = ctx;
ctx->slot = slot;
return addr;
}
--
2.53.0
next reply other threads:[~2026-10-19 16:19 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 16:18 Andrea Parri [this message]
2026-10-19 16:21 ` Bradley Morgan
2026-10-20 19:11 ` Mathieu Desnoyers
2026-10-20 21:12 ` Paul E. McKenney
2026-10-21 9:07 ` Andrea Parri
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=20261019161852.12783-1-parri.andrea@gmail.com \
--to=parri.andrea@gmail.com \
--cc=boqun@kernel.org \
--cc=brads@mainlining.org \
--cc=gary@garyguo.net \
--cc=kunwu.chan@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lkmm@lists.linux.dev \
--cc=mathieu.desnoyers@efficios.com \
--cc=paulmck@kernel.org \
--cc=rcu@vger.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®