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

From: Paul E. McKenney

Date: Wed Jul 08 2026 - 23:29:55 EST


On Wed, Jul 08, 2026 at 10:45:23PM -0400, Mathieu Desnoyers wrote:
> On 2026-07-08 18:55, Paul E. McKenney wrote:
> [...]
> > OK, I did check, and all of the hazptr_torture_do_pending() function's
> > hazard pointers have been passed to hazptr_detach_from_task().
> >
> > But sometimes a given hazard pointer might be passed to
> > hazptr_detach_from_task() twice, once just before that llist_add(),
> > and again if hazptr_torture_reader_tail() decides to release that hazard
> > pointer via smp_call_function_single().
> >
> > Do I instead need to be careful to avoid detaching a given hazard pointer
> > more than once?
> That should be OK to call the "detach" more than once, because it checks
> if the hazptr is already detached with:
>
> + if (unlikely(hazptr_slot_is_backup(ctx, slot)))
> + return;
>
> (while under preempt off)
>
> My concern is about this worker thread:
>
> static int hazptr_torture_do_pending(void *arg)
> {
> int cpu = 0;
> DEFINE_TORTURE_RANDOM(rand);
>
> VERBOSE_TOROUT_STRING("hazptr_torture_do_pending task started");
> do {
> if (stutter_will_wait()) {
> for_each_possible_cpu(cpu)
> hazptr_torture_do_one_pending(cpu, &rand);
> } else {
> cpu = cpumask_next_wrap(cpu, cpu_possible_mask);
> hazptr_torture_do_one_pending(cpu, &rand);
> }
> if (torture_must_stop())
> torture_hrtimeout_ms(kthread_do_pending_ms, USEC_PER_MSEC, &rand);
> // Omit stutter_wait() because this function needs to do cleanup.
> } while (!torture_must_stop());
> torture_kthread_stopping("hazptr_torture_do_pending");
> return 0;
> }
>
> which as you will notice happily invoke "do_one_pending" on either
> all possible cpus or on a next cpu (non-local), which does:
>
> llhp = per_cpu_ptr(&hazptr_pending, cpu);
> llnp = llist_del_all(llhp);

Except that every item on every CPU's llist has already been detached,
courtesy of the call to hazptr_detach_from_task() from within
hazptr_torture_defer() prior to the llist_add().

> if (!llnp)
> return;
> llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
> hazptr_torture_reader_tail(hppp, trsp);
> atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
> raw_smp_processor_id()));
> kfree(hppp);
> }
>
> And here the reader_tail calls cur_ops->readunlock(hcp, htp); which
> does a hazptr_release from the wrong CPU without previously detaching
> the hazptr from its associated thread as it should.
>
> Does this line of thinking make sense ?

Huh.

There are actually two calls to hazptr_detach_from_task() within
hazptr_torture_defer(), one outside of the guard(preempt) and the other
within, as in I somehow misapplied your patch.

But that would certainly not explain failures in the NOPREEMPT case.

Still, time to ditch the call preceding the guard(preempt) and see
what happens. Like this:

static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_random_state *trsp)
{
int cpu = torture_random(trsp) % nr_cpu_ids;
struct llist_head *llhp;

guard(preempt)();
hazptr_detach_from_task(&hppp->hpp_hc);
cpu = cpumask_next_wrap(cpu, cpu_online_mask);
llhp = per_cpu_ptr(&hazptr_pending, cpu);
llist_add(&hppp->hpp_node, llhp);
atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id()));
}

Except that the same thing happens:

[ 8.042111] Oops: general protection fault, probably for non-canonical address 0xdead000000000110: 0000 [#1] SMP PTI
[ 8.043367] CPU: 8 UID: 0 PID: 135 Comm: hazptr_torture_ Not tainted 7.1.0-rc4-00080-g3d59e48086a0-dirty #1519 PREEMPT(full)
[ 8.044685] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014
[ 8.045626] RIP: 0010:hazptr_synchronize_overflow_list+0x3e/0x90
[ 8.046356] Code: 08 48 89 c6 48 85 db 74 54 4d 8b 65 10 eb 1b e8 48 12 fa 00 f3 90 4c 89 ef e8 4e 14 fa 00 48 89 c6 49 8b 45 10 4c 39 e0 75 41 <48> 8b 43 10 4c 89 ef 48 39 e8 74 d9 48 83 f8 01 74 d3 e8 1b 12 fa
[ 8.048592] RSP: 0000:ffffb289004f7e60 EFLAGS: 00010086
[ 8.049231] RAX: 0000000000000801 RBX: dead000000000100 RCX: 0000000000000003
[ 8.050136] RDX: 0000000000000001 RSI: 0000000000000286 RDI: ffff9006df3d80e0
[ 8.050993] RBP: ffffffff9b002940 R08: 0000000000122a09 R09: 0000000000000008
[ 8.051836] R10: 0000000000000001 R11: ffff9006c1d1b200 R12: 0000000000000801
[ 8.052889] R13: ffff9006df3d80e0 R14: ffffffff9b002940 R15: ffff9006df3d80c0
[ 8.053746] FS: 0000000000000000(0000) GS:ffff90074448f000(0000) knlGS:0000000000000000
[ 8.054641] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 8.055286] CR2: 0000000000000000 CR3: 0000000017830000 CR4: 00000000000006f0
[ 8.056086] Call Trace:
[ 8.056378] <TASK>
[ 8.056648] hazptr_synchronize+0xd1/0x130
[ 8.057118] hazptr_torture_writer+0x180/0x3f0
[ 8.057642] ? __pfx_hazptr_torture_writer+0x10/0x10
[ 8.058241] kthread+0xe5/0x120
[ 8.058629] ? __pfx_kthread+0x10/0x10
[ 8.059090] ret_from_fork+0x1bd/0x220
[ 8.059529] ? __pfx_kthread+0x10/0x10
[ 8.059954] ret_from_fork_asm+0x1a/0x30
[ 8.060417] </TASK>
[ 8.060685] Modules linked in:
[ 8.061041] ---[ end trace 0000000000000000 ]---

So what am I missing here?

Thanx, Paul