From: sashiko-bot@kernel.org
To: "Karl Mehltretter" <kmehltretter@gmail.com>
Cc: linux-trace-kernel@vger.kernel.org,
linux-rt-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v2 10/13] entry: Classify ordinary hardirq return-tail work as hardirq context
Date: Wed, 28 Oct 2026 03:57:39 +0000 [thread overview]
Message-ID: <sashiko-outbox-187457@kernel.org> (raw)
In-Reply-To: <20261028033127.82193-11-kmehltretter@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] entry: irqentry_exit_to_kernel_mode_after_preempt() breaks preemptirqsoff
- [Medium] softirq: irqentry_hardirq_tail_exit() repeats wakeups with wake_flags = 0
- [Medium] entry: irqentry_hardirq_tail_exit() wakes softirq threads after preempt check
--
commit d2af631b0f352ba4a12e0eb8fa80f5ff66476c74
Author: Karl Mehltretter <kmehltretter@gmail.com>
entry: Classify ordinary hardirq return-tail work as hardirq context
This commit stages a fresh hardirq-count attempt and restores lockdep
classification across traceable return-tail work on return from ordinary
hardirqs. It adds irqentry_hardirq_tail_enter() and
irqentry_hardirq_tail_exit() around the post-preemption return tail so
return-tail callbacks run as hardirq context.
> diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h
> index 6f649f6613312..f51aafc1b49fd 100644
> --- a/include/linux/irq-entry-common.h
> +++ b/include/linux/irq-entry-common.h
> @@ -383,6 +383,32 @@ irqentry_hardirq_count_state_set(irqentry_state_t *state,
> IRQENTRY_HARDIRQ_APPLIED : IRQENTRY_HARDIRQ_REJECTED;
> }
>
> +/**
> + * irqentry_hardirq_tail_enter - Stage ordinary-hardirq return-tail state
> + * @state: Pointer to state returned by the matching irqentry_hardirq_enter()
> + *
> + * Make a fresh staged hardirq-count attempt and establish the lockdep
> + * classification which traceable return-tail work needs. Accounting, RCU and
> + * tick handling belong to the completed interrupt and must not be repeated
> + * here.
> + */
> +static __always_inline void
> +irqentry_hardirq_tail_enter(irqentry_state_t *state)
> +{
> + struct irqentry_hardirq_cookie cookie;
> +
> + if (!state->hardirq)
> + return;
> +
> + cookie = irqentry_hardirq_count_start();
> + lockdep_hardirq_enter();
> +
> + instrumentation_begin();
> + irqentry_hardirq_count_finish(cookie);
> + instrumentation_end();
> + irqentry_hardirq_count_state_set(state, cookie);
> +}
[ ... ]
> @@ -543,6 +574,31 @@ static __always_inline void irqentry_hrtimer_rearm_deferred(void)
> static __always_inline void
> irqentry_exit_to_kernel_mode_after_preempt(struct pt_regs *regs, irqentry_state_t state)
> {
> + struct ct_irq_exit_state ct_state = { };
> +
> + if (state.hardirq) {
> + if (!regs_irqs_disabled(regs)) {
> + instrumentation_begin();
> + hrtimer_rearm_deferred();
> + /* Tell the tracer that the return will enable IRQs. */
> + trace_hardirqs_on_prepare();
> + lockdep_hardirqs_on_prepare_irqtail();
> + instrumentation_end();
> + }
> +
> + if (state.exit_rcu)
> + ct_state = ct_irq_exit_prepare();
> +
> + irqentry_hardirq_tail_exit(irqentry_hardirq_count_state(state));
[Severity: High]
Does ordering trace_hardirqs_on_prepare() before irqentry_hardirq_tail_exit()
cause a regression for the preemptirqsoff tracer when returning to
preemptible, IRQ-enabled kernel mode?
In irqentry_exit_to_kernel_mode(), irqentry_hardirq_tail_enter() stages
HARDIRQ_OFFSET in preempt_count() and calls:
irqentry_hardirq_count_finish()
preempt_latency_start(HARDIRQ_OFFSET)
trace_preempt_off()
Next, when !regs_irqs_disabled(regs),
irqentry_exit_to_kernel_mode_after_preempt() calls trace_hardirqs_on_prepare()
while HARDIRQ_OFFSET is still set in preempt_count(). In
kernel/trace/trace_irqsoff.c:
void tracer_hardirqs_on(unsigned long a0, unsigned long a1)
{
if (!preempt_trace(preempt_count()) && irq_trace())
stop_critical_timing(a0, a1);
}
When the preemptirqsoff tracer is active (both CONFIG_IRQSOFF_TRACER and
CONFIG_PREEMPT_TRACER enabled, so trace_type has both TRACER_IRQS_OFF and
TRACER_PREEMPT_OFF), preempt_trace(preempt_count()) evaluates to true because
preempt_count() contains HARDIRQ_OFFSET. As a result, tracer_hardirqs_on()
skips stop_critical_timing(), while trace_hardirqs_on_prepare() still clears
tracing_irq_cpu.
Immediately after that, irqentry_hardirq_tail_exit() calls:
irqentry_hardirq_exit_prepare()
irqentry_hardirq_count_sub_prepare()
preempt_latency_stop(HARDIRQ_OFFSET)
trace_preempt_on()
tracer_preempt_on()
In kernel/trace/trace_irqsoff.c:
void tracer_preempt_on(unsigned long a0, unsigned long a1)
{
if (preempt_trace(preempt_count()) && !irq_trace())
stop_critical_timing(a0, a1);
}
Because hardware interrupts remain disabled at the CPU until the exception
return instruction, irqs_disabled() is still true, so irq_trace() evaluates to
true and tracer_preempt_on() also skips stop_critical_timing().
Finally, irqentry_hardirq_exit_finish() removes HARDIRQ_OFFSET via
__preempt_count_sub(HARDIRQ_OFFSET). Won't this leave per_cpu(tracing_cpu, cpu)
stuck active after returning to preemptible, IRQ-enabled kernel execution, as
well as emitting extra preempt_disable, irq_enable, and preempt_enable trace
events in inverted order?
> diff --git a/kernel/softirq.c b/kernel/softirq.c
> index 169c559832501..85b167db37c29 100644
> --- a/kernel/softirq.c
> +++ b/kernel/softirq.c
> @@ -837,6 +837,27 @@ irq_exit_wake_late_softirqs(unsigned int wake_flags)
> irq_exit_wake_ksoftirqd();
> }
>
> +/**
> + * irqentry_hardirq_tail_exit - Close work and remove return-tail state
> + * @cookie: Staged hardirq-count cookie from irqentry_hardirq_tail_enter()
> + *
> + * Run every instrumentable teardown operation, including the final worker
> + * closure, before conditionally removing any applied raw hardirq count and
> + * ending lockdep nesting.
> + */
> +void noinstr
> +irqentry_hardirq_tail_exit(struct irqentry_hardirq_cookie cookie)
> +{
> + bool do_sub;
> +
> + instrumentation_begin();
> + do_sub = irqentry_hardirq_exit_prepare(cookie);
> + irq_exit_wake_late_softirqs(0);
> + instrumentation_end();
> +
> + irqentry_hardirq_exit_finish(do_sub);
> +}
[Severity: Medium]
Can passing a hardcoded 0 to irq_exit_wake_late_softirqs(0) in
irqentry_hardirq_tail_exit() cause redundant wake_up_process() calls for
ksoftirqd and ktimerd?
During the earlier main hardirq exit in irq_exit_rcu_preempt_counted_prepare(),
__irq_exit_rcu(true) records IRQ_EXIT_WAKE_KSOFTIRQD and IRQ_EXIT_WAKE_KTIMERD
in wake_flags so irq_exit_wake_late_softirqs(wake_flags) avoids waking those
threads a second time. However, local_softirq_pending() or
local_timers_pending_force_th() remains non-zero until the woken thread
actually runs on the CPU and clears the pending bits.
If irqentry_exit_to_kernel_mode_preempt() does not switch to the worker thread
(for example, on any interrupt from an RCU-idle context where state.exit_rcu is
true, when the interrupted kernel context has preemption disabled, when
regs_irqs_disabled(regs) is true, or when current has higher priority),
irqentry_hardirq_tail_exit() runs with those pending bits still set:
irq_exit_wake_late_softirqs(0)
if (!ksoftirqd_wake_requested)
ksoftirqd_wake_requested = irq_exit_wake_ksoftirqd();
if (!ktimerd_wake_requested)
ktimerd_wake_requested = irq_exit_wake_timersd();
Because wake_flags is 0, won't this call wakeup_softirqd() or wake_timersd()
a second time even when no return-tail callback raised new work, and do so
after trace_hardirqs_on_prepare(), ct_irq_exit_prepare(), and
irqentry_hardirq_exit_prepare() have already completed?
[Severity: Medium]
What happens if a return-tail callback raises softirq or forced-thread timer
work while returning to a preemptible kernel context?
In irqentry_exit_to_kernel_mode():
instrumentation_begin();
irqentry_exit_to_kernel_mode_preempt(regs, state);
instrumentation_end();
irqentry_hardirq_tail_enter(&state);
irqentry_exit_to_kernel_mode_after_preempt(regs, state);
the preemption check in irqentry_exit_to_kernel_mode_preempt() runs before the
hardirq return tail is entered.
If a callback inside the return tail (such as during hrtimer_rearm_deferred(),
trace_hardirqs_on_prepare(), lockdep_hardirqs_on_prepare_irqtail(), or
irqentry_hardirq_exit_prepare()) raises softirq or timer work,
irq_exit_wake_late_softirqs(0) in irqentry_hardirq_tail_exit() wakes ksoftirqd
or ktimerd while HARDIRQ_OFFSET is still set in preempt_count(), setting
need_resched() on current. Immediately afterwards,
irqentry_hardirq_exit_finish() drops HARDIRQ_OFFSET via
__preempt_count_sub(HARDIRQ_OFFSET) without checking for rescheduling.
Could this return to a preemptible kernel task with need_resched() set and
miss preempting for the newly woken worker thread (including a higher-priority
SCHED_FIFO ktimerd/ksoftirqd under PREEMPT_RT or force_irqthreads)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028033127.82193-1-kmehltretter@gmail.com?part=10
next prev parent reply other threads:[~2026-10-28 3:57 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 3:31 [RFC PATCH v2 00/13] entry: Classify very early hardirq entry and return-tail callbacks Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 01/13] arm64: Always inline raw preempt count updates Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 02/13] arm64: percpu: Annotate __kern_my_cpu_offset() as '__always_inline' Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 03/13] sched: Split preempt-count bookkeeping from raw updates Karl Mehltretter
2026-10-29 9:32 ` Peter Zijlstra
2026-10-28 3:31 ` [RFC PATCH v2 04/13] lockdep: Use raw access for pinned hardirq state Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 05/13] entry: Add the pre-counted ordinary-hardirq entry/exit contract Karl Mehltretter
2026-10-28 3:54 ` sashiko-bot
2026-10-28 3:31 ` [RFC PATCH v2 06/13] entry: Skip inactive IRQ-return reschedule calls Karl Mehltretter
2026-10-28 3:51 ` sashiko-bot
2026-10-28 3:31 ` [RFC PATCH v2 07/13] context_tracking: Split IRQ-exit preparation from the EQS transition Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 08/13] lockdep: Prepare IRQ-on state from a hardirq return tail Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 09/13] entry: Carry ordinary-hardirq provenance through kernel return Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 10/13] entry: Classify ordinary hardirq return-tail work as hardirq context Karl Mehltretter
2026-10-28 3:57 ` sashiko-bot [this message]
2026-10-28 3:31 ` [RFC PATCH v2 11/13] arm64: Use the pre-counted hardirq lifecycle Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 12/13] Documentation: entry: Describe " Karl Mehltretter
2026-10-28 3:31 ` [RFC PATCH v2 13/13] locking/selftest: Exercise hardirq return-tail preparation 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=sashiko-outbox-187457@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=kmehltretter@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=sashiko-reviews@lists.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