Linux 内核 x86-64 机器检查(Machine Check)可配置 sysfs 参数完全指南
2026/9/14 9:41:58 网站建设 项目流程

Linux 内核 x86-64 机器检查(Machine Check)可配置 sysfs 参数完全指南

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

机器检查(Machine Check)是 x86 体系结构内置的硬件错误报告机制:CPU 检测到内部硬件错误后,通过 MCA(Machine Check Architecture)记录到各银行(bank)的状态寄存器中。本文以 Linux 内核源码树中的 Documentation/arch/x86/x86_64/machinecheck.rst 为骨架,结合 arch/x86/kernel/cpu/mce/core.c、Documentation/ABI/testing/sysfs-mce 等内核实现,系统讲解/sys/devices/system/machinecheck/machinecheckN/下每个可配置项的含义、默认值、生效机制与实战用法。读完本文,你将掌握:如何读懂"Machine check errors logged"日志并驱动 mcelog 解码;如何通过 sysfs 细粒度开关各银行的子事件;如何调优纠正错误(CE)轮询间隔、配置错误触发脚本与 monarch 超时;以及 ignore_ce、dont_log_ce、cmci_disabled 三个"关闭纠正错误处理"选项之间的本质区别。

机器检查与 MCE 银行(bank)体系

什么是机器检查

机器检查报告的是CPU 检测到的内部硬件错误条件。根据错误的严重程度,其处理方式截然不同:

  • 不可纠正错误(Uncorrected errors):通常会触发一次机器检查异常(Machine Check Exception,MCE),往往伴随内核 panic(在可恢复场景下也可能触发系统软离线或进程杀死);
  • 可纠正错误(Corrected errors):不会引发异常,只会产生一条机器检查日志条目,由内核周期轮询或 CMCI(Corrected Machine Check Interrupt,可纠正机器检查中断)机制收集。

这些错误被组织在银行(bank)中。每个银行通常对应一个硬件子系统(如指令缓存、数据缓存、总线、内存控制器等),一个银行内部又细分为多个子事件(subevent)。需要特别强调的是:银行与子事件的确切含义是 CPU 厂商相关的——Intel 与 AMD 的银行布局、编号和事件位定义各不相同,甚至同厂商不同微架构之间也有差异。这也是为什么内核不做统一解码,而是把解码工作交给用户态工具。

mcelog:解码 MCE 记录的标准工具

mcelog是解码这些机器检查记录的事实标准工具。当你在系统日志中看到"Machine check errors logged"消息时,就应该运行 mcelog 从/dev/mcelog字符设备收集并解码机器检查条目(该设备由内核的 arch/x86/kernel/cpu/mce/dev-mcelog.c 提供)。

通常的做法是从cron定期运行 mcelog。但正如后文将要介绍的trigger属性,sysfs 还提供了一种比 cron 更快的事件触发机制,二者可以互相替代或配合使用。

/sys/devices/system/machinecheck/machinecheckN/ 目录结构

内核为每个 CPU在 sysfs 中创建一个目录:

/sys/devices/system/machinecheck/machinecheckN/

其中N为 CPU 编号。该目录的创建逻辑位于 arch/x86/kernel/cpu/mce/core.c,内核将 machinecheck 注册为一个总线子系统(mce_subsys),总线名与设备名均为machinecheck

static const struct bus_type mce_subsys = { .name = "machinecheck", .dev_name = "machinecheck", };

该目录下包含以下可配置条目,本文逐一展开:

条目权限作用默认值
bank<Y>064464 位十六进制位掩码,开关银行 Y 的特定子事件全 1(全部使能)
check_interval0644轮询纠正错误的间隔(秒),轮询发现/停止发现错误时指数加速/退避5 分钟(300s)
trigger0644检测到 MCE 事件时运行的程序路径空(不触发)
monarch_timeout0644异常时等待其他 CPU 上报 MCE 的超时时间(微秒)依平台而定
ignore_ce0644关闭纠正错误的轮询与 CMCI0
dont_log_ce0644关闭纠正错误的日志记录(静默清除)0
cmci_disabled0644关闭 CMCI 特性0

这些属性的 sysfs 定义同样在 arch/x86/kernel/cpu/mce/core.c 中:

static DEVICE_INT_ATTR(monarch_timeout, 0644, mca_cfg.monarch_timeout); static DEVICE_BOOL_ATTR(dont_log_ce, 0644, mca_cfg.dont_log_ce); static DEVICE_BOOL_ATTR(print_all, 0644, mca_cfg.print_all); static struct dev_ext_attribute dev_attr_check_interval = { __ATTR(check_interval, 0644, device_show_int, store_int_with_restart), ... };

注意事项:部分条目是"全局共享"的

文档明确提醒:check_intervaltrigger等条目虽然在每个 CPU 目录下都出现,但它们实际上是所有 CPU 之间共享的全局变量。修改任意一个 CPU 目录下的值,等价于修改全局配置(这也解释了为什么mce_restart()需要持有全局的mce_sysfs_mutex互斥锁来重启轮询定时器)。从源码看,check_interval是一个文件级静态全局变量(core.c),trigger对应的mce_helper同样是全局缓冲区(dev-mcelog.c)。

bank :按子事件粒度开关错误报告

语义与默认值

bank<Y>是一个64 位十六进制位掩码,用于开关银行 Y 内的特定子事件:

  • 位掩码中某一位为0:对应的子事件不再上报
  • 位掩码中某一位为1:对应子事件正常上报;
  • 默认所有位均为 1,即所有事件都使能

注意:BIOS 还会维护另一套按银行屏蔽事件的掩码,这套掩码不会在此处显示,因此 sysfs 中看到的全 1 不代表 BIOS 没有屏蔽某些事件。

内核实现:读写路径

bank<Y>的读写实现在 arch/x86/kernel/cpu/mce/core.c 中。读取时直接输出该 CPU 上银行 Y 的控制值b->ctl;写入时通过kstrtou64()解析用户输入并更新b->ctl,随后在mce_sysfs_mutex保护下调用mce_restart()重启相关机制使配置生效:

static ssize_t set_bank(struct device *s, struct device_attribute *attr, const char *buf, size_t size) { ... if (kstrtou64(buf, 0, &new) < 0) return -EINVAL; ... b->ctl = new; mutex_lock(&mce_sysfs_mutex); mce_restart(); mutex_unlock(&mce_sysfs_mutex); return size; }

从内核初始化路径看,银行初始默认值在 core.c 中为b->ctl = -1ULL(即全 1),与文档所述"默认全部使能"完全一致。此外,不同厂商/微架构会通过 quirk 覆盖个别银行——例如源码中针对某些 CPU 的 IFU(指令取指单元)银行错误做了特殊处理(core.c),这说明银行与事件编号确实高度依赖具体 CPU。

实战:屏蔽/恢复某个银行

# 查看 CPU0 上银行 1 当前的子事件掩码 cat /sys/devices/system/machinecheck/machinecheck0/bank1 # 屏蔽银行 1 的 bit 3 子事件(其余位保持不变) OLD=$(cat /sys/devices/system/machinecheck/machinecheck0/bank1) NEW=$(( OLD & ~(1 << 3) )) printf '%llx\n' "$NEW" > /sys/devices/system/machinecheck/machinecheck0/bank1 # 恢复全部事件 echo ffffffffffffffff > /sys/devices/system/machinecheck/machinecheck0/bank1

提示:改银行掩码属于调试/规避手段,建议先结合 mcelog 解码确认具体子事件含义,再决定是否屏蔽,避免掩盖真实的硬件故障信号。

check_interval:纠正错误轮询间隔与指数退避

语义与默认值

check_interval表示多久轮询一次纠正的机器检查错误,单位为秒。有两个关键细节:

  1. 输出是十六进制(源码中用device_show_int显示,注意0x12c表示 300 秒);
  2. 默认值为 5 分钟(300 秒),在 arch/x86/kernel/cpu/mce/internal.h 中定义为#define INITIAL_CHECK_INTERVAL 5 * 60 /* 5 minutes */

动态调整:指数加速与退避

轮询间隔并非固定不变。内核实现了自适应的指数调整策略:

  • 当轮询器发现 MCE时,触发指数加速——更频繁地轮询(最多每 10ms 一次,即HZ/100);
  • 当轮询器不再发现 MCE时,触发指数退避——逐渐降低轮询频率,直到恢复到check_interval
  • check_interval既是初始轮询间隔,也是轮询间隔的上限
  • 设为0表示完全不轮询纠正错误(但部分纠正错误仍可能通过其他途径上报,例如 CMCI)。

该逻辑实现在 arch/x86/kernel/cpu/mce/core.c 的mce_timer_fn()中:

if (!mce_gen_pool_empty()) iv = max(iv / 2, (unsigned long) HZ/100); /* 指数加速 */ else iv = min(iv * 2, round_jiffies_relative(check_interval * HZ)); /* 指数退避 */

此外,当某个银行进入错误风暴(storm)模式时,内核会切换到每秒轮询一次(__start_timer(t, HZ)),风暴结束后再恢复默认间隔(core.c)。

实战:修改轮询间隔

# 查看当前轮询间隔(注意:十六进制输出,0x12c = 300 秒 = 5 分钟) cat /sys/devices/system/machinecheck/machinecheck0/check_interval # 设置为 60 秒 echo 60 > /sys/devices/system/machinecheck/machinecheck0/check_interval # 完全关闭纠正错误轮询 echo 0 > /sys/devices/system/machinecheck/machinecheck0/check_interval

写入后,store_int_with_restart()(core.c)会检测到check_interval变化,并调用mce_restart()重新初始化各 CPU 的轮询定时器。由于该变量是全局共享的,修改任意 CPU 目录下的值即可。

trigger:MCE 事件触发程序

trigger属性用于设置一个在检测到机器检查事件时运行的程序。它是"定期从 cron 运行 mcelog"的替代方案,优点是检测事件更快(事件发生后立即触发,无需等待下一个 cron 周期)。

内核实现

实现在 arch/x86/kernel/cpu/mce/dev-mcelog.c。内核将用户写入的路径保存在全局mce_helper缓冲区中;当有 MCE 事件产生时,mce_work_trigger()会调度一个工作队列,最终通过call_usermodehelper()执行该程序:

static void mce_do_trigger(struct work_struct *work) { call_usermodehelper(mce_helper, mce_helper_argv, NULL, UMH_NO_WAIT); } void mce_work_trigger(void) { if (mce_helper[0]) schedule_work(&mce_trigger_work); }

实战:设置触发脚本

# 让 MCE 事件触发 mcelog echo /usr/sbin/mcelog > /sys/devices/system/machinecheck/machinecheck0/trigger # 查看当前配置 cat /sys/devices/system/machinecheck/machinecheck0/trigger # 取消触发(写入空串) echo > /sys/devices/system/machinecheck/machinecheck0/trigger

脚本被call_usermodehelper以异步方式(UMH_NO_WAIT)执行,因此应为可执行程序或脚本,并注意其运行环境(环境变量极少、无终端)。同样地,trigger也是全局共享的。

monarch_timeout:异常时等待其他 CPU 的超时

当 CPU 发生机器检查异常时,x86 的 MCA 协议要求多个 CPU 之间协作:一个 CPU 作为monarch(主控者),其他 CPU 作为slave(从属者)向它汇报。monarch_timeout指定monarch 等待其他 CPU 也发生机器检查的最长时间

  • 单位为微秒(us)
  • 设为0表示不等待其他 CPU(禁用等待)。

内核通过mca_cfg.monarch_timeout配置该值(core.c 初始为 -1,表示"未显式设置"),在异常处理路径中将其换算为纳秒超时(timeout = (u64)mca_cfg.monarch_timeout * NSEC_PER_USEC,见 core.c)。平台相关代码会在初始化时给出默认值:例如对较新的 Intel CPU 默认设为 1 秒(USEC_PER_SEC,见 core.c);在部分不支持该机制的配置下会回退为 0(core.c)。

# 查看当前超时(微秒) cat /sys/devices/system/machinecheck/machinecheck0/monarch_timeout # 设置为 2 秒 echo 2000000 > /sys/devices/system/machinecheck/machinecheck0/monarch_timeout # 禁用等待其他 CPU echo 0 > /sys/devices/system/machinecheck/machinecheck0/monarch_timeout

该值也可通过内核命令行参数mce=monarch_timeout=<us>设置,见后文。

ignore_ce、dont_log_ce 与 cmci_disabled:三者的区别

这三个属性都用于关闭纠正错误(CE)的处理,但粒度完全不同,务必区分清楚。它们在 arch/x86/kernel/cpu/mce/core.c 中均有独立的 setter 实现,修改时会在mce_sysfs_mutex保护下对每个 CPU 执行使能/禁用动作。

ignore_ce:整体关闭纠正错误的收集

  • 作用:关闭纠正错误的轮询与 CMCI(两种收集途径全部关闭);
  • 副作用:所有纠正事件不会被清除,而是保留在银行 MSR 中
  • 适用场景:希望彻底停止纠正错误处理、且允许错误状态寄存器保持原样时。

源码中开启该选项的路径为:删除所有轮询定时器(mce_timer_delete_all())+ 在每个 CPU 上禁用 CMCI(mce_disable_cmci),并置mca_cfg.ignore_ce = true(core.c)。

dont_log_ce:只关日志、仍清除状态

  • 作用仅关闭纠正错误的日志记录
  • 行为:所有上报的纠正错误都会被静默清除(不产生日志);
  • 适用场景:完全不在乎纠正错误、希望避免日志噪音、又不想让错误状态寄存器一直挂着时。

在轮询路径machine_check_poll()中可以看到该选项的实际作用:if (mca_cfg.dont_log_ce && !mce_usable_address(m)) ...跳过日志记录但继续clear_bank(m)清除银行状态(core.c)。

cmci_disabled:只关 CMCI

  • 作用:仅关闭CMCI(Corrected Machine Check Interrupt)特性;
  • 行为:关闭后纠正错误不再通过中断方式上报,但仍可通过轮询机制收集;
  • 适用场景:CMCI 中断风暴或与虚拟化等环境冲突时,保留轮询兜底。

对比小结

属性关闭轮询关闭 CMCI关闭日志是否清除银行状态
ignore_ce❌ 保留在 MSR
dont_log_ce✅ 静默清除
cmci_disabled依轮询结果
# 例:整体忽略纠正错误 echo 1 > /sys/devices/system/machinecheck/machinecheck0/ignore_ce # 例:只静默纠正错误日志 echo 1 > /sys/devices/system/machinecheck/machinecheck0/dont_log_ce # 例:只关闭 CMCI(保留轮询) echo 1 > /sys/devices/system/machinecheck/machinecheck0/cmci_disabled

这三个属性同样可等价地通过内核命令行参数设置:mce=ignore_cemce=dont_log_cemce=cmci_disabled(见 core.c 中的解析代码,其中cmci_disabled在启用 CMCI 的 CPU 上默认关闭)。

内核命令行参数:mce= 引导选项

上述部分配置还可以通过内核命令行(/proc/cmdline或引导加载器)在启动时设置。从 arch/x86/kernel/cpu/mce/core.c 的解析逻辑可以看出:

引导参数等价 sysfs 操作
mce=ignore_ce启动即置ignore_ce
mce=dont_log_ce启动即置dont_log_ce
mce=cmci_disabled启动即关闭 CMCI
mce=monarch_timeout=<us>启动即设置 monarch 超时(微秒)
# 示例:GRUB 内核命令行 mce=ignore_ce mce=monarch_timeout=2000000

引导参数在系统早期(sysfs 尚不可用时)就能生效,适合需要在启动阶段就改变 MCE 行为的场景;sysfs 写入则适合运行期动态调整,无需重启。

故障排查与诊断流程

综合文档与源码,一套完整的机器检查诊断工作流如下:

  1. 观察日志:关注 dmesg/系统日志中的Machine check errors logged消息;
  2. 运行 mcelog:从/dev/mcelog收集并解码 MCE 记录(定期 cron 或配置trigger事件触发);
  3. 查看银行状态cat /sys/devices/system/machinecheck/machinecheckN/bank*,了解各银行子事件掩码与默认使能情况;
  4. 按需调整
    • 轮询过快/过慢 → 调整check_interval
    • 纠正错误日志刷屏 →dont_log_ce=1(静默清除)或ignore_ce=1(完全停止收集);
    • CMCI 中断异常 →cmci_disabled=1
    • 多 CPU 上报协作异常 → 调整monarch_timeout
  5. 回到硬件层面:由于银行与子事件含义是 CPU 厂商相关的,遇到难以理解的条目时,应查阅 Intel/AMD 架构手册中的 MCA 章节。此外,架构细节还可参考 mcelog 作者 Andi Kleen 撰写的机器检查架构介绍文档http://one.firstfloor.org/~andi/mce.pdf(该链接同时被 machinecheck.rst 与 sysfs-mce 引用)。

相关内核文档与源码索引

  • 本文主题文档:Documentation/arch/x86/x86_64/machinecheck.rst
  • sysfs 接口 ABI 规范:Documentation/ABI/testing/sysfs-mce
  • MCE 核心实现(银行、轮询、sysfs 属性、引导参数解析):arch/x86/kernel/cpu/mce/core.c
  • /dev/mcelog 字符设备与 trigger 触发逻辑:arch/x86/kernel/cpu/mce/dev-mcelog.c
  • 轮询默认间隔定义:arch/x86/kernel/cpu/mce/internal.h
  • 相关子系统:AMD 阈值中断(arch/x86/kernel/cpu/mce/threshold.c)、Intel CMCI 实现(arch/x86/kernel/cpu/mce/intel.c)、MCE 错误注入调试工具(arch/x86/kernel/cpu/mce/inject.c)

说明:Documentation/arch/x86/x86_64/machinecheck.rst中提到的"AMD threshold interrupt configuration"(AMD 阈值中断配置)条目在文档中标注为 TBD(待补充),对应实现可参考 arch/x86/kernel/cpu/mce/threshold.c 中的阈值中断(threshold interrupt)逻辑。

【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询