MCE (Machine Check Exception)
x86 Machine Check Architecture(MCA)의 뱅크/레지스터(Register) 구조, MCE 심각도 분류(SRAR/SRAO/UCNA), 커널 처리 경로(Monarch 랑데부), CMCI 스톰, AMD SMCA, IIO(Integrated I/O) PCIe 에러 아키텍처(Completion Timeout, Poisoned TLP, Surprise Link Down, DMA/VT-d 에러), Uncore MCA 에러 종합(M2M/CHA/UPI/PCU/TOR/MDF), KVM 가상화 MCE 전파(VMEXIT, 가상 MCE 뱅크, HWPoison→게스트), APEI/GHES 펌웨어(Firmware) 연동, EDAC DIMM 디코드, memory_failure()/HWPoison 복구, GPU/가속기 연쇄 에러 분석(Xid↔IIO MCE 상관분석, AI/ML 클러스터 영향), ARM/RISC-V/POWER EEH 비교, DDR5 On-Die ECC, Row Hammer, MCE 디버깅 기법(perf/ftrace/eBPF), 로그 디코딩 실전과 운영 플레이북까지 다룹니다.
핵심 요약
- Machine Check Exception — x86 벡터 18번 예외로, CPU/메모리/버스(Bus)의 하드웨어 오류를 보고합니다. CR4.MCE=1일 때만 활성화됩니다.
- MCA 뱅크 — CPU 내부의 에러 감지 유닛(L1/L2/L3 캐시(Cache), 메모리 컨트롤러, 인터커넥트 등)이 각각 독립 레지스터 세트를 가집니다.
- 심각도 3단계 — SRAR(즉시 복구 필요), SRAO(백그라운드 처리), UCNA(정보 전달만): 커널은 STATUS 비트로 자동 판별합니다.
- Monarch 메커니즘 — MCE 발생 시 모든 CPU를 동기화하고, 한 CPU(Monarch)가 최종 의사결정(패닉/복구/무시)을 내립니다.
- memory_failure() — HWPoison으로 물리 페이지(Page)를 오프라인 처리하고, 해당 메모리를 사용하던 프로세스에 SIGBUS를 전달합니다.
단계별 이해
- 1단계 — MCA 아키텍처 이해: MCE가 무엇이고, x86 예외 계층에서 어디에 위치하는지 파악합니다. (MCE 아키텍처 개요)
- 2단계 — 레지스터 구조: MCA 뱅크, MCi_STATUS 비트 필드, 에러 코드 체계를 이해합니다. (MCA 뱅크, STATUS 비트 필드, 에러 코드)
- 3단계 — 커널 처리 경로: 하드웨어 예외가 커널 핸들러(Handler)에 도달하고, Monarch가 선출되어 의사결정하는 과정을 추적합니다. (진입 경로, 처리 흐름, Monarch)
- 4단계 — 복구 메커니즘: memory_failure(), HWPoison, CMCI를 통한 오류 복구와 교정 인터럽트를 학습합니다. (HWPoison, CMCI)
- 5단계 — IIO와 Uncore: IIO(Integrated I/O)의 PCIe 에러 처리, Uncore 영역(M2M/CHA/UPI/TOR/MDF)의 에러 유형과 연쇄 패턴을 이해합니다. (IIO 아키텍처, Uncore 에러 종합)
- 6단계 — 가상화와 가속기: KVM 환경에서의 MCE 전파, GPU/가속기 연쇄 에러를 이해합니다. (KVM 가상화, GPU/가속기 에러)
- 7단계 — 플랫폼 통합: APEI/GHES, EDAC, AMD SMCA 등 다양한 플랫폼별 통합 경로를 이해합니다. (APEI/GHES, EDAC)
- 8단계 — 운영 실전: 로그 디코딩, 패턴 인식, 디버깅 기법(perf/eBPF), 테스트 주입, 플레이북을 통해 현장 대응 역량을 갖춥니다. (로그 디코딩, 디버깅 기법, 에러 패턴, 플레이북)
MCE 아키텍처 개요
Machine Check Exception(MCE)은 x86 아키텍처에서 벡터 18(#MC)로 정의된 abort 클래스 예외입니다. CPU 내부 또는 외부에서 발생하는 하드웨어 오류 — ECC 메모리 오류, 캐시 패리티 오류, 버스 타임아웃, TLB 오류 등 — 를 소프트웨어에 알려주는 유일한 표준 메커니즘입니다.
예외 특성
| 속성 | 값 | 의미 |
|---|---|---|
| 벡터 번호 | 18 (#MC) | IDT 엔트리 18 |
| 클래스 | Abort | 재시작(Reboot) 불가능할 수 있음 |
| 에러 코드 | 없음 | 정보는 MSR(MCA 뱅크)에 저장 |
| IST | IST 3 (커널 6.x 기준) | 전용 스택에서 실행 |
| 활성화 조건 | CR4.MCE = 1 | 부팅 시 커널이 설정 |
| 마스킹 | 불가 | cli/sti로 차단 불가 |
MCE의 위치: x86 예외 계층
MCE는 동기(synchronous)와 비동기(asynchronous) 모두 가능합니다. 명령어 실행 중 발견된 메모리 오류(SRAR)는 동기적이고, 백그라운드 스크러빙이 발견한 오류(SRAO)는 비동기적입니다. 커널은 동기 MCE를 더 긴급하게 처리합니다.
/* arch/x86/kernel/cpu/mce/core.c — MCE 활성화 */
void mcheck_cpu_init(struct cpuinfo_x86 *c)
{
/* CR4.MCE 비트 설정 — 이것이 없으면 MCE 시 triple fault → 리셋 */
cr4_set_bits(X86_CR4_MCE);
/* 각 MCA 뱅크의 CTL 레지스터를 0xFFFFFFFFFFFFFFFF로 설정 (모든 에러 활성화) */
for (i = 0; i < num_banks; i++)
wrmsrl(MSR_IA32_MCx_CTL(i), 0xFFFFFFFFFFFFFFFFULL);
}
MCA 뱅크와 레지스터
MCA(Machine Check Architecture)는 CPU 내부의 에러 감지 유닛을 뱅크(bank) 단위로 구성합니다. 각 뱅크는 특정 하드웨어 컴포넌트(L1 캐시, L2 캐시, 메모리 컨트롤러, QPI/UPI 등)에 매핑(Mapping)되며, 독립적인 레지스터 세트를 가집니다.
글로벌 레지스터
| MSR | 주소 | 역할 |
|---|---|---|
| MCG_CAP | 0x179 | 뱅크 수(Count), 확장 기능 플래그(MCG_EXT_P, MCG_SER_P, MCG_ELOG_P, MCG_LMCE_P) |
| MCG_STATUS | 0x17A | RIPV(Restart IP Valid), EIPV(Error IP Valid), MCIP(MC In Progress), LMCE_S |
| MCG_CTL | 0x17B | MCG_CTL_P=1일 때만 존재. 모든 뱅크의 글로벌 활성화/비활성화 |
| MCG_EXT_CTL | 0x4D0 | LMCE_EN 비트 — Local MCE 활성화 |
뱅크별 레지스터 (i = 0..N-1)
| MSR | 주소 공식 | 역할 |
|---|---|---|
| MCi_CTL | 0x400 + 4*i | 에러 유형별 활성화/비활성화 비트마스크 |
| MCi_STATUS | 0x401 + 4*i | 에러 정보 — 가장 핵심적인 레지스터 |
| MCi_ADDR | 0x402 + 4*i | 에러 발생 주소 (ADDRV=1일 때 유효) |
| MCi_MISC | 0x403 + 4*i | 추가 정보 — 주소 모드, LSB 등 (MISCV=1일 때 유효) |
| MCi_CTL2 | 0x280 + i | CMCI 임계값 설정, CMCI_EN 비트 |
/* arch/x86/kernel/cpu/mce/core.c — 뱅크 스캔 */
static void __mcheck_cpu_mce_banks_init(void)
{
int i;
u64 cap;
rdmsrl(MSR_IA32_MCG_CAP, cap);
num_banks = cap & MCG_BANKCNT_MASK; /* bits [7:0] */
for (i = 0; i < num_banks; i++) {
struct mce_bank *b = &mce_banks[i];
b->ctl = ~(u64)0; /* 모든 에러 유형 활성화 */
wrmsrl(MSR_IA32_MCx_CTL(i), b->ctl);
}
}
cat /sys/devices/system/machinecheck/machinecheck0/bank*로
각 뱅크의 CTL 값을 확인하고, mcelog --client 또는 rasdaemon으로
뱅크별 에러 발생 이력을 추적할 수 있습니다.
IA32_MCi_STATUS 비트 필드 상세
MCi_STATUS는 MCA의 핵심 레지스터로, 64비트 전체가 에러 정보를 담고 있습니다. 커널의 MCE 핸들러는 이 레지스터의 비트 조합으로 에러의 종류, 심각도, 복구 가능성을 판단합니다.
| 비트 | 이름 | 의미 | 1일 때 동작 |
|---|---|---|---|
| 63 | VAL | STATUS 유효성 | 이 레지스터에 유효한 에러 정보가 있음 |
| 62 | OVER | 오버플로 | 이전 에러를 읽기 전에 새 에러 발생 — 정보 손실 가능 |
| 61 | UC | Uncorrected | 하드웨어가 자동 교정하지 못한 에러 |
| 60 | EN | Error Enabled | MCi_CTL에서 이 에러 유형이 활성화된 상태 |
| 59 | MISCV | MISC Valid | MCi_MISC 레지스터에 추가 정보 있음 |
| 58 | ADDRV | Address Valid | MCi_ADDR에 에러 주소가 유효 |
| 57 | PCC | Processor Context Corrupt | CPU 내부 상태 손상 — 안전한 실행 계속 불가 |
| 56 | S | Signaling (SER_P) | 소프트웨어 복구 가능성 표시 (MCG_SER_P 필요) |
| 55 | AR | Action Required | 즉시 조치 필요 (S=1이어야 의미 있음) |
| 15:0 | MCA Error Code | 표준 에러 코드 | 에러 유형 분류 (Simple/Compound) |
MCA 에러 코드 분류
MCi_STATUS의 하위 16비트(bits 15:0)는 MCA 에러 코드로, Intel SDM에서 정의한 표준 포맷을 따릅니다. 크게 Simple Error Code와 Compound Error Code로 나뉩니다.
Simple Error Code (단순 에러 코드)
| 코드 | 이름 | 설명 |
|---|---|---|
| 0x0000 | No Error | 에러 없음 |
| 0x0001 | Unclassified | 분류 불가 에러 |
| 0x0002 | Microcode ROM Parity | 마이크로코드 ROM 패리티 에러 |
| 0x0003 | External | 외부 소스 에러 |
| 0x0004 | FRC | FRC(Functional Redundancy Check) 에러 |
| 0x0005 | Internal Parity | 내부 패리티 에러 |
| 0x0400 | Internal Timer | 내부 타이머(Timer) 에러 |
Compound Error Code (복합 에러 코드)
복합 에러 코드는 000F 0FTT LLLL 형식의 비트 패턴으로 인코딩됩니다.
| 필드 | 비트 | 값 | 의미 |
|---|---|---|---|
| LLLL (Level) | 3:0 | 0000 | Level 0 |
| 0001 | Level 1 | ||
| 0010 | Level 2 | ||
| 0011 | Generic / Level 3 | ||
| 0100~1111 | 예약 | ||
| TT (Transaction) | 5:4 | 00 | Instruction |
| 01 | Data | ||
| 10 | Generic | ||
| 11 | 예약 | ||
| RRRR (Request) | 11:8 | 0000 | Generic Error (ERR) |
| 0001 | Generic Read (RD) | ||
| 0010 | Generic Write (WR) | ||
| 0011 | Data Read (DRD) | ||
| 0100 | Data Write (DWR) | ||
| CCCC (Channel) | Varies | memory hierarchy | 캐시/메모리 계층 에러 |
| bus/interconnect | 버스/인터커넥트 에러 | ||
| timeout | 타임아웃 에러 |
0x0011— L1 캐시 데이터 읽기 에러0x0019— L1 캐시 명령어 프리페치 에러0x00C0— L2 캐시 에러0x0150— 메모리 컨트롤러 에러 (DIMM ECC)0x0800— 버스/인터커넥트 에러
/* arch/x86/kernel/cpu/mce/severity.c — 에러 코드 디코딩 */
#define MCESEV(s, m, c...) { .sev = MCE_##s##_SEVERITY, .msg = m, ##c }
static struct severity {
u64 mask;
u64 result;
unsigned char sev;
unsigned char mcgmask;
unsigned char mcgres;
char *msg;
} severities[] = {
MCESEV(NO, "Invalid",
SER, BITCLR(MCI_STATUS_VAL)),
MCESEV(NO, "Not enabled",
SER, BITCLR(MCI_STATUS_EN)),
MCESEV(PANIC, "Processor context corrupt",
BITSET(MCI_STATUS_PCC)),
/* ... */
};
메모리 관련 에러 코드 상세
메모리 컨트롤러에서 발생하는 에러는 가장 흔한 MCE 원인입니다. 다음은 Intel/AMD 메모리 컨트롤러 뱅크에서 자주 보이는 에러 코드 패턴입니다.
| STATUS 하위 16비트 | 에러 분류 | 일반적 원인 |
|---|---|---|
0x0001 | Unclassified | 분류 불가 내부 에러 |
0x0010 | Generic TLB Error | TLB 패리티/ECC 에러 |
0x0011 | L1 Data TLB Read | L1 DTLB 읽기 에러 |
0x0012 | L2 Data TLB Read | L2 DTLB 읽기 에러 |
0x0100 | Generic Cache L0 | L0 마이크로-op 캐시 에러 (AMD) |
0x0110 | L1 Cache Generic | L1 캐시 일반 에러 |
0x0111 | L1 Cache Data Read | L1D 읽기 에러 |
0x0120 | L2 Cache Generic | L2 캐시 일반 에러 |
0x0150 | Memory Controller | DRAM ECC 에러 (가장 흔함) |
0x0151 | Memory Read | DRAM 읽기 ECC 에러 |
0x0152 | Memory Write | DRAM 쓰기 에러 |
0x0800 | Bus/Interconnect | QPI/UPI 링크 에러 |
0x0C0F | L3 Cache Generic | LLC 에러 |
mcelog --ascii나 edac-util이 자동으로 에러 코드를 해석해줍니다.
수동 해석이 필요하면 Intel SDM Vol. 3B, Chapter 16 "Machine-Check Architecture"를 참조하세요.
MCE 심각도 분류
커널 함수 mce_severity()는 MCi_STATUS 비트를 분석하여 에러의 심각도를 결정합니다.
Intel의 Software Developer's Manual에서 정의한 4가지 등급과 커널의 내부 분류가 있습니다.
Intel MCA Recovery 분류
| 분류 | UC | S | AR | PCC | 의미 | 커널 동작 |
|---|---|---|---|---|---|---|
| SRAR | 1 | 1 | 1 | 0 | Software Recoverable Action Required — 즉시 복구 필요 | memory_failure() 호출, 프로세스에 SIGBUS |
| SRAO | 1 | 1 | 0 | 0 | Software Recoverable Action Optional — 백그라운드 처리 | memory_failure() 오프라인, 워크큐에서 처리 |
| UCNA | 1 | 0 | 0 | 0 | Uncorrected No Action — 정보만 전달 | 로그 기록, CMCI로 전달 |
| CE | 0 | - | - | - | Corrected Error — 하드웨어가 자동 교정 | 로그 기록, CMCI/폴링(Polling)으로 수집 |
| PCC=1 | 1 | - | - | 1 | Processor Context Corrupt | 무조건 패닉 (tolerant=3 제외) |
/* arch/x86/kernel/cpu/mce/severity.c */
static int mce_severity_intel(struct mce *m, struct pt_regs *regs,
char **msg, bool is_excp)
{
/* severities[] 테이블을 순회하며 mask/result 매칭 */
for (struct severity *s = severities;; s++) {
if ((m->status & s->mask) != s->result)
continue;
if ((m->mcgstatus & s->mcgmask) != s->mcgres)
continue;
*msg = s->msg;
return s->sev;
}
}
커널 MCE 진입 경로
MCE가 발생하면 CPU는 IDT의 벡터 18 핸들러로 점프합니다. 리눅스 커널 6.x에서의 진입 경로는 어셈블리(Assembly) 엔트리 포인트에서 시작하여 C 핸들러까지 이어집니다.
/* arch/x86/kernel/cpu/mce/core.c */
DEFINE_IDTENTRY_MCE(exc_machine_check)
{
/* MCE 핸들러는 NMI-like 컨텍스트에서 실행 */
if (user_mode(regs))
nmi_enter();
do_machine_check(regs);
if (user_mode(regs))
nmi_exit();
}
/* IST 3 설정: arch/x86/kernel/idt.c */
static const __initconst struct idt_data def_idts[] = {
/* ... */
ISTG(X86_TRAP_MC, asm_exc_machine_check, IST_INDEX_MCE), /* IST 3 */
};
printk()도 안전하지 않을 수 있습니다.
CPU 상태가 손상되었을 수 있으므로, 최소한의 작업(MSR 읽기, 구조체(Struct) 기록)만 수행하고,
상세 처리는 뒤로 미룹니다.
do_machine_check() 처리 흐름
do_machine_check()는 MCE의 핵심 처리 함수입니다.
멀티 CPU 환경에서 여러 코어가 동시에 MCE를 받을 수 있으므로,
Monarch 랑데부 프로토콜로 동기화한 뒤 한 CPU가 최종 결정을 내립니다.
/* arch/x86/kernel/cpu/mce/core.c — 핵심 흐름 */
noinstr void do_machine_check(struct pt_regs *regs)
{
int worst = MCE_NO_SEVERITY;
int order, no_way_out;
/* Phase 1: Monarch 선출 및 랑데부 */
order = mce_start(&no_way_out);
/* Phase 2: 자신의 MCA 뱅크 스캔 */
for (i = 0; i < num_banks; i++) {
rdmsrl(MSR_IA32_MCx_STATUS(i), m.status);
if (!(m.status & MCI_STATUS_VAL))
continue;
severity = mce_severity(&m, regs, &msg, true);
if (severity > worst)
worst = severity;
mce_log(&m); /* 링 버퍼에 기록 */
}
/* Phase 3: Monarch가 최종 결정 */
if (order == 1) /* Monarch */
mce_reign(); /* worst severity 수집 → panic/recover */
/* Phase 4: 랑데부 완료 */
mce_end(order);
/* Phase 5: 에러 클리어 */
for (i = 0; i < num_banks; i++)
wrmsrl(MSR_IA32_MCx_STATUS(i), 0);
}
Monarch 메커니즘
MCE는 일반적으로 모든 CPU에 동시에 브로드캐스트됩니다(LMCE 제외). 여러 CPU가 동시에 핸들러를 실행하면 의사결정이 분산되어 일관성을 잃을 수 있으므로, Monarch(군주) 프로토콜로 하나의 CPU가 최종 결정권을 갖습니다.
Monarch 선출 규칙
mce_start()에서atomic_inc_return(&mce_callin)을 호출- 리턴 값이 1인 CPU가 Monarch (가장 먼저 도착한 CPU)
- 나머지 CPU(Subject)는 Monarch의 결정을 대기
- Monarch는 모든 Subject가
mce_callin에 도달하거나 타임아웃까지 대기
tolerant 레벨
/sys/devices/system/machinecheck/machinecheck0/tolerant 설정으로
MCE 대응 수준을 조절합니다.
| 레벨 | 동작 | 용도 |
|---|---|---|
| 0 | 모든 MCE에서 즉시 패닉 | 안전 최우선 환경 (의료/항공) |
| 1 (기본값) | PCC가 아니면 복구 시도, PCC → 패닉 | 일반 서버/데스크톱 |
| 2 | RIPV가 없어도 복구 시도 | 고가용성 서버 |
| 3 | MCE를 최대한 무시 (디버그용) | 테스트/개발 전용 |
mce_panic()의 결정 시점
/* arch/x86/kernel/cpu/mce/core.c */
static void mce_reign(void)
{
int global_worst = MCE_NO_SEVERITY;
/* 모든 CPU의 worst severity 수집 */
for_each_possible_cpu(cpu) {
int severity = per_cpu(mce_worst_severity, cpu);
if (severity > global_worst)
global_worst = severity;
}
if (global_worst >= MCE_PANIC_SEVERITY) {
mce_panic("Fatal machine check", &m, msg);
/* 여기서 리턴하지 않음 */
}
/* PANIC이 아니면 복구 경로로 진행 */
}
Monarch 타임아웃
Monarch는 Subject CPU의 도착을 monarch_timeout(기본 ~500us, CPU마다 조정)까지 대기합니다.
타임아웃이 발생하면 Monarch는 도착한 CPU 정보만으로 결정을 내립니다.
이는 MCE 자체가 일부 CPU를 무응답 상태로 만들었을 수 있기 때문입니다.
MCG_STATUS.MCIP 비트
MCG_STATUS의 MCIP(Machine Check In Progress) 비트는 MCE 핸들러가 실행 중임을 나타냅니다.
이 비트가 1인 상태에서 새로운 MCE가 발생하면 CPU는 shutdown 상태에 들어갑니다(triple fault).
따라서 커널은 MCE 핸들러 완료 시 반드시 MCG_STATUS.MCIP = 0으로 클리어해야 합니다.
/* MCE 핸들러 완료 시 MCIP 클리어 */
static void mce_wrmsrl(u32 msr, u64 v)
{
if (msr == MSR_IA32_MCG_STATUS) {
v &= ~MCG_STATUS_MCIP; /* MCIP 클리어 → 다음 MCE 수신 가능 */
}
wrmsrl(msr, v);
}
Monarch와 가상화(Virtualization)
KVM 가상화 환경에서 MCE가 발생하면, 하이퍼바이저(Hypervisor)가 먼저 MCE를 가로챕니다(VMEXIT). 호스트 커널의 Monarch가 에러를 분석한 후, 게스트에 영향이 있으면 게스트에 가상 MCE를 주입합니다. LMCE 지원 시 게스트 vCPU에만 MCE를 전달하여 다른 vCPU의 VMEXIT를 방지합니다.
Monarch 상태 머신
Monarch 프로토콜은 명확한 상태 전이를 통해 진행됩니다. 모든 CPU가 MCE를 수신하면
mce_start()에서 동기화 배리어를 형성하고, Monarch CPU(가장 낮은 번호)가
최종 결정을 내립니다.
/* arch/x86/kernel/cpu/mce/core.c — mce_start() 내부 로직 */
static int mce_start(int *no_way_out)
{
int order;
int cpus = num_online_cpus();
u64 timeout = (u64)mca_cfg.monarch_timeout * NSEC_PER_USEC;
if (!timeout)
return -1; /* Monarch 비활성화 → 독립 처리 */
/* 1단계: CALLIN — 모든 CPU가 도착을 알림 */
atomic_add(*no_way_out, &global_nwo);
order = atomic_inc_return(&mce_callin); /* 도착 순서 기록 */
/* 2단계: CALLIN 배리어 — 모든 CPU 도착 대기 */
mce_barrier_enter(&mce_callin_barrier, timeout);
/* order == 1인 CPU가 Monarch (첫 번째 도착) */
if (order == 1) {
/* Monarch: 글로벌 상태 초기화 */
atomic_set(&global_nwo, 0);
atomic_set(&mce_executing, 0);
}
/* 타임아웃 처리: 일부 CPU 미도착 시 도착한 CPU만으로 진행 */
if (atomic_read(&mce_callin) != cpus) {
mce_timed_out(&timeout,
"Not all CPUs arrived at MCE rendezvous\n");
}
return order;
}
Monarch 결정 매트릭스
Monarch CPU는 mce_reign()에서 수집된 모든 MCE 정보를 종합하여
global_worst 심각도와 시스템 설정에 따라 최종 조치를 결정합니다.
| global_worst 심각도 | RIPV 비트 | tolerant 레벨 | 최종 조치 |
|---|---|---|---|
MCE_PANIC_SEVERITY |
무관 | 0, 1, 2 | PANIC — mce_panic() 호출, 시스템 즉시 정지 |
MCE_PANIC_SEVERITY |
무관 | 3 | LOG — tolerant=3이면 패닉 억제, 로그만 기록 |
MCE_AR_SEVERITY |
1 (유효) | 0, 1 | RECOVER — memory_failure()로 페이지 오프라인, 프로세스에 SIGBUS |
MCE_AR_SEVERITY |
0 (무효) | 0 | PANIC — 복귀 지점 없음, 복구 불가 |
MCE_AR_SEVERITY |
0 (무효) | 1, 2 | RECOVER — 최선 노력(best-effort) 복구 시도 |
MCE_UC_SEVERITY |
1 | 0, 1, 2 | RECOVER — 비동기 UCE, 페이지 오프라인 후 계속 |
MCE_UC_SEVERITY |
0 | 0 | PANIC — 복귀 불가 + 엄격 모드 |
MCE_DEFERRED_SEVERITY |
무관 | 무관 | LOG — 지연(Latency) 처리, 나중에 소프트웨어 접근 시 처리 |
MCE_KEEP_SEVERITY |
무관 | 무관 | LOG — 정보성 기록, 조치 불필요 |
MCE_NO_SEVERITY |
무관 | 무관 | 무시 — 오류 아님 (spurious MCE) |
/sys/devices/system/machinecheck/machinecheck0/tolerant 값에 따라
0 = 최대한 엄격(패닉 우선), 1 = 기본값(복구 가능하면 시도),
2 = 관대(패닉 회피 노력), 3 = 패닉 완전 억제(디버깅(Debugging) 전용)로 동작합니다.
MCE와 CPU 핫플러그(Hotplug)
CPU 핫플러그 도중 MCE가 발생하면 특수한 상황이 발생합니다. 오프라인 상태이거나 핫플러그 진행 중인 CPU는 Monarch 랑데부에서 제외되어야 합니다.
커널은 다음과 같은 메커니즘으로 이 문제를 처리합니다:
- 오프라인 CPU 제외:
mce_start()에서num_online_cpus()를 사용하여 현재 온라인 CPU 수만 카운트합니다. 오프라인 CPU는 배리어 도착 대상에 포함되지 않습니다. - 핫플러그 진행 중: CPU가
CPU_DEAD상태로 전환되는 과정에서 MCE가 발생하면, 해당 CPU의 MCE 핸들러는 이미 해제(mce_cpu_dead())되어 #MC 인터럽트를 받지 못합니다. 따라서 자연스럽게 랑데부에서 제외됩니다. - CPU 온라인 시:
mce_cpu_online()에서 MCE 뱅크를 재초기화하고, 타이머를 재설정하며, CMCI를 재활성화합니다. 이전에 누적된 CE는 새로 폴링됩니다. - 타임아웃 보호: 핫플러그 경합(Contention)으로 CPU 수가 일시적으로 불일치하는 경우,
monarch_timeout이 데드락을 방지합니다. 타임아웃 만료 시 도착한 CPU만으로 진행합니다.
/* arch/x86/kernel/cpu/mce/core.c — CPU 핫플러그 콜백 */
static int mce_cpu_online(unsigned int cpu)
{
struct timer_list *t = this_cpu_ptr(&mce_timer);
mce_device_create(cpu); /* sysfs 노드 생성 */
mce_threshold_create_device(cpu);
mce_reenable_cpu(); /* MCE 뱅크 재활성화 */
mce_start_timer(t); /* 폴링 타이머 시작 */
return 0;
}
static int mce_cpu_dead(unsigned int cpu)
{
mce_threshold_remove_device(cpu);
mce_device_remove(cpu); /* sysfs 노드 제거 */
return 0;
/* 이후 이 CPU는 #MC를 수신하지 않음 → Monarch 랑데부 제외 */
}
MCE Notifier Chain과 복구
Notifier Chain 등록 우선순위(Priority)
| 우선순위 | 등록자 | 역할 |
|---|---|---|
| MCE_PRIO_FIRST | mce_default_notifier | dmesg 출력 (기본 디코더) |
| MCE_PRIO_MCELOG | dev_mcelog | /dev/mcelog 인터페이스 |
| MCE_PRIO_EDAC | EDAC 드라이버 | DIMM 위치 디코딩 |
| MCE_PRIO_NFIT | NVDIMM/NFIT | NVDIMM ARS |
| MCE_PRIO_CEC | Corrected Error Collector | CE 누적 추적 |
kill_me_now vs kill_me_later
SRAR 에러에서 커널은 두 가지 킬 전략을 사용합니다.
- kill_me_now — MCE 핸들러 내에서 즉시
force_sig(SIGBUS)를 전송. 현재 태스크(Task)가 에러 데이터를 소비한 경우에 사용. - kill_me_later — 핸들러 리턴 후 태스크가 다시 스케줄될 때
SIGBUS를 전송. 현재 태스크와 무관한 에러일 때 사용.
/* memory_failure_queue() — 비동기 처리 큐 */
void memory_failure_queue(unsigned long pfn, int flags)
{
struct memory_failure_entry entry = {
.pfn = pfn,
.flags = flags,
};
kfifo_put(&mf_cpu->fifo, entry);
schedule_work_on(smp_processor_id(), &mf_cpu->work);
}
MCE 링 버퍼(Ring Buffer) 구조
커널은 MCE 이벤트를 순환(circular) 링 버퍼에 기록합니다. 이 버퍼(Buffer)는 프로듀서-컨슈머 모델로 동작하며, NMI 컨텍스트에서도 안전하게 쓸 수 있도록 설계되어 있습니다.
MCE 이벤트 필터링
perf를 사용하면 MCE 이벤트를 실시간(Real-time)으로 캡처하고 디코딩할 수 있습니다.
mce:mce_record 트레이스포인트는 모든 MCE 로깅 경로에서 호출됩니다.
# MCE 트레이스 이벤트 캡처
$ perf record -e mce:mce_record -a --overwrite # 시스템 전체 기록
# 기록된 MCE 이벤트 디코딩
$ perf script
mcelog 3421 [002] 1842.553291: mce:mce_record: \
CPU: 2, MCGc: 0x0, MCGi: 0xf10, \
Bank: 9, MCi: 0xbd80000000100134, \
Addr: 0x3a7f8c000, MISC: 0x8c, severity: CE
# 특정 뱅크만 필터링 (Bank 9 = Memory Controller)
$ perf record -e mce:mce_record --filter 'bank == 9' -a
# ftrace로 직접 캡처 (perf 없이)
$ echo 1 > /sys/kernel/debug/tracing/events/mce/mce_record/enable
$ cat /sys/kernel/debug/tracing/trace_pipe
rasdaemon은 이러한 트레이스 이벤트를 SQLite DB에 저장하여
장기간 CE 추적과 페이지별 오류 통계를 제공합니다.
레거시 mcelog 데몬은 /dev/mcelog에서 직접 읽는 방식이며,
최신 커널에서는 rasdaemon 사용이 권장됩니다.
# rasdaemon으로 MCE 이력 조회
$ ras-mc-ctl --errors
# Label CE count UE count Last error
# DIMM0 47 0 2026-03-15 14:23:01 Bank 9 CE
# DIMM1 0 0 —
# DIMM2 3 1 2026-03-14 09:11:44 Bank 9 UCE
# 페이지별 CE 누적 카운트 (메모리 교체 판단 기준)
$ ras-mc-ctl --error-count
# mc0: csrow0: ch0: 47 CEs, ch1: 0 CEs
# SQLite DB 직접 쿼리
$ sqlite3 /var/lib/rasdaemon/ras-mc_event.db \
"SELECT timestamp, err_type, mc_location FROM mce_record \
WHERE mcgstatus LIKE '%MCIP%' ORDER BY id DESC LIMIT 10;"
memory_failure()와 HWPoison
memory_failure()는 물리 메모리(Physical Memory) 페이지의 하드웨어 오류를 처리하는 핵심 함수입니다.
커널은 해당 페이지를 HWPoison으로 마킹하고, 페이지 유형에 따라 적절한 복구 조치를 수행합니다.
/* mm/memory-failure.c — 핵심 함수 */
int memory_failure(unsigned long pfn, int flags)
{
struct page *p = pfn_to_page(pfn);
/* 1. HWPoison 마킹 */
SetPageHWPoison(p);
/* 2. 페이지 잠금 및 참조 획득 */
shake_page(p); /* LRU에서 제거 시도 */
/* 3. 페이지 유형에 따른 처리 */
if (PageHuge(p))
return memory_failure_hugetlb(pfn, p, flags);
if (!PageLRU(p))
return identify_page_state(pfn, p, flags); /* 커널 페이지 등 */
/* 4. 프로세스에 SIGBUS 전송 */
collect_procs(p, &tokill); /* 매핑한 프로세스 목록 */
kill_procs(&tokill, flags & MF_ACTION_REQUIRED, pfn, flags);
/* 5. 페이지 오프라인 */
page_action(ps, p, pfn);
return 0;
}
/proc/kpageflags의 bit 19(HWPOISON)로 특정 페이지의
HWPoison 상태를 확인할 수 있습니다. page-types -l -b hwpoison 도구도 유용합니다.
page_action[] 타입별 처리 상세
memory_failure()는 오류 페이지의 유형을 판별한 뒤 page_action[] 배열에서
해당 타입의 핸들러를 호출합니다. 커널 소스(mm/memory-failure.c)의
error_states[] 배열이 이 매핑을 정의하며, 각 페이지 상태에 따라 복구 가능 여부와
처리 방식이 크게 달라집니다.
| 타입 (enum) | 대상 페이지 | 핸들러 함수 | 동작 | 결과 |
|---|---|---|---|---|
me_kernel |
커널이 직접 사용 중인 페이지 (slab, page table 등) | me_kernel() |
복구 불가 — 즉시 panic() 또는 프로세스 kill |
MF_FAILED |
me_unknown |
유형 판별 불가 페이지 | me_unknown() |
보수적 처리: 오프라인 시도, 실패 시 무시 | MF_IGNORED / MF_FAILED |
me_pagecache_clean |
깨끗한 페이지 캐시 (디스크와 동기화됨) | me_pagecache_clean() |
truncate_error_page()로 페이지 무효화(Invalidation) — 다음 접근 시 디스크에서 재로드 |
MF_RECOVERED |
me_pagecache_dirty |
더티 페이지 캐시 (아직 디스크에 미기록) | me_pagecache_dirty() |
매핑 프로세스에 SIGBUS, 페이지 무효화 — 데이터 손실 가능 | MF_RECOVERED (일부 데이터 유실) |
me_swapcache_clean |
스왑(Swap) 캐시 (스왑 영역(Swap Area)과 동기화됨) | me_swapcache_clean() |
스왑 슬롯 해제, 페이지 삭제 — 스왑에서 재로드 가능 | MF_RECOVERED |
me_swapcache_dirty |
더티 스왑 캐시 (수정 후 미기록) | me_swapcache_dirty() |
스왑 슬롯 무효화, 매핑 프로세스에 SIGBUS — 복구 불가능한 데이터 | MF_DELAYED (접근 시 kill) |
me_huge_page |
Huge page (2 MB / 1 GB) | me_huge_page() |
split_huge_page() 호출 → 4 KB 서브페이지로 분리 후 해당 페이지만 재처리 |
재귀 호출 |
me_free |
buddy allocator의 프리 페이지 | me_free() |
dissolve_free_huge_page() + buddy에서 영구 제거 — 프로세스 영향 없음 |
MF_RECOVERED |
/* mm/memory-failure.c — error_states[] 배열 (커널 6.x 기준 간략화) */
static struct page_state error_states[] = {
{ 0, 0, "free buddy", me_free },
{ slab, slab, "kernel slab", me_kernel },
{ head, head, "huge page", me_huge_page },
{ sc|dirty, sc|dirty, "swapcache dirty", me_swapcache_dirty },
{ sc, sc, "swapcache clean", me_swapcache_clean },
{ mlock|dirty, dirty, "dirty mlocked", me_pagecache_dirty },
{ mlock, mlock, "clean mlocked", me_pagecache_clean },
{ lru|dirty, dirty, "dirty LRU", me_pagecache_dirty },
{ lru, lru, "clean LRU", me_pagecache_clean },
{ 0, 0, "unknown page", me_unknown },
};
error_states[] 배열은 위에서 아래로 순서대로 검사됩니다.
첫 번째 매칭되는 엔트리의 핸들러가 호출되므로, 커널 슬랩 페이지는 항상 LRU 페이지보다 먼저
검사되어 적절한 처리(panic 또는 slab 격리(Isolation))를 보장합니다.
ECC 유형과 MCE 관계
서버 메모리에서 사용되는 ECC(Error Correcting Code) 수준에 따라 MCE의 CE(Corrected Error)와 UC(Uncorrected Error) 발생 패턴이 달라집니다. ECC가 강력할수록 더 많은 비트 오류를 CE로 교정하여 UC 발생을 줄입니다.
edac-util -s 또는 /sys/devices/system/edac/mc/mc*/에서
메모리 컨트롤러의 ECC 모드를 확인할 수 있습니다. edac_mode 파일에
S4ECD4ED(Chipkill), S8ECD8ED, SECDED 등이 표시됩니다.
MCi_MISC 비트 필드 상세
MCi_MISC 레지스터는 오류 주소의 해석 방법과 복구 가능 주소의 정밀도를 알려줍니다.
특히 UCR(Uncorrected Recoverable) 에러에서 memory_failure()에 전달할 물리 주소(Physical Address)의
유효 범위를 결정하는 데 핵심적인 역할을 합니다.
| 비트 범위 | 필드 이름 | 설명 |
|---|---|---|
[63:57] |
예약 | 미사용 (0) |
[56] |
Overflow (OVF) | 추가 에러 정보 오버플로 여부 |
[55:44] |
모델 의존 | CPU 모델별 추가 정보 (ECC 신드롬, 칩 번호 등) |
[43:40] |
예약 | 미사용 (0) |
[39:32] |
모델 의존 | CPU별 추가 에러 정보 |
[8:6] |
Address Mode | MCi_ADDR의 주소 해석 방식:
|
[5:0] |
Recoverable Addr LSB | 주소의 유효 최하위 비트 위치. 예: 값이 12이면 4 KB(페이지) 단위 정밀도,
값이 6이면 64 B(캐시라인) 단위 정밀도 |
/* arch/x86/kernel/cpu/mce/severity.c — MISC 비트 해석 */
#define MCI_MISC_ADDR_LSB(m) ((m) & 0x3f) /* bits [5:0] */
#define MCI_MISC_ADDR_MODE(m) (((m) >> 6) & 7) /* bits [8:6] */
#define MCI_MISC_ADDR_PHYS 2 /* 물리 주소 모드 */
/* UCR 에러에서 PFN 추출 시 LSB를 확인 */
if (MCI_MISC_ADDR_MODE(m->misc) != MCI_MISC_ADDR_PHYS)
return; /* 물리 주소가 아니면 memory_failure 호출 불가 */
int lsb = MCI_MISC_ADDR_LSB(m->misc);
if (lsb > PAGE_SHIFT)
lsb = PAGE_SHIFT; /* 페이지 단위로 클램프 */
MCi_ADDR 주소 해석
MCi_ADDR 레지스터에 기록된 주소는 MCi_MISC의 Address Mode에 따라
해석 방법이 다릅니다. 대부분의 메모리 에러에서는 물리 주소 모드(010)가 사용되며,
커널은 이 주소에서 PFN(Page Frame Number)을 추출하여 memory_failure()에 전달합니다.
| 단계 | 연산 | 설명 |
|---|---|---|
| 1. Address Mode 확인 | MCi_MISC[8:6] == 010 |
물리 주소 모드인지 검증 — 다른 모드면 PFN 추출 불가 |
| 2. LSB 마스킹 | addr &= ~((1UL << lsb) - 1) |
MCi_MISC[5:0]이 가리키는 LSB 이하 비트를 0으로 클리어 |
| 3. PFN 추출 | pfn = addr >> PAGE_SHIFT |
물리 주소를 12비트 시프트하여 페이지 프레임(Page Frame) 번호 산출 |
| 4. memory_failure 호출 | memory_failure(pfn, flags) |
해당 PFN의 페이지를 HWPoison으로 마킹하고 복구 처리 |
| 5. DIMM 매핑 (EDAC) | edac_mc_handle_error() |
물리 주소 → DIMM 슬롯/채널/랭크/뱅크/로우 매핑 (address decoder) |
/* arch/x86/kernel/cpu/mce/core.c — MCi_ADDR에서 PFN 추출 */
static void mce_log_and_process(struct mce *m)
{
unsigned long pfn;
int lsb;
/* 물리 주소 모드가 아니면 복구 불가 */
if (MCI_MISC_ADDR_MODE(m->misc) != MCI_MISC_ADDR_PHYS)
return;
/* 유효 비트 하한 */
lsb = MCI_MISC_ADDR_LSB(m->misc);
if (lsb > PAGE_SHIFT)
lsb = PAGE_SHIFT;
/* PFN 추출: 하위 비트 마스크 후 페이지 시프트 */
pfn = (m->addr >> PAGE_SHIFT) &
(((1UL << (46 - PAGE_SHIFT)) - 1));
/* memory_failure 호출 (MF_ACTION_REQUIRED는 SRAR 에러에서 설정) */
memory_failure(pfn, m->mcgstatus & MCG_STATUS_RIPV ?
MF_ACTION_REQUIRED : 0);
}
/* EDAC에서 물리 주소 → DIMM 위치 디코딩 (drivers/edac/skx_common.c) */
static int skx_decode_addr(u64 addr, struct decoded_addr *res)
{
/* System Address → Socket → iMC → Channel → DIMM → Rank → Bank → Row/Col */
res->socket = skx_sad_decode(addr);
res->imc = skx_tad_decode(addr);
res->channel = skx_channel_decode(addr);
res->dimm = skx_dimm_decode(addr);
return 0;
}
/sys/devices/system/edac/mc/mc*/dimm*/dimm_label에서
DIMM 물리 슬롯 이름을 확인하고, edac-util -l로 전체 매핑을 조회합니다.
mcelog --dmi는 DMI/SMBIOS 테이블을 사용하여 주소를 DIMM 슬롯으로 변환합니다.
CMCI (Corrected MCE Interrupt)
CMCI(Corrected Machine Check Error Interrupt)는 교정된 에러(CE)를 효율적으로 수집하는 메커니즘입니다. 전통적인 MCE 폴링은 주기적으로 MSR을 읽어야 했지만, CMCI는 CE가 임계값에 도달하면 인터럽트를 발생시켜 즉시 알려줍니다.
CMCI vs 폴링 비교
| 특성 | CMCI | 폴링 |
|---|---|---|
| 지연 시간 | 즉시 (인터럽트) | check_interval 주기 (기본 5분) |
| CPU 오버헤드(Overhead) | 낮음 (이벤트 기반) | 주기적 MSR 읽기 |
| 스톰 대응 | 자동 폴링 전환 | 해당 없음 |
| 지원 CPU | Intel Xeon 5500+ / AMD Zen+ | 모든 MCA 지원 CPU |
/* arch/x86/kernel/cpu/mce/intel.c */
static void cmci_storm_begin(int bank)
{
__clear_bit(bank, this_cpu_ptr(mce_poll_banks));
cmci_toggle_interrupt_mode(false); /* CMCI_EN = 0 */
/* 폴링 타이머 활성화 */
mce_timer_kick(CMCI_STORM_INTERVAL);
}
static void cmci_storm_end(int bank)
{
cmci_toggle_interrupt_mode(true); /* CMCI_EN = 1 복원 */
mce_timer_kick(CMCI_POLL_INTERVAL);
}
CMCI 임계값 설정
MCi_CTL2 레지스터의 bits [14:0]이 CMCI 임계값을 결정합니다. CE가 이 값에 도달하면 CMCI 인터럽트가 발생합니다. 커널은 기본적으로 임계값을 1로 설정하여 첫 번째 CE부터 즉시 인터럽트를 받습니다.
/* arch/x86/kernel/cpu/mce/intel.c — CMCI 임계값 설정 */
static void cmci_set_threshold(int bank, int thresh)
{
u64 val;
rdmsrl(MSR_IA32_MCx_CTL2(bank), val);
val &= ~MCI_CTL2_CMCI_THRESHOLD_MASK;
val |= (u64)thresh & MCI_CTL2_CMCI_THRESHOLD_MASK;
val |= MCI_CTL2_CMCI_EN; /* bit 30: CMCI 활성화 */
wrmsrl(MSR_IA32_MCx_CTL2(bank), val);
}
Corrected Error Collector (CEC)
Intel CPU에서 커널은 CEC(Corrected Error Collector)를 사용하여
CE가 반복되는 물리 페이지를 추적합니다. 동일 페이지에서 CE가 임계값을 초과하면
soft_offline_page()를 호출하여 예방적으로 오프라인합니다.
/* arch/x86/kernel/cpu/mce/intel.c — CEC */
static int cec_add_elem(u64 pfn)
{
/* pfn을 decay table에 삽입 */
/* 동일 pfn의 카운트가 action_threshold 초과 시: */
if (count >= action_threshold) {
/* 페이지 예방 오프라인 */
soft_offline_page(pfn_to_page(pfn), MF_SOFT_OFFLINE);
return 1;
}
return 0;
}
/sys/kernel/debug/cec/decay_interval로 감쇠 주기를 조정할 수 있습니다.
AMD Scalable MCA (SMCA)
AMD는 Zen 아키텍처부터 Scalable MCA(SMCA)를 도입하여 기존 MCA의 한계를 극복했습니다. SMCA는 뱅크별로 하드웨어 유닛을 동적으로 식별할 수 있어, 마이크로아키텍처 변경에 유연하게 대응합니다.
SMCA vs 기존 MCA
| 특성 | 기존 MCA (Intel/레거시 AMD) | AMD SMCA |
|---|---|---|
| 뱅크 식별 | 모델별 고정 매핑 (매뉴얼 참조) | IPID 레지스터로 동적 식별 |
| 에러 코드 | 표준 MCA 코드 | 확장 코드 + SYND (Syndrome) |
| 추가 레지스터 | CTL/STATUS/ADDR/MISC | + IPID, SYND, DESTAT, DEADDR |
| 인스턴스 | 뱅크 번호 = 유닛 | HWID/MCATYPE으로 유닛+인스턴스 |
SMCA 추가 레지스터
| 레지스터 | MSR 오프셋 | 내용 |
|---|---|---|
| MCi_IPID | +5 | Hardware ID(HWID) + MCA Type → 유닛 식별 |
| MCi_SYND | +6 | Syndrome — ECC 신드롬, 에러 비트 위치 |
| MCi_DESTAT | +8 | Deferred Error Status |
| MCi_DEADDR | +9 | Deferred Error Address |
/* arch/x86/kernel/cpu/mce/amd.c — SMCA 뱅크 타입 결정 */
enum smca_bank_types smca_get_bank_type(unsigned int cpu, unsigned int bank)
{
struct smca_bank *b;
if (bank >= MAX_NR_BANKS)
return N_SMCA_BANK_TYPES;
b = &smca_banks[bank];
if (!b->hwid)
return N_SMCA_BANK_TYPES;
return b->hwid->bank_type; /* SMCA_LS, SMCA_IF, SMCA_L2, SMCA_DE, ... */
}
/* SMCA 하드웨어 유닛 목록 */
static const struct smca_hwid smca_hwid_mcatypes[] = {
{ SMCA_LS, HWID_MCATYPE(0xB0, 0x0) }, /* Load Store */
{ SMCA_LS_V2, HWID_MCATYPE(0xB0, 0x10) }, /* Load Store V2 */
{ SMCA_IF, HWID_MCATYPE(0xB1, 0x0) }, /* Instruction Fetch */
{ SMCA_L2_CACHE, HWID_MCATYPE(0xB2, 0x0) }, /* L2 Cache */
{ SMCA_DE, HWID_MCATYPE(0xB3, 0x0) }, /* Decode Unit */
{ SMCA_EX, HWID_MCATYPE(0xB6, 0x0) }, /* Execution Unit */
{ SMCA_FP, HWID_MCATYPE(0xB7, 0x0) }, /* Floating Point */
{ SMCA_UMC, HWID_MCATYPE(0x96, 0x0) }, /* Unified Memory Controller */
/* ... */
};
rasdaemon은 SMCA의 IPID를 자동으로 디코딩하여
"Load Store Unit" 같은 사람이 읽을 수 있는 이름을 출력합니다.
커널 로그에서는 Scalable MCA: ... IPID: ... 형태로 출력됩니다.
Intel CPU 뱅크 매핑 (Skylake-SP 이후)
Intel 전통 MCA에서는 뱅크 번호가 CPU 마이크로아키텍처마다 다르게 매핑됩니다. 아래는 Skylake-SP(Xeon Scalable) 이후 서버 CPU의 일반적인 뱅크 할당입니다. Ice Lake-SP, Sapphire Rapids에서는 일부 뱅크가 추가/변경될 수 있습니다.
| 뱅크 번호 | 유닛 | 설명 | 일반적인 에러 |
|---|---|---|---|
| Bank 0 | IFU (Instruction Fetch Unit) | 명령어 페치 파이프라인(Pipeline) | I-Cache 패리티, uop 캐시 에러 |
| Bank 1 | DCU (Data Cache Unit) | L1 데이터 캐시 | D-Cache ECC, 로드/스토어 에러 |
| Bank 2 | DTLB | 데이터 TLB | TLB 패리티 에러 |
| Bank 3 | MLC (Mid-Level Cache) | L2 캐시 | L2 ECC CE/UC |
| Bank 4 | PCU (Power Control Unit) | 전력 관리 유닛 | 전압/클럭 관련 내부 에러 |
| Bank 5-6 | Intel UPI | 소켓(Socket) 간 인터커넥트 | 링크 CRC, 프로토콜 에러 |
| Bank 7-8 | IIO (Integrated I/O) | PCIe 루트 포트, CBDMA | PCIe CE/UC, DMA 에러 |
| Bank 9-12 | M2M (Mesh to Memory) | 메시 → 메모리 인터페이스 | 주소 디코드 에러, patrol scrub CE |
| Bank 13-20 | iMC (integrated Memory Controller) | 메모리 채널 컨트롤러 | DRAM ECC CE/UC — 가장 빈번 |
| Bank 21-24 | CHA (Caching Home Agent) | LLC 슬라이스 + 디렉토리 | LLC ECC, 스누프 프로토콜 에러 |
mcelog의 --dmi 옵션이나
커널 로그의 CPU N Bank M에서 확인할 수 있습니다.
AMD Zen SMCA 뱅크 타입 전체 목록
SMCA에서는 MCi_IPID 레지스터의 HWID(bits [43:32])와
MCATYPE(bits [63:48])으로 하드웨어 유닛을 식별합니다.
아래는 커널 소스(arch/x86/kernel/cpu/mce/amd.c)에 정의된 전체 SMCA 뱅크 타입입니다.
| enum 이름 | HWID | MCATYPE | 유닛 설명 | 주요 에러 |
|---|---|---|---|---|
SMCA_LS | 0xB0 | 0x0 | Load Store Unit | 로드/스토어 데이터 에러 |
SMCA_LS_V2 | 0xB0 | 0x10 | Load Store Unit V2 (Zen 3+) | 확장 LS 에러 코드 |
SMCA_IF | 0xB1 | 0x0 | Instruction Fetch | I-Cache, ITB(ITLB) 에러 |
SMCA_L2_CACHE | 0xB2 | 0x0 | L2 Cache | L2 ECC CE/UC |
SMCA_DE | 0xB3 | 0x0 | Decode Unit | uop 캐시, 디코더 에러 |
SMCA_RESERVED | 0xB4 | 0x0 | 예약 | 미사용 |
SMCA_EX | 0xB6 | 0x0 | Execution Unit | 정수/분기 실행 에러 |
SMCA_FP | 0xB7 | 0x0 | Floating Point | FPU, SIMD 연산 에러 |
SMCA_L3_CACHE | 0xB8 | 0x0 | L3 Cache | L3 ECC CE/UC, 태그 에러 |
SMCA_CS | 0x2B | 0x0 | Coherent Slave (xGMI/IF) | 인터커넥트 프로토콜 에러 |
SMCA_CS_V2 | 0x2B | 0x2 | Coherent Slave V2 (Zen 3+) | 확장 IF 에러 |
SMCA_PIE | 0x2E | 0x0 | Power, Interrupts, etc. | APIC, 전력 관리 에러 |
SMCA_UMC | 0x96 | 0x0 | Unified Memory Controller | DRAM ECC CE/UC — 가장 빈번 |
SMCA_UMC_V2 | 0x96 | 0x10 | UMC V2 (Zen 4+ DDR5) | DDR5 ECC, On-die ECC 이벤트 |
SMCA_PB | 0x05 | 0x0 | Parameter Block | 퓨즈/파라미터 에러 |
SMCA_PSP | 0xFF | 0x0 | Platform Security Processor | PSP 펌웨어 에러 |
SMCA_PSP_V2 | 0xFF | 0x1 | PSP V2 | 확장 PSP 에러 |
SMCA_SMU | 0x01 | 0x0 | System Management Unit | SMU 펌웨어 에러 |
SMCA_SMU_V2 | 0x01 | 0x10 | SMU V2 | 확장 SMU 에러 |
SMCA_MP5 | 0x01 | 0x2 | Microprocessor 5 Unit | MP5 내부 에러 |
SMCA_MPDMA | 0x01 | 0x3 | MPDMA | DMA 엔진 에러 |
SMCA_NBIO | 0x2E | 0x1 | Northbridge I/O Unit | PCIe, IOMMU 에러 |
SMCA_PCIE | 0x2E | 0x2 | PCIe Root Port | PCIe AER CE/UC |
SMCA_PCIE_V2 | 0x2E | 0x3 | PCIe V2 (Zen 4+) | 확장 PCIe 에러 |
SMCA_XGMI_PCS | 0x50 | 0x0 | xGMI PCS (Physical Coding) | 소켓 간 링크 물리 계층 에러 |
SMCA_NBIF | 0x2E | 0x4 | NB-IF (Northbridge Interface) | 데이터 패브릭 인터페이스 에러 |
SMCA_SHUB | 0x80 | 0x0 | System Hub | 사우스브릿지 연결 에러 |
SMCA_SATA | 0xA8 | 0x0 | SATA Controller | SATA PHY/프로토콜 에러 |
SMCA_USB | 0xAA | 0x0 | USB Controller | USB 내부 에러 |
SMCA_GMI_PCS | 0x50 | 0x1 | GMI PCS (Global Memory Interconnect) | 다이 간 링크 에러 |
SMCA_XGMI_PHY | 0x59 | 0x0 | xGMI PHY | xGMI 물리 계층 트레이닝 에러 |
SMCA_WAFL_PHY | 0x59 | 0x1 | WAFL PHY | WAFL 링크 에러 |
SMCA_GMI_PHY | 0x59 | 0x2 | GMI PHY | GMI 물리 계층 에러 |
MCi_IPID의 bits [31:0]인 InstanceId로
같은 타입의 서로 다른 인스턴스를 구별합니다. rasdaemon 로그에서
instance: N으로 확인됩니다.
Deferred Error (SMCA 고유)
AMD SMCA는 Deferred Error라는 고유한 에러 유형을 지원합니다. Deferred Error는 즉각적인 데이터 손상을 일으키지는 않지만, 나중에 소비되면 문제가 될 수 있는 "잠재적 오류"입니다. 주로 배경 패트롤 스크러빙(background patrol scrubbing)에서 발견된 에러를 보고하는 데 사용됩니다.
| 레지스터 | MSR | 설명 |
|---|---|---|
MCi_DESTAT |
MCi_BASE + 8 | Deferred Error Status — MCi_STATUS와 동일한 형식이지만,
지연된(deferred) 에러 전용. MCi_STATUS에 새 에러가 기록되어도
DESTAT의 내용은 보존됨 |
MCi_DEADDR |
MCi_BASE + 9 | Deferred Error Address — 지연 에러가 발생한 물리 주소.
memory_failure()에 전달하여 해당 페이지를 오프라인할 수 있음 |
일반적인 UC(Uncorrected) 에러와 Deferred Error의 핵심적인 차이점은 다음과 같습니다:
| 특성 | UC Error (일반) | Deferred Error |
|---|---|---|
| 발생 시점 | 데이터가 실제 소비될 때 (load, execution) | 패트롤 스크러빙 등 백그라운드 검사에서 발견 |
| 즉각 영향 | 프로세스 kill 또는 panic (SRAR/SRAO) | 즉각적 영향 없음 — 아직 소비되지 않은 데이터 |
| STATUS 기록 | MCi_STATUS.UC=1, MCi_STATUS.Deferred=0 |
MCi_STATUS.Deferred=1 (bit 32) |
| 별도 레지스터 | 없음 (MCi_STATUS/ADDR 사용) | MCi_DESTAT / MCi_DEADDR 전용 레지스터 |
| 커널 처리 | memory_failure(pfn, MF_ACTION_REQUIRED) |
memory_failure(pfn, 0) — 선제적 오프라인 |
| 복구 전략 | Late Kill / Early Kill 정책 적용 | 페이지 마이그레이션 후 오프라인 (데이터 보존 가능) |
/* arch/x86/kernel/cpu/mce/amd.c — Deferred Error 처리 */
static void amd_deferred_error_interrupt(void)
{
unsigned int bank;
for (bank = 0; bank < this_cpu_read(mce_num_banks); bank++) {
u64 status, addr;
rdmsrl(MSR_AMD64_SMCA_MCx_DESTAT(bank), status);
if (!(status & MCI_STATUS_VAL))
continue;
if (!(status & MCI_STATUS_DEFERRED)) /* bit 32: Deferred 플래그 */
continue;
rdmsrl(MSR_AMD64_SMCA_MCx_DEADDR(bank), addr);
/* 지연 에러 주소로 선제적 memory_failure 호출 */
if (addr) {
unsigned long pfn = addr >> PAGE_SHIFT;
memory_failure(pfn, 0); /* MF_ACTION_REQUIRED 미설정 */
}
/* DESTAT 클리어 */
wrmsrl(MSR_AMD64_SMCA_MCx_DESTAT(bank), 0);
}
}
/* Deferred Error는 APIC LVT에 별도 벡터(DEFERRED_ERROR_VECTOR)로 전달됨 */
/* AMD 특유: 패트롤 스크러빙이 CE로 교정 불가한 오류를 발견하면 */
/* 즉시 UC를 발생시키지 않고 DESTAT에 기록 → 소프트웨어가 선제 대응 가능 */
IIO (Integrated I/O) MCA 아키텍처
Intel 서버 CPU(Xeon Scalable 이후)에서 IIO(Integrated I/O)는 CPU 다이에 통합된 I/O 서브시스템으로, PCIe 루트 포트, DMI(Direct Media Interface), CBDMA/IOAT(Crystal Beach DMA), VT-d(Intel Virtualization Technology for Directed I/O) 엔진 등을 포함합니다. IIO는 CPU 내부에서 I/O 장치와 메시 인터커넥트 사이를 연결하는 핵심 컴포넌트이며, 전용 MCA 뱅크(일반적으로 Bank 5~8)를 통해 I/O 관련 하드웨어 에러를 보고합니다.
IIO 내부 구조
각 IIO 스택(Stack)은 독립적인 PCIe 루트 컴플렉스를 제공합니다. Intel Xeon Scalable 플랫폼에서 소켓당 최대 6개의 IIO 스택이 존재하며, 각 스택은 PCIe 루트 포트, DMA 엔진, IOMMU를 포함합니다.
IIO MCA 뱅크 구조
IIO에서 보고되는 에러는 일반적으로 Bank 5~8에 매핑됩니다(CPU 모델에 따라 다름). 각 뱅크는 특정 IIO 스택에 대응합니다. IIO 뱅크의 MCi_STATUS 에러 코드는 표준 MCA 복합 에러 코드 외에 Intel 모델 고유 에러 코드(MSCOD)도 포함합니다.
| 뱅크 | IIO 스택 | 일반적 장치 | 보고 에러 유형 |
|---|---|---|---|
| Bank 5 | IIO Stack 0 (DMI) | PCH 연결 장치(SATA, USB, 온보드 LAN) | DMI 링크 에러, PCH 통신 타임아웃 |
| Bank 6 | IIO Stack 1 | PCIe Slot 1~2 (GPU, NIC) | PCIe CE/UC, Completion Timeout, Poisoned TLP |
| Bank 7 | IIO Stack 2 | PCIe Slot 3~4 (NVMe, FPGA) | PCIe CE/UC, ECRC Error, Unexpected Completion |
| Bank 8 | IIO Stack 3 (CBDMA) | DMA 엔진, DSA, 추가 PCIe | DMA 에러, IOAT 채널 에러, PCIe 에러 |
IIO MCi_STATUS 에러 코드 해석
IIO 뱅크의 MCi_STATUS 레지스터에서 에러 코드(bits 15:0)는 표준 MCA 형식을 따르지만, MSCOD(Model-Specific Error Code, bits 31:16)에 IIO 고유의 상세 정보가 인코딩됩니다. PCIe 에러의 경우 MSCOD 필드에서 PCIe AER 에러 유형을 구분할 수 있습니다.
| MSCOD (bits 31:16) | 에러 유형 | 설명 | 일반적 원인 |
|---|---|---|---|
0x0000 | Completion Timeout | PCIe 요청에 대한 응답 타임아웃 | 장치 응답 불가, 링크 다운, FLR 중 접근 |
0x0001 | Receiver Overflow | IIO 수신 버퍼 오버플로(Buffer Overflow) | 과부하, 흐름 제어(Flow Control) 실패 |
0x0002 | Poisoned TLP | 독성 데이터(Poison bit) TLP 수신 | 업스트림 장치 메모리 에러, ECC 실패 |
0x0003 | Unsupported Request | 지원하지 않는 PCIe 요청 | 드라이버 버그, BAR 매핑 오류 |
0x0004 | ECRC Error | End-to-End CRC 검증 실패 | PCIe 라이저/케이블 불량, 신호 무결성(Integrity) |
0x0005 | Uncorrectable Internal | IIO 내부 패리티/로직 에러 | CPU 하드웨어 결함, 마이크로코드 버그 |
0x0006 | Malformed TLP | 형식 오류 TLP 수신 | 장치 하드웨어 결함, 링크 트레이닝 불완전 |
0x0007 | Flow Control Protocol | 흐름 제어 프로토콜 위반 | 장치 또는 스위치 펌웨어 버그 |
0x0008 | Surprise Link Down | 예기치 않은 PCIe 링크 다운 | 물리적 분리, 전원 불안정, 열 문제 |
0x0010 | Correctable Internal | IIO 내부 교정 가능 에러 | 일시적 패리티, 재시도로 복구 |
0x0020 | Header Log Overflow | 에러 헤더 로그 오버플로 | 에러 폭주 |
0x0080 | Advisory Non-Fatal | 권고 비치명적 에러 | Poisoned TLP가 Advisory로 재분류 |
0x0200 | DMA Channel Error | CBDMA/IOAT/DSA 채널 에러 | DMA 디스크립터 오류, 주소 변환(Address Translation) 실패 |
0x0400 | VT-d Fault | IOMMU 주소 변환 실패 | DMAR 테이블 오류, 잘못된 DMA 주소 |
IIO MCi_MISC 레지스터 해석
IIO 뱅크에서 MCi_MISC(MISCV=1일 때)에는 에러와 관련된 PCIe 장치의
Bus/Device/Function(BDF) 정보가 인코딩됩니다. 이를 통해 에러를 발생시킨 특정 PCIe 장치를
정확히 식별할 수 있습니다.
| 비트 범위 | 필드 | 설명 |
|---|---|---|
| bits 7:0 | Function Number | PCIe 장치 Function 번호 (0~7) |
| bits 12:8 | Device Number | PCIe 장치 Device 번호 (0~31) |
| bits 20:13 | Bus Number | PCIe 장치 Bus 번호 (0~255) |
| bits 23:21 | Segment Group | PCIe Segment Group (다중 세그먼트 시) |
| bits 31:24 | Root Port | 에러를 감지한 루트 포트 번호 |
/* IIO MCA MISC에서 BDF 추출 예시 (커널 내부) */
static void decode_iio_mce(struct mce *m)
{
u8 func = m->misc & 0xFF;
u8 dev = (m->misc >> 8) & 0x1F;
u16 bus = (m->misc >> 13) & 0xFF;
pr_emerg("IIO MCE: PCIe BDF %02x:%02x.%x MSCOD=0x%04x\n",
bus, dev, func,
(u16)(m->status >> 16) & 0xFFFF);
}
PCIe AER vs IIO MCA: 에러 보고 이중 경로
동일한 PCIe 에러가 두 가지 경로로 보고될 수 있습니다: PCIe AER(Advanced Error Reporting)와 IIO MCA 뱅크입니다. 이 두 경로는 독립적이며, 에러 발생 시 둘 다 트리거될 수 있습니다.
| 특성 | PCIe AER | IIO MCA |
|---|---|---|
| 트리거 | PCIe Config Space AER Capability | CPU 내부 IIO 로직 |
| 인터럽트 | MSI/MSI-X (일반 인터럽트) | #MC (벡터 18) 또는 CMCI |
| 에러 정보 | AER 레지스터 (Header Log 포함) | MCi_STATUS/ADDR/MISC (BDF 인코딩) |
| CE 처리 | AER Correctable → 로그만 | CMCI 또는 폴링으로 수집 |
| UC 처리 | AER Fatal → PCIe 링크 리셋, 드라이버 복구 | MCE Severity 판단 → 패닉 가능 |
| Firmware-First | GHES가 AER을 가로챌 수 있음 | Firmware-First 시 GHES/BERT 경유 |
| 커널 핸들러 | aer_irq() → pcie_do_recovery() | do_machine_check() → severity |
| 장점 | 장치 식별 정확, 드라이버 복구 프레임워크 | CPU 레벨 무결성 보장, 데이터 오염 방지 |
ghes_proc() → memory_failure() 또는
aer_recover_work_func()로 처리됩니다.
APEI/GHES 통합 섹션을 참고하세요.
IIO 에러 시나리오별 분석
시나리오 1: PCIe Completion Timeout (CTO)
가장 흔한 IIO 에러입니다. CPU가 PCIe 장치에 요청을 보냈으나, 설정된 시간(기본 50ms~3.2초) 내에 응답을 받지 못한 경우입니다.
| 항목 | 내용 |
|---|---|
| MCA 코드 | MSCOD=0x0000, MCA 에러 코드=0x0e0b 범위 |
| dmesg 시그니처 | Hardware Error ... IIO MCA Bank ... Completion Timeout |
| AER 대응 | pcieport ... AER: Uncorrectable (Non-Fatal) error received |
| 흔한 원인 |
|
| 조치 |
|
시나리오 2: Poisoned TLP 수신
PCIe 장치가 "독성(Poisoned)" 데이터를 포함한 TLP를 전송한 경우입니다. 이는 상류 장치의 메모리(예: GPU VRAM)에서 ECC 에러가 발생했음을 의미합니다.
| 항목 | 내용 |
|---|---|
| MCA 코드 | MSCOD=0x0002 |
| 의미 | 장치가 DMA 읽기에서 에러 데이터를 CPU에 전달 (EP bit set in TLP header) |
| 영향 |
|
| GPU 컨텍스트 | GPU VRAM ECC UC → Poisoned Completion → IIO MCE. NVIDIA GPU는 Xid 에러(Xid 48: DBE 에러)와 동시에 발생할 수 있음 |
| NVMe 컨텍스트 | NVMe 컨트롤러 내부 DRAM 에러 → Poisoned DMA Read Completion → IIO MCE → I/O 에러 |
| 조치 |
|
시나리오 3: Surprise Link Down
| 항목 | 내용 |
|---|---|
| MCA 코드 | MSCOD=0x0008 |
| dmesg 시그니처 | pcieport ... link down + IIO MCA 에러 |
| 원인 |
|
| 핫플러그 vs 에러 | 핫플러그 지원 슬롯에서의 의도적 제거는 Surprise Link Down을 정상 이벤트로 처리. 비핫플러그 슬롯에서 발생하면 에러로 취급 |
| 조치 |
|
시나리오 4: DMA/IOAT 엔진 에러 (CBDMA/DSA)
| 항목 | 내용 |
|---|---|
| MCA 코드 | MSCOD=0x0200 범위 |
| 영향 범위 | IOAT(I/O Acceleration Technology) DMA 채널, DSA(Data Streaming Accelerator) |
| 원인 |
|
| 커널 IOAT 관련 | 커널의 ioatdma 드라이버가 네트워크 DMA offload,
dmaengine 프레임워크에서 사용. 에러 시 채널 리셋 후 재시도 |
| DSA 관련 | SPR+ CPU의 DSA(Data Streaming Accelerator)는 idxd 드라이버로 관리.
메모리 복사/비교/CRC 가속. 에러 시 work queue 리셋 |
시나리오 5: VT-d (IOMMU) 변환 실패
| 항목 | 내용 |
|---|---|
| MCA 코드 | MSCOD=0x0400 범위 |
| dmesg 시그니처 | DMAR: DRHD: handling fault status reg ... reason 06 |
| 원인 |
|
| 가상화 영향 | KVM/QEMU VFIO passthrough 환경에서 게스트가 잘못된 DMA 주소를 프로그래밍하면 VT-d fault → IIO MCA 에러 → 게스트 강제 종료 가능 |
IIO 데이터 경로와 에러 감지 지점
IIO 스택 내부에서 PCIe 트랜잭션(Transaction)은 여러 단계를 거치며, 각 단계마다 독립적인 에러 감지 로직이 있습니다. 에러가 어느 단계에서 감지되었는지에 따라 MSCOD와 복구 가능성이 달라집니다.
| 에러 지점 | 컴포넌트 | 감지 에러 | MSCOD | 심각도 |
|---|---|---|---|---|
| ① | IRP 주소 디코드 | 잘못된 BAR/MMIO 주소 접근 | 0x0003 | UC (Unsupported Request) |
| ② | IRP CTO 타이머 | Completion Timeout | 0x0000 | UC Non-Fatal 또는 Fatal |
| ③ | ITC Ingress | Poisoned TLP 수신 | 0x0002 | UC — Poison 전파 여부에 따라 변동 |
| ④ | ITC 버퍼 | 수신 버퍼 오버플로 | 0x0001 | UC (데이터 손실) |
| ⑤ | OTC 패리티 | Egress 경로 내부 패리티 | 0x0005 | UC Internal |
| ⑥ | OTC 크레딧 | 흐름 제어 크레딧 에러 | 0x0007 | UC (링크 교착 가능) |
| ⑦ | PCIe TL | ECRC 검증 실패 | 0x0004 | UC (데이터 무결성) |
| ⑧ | PCIe TL | Malformed TLP | 0x0006 | UC Fatal |
| ⑨ | PCIe PL | Surprise Link Down | 0x0008 | UC Fatal |
| ⑩ | PCIe DLL | DLLP CRC / Replay Timeout | 0x0010 (CE) | CE (자동 재전송(Retransmission)) |
| ⑪ | VT-d | DMA 주소 변환 실패 | 0x0400 | UC (DMA Remap Fault) |
IIO Poison 전파 모델과 Viral Mode
PCIe 장치에서 Poisoned TLP를 수신한 IIO는 두 가지 전략 중 하나를 선택합니다: Containment(격리) 또는 Propagation(전파). 이 동작은 BIOS의 Viral Mode 설정과 IIO 내부 로직에 의해 결정됩니다.
| BIOS 설정 | 동작 | 권장 환경 | MCE 관점 |
|---|---|---|---|
| Viral Mode Disabled (기본) | IIO가 Poison을 즉시 소비하고 MCE/CMCI 발생 | 일반 서버, HPC | IIO 뱅크에서 즉시 에러 보고. 피해 범위 명확 |
| Viral Mode Enabled | Poison을 메모리까지 전파, 실제 소비 시점에 SRAR MCE | 미션 크리티컬 (정확한 소비자 식별 필요) | 시간차 MCE. memory_failure()로 해당 페이지만 오프라인 |
| Viral + MCA Recovery | Poison 전파 + 소비 시 프로세스만 킬 (패닉 회피) | 클라우드, 가상화 (VM 격리) | 가장 정교한 복구. CPU의 MCG_CAP.MCG_SER_P=1 필요 |
IIO와 PCIe 스위치/팬아웃 토폴로지(Topology)
GPU 클러스터, NVMe JBOF, 스토리지 어레이에서는 PCIe 스위치(예: Broadcom PEX, Microchip Switchtec)를 통해 여러 장치를 하나의 IIO 스택에 연결합니다. 스위치를 경유한 에러는 디버깅이 더 복잡합니다.
| 토폴로지 | IIO 관점 | 에러 진단 특징 |
|---|---|---|
| 직결 (Direct Attach) | IIO Root Port → 장치 (1:1) | MCi_MISC BDF가 정확히 에러 장치를 가리킴 |
| PCIe 스위치 경유 | IIO Root Port → 스위치 → 장치 (1:N) | MCi_MISC BDF가 스위치 Downstream Port를 가리킴. 실제 에러 장치는 스위치의 AER 로그에서 확인해야 함 |
| 다단 스위치 (Cascaded Switch) | IIO → Switch → Switch → 장치 | BDF가 최상위 스위치를 가리킴. 하위 스위치의 포트별 AER 확인 필요 |
| NTB (Non-Transparent Bridge) | IIO → NTB → 원격 호스트 | NTB 뒤의 에러는 NTB 자체 에러로 보고됨. 원격 호스트 로그 확인 필수 |
| GPU NVLink/NVSwitch | IIO → GPU (NVLink는 별도 경로) | GPU 간 P2P(NVLink)는 IIO 무관. GPU↔CPU는 IIO 경유. GPU PCIe BAR 접근 시 IIO 에러 가능 |
| CXL over IIO | IIO → CXL 장치 (PCIe 물리 계층 공유) | CXL.io 에러는 IIO MCA로, CXL.mem 에러는 GHES/CPER로 보고 |
# PCIe 스위치 뒤 장치 에러 추적 워크플로
# 1. IIO MCE에서 BDF 식별 → 스위치 Downstream Port일 수 있음
$ lspci -s <BDF>
# 출력 예: 85:00.0 PCI bridge: Broadcom PEX 8747 (rev ca)
# 2. 스위치 하위 장치 나열
$ lspci -tv -s <BDF>
# 3. 스위치 자체 AER 상태 확인
$ cat /sys/bus/pci/devices/0000:<BDF>/aer_dev_nonfatal
# 4. 스위치 하위 포트별 AER 확인
for dev in $(lspci -D | grep "PCI bridge" | awk '{print $1}'); do
echo "--- $dev ---"
cat /sys/bus/pci/devices/$dev/aer_dev_nonfatal 2>/dev/null
done
# 5. Switchtec 관리 도구 (Microchip 스위치)
$ switchtec status /dev/switchtec0
$ switchtec log-dump /dev/switchtec0
추가 IIO 에러 시나리오
시나리오 6: PCIe Replay Timer Timeout (DLLP CE)
| 항목 | 내용 |
|---|---|
| MSCOD | 0x0010 (Correctable Internal — DLLP 계층) |
| 의미 | Data Link Layer의 ACK/NAK 프로토콜에서 재전송 타임아웃. TLP가 성공적으로 전달되지 못해 재전송됨 |
| CE/UC | CE — 자동 재전송으로 복구. 반복 시 링크 품질 저하 경고 |
| 원인 |
|
| 모니터링 | lspci -vvv의 CESta:에서 RxErr+, Replay+ 확인.
카운터가 지속 증가하면 물리 계층 점검 필요 |
| Gen5 특이사항 | PCIe 5.0(32 GT/s)에서는 시그널(Signal) 마진이 매우 좁아 리타이머(Retimer) 없이 장거리 연결 시 Replay CE가 빈번할 수 있음 |
시나리오 7: MMIO 트랜잭션 에러 (CPU→장치 방향)
| 항목 | 내용 |
|---|---|
| 경로 | CPU 코어 → Mesh → IIO OTC → PCIe Core → 장치 BAR 영역 |
| 에러 유형 |
|
| 특이사항 | MMIO 에러는 CPU가 동기적으로 접근하는 경우(드라이버의 readl(),
writel())에 발생. DMA(장치→메모리)와 달리 에러 시점이 명확 |
| 커널 보호 | pci_enable_pcie_error_reporting()으로 드라이버가 AER을 활성화.
MMIO 에러 발생 시 pcie_do_recovery()로 장치 리셋 시도 |
시나리오 8: PCIe AtomicOp / Extended Tag 에러
| 항목 | 내용 |
|---|---|
| 에러 유형 | AtomicOp Egress Blocked, Extended Tag Mismatch |
| 배경 | PCIe AtomicOp은 GPU/FPGA가 호스트 메모리에 원자적 연산(Atomic Operation)을 수행할 때 사용. Extended Tag(10-bit)는 Gen4+에서 미해결 트랜잭션 수를 확대 |
| 원인 |
|
| GPU 관련 | NVIDIA GPU의 PCIe P2P 또는 GPUDirect RDMA에서 AtomicOp 사용. IIO에서 차단되면 GPU 드라이버 초기화 실패 또는 성능 저하 |
| 조치 | BIOS에서 PCIe AtomicOp Egress Enable 활성화.
setpci로 Device Control 2 레지스터의 AtomicOp 비트 확인 |
시나리오 9: DMI (Stack 0) 에러
| 항목 | 내용 |
|---|---|
| IIO 스택 | Stack 0 — DMI(Direct Media Interface)로 PCH에 연결 |
| 에러 유형 |
|
| 영향 | DMI는 PCH의 유일한 상류 링크이므로, DMI 에러 시 PCH 하위 모든 장치(SATA, USB, 온보드 LAN, BMC)에 영향. 심한 경우 서버 관리 불가 |
| 원인 | 메인보드 PCB 결함, CPU/PCH 간 라우팅 문제, PCH 하드웨어 결함 |
| 조치 | 메인보드 교체 검토. DMI CE가 지속되면 CPU 또는 PCH 교체 |
시나리오 10: 가상화 환경 VFIO Passthrough IIO 에러
| 항목 | 내용 |
|---|---|
| 환경 | KVM/QEMU + VFIO passthrough (GPU/NIC/NVMe를 VM에 직접 할당) |
| 에러 유형 |
|
| 격리 | VFIO는 IOMMU(VT-d)로 게스트의 DMA를 격리하지만, PCIe 레벨 에러(CTO, Link Down)는 IIO에서 감지되어 호스트에 MCE를 발생시킴. 이로 인해 하나의 게스트 문제가 호스트 전체에 영향을 줄 수 있음 |
| 완화 |
|
Intel 세대별 IIO 변천사
| CPU 세대 | 코드네임 | PCIe 지원 | IIO 특징 | 주요 변경 |
|---|---|---|---|---|
| Xeon Scalable 1st | Skylake-SP (SKX) | PCIe 3.0, 48레인 | IIO Stack 0~5, Bank 5~8 | IIO MCA 최초 표준화, CBDMA 8채널 |
| Xeon Scalable 2nd | Cascade Lake (CLX) | PCIe 3.0, 48레인 | SKX와 동일 | 마이크로코드 개선 (IIO CE 필터링) |
| Xeon Scalable 3rd | Ice Lake-SP (ICX) | PCIe 4.0, 64레인 | IIO 스택 확장, Bank 5~10 | PCIe Gen4 → 대역폭(Bandwidth) 2배, 링크 에러 감지 강화 |
| Xeon Scalable 4th | Sapphire Rapids (SPR) | PCIe 5.0, 80레인 | IIO 재설계, DSA/IAA/QAT 통합 | DSA(Data Streaming Accelerator), IAA(In-Memory Analytics Accelerator) 추가. CXL 1.1 Type 3 지원. MDF(Mesh Data Fabric) MCA 뱅크 신설 |
| Xeon Scalable 5th | Emerald Rapids (EMR) | PCIe 5.0, 80레인 | SPR 기반 개선 | 마이크로코드 IIO 에러 핸들링 개선, LMCE IIO 지원 확대 |
| Xeon 6 | Granite Rapids (GNR) | PCIe 5.0, 96레인 | IIO 확장, CXL 2.0 | CXL 2.0 Type 1/2/3, 더 많은 IIO 스택, MCA 뱅크 확장 |
lspci -vvv의 LnkSta:에서 링크 속도와 폭 다운그레이드 여부를 확인해야 합니다.
IIO 에러 디버깅 워크플로
# 1. IIO MCA 뱅크 에러 확인
$ dmesg | grep -i "iio\|bank [5-8]\|mce.*bank"
# 2. PCIe AER 에러 동시 확인
$ dmesg | grep -i "aer\|pcieport"
# 3. 에러 발생 장치 BDF 식별 (MCA MISC에서 디코딩)
$ mcelog --client | grep -i "iio\|misc"
# 4. 해당 BDF의 장치 정보 확인
$ lspci -vvvs <BDF>
# 5. PCIe 링크 상태 확인
$ lspci -vvvs <BDF> | grep -i "lnksta\|lnkcap\|devsta\|devctl"
# 6. AER 에러 카운터 확인
$ cat /sys/bus/pci/devices/0000:<BDF>/aer_dev_correctable
$ cat /sys/bus/pci/devices/0000:<BDF>/aer_dev_fatal
$ cat /sys/bus/pci/devices/0000:<BDF>/aer_dev_nonfatal
# 7. 장치별 세부 진단
# GPU:
$ nvidia-smi -q -d ERRORS,TEMPERATURE,POWER
# NVMe:
$ nvme smart-log /dev/nvmeN
$ nvme error-log /dev/nvmeN
# NIC:
$ ethtool -S <interface> | grep -i "error\|drop\|crc"
# 8. PCIe topology와 NUMA 매핑 확인
$ lspci -tv
$ lstopo --of txt
Uncore MCA 에러 종합 분석
IIO 외에도 CPU의 Uncore 영역에는 여러 MCA 에러 소스가 존재합니다. Uncore는 CPU 코어 외부에 있지만 CPU 다이 내부에 통합된 공유 자원 — LLC(Last Level Cache), 메모리 컨트롤러, 메시 인터커넥트, 전력 관리 유닛 등 — 을 포함합니다. 데이터센터에서 발생하는 MCE의 상당 부분이 이 Uncore 영역에서 옵니다.
M2M (Mesh to Memory) 에러
M2M은 CPU 내부 메시 인터커넥트와 iMC(메모리 컨트롤러) 사이의 중개 역할을 합니다. 메모리 요청의 주소 디코딩, 인터리빙, 미러링, 스페어링 결정을 담당합니다. Bank 9~12(SKX 기준)에 매핑됩니다.
| 에러 유형 | MSCOD | 설명 | 일반적 원인 |
|---|---|---|---|
| Patrol Scrub UC | 0x0010 | 패트롤 스크러빙이 교정 불가 에러 발견 | DIMM 열화, ECC 2비트+ 오류 |
| Patrol Scrub CE | 0x0010 (UC=0) | 패트롤 스크러빙이 교정 가능 에러 발견 | 단일 비트 에러 (정상 동작) |
| Demand Read UC | 0x0001 | 데이터 읽기 중 교정 불가 에러 | DRAM 셀 고장, Row Hammer |
| Full Mirror Failover | 0x0020 | 미러 채널로 페일오버 발생 | 기본 채널 DIMM 장애 |
| Partial Mirror | 0x0021 | 부분 미러 영역 에러 | 미러 영역 DIMM 열화 |
| Sparing Failover | 0x0030 | 스페어 랭크로 전환 완료 | CE 임계값 초과 → 자동 스페어링 |
| ADDDC Failover | 0x0040 | ADDDC(Adaptive Double DRAM Device Correction) 활성화 | 단일 DRAM 디바이스 장애 교정 |
| Address Parity | 0x0100 | 메모리 주소 버스 패리티 에러 | 하드웨어 결함 (메모리 버스) |
CHA (Caching Home Agent) / LLC 에러
CHA는 LLC(Last Level Cache) 슬라이스와 코히런스 디렉토리를 관리합니다. Bank 21~24+(코어 수에 따라 가변)에 매핑됩니다.
| 에러 유형 | 설명 | 영향 |
|---|---|---|
| LLC ECC CE | LLC 데이터 ECC 교정 가능 에러 | 자동 교정, 로그만 기록 |
| LLC ECC UC | LLC 데이터 ECC 교정 불가 에러 | 데이터 손상 가능 → PCC=1이면 패닉 |
| LLC Tag Parity | LLC 태그 패리티 에러 | 캐시라인 무효화 후 재페치 (교정 가능 시) |
| Snoop Filter Error | 코히런스 디렉토리 에러 | 멀티소켓에서 데이터 불일치 가능 |
| TOR Timeout | Table of Requests 엔트리 타임아웃 | 미처리 요청 → 시스템 행/패닉 |
| SF/LLC Way Error | 특정 LLC Way 접근 실패 | 캐시 용량 감소 (해당 Way 비활성화) |
TOR (Table of Requests) 타임아웃
TOR 타임아웃은 CHA의 트랜잭션 큐에서 요청이 완료되지 못하고 타임아웃된 경우입니다. 이는 매우 심각한 상황으로, 거의 항상 시스템 패닉을 유발합니다.
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: CPU N: Machine Check: ... Bank CHA ... TOR Timeout |
| 일반적 원인 |
|
| MCi_MISC 정보 | 타임아웃된 요청의 주소, 요청 유형(Read/Write/Snoop), 대상 유닛 정보 |
| 조치 |
|
UPI (Ultra Path Interconnect) 링크 에러
UPI는 멀티소켓 시스템에서 CPU 간 통신을 담당합니다. Bank 5~6(SKX 기준)에 매핑됩니다. 원격 메모리 접근(NUMA), 코히런스 프로토콜, 인터럽트 라우팅에 사용됩니다.
| 에러 유형 | 설명 | 영향 | 원인 |
|---|---|---|---|
| CRC Error (CE) | 링크 CRC 검사 실패 (교정됨) | 자동 재전송, 성능 약간 저하 | 일시적 신호 간섭, 커넥터 열화 |
| CRC Error (UC) | 링크 CRC 교정 불가 | 링크 다운, 원격 소켓 격리 | 하드웨어 결함, 심한 전기적 잡음 |
| L0p/L1 Entry Error | 전력 절약 모드 진입 실패 | 전력 효율 저하 | 링크 트레이닝 문제 |
| Protocol Error | UPI 프로토콜 위반 | 시스템 패닉 (PCC=1 가능) | 마이크로코드/하드웨어 결함 |
| Phy Init Error | 물리 계층 초기화 실패 | 링크 비활성 | 소켓/보드 하드웨어 문제 |
PCU (Power Control Unit) 에러
PCU는 CPU 전력 관리, 주파수 제어(P-state, C-state), 온도 모니터링을 담당합니다. Bank 4에 매핑됩니다. PCU 에러는 전력/열 관련 문제를 나타냅니다.
| 에러 유형 | 설명 | 원인 |
|---|---|---|
| Internal Firmware Error | PCU 마이크로컨트롤러 펌웨어 에러 | 마이크로코드 버그, 전력 시퀀싱 문제 |
| VR (Voltage Regulator) Error | 전압 조정기 통신 에러 | VRM 하드웨어 결함, 전원 공급 불안정 |
| MCA Timeout | PCU 내부 타이머 만료 | 전력 상태 전환 중 교착 |
| Core/Mesh Ratio Error | 주파수 비율 설정 실패 | 오버클럭 또는 불안정한 전력 공급 |
MDF (Mesh Data Fabric) — SPR 이후
Sapphire Rapids부터 도입된 MDF(Mesh Data Fabric)는 기존 CHA/메시의 데이터 패브릭을 별도 MCA 뱅크로 분리한 것입니다. 메시 내부의 데이터 전송 경로에서 발생하는 에러를 보고합니다.
| 에러 유형 | 설명 | 영향 |
|---|---|---|
| Data Fabric Parity | 메시 내부 데이터 전송 패리티 에러 | CE: 자동 교정, UC: 패닉 가능 |
| Credit Overflow/Underflow | 크레딧 기반 흐름 제어 에러 | 메시 교착 가능 |
| Routing Error | 메시 라우팅 테이블(Routing Table) 에러 | 패킷(Packet) 오배달 → 데이터 손상 |
Intel Cascade Lake 아키텍처 기준 MCA 뱅크 상세
Cascade Lake-SP(CLX)는 Xeon Scalable 2세대로, Skylake-SP(SKX)의 MCA 아키텍처를 기반으로 하되 RAS 기능이 크게 강화된 세대입니다. 현재도 데이터센터에서 널리 운용되고 있어, MCE 분석의 기준 아키텍처로 적합합니다. 이후 세대(ICX, SPR, EMR, GNR)와의 차이를 이해하는 데도 Cascade Lake를 기준으로 삼으면 효과적입니다.
Cascade Lake 고유 RAS 기능과 MCA 영향
| 기능 | SKX (1세대) | CLX (2세대) 변경 | MCA 관점 영향 |
|---|---|---|---|
| ADDDC | 미지원 | Adaptive Double DRAM Device Correction 도입 | M2M 뱅크(Bank 9~12)에서 ADDDC Failover 이벤트(MSCOD=0x0040) 보고. ADDDC 활성화 시 단일 DRAM 칩 고장을 교정 → CE로 보고 (UC 방지) |
| Optane PMem (2LM) | 미지원 | Memory Mode: DRAM이 캐시, Optane이 주 메모리 | iMC 뱅크에서 Optane 미디어 에러 보고. 2LM 캐시 미스 시 Optane 접근 → 추가 지연. Optane UC 에러 시 DRAM 캐시에 있으면 복구 가능, 없으면 UC MCE |
| Partial Mirror | Full Mirror만 | 주소 범위 지정 미러링 지원 | M2M 뱅크에서 Partial Mirror Failover(MSCOD=0x0021) 보고. 미러 영역 외 에러는 일반 UC 처리 |
| IIO CE 필터링 | 모든 IIO CE 보고 | 마이크로코드 개선으로 일부 IIO CE 자동 필터링 | IIO CMCI 빈도 감소. spurious CE에 의한 CMCI 스톰 완화 |
| LMCE 확대 | 제한적 | 더 많은 에러 유형에 LMCE 적용 | iMC UC(Demand Read) 시 LMCE로 해당 코어만 MCE 수신 → Monarch 오버헤드 감소 |
| Corrected Error Collector | 기본 | CE 누적 임계값 기반 사전 경고 강화 | CE 누적이 임계값 초과 시 soft_offline_page() 자동 호출.
/sys/devices/system/machinecheck/machinecheck0/ce_count에서 확인 |
- Cascade Lake(CLX): 최대 ~48개 뱅크. 기준 아키텍처. UPI 2개, IIO 6스택, 6채널 DDR4
- Ice Lake-SP(ICX): ~60개 뱅크. PCIe Gen4, 8채널 DDR4. IIO 뱅크 확장(Bank 5~10). CHA 뱅크 수 증가(40코어 → Bank 21~60)
- Sapphire Rapids(SPR): ~80개+ 뱅크. PCIe Gen5, 8채널 DDR5, CXL 1.1. MDF 뱅크 신설(Mesh Data Fabric), HBM MC 뱅크(HBM SKU), DSA/IAA/QAT 가속기 뱅크. M2M → IMC(Integrated Memory Controller)로 재편
- Granite Rapids(GNR): ~100개+ 뱅크. CXL 2.0, 12채널 DDR5(MRDIMM). 더 많은 IIO 스택, CXL DP(Downstream Port) 전용 뱅크 추가
Cascade Lake ADDDC 동작 상세
ADDDC(Adaptive Double DRAM Device Correction)는 CLX에서 도입된 핵심 RAS 기능으로, DIMM 내 단일 DRAM 칩(x4)의 완전 장애까지 교정할 수 있습니다. 기존 SDDC(Single Device Data Correction)의 확장입니다.
| 단계 | 상태 | MCA 이벤트 | 커널 동작 |
|---|---|---|---|
| 1. 정상 | ADDDC 대기 (Standby) | 없음 | 정상 동작, patrol scrub 활성 |
| 2. CE 누적 | SDDC로 교정 중 | iMC CE (Bank 13~20), EDAC 카운터 증가 | rasdaemon 기록, 모니터링 |
| 3. CE 임계값 초과 | ADDDC Region 활성화 | M2M ADDDC Failover (MSCOD=0x0040) | EDAC: "ADDDC engaged". 해당 Rank에서 추가 ECC 비트 할당 |
| 4. ADDDC 활성 중 | 2개 DRAM 디바이스 에러까지 교정 | CE로 계속 보고 (UC 방지) | DIMM 교체 권고. ADDDC 소진 시 UC 위험 |
| 5. ADDDC 소진 | 3번째 디바이스 장애 | iMC UC → M2M UC → 패닉 가능 | DIMM 교체 필수 |
# ADDDC 상태 확인 (EDAC sysfs)
$ cat /sys/devices/system/edac/mc/mc0/dimm0/dimm_edac_mode
# 출력 예: "x4 SDDC" 또는 "x4 ADDDC"
# ADDDC Failover 이벤트 확인
$ dmesg | grep -i "adddc\|failover"
$ ras-mc-ctl --errors | grep -i "adddc"
# Cascade Lake에서 ADDDC 활성화 여부 (BIOS 설정)
# BIOS → Advanced → Memory Configuration → ADDDC Sparing: Enabled
Uncore 에러 간 연쇄(Cascade) 패턴
Uncore 에러는 종종 연쇄적으로 발생합니다. 하나의 근본 원인(root cause)이 여러 MCA 뱅크에서 동시다발적으로 에러를 보고하게 만듭니다. 연쇄 패턴을 이해하면 근본 원인을 더 빠르게 식별할 수 있습니다. 아래는 Cascade Lake 아키텍처를 기준으로 한 에러 전파 경로입니다.
연쇄 에러 근본 원인 분석 체크리스트
여러 MCA 뱅크에서 동시에 에러가 보고되면, 다음 절차로 근본 원인을 식별합니다.
- 타임스탬프 정렬:
dmesg -T또는rasdaemonDB에서 에러를 시간 순서로 정렬. 가장 이른 타임스탬프의 에러가 root cause 후보 - OVER 비트 확인: MCi_STATUS.OVER=1인 뱅크는 정보 손실이 있으므로, 해당 뱅크가 실제 1차 에러 소스였을 가능성 높음
- 뱅크 간 우선순위:
- iMC(Bank 13~20) UC가 있으면 → DIMM이 근본 원인일 가능성 최우선
- IIO(Bank 7~8) CTO가 있으면 → PCIe 장치가 근본 원인
- UPI(Bank 5~6) UC가 있으면 → 소켓 간 링크 문제
- CHA TOR Timeout만 있으면 → 2차 에러, 다른 뱅크에서 근본 원인 탐색
- BERT 확인: 패닉 후 재부팅 시
dmesg | grep BERT로 이전 부팅의 에러 정보 수집. BERT에 1차 에러가 남아 있을 수 있음 - BMC/IPMI SEL:
ipmitool sel list에서 OS 레벨 로그와 교차 확인. 펌웨어가 OS보다 먼저 에러를 감지한 경우 있음 - 동일 소켓/채널 집중: 에러가 특정 소켓, 특정 메모리 채널, 특정 PCIe 슬롯에 집중되면 해당 하드웨어 교체 고려
# 연쇄 에러 분석 스크립트 예시
# 모든 MCA 뱅크 에러를 타임스탬프순으로 정렬
$ dmesg -T | grep -i "mce\|hardware error\|machine check" | sort
# rasdaemon DB에서 최근 1시간 에러를 뱅크별로 그룹화
$ sqlite3 /var/lib/rasdaemon/ras-mc_event.db \
"SELECT bank, error_type, COUNT(*), MIN(timestamp), MAX(timestamp)
FROM mce_record
WHERE timestamp > strftime('%s','now','-1 hour')
GROUP BY bank ORDER BY MIN(timestamp);"
# BERT (Boot Error Record) 확인 — 패닉 후 재부팅 시
$ dmesg | grep -A 20 "BERT"
# IPMI SEL 로그와 교차 확인
$ ipmitool sel list | tail -20
- TOR Timeout은 결과지 원인이 아닙니다. TOR Timeout만 보이면 다른 뱅크(iMC, IIO, UPI)에서 근본 원인을 반드시 찾아야 합니다.
- M2M 에러는 경유지입니다. M2M은 메시와 메모리 사이의 중개 역할이므로, M2M UC는 iMC 에러의 전파이거나 patrol scrub 발견일 수 있습니다.
- IIO Poison은 시간차가 있습니다. Viral Mode Enabled 시 Poison이 메모리에 저장된 후 나중에 SRAR로 나타나므로, IIO MCE와 코어 SRAR MCE 사이에 시간 차이가 있을 수 있습니다.
- CLX의 ADDDC는 연쇄를 지연시킵니다. ADDDC가 활성화되면 단일 DRAM 칩 장애를 CE로 교정하여 UC MCE 발생을 지연시키지만, ADDDC가 소진되면 갑자기 UC 연쇄가 시작될 수 있습니다.
LMCE (Local MCE)
전통적으로 MCE는 모든 논리 프로세서에 브로드캐스트됩니다. LMCE(Local Machine Check Exception)는 특정 CPU에만 영향을 미치는 에러를 해당 CPU에만 전달하여 불필요한 Monarch 랑데부를 피합니다.
LMCE 활성화 조건
MCG_CAP.MCG_LMCE_P = 1— CPU가 LMCE를 지원MCG_EXT_CTL.LMCE_EN = 1— 소프트웨어가 LMCE를 활성화IA32_FEATURE_CONTROL.LMCE_ON = 1— 펌웨어가 LMCE를 허용
LMCE의 이점
| 시나리오 | 기존 MCE | LMCE |
|---|---|---|
| 64코어 서버에서 1개 코어 CE | 64개 코어 모두 MCE 핸들러 진입, 랑데부 | 1개 코어만 핸들러 실행 |
| 멀티소켓에서 로컬 캐시 에러 | 전체 소켓 동기화 → 성능 영향 큼 | 해당 소켓의 해당 코어만 처리 |
| 가상화 환경 | 모든 vCPU에 VMEXIT 발생 | 에러 발생 vCPU만 VMEXIT |
/* arch/x86/kernel/cpu/mce/core.c */
static void mce_intel_feature_init(struct cpuinfo_x86 *c)
{
u64 cap, feat;
rdmsrl(MSR_IA32_MCG_CAP, cap);
if (cap & MCG_LMCE_P) {
rdmsrl(MSR_IA32_FEAT_CTL, feat);
if (feat & FEAT_CTL_LMCE_ENABLED) {
rdmsrl(MSR_IA32_MCG_EXT_CTL, cap);
cap |= MCG_EXT_CTL_LMCE_EN;
wrmsrl(MSR_IA32_MCG_EXT_CTL, cap);
}
}
}
/* LMCE 발생 여부 확인 */
static bool mce_is_lmce(struct mce *m)
{
return m->mcgstatus & MCG_STATUS_LMCES;
}
dmesg | grep LMCE로 부팅 시 LMCE 활성화 여부를 확인할 수 있습니다.
또는 rdmsr 0x4D0으로 MCG_EXT_CTL 레지스터를 직접 읽어볼 수 있습니다.
LMCE 지원 CPU 목록
| 프로세서 | 마이크로아키텍처 | LMCE 지원 | 비고 |
|---|---|---|---|
| Intel Xeon E5 v4 | Broadwell-EP (2016) | 최초 지원 | MCG_CAP.MCG_LMCE_P 비트 최초 도입 |
| Intel Xeon Scalable 1세대 | Skylake-SP (2017) | 완전 지원 | 서버 플랫폼 기본 활성화 |
| Intel Xeon Scalable 3세대 | Ice Lake-SP (2021) | 향상된 지원 | LMCE + Enhanced MCA 통합 |
| Intel Xeon Scalable 4세대 | Sapphire Rapids (2023) | 향상된 지원 | In-Band ECC + LMCE 연동 개선 |
| Intel Core 6세대+ | Skylake 클라이언트+ | 지원 (BIOS 의존) | IA32_FEATURE_CONTROL 설정 필요 |
| AMD EPYC/Ryzen | Zen 1~5 | N/A | SMCA(Scalable MCA)로 다른 접근: MCA_STATUS에 에러 소스 CPU 기록, 별도 LMCE 불필요 |
LMCE 판별 로직 상세
do_machine_check()에서 LMCE 여부에 따라 Monarch 랑데부를 건너뛰는 핵심 분기 로직입니다.
MCG_STATUS의 LMCES(bit 3) 비트가 설정되어 있으면 해당 MCE는 로컬 전용이므로
다른 CPU와 동기화할 필요가 없습니다.
/* arch/x86/kernel/cpu/mce/core.c — do_machine_check() 내부 LMCE 분기 */
noinstr void do_machine_check(struct pt_regs *regs)
{
struct mce m;
int worst = 0, order, no_way_out = 0;
bool lmce = false;
/* 1단계: MCG_STATUS 읽기 */
m.mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
/* 2단계: LMCE 여부 판별 — MCG_STATUS.LMCES (bit 3) */
lmce = m.mcgstatus & MCG_STATUS_LMCES;
/* 3단계: MCA 뱅크 순회 — 에러 정보 수집 */
for (i = 0; i < this_cpu_read(mce_num_banks); i++) {
m.status = mce_rdmsrl(mca_msr_reg(i, MCA_STATUS));
if (!(m.status & MCI_STATUS_VAL))
continue;
/* severity 평가, worst 갱신 */
worst = mce_severity(&m, regs, ...);
}
/* 4단계: LMCE면 랑데부 건너뛰기 */
if (!lmce) {
/* 브로드캐스트 MCE — Monarch 랑데부 진입 */
order = mce_start(&no_way_out);
/* ... 모든 CPU 동기화 후 Monarch 판정 ... */
mce_end(order);
} else {
/* LMCE — 이 CPU만 로컬 처리 */
/* 랑데부 불필요, 바로 에러 처리 진행 */
}
/* 5단계: 에러 로깅 및 액션 */
mce_log(&m);
if (worst >= MCE_PANIC_SEVERITY && !lmce)
mce_panic("Fatal MCE", &m, msg);
else if (worst >= MCE_AR_SEVERITY)
kill_me_maybe(cb); /* 프로세스 kill 또는 페이지 오프라인 */
}
IA32_FEATURE_CONTROL.LMCE_ON을
설정하지 않으면 CPU가 LMCE를 지원하더라도 비활성화 상태로 남습니다.
MCE와 KVM 가상화
물리 서버에서 하드웨어 메모리 오류가 발생하면, 호스트 커널의 MCE 핸들러가 먼저 처리합니다. 해당 오류가 게스트 VM에 할당된 메모리 영역에 영향을 미치는 경우, KVM 하이퍼바이저는 게스트에도 MCE를 주입하여 게스트 OS가 적절히 대응할 수 있도록 합니다.
호스트→게스트 MCE 전파 흐름
VMEXIT와 #MC 인터셉트
Intel VT-x 및 AMD-V는 게스트 실행 중 #MC 예외가 발생하면 무조건 VMEXIT를 수행합니다. 호스트가 반드시 먼저 제어권을 확보하는 구조입니다.
| 항목 | Intel VT-x | AMD-V (SVM) |
|---|---|---|
| VMEXIT 사유 | EXIT_REASON_MCE_DURING_VMENTRY (41) | SVM_EXIT_MC (0x052) |
| 인터셉트 설정 | VMCS Exception Bitmap bit 18 | VMCB Intercept[INTERCEPT_MC] |
| 비활성화 가능 여부 | 불가 (항상 인터셉트) | 불가 (항상 인터셉트) |
| 호스트 핸들러 | handle_machine_check() | svm_handle_mce() |
KVM_X86_SET_MCE ioctl을 통한 게스트 MCE 주입
/* include/uapi/linux/kvm.h — MCE 주입 구조체 */
struct kvm_x86_mce {
__u64 status; /* MCi_STATUS 값 */
__u64 addr; /* MCi_ADDR 값 */
__u64 misc; /* MCi_MISC 값 */
__u64 mcg_status; /* MCG_STATUS 값 */
__u8 bank; /* MCA 뱅크 번호 */
__u8 pad1[7];
__u64 pad2[3];
};
/* QEMU에서의 MCE 주입 예시 */
struct kvm_x86_mce mce = {
.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
| MCI_STATUS_ADDRV | 0x0136, /* SRAR: Data Load */
.addr = guest_phys_addr,
.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV,
.bank = 1,
};
ioctl(vcpu_fd, KVM_X86_SET_MCE, &mce);
HWPoison과 게스트 메모리 영향
호스트가 물리 페이지를 HWPoison으로 마킹하면, KVM은 GPA↔HPA 매핑을 추적하여 해당 게스트에 MCE를 주입합니다. 동시에 EPT/NPT 테이블에서 매핑을 제거합니다.
| 단계 | 처리 주체 | 동작 |
|---|---|---|
| 1. 오류 감지 | 호스트 MCE 핸들러 | MCA 뱅크 읽기, 오류 분류 |
| 2. 페이지 격리 | 호스트 메모리 관리 | HWPoison 플래그 설정, 페이지 오프라인 |
| 3. QEMU 알림 | SIGBUS | QEMU 프로세스에 BUS_MCEERR_AO 시그널 전달 |
| 4. MCE 주입 | QEMU → KVM | GPA 계산 후 KVM_X86_SET_MCE ioctl 호출 |
| 5. EPT/NPT 언맵 | KVM MMU | 오염 페이지의 2차 매핑 제거 |
| 6. 게스트 처리 | 게스트 커널 | 게스트 내부 memory_failure() 실행 |
/* arch/x86/kvm/x86.c — MCE 브로드캐스트 및 주입 (개념 코드) */
void kvm_mce_broadcast(struct kvm *kvm, unsigned long pfn)
{
struct kvm_vcpu *vcpu;
gfn_t gfn = kvm_pfn_to_gfn(pfn);
kvm_for_each_vcpu(i, vcpu, kvm) {
struct kvm_x86_mce mce = {
.status = MCI_STATUS_VAL | MCI_STATUS_UC | 0x0136,
.addr = gfn << PAGE_SHIFT,
.bank = 1,
};
/* LMCE 지원 시 오류 소유 vCPU에만 주입 */
if (kvm_vcpu_supports_lmce(vcpu) && kvm_vcpu_owns_gfn(vcpu, gfn)) {
mce.mcg_status |= MCG_STATUS_LMCE_S;
kvm_inject_mce(vcpu, &mce);
break;
}
kvm_inject_mce(vcpu, &mce); /* LMCE 미지원: 브로드캐스트 */
}
}
라이브 마이그레이션과 MCE/HWPoison
| 시나리오 | 처리 방식 | 위험도 |
|---|---|---|
| HWPoison 페이지 존재 (게스트 미접근) | 해당 페이지 스킵, 대상에서 제로 페이지 매핑 | 중간 |
| HWPoison 페이지 존재 (게스트 이미 통보됨) | 페이지 스킵, 가상 MCE 뱅크 상태 전송 | 중간 |
| 마이그레이션 중 UCE 발생 | 마이그레이션 중단 또는 페이지 격리 후 계속 | 높음 |
| vCPU MCE 뱅크 상태 전송 | KVM_GET_MSRS / KVM_SET_MSRS로 직렬화 | 낮음 |
-cpu host,+mce,+mca,+lmce로 게스트에 MCE 기능을 노출합니다.
+lmce를 추가하면 로컬 MCE도 지원하여 가상화 오버헤드를 줄입니다.
APEI/GHES 통합
APEI(ACPI Platform Error Interface)는 플랫폼 펌웨어와 OS 간의 표준 에러 보고 인터페이스입니다. 서버 플랫폼에서는 펌웨어가 먼저 에러를 수집하고(Firmware-First), GHES(Generic Hardware Error Source)를 통해 OS에 전달하는 방식이 일반적입니다.
APEI 테이블 구성요소
| ACPI 테이블 | 역할 |
|---|---|
| HEST (Hardware Error Source Table) | 하드웨어 에러 소스 목록과 각 소스의 통지 방법 정의 |
| BERT (Boot Error Record Table) | 이전 부팅에서 발생한 치명적 에러를 다음 부팅에서 읽기 |
| ERST (Error Record Serialization Table) | 에러 레코드를 비휘발성 저장소에 영속 저장 |
| EINJ (Error Injection Table) | 테스트용 에러 주입 인터페이스 |
CPER (Common Platform Error Record)
GHES가 전달하는 에러 데이터는 CPER 포맷을 따릅니다. 각 에러 레코드에는 하나 이상의 Section이 포함되며, 섹션 타입별로 메모리 에러, PCIe 에러, 프로세서 에러 등을 구분합니다.
/* drivers/acpi/apei/ghes.c — GHES 처리 */
static int ghes_proc(struct ghes *ghes)
{
struct acpi_hest_generic_status *estatus;
estatus = ghes->estatus;
ghes_copy_tofrom_phys(estatus, ghes->buffer_paddr, ...);
/* CPER 섹션 순회 */
apei_estatus_for_each_section(estatus, gdata) {
guid_t *sec_type = (guid_t *)gdata->section_type;
if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
/* 메모리 에러 → memory_failure() */
ghes_handle_memory_failure(gdata, sev);
} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
/* PCIe 에러 */
ghes_handle_aer(gdata);
}
}
}
GHES 통지 방법
GHES는 에러를 OS에 알리는 여러 통지 메커니즘을 지원합니다. 통지 방법은 HEST 테이블에 정의되며, 에러 심각도에 따라 다른 경로를 사용합니다.
| 통지 유형 | 사용 시나리오 | 커널 핸들러 |
|---|---|---|
| SCI (System Control Interrupt) | CE, 비긴급 에러 | ACPI SCI → ghes_proc() |
| NMI | UC, 긴급 에러 | NMI 핸들러 → ghes_proc() |
| GPIO/IRQ | 플랫폼 고유 인터럽트 | IRQ 핸들러 → ghes_proc() |
| Polled | 폴링 기반 수집 | 타이머 → ghes_proc() |
| SEA (ARM) | 동기 외부 중단 | do_sea() → ghes_notify_sea() |
| SEI (ARM) | 비동기 에러 | do_serror() → ghes_notify_sei() |
BERT (Boot Error Record Table)
이전 부팅에서 치명적 에러로 시스템이 리셋된 경우, 펌웨어는 에러 정보를 BERT 영역에 저장합니다. 다음 부팅 시 커널이 BERT를 읽어 이전 에러를 dmesg에 출력합니다.
# BERT 데이터 확인
$ dmesg | grep BERT
[ 0.123456] BERT: Error records from previous boot:
[ 0.123457] [Firmware Error]: Hardware error from APEI Generic Hardware Error Source: 0
[ 0.123458] severity: fatal
# BERT ACPI 테이블 덤프 (디버그)
$ sudo cat /sys/firmware/acpi/tables/BERT | hexdump -C | head
ERST (Error Record Serialization Table)
ERST는 에러 레코드를 비휘발성 저장소(플래시, NVRAM)에 영속적으로 저장하는 인터페이스입니다.
커널은 /sys/firmware/acpi/tables/ERST를 통해 에러 레코드를 쓰고 읽을 수 있습니다.
이는 crash dump와 함께 사후 분석에 활용됩니다.
CPER 메모리 에러 섹션 구조
GHES가 전달하는 메모리 에러는 UEFI 표준의 CPER Memory Error Section
(CPER_SEC_PLATFORM_MEM) 포맷을 따릅니다.
각 필드에는 Validation Bits가 있어 해당 필드가 유효한지를 나타냅니다.
| 필드 | 크기 | Validation Bit | 설명 |
|---|---|---|---|
| Validation Bits | 8 바이트 | — | 이후 각 필드의 유효 여부를 나타내는 비트맵(Bitmap) |
| Error Status | 8 바이트 | Bit 0 | UEFI 에러 상태 코드 (에러 유형: 내부/버스/메모리 등) |
| Physical Address | 8 바이트 | Bit 1 | 에러가 발생한 물리 주소 — memory_failure(pfn) 호출에 사용 |
| Physical Address Mask | 8 바이트 | Bit 2 | 유효 비트 범위 (어느 범위까지 영향받는지) |
| Node | 2 바이트 | Bit 3 | NUMA 노드 번호 |
| Card | 2 바이트 | Bit 4 | 메모리 카드/라이저 번호 |
| Module | 2 바이트 | Bit 5 | DIMM 모듈 번호 |
| Bank | 2 바이트 | Bit 6 | DRAM 뱅크 번호 |
| Device | 2 바이트 | Bit 7 | DRAM 디바이스 (칩) 번호 |
| Row | 2 바이트 | Bit 8 | 에러 발생 행 주소 |
| Column | 2 바이트 | Bit 9 | 에러 발생 열 주소 |
| Bit Position | 2 바이트 | Bit 10 | 에러 발생 비트 위치 (싱글비트 CE 시) |
| Error Type | 1 바이트 | Bit 14 | 0=Unknown, 2=Multi-bit, 3=Single-symbol Chipkill, 8=Scrub CE 등 |
| Module Handle (SMBIOS) | 2 바이트 | Bit 11 | SMBIOS Type 17 핸들 — DIMM 식별 (dmidecode 연동) |
/* drivers/acpi/apei/ghes.c — CPER 메모리 섹션 처리 */
static void ghes_handle_memory_failure(
struct acpi_hest_generic_data *gdata, int sev)
{
struct cper_sec_mem_err *mem_err;
unsigned long pfn;
mem_err = acpi_hest_get_payload(gdata);
/* Validation Bits 확인: 물리 주소 유효한지 */
if (!(mem_err->validation_bits & CPER_MEM_VALID_PA))
return;
/* Physical Address to PFN 변환 */
pfn = mem_err->physical_addr >> PAGE_SHIFT;
/* Physical Address Mask 적용 (유효 범위 계산) */
if (mem_err->validation_bits & CPER_MEM_VALID_PA_MASK)
pfn &= ~(mem_err->physical_addr_mask >> PAGE_SHIFT);
/* severity에 따라 분기 */
if (sev == GHES_SEV_RECOVERABLE) {
/* Action Required: 즉시 memory_failure() */
memory_failure_queue(pfn, MF_ACTION_REQUIRED);
} else {
/* Corrected: soft offline (예방적 페이지 격리) */
memory_failure_queue(pfn, 0);
}
/* 트레이스 이벤트 발생 — rasdaemon/mcelog 수집용 */
trace_mc_event(err_type, ...,
mem_err->node, mem_err->card, mem_err->module,
mem_err->bank, mem_err->device,
mem_err->row, mem_err->column, ...);
}
GHES 에러 심각도 매핑
GHES의 에러 심각도(CPER Error Severity)는 커널 내부의 MCE severity 수준으로 매핑됩니다. 이 매핑은 에러 처리 액션(패닉, 프로세스 kill, 로깅 등)을 결정합니다.
| GHES Severity | CPER 값 | 커널 매핑 | 처리 액션 |
|---|---|---|---|
| Fatal | CPER_SEV_FATAL (0) |
MCE_PANIC_SEVERITY |
즉시 커널 패닉(Kernel Panic) — 복구 불가, 시스템 정지 |
| Recoverable | CPER_SEV_RECOVERABLE (1) |
MCE_AR_SEVERITY |
memory_failure() → 페이지 오프라인, 프로세스 SIGBUS kill |
| Corrected | CPER_SEV_CORRECTED (2) |
MCE_KEEP_SEVERITY |
로깅 + 임계치 기반 soft offline (예방적 격리) |
| Informational | CPER_SEV_INFORMATIONAL (3) |
MCE_NO_SEVERITY |
로깅만 — 통계 수집, 추세 분석용 |
dmesg | grep "Hardware error"로 GHES가 보고한 에러의
심각도를 확인할 수 있습니다. severity: fatal이면 시스템이 이미 패닉 상태이므로
crash dump에서 확인해야 합니다.
rasdaemon --record를 실행하면 CE/UCE 이벤트를 SQLite DB에 기록하여
시계열 추세를 분석할 수 있습니다.
Firmware-First vs OS-First 선택 기준
에러 처리의 주체를 펌웨어(Firmware-First)와 OS(OS-First) 중 어디로 설정할지는 플랫폼 유형과 운영 환경에 따라 달라집니다. 다음 표는 실무 기준의 선택 가이드입니다.
| 플랫폼 유형 | 권장 방식 | 에러 경로 | 근거 |
|---|---|---|---|
| 서버 (UEFI + BMC/IPMI) | Firmware-First | HW → SMI → 펌웨어 → GHES(SCI/NMI) → OS | 펌웨어가 SEL에 기록, BMC 원격 알림, DIMM 위치 식별 가능 |
| 데스크톱/워크스테이션 | OS-First | HW → #MC → do_machine_check() 직접 | 대부분 HEST 없음. OS가 MCA 뱅크를 직접 읽는 것이 유일한 경로 |
| VM 게스트 | 하이퍼바이저 위임 | HW → 호스트 MCE → 하이퍼바이저 → 가상 MCE 주입 | 게스트는 MCA 뱅크 접근 불가. KVM은 kvm_mce_broadcast()로 가상 MCE 주입 |
| ARM 서버 | Firmware-First (SDEI/SEA) | HW → EL3 펌웨어 → GHES(SEA/SDEI) → OS | ARM APEI 지원. RAS 확장(ARMv8.2+) 필요 |
| 임베디드/IoT | 플랫폼 고유 | 플랫폼별 인터럽트 / GPIO / 폴링 | ACPI/UEFI 없는 환경. DT 기반 에러 핸들러 또는 SoC 고유 드라이버 |
| 클라우드 인스턴스 | CSP 위임 | HW → CSP 펌웨어 → 라이브 마이그레이션 또는 가상 MCE | AWS/GCP/Azure가 HW 에러 관리. 게스트에 필터링된 에러만 전달 |
mce=no_cmci와 BIOS에서 Firmware-First 비활성화를 함께 설정하면
OS-First로 전환할 수 있지만, DIMM 위치 식별 등 펌웨어 기능을 잃게 됩니다.
EDAC 서브시스템 연동
EDAC(Error Detection And Correction)은 ECC 메모리 에러를 추적하고 물리적 DIMM 위치까지 디코딩하는 리눅스 커널 서브시스템입니다. MCE 또는 GHES로부터 에러 통지를 받아 어떤 DIMM의 어떤 행/열에서 에러가 발생했는지 알려줍니다.
주요 EDAC 드라이버
| 드라이버 | 플랫폼 | 모듈명 |
|---|---|---|
| i10nm_edac | Intel Ice Lake / Sapphire Rapids | i10nm_edac |
| skx_edac | Intel Skylake-SP / Cascade Lake | skx_edac |
| sb_edac | Intel Sandy/Ivy Bridge | sb_edac |
| amd64_edac | AMD K8/Fam10h~Zen4 | amd64_edac |
| ghes_edac | GHES 기반 (모든 UEFI 서버) | ghes_edac |
/* drivers/edac/edac_mc.c */
void edac_mc_handle_error(
const enum hw_event_mc_err_type type,
struct mem_ctl_info *mci,
const u16 error_count,
const unsigned long page_frame_number,
const unsigned long offset_in_page,
const unsigned long syndrome,
const int top_layer, /* 채널 */
const int mid_layer, /* DIMM */
const int low_layer, /* 행/열 */
const char *msg,
const char *other_detail)
{
/* EDAC 에러 카운터 증가 + tracepoint + sysfs 업데이트 */
trace_mc_event(type, msg, label, error_count, ...);
edac_inc_ce_error(mci, enable_per_layer_report, pos);
}
edac-util -s (요약), edac-util -r (DIMM별 상세),
ras-mc-ctl --summary (rasdaemon 연동 요약).
EDAC sysfs 인터페이스 상세
EDAC은 /sys/devices/system/edac/ 아래에 계층적 sysfs 인터페이스를 제공합니다.
메모리 컨트롤러(mc), 칩셋 검증(pci), 에러 카운터 등을 확인할 수 있습니다.
# EDAC 메모리 컨트롤러 계층
/sys/devices/system/edac/mc/
├── mc0/ # 메모리 컨트롤러 0
│ ├── ce_count # 총 CE 횟수
│ ├── ue_count # 총 UE 횟수
│ ├── ce_noinfo_count # 위치 정보 없는 CE
│ ├── ue_noinfo_count # 위치 정보 없는 UE
│ ├── seconds_since_reset # 카운터 리셋 후 경과 시간
│ ├── mc_name # 드라이버 이름 (예: "skx_edac")
│ ├── size_mb # 총 메모리 크기
│ ├── dimm0/ # DIMM 0
│ │ ├── dimm_ce_count # 이 DIMM의 CE 횟수
│ │ ├── dimm_dev_type # DDR4, DDR5 등
│ │ ├── dimm_edac_mode # SECDED, Chipkill 등
│ │ ├── dimm_label # DIMM 물리 위치 레이블
│ │ ├── dimm_location # 채널/슬롯/랭크
│ │ └── dimm_mem_type # Registered, Unbuffered
│ └── csrow0/ # 칩-셀렉트 행 0
│ ├── ch0_ce_count # 채널 0의 CE
│ └── ch1_ce_count # 채널 1의 CE
echo "DIMM_A1" > /sys/devices/system/edac/mc/mc0/dimm0/dimm_label로
물리 슬롯 이름을 설정하면 에러 메시지에 해당 레이블이 포함되어 DIMM 식별이 쉬워집니다.
ras-mc-ctl --register-labels를 사용하면 DMI/SMBIOS에서 자동 설정합니다.
EDAC 주소 디코딩 알고리즘
MCE/GHES로부터 전달받은 물리 주소를 실제 DIMM 위치로 변환하는 과정은 플랫폼마다 다릅니다. 주소 인터리빙, 채널 해싱, 랭크 인터리빙 등 메모리 컨트롤러 설정에 따라 동일한 물리 주소라도 다른 DIMM에 매핑될 수 있습니다.
| 디코딩 방식 | 플랫폼 | 디코드 소스 | 정확도 | 특징 |
|---|---|---|---|---|
| Intel ADXL | SKX / ICX / SPR | ACPI DSM 디코드 테이블 | 매우 높음 | 런타임 메모리 재매핑(sparing, mirroring failover) 자동 반영 |
| AMD UMC 레지스터 | Zen ~ Zen4+ | UMC PCI config space | 높음 | Data Fabric interleave 비트 해석, NPS 모드 반영 |
| GHES/CPER | 모든 UEFI 서버 | 펌웨어 사전 디코드 | 높음 (펌웨어 의존) | Physical Address Mask로 에러 범위 표시 가능 |
| 레거시 (sb_edac 등) | Sandy/Ivy Bridge | PCI SAD/TAD 레지스터 | 중간 | 수동 레지스터 파싱, 일부 인터리빙 모드 미지원 |
/* Intel ADXL 기반 디코딩 — drivers/edac/skx_common.c */
static int skx_adxl_decode(struct decoded_addr *res, bool error_in_1st_level_mem)
{
struct skx_dev *d;
int i, len = 0;
/* ADXL 라이브러리에 물리 주소 전달 → 디코드 테이블 참조 */
if (adxl_decode(res->addr, adxl_values)) {
edac_dbg(0, "Failed to decode addr 0x%llx\n", res->addr);
return -EINVAL;
}
/* 디코드 결과에서 소켓/IMC/채널/DIMM/랭크 추출 */
res->socket = (int)adxl_values[adxl_component_indices[INDEX_SOCKET]];
res->imc = (int)adxl_values[adxl_component_indices[INDEX_MEMCTRL]];
res->channel = (int)adxl_values[adxl_component_indices[INDEX_CHANNEL]];
res->dimm = (int)adxl_values[adxl_component_indices[INDEX_DIMM]];
res->rank = (int)adxl_values[adxl_component_indices[INDEX_RANK]];
/* mem_ctl_info에서 해당 컨트롤러/DIMM 구조체 검색 */
if (skx_adxl_get_mci(res))
return -ENODEV;
return 0;
}
/* AMD UMC 기반 디코딩 — drivers/edac/amd64_edac.c */
static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid,
u8 umc, u64 *sys_addr)
{
/* UMC 정규화 주소 → 시스템 물리 주소 변환 */
/* Data Fabric interleave 비트, NPS 모드, CS base/mask 참조 */
get_df_interleave(nid, umc, &intlv_num_chan, &intlv_addr_sel);
/* ... */
}
EDAC과 rasdaemon 연동
rasdaemon은 커널 EDAC 서브시스템이 발생시키는 ras:mc_event tracepoint를
구독하여 메모리 에러를 SQLite 데이터베이스에 기록합니다. 이를 통해 장기적인 DIMM 건강 추이 분석과
사전 교체 판단이 가능합니다.
| 데이터 흐름 단계 | 구성 요소 | 설명 |
|---|---|---|
| 1. 에러 감지 | HW → MCE/GHES | ECC 에러 발생 → MCA bank 또는 GHES CPER 기록 |
| 2. EDAC 처리 | edac_mc_handle_error() | 물리 주소 → DIMM 위치 디코딩, ce_count/ue_count 증가 |
| 3. Tracepoint 발행 | trace_mc_event() | ras:mc_event tracepoint로 에러 정보 브로드캐스트 |
| 4. rasdaemon 수집 | rasdaemon → tracefs | /sys/kernel/tracing/events/ras/mc_event 구독 |
| 5. DB 기록 | SQLite | /var/lib/rasdaemon/ras-mc_event.db에 영구 저장 |
# rasdaemon 설치 및 서비스 시작
$ sudo apt install rasdaemon # Debian/Ubuntu
$ sudo dnf install rasdaemon # RHEL/Fedora
$ sudo systemctl enable --now rasdaemon
# rasdaemon이 trace event를 수신하는지 확인
$ sudo cat /sys/kernel/tracing/events/ras/mc_event/enable
1
# SQLite DB 위치 확인
$ ls -la /var/lib/rasdaemon/ras-mc_event.db
DIMM 건강 분석 SQL 쿼리: rasdaemon의 SQLite DB를 직접 쿼리하면 EDAC sysfs보다 훨씬 풍부한 시계열 분석이 가능합니다.
-- 최근 7일간 DIMM별 CE 추이
SELECT
date(timestamp, 'unixepoch') AS day,
mc, top_layer AS channel, mid_layer AS dimm,
COUNT(*) AS ce_count
FROM mc_event
WHERE err_type = 'Corrected'
AND timestamp > strftime('%s', 'now', '-7 days')
GROUP BY day, mc, channel, dimm
ORDER BY day, ce_count DESC;
-- 에러가 가장 많은 상위 5개 DIMM
SELECT
mc, top_layer AS channel, mid_layer AS dimm,
SUM(CASE WHEN err_type = 'Corrected' THEN error_count ELSE 0 END) AS total_ce,
SUM(CASE WHEN err_type = 'Uncorrected' THEN error_count ELSE 0 END) AS total_ue,
MIN(datetime(timestamp, 'unixepoch')) AS first_error,
MAX(datetime(timestamp, 'unixepoch')) AS last_error
FROM mc_event
GROUP BY mc, channel, dimm
ORDER BY total_ce + total_ue * 1000 DESC
LIMIT 5;
-- CE → UE 에스컬레이션 탐지: CE 급증 후 UE 발생한 DIMM
SELECT ue.mc, ue.top_layer AS channel, ue.mid_layer AS dimm,
ce_before.ce_count AS ce_before_ue,
datetime(ue.timestamp, 'unixepoch') AS ue_time
FROM mc_event ue
JOIN (
SELECT mc, top_layer, mid_layer, COUNT(*) AS ce_count,
MAX(timestamp) AS last_ce
FROM mc_event
WHERE err_type = 'Corrected'
GROUP BY mc, top_layer, mid_layer
) ce_before ON ue.mc = ce_before.mc
AND ue.top_layer = ce_before.top_layer
AND ue.mid_layer = ce_before.mid_layer
WHERE ue.err_type = 'Uncorrected'
AND ce_before.ce_count > 10
ORDER BY ce_before.ce_count DESC;
RAS 유저스페이스 도구
MCE 이벤트를 수집, 디코딩, 분석하는 유저스페이스 도구입니다.
mcelog은 레거시이고, 현재는 rasdaemon이 표준입니다.
도구 비교
| 도구 | 상태 | 데이터 소스 | 저장 | 특징 |
|---|---|---|---|---|
| mcelog | 레거시 (deprecated) | /dev/mcelog | 로그 파일 | 독립 데몬, 간단한 디코딩. 최신 커널에서 /dev/mcelog 제거 추세 |
| rasdaemon | 현재 표준 | perf tracepoint | SQLite DB | 다양한 에러 소스 통합(MCE/EDAC/GHES/AER/CXL), 구조화 로깅 |
| ras-mc-ctl | rasdaemon 포함 | rasdaemon DB | - | EDAC 요약, 에러 카운트, DIMM 레이블 관리 |
| edac-util | 활성 | sysfs EDAC | - | EDAC sysfs 인터페이스의 사용자 친화적 래퍼 |
rasdaemon 설정 및 사용
# 설치 (RHEL/CentOS)
$ sudo yum install rasdaemon
# 서비스 시작
$ sudo systemctl enable --now rasdaemon
# 에러 요약 조회
$ sudo ras-mc-ctl --summary
Memory controller events summary:
Corrected on DIMM Label(s): CPU_SrcID#0_MC#0_Chan#0_DIMM#0 location: 0:0:0:-1 errors: 42
No Uncorrected errors.
# SQLite DB 직접 조회
$ sudo sqlite3 /var/lib/rasdaemon/ras-mc_event.db \
"SELECT timestamp, err_type, mc, top_layer, mid_layer, msg FROM mc_event ORDER BY id DESC LIMIT 10;"
# MCE 이벤트 조회
$ sudo ras-mc-ctl --errors
$ sudo sqlite3 /var/lib/rasdaemon/ras-mc_event.db \
"SELECT timestamp, mcgstatus, status, addr, bank FROM mce_record ORDER BY id DESC LIMIT 5;"
systemd-journald도 MCE 이벤트를
journalctl -k -g "mce:"로 필터링할 수 있지만,
구조화된 분석에는 rasdaemon의 SQLite DB가 훨씬 효과적입니다.
rasdaemon 아키텍처
rasdaemon은 커널의 perf_event tracepoint를 구독하여 에러 이벤트를 수집합니다.
ras:mc_event, ras:aer_event, ras:mce_record,
ras:cxl_poison 등의 tracepoint를 모니터링하며, 수집된 데이터를 SQLite 데이터베이스에 저장합니다.
# rasdaemon이 구독하는 tracepoint 확인
$ sudo ls /sys/kernel/debug/tracing/events/ras/
aer_event cxl_general_media cxl_poison mc_event
arm_event cxl_memory_module extlog_mem_event mce_record
cxl_dram cxl_overflow non_standard_event
# 실시간 트레이스 확인 (디버그 용)
$ sudo cat /sys/kernel/debug/tracing/events/ras/mc_event/enable
1
$ sudo cat /sys/kernel/debug/tracing/trace_pipe | grep mc_event
rasdaemon vs mcelog 마이그레이션
기존 mcelog 사용 환경에서 rasdaemon으로 전환할 때 주의할 점:
/dev/mcelog인터페이스는 커널 6.x에서 제거 추세 — mcelog 사용 불가해질 수 있음- rasdaemon은 모든 RAS 소스(MCE/EDAC/AER/CXL/ARM)를 통합 관리
- SQLite 기반이므로 SQL 쿼리로 유연한 분석 가능
- ABRT(Automatic Bug Reporting Tool)와 연동하여 자동 리포팅 가능
# mcelog → rasdaemon 전환
$ sudo systemctl stop mcelog
$ sudo systemctl disable mcelog
$ sudo systemctl enable --now rasdaemon
# 이전 mcelog 데이터는 수동 마이그레이션 필요
# rasdaemon은 tracepoint 기반이므로 /dev/mcelog 비사용
ras-mc-ctl --summary— 전체 에러 요약ras-mc-ctl --errors— 에러 목록 상세ras-mc-ctl --mainboard— 메인보드 정보ras-mc-ctl --layout— DIMM 레이아웃ras-mc-ctl --status— EDAC 드라이버 상태
CXL 메모리 에러와 MCE
CXL(Compute Express Link) 메모리 장치는 기존 DRAM과는 다른 에러 보고 경로를 사용합니다.
CXL 프로토콜 에러는 PCIe AER 또는 GHES를 통해 보고되며, 궁극적으로 memory_failure()로 이어집니다.
CXL 에러 경로
| 에러 유형 | 보고 경로 | 커널 처리 |
|---|---|---|
| CXL.mem 프로토콜 에러 | GHES → CPER CXL 섹션 | cxl_cper_handle_prot_err() |
| CXL 미디어 에러 (UC) | GHES → CPER Memory → MCE notifier | memory_failure() → HWPoison |
| CXL 미디어 에러 (CE) | CXL 이벤트 로그 / GHES | EDAC/rasdaemon 통계 |
| CXL Poison List | CXL mailbox 명령 | cxl_mem_get_poison() → 선제 오프라인 |
/* drivers/cxl/core/mbox.c — Poison List 조회 */
int cxl_mem_get_poison(struct cxl_memdev *cxlmd,
u64 offset, u64 len,
struct cxl_region *cxlr)
{
struct cxl_mbox_poison_out *po;
int rc;
rc = cxl_internal_send_cmd(mds, &mbox_cmd);
if (rc)
return rc;
/* Poison 리스트 엔트리 순회 */
for (i = 0; i < le16_to_cpu(po->count); i++) {
trace_cxl_poison(cxlmd, cxlr, &po->record[i],
po->flags, po->overflow_ts, CXL_POISON_TRACE_LIST);
}
return 0;
}
GPU/가속기 MCE 연쇄 에러 분석
현대 데이터센터와 AI/ML 학습 클러스터에서 GPU 및 하드웨어 가속기에서 발생하는 하드웨어 에러(ECC 오류, DMA 장애 등)는 PCIe AER을 거쳐 CPU의 IIO MCA 뱅크에 기록되고, 최종적으로 MCE로 전파됩니다. 이 연쇄 과정을 정확히 이해해야 장애 원인을 신속하게 격리할 수 있습니다.
GPU 에러 연쇄 전파 경로
Xid 에러 코드와 MCE 상관관계
| Xid 코드 | 설명 | MCE 전파 여부 | IIO MSCOD | 증상 | 조치 |
|---|---|---|---|---|---|
Xid 48 | DBE (Double Bit Error) | 높음 (UE) | 0x0136 | 학습 NaN, GPU 행(hang) | GPU 교체, nvidia-smi -r |
Xid 63 | Row remapping 실패 | 중간 | 0x0136 | ECC 에러 급증 후 remapping 실패 | GPU 교체 예약 |
Xid 79 | GPU 복구 실패 | 높음 | 0x0402 | GPU 응답 없음 | 노드 재시작 필요 |
Xid 94 | Contained ECC 에러 | 낮음 (CE) | - | 정상 동작, CE 카운터 증가 | 카운터 모니터링 |
Xid 95 | Uncontained ECC 에러 | 높음 (UE) | 0x0136 | 프로세스 강제 종료, 패닉 가능 | 즉시 GPU 교체 |
GPU Xid와 MCE 뱅크/IIO 스택 상관분석
# GPU의 PCIe 위치 확인
$ lspci -d 10de: -vvs 41:00.0
41:00.0 3D controller: NVIDIA Corporation A100
UESta: 0x00004000 # ← Completion Abort 비트
# MCE 로그에서 Bank 번호 → IIO 스택 역추적
# Bank 6 = IIO Stack 1 (Skylake-SP 기준)
mce: [Hardware Error]: CPU 24 Bank 6:
STATUS 0xbc20000001000136
# MSCOD=0x0136: Received Parity Error
# UC=1, PCC=1 → 프로세서 컨텍스트 손상
# GPU ECC 상태 확인
$ nvidia-smi --query-gpu=ecc.errors.corrected.volatile.total,\
ecc.errors.uncorrected.volatile.total,retired_pages.count --format=csv
GPU 관련 MCE 패턴 종합표
| 패턴 | MSCOD | 증상 | 근본 원인 | 조치 |
|---|---|---|---|---|
| GPU ECC UE | 0x0136 | 학습 NaN, Xid 48/95 | HBM/GDDR 결함 | GPU 교체 |
| Completion Timeout | 0x0402 | GPU 행, 디바이스 소실 | GPU 펌웨어/PCIe 링크 | PCIe 리트레이닝, FLR |
| Poisoned TLP | 0x0136 | DMA 읽기 실패 | 호스트 메모리 UCE | DIMM 교체 |
| VT-d Translation Fault | 0x0010 | IOMMU 폴트 | 드라이버/IOMMU 설정 오류 | iommu=pt, 드라이버 업데이트 |
| NVLink Degradation | 0x0136 | Xid 74, 성능 저하 | NVLink 케이블/NVSwitch 결함 | 케이블 교체 |
AI/ML 학습 클러스터 MCE 영향
/* 분산 학습 중 단일 GPU ECC UE 발생 시 타임라인 */
T+0ms GPU 3의 HBM에서 DBE(Double Bit Error) 발생
T+0.1ms NVIDIA 드라이버 Xid 48 기록
T+0.5ms PCIe AER Uncorrectable Error 전파
T+1ms IIO MCA Bank 7에 기록 → MCE 핸들러 호출
T+2ms memory_failure() 호출 — 오염 페이지 오프라인
T+100ms GPU CUDA 컨텍스트 무효화
T+200ms PyTorch/NCCL: Rank 3 통신 실패 감지
T+30s NCCL timeout → 전체 AllReduce 데드락
T+60s 모든 Rank 학습 중단 → 마지막 체크포인트로 롤백 필요
// 영향: 8-GPU 노드 1대의 GPU 1개 장애
// → 전체 클러스터 학습 중단
클러스터 운영 권장사항
- 사전 모니터링:
rasdaemon과nvidia-smi연동, CE 급증 시 자동 노드 드레인 - 체크포인트 전략: CE 급증 시 체크포인트 주기 단축
- tolerant 설정:
tolerant=1로 복구 가능 MCE 패닉 방지 - GPU 헬스체크:
dcgmi diag -r 3(NVIDIA DCGM)으로 주기적 GPU 진단 - NUMA/IIO 인지 배치: GPU와 동일 소켓/IIO 스택의 DIMM CE 감시 → NUMA 토폴로지 기반 장애 격리
아키텍처별 하드웨어 에러 처리 비교
MCE는 x86 고유 개념이지만, ARM, RISC-V, POWER 등 모든 서버급 아키텍처에 유사한 하드웨어 에러 보고 메커니즘이 있습니다. 각 아키텍처의 에러 감지/보고/복구 설계를 비교하면 하드웨어 RAS(Reliability, Availability, Serviceability)의 본질적인 문제와 해결 패턴을 더 깊이 이해할 수 있습니다.
ARM RAS Extension 상세
| 메커니즘 | 유형 | 특성 |
|---|---|---|
| SEA (Synchronous External Abort) | 동기 | x86 SRAR과 유사. 데이터 소비 중 UC 에러. memory_failure(MF_ACTION_REQUIRED) |
| SEI/SError | 비동기 | x86 SRAO/UCNA와 유사. 비동기적 외부 에러. 복구 가능성은 ESR_ELx로 판단 |
| SDEI (Software Delegated Exception Interface) | 펌웨어 위임 | Firmware-First 모델. EL3/Secure World에서 에러 수집 → GHES로 전달 |
| ERRn 레지스터 | RAS Extension v1 | ERR<n>STATUS, ERR<n>ADDR, ERR<n>MISC0/1 — x86 MCA 뱅크와 구조적으로 유사 |
/* arch/arm64/mm/fault.c — SEA 처리 */
static int do_sea(unsigned long far, unsigned long esr,
struct pt_regs *regs)
{
/* GHES SEA handler에 먼저 전달 */
if (ghes_notify_sea() >= 0)
return 0; /* GHES가 처리 완료 */
/* GHES가 처리하지 못하면 프로세스 킬 */
arm64_notify_die("Synchronous External Abort", regs, esr);
return 0;
}
ARM RAS Extension v1 레지스터
ARM RAS Extension v1은 x86 MCA 뱅크와 유사한 에러 레코드 레지스터를 정의합니다. 각 에러 노드(Error Record)는 ERRn 접두사를 가진 시스템 레지스터 세트로 구성됩니다.
| 레지스터 | 역할 | x86 MCA 대응 |
|---|---|---|
| ERRIDR_EL1 | 에러 레코드 수 및 기능 확인 | MCG_CAP |
| ERRnSTATUS | 에러 상태 (V/UE/CE/OF 비트) | MCi_STATUS |
| ERRnADDR | 에러 발생 물리 주소 | MCi_ADDR |
| ERRnMISC0/1 | 추가 에러 정보 | MCi_MISC |
| ERRnCTLR | 에러 보고 활성화 제어 | MCi_CTL |
| ERRnFR | Feature Register — 지원 기능 | 해당 없음 |
RISC-V 하드웨어 에러 표준화 현황
RISC-V는 아직 하드웨어 에러 보고에 대한 공식 표준이 확립되지 않았습니다. 현재 진행 중인 표준화 작업과 벤더 독자 구현이 혼재합니다:
- SBI MPXY (Message Proxy Extension) — 펌웨어-OS 간 에러 메시지 전달
- SiFive S76 — BEU(Bus Error Unit) + 커스텀 인터럽트
- T-Head C910 — 플랫폼 고유 CSR 기반 에러 보고
- UEFI 서버 — ACPI GHES 경유 (x86과 동일한 최종 경로)
IBM POWER EEH (Enhanced Error Handling) 상세
IBM POWER 아키텍처는 PCI I/O 에러 처리를 위해 EEH(Enhanced Error Handling)를 제공합니다. x86의 AER과 유사한 역할이지만, 하이퍼바이저 계층을 통한 격리와 자동화된 단계적 복구에 초점을 맞춥니다.
EEH 동작 메커니즘
- 에러 감지: PHB(PCI Host Bridge)가 PCI/PCIe 버스 에러를 감지
- PE 격리: 에러 발생 PE(Partitionable Endpoint)를 즉시 I/O 격리(frozen) 상태로 전환. MMIO 읽기는 모두
0xFF반환 - OPAL 통지: 하이퍼바이저가 OS에 EEH 이벤트 전달
- 단계적 복구: I/O 재시도 → 슬롯 리셋 → PHB 리셋 → 실패 시 PE 영구 비활성화 (최대 5회)
/* arch/powerpc/kernel/eeh_driver.c — EEH 복구 콜백 */
static int eeh_report_error(struct eeh_dev *edev, struct eeh_pe *pe)
{
struct pci_driver *drv = eeh_pcid_get(edev->pdev);
if (!drv || !drv->err_handler || !drv->err_handler->error_detected)
return PCI_ERS_RESULT_NONE;
/* 드라이버에 에러 통지 — 복구 가능 여부 응답 */
return drv->err_handler->error_detected(edev->pdev, edev->state);
}
POWER vs x86 vs ARM 에러 처리 비교
| 비교 항목 | IBM POWER | x86 (Intel/AMD) | ARM (Neoverse) |
|---|---|---|---|
| 에러 예외 | Machine Check (벡터 0x200) | #MC (벡터 18) | SEA / SError |
| 에러 레지스터 | SRR1, DSISR, OPAL 에러 로그 | MCA 뱅크 (MCi_STATUS/ADDR/MISC) | ERRn 레지스터 (RAS Extension) |
| PCIe I/O 에러 | EEH (PHB 기반 PE 격리) | AER + IIO MCA | SMMU + AER + GHES |
| 에러 격리 단위 | PE (Partitionable Endpoint) | PCIe 장치/기능 단위 | SMMU Stream ID 기반 |
| 복구 메커니즘 | EEH 단계적 복구 (최대 5회) | AER 드라이버 콜백 + memory_failure() | GHES → memory_failure() |
| 펌웨어 역할 | OPAL/skiboot (필수, 에러 수집/필터링) | APEI/GHES (선택적) | SDEI → GHES (주요 경로) |
| Poison/SUE | SUE (Special Uncorrectable Error) | Viral Mode / Data Poisoning | Poison 비트 (CMN-700) |
| CPU 동기화 | Hypervisor 위임 (FWNMI) | Monarch 랑데부 | 코어별 독립 처리 |
| 메모리 에러 복구 | memory_failure() + HWPoison (OPAL 경유) | memory_failure() + HWPoison | memory_failure() + HWPoison (GHES 경유) |
| 가상화 환경 | FWNMI — 하이퍼바이저가 게스트에 에러 주입 | LMCE + vMCE (KVM) | SDEI → 게스트 GHES |
ARM RAS Extension v1/v1.1
ARMv8.2-A에서 도입된 FEAT_RAS(RAS Extension v1)는 하드웨어 에러 감지와 보고를 표준화한 것으로, x86 MCA에 대응하는 ARM의 공식 RAS 프레임워크입니다. ARMv8.4-A에서 FEAT_RASv1p1(v1.1)으로 강화되었습니다.
ARM FEAT_RAS vs FEAT_RASv1p1 차이
| 기능 | FEAT_RAS (v1, ARMv8.2) | FEAT_RASv1p1 (v1.1, ARMv8.4) |
|---|---|---|
| 에러 레코드 | ERRnSTATUS/ADDR/MISC0/1/CTLR/FR | 동일 + ERRnPFGF, ERRnPFGCTL (Fault Injection) |
| DoubleFault | 미지원 | FEAT_DoubleFault — 에러 핸들링 중 2번째 에러 시 EL3로 라우팅 |
| 에러 분류 세분화 | CE/UC 2단계 | CE/DE(Deferred)/UC/UEO(Uncorrected Error Overwritten) 4단계 |
| Deferred Error | 기본 지원 | DE(Deferred Error) 전용 상태 비트 표준화, AMD SMCA Deferred와 유사 |
| SError 분류 | AET 필드 (2비트) | IESB(Implicit Error Synchronization Barrier) 추가 — 에러 동기화 강화 |
| Fault Injection | 미지원 | ERRnPFG* 레지스터로 소프트웨어 에러 주입 가능 (x86 EINJ 대응) |
| Timestamp | 미지원 | ERRnTS 레지스터 — 에러 타임스탬프 기록 (Generic Timer 기반) |
ARM Neoverse 플랫폼별 RAS 구현
ARM Neoverse는 서버/인프라용 CPU IP로, 세대별로 RAS 기능이 다릅니다. AWS Graviton, Ampere Altra, Microsoft Cobalt 등 클라우드 서버에 사용됩니다.
| 코어 IP | 사용 제품 | RAS 버전 | 에러 노드 수 | 주요 RAS 기능 |
|---|---|---|---|---|
| Neoverse N1 | Graviton2, Altra | FEAT_RAS v1 | 코어당 ~10개 | L1/L2 ECC, 캐시 라인(Cache Line) 무효화 복구, SEA/SError, GHES 연동 |
| Neoverse V1 | Graviton3 | FEAT_RAS v1.1 | 코어당 ~14개 | V1 추가: DoubleFault, Deferred Error 표준화, 256-bit SVE 에러 감지 |
| Neoverse N2 | Graviton4, Cobalt 100 | FEAT_RAS v1.1 | 코어당 ~16개 | N2 추가: MPAM RAS 통합, CMN-700 인터커넥트 에러 강화 |
| Neoverse V2 | Graviton4(고성능 변종) | FEAT_RAS v1.1 | 코어당 ~18개 | V2 추가: 128-bit SME 에러 감지, 향상된 에러 신드롬 |
| Neoverse V3/N3 | 차세대 | FEAT_RAS v2 (예정) | 확장 | FEAT_RASv2: 에러 카운터 표준화, 더 정교한 에러 분류 |
ARM CMN (Coherent Mesh Network) 에러
ARM 서버 SoC의 인터커넥트인 CMN-600/CMN-700은 x86의 Mesh/CHA에 대응합니다. CMN 에러는 별도의 에러 노드를 통해 보고되며, GHES 경로로 OS에 전달됩니다.
| CMN 에러 유형 | x86 대응 | 설명 |
|---|---|---|
| SN(Slave Node) ECC Error | CHA LLC ECC | System Level Cache(SLC) ECC 에러 |
| HN-F(Home Node Filter) Error | CHA Snoop Filter | 코히런스 디렉토리 에러 |
| XP(Cross Point) Error | Mesh Data Fabric | 메시 라우팅 노드 에러 |
| RN-D(Request Node DMA) Error | IIO DMA | DMA 에이전트 에러 |
| SBSX(SB SLC Exclusive) Error | M2M | 메모리 인터페이스 노드 에러 |
Intel vs AMD MCA 아키텍처 차이 종합
x86 내에서도 Intel과 AMD는 MCA 구현에 상당한 차이가 있습니다. 같은 "MCA"라도 뱅크 식별 방식, 에러 분류, 복구 메커니즘, 레지스터 확장이 다릅니다.
| 비교 항목 | Intel (Xeon Scalable) | AMD (EPYC, Zen) |
|---|---|---|
| 뱅크 식별 | 고정 번호 (Bank 0=IFU, Bank 4=PCU 등). CPU 세대마다 번호-유닛 매핑이 변경됨. SDM/에러코드 레퍼런스 참조 필요 | MCi_IPID 레지스터의 HWID+MCATYPE으로 런타임 식별. Zen 1~5 모두 동일 디코딩 코드 사용 가능 |
| 레지스터 수 | 뱅크당 5개 (CTL, STATUS, ADDR, MISC, CTL2) | 뱅크당 최대 10개 (+IPID, SYND, DESTAT, DEADDR, CONFIG) |
| 에러 분류 | SRAR/SRAO/UCNA (STATUS 비트 조합으로 분류). MCG_CAP.MCG_SER_P=1이면 복구 가능 | Deferred Error가 별도 유형으로 존재. SRAR/SRAO 외에 Deferred(잠재적 에러) 독립 처리 |
| CE 보고 | CMCI (MCi_CTL2.CMCI_EN, 임계값 기반). CMCI 스톰 시 자동 폴링 전환 | Threshold Interrupt (APIC LVT). 뱅크별 CE 임계값 설정. CMCI 호환 모드도 지원 |
| 브로드캐스트 제어 | LMCE (Local MCE) — 특정 에러를 해당 CPU에만 전달. 다른 CPU의 VMEXIT 방지 | LMCE 미지원. 모든 UC MCE는 브로드캐스트. (Zen 5+에서 지원 가능성) |
| ECC 신드롬 | MCi_MISC에 제한적 신드롬 정보 | MCi_SYND 전용 레지스터에 상세 신드롬 (에러 비트 위치, ECC 패턴) |
| Deferred Error | 없음 (patrol scrub UC → 즉시 MCE 또는 SRAO) | DESTAT/DEADDR로 잠재 에러 기록 → 소프트웨어가 선제 오프라인 가능 |
| I/O 에러 | IIO 뱅크 (MSCOD로 PCIe 에러 세분화). AER과 이중 보고 | NBIO/PCIE 뱅크 (SMCA IPID로 식별). PCIe AER은 별도 |
| 인터커넥트 | UPI (Bank 5~6). 소켓 간 CRC/Protocol Error | xGMI PCS/PHY, GMI PCS/PHY, WAFL. 다이 간/소켓 간 링크 별도 뱅크 |
| 메모리 컨트롤러 | iMC(Bank 13~20) + M2M(Bank 9~12). ADDDC, Mirroring, Sparing | UMC (SMCA_UMC). On-die ECC(DDR5), Post Package Repair(PPR) |
| 보안 프로세서 | 해당 없음 (별도 MCA 뱅크 없음) | PSP(Platform Security Processor) 뱅크 — PSP 펌웨어 에러 보고 |
| Poison 전파 | Viral Mode (BIOS 설정) — Containment/Propagation 선택 | Data Poisoning 기본 지원 — Deferred Error로 선제 대응 가능 |
| 커널 소스 | arch/x86/kernel/cpu/mce/intel.c, severity.c |
arch/x86/kernel/cpu/mce/amd.c, smca.c |
- 에러 로그 분석: Intel은
CPU N Bank M MSCOD=...를 모델별 매뉴얼로 해석. AMD는Scalable MCA: IPID=... HWID=0xB0 → Load Store로 자동 식별 - 메모리 에러 대응: Intel은 ADDDC+Patrol Scrub UC → MCE → memory_failure(). AMD는 Patrol Scrub → Deferred Error → 소프트웨어 선제 오프라인 (UC MCE 없이 페이지 격리 가능)
- PCIe 에러: Intel IIO MSCOD로 CTO/Poison/LinkDown 세분화. AMD NBIO/PCIE SMCA는 IPID로 식별하지만 세부 에러 코드는 덜 세분화
- 디버깅 도구: 둘 다
rasdaemon이 자동 디코딩. Intel은mcelog도 사용 가능. AMD는rasdaemon이 SMCA를 더 잘 지원
크로스 아키텍처 에러 처리 종합 비교
x86(Intel/AMD), ARM, RISC-V, POWER를 포괄하는 종합 비교표입니다. 서버 플랫폼을 설계하거나 다중 아키텍처 환경을 운영할 때 참고합니다.
| 비교 항목 | x86 Intel | x86 AMD | ARM (Neoverse) | RISC-V | POWER (IBM) |
|---|---|---|---|---|---|
| 에러 예외 | #MC (벡터 18) | #MC (벡터 18) | SEA / SError | 플랫폼 종속 | Machine Check (0x200) |
| 에러 레지스터 | MCA 뱅크 (MSR) | SMCA 뱅크 (MSR + IPID) | ERRn 레지스터 | CSR (벤더 독자) | SRR1 + DSISR |
| 뱅크/노드 수 | ~48~100개 | ~32~64개 | 코어당 ~10~18개 | 미표준화 | ~20~40개 |
| CE 인터럽트 | CMCI | Threshold Int. | Fault IRQ / GHES | 플랫폼 종속 | CE 핸들러 |
| Firmware-First | APEI/GHES (선택) | APEI/GHES (선택) | SDEI → GHES (주요 경로) | SBI + GHES (개발 중) | OPAL/skiboot (필수) |
| 에러 동기화 | Monarch 랑데부 | Monarch 랑데부 | 코어별 독립 처리 | 미정의 | Hypervisor 위임 |
| 메모리 복구 | memory_failure() + HWPoison | memory_failure() + HWPoison | memory_failure() + HWPoison | memory_failure() (GHES 경유 시) | memory_failure() + HWPoison |
| Deferred Error | 없음 | DESTAT/DEADDR | FEAT_RASv1p1 DE | 없음 | 없음 |
| 에러 주입 | EINJ (ACPI) | EINJ (ACPI) | ERRnPFG (v1.1) | 벤더 종속 | OPAL inject |
| Poison 전파 | Viral Mode | Data Poisoning | Poison 비트 (CMN) | 미정의 | SUE (Special Uncorrectable Error) |
| PCIe 에러 | IIO MCA + AER | NBIO/PCIE SMCA + AER | SMMU + AER + GHES | AER + 플랫폼 | EEH (Enhanced Error Handling) |
| 메모리 ECC | SDDC/ADDDC/Mirroring | On-die ECC + PPR | 플랫폼 종속 | 플랫폼 종속 | Chipkill + 동적 Sparing |
| EDAC 드라이버 | i10nm_edac, skx_edac | amd64_edac | 플랫폼별 (thunderx2, xgene 등) | 없음/개발 중 | pasemi_edac 등 |
| 유저스페이스 도구 | rasdaemon, mcelog | rasdaemon | rasdaemon (GHES 경유) | 없음/개발 중 | opal-prd |
| 성숙도 | 매우 높음 (20년+) | 높음 (Zen 이후 ~8년) | 중간 (서버 진출 ~6년) | 낮음 (초기) | 매우 높음 (30년+) |
IBM POWER의 EEH (Enhanced Error Handling)
IBM POWER 아키텍처의 EEH는 PCIe 에러 처리에서 x86 AER/IIO MCA보다 진보된 모델입니다. x86에서는 PCIe UC 에러 시 장치가 사실상 죽지만, POWER EEH는 자동 복구 단계를 제공합니다.
| EEH 복구 단계 | 동작 | x86 대응 |
|---|---|---|
| 1. I/O Freeze | 에러 장치의 MMIO/DMA를 냉동 (데이터 오염 차단) | AER pci_channel_io_frozen 유사 |
| 2. MMIO Detect | 냉동된 장치의 MMIO 읽기가 all-1s(0xFFFFFFFF) 반환 → 드라이버가 감지 | x86에서도 동일 패턴이지만 표준화되지 않음 |
| 3. Slot Reset | PHB(PCI Host Bridge) 레벨 리셋 → 장치 재초기화 | AER pcie_do_recovery() 유사하지만 더 강력 |
| 4. Resume | 드라이버의 resume 콜백(Callback)으로 정상 동작 복귀 | AER mmio_enabled/resume 콜백 |
| 5. Full Reset | 위 단계 실패 시 PE(Partitionable Endpoint) 전체 리셋 | x86에는 대응 없음 (보통 재부팅) |
- 메모리 RAS 최우선: Intel ADDDC + Mirroring 또는 IBM POWER Chipkill + 동적 Sparing
- PCIe 복구 최우선: IBM POWER EEH가 가장 성숙. x86은 AER+IIO MCA 조합으로 대응
- Deferred Error (선제 대응): AMD SMCA 또는 ARM RAS v1.1이 Deferred Error를 네이티브 지원
- 다중 아키텍처 운영: GHES/CPER 기반 통합 모니터링이 핵심. rasdaemon이 x86/ARM 모두 지원하므로 통합 대시보드 구축 가능
- ARM 서버 주의: ARM은 Firmware-First(SDEI→GHES)가 주요 경로이므로, 펌웨어(TF-A/UEFI) 품질이 RAS 성능에 직접 영향. Intel/AMD의 OS-First 모델보다 펌웨어 의존도 높음
RISC-V 하드웨어 에러 표준화 현황
RISC-V는 아직 하드웨어 에러 보고에 대한 공식 표준이 확립되지 않았습니다. 현재 진행 중인 표준화 작업과 벤더 독자 구현이 혼재합니다:
- SBI MPXY (Message Proxy Extension) — 펌웨어-OS 간 에러 메시지 전달
- SiFive S76 — BEU(Bus Error Unit) + 커스텀 인터럽트
- T-Head C910 — 플랫폼 고유 CSR 기반 에러 보고
- UEFI 서버 — ACPI GHES 경유 (x86과 동일한 최종 경로)
- Sscofpmf — 성능 카운터 오버플로 확장 (에러 카운팅에 활용 가능)
- Smcdeleg — 카운터 위임 확장 (에러 모니터링 위임)
MCE 테스트 주입
MCE 처리 경로의 정확성을 검증하려면 하드웨어 에러를 시뮬레이션해야 합니다. 리눅스 커널은 여러 단계의 주입 메커니즘을 제공합니다.
주입 방법 비교
| 방법 | 수준 | 요구사항 | 특징 |
|---|---|---|---|
| mce-inject | 소프트웨어 MCE | CONFIG_X86_MCE_INJECT=y | 커널 내부에서 가짜 MCE 생성. MCA 뱅크를 실제로 쓰지 않음 |
| EINJ | 펌웨어 레벨 | CONFIG_ACPI_APEI_EINJ=y, BIOS 지원 | ACPI EINJ 테이블로 실제 메모리 에러 주입. 가장 현실적 |
| hwpoison-inject | 페이지 레벨 | CONFIG_HWPOISON_INJECT=y | debugfs에서 직접 PFN 지정하여 HWPoison 마킹 |
mce-inject 사용법
# mce-inject 모듈 로드
$ sudo modprobe mce-inject
# 주입 파일 작성 (SRAO 메모리 에러)
$ cat > /tmp/mce-test.txt <<EOF
CPU 0 BANK 9
STATUS 0xBD2000000000017A
ADDR 0x12345678
MISC 0x0000000000000086
MCGSTATUS 0x0
EOF
# 주입 실행
$ sudo sh -c "cat /tmp/mce-test.txt > /sys/kernel/debug/mce-inject/mce-inject"
# dmesg 확인
$ dmesg | tail -20
EINJ (Error Injection) 사용법
# EINJ 모듈 로드
$ sudo modprobe einj
# 지원되는 에러 유형 확인
$ cat /sys/kernel/debug/apei/einj/available_error_type
0x00000002 Memory Correctable
0x00000008 Memory Uncorrectable non-fatal
0x00000010 Memory Uncorrectable fatal
# CE 주입 (특정 물리 주소)
$ sudo sh -c "echo 0x2 > /sys/kernel/debug/apei/einj/error_type"
$ sudo sh -c "echo 0x100000000 > /sys/kernel/debug/apei/einj/param1" # 물리 주소
$ sudo sh -c "echo 0xFFFFFFFFFFFFF000 > /sys/kernel/debug/apei/einj/param2" # 마스크
$ sudo sh -c "echo 1 > /sys/kernel/debug/apei/einj/error_inject"
hwpoison-inject 사용법
# 특정 PFN에 HWPoison 주입
$ sudo sh -c "echo 0x12345 > /sys/kernel/debug/hwpoison/corrupt-pfn"
# soft offline (CE 예방)
$ sudo sh -c "echo 0x12345 > /sys/kernel/debug/hwpoison/soft-offline-pfn"
# HWPoison 해제
$ sudo sh -c "echo 0x12345 > /sys/kernel/debug/hwpoison/unpoison-pfn"
tolerant=3 설정으로 패닉을 방지하거나
VM 스냅샷을 준비하세요.
MCE 테스트 시나리오 매트릭스
| 시나리오 | 주입 방법 | 예상 결과 | 검증 사항 |
|---|---|---|---|
| CE 단일 발생 | mce-inject (UC=0) | dmesg CE 로그, EDAC 카운트 증가 | rasdaemon DB에 기록 확인 |
| CE 폭주 (CMCI 스톰) | mce-inject 반복 | CMCI → 폴링 전환 | cmci_storm 감지 로그 확인 |
| SRAR (데이터 소비) | EINJ + 메모리 접근 | SIGBUS, 프로세스 종료 | HWPoison 페이지 확인 |
| SRAO (백그라운드) | mce-inject (UC=1,S=1,AR=0) | 페이지 오프라인 | buddy allocator 제외 확인 |
| UC+PCC (패닉) | mce-inject (UC=1,PCC=1) | 시스템 패닉 | BERT 또는 crash dump |
| HWPoison 복구 | hwpoison-inject | 페이지 오프라인, 매핑 제거 | /proc/kpageflags 확인 |
| soft offline | hwpoison soft-offline | 마이그레이션 후 페이지 격리 | vmstat migration 카운트 |
가상 머신에서의 MCE 테스트
QEMU/KVM에서 MCE를 주입하여 게스트 커널의 MCE 핸들링을 테스트할 수 있습니다.
# QEMU 모니터에서 게스트에 MCE 주입
(qemu) mce 0 9 0xBD2000000000017A 0x12345678 0x86
# 파라미터: CPU BANK STATUS ADDR MISC
# CPU 0, Bank 9, STATUS = SRAO 메모리 에러
# KVM에서 ioctl로 프로그래밍 주입
# KVM_X86_SET_MCE ioctl → struct kvm_x86_mce
MCE 디버깅 기법 (perf/ftrace/eBPF)
MCE 이벤트는 하드웨어 오류의 핵심 진단 정보를 담고 있지만, 발생 빈도가 낮고 재현이 어려워 체계적인 디버깅 도구 활용이 필수적입니다. 리눅스 커널은 perf, ftrace, eBPF 등 다양한 트레이싱 프레임워크를 통해 MCE 이벤트를 실시간 모니터링하고 분석할 수 있는 인프라를 제공합니다.
perf를 이용한 MCE 이벤트 추적
# MCE 관련 트레이스포인트 확인
$ perf list 'ras:*'
# MCE 이벤트 실시간 기록
$ perf record -e ras:mce_record -a --overwrite -o mce_trace.data
# 기록된 MCE 이벤트 상세 출력
$ perf script -i mce_trace.data
# EDAC/AER 이벤트도 함께 추적
$ perf record -e ras:mce_record -e ras:mc_event -e ras:aer_event -a -o ras_all.data
ftrace를 이용한 MCE 핸들러 추적
# function_graph 트레이서로 MCE 핸들러 추적
$ cd /sys/kernel/debug/tracing
$ echo 0 > tracing_on
$ echo function_graph > current_tracer
$ echo do_machine_check > set_graph_function
$ echo 1 > tracing_on
# MCE 관련 함수만 필터링
$ echo do_machine_check > set_ftrace_filter
$ echo mce_severity >> set_ftrace_filter
$ echo mce_reign >> set_ftrace_filter
# 트레이스포인트 직접 활성화
$ echo 1 > events/ras/mce_record/enable
eBPF/bpftrace를 이용한 MCE 모니터링
# MCE 이벤트 발생 시 뱅크 번호와 상태 출력
$ bpftrace -e '
tracepoint:ras:mce_record {
printf("MCE: cpu=%d bank=%d status=0x%llx addr=0x%llx\n",
args->cpu, args->bank, args->status, args->addr);
}'
# MCE 뱅크별 발생 횟수 집계
$ bpftrace -e '
tracepoint:ras:mce_record {
@mce_by_bank[args->bank] = count();
@mce_by_cpu[args->cpu] = count();
}'
# 물리 주소 기반 반복 오류 감지
$ bpftrace -e '
tracepoint:ras:mce_record {
@addr_count[args->addr] = count();
if (@addr_count[args->addr] >= 3) {
printf("WARNING: 반복 MCE at addr=0x%llx (count=%d)\n",
args->addr, @addr_count[args->addr]);
}
}'
# do_machine_check() 실행 시간 측정
$ bpftrace -e '
kprobe:do_machine_check { @start[tid] = nsecs; }
kretprobe:do_machine_check /@start[tid]/ {
@mce_latency_us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'
크래시 덤프 분석 (kdump + crash)
치명적 MCE로 인한 커널 패닉 이후에는 kdump가 캡처한 메모리 덤프를 crash 유틸리티로 분석합니다.
# crash 유틸리티로 덤프 분석
$ crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/<timestamp>/vmcore
# crash 프롬프트에서 MCE 정보 조회
crash> log | grep -i "mce\|machine check"
crash> bt -a # 모든 CPU 백트레이스
crash> struct mce mcelog.entry[0]
crash> px mcelog.entry[0].status
crash> px mcelog.entry[0].addr
MCE 이벤트 상관 분석
# 타임스탬프 기반 통합 분석
$ journalctl --since "2 hours ago" -k | \
grep -iE "mce|edac|aer|ghes|hardware.error" | \
sort -k1,3 | tee /tmp/hw_error_timeline.log
# rasdaemon DB에서 시간 범위 기반 교차 조회
$ sqlite3 /var/lib/rasdaemon/ras-mc_event.db "
SELECT 'MCE' as type, timestamp, error_msg FROM mce_record
WHERE timestamp > datetime('now', '-2 hours')
UNION ALL
SELECT 'MC' as type, timestamp, error_msg FROM mc_event
WHERE timestamp > datetime('now', '-2 hours')
UNION ALL
SELECT 'AER' as type, timestamp, error_msg FROM aer_event
WHERE timestamp > datetime('now', '-2 hours')
ORDER BY timestamp;"
ADDR 필드 물리 주소를 EDAC의 DIMM 위치(채널/슬롯/랭크)와 매핑하면, 특정 DIMM의 물리적 교체 여부를 판단할 수 있습니다. page-types -b hwpoison으로 오프라인 페이지도 확인하세요.
커널 설정 옵션
MCE/RAS 관련 커널 설정 옵션의 종합 목록입니다. 프로덕션 서버에서는 대부분 활성화하는 것이 권장됩니다.
| 설정 | 기본값 | 설명 |
|---|---|---|
CONFIG_X86_MCE | y (x86에서) | MCE 핸들러 핵심. 비활성화하면 MCE 시 triple fault |
CONFIG_X86_MCE_INTEL | y | Intel 전용 MCE 확장 (CMCI, LMCE, CEC 등) |
CONFIG_X86_MCE_AMD | y | AMD 전용 MCE 확장 (SMCA, Threshold 인터럽트) |
CONFIG_X86_MCE_THRESHOLD | y | AMD Threshold 카운터 인터럽트 지원 |
CONFIG_X86_MCE_INJECT | m | MCE 주입 모듈 (테스트용) |
CONFIG_MEMORY_FAILURE | y | memory_failure() / HWPoison 지원 |
CONFIG_HWPOISON_INJECT | m | HWPoison 주입 debugfs 인터페이스 |
CONFIG_EDAC | y | EDAC 서브시스템 핵심 |
CONFIG_EDAC_GHES | y | GHES 기반 EDAC 드라이버 |
CONFIG_EDAC_SKX | m | Intel Skylake-SP EDAC |
CONFIG_EDAC_I10NM | m | Intel Ice Lake / SPR EDAC |
CONFIG_EDAC_AMD64 | m | AMD64 EDAC (Fam10h~Zen4) |
CONFIG_ACPI_APEI | y | ACPI Platform Error Interface |
CONFIG_ACPI_APEI_GHES | y | GHES(Generic HW Error Source) 지원 |
CONFIG_ACPI_APEI_EINJ | m | EINJ 에러 주입 지원 |
CONFIG_ACPI_APEI_ERST_DEBUG | m | ERST 디버그 인터페이스 |
CONFIG_RAS_CEC | y | Corrected Error Collector (Intel) |
zcat /proc/config.gz | grep -E "MCE|EDAC|APEI|MEMORY_FAILURE|HWPOISON"
또는 grep -r "MCE\|EDAC" /boot/config-$(uname -r)
MCE 로그 디코딩 실전
실제 MCE 로그를 단계별로 해석하는 실전 가이드입니다. dmesg에 출력되는 MCE 메시지의 각 필드가 무엇을 의미하는지 분석합니다.
STATUS 디코딩 단계별 안내
예시: STATUS = 0xBE00000000800400
# 64비트 이진 분해
0xBE00000000800400 =
1 0 1 1 1 1 1 0 0000... 1000 0000 0000 0100 0000 0000
63 62 61 60 59 58 57 56 [31:16] [15:0] = 0x0400
# 상위 비트 해석
bit 63 (VAL) = 1 → 유효한 에러
bit 62 (OVER) = 0 → 오버플로 없음
bit 61 (UC) = 1 → 교정 불가 (Uncorrected)
bit 60 (EN) = 1 → 에러 보고 활성
bit 59 (MISCV) = 1 → MISC 레지스터 유효
bit 58 (ADDRV) = 1 → ADDR 레지스터 유효
bit 57 (PCC) = 1 → 프로세서 컨텍스트 손상!
bit 56 (S) = 0 → MCG_SER_P 미지원 또는 비활성
# 에러 코드 (bits 15:0) = 0x0400
0x0400 = 0000 0100 0000 0000
→ Internal Timer Error (Simple Error Code)
# 결론: UC + PCC → 프로세서 상태 복구 불가 → 시스템 패닉
유용한 디코딩 도구
# mcelog으로 디코딩 (레거시)
$ sudo mcelog --ascii --file /tmp/mce.log
# rasdaemon SQLite 쿼리
$ sudo sqlite3 /var/lib/rasdaemon/ras-mc_event.db \
"SELECT datetime(timestamp,'unixepoch'), bank, status, addr,
CASE WHEN (status >> 61) & 1 THEN 'UC' ELSE 'CE' END as type
FROM mce_record ORDER BY id DESC LIMIT 10;"
# edac-util로 DIMM 매핑 확인
$ edac-util -s
$ edac-util -r
CE (Corrected Error) 로그 예제
# EDAC CE 로그 예시
[54321.987] EDAC MC0: 1 CE memory read error on CPU_SrcID#0_MC#0_Chan#0_DIMM#0
(channel:0 slot:0 page:0x1234 offset:0x0 grain:32 syndrome:0x0)
[54321.987] EDAC MC0: 1 CE ... area:DRAM err_code:0001:0091
# 해석:
# - MC0: 메모리 컨트롤러 0
# - 1 CE: Corrected Error 1회
# - Channel 0, Slot 0: 첫 번째 채널의 첫 번째 DIMM
# - page:0x1234: 물리 페이지 프레임 번호
# - grain:32: 에러 입도 (바이트)
# - err_code:0001:0091: 계층/에러 코드
SRAR (Action Required) 로그 예제
[98765.432] mce: [Hardware Error]: CPU 12 Bank 7: bd80000000100134
[98765.432] mce: [Hardware Error]: RIP !INEXACT! 33:<00007f1234567890>
[98765.432] mce: [Hardware Error]: TSC a1b2c3d4e5f67890 ADDR 0000000087654000
[98765.432] mce: [Hardware Error]: MISC 000000000000008c PPIN 1234567890abcdef
[98765.432] mce: [Hardware Error]: PROCESSOR 2:806f8 TIME 1710000000 SOCKET 1 APIC 24
[98765.432] Memory failure 0x87654: recovery action for dirty LRU page: Recovered
# STATUS 0xBD80000000100134 디코딩:
# bit 63 VAL=1, bit 61 UC=1, bit 60 EN=1
# bit 59 MISCV=1, bit 58 ADDRV=1
# bit 57 PCC=0 → 프로세서 컨텍스트 정상
# bit 56 S=1, bit 55 AR=1 → SRAR
# Error Code (bits 15:0) = 0x0134 → L3 캐시 데이터 읽기 에러
# 결과: memory_failure()로 페이지 복구 성공 ("Recovered")
자주 발생하는 MCE 패턴
데이터센터에서 흔히 관찰되는 MCE 패턴과 각각의 원인, 대응 방법을 정리합니다.
패턴 1: CE 누적 (DIMM 열화)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | EDAC MC0: 1 CE ... on DIMM0 (반복) |
| STATUS 특성 | UC=0, VAL=1 — CE (Corrected Error) |
| 원인 | DIMM 셀 열화, 접촉 불량, 전압 마진 부족 |
| 조치 | 1) DIMM 재장착 시도 2) CE 임계값 초과 시 교체 3) BIOS에서 스페어링 활성화 |
| 긴급도 | 낮음 → 중간 (교체 계획 수립) |
패턴 2: UC no PCC (복구 가능한 에러)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: Uncorrected hardware memory error in user-access at ... |
| STATUS 특성 | UC=1, PCC=0, S=1, AR=1 → SRAR |
| 원인 | 메모리 비트 플립 (ECC 2비트 이상 오류) |
| 조치 | 1) 해당 페이지 HWPoison 자동 처리됨 2) 동일 DIMM 모니터링 3) 반복 시 DIMM 교체 |
| 긴급도 | 중간 (서비스 재시작 필요할 수 있음) |
패턴 3: UC + PCC (시스템 패닉)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | Kernel panic - not syncing: Fatal machine check on current CPU |
| STATUS 특성 | UC=1, PCC=1 — 프로세서 컨텍스트 손상 |
| 원인 | CPU 캐시 패리티, 마이크로코드 버그, 전력 불안정, 과열 |
| 조치 | 1) BERT 데이터 확인 2) 마이크로코드 업데이트 3) CPU 교체 고려 4) 전력/냉각 점검 |
| 긴급도 | 높음 (즉시 대응) |
패턴 4: OVER + UC (에러 오버플로)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: Overflowed uncorrected error |
| STATUS 특성 | OVER=1, UC=1 — 이전 에러가 읽히기 전에 새 에러 발생 |
| 원인 | 에러 폭주, 하드웨어 심각한 결함 |
| 조치 | 즉시 하드웨어 점검. 정보 손실로 인해 root cause 분석 어려울 수 있음 |
| 긴급도 | 매우 높음 |
패턴 5: Thermal MCE
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | CPU0: Temperature above threshold, cpu clock throttled |
| 원인 | CPU 과열 — 쿨러 고장, 열 전도 패드 열화, 데이터센터 냉방 문제 |
| 조치 | 1) 즉시 냉각 확인 2) 팬 RPM 점검 3) thermal paste 재도포 4) 부하 분산(Load Balancing) |
| 참고 | Thermal Management 페이지 참조 |
패턴 6: 버스/인터커넥트 에러
| 항목 | 내용 |
|---|---|
| 에러 코드 | 0x0800 범위 (Bus/Interconnect Error) |
| 원인 | QPI/UPI 링크 에러, PCIe 버스 에러, 물리적 커넥터 문제 |
| 조치 | 1) PCIe 카드 재장착 2) 케이블 점검 3) BIOS에서 링크 속도 조정 |
패턴 7: IIO PCIe Completion Timeout
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: IIO Bank ... MSCOD=0x0000 Completion Timeout +
pcieport ... AER: Uncorrectable error received |
| MCA 뱅크 | Bank 5~8 (IIO 스택, CPU 모델별 가변) |
| MSCOD | 0x0000 (Completion Timeout) |
| 원인 | GPU/NVMe/NIC 행(hang), FLR 중 접근, PCIe 링크 불안정, 라이저 접촉 불량 |
| 조치 | 1) lspci -vvvs <BDF>로 링크 상태 확인
2) 장치 펌웨어 업데이트 3) 물리적 재장착 4) CTO 값 조정 |
| 긴급도 | 중간~높음 (장치 유형에 따라 서비스 영향 판단) |
패턴 8: IIO Poisoned TLP (데이터 독성 전파)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: IIO Bank ... MSCOD=0x0002 Poisoned TLP |
| MSCOD | 0x0002 (Poisoned TLP) |
| 원인 | GPU VRAM ECC UC, NVMe 내부 DRAM 에러, 장치 메모리 결함 |
| GPU 관련 | NVIDIA Xid 48/63 (DBE/Row Remapper Failure)과 동시 발생 가능 |
| 조치 | 1) 장치 에러 로그 확인 2) 장치 ECC 상태 확인 3) BIOS Viral Mode 설정 확인 4) 장치 교체 검토 |
| 긴급도 | 높음 (데이터 무결성 위협) |
패턴 9: TOR (Table of Requests) 타임아웃
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | Kernel panic ... MCE: CHA Bank ... TOR Timeout |
| MCA 뱅크 | Bank 21~24+ (CHA/LLC) |
| 원인 | DIMM 하드 페일 → 메모리 접근 불가, PCIe 장치 무응답 → DMA 체류, UPI 링크 다운 → 원격 메모리 접근 중단, 마이크로코드 버그 |
| 분석 핵심 | TOR Timeout은 거의 항상 2차 에러. 동시 발생한 다른 뱅크 에러(IIO/M2M/UPI)에서 근본 원인을 찾아야 함 |
| 조치 | 1) BERT 데이터에서 선행 에러 확인 2) 동시 발생 MCA 뱅크 에러 전수 분석 3) 마이크로코드 업데이트 4) 메모리/장치 하드웨어 점검 |
| 긴급도 | 매우 높음 (패닉 후 원인 분석 필수) |
패턴 10: UPI 링크 에러 (멀티소켓)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: CPU N Bank 5 ... UPI CRC Error |
| MCA 뱅크 | Bank 5~6 (UPI) |
| CE 상황 | CRC 교정 가능 → 자동 재전송, 성능 약간 저하. CE 빈도 증가 시 링크 열화 경고 |
| UC 상황 | 링크 다운 → 원격 NUMA 노드 접근 불가 → 해당 노드 프로세스 대량 SIGBUS/패닉 |
| 원인 | 소켓 간 인터커넥트 하드웨어 열화, 메인보드 UPI 트레이스 결함, 전기적 잡음 |
| 조치 | 1) UPI CE 추세 모니터링 2) BIOS에서 UPI 링크 속도 다운그레이드 시도 3) CPU 또는 메인보드 교체 검토 |
패턴 11: M2M Patrol Scrub UC (사전 발견 메모리 에러)
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | mce: M2M Bank ... Patrol Scrub Error UC |
| MCA 뱅크 | Bank 9~12 (M2M) |
| 의미 | Patrol Scrubbing이 배경에서 교정 불가 메모리 에러를 사전 발견 |
| 장점 | 아직 소비되지 않은 데이터 → SRAO로 처리 가능 (memory_failure로 페이지 오프라인) |
| 원인 | DIMM 열화, ECC 2비트+ 오류 (Demand 접근 전에 발견) |
| 조치 | 1) 해당 DIMM CE 이력 확인 2) HWPoison 페이지 확인 3) DIMM 교체 계획 |
| 긴급도 | 중간 (사전 발견이므로 즉각적 서비스 영향은 제한적) |
패턴 12: GPU/가속기 관련 IIO 연쇄 에러
| 항목 | 내용 |
|---|---|
| 에러 체인 | GPU VRAM ECC UC → Poisoned TLP → IIO MCE (MSCOD=0x0002) → 소비 코어 SRAR → SIGBUS |
| NVIDIA 시그니처 | dmesg에 NVRM: Xid ... 48(DBE),
Xid ... 63(Row Remapper Failure) 등장 |
| AMD GPU 시그니처 | dmesg에 amdgpu: ... ECC error |
| 조치 |
|
| 긴급도 | 높음 (학습 작업 중단, GPU 하드웨어 결함 확인 필요) |
패턴 13: Row Hammer 유발 MCE
Row Hammer는 DRAM의 물리적 특성을 이용한 현상으로, 특정 행을 반복 접근(Hammering)하면 인접 행(Victim Row)의 셀에서 전하 누출이 발생하여 비트 플립(Bit Flip)이 일어납니다. 이는 의도적 공격뿐 아니라 고부하 워크로드에서도 자연적으로 발생할 수 있습니다.
Row Hammer → MCE 발생 경로
- 반복 행 활성화: 공격자 또는 워크로드가 동일 DRAM 행을 수만~수십만 회 반복 접근 (
clflush+ 메모리 읽기 루프) - 인접 행 비트 플립: Victim Row의 셀 커패시터(Capacitor) 전하가 누출되어 0→1 또는 1→0 전환
- ECC 감지: 메모리 컨트롤러의 ECC가 비트 오류를 감지 — 단일 비트 → CE, 다중 비트 → UCE → memory_failure() 호출
- MCE 생성: MCA 뱅크(메모리 컨트롤러)에 에러 기록, STATUS.VAL=1 설정
TRR (Target Row Refresh)과 한계
DDR4 이후 DRAM 제조사들은 TRR(Target Row Refresh)을 도입하여 Aggressor Row 감지 시 Victim Row를 사전 리프레시(Refresh)합니다. 그러나 TRRespass, Half-Double, Blacksmith 등 연구에서 TRR 우회 패턴이 발견되었으며, DDR5에서는 표준화된 RFM(Refresh Management) 명령으로 개선되었으나 공정 미세화에 따라 취약성은 증가 추세입니다.
| 항목 | 내용 |
|---|---|
| dmesg 시그니처 | EDAC MC0: 1 CE ... on DIMM0 (단시간 대량 반복) +
mce: [Hardware Error]: ... ADDR: 동일 뱅크 내 인접 주소 |
| STATUS 특성 | UC=0 (CE 단계) 또는 UC=1, PCC=0 (UCE 확대 시). 에러 코드 0x0150~0x0151 (메모리 컨트롤러) |
| 원인 | DRAM Row Hammer — 워크로드 또는 의도적 공격에 의한 인접 행 비트 플립 |
| 구별 방법 | 1) CE가 동일 DIMM의 인접 물리 행에 집중 2) 짧은 시간에 비정상적 CE 폭증 3) edac-util로 특정 랭크/뱅크 집중 확인 |
| 조치 | 1) BIOS에서 TRR/pTRR 활성화 확인 2) DIMM 펌웨어 업데이트 3) ECC 모드 강화(Chipkill) 4) 반복 시 DIMM 교체 5) 보안 관점: CAP_SYS_RAWIO 제한 검토 |
| 긴급도 | 중간~높음 (CE 단계: 모니터링, UCE 확대 시: 즉시 대응) |
성능 영향과 튜닝
MCE 서브시스템은 정상 상태에서 거의 오버헤드가 없지만, 에러 발생 시나 폴링 설정에 따라 성능에 영향을 줄 수 있습니다. 대규모 서버 클러스터에서는 MCE 관련 설정이 전체 가용성에 영향을 미칠 수 있으므로 적절한 튜닝이 필요합니다.
MCE 관련 커널 부트 파라미터
| 파라미터 | 설명 | 기본값 |
|---|---|---|
mce=off | MCE 핸들링 완전 비활성화 (위험) | 비활성 |
mce=dont_log_ce | CE를 dmesg에 기록하지 않음 | 비활성 |
mce=ignore_ce | CE를 완전히 무시 | 비활성 |
mce=no_cmci | CMCI 비활성화 (폴링만 사용) | 비활성 |
mce=bootlog | 부팅 시 MCA 뱅크의 이전 에러 로깅 | 활성 |
mce=recovery | SRAR/SRAO 복구 경로 강제 활성화 | 자동 감지 |
memory_corruption_check=1 | 주기적 메모리 무결성 검사 | 0 |
sysfs 튜닝 파라미터
| 파라미터 | 경로 | 기본값 | 설명 |
|---|---|---|---|
| check_interval | /sys/devices/system/machinecheck/machinecheck0/check_interval | 300 (5분) | MCE 폴링 주기 (초). CMCI 비지원 CPU에서만 의미 있음 |
| tolerant | /sys/devices/system/machinecheck/machinecheck0/tolerant | 1 | MCE 대응 레벨 (0=항상 패닉, 1=기본, 2=관대, 3=무시) |
| monarch_timeout | /sys/devices/system/machinecheck/machinecheck0/monarch_timeout | CPU 종속 | Monarch 랑데부 타임아웃 (us). 0이면 타임아웃 없음 |
| dont_log_ce | /sys/devices/system/machinecheck/machinecheck0/dont_log_ce | 0 | 1이면 CE를 dmesg에 기록하지 않음 (CE 폭주 시 유용) |
| ignore_ce | /sys/devices/system/machinecheck/machinecheck0/ignore_ce | 0 | 1이면 CE를 완전히 무시 (CMCI도 비활성화) |
ECC Patrol Scrubbing
메모리 스크러빙은 DRAM의 모든 위치를 주기적으로 읽어 CE를 사전에 발견하고 교정합니다. 스크럽 주기가 짧을수록 UC 에러(CE 누적 → 2비트 오류)를 예방하지만, 메모리 대역폭을 소비합니다.
| 설정 | 위치 | 권장값 |
|---|---|---|
| Patrol Scrub | BIOS/UEFI 설정 | Enabled (기본) |
| Scrub Rate | BIOS/UEFI 설정 | 24시간 이내 전체 메모리 스캔 |
| EDAC scrub rate | sysfs (일부 드라이버) | 드라이버 종속 |
- Memory Patrol Scrub: Enabled
- Memory ECC: Enabled (기본)
- Memory RAS Mode: Independent (성능) 또는 Mirroring (가용성)
- ADDDC (Adaptive Double DRAM Device Correction): Enabled (서버)
- Corrected Error Threshold: 제조사 권장값
성능 영향 요약
| 상황 | 성능 영향 | 완화 방법 |
|---|---|---|
| 정상 (에러 없음) | 무시할 수 있음 (~0%) | - |
| MCE 폴링 (CMCI 없음) | 매우 낮음 (5분마다 MSR 읽기) | CMCI 지원 CPU 사용 |
| CMCI 스톰 | 중간 (인터럽트 폭주) | 자동 폴링 전환 (커널 기본 동작) |
| MCE 핸들링 (UC) | 높음 (모든 CPU 동기화) | LMCE 활성화 |
| memory_failure() | 해당 페이지 오프라인 | 총 메모리 대비 무시 가능 |
| Patrol Scrub | 낮음 (~1-3% 대역폭) | 비활성화 가능하나 비권장 |
Prometheus + Grafana 모니터링 구축
대규모 서버 환경에서는 rasdaemon의 SQLite DB를 Prometheus 메트릭으로 변환하여
Grafana 대시보드에서 실시간 모니터링하고, Alertmanager로 임계값 알림을 설정합니다.
node_exporter의 textfile collector를 활용하면 별도 exporter 개발 없이 구현 가능합니다.
#!/bin/bash
# /usr/local/bin/edac-metrics.sh
# node_exporter textfile collector용 EDAC 메트릭 생성 스크립트
# cron: */5 * * * * /usr/local/bin/edac-metrics.sh
TEXTFILE_DIR="/var/lib/node_exporter/textfile_collector"
PROM_FILE="${TEXTFILE_DIR}/edac.prom"
TMP_FILE="${PROM_FILE}.tmp"
DB="/var/lib/rasdaemon/ras-mc_event.db"
mkdir -p "${TEXTFILE_DIR}"
{
echo "# HELP edac_ce_total Total corrected errors per DIMM"
echo "# TYPE edac_ce_total counter"
# EDAC sysfs에서 직접 메트릭 수집
for mc in /sys/devices/system/edac/mc/mc*; do
[ -d "$mc" ] || continue
mc_id=$(basename "$mc" | sed 's/mc//')
for dimm in "${mc}"/dimm*; do
[ -d "$dimm" ] || continue
dimm_id=$(basename "$dimm" | sed 's/dimm//')
label=$(cat "${dimm}/dimm_label" 2>/dev/null || echo "unknown")
ce=$(cat "${dimm}/dimm_ce_count" 2>/dev/null || echo 0)
ue=$(cat "${dimm}/dimm_ue_count" 2>/dev/null || echo 0)
echo "edac_ce_total{mc=\"${mc_id}\",dimm=\"${dimm_id}\",label=\"${label}\"} ${ce}"
echo "edac_ue_total{mc=\"${mc_id}\",dimm=\"${dimm_id}\",label=\"${label}\"} ${ue}"
done
done
echo "# HELP edac_ue_total Total uncorrected errors per DIMM"
echo "# TYPE edac_ue_total counter"
# rasdaemon DB에서 최근 1시간 CE rate 계산
echo "# HELP edac_ce_rate_1h CE count in last hour"
echo "# TYPE edac_ce_rate_1h gauge"
if [ -f "${DB}" ]; then
sqlite3 "${DB}" "SELECT mc, top_layer, mid_layer, COUNT(*) FROM mc_event \
WHERE err_type='Corrected' AND timestamp > strftime('%s','now','-1 hour') \
GROUP BY mc, top_layer, mid_layer;" | while IFS='|' read mc ch dm cnt; do
echo "edac_ce_rate_1h{mc=\"${mc}\",channel=\"${ch}\",dimm=\"${dm}\"} ${cnt}"
done
fi
} > "${TMP_FILE}"
mv "${TMP_FILE}" "${PROM_FILE}"
# node_exporter 실행 시 textfile collector 경로 지정
$ node_exporter --collector.textfile.directory=/var/lib/node_exporter/textfile_collector
# Prometheus에서 확인
$ curl -s http://localhost:9090/api/v1/query?query=edac_ce_total | jq .
# Alertmanager 규칙 예시 (prometheus.rules.yml)
# CE가 1시간에 50회 이상이면 경고, UE 발생 시 긴급
# Prometheus alerting rules — /etc/prometheus/rules/edac.rules.yml
groups:
- name: edac_alerts
rules:
- alert: HighCorrectedErrorRate
expr: increase(edac_ce_total[1h]) > 50
for: 5m
labels:
severity: warning
annotations:
summary: "DIMM {{ $labels.label }} CE 급증 ({{ $value }}/h)"
description: "MC {{ $labels.mc }} DIMM {{ $labels.dimm }}에서 1시간 내 {{ $value }}건의 CE 발생. DIMM 교체 검토 필요."
- alert: UncorrectedError
expr: increase(edac_ue_total[5m]) > 0
labels:
severity: critical
annotations:
summary: "DIMM {{ $labels.label }} UE 발생!"
description: "MC {{ $labels.mc }} DIMM {{ $labels.dimm }}에서 복구 불가 에러. 즉시 DIMM 교체 필요."
- alert: CEtoUEEscalation
expr: increase(edac_ce_total[24h]) > 100 and increase(edac_ue_total[24h]) > 0
labels:
severity: critical
annotations:
summary: "CE→UE 에스컬레이션 감지: {{ $labels.label }}"
- 패널 1: 시간축 CE/UE 추이 그래프 (increase(edac_ce_total[1h]) by (label))
- 패널 2: DIMM별 누적 에러 히트맵 (소켓×채널 행렬)
- 패널 3: 서버별 EDAC 상태 테이블 (정상/주의/위험 색상 코딩)
- 패널 4: 최근 24시간 UE 발생 서버 리스트 (즉시 대응 필요)
대규모 서버 클러스터 MCE 통계
Google, Meta(Facebook), Microsoft 등이 발표한 대규모 데이터센터 연구에서 DRAM 에러율과 DIMM 수명 패턴에 대한 실측 데이터를 확인할 수 있습니다. 이 데이터는 DIMM 교체 정책과 용량 계획에 중요한 근거가 됩니다.
| 연구 출처 | 플릿 규모 | CE 발생 DIMM 비율 | UE 발생 DIMM 비율 | 주요 발견 |
|---|---|---|---|---|
| Google (2009) | ~수십만 서버 | 연간 ~32% 서버에서 CE | 연간 ~1.3% 서버에서 UE | CE 있는 DIMM은 이후 CE 발생 확률 6~30배 증가 |
| Meta/Facebook (2015) | ~수백만 DIMM | 연간 ~20% DIMM에서 CE | 연간 ~0.03% DIMM에서 UE | 온도 10도 상승 시 에러율 ~2배 증가 |
| Microsoft Azure (2018) | 수백만 서버 | 연간 ~9% 서버에서 CE | - | CE 패턴으로 72시간 내 UE 예측 가능 (논문 기반) |
CE → UE 에스컬레이션 확률
| CE 패턴 | 24시간 내 UE 확률 | 7일 내 UE 확률 | 권장 조치 |
|---|---|---|---|
| 단일 CE (1회) | < 0.1% | ~0.5% | 모니터링 유지 |
| 반복 CE (같은 주소, 10회+) | ~2-5% | ~8-15% | 해당 페이지 soft offline, DIMM 교체 계획 |
| CE 폭주 (24시간 100회+) | ~10-20% | ~25-40% | 즉시 DIMM 교체, 워크로드 마이그레이션 |
| 다중 행/열 CE (여러 주소) | ~15-30% | ~35-50% | 긴급 DIMM 교체, 행/열 패턴은 DRAM 셀 열화 표시 |
DIMM 수명 곡선 (Bathtub Curve)
| 운영 기간 | 고장률 특성 | 주요 원인 | 운영 가이드 |
|---|---|---|---|
| 0~6개월 (초기 고장) | 상대적으로 높음 (~2-3x 정상) | 제조 결함, 솔더링 불량, 초기 전기적 스트레스 | DOA 검사 강화, burn-in 테스트 수행 |
| 6개월~3년 (안정기) | 최저 (기준 고장률) | 랜덤 소프트 에러 (우주선, 알파입자) | 정상 모니터링, patrol scrub 유지 |
| 3~5년+ (마모기) | 점진적 증가 (~1.5-4x) | DRAM 셀 열화, Row Hammer, 반복 스트레스 | CE 임계값 강화, 사전 교체 프로그램 운영 |
MCE와 ECC 메모리 모드
서버 메모리 컨트롤러는 여러 ECC/RAS 모드를 지원하며, 가용성과 성능 사이의 트레이드오프에 따라 적절한 모드를 선택해야 합니다. BIOS/UEFI에서 설정하며, MCE/EDAC이 보고하는 에러 유형과 복구 능력에 직접 영향을 줍니다.
| 모드 | 용량 영향 | 대역폭 영향 | ECC 능력 | 가용성 | 적용 시나리오 |
|---|---|---|---|---|---|
| Independent | 100% 사용 가능 | 최대 대역폭 | 기본 SECDED (1비트 교정, 2비트 검출) | 보통 | HPC, 대용량 메모리 필요 환경, 일반 서버 |
| Mirroring | 50% (절반이 미러) | 읽기: ~동일, 쓰기: ~동일 | 전체 채널 장애까지 복구 가능 | 최고 | 미션 크리티컬 DB, 금융, 의료 시스템 |
| Lockstep | 100% (채널 쌍 결합) | 50% (2채널 → 1논리채널) | x8 SDDC (단일 DRAM 칩 전체 오류 교정) | 높음 | Chipkill 필요 환경, 단일 칩 고장 허용 |
| Sparing | 1 Rank/DIMM 예비 | 전환 시 일시 저하 | CE 임계값 초과 시 자동 전환 | 높음 | 장기 무중단 운영 서버, 교체 주기가 긴 환경 |
| ADDDC | ~100% (동적) | 활성화 시 약간 저하 | 2x DRAM 디바이스 에러 교정 (Adaptive) | 매우 높음 | Intel SPR+, DDR5 서버, 최신 데이터센터 |
- 기본 권장: Independent + ADDDC Enabled (DDR5 서버). 용량 손실 없이 높은 RAS 제공
- 최고 가용성: Mirroring + ADDDC. 미러 채널 내에서도 ADDDC로 이중 보호
- HPC/대역폭: Independent + Patrol Scrub. 성능 최우선, CE 모니터링으로 보완
- 레거시 DDR4: Lockstep (x4 DIMM) 또는 Rank Sparing. Chipkill 수준 보호
dimm_edac_mode sysfs 항목으로 보고합니다.
DDR5 On-Die ECC와 MCE 변화
DDR5는 JEDEC 표준으로 On-Die ECC를 필수 기능으로 규정합니다. 각 DRAM 다이(Die) 내부에 ECC 엔진이 탑재되어, 데이터가 DRAM 칩 외부로 나가기 전에 단일 비트 오류를 자체 교정합니다. 이 변화는 호스트 측 MCE 패턴에 근본적인 영향을 미칩니다.
On-Die ECC 동작 원리
- 내부 구조: DDR5 다이는 128비트 데이터에 8비트 ECC 체크 비트를 추가하여 총 136비트를 저장. 읽기 시 내부에서 SECDED 수행
- 투명성: On-Die ECC 교정은 호스트 메모리 컨트롤러에 투명(Transparent)하므로, 교정된 오류는 CE MCE로 보고되지 않음
- 2계층 ECC: DDR5 서버 환경에서는 On-Die ECC(1차) + 호스트 ECC/Chipkill(2차)의 이중 보호가 적용됨
MCE 모니터링에 미치는 영향
- CE 감소 착시: DIMM 셀이 열화되더라도 On-Die ECC가 교정하므로 호스트 CE 카운트가 낮게 유지됨. DIMM이 실제로는 악화 중이어도 "정상"으로 보일 수 있음
- 급격한 UCE 전환: On-Die ECC 교정 한계를 넘는 오류(2비트 이상)가 발생하면, CE 없이 갑자기 UCE로 나타날 수 있음
- 기존 CE 임계값 무효화: DDR4 시절 "CE 100회/일 초과 시 DIMM 교체" 같은 정책이 DDR5에서는 적합하지 않을 수 있음
DDR5 시대의 대안적 건강 모니터링
- DDR5 ECS(Error Check and Scrub): JEDEC 표준의 인밴드(In-band) ECS 명령으로 DRAM 내부 오류 카운터 조회 가능. 호스트 메모리 컨트롤러가 주기적으로 ECS 수행
- 내부 에러 로깅: 일부 DDR5 DRAM은 MR(Mode Register)를 통해 On-Die ECC 교정 횟수를 노출. 하드웨어 벤더별 지원 수준 차이 있음
- CXL 메모리 건강 명령: CXL 연결 메모리의 경우 CXL 2.0+ Health Status 명령(
Get Health Info)으로 미디어 에러율, 온도, 수명 정보를 직접 조회 - Intel PPR(Post Package Repair): Hard PPR은 결함 있는 DRAM 행을 영구적으로 스페어 행으로 대체. Soft PPR은 재부팅 전까지만 유효한 임시 수리
- Device-level Sparing: Intel Sapphire Rapids 이후, 결함 DRAM 디바이스를 투명하게 스페어 디바이스로 교체 (ADDDC 확장)
DDR4 ECC vs DDR5 On-Die ECC + Host ECC 비교
| 비교 항목 | DDR4 (Host ECC만) | DDR5 (On-Die ECC + Host ECC) |
|---|---|---|
| 1차 ECC | 없음 (호스트 ECC만) | On-Die ECC (DRAM 내부 SECDED) |
| 2차 ECC | SECDED / Chipkill (호스트) | SECDED / Chipkill (호스트) |
| 단일 비트 오류 | 호스트 CE MCE 생성 | On-Die에서 교정, CE MCE 미생성 |
| 2비트 오류 (같은 워드) | 호스트 UCE → memory_failure() | On-Die ECC 미교정 → 호스트 ECC 감지, CE 또는 UCE |
| CE 기반 DIMM 건강 감시 | 효과적 (CE 증가 = 열화 신호) | 제한적 (On-Die ECC가 CE를 마스킹) |
| 대안 모니터링 | CE 카운터, EDAC sysfs | ECS, 내부 에러 로깅, CXL Health, PPR 이력 |
| UCE 발생 패턴 | CE 점진적 증가 후 UCE 전환 | CE 경고 없이 갑작스런 UCE 가능 |
| Patrol Scrub 효과 | 잠재 CE 조기 발견에 효과적 | On-Die 교정으로 마스킹된 오류는 감지 불가, ECS 병행 필요 |
| RAS 운영 전략 | CE 임계값 기반 사전 교체 | 다층 모니터링 필요 (ECS + CE + CXL Health + PPR 이력 종합 판단) |
rasdaemon의 DDR5 지원 버전(v0.8.0+)을 사용하여 ECS 이벤트와 CE를 함께 모니터링하세요.
CE 카운트만으로 DIMM 교체를 판단하지 말고, ECS 결과와 PPR 이력을 종합적으로 평가해야 합니다.
MCE 운영 플레이북
CE 인시던트 대응 체크리스트
- 에러 수집:
ras-mc-ctl --summary로 DIMM별 CE 카운트 확인 - 추세 분석: rasdaemon SQLite에서 시간대별 CE 빈도 쿼리
- DIMM 식별: EDAC sysfs에서 소켓/채널/DIMM 슬롯 위치 확인
- 임계값 판단: 24시간 내 CE > 100 → DIMM 교체 권장 (벤더 가이드 참조)
- 선제 조치:
soft_offline_page()로 문제 페이지 사전 격리 고려
UC 인시던트 대응 체크리스트
- 서비스 영향 확인: SIGBUS로 종료된 프로세스 확인 (
dmesg | grep -i sigbus) - HWPoison 상태:
page-types -l -b hwpoison으로 오프라인 페이지 수 확인 - 이전 CE 이력: 동일 DIMM에서 CE 누적이 있었는지 확인 (예측 가능했는지)
- DIMM 교체: UC 발생 DIMM은 교체 우선순위 최상위로 에스컬레이션
- 마이크로코드: 최신 마이크로코드 적용 여부 확인
패닉 후 복구 체크리스트
- BERT 확인: 재부팅 후
dmesg | grep BERT로 이전 에러 정보 수집 - crash dump: kdump/ERST로 저장된 vmcore 분석 (
crash유틸리티) - BIOS 에러 로그: BMC/IPMI SEL 로그 확인 (
ipmitool sel list) - 하드웨어 점검: CPU/DIMM/보드 교체 결정
- 사후 보고서: 타임라인, root cause, 재발 방지 작성
모니터링 설정 권장사항
# rasdaemon + 알림 설정 예시
# 1. rasdaemon 서비스 활성화
$ sudo systemctl enable --now rasdaemon
# 2. CE 모니터링 스크립트 (cron에 등록)
$ cat /usr/local/bin/mce-monitor.sh
#!/bin/bash
CE_COUNT=$(sqlite3 /var/lib/rasdaemon/ras-mc_event.db \
"SELECT COUNT(*) FROM mc_event WHERE err_type='Corrected' AND timestamp > strftime('%s','now','-1 hour');")
if [ "$CE_COUNT" -gt 10 ]; then
echo "ALERT: $CE_COUNT CE errors in last hour" | mail -s "MCE Alert" ops@example.com
fi
# 3. journald 기반 실시간 감시
$ journalctl -k -f -g "mce:|EDAC|Hardware Error"
관련 문서
MCE는 하드웨어 에러 처리의 핵심 인프라로, 커널의 여러 서브시스템과 깊이 연관됩니다. 아래 문서들은 MCE의 다양한 측면을 이해하는 데 도움이 됩니다.
- 프로세서 공식 사양
- Intel SDM Vol. 3B, Chapter 15–16 "Machine-Check Architecture" — MCA 뱅크, 에러 코드, 복구 모델의 공식 사양입니다
- AMD BKDG/PPR (Processor Programming Reference) — AMD Scalable MCA(SMCA) 뱅크 타입, IPID, SYND 레지스터 사양입니다
- UEFI Specification, Appendix N "Common Platform Error Record" — CPER 포맷 정의입니다
- ACPI Specification, Chapter 18 "ACPI Platform Error Interfaces (APEI)" — HEST/BERT/ERST/EINJ 사양입니다
- 커널 공식 문서
- Machine Check Exception — The Linux Kernel documentation — x86 MCE 서브시스템의 공식 커널 문서입니다
- APEI Error Injection (EINJ) — The Linux Kernel documentation — APEI EINJ를 통한 하드웨어 에러 주입 방법을 설명합니다
- Reliability, Availability and Serviceability (RAS) — The Linux Kernel documentation — 리눅스 RAS 프레임워크의 전체 구조를 설명합니다
- EDAC — Error Detection And Correction — The Linux Kernel documentation — EDAC 드라이버 API와 메모리 에러 보고 체계를 설명합니다
- Kernel Parameters — The Linux Kernel documentation —
mce=,edac_report=등 MCE/EDAC 관련 부트 파라미터를 확인할 수 있습니다 - 커널 소스 코드 (Elixir Cross Referencer)
- arch/x86/kernel/cpu/mce/ — MCE 핵심 구현 코드입니다 (
core.c,severity.c,intel.c,amd.c등) - mm/memory-failure.c — HWPoison 기반 메모리 페이지 오프라인 처리 코드입니다
- drivers/edac/ — EDAC 드라이버 디렉터리입니다 (
edac_mc.c,skx_edac.c,amd64_edac.c등) - drivers/acpi/apei/ — APEI 구현 코드입니다 (
ghes.c,einj.c,erst.c등) - drivers/ras/cec.c — Corrected Errors Collector 구현 코드로, CE 패턴 기반 페이지 오프라인 결정 로직입니다
- include/linux/ras.h — RAS 프레임워크 헤더로, 트레이스포인트 및 CEC 인터페이스를 정의합니다
- LWN.net 기사
- Machine check handling on Linux (LWN, 2007) — 리눅스 MCE 처리의 기초를 설명하는 초기 심층 기사입니다
- Memory error handling in the kernel (LWN, 2009) — 메모리 에러 감지와 HWPoison 메커니즘의 도입 배경을 다룹니다
- Machine-check exception recovery (LWN, 2015) — UCR(Uncorrected Recoverable) 에러에서의 복구 전략을 설명합니다
- Correctable memory errors should not cause kernel panics (LWN, 2019) — CE 임계값과 커널 패닉 정책에 대한 논의입니다
- Copy-on-write poison recovery (LWN, 2022) — CoW 시 poison 페이지 전파를 방지하는 복구 기법을 다룹니다
- mcelog 및 rasdaemon
- mcelog — Machine Check Event Logger — x86 MCE 이벤트를 사용자 공간에서 수집·분석하는 전통적인 도구입니다
- rasdaemon — RAS Error Logging Daemon — 커널 트레이스포인트 기반으로 MCE, EDAC, PCIe AER 등 하드웨어 에러를 기록하는 최신 도구입니다
- 컨퍼런스 발표 및 기술 문서
- Tony Luck — "Linux Kernel Machine Check Recovery" (Linux Plumbers Conference 2019) — Intel MCE 유지보수자의 복구 아키텍처 발표입니다
- Andi Kleen — "Dealing with Machine Check Exceptions in the Linux Kernel" (OLS 2006) — 리눅스 MCE 서브시스템 설계 원칙을 다룬 초기 논문입니다
- Borislav Petkov — "RAS in Linux" (LinuxCon 2018) — 리눅스 RAS 스택 전체 구조와 최신 개선 사항을 설명합니다
- Intel Machine Check Architecture Technical Reference — Intel MCA 에러 코드 디코딩 레퍼런스입니다
- "Linux Memory Error Handling Mechanisms and Recent Progress" (SNIA, 2023) — 메모리 에러 핸들링의 최근 발전 동향을 종합적으로 다룹니다