RCU stall 是软锁或中断风暴的症状而非原因;关键依据是softirq堆积、idle=000/0/0及ksoftirqd高CPU占用;可用sysrq实时抓栈,ftrace定位耗时函数,参数调优仅缓解不根治。

确认 stall 是否真由软锁或中断风暴引发
RCU stall 报错本身不是原因,而是症状。你看到 INFO: rcu_sched detected stalls on CPUs/tasks,紧接着系统卡死、softirq=0/65535、fqs=0、NMI backtrace 等,才提示软锁或中断风暴可能性极高——尤其是当 stall 日志里反复出现同一 CPU(比如 CPU 3),且 idle=000/0/0(非空闲)+ softirq=N/M 中 M 远大于 N,说明软中断队列严重堆积。
关键判断依据:
- 若 softirq 第二项(等待数)持续飙升,cat /proc/softirqs 对应列(如 NET_RX、NET_TX、HI)在 stall 前几秒突增数十倍 → 很可能是中断风暴或软中断处理函数卡住;
- 若 idle 字段始终为 000/0/0 或极小值,且 ps aux --sort=-pcpu | head -5 显示某内核线程(如 ksoftirqd/3)或驱动模块线程 CPU 占用 100% → 更倾向软锁(如自旋锁死循环、disable_irq_nosync 后忘开、readl/writel 在无超时轮询中阻塞);
- 若 stall 发生在 emulator(如 QEMU + cmodel)、RT 内核或 tickless idle 场景下,且 (0 ticks this GP) 频繁出现 → 很可能不是锁,而是调度器无法触发 tick,RCU 等不到 QS。
用 sysrq 实时抓取卡死现场的堆栈
这是最轻量、最可靠的第一响应手段,无需提前配置,只要 /proc/sys/kernel/sysrq 是 1 就能用。系统刚卡但尚未 panic 时,立刻按物理键盘组合键(串口/SSH 不生效):
-
Alt + SysRq + l(小写 L)→ 转储所有 CPU 的当前堆栈,你会看到每个 CPU 上正在执行的函数,重点关注 stalled CPU 的RIP指向哪(比如readl+0xb/0x10或spin_lock+0x1a/0x30) -
Alt + SysRq + w→ 列出所有不可中断状态(D状态)进程,确认是否有驱动线程卡在wait_event_interruptible或mutex_lock -
Alt + SysRq + t→ 查看完整 task list,找running或uninterruptible状态下长时间未切换的 task(尤其注意名字含模块名或驱动名的)
⚠️ 注意:部分笔记本 SysRq 键需配合 Fn;虚拟机中需确保 keyboard: passthrough 开启;若用串口控制台,需在内核启动参数加 console=ttyS0,115200 并启用 sysrq_always_enabled。
用 ftrace 锁定软中断或驱动函数的异常耗时
如果 stall 偶发且无法手动触发 sysrq,就靠 ftrace 提前布防。重点不是录全量 trace,而是聚焦 RCU 和 softirq 关键路径:
- 启用 softirq 跟踪:
echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_entry/enable
echo 1 > /sys/kernel/debug/tracing/events/irq/softirq_exit/enable
echo function > /sys/kernel/debug/tracing/current_tracer
echo 'do_softirq*|__do_softirq|rcu_*|note_gp_changes' > /sys/kernel/debug/tracing/set_ftrace_filter - 加一个简单 watchdog 脚本,在后台轮询
dmesg -t | tail -20 | grep "rcu_sched.*stall",一旦命中立即:cat /sys/kernel/debug/tracing/trace_pipe > /tmp/ftrace-stall-$(date +%s).log & - 特别关注
__do_softirq执行时间是否超 2ms(正常应在 sub-ms 级),以及它调用的驱动 handler(如igb_poll、nvme_irq)是否陷入长循环
常见陷阱:function_graph tracer 开销大,可能掩盖问题;生产环境建议用 function tracer + 精确 filter,避免 trace buffer 溢出丢帧。
硬性规避:从内核参数和驱动行为入手
很多“卡死”本质是设计缺陷被 RCU 暴露出来,修复要落在驱动或子系统层。临时缓解可从启动参数切入:
- 加
rcu_cpu_stall_timeout=60(默认 21 秒)给宽限期更多缓冲,避免误报(但不解决根本) - 对已知有问题的驱动(如某些老网卡、PCIe 设备),加
pci=noacpi或irqpoll降低中断路由复杂度 - 若确认是
readl/writel轮询导致(见 calltrace 中readl+0xb/0x10),必须在驱动中改用带超时的readl_poll_timeout,并确保每次读前mb()内存屏障 - RT 内核用户务必检查:是否在
preempt_disable区域里做了耗时操作?cond_resched_rcu_qs()是否被遗漏?因为cond_resched()不触发 RCU QS
真正难啃的是那些“看起来没锁、也没关中断,但就是不调度”的情况——比如在 stop_machine 上下文中做长计算,或在 atomic_notifier_call_chain 里调用了可能 sleep 的函数。这类问题不会出现在 sysrq 输出里,只能靠 ftrace + 源码交叉比对。








