Panic 递归保护
TGOSKits、ArceOS 和 StarryOS 的 panic/oops 递归保护用于避免异常处理再次进入带锁输出、backtrace 或 panic 主路径,降低次生故障覆盖原始故障信息的风险。
目的与意义
在当前 Starry lockdep 调试中,已经观察到这样一种现象:
- 先触发一个 lockdep 违例
- 如果违例后立即直接停机,系统可以稳定 结束
- 如果违例后继续走普通 panic 路径,则可能出现 page fault、卡住、串口无响应等次生故障
这说明这里其实有两类不同问题:
- 是否应该进入 panic
- 进入 panic 之后,异常路径本身是否足够健壮
前者通常属于具体功能或锁顺序问题;后者则属于通用的内核异常路径设计问题。
把 panic/oops 递归保护单独设计出来的意义在于:
- 它不只服务于 lockdep
- 它同样适用于 panic、oops、BUG、die 等其他异常收尾路径
- 它能降低“主故障之后又在异常路径里继续放大故障”的概率
因此,这类机制应被视为一个独立主题,而不是夹带在某个具体修复里顺手处理。
基本原理
这类机制的设计思路,当前主要参照 Linux 的 panic/oops 路径实现。
原因不是为了机械地“和 Linux 保持一致”,而是因为 Linux 长期处理过大量:
- SMP 并发 panic
- 异常路径递归输出
- 控制台/锁/调试路径在故障态下再次放大问题
等实际问题,因此它在 panic/oops 路径上的分层思路具有直接参考价值。
Linux 没有试图用一个简单布尔值解决所有异常路径问题,而是把问题拆成两层。
1. Panic 主路径所有权
Linux 用 panic_cpu 这样的全局原 子状态来约束:
- 只有一个 CPU 执行 panic 主路径
- 其他并发进入 panic 的 CPU 不再重复跑完整 panic 流程
典型逻辑是:
old_cpu = PANIC_CPU_INVALID;
this_cpu = raw_smp_processor_id();
if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
/* go ahead */
} else if (old_cpu != this_cpu)
panic_smp_self_stop();
它解决的是 SMP 并发 panic 主路径冲突。