Linux RT 分析Linux 6.12.107

03 · 中断线程化:驱动逻辑离开 hardirq

结论:RT 内核把 force_irqthreads() 编译期固定为 true,所有没有标记 IRQF_NO_THREAD/IRQF_PERCPU/IRQF_ONESHOT 的中断都被强制线程化:hardirq 里只剩一个「屏蔽本线 + 唤醒 irq/N-name 线程」的桩,驱动逻辑在 SCHED_FIFO 50 的内核线程里、在 bh 关闭状态下运行,可以被更高优先级任务抢占。代价是每个中断多一次线程唤醒与上下文切换(~2–5 µs),高中断率设备的吞吐下降;收益是 hardirq 停留时间从「驱动决定」变成「几 µs」,并且中断优先级变得可配置(chrt)。

第一层:从设备中断到任务唤醒

先看结论:hardirq 只剩几 µs 的唤醒桩,驱动逻辑在 SCHED_FIFO 50 的线程里跑,中断从此有了可配置的优先级。

非 RT驱动 handler + softirq 整段不可抢占
非 RT:驱动 handler + softirq 整段不可抢占
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RTprimary 只唤醒线程,驱动逻辑在线程里跑
RT:primary 只唤醒线程,驱动逻辑在线程里跑

第二层:内核路径逐步对照

先看结论force_irqthreads() 编译期恒真;非 NO_THREAD/PERCPU/ONESHOT 的中断全部被强制 ONESHOT + 线程化,线程里只关 bh、不关中断。

非 RTirqentry → handler → irq_exit 里的 softirq 循环
非 RT:irqentry → handler → irq_exit 里的 softirq 循环
同一场景 · 换成 PREEMPT_RT 内核
PREEMPT_RTprimary → IRQ_WAKE_THREAD → irq_thread → irq_forced_thread_fn
RT:primary → IRQ_WAKE_THREAD → irq_thread → irq_forced_thread_fn

强制线程化的开关

include/linux/interrupt.h:516-525

#ifdef CONFIG_IRQ_FORCED_THREADING
# ifdef CONFIG_PREEMPT_RT
#  define force_irqthreads()    (true)
# else
DECLARE_STATIC_KEY_FALSE(force_irqthreads_key);
#  define force_irqthreads()    (static_branch_unlikely(&force_irqthreads_key))
# endif

注意 PREEMPT_RT 并不 select IRQ_FORCED_THREADINGkernel/Kconfig.preempt:70-86 里没有),该符号由各架构自己选(arch/x86/Kconfig:297arch/arm64/Kconfig:249arch/riscv/Kconfig:187 等);RT 只是把运行时判断改成常量。非 RT 内核用 threadirqs 启动参数打开同样的机制(kernel/irq/manage.c:27-35Documentation/admin-guide/kernel-parameters.txt:6725-6727),RT 上这个参数不存在。

谁被线程化、谁豁免

kernel/irq/manage.c:1368-1400 irq_setup_forced_threading()

线程本身

local_bh_disable();
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
    local_irq_disable();          /* 非 RT + threadirqs 时还要模拟关中断 */
ret = action->thread_fn(action->irq, action->dev_id);
...
irq_finalize_oneshot(desc, action);   /* 解除屏蔽 */
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
    local_irq_enable();
local_bh_enable();                     /* 期间 raise 的 softirq 在这里就地跑 */

注释(:1184-1189)解释了为什么要关 bh:没有显式申请线程化的驱动依赖 hardirq 隐含的 bh/preempt 关闭。RT 下只关 bh(那是一把 per-CPU 锁,第 04 章),中断保持打开。显式 request_threaded_irq()thread_fnirq_thread_fn():1209-1225),什么都不关,完全可抢占。

hardirq 侧

kernel/irq/handle.c:139-177:遍历 action,调用 primary,返回 IRQ_WAKE_THREAD__irq_wake_thread():61-76)——test_and_set_bit(IRQTF_RUNTHREAD) 保证多次硬中断合并成一次线程运行。irq_exit() 在 RT 下不跑 softirq,只唤醒 ksoftirqd(kernel/softirq.c:295-299)。

网络驱动的联动

Documentation/networking/napi.rst:145-148napi_schedule_irqoff() 在中断线程化(如 RT)时退化为 napi_schedule();实现见 net/core/dev.c:6339-6351,因为「hard IRQ 已关」的假设在 RT 上不成立。RT 还拒绝 irqfixup/irqpoll 启动参数(kernel/irq/spurious.c:450-468)。

ARM64:GIC、伪 NMI 与 PMR 屏蔽

性能影响

净影响:hardirq 停留时间从「驱动决定」变为常数;每个中断多一次线程切换,高中断率设备吞吐下降 5–40%。

指标 非 RT RT 说明
hardirq 停留时间 = 驱动 handler 时间(10 µs – 1 ms) 几 µs(屏蔽 + 唤醒) 对所有任务的最坏延迟贡献大幅缩小
中断→驱动逻辑开始 0 唤醒 + 切换:2–5 µs(若 irq 线程被更高优先级挡住则更久) 中断响应时间略增
每中断 CPU 开销 基线 +1 次线程切换 + bh 锁进出 100k+ IRQ/s 的网卡/NVMe 可见吞吐损失
中断优先级 硬件决定,无法调整 每个线程可 chrt,可低于 RT 应用 关键收益之一
中断合并 驱动/硬件 coalescing 同上 + IRQTF_RUNTHREAD 天然合并;RT 默认不开软 IRQ 合并(net/core/dev.c:11202-11210
中断风暴影响面 全 CPU 所有任务 仅低于 50 的任务;RT 应用(>50)不受影响

源码定位

主题 位置
force_irqthreads() include/linux/interrupt.h:516-525
threadirqs 参数(非 RT) kernel/irq/manage.c:27-35
线程化决策 kernel/irq/manage.c:1368-1400kernel/irq/settings.h:115-118
标志 include/linux/interrupt.h:77, 80, 83, 90
irq_forced_thread_fn / irq_thread_fn kernel/irq/manage.c:1184-1225
irq_finalize_oneshot kernel/irq/manage.c:1134-1177
irq_thread 主循环、优先级 kernel/irq/manage.c:1300-1329kernel/sched/syscalls.c:856-861
线程创建与命名 kernel/irq/manage.c:1457-1488
hardirq 侧唤醒 kernel/irq/handle.c:61-90, 139-177
irqfixup/irqpoll 拒绝 kernel/irq/spurious.c:450-468
NAPI 联动 net/core/dev.c:6339-6351Documentation/networking/napi.rst:145-148
arm64:arch timer / IPI 请求 drivers/clocksource/arm_arch_timer.c:1251-1269, 1326arch/arm64/kernel/smp.c:1016-1091include/linux/interrupt.h:192-193
arm64:伪 NMI arch/arm64/Kconfig:2292-2315arch/arm64/kernel/cpufeature.c:1091-1113, 2221-2232drivers/irqchip/irq-gic-v3.c:515-519, 627-635, 919-922, 966-969, 2045-2053include/linux/irqchip/arm-gic-v3-prio.h:28-48
arm64:PMR 关中断 arch/arm64/include/asm/irqflags.h:33-183arch/arm64/include/asm/ptrace.h:26-29, 215-221
arm64:硬死锁检测 arch/arm64/kernel/watchdog_hld.c:15-35kernel/watchdog_perf.c:229-241
arm64:PMU / ITS / mailbox drivers/perf/arm_pmu.c:636-656drivers/irqchip/irq-gic-v3-its.c:1678, 1991drivers/mailbox/tegra-hsp.c:711, 858drivers/mailbox/omap-mailbox.c:233

调优要点

小结