From: Karl Mehltretter <kmehltretter@gmail.com>
To: Steven Rostedt <rostedt@goodmis.org>,
Gabriele Monaco <gmonaco@redhat.com>
Cc: Karl Mehltretter <kmehltretter@gmail.com>,
Masami Hiramatsu <mhiramat@kernel.org>,
Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
Thomas Gleixner <tglx@kernel.org>,
Sebastian Andrzej Siewior <bigeasy@linutronix.de>,
Clark Williams <clrkwllms@kernel.org>,
Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Andy Lutomirski <luto@kernel.org>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
Wen Yang <wen.yang@linux.dev>,
linux-trace-kernel@vger.kernel.org,
linux-rt-devel@lists.linux.dev, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [RFC PATCH v2 1/9] tracing/irq: Add generic IRQ context boundary tracepoints
Date: Sat, 7 Nov 2026 18:12:44 +0100 [thread overview]
Message-ID: <20261107171252.25090-2-kmehltretter@gmail.com> (raw)
In-Reply-To: <20261107171252.25090-1-kmehltretter@gmail.com>
IRQ action tracepoints describe handlers, not the enclosing interrupt
context. One hardirq context may run several actions, while synthetic
dispatch can invoke an action outside hardirq context.
Add bare tracepoints at the generic hardirq and serving-softirq
boundaries using DECLARE_TRACE() so they can be hooked by in-kernel
verifiers and tests without exposing a new user-space tracefs ABI.
Record hardirq depth and whether serving-softirq state temporarily
replaces hardirq state during interrupt return.
A boundary callback can raise work after the normal pending-work
decision. Include work raised at softirq entry in the initial pending
snapshot, and hand work raised at a final boundary to the appropriate
worker.
The tracepoints cover irq_enter() or irq_enter_rcu() and their matching
exits. Raw and direct low-level entry users remain outside their scope.
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
include/trace/events/irq.h | 56 +++++++++++++++++++++++++
kernel/softirq.c | 83 +++++++++++++++++++++++++++++++++++++-
2 files changed, 137 insertions(+), 2 deletions(-)
diff --git a/include/trace/events/irq.h b/include/trace/events/irq.h
index 837c1740d0d07..1b2e05aaf7763 100644
--- a/include/trace/events/irq.h
+++ b/include/trace/events/irq.h
@@ -207,6 +207,62 @@ DEFINE_EVENT(tasklet, tasklet_exit,
TP_ARGS(t, func)
);
+/*
+ * Following tracepoints are not exported in tracefs and provide hooking
+ * mechanisms only for testing and debugging purposes.
+ */
+
+/*
+ * hardirq_context_entry - generic hardirq state has been established
+ * @depth: hardirq nesting depth, including the context being entered
+ *
+ * This tracepoint is emitted after generic hardirq preempt-count, lockdep,
+ * timer, and accounting state has been established. It does not describe the
+ * architecture-specific interval before irq_enter() or irq_enter_rcu().
+ */
+DECLARE_TRACE(hardirq_context_entry,
+ TP_PROTO(unsigned int depth),
+ TP_ARGS(depth));
+
+/*
+ * hardirq_context_exit - generic hardirq state is about to be removed
+ * @depth: hardirq nesting depth, including the context being exited
+ *
+ * This tracepoint is emitted with interrupts disabled near the end of generic
+ * hardirq exit, after tick-exit work and before hardirq preempt-count state is
+ * removed. A late-work handoff may run after callbacks return. Hardirq
+ * accounting ends earlier.
+ */
+DECLARE_TRACE(hardirq_context_exit,
+ TP_PROTO(unsigned int depth),
+ TP_ARGS(depth));
+
+/*
+ * softirq_context_entry - serving-softirq state has been established
+ * @serving: whether the current task is serving a softirq
+ * @from_hardirq: whether hardirq state was replaced by serving-softirq state
+ *
+ * This tracepoint runs with local interrupts disabled after the serving bit is
+ * set. On PREEMPT_RT it includes preparation before handle_softirqs(), even
+ * if the pending-work check finds nothing. It does not mark an individual
+ * action or the start of CPU-time accounting.
+ */
+DECLARE_TRACE(softirq_context_entry,
+ TP_PROTO(bool serving, bool from_hardirq),
+ TP_ARGS(serving, from_hardirq));
+
+/*
+ * softirq_context_exit - serving-softirq state is about to be removed
+ * @serving: whether the current task is serving a softirq
+ * @from_hardirq: whether hardirq state will replace serving-softirq state
+ *
+ * This tracepoint runs with local interrupts disabled before the serving bit is
+ * cleared. On PREEMPT_RT this includes teardown after handle_softirqs().
+ */
+DECLARE_TRACE(softirq_context_exit,
+ TP_PROTO(bool serving, bool from_hardirq),
+ TP_ARGS(serving, from_hardirq));
+
#endif /* _TRACE_IRQ_H */
/* This part must be outside protection */
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 7fc2200392cdf..0d5fe265e354b 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -178,7 +178,9 @@ bool local_bh_blocked(void)
void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{
+ unsigned long trace_flags = 0;
unsigned long flags;
+ bool trace_serving;
int newcnt;
WARN_ON_ONCE(in_hardirq());
@@ -200,6 +202,16 @@ void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
}
}
+ /*
+ * A serving interval starts here, before handle_softirqs(). When its
+ * entry is traced, keep IRQs and task switches from observing the bit
+ * before the event. Any blocking BH lock was acquired above.
+ */
+ trace_serving = (cnt & SOFTIRQ_OFFSET) &&
+ trace_softirq_context_entry_tp_enabled();
+ if (trace_serving)
+ local_irq_save(trace_flags);
+
/*
* Track the per CPU softirq disabled state. On RT this is per CPU
* state to allow preemption of bottom half disabled sections.
@@ -233,15 +245,27 @@ void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
raw_local_irq_restore(flags);
}
}
+
+ if (trace_serving) {
+ trace_softirq_context_entry_tp(!!in_serving_softirq(), false);
+ local_irq_restore(trace_flags);
+ }
}
EXPORT_SYMBOL(__local_bh_disable_ip);
static void __local_bh_enable(unsigned int cnt, bool unlock)
{
+ bool entering = (cnt & SOFTIRQ_OFFSET) && !in_serving_softirq();
+ bool trace_exiting = (cnt & SOFTIRQ_OFFSET) && !entering &&
+ trace_softirq_context_exit_tp_enabled();
unsigned long flags;
bool sirq_en = false;
int newcnt;
+ /* Callers already disabled IRQs around the counter and its events. */
+ if (trace_exiting)
+ trace_softirq_context_exit_tp(!!in_serving_softirq(), false);
+
if (IS_ENABLED(CONFIG_PREEMPT_RT_NEEDS_BH_LOCK)) {
DEBUG_LOCKS_WARN_ON(current->softirq_disable_cnt !=
this_cpu_read(softirq_ctrl.cnt));
@@ -276,6 +300,18 @@ static void __local_bh_enable(unsigned int cnt, bool unlock)
WARN_ON_ONCE(current->softirq_disable_cnt < 0);
}
}
+
+ /*
+ * An exit probe can raise a softirq while serving state still suppresses
+ * the normal ksoftirqd wakeup. Hand it off once no outer BH-disable
+ * reference remains.
+ */
+ if (trace_exiting && sirq_en && unlikely(local_softirq_pending()))
+ wakeup_softirqd();
+
+ /* local_bh_enable() can turn BH-disabled state into serving state. */
+ if (entering)
+ trace_softirq_context_entry_tp(!!in_serving_softirq(), false);
}
void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
@@ -640,11 +676,15 @@ static void handle_softirqs(bool ksirqd)
*/
current->flags &= ~PF_MEMALLOC;
- pending = local_softirq_pending();
-
from_irq_exit = softirq_handle_begin();
in_hardirq = lockdep_softirq_start();
account_softirq_enter(current);
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+ trace_softirq_context_entry_tp(!!in_serving_softirq(),
+ from_irq_exit);
+
+ /* Include work raised by the context-entry event. */
+ pending = local_softirq_pending();
restart:
/* Reset the pending bitmask before enabling irqs */
@@ -692,6 +732,14 @@ static void handle_softirqs(bool ksirqd)
wakeup_softirqd();
}
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT) &&
+ trace_softirq_context_exit_tp_enabled()) {
+ trace_softirq_context_exit_tp(!!in_serving_softirq(),
+ from_irq_exit);
+ /* The final pending-work decision above has already completed. */
+ if (unlikely(local_softirq_pending()))
+ wakeup_softirqd();
+ }
account_softirq_exit(current);
lockdep_softirq_end(in_hardirq);
softirq_handle_end(from_irq_exit);
@@ -722,6 +770,7 @@ void irq_enter_rcu(void)
tick_irq_enter();
account_hardirq_enter(current);
+ trace_hardirq_context_entry_tp(hardirq_count() >> HARDIRQ_SHIFT);
}
/**
@@ -796,6 +845,36 @@ static inline void __irq_exit_rcu(void)
wake_timersd();
tick_irq_exit();
+ if (trace_hardirq_context_exit_tp_enabled()) {
+ bool ksoftirqd_wake_requested = false;
+ bool timersd_wake_requested = false;
+
+ trace_hardirq_context_exit_tp(hardirq_count() >> HARDIRQ_SHIFT);
+
+ /* Hand work raised by the final boundary event to its worker. */
+ if (hardirq_count() == HARDIRQ_OFFSET) {
+ if (irq_count() == HARDIRQ_OFFSET &&
+ unlikely(local_softirq_pending()) &&
+ should_wake_ksoftirqd()) {
+ wakeup_softirqd();
+ ksoftirqd_wake_requested = true;
+ }
+
+ if (IS_ENABLED(CONFIG_IRQ_FORCED_THREADING) &&
+ force_irqthreads() && local_timers_pending_force_th()) {
+ wake_timersd();
+ timersd_wake_requested = true;
+ }
+
+ /* The timersd wakeup can itself raise a softirq. */
+ if (timersd_wake_requested &&
+ !ksoftirqd_wake_requested &&
+ irq_count() == HARDIRQ_OFFSET &&
+ unlikely(local_softirq_pending()) &&
+ should_wake_ksoftirqd())
+ wakeup_softirqd();
+ }
+ }
preempt_count_sub(HARDIRQ_OFFSET);
}
--
2.39.5 (Apple Git-154)
next prev parent reply other threads:[~2026-11-07 17:13 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 17:12 [RFC PATCH v2 0/9] rv: Verify generic IRQ context transitions Karl Mehltretter
2026-11-07 17:12 ` Karl Mehltretter [this message]
2026-11-07 17:12 ` [RFC PATCH v2 2/9] tracing/sched: Add IRQ preemption lifecycle tracepoints Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 3/9] rv: Add a monitor for generic IRQ context transitions Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 4/9] rv: Add a monitor for IRQ-exit scheduling ownership Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 5/9] docs/rv: Document the IRQ monitors Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 6/9] rv: Add KUnit coverage for IRQ context transitions Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 7/9] rv: Add KUnit coverage for IRQ-exit scheduling Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 8/9] rv: Test empty PREEMPT_RT softirq worker intervals Karl Mehltretter
2026-11-07 17:12 ` [RFC PATCH v2 9/9] rv: Test IRQ-exit softirq provenance and handoff Karl Mehltretter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261107171252.25090-2-kmehltretter@gmail.com \
--to=kmehltretter@gmail.com \
--cc=bigeasy@linutronix.de \
--cc=bsegall@google.com \
--cc=clrkwllms@kernel.org \
--cc=corbet@lwn.net \
--cc=dietmar.eggemann@arm.com \
--cc=gmonaco@redhat.com \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=luto@kernel.org \
--cc=mathieu.desnoyers@efficios.com \
--cc=mgorman@suse.de \
--cc=mhiramat@kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rdunlap@infradead.org \
--cc=rostedt@goodmis.org \
--cc=skhan@linuxfoundation.org \
--cc=tglx@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=wen.yang@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®