Re: [RFC PATCH v5 1/2] hazptr: Implement Hazard Pointers

From: Mathieu Desnoyers

Date: Sat Jun 27 2026 - 11:21:46 EST


On 2026-06-27 08:56, Mathieu Desnoyers wrote:
On 2026-06-27 00:14, Paul E. McKenney wrote:
On Fri, Jun 26, 2026 at 11:56:13PM -0400, Mathieu Desnoyers wrote:
On 2026-06-26 19:06, Paul E. McKenney wrote:
[...]
Initial runs suggest that hazard pointers cannot be acquired by one
task and released by another.  Is that expected behavior, or is my test
improperly passing the hazard pointers?  If the latter, we should of
course document the proper hazard-pointer-passing mechanism.

This is unexpected. I've looked at your test code quickly and could
not figure out what's wrong though. But it's late. How do you observe
that it fails ?

Not an emergency, so your schedule is my schedule.  Especially given
that I am mostly taking this coming week off.  And that it took me such
a long time to get this torture test done.  ;-)

I run either scenario (PREEMPT or NOPREEMPT) with:

--bootarg "hazptrtorture.onoff_interval=3333 hazptrtorture.stat_interval=15"

I get the following on the console, and things go downhill from there.
I get the same thing when I turn off CPU hotplug, for whatever that is
worth.
That OOPS is very instructive. I think I found the root cause.

I need to add an API to hazptr so users wishing to explicitly transfer
ownership of the hazptr ctx to another thread can do so.

Currently the scheduler hook is responsible for moving the hazptr
ctx from per-cpu to the backup slots, which makes it entirely movable
afterwards.

But this assumes the ctx is thread-local. Once you explicitly move
ownership of that ctx to another thread, you race with the scheduler of
the original thread.

Let me see what I can do to fix that.

Thanks a lot for the test-case !

Can you try with the attached POC diff ?

Thanks,

Mathieu

--
Mathieu Desnoyers
EfficiOS Inc.
https://www.efficios.comdiff --git a/include/linux/hazptr.h b/include/linux/hazptr.h
index 461f481a480b..d96414575739 100644
--- a/include/linux/hazptr.h
+++ b/include/linux/hazptr.h
@@ -98,6 +98,41 @@ bool hazptr_slot_is_backup(struct hazptr_ctx *ctx, struct hazptr_slot *slot)
return slot == &ctx->backup_slot.slot;
}

+/* Internal helper. */
+static inline
+void hazptr_promote_to_backup_slot(struct hazptr_ctx *ctx, struct hazptr_slot *slot)
+{
+ struct hazptr_slot *backup_slot;
+
+ backup_slot = hazptr_chain_backup_slot(ctx);
+ /*
+ * Move hazard pointer from the per-CPU slot to the
+ * backup slot. This requires hazard pointer
+ * synchronize to iterate on per-CPU slots with
+ * load-acquire before iterating on the overflow list.
+ */
+ WRITE_ONCE(backup_slot->addr, slot->addr);
+ /*
+ * store-release orders store to backup slot addr before
+ * store to per-CPU slot addr.
+ */
+ smp_store_release(&slot->addr, NULL);
+ /* Use the backup slot for context. */
+ ctx->slot = backup_slot;
+}
+
+static inline
+void hazptr_detach_from_task(struct hazptr_ctx *ctx)
+{
+ struct hazptr_slot *slot;
+
+ guard(preempt)();
+ slot = ctx->slot;
+ if (unlikely(hazptr_slot_is_backup(ctx, slot)))
+ return;
+ hazptr_promote_to_backup_slot(ctx, slot);
+}
+
static inline
void hazptr_note_context_switch(void)
{
@@ -106,27 +141,11 @@ void hazptr_note_context_switch(void)

for (idx = 0; idx < NR_HAZPTR_PERCPU_SLOTS; idx++) {
struct hazptr_slot_item *item = &percpu_slots->items[idx];
- struct hazptr_slot *slot = &item->slot, *backup_slot;
- struct hazptr_ctx *ctx;
+ struct hazptr_slot *slot = &item->slot;

if (!slot->addr)
continue;
- ctx = item->ctx.ctx;
- backup_slot = hazptr_chain_backup_slot(ctx);
- /*
- * Move hazard pointer from the per-CPU slot to the
- * backup slot. This requires hazard pointer
- * synchronize to iterate on per-CPU slots with
- * load-acquire before iterating on the overflow list.
- */
- WRITE_ONCE(backup_slot->addr, slot->addr);
- /*
- * store-release orders store to backup slot addr before
- * store to per-CPU slot addr.
- */
- smp_store_release(&slot->addr, NULL);
- /* Use the backup slot for context. */
- ctx->slot = backup_slot;
+ hazptr_promote_to_backup_slot(item->ctx.ctx, slot);
}
}

diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 311b387d6ae7..93b383887fc5 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -427,6 +427,7 @@ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_ran
int cpu = torture_random(trsp) % nr_cpu_ids;
struct llist_head *llhp;

+ hazptr_detach_from_task(&hppp->hpp_hc);
guard(preempt)();
cpu = cpumask_next_wrap(cpu, cpu_online_mask);
llhp = per_cpu_ptr(&hazptr_pending, cpu);