On Tue, Sep 22, 2026 at 3:55 PM Boqun Feng <[email protected]> wrote:
>
> On Tue, Sep 22, 2026 at 03:09:49PM +0800, Kunwu Chan wrote:
> > Add hazptr reader and synchronize operations so that synchronize
> > latency can be measured alongside RCU and SRCU.
> >
> > The read side acquires the hazard pointer in readlock() and holds
> > it until readunlock(), matching the RCU/SRCU reader model. The
> > address of a static object serves as the synchronize target, which
> > is stable and never reclaimed. Both normal and expedited sync map
> > to hazptr_synchronize(), since hazptr has no expedited concept.
> >
> > Signed-off-by: Kunwu Chan <[email protected]>
> > ---
> > kernel/rcu/rcuscale.c | 65 ++++++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 64 insertions(+), 1 deletion(-)
> >
> > diff --git a/kernel/rcu/rcuscale.c b/kernel/rcu/rcuscale.c
> > index 1097ec15879c..072ddf9526c3 100644
> > --- a/kernel/rcu/rcuscale.c
> > +++ b/kernel/rcu/rcuscale.c
> > @@ -39,6 +39,7 @@
> > #include <linux/torture.h>
> > #include <linux/vmalloc.h>
> > #include <linux/rcupdate_trace.h>
> > +#include <linux/hazptr.h>
> > #include <linux/sched/debug.h>
> >
> > #include "rcu.h"
> > @@ -418,6 +419,66 @@ static struct rcu_scale_ops tasks_tracing_ops = {
> >
> > #endif // #else // #ifdef CONFIG_TASKS_TRACE_RCU
> >
> > +#if IS_ENABLED(CONFIG_HAZPTR_TORTURE_TEST)
> > +
> > +static int hazptr_scale_obj; /* Stable, non-NULL, never reclaimed. */
>
> You can probaly make hazptr_scale_obj an arrary, and let an updater
> either randomly or round-robin select an object to wait, it'll reflect
> better to a real world workload.
>
> > +static void *hazptr_scale_ptr = &hazptr_scale_obj;
> > +
> > +struct hazptr_scale_state {
> > + struct hazptr_ctx ctx;
> > + void *addr;
> > +};
> > +static DEFINE_PER_CPU(struct hazptr_scale_state, hazptr_scale_state);
> > +
> > +static int hazptr_scale_read_lock(void)
> > +{
> > + struct hazptr_scale_state *state = this_cpu_ptr(&hazptr_scale_state);
> > +
> > + preempt_disable();
>
> I think you can drop the preempt_disable() and preempt_enable() below,
> since the new hazptr_acquire()/hazptr_release() work without them (i.e.
> the hazptr acquisition no longer requires preemption disable).
Thanks, Boqun. Both points make sense.
I'll drop the explicit preempt_disable()/preempt_enable() and use an array of
objects with round-robin selection on the updater side.
Thanks,
Kunwu
>
> Regards,
> Boqun
>
> > + state->addr = hazptr_acquire(&state->ctx, &hazptr_scale_ptr);
> > + return 0;
> > +}
> > +
> > +static void hazptr_scale_read_unlock(int idx)
> > +{
> > + struct hazptr_scale_state *state = this_cpu_ptr(&hazptr_scale_state);
> > +
> > + udelay(10);
> > + hazptr_release(&state->ctx, state->addr);
> > + preempt_enable();
> > +}
> > +
> > +static unsigned long hazptr_scale_completed(void)
> > +{
> > + return 0;
> > +}
> > +
> > +static void hazptr_scale_sync(void)
> > +{
> > + hazptr_synchronize(hazptr_scale_ptr);
> > +}
> > +
> > +static void hazptr_scale_sync_exp(void)
> > +{
> > + hazptr_synchronize(hazptr_scale_ptr);
> > +}
> > +
> > +static struct rcu_scale_ops hazptr_scale_ops = {
> > + .ptype = 0,
> > + .readlock = hazptr_scale_read_lock,
> > + .readunlock = hazptr_scale_read_unlock,
> > + .get_gp_seq = hazptr_scale_completed,
> > + .gp_diff = NULL,
> > + .sync = hazptr_scale_sync,
> > + .exp_sync = hazptr_scale_sync_exp,
> > + .name = "hazptr",
> > +};
> > +
> > +#define HAZPTR_SCALE_OPS &hazptr_scale_ops,
> > +#else
> > +#define HAZPTR_SCALE_OPS
> > +#endif
> > +
> > static unsigned long rcuscale_seq_diff(unsigned long new, unsigned long
> > old)
> > {
> > if (!cur_ops->gp_diff)
> > @@ -1110,7 +1171,9 @@ rcu_scale_init(void)
> > long i;
> > long j;
> > static struct rcu_scale_ops *scale_ops[] = {
> > - &rcu_ops, &srcu_ops, &srcud_ops, TASKS_OPS TASKS_RUDE_OPS
> > TASKS_TRACING_OPS
> > + &rcu_ops, &srcu_ops, &srcud_ops,
> > + TASKS_OPS TASKS_RUDE_OPS TASKS_TRACING_OPS
> > + HAZPTR_SCALE_OPS
> > };
> >
> > if (!torture_init_begin(scale_type, verbose))
> > --
> > 2.43.0
> >