On Mon, 28 Sep 2026 14:11:24 +0530 Shashank Mohan Jain <[email protected]>
wrote:
> objpool_push() adds an object to the slot of the local CPU with
> interrupts disabled. It reserves an entry with a cmpxchg() on
> slot->tail, writes the entry, and publishes it with
> smp_store_release(&slot->last, tail + 1).
>
> A push from NMI context can still interrupt it and push to the same
> slot. kretprobes may run in NMI context since commit e03b4a084ea6
> ("kprobes: Remove NMI context check"). At the time, kretprobe instances
> came from a CAS-based lockless freelist, which tolerates that. Commit
> 4bbd93455659 ("kprobes: kretprobe scalability improvement") moved
> kretprobes and rethook to objpool.
>
> With rethook, rethook_trampoline_handler() recycles instances with
> objpool_push() after the user handler has run, when no kprobe is marked
> running anymore; rethook_flush_task() does the same. An NMI that arrives
> during such a push and runs a function probed by the same kretprobe takes
> an instance in pre_handler_kretprobe(), and when the function returns
> inside the NMI, pushes it back to the same slot. This needs a kretprobe
> on a function that runs both in NMI context and outside it, for instance
> one that perf calls from the PMU NMI handler on x86. Before v6.14, fprobe
> also used rethook and could push from NMI the same way, with one pool
> shared by all functions of an fprobe.
>
> ...
>
> Publish the entries in order instead:
Thanks.
> --- a/include/linux/objpool.h
> +++ b/include/linux/objpool.h
> @@ -193,19 +193,40 @@ __objpool_try_add_slot(void *obj, struct objpool_head
> *pool, int cpu)
> struct objpool_slot *slot = pool->cpu_slots[cpu];
> uint32_t head, tail;
>
> - /* loading tail and head as a local snapshot, tail first */
> + /*
> + * Only the local CPU pushes to its slot, with irqs disabled, but a
> + * push from NMI context (a kretprobe'd function returning in NMI)
> + * can interrupt this one at any point.
> + */
> tail = READ_ONCE(slot->tail);
> + while (!try_cmpxchg_acquire(&slot->tail, &tail, tail + 1))
> + ;
>
> - do {
> - head = READ_ONCE(slot->head);
> - /* fault caught: something must be wrong */
> - WARN_ON_ONCE(tail - head > pool->nr_objs);
> - } while (!try_cmpxchg_acquire(&slot->tail, &tail, tail + 1));
> + /*
> + * fault caught: something must be wrong. Read head only after the
> + * reservation: a nested push and a pop on another CPU could have
> + * moved head past an older snapshot of tail.
> + */
> + head = READ_ONCE(slot->head);
> + WARN_ON_ONCE(tail - head > pool->nr_objs);
This code can run in NMI?
Calling WARN_ON from NMI sounds quite sketchy - the warning handler
does all sorts of stuff. I see this is pre-existing but perhaps this
is a chance to address it.
Sashiko liked [1/2] but had a lot to say about the test module:
https://sashiko.dev/#/patchset/[email protected]