Re: [PATCH v6] softirq: Preserve interrupt context during IRQ exit

From: Frederic Weisbecker

Date: Wed Oct 14 2026 - 09:28:55 EST


Le Sun, Oct 11, 2026 at 08:07:42AM +0200, Karl Mehltretter a écrit :
> __irq_exit_rcu() removes HARDIRQ_OFFSET from preempt_count() before
> the IRQ exit work. On the outermost IRQ exit, with no NMI or softirq
> state remaining, this makes context checks report task context. The
> softirq-dispatch, timer-wakeup and tick-exit checks account for the early
> decrement, but other code reached in this window sees the wrong context:
>
> - ftrace, perf and the ring buffer record task context and use the
> task recursion and context slots.
> - KCSAN attributes accesses to the interrupted task. KMSAN uses and
> changes the interrupted task's KMSAN context. KCOV and the printk
> caller id see a task.
> - On PREEMPT_RT can_spin_trylock() and local_trylock() reject hard
> interrupt context to avoid interfering with PI when the interrupted
> task is blocked on a lock. Those checks do not reject calls made in
> this window. BPF programs attached to sched_waking or sched_wakeup
> can reach the trylock path through kmalloc_nolock().
> - An oops kills the interrupted task instead of ending in "Fatal
> exception in interrupt".
>
> The early decrement predates git. No failure caused by this
> misclassification is known. lockdep is unaffected because
> lockdep_hardirq_exit() is the last operation in irq_exit().
>
> Keep HARDIRQ_OFFSET until the end of __irq_exit_rcu(), including
> tick_irq_exit(). Softirq handlers must run in BH context, so replace
> HARDIRQ_OFFSET with SOFTIRQ_OFFSET while they run and restore it
> afterwards. Use one raw counter update for each transition to preserve
> the preemption disable location recorded by irq_enter_rcu(), and update
> lockdep explicitly. Detect IRQ exit with in_hardirq() in
> softirq_handle_begin() because __do_softirq() can be reached through
> do_softirq_own_stack() and cannot take an argument.
>
> Adjust the exit checks for HARDIRQ_OFFSET still being present. Use
> irq_count() == HARDIRQ_OFFSET for softirq dispatch, as in irq_enter_rcu().
> Use hardirq_count() == HARDIRQ_OFFSET for the timer thread wakeup and
> tick_irq_exit(). NMI exits use a separate path. An IRQ nested in an NMI
> has an extra HARDIRQ_OFFSET from NMI entry, so it is excluded too. The
> timer thread check must ignore softirq_count() to allow wakeups when an
> IRQ interrupts softirq processing or a BH-disabled section. The timer
> thread handles all pending softirqs, including any raised during its
> wakeup.
>
> A softirq raised from a tracepoint on the final preempt_count_sub()
> waits for the next interrupt exit and can trigger NOHZ tick-stop
> warnings. Handling that instrumentation case does not justify a check
> on every interrupt exit. A tracepoint on tick_irq_exit() can also raise
> a softirq after the normal softirq handling has finished.
>
> The number of counter updates and interrupt time accounting are
> unchanged. The preemptoff tracer now reports the interrupt and subsequent
> softirq processing as one section. With nofuncgraph-irqs, function graph
> tracing also skips the IRQ exit work, including the __do_softirq() frame.
>
> Suggested-by: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
> Link: https://lore.kernel.org/r/20260813130826.GW687043@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
> Assisted-by: LLM
> Signed-off-by: Karl Mehltretter <kmehltretter@xxxxxxxxx>

Reviewed-by: Frederic Weisbecker <frederic@xxxxxxxxxx>

--
Frederic Weisbecker
SUSE Labs