软锁死检测器和硬锁死检测器(又名 nmi_watchdog)¶
Linux 内核可以充当看门狗,以检测软锁死和硬锁死。
“软锁死”定义为一个错误,导致内核在内核模式下循环超过 20 秒(有关详细信息,请参见下面的“实现”),而没有给其他任务运行的机会。检测到时会显示当前的堆栈跟踪,默认情况下,系统将保持锁定状态。或者,可以将内核配置为崩溃;为此,提供了 sysctl“kernel.softlockup_panic”,内核参数“softlockup_panic”(有关详细信息,请参见“内核的命令行参数”)和编译选项“BOOTPARAM_SOFTLOCKUP_PANIC”。
“硬锁死”定义为一个错误,导致 CPU 在内核模式下循环超过 10 秒(有关详细信息,请参见下面的“实现”),而没有让其他中断有机会运行。与软锁死情况类似,检测到时会显示当前的堆栈跟踪,并且系统将保持锁定状态,除非更改默认行为,可以通过 sysctl “hardlockup_panic”,编译时旋钮 “BOOTPARAM_HARDLOCKUP_PANIC” 和内核参数“nmi_watchdog”来完成更改(有关详细信息,请参见“内核的命令行参数”)。
崩溃选项可以与 panic_timeout(此超时通过令人困惑地命名的 “kernel.panic” sysctl 设置)结合使用,以使系统在指定的时间后自动重启。
实现¶
软锁死和硬锁死检测器分别基于 hrtimer 和 perf 子系统构建。这直接导致它们原则上应在存在这些子系统的任何架构中工作。
会运行周期性的 hrtimer 来生成中断并启动看门狗作业。每隔 “watchdog_thresh”(编译时初始化为 10,可通过具有相同名称的 sysctl 进行配置)秒生成一个 NMI perf 事件,以检查硬锁死。如果系统中的任何 CPU 在该时间内未收到任何 hrtimer 中断,“硬锁死检测器”(NMI perf 事件的处理程序)将生成内核警告或调用崩溃,具体取决于配置。
看门狗作业在停止调度线程中运行,该线程每次被调度时都会更新时间戳。如果该时间戳在 2 * watchdog_thresh 秒(软锁死阈值)内未更新,“软锁死检测器”(在 hrtimer 回调函数内编码)会将有用的调试信息转储到系统日志,之后,如果被指示这样做,它将调用崩溃,或继续执行其他内核代码。
hrtimer 的周期为 2 * watchdog_thresh/5,这意味着它有两到三次机会在硬锁死检测器启动之前生成中断。
如上所述,提供了一个内核旋钮,允许管理员配置 hrtimer 和 perf 事件的周期。特定环境的正确值是在对锁死做出快速响应和检测开销之间进行权衡。
默认情况下,看门狗在所有在线核心上运行。但是,在配置了 NO_HZ_FULL 的内核上,默认情况下,看门狗仅在管家核心上运行,而不在“nohz_full”引导参数中指定的核心上运行。如果我们允许看门狗默认在“nohz_full”核心上运行,我们将必须运行计时器节拍以激活调度程序,这将阻止“nohz_full”功能保护这些核心上的用户代码免受内核影响。当然,默认情况下在 nohz_full 核心上禁用它意味着当这些核心进入内核时,默认情况下我们将无法检测到它们是否锁死。但是,允许看门狗继续在管家(非无滴答)核心上运行意味着我们将继续在这些核心上正确检测到锁死。
在任何一种情况下,可以通过 kernel.watchdog_cpumask sysctl 调整从运行看门狗中排除的核心集。对于 nohz_full 核心,这对于调试内核似乎挂在 nohz_full 核心上的情况可能很有用。