> +/* > + * hp_allocate: Allocate a hazard pointer. > + * > + * Allocate a hazard pointer slot for @addr. The object existence should > + * be guaranteed by the caller. Expects to be called from preempt > + * disable context. > + * > + * Returns a hazard pointer context. So you made the WTF'o'meter crack, this here function does not allocate nothing. Naming is bad. At best this is something like try-set-hazard-pointer or somesuch. > + */ > +static inline > +struct hp_ctx hp_allocate(struct hp_slot __percpu *percpu_slots, void *addr) > +{ > + struct hp_slot *slot; > + struct hp_ctx ctx; > + > + if (!addr) > + goto fail; > + slot = this_cpu_ptr(percpu_slots); > + /* > + * A single hazard pointer slot per CPU is available currently. > + * Other hazard pointer domains can eventually have a different > + * configuration. > + */ > + if (READ_ONCE(slot->addr)) > + goto fail; > + WRITE_ONCE(slot->addr, addr); /* Store B */ > + ctx.slot = slot; > + ctx.addr = addr; > + return ctx; > + > +fail: > + ctx.slot = NULL; > + ctx.addr = NULL; > + return ctx; > +} > + > +/* > + * hp_dereference_allocate: Dereference and allocate a hazard pointer. > + * > + * Returns a hazard pointer context. Expects to be called from preempt > + * disable context. > + */ More terrible naming. Same as above, but additionally, I would expect a 'dereference' to actually dereference the pointer and have a return value of the dereferenced type. This function seems to double check and update the hp_ctx thing. I'm not at all sure yet wtf this is doing -- and the total lack of comments aren't helping. > +static inline > +struct hp_ctx hp_dereference_allocate(struct hp_slot __percpu *percpu_slots, void * const * addr_p) > +{ > + void *addr, *addr2; > + struct hp_ctx ctx; > + > + addr = READ_ONCE(*addr_p); > +retry: > + ctx = hp_allocate(percpu_slots, addr); > + if (!hp_ctx_addr(ctx)) > + goto fail; > + /* Memory ordering: Store B before Load A. */ > + smp_mb(); > + /* > + * Use RCU dereference without lockdep checks, because > + * lockdep is not aware of HP guarantees. > + */ > + addr2 = rcu_access_pointer(*addr_p); /* Load A */ > + /* > + * If @addr_p content has changed since the first load, > + * clear the hazard pointer and try again. > + */ > + if (!ptr_eq(addr2, addr)) { > + WRITE_ONCE(ctx.slot->addr, NULL); > + if (!addr2) > + goto fail; > + addr = addr2; > + goto retry; > + } > + /* > + * Use addr2 loaded from rcu_access_pointer() to preserve > + * address dependency ordering. > + */ > + ctx.addr = addr2; > + return ctx; > + > +fail: > + ctx.slot = NULL; > + ctx.addr = NULL; > + return ctx; > +} > + > +/* Retire the hazard pointer in @ctx. */ > +static inline > +void hp_retire(const struct hp_ctx ctx) > +{ > + smp_store_release(&ctx.slot->addr, NULL); > +} > + > +#endif /* _LINUX_HP_H */ > diff --git a/kernel/Makefile b/kernel/Makefile > index 3c13240dfc9f..ec16de96fa80 100644 > --- a/kernel/Makefile > +++ b/kernel/Makefile > @@ -7,7 +7,7 @@ obj-y = fork.o exec_domain.o panic.o \ > cpu.o exit.o softirq.o resource.o \ > sysctl.o capability.o ptrace.o user.o \ > signal.o sys.o umh.o workqueue.o pid.o task_work.o \ > - extable.o params.o \ > + extable.o params.o hp.o \ > kthread.o sys_ni.o nsproxy.o \ > notifier.o ksysfs.o cred.o reboot.o \ > async.o range.o smpboot.o ucount.o regset.o ksyms_common.o > diff --git a/kernel/hp.c b/kernel/hp.c > new file mode 100644 > index 000000000000..b2447bf15300 > --- /dev/null > +++ b/kernel/hp.c > @@ -0,0 +1,46 @@ > +// SPDX-FileCopyrightText: 2024 Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx> > +// > +// SPDX-License-Identifier: LGPL-2.1-or-later > + > +/* > + * HP: Hazard Pointers > + */ > + > +#include <linux/hp.h> > +#include <linux/percpu.h> > + > +/* > + * hp_scan: Scan hazard pointer domain for @addr. > + * > + * Scan hazard pointer domain for @addr. > + * If @retire_cb is non-NULL, invoke @callback for each slot containing > + * @addr. > + * Wait to observe that each slot contains a value that differs from > + * @addr before returning. > + */ > +void hp_scan(struct hp_slot __percpu *percpu_slots, void *addr, > + void (*retire_cb)(int cpu, struct hp_slot *slot, void *addr)) > +{ > + int cpu; > + > + /* > + * Store A precedes hp_scan(): it unpublishes addr (sets it to > + * NULL or to a different value), and thus hides it from hazard > + * pointer readers. > + */ > + > + if (!addr) > + return; > + /* Memory ordering: Store A before Load B. */ > + smp_mb(); > + /* Scan all CPUs slots. */ > + for_each_possible_cpu(cpu) { > + struct hp_slot *slot = per_cpu_ptr(percpu_slots, cpu); > + > + if (retire_cb && smp_load_acquire(&slot->addr) == addr) /* Load B */ > + retire_cb(cpu, slot, addr); Is retirce_cb allowed to cmpxchg the thing? > + /* Busy-wait if node is found. */ > + while ((smp_load_acquire(&slot->addr)) == addr) /* Load B */ > + cpu_relax(); This really should be using smp_cond_load_acquire() > + } > +}