freenode
Kernel & Low-Level

RFC lets Tasks RCU treat ordinary preemption as quiescent

Josef Bacik proposes trampoline nesting so PREEMPT_LAZY kernels stop stalling BPF and kprobe teardown behind long-running kthreads.

Josef Bacik has posted an RFC series on the BPF kernel list that would let Tasks RCU treat preemption outside trampolines as a quiescent state, aiming to stop multi-minute stalls that can panic machines when BPF programs, ftrace ops, or kprobes are torn down under PREEMPT_LAZY.

Tasks RCU exists so ftrace, BPF, and kprobes can free trampoline text only after no task can still be executing in it. Today the only signals it accepts are a voluntary context switch, return to usermode, or idle. A preempted task is always assumed to sit inside a trampoline. That trade-off was tolerable when server builds compiled Tasks RCU away and desktops rarely ran long in-kernel loops. PREEMPT_LAZY changes both halves: Tasks RCU is live on server configs, and cond_resched() is a no-op, so a CPU-bound kthread or kworker only loses the CPU by being preempted, the event Tasks RCU refuses to count.

The failure showed up as a cgroup writeback worker draining a large cgwb for about eleven minutes on arm64. A BPF detach on another CPU reached synchronize_rcu_tasks() while holding trampoline_mutex; more than forty tasks piled up behind the mutex and the hung-task detector panicked the machine. The kprobe jump optimizer is worse in principle: it waits under kprobe_mutex, text_mutex, and the CPU hotplug lock, so one long-running kthread can stall static-key updates and hotplug for its whole run. The current answer is to find each such loop and add cond_resched_tasks_rcu_qs(), the kind of annotation lazy preemption was meant to retire.

Bacik's series takes the other direction. A per-task nesting counter is incremented on trampoline entry and decremented on exit for ftrace callers and their dynamic copies, BPF trampoline images, the x86 optprobe template, and out-of-line direct trampolines, with matching changes on x86-64 and arm64. While the count is non-zero the task is not Tasks-RCU quiescent; preemption anywhere else can be. A few-instruction window around the counter updates is covered on irq-exit preemption by checking the interrupted instruction pointer, treating dynamically allocated text, certain static ftrace stubs, return thunks, and kprobe-optimized regions as still protected. Direct-call trampolines in module text get a sticky module flag so the same path covers them. rcutorture's tasks flavor is adjusted to model a trampoline rather than arbitrary kernel code, and the ftrace direct-call samples follow the new contract.

Nothing enables the full preemption-as-quiescent path yet; architectures would opt in later. The series is an RFC exploring whether trampolines can state their own presence instead of forcing every long kernel loop to annotate for Tasks RCU.