Re: [RFC PATCH v4 3/4] hazptr: Implement Hazard Pointers

Joel Fernandes <[email protected]> Fri, 19 Dec 2025 01:06:16 -0500
Newsgroups dev.linux.lists.lkmm,org.kernel.vger.linux-kernel,org.kernel.vger.rcu,org.kvack.linux-mm
Message-ID <[email protected]>
Hi Boqun,

> On Dec 18, 2025, at 9:07=E2=80=AFPM, Boqun Feng <[email protected]> wro=
te:
>=20
> =EF=BB=BFOn Thu, Dec 18, 2025 at 06:36:00PM -0500, Mathieu Desnoyers wrote=
:
>> On 2025-12-18 15:22, Boqun Feng wrote:
>> [...]
>>>>> Could you utilize this[1] to see a
>>>>> comparison of the reader-side performance against RCU/SRCU?
>>>>=20
>>>> Good point ! Let's see.
>>>>=20
>>>> On a AMD 2x EPYC 9654 96-Core Processor with 192 cores,
>>>> hyperthreading disabled,
>>>> CONFIG_PREEMPT=3Dy,
>>>> CONFIG_PREEMPT_RCU=3Dy,
>>>> CONFIG_PREEMPT_HAZPTR=3Dy.
>>>>=20
>>>> scale_type                 ns
>>>> -----------------------
>>>> hazptr-smp-mb             13.1   <- this implementation
>>>> hazptr-barrier            11.5   <- replace smp_mb() on acquire with ba=
rrier(), requires IPIs on synchronize.
>>>> hazptr-smp-mb-hlist       12.7   <- replace per-task hp context and per=
-cpu overflow lists by hlist.
>>>> rcu                       17.0
>>>> srcu                      20.0
>>>> srcu-fast                  1.5
>>>> rcu-tasks                  0.0
>>>> rcu-trace                  1.7
>>>> refcnt                  1148.0
>>>> rwlock                  1190.0
>>>> rwsem                   4199.3
>>>> lock                   41070.6
>>>> lock-irq               46176.3
>>>> acqrel                     1.1
>>>>=20
>>>> So only srcu-fast, rcu-tasks, rcu-trace and a plain acqrel
>>>> appear to beat hazptr read-side performance.
>>>>=20
>>>=20
>>> Could you also see the reader-side performance impact when the percpu
>>> hazard pointer slots are used up? I.e. the worst case.
>>=20
>> I've modified the code to populate "(void *)1UL" in the 7 first slots
>> at bootup, here is the result:
>>=20
>> hazptr-smp-mb-7-fail    16.3 ns
>>=20
>> So we go from 13.1 ns to 16.3 ns when all but one slots are used.
>>=20
>> And if we pre-populate the 8 slots for each cpu, and thus force
>> fallback to overflow list:
>>=20
>> hazptr-smp-mb-8-fail    67.1 ns
>>=20
>=20
> Thank you! So involving locking seems to hurt performance more than
> per-CPU/per-task operations. This may suggest that enabling
> PREEMPT_HAZPTR by default has an acceptable performance.

My impression is we do other locking on preemption anyway, such as the block=
 list for preempted RCU critical sections. So maybe that's okay. As you said=
, it is an acceptable performance.

>>>=20
>>>> [...]
>>>>=20
>>>>>> +/*
>>>>>> + * Perform piecewise iteration on overflow list waiting until "addr"=
 is
>>>>>> + * not present. Raw spinlock is released and taken between each list=

>>>>>> + * item and busy loop iteration. The overflow list generation is che=
cked
>>>>>> + * each time the lock is taken to validate that the list has not cha=
nged
>>>>>> + * before resuming iteration or busy wait. If the generation has
>>>>>> + * changed, retry the entire list traversal.
>>>>>> + */
>>>>>> +static
>>>>>> +void hazptr_synchronize_overflow_list(struct overflow_list *overflow=
_list, void *addr)
>>>>>> +{
>>>>>> +    struct hazptr_backup_slot *backup_slot;
>>>>>> +    uint64_t snapshot_gen;
>>>>>> +
>>>>>> +    raw_spin_lock(&overflow_list->lock);
>>>>>> +retry:
>>>>>> +    snapshot_gen =3D overflow_list->gen;
>>>>>> +    list_for_each_entry(backup_slot, &overflow_list->head, node) {
>>>>>> +        /* Busy-wait if node is found. */
>>>>>> +        while (smp_load_acquire(&backup_slot->slot.addr) =3D=3D addr=
) { /* Load B */
>>>>>> +            raw_spin_unlock(&overflow_list->lock);
>>>>>> +            cpu_relax();
>>>>>=20
>>>>> I think we should prioritize the scan thread solution [2] instead of
>>>>> busy waiting hazrd pointer updaters, because when we have multiple
>>>>> hazard pointer usages we would want to consolidate the scans from
>>>>> updater side.

Yeah the scan thread idea also fixes the scan cost issue with per-task slots=
 if we batch. If we implement a separate hazard pointer flavor along those l=
ines, then maybe we should definitely do a worker thread.

>>>> I agree that batching scans with a worker thread is a logical next step=
.
>>>>=20
>>>>> If so, the whole ->gen can be avoided.
>>>>=20
>>>> How would it allow removing the generation trick without causing long
>>>> raw spinlock latencies ?
>>>>=20
>>>=20
>>> Because we won't need to busy-wait for the readers to go away, we can
>>> check whether they are still there in the next scan.
>>>=20
>>> so:
>>>=20
>>>    list_for_each_entry(backup_slot, &overflow_list->head, node) {
>>>        /* Busy-wait if node is found. */
>>>        if (smp_load_acquire(&backup_slot->slot.addr) =3D=3D addr) { /* L=
oad B */
>>>            <mark addr as unable to free and move on>
>>=20
>> But then you still iterate on a possibly large list of overflow nodes,
>> with a raw spinlock held. That raw spinlock is taken by the scheduler
>> on context switch. This can cause very long scheduler latency.
>>=20
>=20
> That's fair.

What about combining both approaches? =20
Can we do the generation trick along with worker thread scanning? I feel tha=
t should be doable.

>=20
>> So breaking up the iteration into pieces is not just to handle
>> busy-waiting, but also to make sure we don't increase the
>> system latency by holding a raw spinlock (taken with rq lock
>> held) for more than the little time needed to iterate to the next
>> node.
>>=20
>=20
> I agree that it helps reduce the latency, but I feel like with a scan
> thread in the picture (and we don't need to busy-wait), we should use
> a forward-progress-guaranteed way in the updater side scan, which means
> we may need to explore other solutions for the latency (e.g.
> fine-grained locking hashlist for the overflow list) than the generation
> counter.

Hmm.. That only works I guess if there is no interference between the finger=
 grained list being iterated and the list being overflowed into. Otherwise, I=
 think it might run into the same issue, but I could be missing something ab=
out the idea.

thanks,

 - Joel


>=20
> Regards,
> Boqun
>=20
>> Thanks,
>>=20
>> Mathieu
>>=20
>> --
>> Mathieu Desnoyers
>> EfficiOS Inc.
>> https://www.efficios.com
>=20