Machine Check Exception (MCE) 是CPU发现硬件错误时触发的异常(exception),中断号是18,异常的类型是abort:
导致MCE的原因主要有:总线故障、内存ECC校验错、cache错误、TLB错误、内部时钟错误,等等。不仅硬件故障会引起MCE,不恰当的BIOS配置、firmware bug、软件bug也有可能引起MCE。
在 Linux 系统上,如果发生的MCE错误属于可以自动纠正的类型,那么系统保持继续运行,MCE错误日志会记录在一个ring buffer中(这个ring buffer通过设备文件/dev/mcelog来访问),用 mcelog(8) 命令可以读取MCE日志,系统通常会通过cron任务或者mcelog.service把ring buffer中的MCE日志写入/var/log/mcelog文件中。如果发生的MCE错误属于无法恢复的类型,那么系统会panic,错误信息会输出在终端上和message buffer里。
分析MCE需要参考Intel手册第3卷,15章Machine-Check Architecture和16章Interpreting Machine-Check Error Codes。由于MCE在不同型号的CPU上有差异,解读的方法也有不同,第16章是专门解释在不同的CPU型号上如何解读MCE错误码。
每个CPU上有一组寄存器称为 Machine-Check MSR (Model-Specific Register),用于Machine-Check的控制与记录,分为全局寄存器和若干Bank寄存器(CPU的硬件单元分成若干组,每一组称为一个Bank)。当发生MCE时,错误信息记录在全局状态寄存器 MCG_STATUS MSR 和Bank寄存器 MCi_STATUS MSR 中,如下图×××框所示:
分析MCE的方法,就是根据Intel手册解读上述寄存器中记录的错误信息。Linux内核把MCE的信息保存在下面的结构体中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /arch/x86/include/asm/mce.h :
0067 struct mce { 0068 __u64 status; /* 对应 IA32_MCi_STATUS MSR */ 0069 __u64 misc; 0070 __u64 addr; 0071 __u64 mcgstatus;/*对应 IA32_MCG_STATUS MSR */ 0072 __u64 ip; 0073 __u64 tsc; /* cpu time stamp counter */ 0074 __u64 time; /* wall time_t when error was detected */ 0075 __u8 cpuvendor; /* cpu vendor as encoded in system.h */ 0076 __u8 inject_flags; /* software inject flags */ 0077 __u16 pad; 0078 __u32 cpuid; /* CPUID 1 EAX */ 0079 __u8 cs; /* code segment */ 0080 __u8 bank; /* machine check bank */ 0081 __u8 cpu; /* cpu number; obsolete; use extcpu now */ 0082 __u8 finished; /* entry is valid */ 0083 __u32 extcpu; /* linux cpu number that detected the error */ 0084 __u32 socketid; /* CPU socket ID */ 0085 __u32 apicid; /* CPU initial apic ID */ 0086 __u64 mcgcap; /* MCGCAP MSR: machine check capabilities of CPU */ 0087 }; |
下面的MCE错误信息截取自一台因MCE而crash的机器,我们以此为例来解读一下MCE信息。
注:产生以下信息的内核函数是:
static void print_mce(struct mce *m)
源程序:arch/x86/kernel/cpu/mcheck/mce.c
如果需要的话,阅读源程序可以理解输出的信息与原始数据的对应关系。
1 2 3 4 5 | [Hardware Error]: CPU 3: Machine Check Exception: 4 Bank 5: be00000000800400 [Hardware Error]: TSC 128727f47a97 ADDR 3f628bd69349 MISC 1 [Hardware Error]: PROCESSOR 0:106a5 TIME 1450279905 SOCKET 1 APIC 14 [Hardware Error]: Machine check: Processor context corrupt Kernel panic - not syncing: Fatal Machine check |
其中CPU和Bank是MCE的接收者:
CPU 3 – 表示检测到MCE错误的是3号CPU,对应struct mce的extcpu字段;
Bank 5 – 一组硬件单元称为一个bank,每个bank对应一组machine-check寄存器;
MCE的错误代码包括两部分:
Machine Check Exception: 4 – 表示 IA32_MCG_STATUS MSR寄存器的状态码是4(含义见后文),对应 mcgstatus字段;
be00000000800400 – 表示 IA32_MCi_STATUS MSR寄存器中的错误码(含义见后文),对应status字段。
Machine Check Excheption: 4 的含义
它来自全局状态寄存器 IA32_MCG_STATUS MSR,(对应struct mce的 mcgstatus字段),只用到三个bit,如下所示。4表示machine-check in progress。
Bit 0: Restart IP Valid. 表示程序的执行是否可以在被异常中断的指令处重新开始。
Bit 1: Error IP Valid. 表示被中断的指令是否与MCE错误直接相关。
Bit 2: Machine Check In Progress. 表示 machine check 正在进行中。
be00000000800400 的含义
它来自bank寄存器IA32_MCi_STATUS MSR,(对应struct mce的status字段)。
be00000000800400 的二进制位如下:
Bit 63: VAL. 表示本寄存器中包含有效的错误码
Bit 61: UC. 表示是无法纠正的MCE
Bit 60: EN. 表示处于允许报告错误的状态
Bit 59: MISCV. 表示MCi_MISC寄存器中含有对该错误的补充信息
Bit 58: ADDRV. 表示MCi_ADDR寄存器含有发生错误的内存地址
Bit 57: PCC. 表示该CPU的上下文状态已被该错误破坏,无法恢复软件代码的运行
Bits [16:31] 包含特定CPU型号相关的扩展错误码. 本例中是0x0080.
Bits [0:15] 包含MCE错误码,该错误码是所有CPU型号通用的,分为两类:simple error codes(简单错误码) 和 compound error codes(复合错误码),本例中0x0400表示Internal timer error:
– Simple Error Codes:
0000 0000 0000 0000 – 没有错误.
0000 0000 0000 0001 – Unclassified. 未分类的错误类型.
0000 0000 0000 0010 – ROM微码校验错
0000 0000 0000 0011 – MCE是由于别的CPU的BINT# 引起的.
0000 0000 0000 0100 – Functional redundancy check (FRC) master/slave error.
0000 0000 0000 0101 – Internal parity error.
0000 0100 0000 0000 – Internal timer error.
0000 01xx xxxx xxxx – Internal unclassified error. 至少有一个x等于1
– Compound Error Codes:
000F 0000 0000 11LL – Generic cache hierarchy errors.
000F 0000 0001 TTLL – TLB errors.
000F 0000 1MMM CCCC – Memory controller errors (Intel-only).
000F 0001 RRRR TTLL – Memory errors in the cache hierarchy.
000F 1PPT RRRR IILL – Bus and interconnect errors.
下一步,由于Bits [16:31] 是非零值0x0080,包含的是特定CPU型号相关的扩展错误码,我们要参考Intel手册第三卷第16章。首先确定CPU型号,我们需要的是CPU faimily和model,从/proc/cpuinfo中可以找到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # less /proc/cpuinfo ... processor : 3 vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Xeon(R) CPU L5520 @ 2.27GHz stepping : 5 cpu MHz : 2266.700 cache size : 8192 KB physical id : 1 siblings : 8 core id : 2 cpu cores : 4 apicid : 20 ... |
根据cpu family/model,即06_1AH,找到Intel手册中对应的章节,但是没找到匹配Internal timer error和0x0080的条目。所以只能到此为止了。
总结我们的发现:
CPU 3 发现了无法纠正的MCE,是Internal timer error,被中断的指令与MCE不相关,被中断的程序指令不能恢复运行,CPU的上下文已被MCE破坏。
怎样禁用MCE
可以在 /boot/grub/grub.conf 中加入以下内容:mce=off。
还有其它的MCE选项,比如禁用CMCI(Corrected Machine Check Interrupt),或者禁用MCE日志,等等,例如:
mce=off — Disable machine check
mce=no_cmci — Disable CMCI(Corrected Machine Check Interrupt)
参见文档:Documentation/x86/x86_64/boot-options.txt
每个 CPU 都有一个sysfs接口:
/sys/devices/system/machinecheck/machinecheckN
注:(N = CPU number)
其中包含的可调参数参见:Documentation/x86/x86_64/machinecheck
参考资料:
Intel – Intel 64 and IA-32 Architectures Software Developer’s Manual . Chapters 15 and 16.
AMD – AMD64 Architecture Programmer’s Manual Volume 2: System Programming . Chapter 9.