Machine check handler や Generic Hardware Error Source 、あるいはさいきんのLinuxのメモリ監視機能について

こんにちわ。せじまです。

Surface Pro Xほしいですね。 Surface DuoもSurface Neoもほしいですね。Microsoftさんが、こんなにもワクワクするハードウェアを提供してくれるようになるとは、最高ですね。楽しみですね。

どうでもいいですが、Pro、Duo、Neoと韻を踏んでていいなと個人的には感心しています。

はじめに

いちおう、わたしの専門分野は InnoDB かな?と思っているのですが、サーバの低レイヤーの部分も、嗜む程度に見ています。一年くらい前、Skylake-SP世代のサーバをオンプレミス環境に導入した際、いろいろ調査するなどしました。

あまりにもニッチで誰得なのか(いや少なくとも俺得ではある)という話もあるのですが、Skylake-SPあたりから変わりつつある部分ですので、せっかくなので備忘録を兼ねて書いておこうと思います。
はじめに断っておきますと、わたしはこちら方面シロウトなので、 Kernel/VM 界隈からご意見・ご感想いただけますと幸甚です。

今日は、Linux の Machine check handler や、 Generic Hardware Error Source、 あるいはメモリ監視機構の話をします。

※Ubuntu 16.04 LTS で kernel 4.15 をベースにお話します。

具体的にどのような話をするか最初にまとめますと、次のような話になります。

  • サーバ製品側でメモリ監視機能が搭載されていた場合、むかしから、EDACを無効化し mce=ignore_ce することは推奨されていたようです。ただ、このあたり詳しくはベンダーさんに確認するのが良いでしょう。
  • SKylake-SP以降、ごく一部のベンダーでは ghes_edac が有効になるようになりました。今後、 ghes_edac が有効になるベンダーは増えていくかもしれません。
  • ghes_edac が有効になっている場合でも、 mce=ignore_ce にするのは良いかもしれません。これも詳しくはベンダーさんに確認してください。
  • MCE とか GHES とか GHES_EDAC とか、自分で調べても良いかなと思ったので、自分で調べました。
  • ghes.disable=1 にすると、 ghes_edac を無効化することはできます。
  • 特定の不具合に対する workaround として ghes.disable=1 が推奨されることはあるのですが、そういうことでもない限り、 ghes_edac 無効化しなくても良いかも、と認識しています。

では、はじめます。

サーバのメモリ監視機能

サーバ製品には、メモリの故障を検知する機能を持つものがあります。

例えば、HPEのサーバには、HPE Advanced Memory Error Detection Technology(HP Advanced Memory Error Detection Technology)という機能が、実に20年以上前から搭載されているようで、訂正可能なエラーの発生傾向から、訂正不可能なエラーの発生を、事前に検知することが可能だったようです。

詳しくは、次のTechnology briefに、詳細な解説があります。

HP Advanced Memory Error Detection Technology

HPEに限らず、サーバ製品にはメモリ監視機能を搭載したものが多いのですが、このメモリ監視機能が、Linux の Machine check handler や hardware-driven EDAC driver (chipset-specific EDAC module)と競合することがあります。

Machine check handler

Machine Check Exception というものがあります。むかしから物理サーバを使ってる年季の入ったエンジニアであれば、いままで何度か見たことがあるのではないでしょうか。あるいは、WindowsやMacをクライアントとして使っている際、不幸にも遭遇したことがある方もいらっしゃるでしょう。

詳しい説明は、WikipediaのMachine-check exceptionや、 Machine check handling on Linux を読んで頂くと良いかもしれません。

Ubuntu で kernel 4.15 は、arch/x86/kernel/cpu/mcheck/mce.c は kernel module ではなく、CONFIG_X86_MCE=y で build されているようです。mce.c がload されているのは、次のように dmesg で確認できます。

ここでいう MCE banks とは何でしょうか。さきほどの “Machine check handling on Linux” には、次のような記述があります。

In addition there are some more registers for each bank. A bank is a group of
errors generated by a specific subsystem (like CPU, bus unit, cache, north
bridge).

The number and meaning of banks is CPU dependent.
Each bank has a number of sub-errors that can be enabled or disabled
individually. Normally a generic machine check handler enables all errors and
all banks

A machine check bank also has a register for the address associated with the
error.

雑にいうと、 Machine Check bank は、CPU、バス、キャッシュ、PCI-Express などで発生したエラーの情報を、格納するところと考えて良さそうですね。

また、 さきほどの “Machine check handling on Linux” には、次のような記述もあります。machine check には、 uncrrectable な Machine Check Exception と、 correctable な silent machine check の、二種類があるようです。

There are two main kinds of machine check: machine check exceptions
(MCEs) and silent machine check. A machine check exception happens when
there is an error that the hardware cannot correct. It will cause the CPU to
interrupt the current program and call a special exception handler.

With a silent machine check the hardware was able to correct the error, but
logged the event to internal registers. There the event can be read by the
operating system or the firmware later. Silent machine checks don’t need
immediate software or administrator action, but it is useful to log and analyze
them to get early cues about hardware problems.

では、 Linux はこれらに対してどのような実装になっているのでしょうか。 Linux の Machine check handler は、 corrected error を polling でチェックする実装になっているそうで、 mce=ignore_ceのドキュメントに、次のような記述があります。

mce=ignore_ce にして嬉しいのはどういったときかというと、その一つは、サーバのBIOSなどが、メモリ監視機能を持っていたときです。サーバに組み込まれているメモリ監視機能が、メモリ故障の予兆検知のために corrected error を活かせなくなる可能性があるので、 mce=ignore_ce にすることが望ましいとされています。

SUSE のドキュメントにも、mce=ignore_ce に関する記述が存在します。

Considerations for dealing with correctable memory error messages

ちなみに、 mce=ignore_ce になっているかどうかは、 /sys 配下をみると確認することができます。

ghes_edac

Ubuntu に限らず、kernel 4.15 以降 かつ Skylake-SP 世代の HPE のサーバでは、 Skylake-SP 用の EDAC 用 kernel module(skx_edac.ko) ではなく、kernel組み込みの ghes_edac が 有効になるようになりました。

https://github.com/torvalds/linux/commit/5deed6b6a479ad5851d7ead6412dc6faa84a694e

drivers/edac/ghes_edac.c を使う際、 hardware-driven EDAC driver ( chipset-specific EDAC module)は自動的に無効化されます。
次に解説されているように、 ghes_edac.c は Firmware first な EDAC driver として設計されているそうです。

https://lwn.net/Articles/539429/

There are currently 3 error mechanisms inside the Linux Kernel: edac, mcelog and ghes.

Unfortunately, not all those error mechanisms will work at the same time, as accessing the error registers by the BIOS may interfere on reading them from OS.

So, all those 3 mechanisms need to be integrated, in order to avoid such problems.

This patch series adds a new EDAC driver that uses "Firmware first" APEI/GHES as an error report mechanism. It automatically disables the hardware-driven EDAC drivers when GHES is enabled, preventing to have both OS and BIOS to read at the very same error mechanisms.

kernel 4.15 の Kconfig にも、 ghes_edac についての解説があります。確認したところ、 Ubuntu において kernel 4.15 では、 CONFIG_EDAC_GHES=y になっていますので、ghes_edac.ko は module ではなく、 kernel 組み込みになりました。ただ、次の解説にあるように、 ghes.disable=1 とすることで、 ghes_edac を無効化することは可能です。

ghes_edac.c の Frimware First mode が有用なのは、サーバのメモリ監視機能との競合が避けられるからのようです。

ちなみに、 ghes.disable=1 にして ghes_edac を無効化すると、Skylake-SP では skx_edac.ko が load されるようでした。 skx_edac.ko がサーバのメモリ監視機能と競合するのであれば、 skx_edac.ko は無効化した方が良いんでしょうね。

Firmware First mode については、 Intel の次の white paper に記述があります。

https://firmware.intel.com/sites/default/files/resources/A_Tour_beyond_BIOS_Implementing_APEI_with_UEFI_White_Paper.pdf

APEI Error handling models

APEI offers two error models, Firmware first model and OS Native model. Firmware 1st is used when the host firmware needs to initially examine the error and attempt recovery or corrective action in an OS transparent way. This model is also used when certain OEMs want more control over error handling before the OS takes control, such as for purposes of executing some management functions. In the Firmware 1st model, all errors are initially signaled to the host firmware via SMI or other General Purpose Input (GPI) events. Then host firmware analyzes and decides what to do, and at the end of the flow creates a detailed APEI error log with FRU information to OS. Finally, the host firmware will then signal the OS about the existence of the error via SCI, NMI, or other interrupts.

The OS native model, on the other hand, provides handling of the error directly by the OS or OS level software by directly accessing the hardware registers and analyzing the error. This requires standard architecture in the hardware for providing error information in the hardware and signaling, for e.g. industry standard PCIe AER and x86 MCA architectures. This model takes the burden off of host firmware.

Platforms can use combined model also, where some errors are handled firmware 1st, some natively and some both (a.k.a. parallel model). Many of the servers employ this combined model for better handling and managing the server better via remotely.

たいへん雑に言いますと、 OS Native model は hardware register (error bank) から、OSが直接 error を読み込んでしまいますが、 firmware first model は、 firmware が最初に bank を読むような振る舞い、ということでしょう。

ghes.c が firmware first mode で初期化されている場合、次のように dmesg で確認することができます。

https://github.com/torvalds/linux/commit/9fb0bfe1408d5506b7b83d13d1eed573fd71d67d

https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/ghes.c#L1235

ghes_edac に対応しているベンダーは、まだ限定的なようで、多くのベンダーは、 sky_edac.ko(hardware-driven EDAC driver あるいは chipset-specific EDAC module) で動作しているようです。それは何故かと言いますと、次のような commit log があります。

https://github.com/torvalds/linux/commit/5deed6b6a479ad5851d7ead6412dc6faa84a694e#diff-1690ecd81c78e5312438ae4279550e8d

必ずしもすべてのベンダーのBIOSが、APEIに正しく準拠しているとは限らない、といったところなのでしょう。

ghes.c が firmware first mode で初期化されているサーバに置いて、 /sys/devices 配下は、例えば次のようになったりします。

/sys/devices/platform/GHES.* というのは、ACPI Table の HEST から読み込んだ Source Id によるものようです。具体的に、初期化してるのは次のあたりのようです。

https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/hest.c#L222
https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/hest.c#L253
https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/hest.c#L192
https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/hest.c#L202
https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/hest.c#L151
https://github.com/torvalds/linux/blob/v4.15/drivers/acpi/apei/hest.c#L173

HEST は、次のように disassemble して確認できます。

先程の例ではSourece Idが 0000, 0001, FFFE なので、それぞれ GHES.0, GHES.1, GHES.65534 に 対応しているようです。

ghes_edac は、どの Source Id が Memory Controller なのか、事前に知っているわけではないと考えられます。
ghes_edac の初期化周りを確認すると、SMBIOSの仕様に従って、 dmidecode して DIMMの情報を読み取っているだけで、 Source Id を意識しているわけではないように見えます。

https://github.com/torvalds/linux/blob/v4.15/drivers/edac/ghes_edac.c#L497
https://github.com/torvalds/linux/blob/v4.15/drivers/firmware/dmi_scan.c#L1023
https://github.com/torvalds/linux/blob/v4.15/drivers/firmware/dmi_scan.c#L1035
https://github.com/torvalds/linux/blob/v4.15/drivers/firmware/dmi_scan.c#L89-L135
https://github.com/torvalds/linux/blob/v4.15/drivers/firmware/dmi_scan.c#L115
https://github.com/torvalds/linux/blob/v4.15/drivers/edac/ghes_edac.c#L84-L173

そして、ghes_edac_report_mem_error() で、どのようなエラーであるか判定するなどし ているようです。

https://github.com/torvalds/linux/blob/v4.15/drivers/edac/ghes_edac.c#L234-L292

Section Type は、Generic Error Data Entry に含まれているものですが、それぞれ、 後述 するACPIやUEFIのspecに記述があります。

https://github.com/torvalds/linux/blob/v4.15/include/acpi/actbl1.h#L655-L658

GHES(Generic Hardware Error Source)

GHES の仕様については ACPI Specification version 4.0, section 17.3.2.6 に記述されています。

17.3.2.6 Generic Hardware Error Source

The platform may describe a generic hardware error source to OSPM using the Generic Hardware Error Source structure. A generic hardware error source is an error source that either notifies OSPM of the presence of an error using a non-standard notification mechanism or reports error information that is encoded in a non-standard format.

Using the information in a Generic Hardware Error Source structure, OSPM configures an error handler to read the error data from an error status block – a range of memory set aside by the platform for recording error status information.

As the generic hardware error source is non-standard, OSPM does not implement built-in support for configuration and control operations. The error source must be configured by system firmware during boot.

APEI Error handling models で Firmware first model が採用されているケースでは、 GHES が使われる、ということでしょう。

Generic Error Data entry

ACPI 6.1 の specの Table 18-343 Generic Error Data Entry に記述があります。 Section Type は offset 0 にあり、Section Descriptor については UEFI specification に記述されています。

Section Descriptor

UEFIのspecのN.2.2 Section Descriptor に記述がありました。

UEFI Specification に対応する Section Type は、 kernel 4.15 では次のように、CPUや PCI-Expressなどに関するものが定義されています。

https://github.com/torvalds/linux/blob/v4.15/include/linux/cper.h#L166-L216

これらを踏まえて

現状、わたしとしては次のように認識しています。

  • サーバ製品側で持っているメモリ監視機能は、MCE Bank から Correctable Memory Error などを読み込んで、その情報を活用していることがある。
  • よって、 mce=ignore_ce を設定し、Linux の Machine check handler が polling で corrected error を読み取らないようにするのは、適切な場合がある。(詳しくはベンダーさんに確認する)
  • sky_edac.ko などの hardware-driven EDAC driver あるいは chipset-specific EDAC module がサーバ製品側のメモリ監視機能と競合する場合、無効化した方が適切な場合がある。(詳しくはベンダーさんに確認する)
  • ghes_edac は Firmware First であり、サーバ製品側のメモリ監視機能と競合しないよう、意識して設計されている。
  • kernel 4.15 の ghes は、HEST に定義された Source Id からエラーの情報を取得し、適切にハンドリングしている。それらの情報は、 Memory だけでな く、 PCI-Express などのエラーも含まれると考えられる。
  • ghes が有効になっていると、 kernel 側で PCI-Express などのエラー情報なども取得できるであろうから、ghes_edac が有効になっている環境において、 よほどの不具合がない限り、 ghes.disable=1 にする必要はないと考えられる。(詳しくはベンダーさんに確認する)
  • Generic Hardware Error Source が活用できたら何が嬉しいかというと、特定のチップセットや、特定のCPUの世代向けに開発されたEDAC moduleを使わなくても、kernel はハードウェアのエラーハンドリングを実現できるようになる。ただその場合、 firmware などが適切に設計されていることが求められる。

もともと、メモリ監視機能は、HPCなどでDRAMたくさん使うようになった結果、メモリの故障に悩まされるようになったので、その影響を緩和するために改善されていったもののようです。さいきんのサーバ用CPUは、昔と違って桁違いに大量のDRAMを詰めるようになりましたので、こういった設定を見直してみるのも、安定稼働のためには良いのではないかな、そう思ったりします。

おわりに

そうとうニッチな内容でしたが、自分としては久々にちょう頭を使ったので、せっかくなので公開させていただこうかと思いました。どなたかのお役に立てれば幸いです。

個人的には、BIOSやfirmware、UEFIの部分で、仕様レベルでこういった改善が積み上げられていってるのに、地味に感動したりしました。

次回は、 MySQL に関する話をしたいと思います。

References