Re: [EXT.MSG] Re: [PATCH RFC bpf-next 0/2] bpf: Do not let preemption cause false recursion drops in trampoline progs

From: Mike Bazov

Date: Thu Nov 05 2026 - 18:35:57 EST


On Thu Nov 5, 2026 at 5:14 AM UTC, Alexei Starovoitov wrote:
> Also try NO_HRTICK in /sys/kernel/debug/sched/features.
> That may be decent workaround for now.

The 7.0 numbers I posted were already with NO_HRTICK, and either way
our agent runs on customer machines, so changing scheduler settings
there isn't an option for us.

> hmm. you really cannot tolerate that small drop? it's tracing at the end.

For security monitoring each skip is an event we never see, and the one
we miss can be exactly the one that shows an attack. For stateful
protocol parsing, one skipped sendmsg/recvmsg can desync the parser for
the rest of that connection.

> let's try to think how per-task counter would work.
> I'm guessing 128 connections is 128 threads servicing them,
> so 200 cpus will be hammering the same per-task counter which will
> cause real perf loss.
> Gotta do better than per-task.
> Also it needs to be per-task + per-prog. How will it look like?

On Thu, Nov 05, 2026 at 01:26 PM Peter Zijlstra wrote:
> I would strongly prefer all BPF programs that attach to known
> preemptible sites to use a per-task recursion counter or something of
> that sort.

This is what I have in mind for v2:

- Every trampoline and raw_tp run already has a run ctx on the running
task's stack. Add the prog pointer to it, and link the task's active
run ctxs through one new pointer in task_struct (other prog types
don't touch it), so each task has a short list of the progs it is
currently inside.
- On entry, walk only the current task's list. If the prog is already
there, it's real recursion (the same task, or an irq/nmi on top of it,
since current doesn't change there): skip it, as today. Otherwise push
the entry, run the prog, and pop it on exit.
- A preempted task's entry stays on its own list, so another task on
the same cpu is no longer blocked by it.
- Cost: the list is empty unless the task is genuinely nested, so the
check is one load and one predictable branch, with no atomics and
nothing shared between tasks or cpus. The walk is capped (e.g. 8),
which also bounds nesting depth. It also works across sleep for
sleepable progs.

To keep the per-cpu guarantee for existing progs:
- Make it opt-in: a prog load flag for fentry/fexit and raw_tp progs
(like BPF_F_SLEEPABLE) saying the prog may run concurrently on one
cpu, and that it doesn't rely on per-cpu exclusivity for its own data
(per-cpu scratch buffers etc.).
- Flagged progs get no private stack. The verifier could also enforce
parts of the per-cpu promise if you prefer.
- Unflagged progs keep today's prog->active check, so existing progs
see no change.

What do you think?

Mike