mca:MCA机制:硬件错误检测架构

一,MCA 硬件检测机制

1,Intel MCA硬件机制

  Intel引入了MACHINE-CHECK ARCHITECTURE(MCA) 和 machine-check exception(#MC)

机制用来对服务器硬件进行自检,并在发现硬件错误的时候发出中断或异常。

系统软件收到中断或异常后,会对其进行响应,进行相应的修复、告警或其他策略等动作。

通过Intel的这个RAS特性,保证在发生crash等错误前,服务器可以有机会做一些容错处理,

大大提升了Intel在数据中心高可靠服务器领域的竞争实力。

 

  Intel在Xeon,Atom,P6等中,都开始支持MCA特性。

通过MCA,系统可以探测硬件错误,如系统总线错误,ECC错误,奇偶校验错误,cache错误,TLB错误等。

MCA机制使用到了一组相关的MSR,通过这些MSR,MCA可以对硬件错误进行检测并记录检测到的错误信息。

注意这里有一个bank的概念,可以简单理解为寄存器组。

如bank1里面有MSR1_STATUS, MSR1_CTL, MSR1_ADDR;

bank2里面有MSR2_STATUS, MSR2_CTL, MSR2_ADDR;

为了方便书写为banks 含有寄存器 MSRi_STATUS, MSRi_CTL, MSRi_ADDR;其中i=0,i=1。

后面讲解MSR的时候,还会进行讲解

 

  处理器在探测到 uncorrected machine-check error(硬件不可自修复)的时候,

就会发送 machine-check exception (#MC)。这种exception是abort类型的。

MCA的实现通常来讲,是不允许在产生#MC后进行可靠的重启。

machine-check-exception的处理函数,可以通过读取相关的MSR来得到相关的错误信息。

从Intel 45nm(最新的已经规划10nm门电路的芯片)的芯片开始,处理器在

发现 corrected machine-check error(硬件可自修复的错误)的时候,

也会发送一个中断给系统软件来响应这类MC错误,这种中断被称作CMCI( corrected machine-check error interrupt)

 

  在支持MCA和CMCI的Intel 64处理上,还支持一些额外的增强特性,

可以让系统软件来恢复一些特性的 uncorrected recoverable machine check 错误(UCR),

让服务器得到更大的稳定稳定性

 

关于特定的芯片兼容性和映射这里就不多讲了,有兴趣的可以参考Intel的SDM。

 

2,Machine check相关的MCR:寄存器   <寄存器官方说明:intel Software Developer Manuals  第15,16章>

     Machine check使用了一组相关的MSR。

 这些MSR有两部分组成,第一部分是全局控制和状态寄存器(global),

 另外一部分是一些记录错误信息的banks寄存器给(banks的概念在前面的文章已经讲过,请看参考文章)。

 MSR的整体布局如下图所示:

          

    全局控制/状态寄存器: 4个64位寄存器

        IA32_MCG_CAP MSR

        IA32_MCG_STATUS MSR

        IA32_MCG_CTL  MSR

        IA32_MCG_EXT_CTL MSR

 

    错误报告寄存器组:(每个硬件单元(如:cpu,memory,cache,chipset等等)都有一组)  5个64位寄存器

        IA32_MCi_CTL MSR    (i为硬件单元编号)

        IA32_MCi_STATUS MSR

        IA32_MCi_ADDR MSR

        IA32_MCi_MISC MSR

        IA32_MCi_CTL2 MSR

    每一个错误报告的bank都同处理器中一个特定的硬件单元(或一组硬件单元)相关联。

    使用RDMSR或WRMSR来对这些寄存器进行读写。

          

     全局相关的寄存器组定义了如何开启 MCA 的能力。

     每一个BANK则具体对应一类错误源,如 CPU,MEMORY,CACHE,CHIPSET 等等。

     每一个BANK都可以进行单独的控制,这样软件就能够针对每一个BANK使用特定的方式进行处理。

     由于MCA以时间窗口为单位对错误进行采样,因此在每一个采样结束时有可能会发现有不止一个的错误产生,

     但是只会触发一次中断或者异常,因此当软件进行处理时必要要轮询所有的BANK以确保每一个产生的错误都可以被处理。   

 

 2.1,IA32_MCG_CAP MSR:

         IA32_MCG_CAP 寄存器 MSR_IA32_MCG_CAP =0x179 为只读寄存器,报告了当前芯片MCA相关的能力信息。

          

    64位,其中

    Count field: bits 7:0 当前处理器记录错误信息的banks的数量(每个bank对应一个硬件单元)

    MCG_CTL_P(control MSR present) flag: bit 8 当前处理器是否实现了 IA32_MCG_CTL MSR

    MCG_EXT_P(extended MSRs present) flag: bit 9 当前处理器是否实现了 extended machine-check state寄存器(该寄存器地址为180H)

    MCG_CMCI_P(Corrected MC error counting/signaling extension present) flag: bit 10

                当前处理器是否支持 corrected MC error事件和 count threshold of corrected MC errors时间相隔的MSR。

                如果设置了也并不是说所有的bank都支持,系统软件需要逐个bank的检查

    MCG_TES_P(threshold-based error status present) flag: bit 11 当设置了,表明 IA32_MCi_STATUS[56:53]是架构通用的,

                其中 56:55被保留, 54:53用来报告 threshold-based error的状态。

                如果没有设置IA32_MCi_STATUS[56:53]是特定芯片相关的   

    MCG_EXT_CNT:bits 23:16 表明 extended machine-check state registers的数量,只有当 MCG_EXT_P设置的时候,该位才有意义

    MCG_SER_P(software error recovery support present) flag,:bit 24 表明当前处理器支持用软件进行错误恢复

                IA32_MCi_STATUS[56:55]用来表示是否已经发送了#MC信号和表示是否软件必须进行错误恢复。

    MCG_EMC_P(Enhanced Machine Check Capability) flag:bit 25 表明处理是否支持增强的 machine check能力(firmware first signaling)

    MCG_ELOG_P(extended error logging) flag:bit 26 如果设定表明当探测到一个错误的时候,处理器允许引入 firmware,

                firmware可以提供更多的平台特性信息到MC的banks寄存器中,平台特定信息用 ACPI的Generic Error Data Entry格式提供

    MCG_LMCE_P(local machine check exception) flag:bit 27 如果设定,就表示下面两个接口被实现了:

                extended state LMCE_S (located in bit 3 of IA32_MCG_STATUS)和支持LMCE的IA32_MCG_EXT_CTL MSR。

                如果LMCE被使能了,那么MC的errors被精准发送到发生错误的CPU上,而不是所有逻辑核上.

 

    如果对 IA32_MCG_CAP MSR进行写入操作,将产生未定义的行为

 

  2.2,IA32_MCG_STATUS MSR

 A32_MCG_STATUS寄存器MSR_IA32_MCG_STATUS=0x17a用来描述在产生machine-check exception后当前处理器的状态。

     

   64位寄存器,其中:

    RIPV(restart IP valid)flag: bit 0 当设置了,表明当产生#MC后,

                处理器是否可以可信的的重新支持栈顶的EIP指针

    EIPV(error IP valid)flag: bit 1 当设置了,表明EIP指针是产生该错误的指令

    MCIP(machine check in progress)flag: bit 2 当设置了,表明已经产生了一个#MC。

                软件可以对其进行修改(当处理完#MC就会清除这个标志)。

                如果在MCIP设置的时候,又产生了#MC,那么将会让处理器进入shutdown状态,不允许#MC嵌套

    LMCE_S(local machine check exception signaled): bit 3 当设定了,表明产生了一个本地精准#MC,

                也就是说该#MC被发送给产生错误的逻辑核上,而不是广播出去

    IA32_MCG_STATUS[63:04]被保留,写入非0数据将会产生#GP.

 

  2.3, IA32_MCG_CTL MSR

    IA32_MCG_CTL 寄存器只有当 IA32_MCG_CAP.MCG_CTL_P 被设定的时候才会出现在处理器中。

    IA32_MCG_CTL 控制着 machine-check exceptions的报告。如果出现了该MSR,

            全部位写1来使能 machine-check的特性,全部位写0来关闭一些 machine-check特性。

            其他的值是未定义的或者是特定处理器相关的。

 

  2.4, IA32_MCG_EXT_CTL MSR

    IA32_MCG_EXT_CTL 寄存器只有当 IA32_MCG_CAP. MCG_LMCE_P 被设定的时候,才会在处理器中有。

    IA32_MCG_EXT_CTL.LMCE_EN (bit 0)用来使能处理器的LMCE特性(精准单播)

    如果 IA32_MCG_CAP. MCG_LMCE_P = 0或 IA32_FEATURE_CONTROL.LMCE_ON = 0,向 IA32_MCG_EXT_CTL进行读写操作将会导致#GP

    在RESET的时候, IA32_MCG_EXT_CTL MSR会被清零。

    64位寄存器,其中:

    LMCE_EN(local machine check exception enable)flag:bit 0 系统软件可以对其进行设定来使能LMCE特性。

    只有在对 IA32_FEATURE_CONTROL 进行了LMCE的使能后,才可以设置 LMCE_EN

                  

    如果要想使用LMCE,就需要对系统软件和平台软件都进行适当的设定。

     a,平台软件可以通过 IA32_FEATURE_CONTROL.LMCE_ON(bit 20)来开启LMCE功能。

     b,对于系统软件来说,在设定 IA32_MCG_EXT_CTL.LMCE_EN (bit0)之前,

       需要确保 IA32_FEATURE_CONTROL.Lock (bit 0)和IA32_FEATURE_CONTROL.LMCE_ON (bit 20)都已经被设定了。

       都使能后,软件不能假设哪种类型的错误会以LMCE的形式传递,

    硬件在发生error的时候决定是否该特定的error会以LMCE的方式传递到哪个逻辑核上。

 

  2.4, 错误报告寄存器组:

       每一个错误报告寄存器组(error-reporting register bank)都含有:

             IA32_MCi_CTL, IA32_MCi_STATUS, IA32_MCi_ADDR, 和IA32_MCi_MISC 四个MSR。

       IA32_MCG_CAP[7:0]指定了bank的数量。第一个error-reporting register(IA32_MC0_CTL)的起始地址为400H

 

    2.4.1,IA32_MCi_CTL MSRs

     IA32_MCi_CTL MSR 控制着某个(某组)硬件单元产生的error的 error reporting。

     64个标志位( EEj )的每一位代表一种可能的error,设定EEj的相应标志来使能相关error的reporting,清除了就关闭相应error的 reporting功能。

     如果处理器没有实现某些error,那么是不会改变相应位的。

 IA32_MCi_CTL如下所示:

          

 

   2.4.2, IA32_MCi_STATUS MSRS

    如果VAL(vaild flag)被设置了,那么每一个 IA32_MCi_STATUS MSR都包含了一个machine-check error的相关信息。

    软件通过显式地写入全0来清除 IA32_MCi_STATUS MSRs。对任何位写入1将会导致#GP.

          

    64位寄存器,其中:

        MCA (machine-check architecture) error code field:bits 15:0 当machine-check error被探测到,

            这里就写入了通用架构相关的 machine-check error code。既然是架构相关,

            那么所有种类的CPU都会理解这些error code的含义。注意这里也会保证和IA32的兼容。

        Model-specific error code field: bits 31:16 指定了 model-specific error code,

            这种error code是每种CPU都有不同解释的,唯一的标识了一种错误

        Reserved, Error Status, and Other Information fields:bits 56:32 这部分很乱,主要是Other Information用来提供额外信息

            和error发生的counter累加器用来判断是否超过阀值需要告警,有兴趣的可以参考SDM。

            最重要的是两个位。当 IA32_MCG_CAP[11] = 1时, S(Signaling) flag:bit 56 为1 表示需要针对该error report需要发送#MC,

            AR (Action Required) flag,:bit 55为1表明是否需要软件采用recovery Action,这也是区分 SRAR 和 SRAO 的地方。

        PCC (processor context corrupt)flag:bit 57 当设定了,就意味着整个处理器都被探测到的错误污染了,没法进行修复或者重新执行指令。

            当没有设置,表明处理器当前还没有被错误污染,软件有可能通过恢复动作让系统正常运行

        ADDRV(IA32_MCi_ADDR register valid)flag:bit 58 当设定了,就代表 IA32_MCi_ADDR寄存器中含有发生错误时候的内存地址

        MISCV(IA32_MCi_MISC register valid)flag:bit 59 当设定了,表明 IA32_MCi_MISC寄存器中包含了错误的额外信息

        EN(error enabled)flag:bit 60  当设定了,就说明了改错误在 IA32_MCi_CTL寄存器中对应的 EEj位被设定了

        UC(error uncorrected)flag:bit 61 当设定了,表明处理器不能依靠硬件来矫正这个错误(如多bit的内存错误),

            当清除则该错误可以被处理器硬件纠正(如单bit内存错误)

        OVER(machine check overflow)flag: bit 62 当设定了,就表明发生了MC嵌套。

            也就是说 error-reporting register bank里面还有着前一个错误信息(VAL=1,软件正在处理)时,

            另外一个错误就发生了。处理器会设置OVER位,软件负责清除。

            通常按照一定的规则对错误内容进行覆盖。通常来说, enabled errors 覆盖 disabled errors,

            uncorrected errors 覆盖 corrected errors。 Uncorrected errors不会覆盖前一个有效的 Uncorrected errors

        VAL(IA32_MCi_STATUS register valid)flag: bit 63 当设定了,就意味着 IA32_MCi_STATUS中的错误信息是有效的,系统软件正在处理。

            当该位被设定时,处理器按照覆写规则来处理更多的错误信息。

            处理器负责设定VAL,系统软件负责清除这个位(当处理完成)。

            覆写规则很操蛋,筒子们自己看SDM吧

 

    2.4.3,IA32_MCi_ADDR MSRs

        当 IA32_MCi_STATUS. ADDRV = 1的时候,IA32_MCi_ADDR中含有产生本次错误的指令或数据内存的内存地址。

        当IA32_MCi_STATUS. ADDRV = 0,任何读写IA32_MCi_ADDR的动作都会导致#GP

        根据遇到的错误不同,返回的地址可能是一个段内偏移量、线性地址或物理地址。

        可以通过写入全0来清除该寄存器,如果在任何一位写入1都会导致#GP。

 

    2.4.4, IA32_MCi_MISC MSRs

        当 IA32_MCi_STATUS. MISCV = 1时, IA32_MCi_MISC MSR中包含了本次 machine-check error的额外信息。

        如果IA32_MCi_STATUS. MISCV = 0,任何读写IA32_MCi_MISC MSR都会导致#GP

        软件通过显示的写入全0来清除该寄存器,当写入任意一位1都会导致#GP

        如果 MISCV =1 且  IA32_MCG_CAP[24] = 1, IA32_MCi_MISC_MSR的定义如下图所示,用来支持软件恢复uncorrected errors

           

        Recoverable Address LSB (bits 5:0): 表明error address的最低有效位。

            例如, IA32_MCi_MISC. LSB =  01001b (9), 那么 IA32_MCi_ADDR中记录的错误地址[43:9]是有效的,[8:0]直接忽略。

                如果是(01100)12,那么就说明页面是4K对其的.

        Address Mode (bits 8:6):  IA32_MCi_ADDR中记录的地址的类型,支持的类型如下所示:

            000:  segment offset

            001: linear address

            010:  physical address

            011:  memory address

            100/110: 保留

            111:  generic

        Model Specific Information:(bits 63:9): 非架构相关的

 

  2.4.5, IA32_MCi_CTL2 MSRs

    IA32_MCi_CTL2 MSR提供了对于 corrected MC error发送信号能力的可编程接口,也同时意味着要求 IA32_MCG_CAP[10] = 1。

    系统软件检查每个bank的 IA32_MCi_CTL2 :

    当 IA32_MCG_CAP[10] = 1,每bank的 IA32_MCi_CTL2 MSR都存在。

    但是对于corrected MC error不一定是每个bank都发送信号的,需要检查相应bank的标志位。

寄存器如下所示:          

                    

    Corrected error count threshold: bits 14:0 系统软件负责初始化这个域,然会会同 IA32_MCi_STATUS[52:38]比较。

        当IA32_MCi_STATUS[52:38]等于阀值的时候,就会发送一个 overflow事件到APIC的 CMCI LVT entry( APIC_BASE+02F0H)。

        如果CMCI功能没有使能,但是 IA32_MCG_CAP[10] = 1,那么这个域总是0

    CMCI_EN (Corrected error interrupt enable/disable/indicator): bits 30 系统软件

        设定这个位来使能发送corrected machine-check error interrupt (CMCI) 的特性。

        如果某bank的CMCI没有使能,但是 IA32_MCG_CAP[10] = 1,写1到这个bit会返回0,这个BIT也就意味着对应bank的CMCI是否被支持。

 

    某些微架构作为corrected MC errors的源,肯能被多个逻辑处理器共享,那么这个bank的内容也就会被共享,

    软件需要负责 IA32_MCi_CTL2 MSR内容在多个逻辑处理器间的一致性问题.

    在系统重启后 IA32_MCi_CTL2 MSRs统统被清零.

 

  2.4.6, IA32_MCG Extended Machine Check State MSRs

      对于Intel志强处理器,当 IA32_MCG_CAP.MCG_EXT_P = 1 时,实现了更多的扩展machine-check state MSRs,可以记录更加详细的错误信息。

      IA32_MCG_CAP.MCG_EXT_CNT表明了这些扩展寄存器的数量。这些扩展寄存器如下所示:

 

64位下的扩展:

 

    这些寄存器通过写入全零来进行清除,写入其他值会导致#GP。

    当硬重启时寄存器被清除( power-up or RESET),当软重启这些寄存器的值保留( INIT reset)

 

    关于兼容性我就不讲了,有兴趣的参考Intel的SDM

 

 

二,Intel MCE UCR ERRORS  : 软件恢复错误

 UCR( uncorrected recoverable machine check errors) 错误恢复是MCA的一种增强特性,

第一个支持该特性的芯片是Intel的45nm芯片(CPUID为 DisplayFamily_DisplayModel: 06H_2EH)。

该特性允许系统软件对于特定类型的 uncorrected errors做出一些恢复性动作以便保持系统的正常稳定运行。

 

1. 软件错误恢复特性能力的探测

  系统软件需要通过 IA32_MCG_CAP. MCG_SER_P(bit 24)来判断是否支持软件恢复特性。

   当 IA32_MCG_CAP[24]被设定了,那么处理器支持软件对错误的恢复,

  如果被清除了,那么意味着处理器不支持软件恢复特性,

  并且 machine check处理函数的主要职责是记录 machine check error后对系统进行关闭。

 

     新的可支持软件恢复的架构级通用的 MCA errors被叫做UCR( Uncorrected Recoverable) errors。

UCR errors 都是硬件不可自动校正的错误( uncorrected errors), 这种错误就意味着错误被系统硬件识别到了,

但是该错误还没有污染处理器的运行上下文,并且已经发送信号通知了CPU进行处理。

对于特定的UCR来说,一旦系统进行了recovery的动作后(如将错误内存页进行了隔离),

系统软件在处理器上就可以继续正常的执行而不会发生重启等崩溃现象。

UCR error reporting提供了一种方法,通过对于数据标记错误而达到容错目的。

machine check处理函数会通过寄存器读取其中的错误,然后对错误进行分析,最后根据错误的种类来完成特定UCR的恢复动作。

 

 

2,IA32_MCi_STATUS MSR用来报告UCR错误,corrected错误和 uncorrected errors错误。

IA32_MCi_STATUS的定义如下所示,通过其中的bit位可以唯一的标明UCR错误。

根据UCR的错误类型,UCR错误可以通过CMCI方式通知,也可以通过MCE( machine check exception)方式发送。

当IA32_MCG_CAP[24] = 1,通过 IA32_MCi_STATUS中的如下bit来判断是否是一个UCR:

    Valid (bit 63) = 1

    UC (bit 61) = 1

    PCC (bit 57) = 0

当 IA32_MCi_STATUS. ADDRV = 1, IA32_MCi_STATUS. MISCV = 1的时候,

IA32_MCi_MISC和  IA32_MCi_ADDR中会含有与该UCR错误相关的更多信息。

IA32_MCi_STATUS中的 MCA error code域标明了UCR的错误类型。

系统软件通过解析 MCA error code域来分析错误类型,然后采用一些相应的错误恢复行为.

另外, IA32_MCi_STATUS[56:55]提供了更多的信息来帮助系统软件更加合理的完成错误的处理.

 

S(Signaling)flag: bit 56 当设定后,如果该bank被报告了一个UCR,那么随之将会产生一个MCE (machine check exception),

        软件负责检查 IA32_MCi_STATUS中的AR标志位和MCA error code来判断到底采用哪种必要的行为来恢复错误。

        当 IA32_MCi_STATUS中的S位被清除,该UCR将不会通过MCE来通知,取而代之的是通过 corrected machine check (CMC)的方式报告。

        当没有设置S位的时候,系统软件什么都不用做。

AR(Action Required)flag:bit 55 当设定后,当一个错误被通知后,系统软件必须要采取必要的恢复动作。

            这个动作必须在其他任务被调度到本处理器前成功完成。

            当 IA32_MCG_STATUS.RIPV = 0,可以执行另外一个可选的执行流程而不必再重新执行exception前的路程。

            如果特定的恢复动作不能成功执行的话,系统软件会执行关机动作。

            当 IA32_MCi_STATUS.AR = 0,系统还是有可能执行恢复动作,但是这不是强制的。

            当 IA32_MCG_STATUS. RIPV = 1,那么系统可以在执行完恢复动作后安全地重新执行栈顶的EIP指针。

 

S和AR都被定义为粘贴位,也就是说一旦这种位被设定后,处理器就不会去清理它们。

只有系统软件和 good power-on reset可以清除S和AR位。

S和AR位只有当系统中报告了UCR错误( MCG_CAP[24] = 1)的时候才会被设置.

 

通过 IA32_MCi_STATUS中的S和AR两位的不同编码,可以将UCR分为如下类别:

    Uncorrected no action required(UCNA):该类UCR不会通过MCE进行通知,而是按照 corrected machine check error的方式报告给系统软件。

            UCNA意味着系统都的某些数据损坏了,但是这些坏数据还没有被使用,并且处理器的状态是有效的,

            所以可能继续正常执行本处理器的代码。UCNA不需要系统软件做任何的动作,就可以继续执行。

            UCNA的 IA32_MCi_STATUS的设置为 UC = 1,PCC = 0, S = 0,AR = 0

    Software recoverable action optional(SRAO): 该类UCR错误会通过MCE的方式进行通知,系统可以选择不进行恢复动作也可以选择进行,

            不是强制的。并且不需要从发生MCE的地方继续重新执行。

            SRAO错误意味着系统中有错误数据,但是坏数据没有被使用,处理器还是处于有效状态。

            SRAO提供了更多的信息让系统软件进行恢复动作。

            SRAO的 IA32_MCi_STATUS的设置为UC = 1,PCC = 0, S = 1,EN = 1, AR = 0。

            恢复的动作通过MCA error code来进行指定。

            如果 IA32_MCi_STATUS的 MISCV和 ADDRV被设定了,那么就可以在 IA32_MCi_MISC和 IA32_MCi_ADDR中读取到更多的错误信息。

            系统软件需要检查 IA32_MCi_STATUS的 MCA error code域来找到该SRAO对应的操作。

            如果MISCV和ADDRV没有被设置的话,建议不要进行系统恢复动作,而是继续正常执行。

    Software recoverable action required(SRAR): 该类UCR要求系统软件在调度其他执行流到本处理器前必须执行一个恢复动作。

            SRAR意味着错误被发现了,并且是在执行流程中发现并报告的错误。

            一个SRAR的 IA32_MCi_STATUS的设置为UC = 1,PCC = 0, S = 1,EN = 1, AR = 1。

            恢复的动作通过MCA error code来进行指定。如果 IA32_MCi_STATUS的 MISCV和 ADDRV被设定了,

            那么就可以在 IA32_MCi_MISC和 IA32_MCi_ADDR中读取到更多的错误信息。

            系统软件需要检查 IA32_MCi_STATUS的 MCA error code域来找到该SRAR对应的操作并执行它。

            如果MISCV和ADDRV没有被设置的话,建议直接采用关机处理.

UCR errors、 corrected errors 和 uncorrected errors分类如下:

     

         

另外,关于覆写规则一般如下

    UCR 覆盖 corrected errors

    Uncorrected (PCC=1) errors 覆盖  UCR (PCC=0) errors.

    UCR errors 不会覆盖前一个 UCR errors

    UCR errors 不会覆盖前一个 UCR errors

 

     IA32_MCi_STATUS.OVER 来标记是否有覆盖的情况发生。注意如果OVER的情况,且AR或PPC被设置了,那么就需要重启系统。

     更多的覆写规则,有兴趣的读者就自己查看SDM吧       

  

  3,INTEL MCE 之增加的 cache error reporting

从Intel Core Duo开始,开始引入了 cache error reporting的增强版本。

在早期的Intel芯片中,cache的状态取决于一个cache中发生错误纠正事件的次数。

在一个叫做 “threshold-based error status”的规范中指出,cache的状态现在取决于同样的纠正错误在cache中影响到的cache line的数量。

影响阀值的因素很多,由Intel给出这个值。如果 IA32_MCG_CAP[11] (MCG_TES_P)为1,那么芯片就支持 threshold-based error status

 

支持 enhanced cache error reporting的芯片都包含一个硬件可以用来跟踪某些cache的操作,并可以提供一个标志位来标识这些cache的健康信息。

当相同校正事件发生的cacheline数量小于阀值的时候,就会报告为 “green”状态;

当超过了阀值的话,就会报告 “yellow”状态。“yellow”状态意味着cache尚可正常操作,但是在短期内需要对服务器进行维护保养了

 

Intel建议我们使用 threshold-base error reporting机制

 

CPU/system/platform不需要像相应 uncorrected error那么紧急的相应"yellow"状态告警。

一个 uncorrected error意味着系统遇到了一个很严重的问题,然而yellow状态意味着收影响的cacheline超过了阀值,

但本事不是一个严重的问题;发生了yellow状态的服务器上的错误已经被纠正了,系统尚可正常运行.

 

对于同一个ECC校正多bit错误产生的 uncorrected error来说,green/yellow状态标识符并不一定会比 uncorrected error来的快。

这样的错误可能会在yellow到达阈值前先发出 uncorrected error。

当然,随着被影响的cacheline的忽略增加, uncorrected error先发生的记录也就随着增大。

 

三,Intel MACHINE-CHECK 特性的初始化伪代码实现:

     如果要使用MCA,首先要初始化芯片来激活 machine-check exception和 error-reporting机制.

 

     1,下面给出的伪代码中展示了如何完成MCA的初始化动作。

     本代码首先检测MCA和exception是否被芯片支持;然后使能 machine-check exception和 error-reporting的bank寄存器。

 

     在主板上电以后, IA32_MCi_STATUS寄存器中的数据并不保证是正确的直到软件将它初始化为全0

 

伪代码如下:

/*获得CPUID, 并检查芯片是否支持MCA和MCE*/if(CPUID[14]){ 支持MCA;} if(CPUID[7]){ 支持MCE;}if(CPU支持MCE){ if(CPU支持MCA){ if(1 == IA32_MCG_CAP.MCG_CTL_P) /*芯片上有IA32_MCG_CTL寄存器*/{ IA32_MCG_CTL = FFFFFFFFFFFFFFFFH; /*使能所有的MCA特性*/ } if(1 == IA32_MCG_CAP.MCG_LMCE_P && 1 == IA32_FEATURE_CONTROL.LOCK && 1 == IA32_FEATURE_CONTROL.LMCE_ON){ /*芯片中有IA32_MCG_EXT_CTL寄存器,且平台支持LMCE特性*/ IA32_MCG_EXT_CTL = IA32_MCG_EXT_CTL | 01H; /*使能LMCE特性,以便精准单播MCE到被影响的CPU*/ } /*确定支持的error-reporting banks数量*/ COUNT = IA32_MCG_CAP.Count; MAX_BANK_NUMBER = COUNT - 1; if(P6处理器家族, 且 EXTMODEL:MODEL <= 1AH){ /*除了第0个bank,使能所有的bank来记录错误*/ for(i=1; i <= MAX_BANK_NUMBER; i++){ /*从1到MAX_BANK_NUMBER*/ IA32_MCi_CTL = 0FFFFFFFFFFFFFFFFH; } }else { /*使能所有的bank来记录错误*/ for(i=0; i <= MAX_BANK_NUMBER; i++){ /*从0到MAX_BANK_NUMBER*/ IA32_MCi_CTL = 0FFFFFFFFFFFFFFFFH; } } /*在power reset的时候,BIOS清除所有的错误状态记录*/ if(BIOS发现在power reset){ for(i=0; i <= MAX_BANK_NUMBER; i++){ /*从0到MAX_BANK_NUMBER*/ IA32_MCi_STATUS = 0x0; } }else{ for(i=0; i <= MAX_BANK_NUMBER; i++){ /*从0到MAX_BANK_NUMBER*/ 记录STATUS中已有的错误; /*既可BIOS做,也可以由OS做*/ IA32_MCi_STATUS = 0x0; /*只能OS做*/ } } } 在IDT的18号向量中,设定 Machine Check Exception (#MC) 的处理函数 CR4.MCE = 1; /*设定CR4的16bit, 使能Machine-Check Exceptions*/}

 

    2, Intel处理器中断18 - Machine-Check Exception (#MC)介绍

      当处理器探测到机器内部错误或者总线错误的时候,就会发送该中断。

    当然,一个外部的硬件代理设备探测到总线错误的时候,也会发送该中断。

    近期的工作是围绕内存错误的,所以发现了内存错误,就会对处理器发送该中断。

    machine-check exception 在Pentium以后的芯片上才得以支持,而且对于不同的CPU模型都是不同的,需要参考相关的model-specific。

    Intel不同处理器家族对于machine-check exception的实现都是不同的,并且Intel不保证在未来的Intel 64 或 IA-32处理器上兼容现在的实现.

 

      当外部的硬件代理设备诊断到一个总线错误的时候,就会对处理器特定的引脚发送信号:

    对于Pentium 4, Intel Xeon, 和P6 family处理器会对INIT# 和 MCERR#管脚发送信号;

    对于Pentium处理器会对BUSCHK#管脚发送信号。

    当这些管脚中的任何一个被使能后,相应的错误信息都会被保存到machine-check相关的处理器中,然后产生一个machine-check exception

 

Exception 错误码:

    相关的错误信息可以通过machine-check相关MSR得到

错误现场指令指针:

    得到错误现场当事运行所运行的指令指针,非常有利于定位和解决问题。Intel提供了相关的方法。

    对于Pentium 4和Intel Xeon处理器,machine-check state相关寄存器中保存的内容就是导致machine-check exception发生的错误指令和相关信息

    对于P6 family处理器MCG_STATUS MSR的EIPV标志位被职位,那么CS和EIP寄存器中的内容就是导致machine-check exceptio发生的错误指令;

        如果EIPV被清除,那么EIP中的指令信息就和错误没有关系.

    对于Pentium处理器,CS 和 EIP 寄存器中的内容都和错误没有关系.

功能开关:

    通过CR4寄存器MCE标志位,可以使能machine-check机制

    对于Pentium 4, Intel Xeon, P6 family, 和Pentium 处理器,如果开关发生了变化,都会导致产生一个machine-check exception,

    随之产生一个abort类的exception。对于abort exception,该exception的信息可以通过machine-check相关的MSR得到。

    注意,一旦该机制被关闭了,运行过程中通常就不能重启该机制.

 

如果machine-check机制没有被使能(CR4寄存器MCE标志位没有置位),一个machine-check exception就会导致处理器进入到shutdown状态.

 

 

四, Intel MCE CMCI:   CMCI是MCA架构中新增的一种中断

1,CMCI 概述

 Corrected machine-check error interrupt (CMCI) 是MCA的增强特性。

 在原来的芯片里面,都是使用一种叫做threshold-based error reporting的机制来处理corrected error.

 但是threshold-based error reporting需要系统软件周期性的轮询检测硬件的corrected MC errors,造成CPU的浪费.

 CMCI 提供了一种机制,当corrected error发生侧次数到达阀值的时候,就会发送一个信号给本地的CPU来通知系统软件。

 当然,系统软件可以通过IA32_MCi_CTL2 MSRs来控制该特性的开关.

 

    默认的情况下,CMCI是被禁止的。 在IA32_MCG_CAP[10] = 1的情况下,系统软件需要使能每一个bank的

        IA32_MCi MSR的 CMCI位来让芯片通过中断报告 hardware corrected errors

 

    系统软件可以通过 IA32_MCi_CTL2 MSR来分别开关每个bank的CMCI特性,并且可以对 IA32_MCi_CTL2 MSR中的阀值数进行设置。

        CR4.MCE的值和 IA32_MCi_CTL MSR的值不会影响到CMCI特性

 

    可以通过向IA32_MCi_CTL2[14:0]中写入希望的阀值的方法,来探知某个bank中是否已经设置了阀值。

        如果可以写入并保存,则该bank支持设置阀值(同时CMCI也一定是支持的)。

        如果写入后,IA32_MCi_CTL2[14:0]都是0,那么就不支持设置阀值。

 

    同样,软件向 MCi_CTL2[30]写入1,来判断是否可以发送CMCI新号。

        如果写入后,MCi_CTL2[30]=0,那么该BANK就不支持CMCI,如果MCi_CTL2[30]=1,那么CMCI就是支持并使能的。

  CMCI操作图如下:

    CMCI中断的传送被配置向为LAPIC寄存器空间中的 LVT CMCI写入,该寄存器默认的起始地址为 APIC_BASE + 2F0H。

如果MC errors影响到了多个逻辑处理器,那么CMCI就会被传递到多个逻辑处理器上。

例如,两个逻辑处理器共享一个cache,在该cache上发生了可校正(corrected)的bit错误,

那么CMCI就会被传递到这两个相关的逻辑处理器上去。

同样的,Package级别的错误会导致CMCI被传递到该Package内所有的逻辑处理器上去。但是,系统级的错误是不会被CMCI处理。

 

通过上图也明显的看出,当Error的数量超过了阀值的限定且Signal位被开启的时候,CMCI就会被传递写入到LAPIC的CMCI LVT中。

 

2,系统软中中对CMCI的实现

系统软件必须负责使能CMCI,并能管理CMCI。

系统软件在相关的逻辑CPU上需要设定CMCI的中断处理程序,可以对CMCI LVT进行编程,

并且可以串行的查询被多个处理器共享的 machine check banks

 

本文将讲述系统软件如何管理CMCI,包括:CMCI的初始化,中断处理函数(如何同最小的方式处理banks访问的竞争问题)

 

2.1,CMCI的初始化

虽然根据 corrected MC error的特性,CMCI可能被传递到多个逻辑处理器上,

但是只需要一个逻辑处理器完成相应的处理且完成相关banks的查询。

下面描述的步骤,将最小化系统响应CMCI的操作.

 

    a,为每个逻辑处理器定义一个每线程数据结构( per-thread)(这里的每线程指的是处理器的超线程)

    来增加CMCI的平衡影响概率。

    特别的每超线程结构都要包括一组banks的域,这些bank的域用来记录CMCI相关的bank寄存器的信息。

    banks域的数量由 IA32_MCG_CAP[7:0]的值决定。

 

    b,初始化每超线程结构的初始化工作必须在系统中的每个逻辑处理器上串行的完成,

    而从哪个逻辑处理器开始初始化每超线程变量是任意的。

    但是初始化工作必须注意一下几点来确保能够正确的处理多处理器共享MSR资源:

        1,每个线程都初始化自己的相应的数据结构,初始值为该超线程不负责处理任何MC bank的CMCI.

        2,该超线程检查所有bank的 IA32_MCi_CTL2[30],该位表示是否本bank的CMCI已经被其他超线程处理了

            如果 IA32_MCi_CTL2[30] = 1,那么该bank的CMCI处理权已经被其他超线程占有了,

                    那么就检查下一个bank,直到所有的bank都检查完毕

            如果 IA32_MCi_CTL2[30] = 0,那么进入步骤3

        3,向 IA32_MCi_CTL2[30]写入1后进行读取,查看是否为1,为1才表示支持CMCI

            如果 IA32_MCi_CTL2[30] = 0,那么表示本bank不支持CMCI,返回步骤b检查下一个bank,直到所有的bank都检查完毕

            如果 IA32_MCi_CTL2[30] = 1,那么修改每超线程数据结构,标明本超线程负责处理本MC bank的CMCI,

                初始化错误阈值数量(bit 15:0). 然后返回b检查下一个bank,直到所有的bank都检查完毕

    c,当处理器线程已经检查了所有的 machine check banks,该线程就会查看他自己是否负责某个(几个)MC bank的CMCI处理

      如果该超线程负责CMCI的处理,那么

    d,确保CMCI的中断处理函数已经被安装

    e,初始化CMCI LVT

    f,记录并清除该thread所管理bank的 IA32_MCi_Status寄存器的值,这样的话才可以记录新的错误信息

 

2.2,CMCI的阈值管理

    IA32_MCi_CTL2[15:0]表示了 Corrected MC error 的阈值域,是架构通用的。

    特别是,这个域是软件写入的,每个thread、每个bank都可以设定不同的值。

    这个阀值用来和 IA32_MCi_STATUS[52:38]中的错误数量进行比较,超过了就发送CMCI。

    下面描述了系统软件处理阈值管理的方法:

        a,系统软件可以通过向 IA32_MCi_CTL2[15:0]中写入1来初始化阈值为1,这样的话就会导致每个 corrected MC error都会发出CMCI

        b,提高阈值的值来减少CMCI的发生频率,从而减小系统负载

            1,探知最大阀值的方法

                向 IA32_MCi_CTL2[15:0]中写入0x7FFFF

                读回 IA32_MCi_CTL2[15:0]的值,低15bit(14:0)就是该处理器支持的最大阀值

            2,设置阀值位步骤a中探测的最大阀值

 

2.3, CMCI中断处理函数:

    下面描述CMCI中断处理函数的处理流程

    1,本线程的中断处理函数 per-thread结构中的私有数据,来查看该中断处理函数负责哪个bank的CMCI。

       如果中断处理函数没有本bankCMCI的处理权,那么就检查下一个bank,处理权是在初始化时候定下来的。

    2,如果本线程拥有本bank的CMCI处理权,那么:

        2.1,通过 IA32_MCi_STATUS.VALID[63]检查MC errors的有效性

            记录MC errors

            清除本MC bank的相关MSR

        2.2,如果没有有效的错误,处理下一个bank

    3,当所有的 MC banks都被处理器了,退出中断处理函数,返回到原先执行程序的执行流中

 

以上描述的每逻辑处理CMCI MC errors的方式是独立运行的,不需要对访问共享MSR资源进行串行加锁访问           

 

 

 

 

五,总述  http://www.ibm.com/developerworks/cn/linux/l-cn-ras/

MCA(Machine Check Architecture)

Machine Check Exception 是一类由硬件错误触发的异常,譬如当 CPU 检测到总线,CHIPSET,内存,

CACHE,TLB 等硬件出现致命错误时会触发这类异常。

一般来说这些错误对系统的稳定性危害极大而且无法恢复,通常会触发系统的复位操作。

这些错误在一个大型的服务器环境如服务器集群或者是云计算的环境下是不可避免的,

因此必须对此有相应的处理机制。在 MCA 架构的出现之前,OS 对 MCE 的处理非常有限,

经常就是简单的重启系统,有时也会显示如下所示的错误输出:

 CPU x: Machine check exception...

或者

 Memory DIMM ID of error: ...

对于管理员而言,简单的系统重启难以接受,而且出错现场经常无法保存,从而无法排错。

即使能够保留下一些日志,除非有很强的专业知识,否则完全不知道真正产生错误的原因是什么。

这些问题都在新的 MCA 中得到了解决和改进。

利用新的 MCA 架构,OS 可以根据不同的错误源产生的错误类别,错误的严重程度,

软件可以选择隔离错误,记录错误,甚至屏蔽错误源(对于不重要的非致命的错误类型)以避免干扰,

或是必须要复位系统。而且在新的 MCA 架构下,错误的记录管理,以及可读性都有了很大的提高。

MCA 对各种错误的如下所示:

MCA 错误分类分级表:

    Non-Recoverable/Fatal:不可恢复/致命的错误--》强制系统复位

        如:Multi-bit Error in Kernel--》System Reset

    Recoverable:不可纠正但是可恢复--》传统的MCE异常(18)处理

        如:Multi-bit Error in Application--》OS Recoverable: System Available

        Patrol Scrub Error--》OS Corrected: Execution Continues

    Corrected: 可纠正    --》CMCI中断进行处理

        如:Single-bit Error in Write-Through Cache    Corrected

            --》Firmware Corrected: Execution Continues

        Most Single-bit Errors

            --》Hardware Corrected: Execution Continues

 

CMCI中断:(存在于每个CPU的Local Vector Table中的中断向量,不是异常)

smp_threshold_interrupt()    mce_threshold_vector()    =intel_threshold_interrupt()static void intel_threshold_interrupt(void){    if (cmci_storm_detect())        return;    machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned));    mce_notify_irq();}

MCE中断:   

machine_check_vector = do_machine_check;    for (i = 0; i < banks; i++) {        __clear_bit(i, toclear);        if (!test_bit(i, valid_banks))            continue;        if (!mce_banks[i].ctl)            continue;

只有在中断使能的前提下,CMCI 才能起作用。

如果 Error threshold 也使能,则只有当产生同类型的错误达到 threshold 的预设值时,一个 CMCI 中断才会产生,

当预设值为 0 或者为负值时 CMCI 不起作用。注意,当中断发生后,软件必须负责清除 MCi_STATUS 寄存器,

否则不会触发下一次 CMCI 中断。CMCI 是基于 BANK 的,也就是说每一个 BANK 都可以根据需要开关 CMCI 中断,

threshold 的值也可以根据需要有所不同。

以 CACHE 相关的 BANK 为例,通过设置 threshold,用户可以对 CACHE 的当前状态进行读取。

当 CACHE 错误事件的增加还未超过 threshold 时,其状态称为"green",表明 CACHE 是健康的;

如果错误事件的记录超出这个 threshold 时,其状态会自动变为"yellow",表示当前 CACHE 仍然是可靠的,

但是由于其报告的错误数量很大,超过了预设的阈值,处于报警状态。

当管理员看到这样的信息时,最好尽快对系统,尤其是对 CPU 做相应的处理。

以 Intel 的 CPU 为例,CMCI 的初始化代码如下所示:

CMCI的初始化:

static void intel_init_cmci(void){ int banks; if (!cmci_supported(&banks)) return; mce_threshold_vector = intel_threshold_interrupt; cmci_discover(banks, 1); //对每个可用的bank进行初始化 /* * For CPU #0 this runs with still disabled APIC, but that's * ok because only the vector is set up. We still do another * check for the banks later for CPU #0 just to make sure * to not miss any events. */ apic_write(APIC_LVTCMCI, THRESHOLD_APIC_VECTOR|APIC_DM_FIXED); cmci_recheck();}

  除了 CMCI,MCA 还新增了对 UCR Error(Uncorrected Recoverable Error)的支持。

上文提到,UCR 通过 MCE 异常处理,不过 UCR 还可以细分为 SRAR 和 SRAO 两种不同的类型的 MCE。

SRAR(software recoverable action required)和 SRAO(software recoverable action optional)的相同点是:

对于已知类型的错误,OS 需要根据错误类型执行不同的动作将系统从错误状态中恢复;

不同点是对于未知类型的错误,SRAR 需要将系统复位,而 SRAO 则可以让系统继续正常运行。

下文讨论的 HWPOISON 就是利用了 UCR 的划分来完成不同的处理行为。

 

  MCE 的错误会被记录在内核特定的缓冲区中,目前最大支持 32 个记录。

当超过这个数时,除非原有的错误记录被读取过,否则新的错误不会覆盖原有的记录,

这是基于更早的错误更有价值的假设来设定的。应用软件可以通过 /dev/mcelog 读取错误记录并进行解码。

这是因为 MCE 产生的原始信息是经过编码的,内核没有实现将解码后的可读信息通过 /dev/mcelog 接口提供给用户,

而是将原始信息交给用户层,由用户层的程序来解码,当然这仅仅是一个策略选择的问题。

目前主要使用 mcelog 这个程序来完成以上功能。由于内核缓冲区十分有限,

因此,mcelog 一般是作为一个 daemon 运行在后台,定时读取 /dev/mcelog 来获取最新的错误记录并将其保存在磁盘上。

 

  由于 MCE 相对于其他中断异常十分稀有,因此测试 MCA 显得格外困难。当前采用 error inject 的方式来测试 MCA。

通过 inject,可以触发真正的异常-可以看作是黑盒测试,通过异常来检测 OS 对 MCE 的处理正确与否;

也可以产生虚假的(faked)异常-可以看作是白盒测试,开发人员可以通过 faked 类型的异常来测试代码的覆盖率和执行流程。

内核通过 mce-inject 模块来实现相应的 inject 接口。如果支持 inject,则 /dev/mcelog 是可写的,否则只读。

展示了 inject 接口是如何支持写操作的过程。

static int inject_init(void){ if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) return -ENOMEM; printk(KERN_INFO "Machine check injector initialized\n"); mce_chrdev_ops.write =mce_write; register_die_notifier(&mce_raise_nb); return 0;}mcelog 设备的注册过程如下:struct file_operations mce_chrdev_ops = { .open = mce_open, .release = mce_release, .read = mce_read, .poll = mce_poll, .unlocked_ioctl = mce_ioctl,};EXPORT_SYMBOL_GPL(mce_chrdev_ops);static struct miscdevice mce_log_device = { MISC_MCELOG_MINOR, "mcelog", &mce_chrdev_ops,};static __init int mcheck_init_device(void){ .... misc_register(&mce_log_device); ....}

目前在用户空间主要使用的注入工具是 mce-inject。通过 mce-inject 和 mcelog 的联动,

就可以触发各种特定的异常并分析异常的结果来测试 MCA 的工作正确与否。

 

HWPOISON:

MCE与HWPOISON关联过程:

void do_machine_check(struct pt_regs *regs, long error_code){ .... if (severity == MCE_AO_SEVERITY && mce_usable_address(&m)) mce_ring_add(m.addr >> PAGE_SHIFT); ....}

HWPOISON核心接口:

void mce_notify_process(void){ unsigned long pfn; mce_notify_irq(); while (mce_ring_get(&pfn)) memory_failure(pfn, MCE_VECTOR); //HWPOISON的主入口。执行策略 recovery/delay/ignore/failure}

 

 

 

 

 

 

 

 

 

 

 

相关推荐

相关文章