← HardwareDUJINLABS.COM

Hardware 07 · Interrupt · Linux v6.18.37

x86 APIC·IOAPIC·MSI: vector에서 Linux IRQ까지

legacy PIC에서 Local APIC, IOAPIC, MSI/MSI-X, x2APIC, interrupt remapping으로 이어지는 x86 interrupt 구조를 한 장치 사건의 주소·데이터·vector 변화로 연결합니다.

검증 깊이
board → Linux 8 layers
그림
4 boundary diagrams
원본 코드
2 excerpts · 116 lines
기준
Linux v6.18.37

01 · QUESTION

무엇을 증명할 것인가

장치가 interrupt를 만들었는데 CPU vector가 오지 않을 때 IOAPIC redirection entry, MSI message, Local APIC IRR/ISR, IDT 중 어느 경계를 먼저 확인해야 하는가?

legacy PIC에서 Local APIC, IOAPIC, MSI/MSI-X, x2APIC, interrupt remapping으로 이어지는 x86 interrupt 구조를 한 장치 사건의 주소·데이터·vector 변화로 연결합니다. 이 글은 Linux 로그를 출발점으로 삼지 않습니다. 같은 사건이 보드, controller, firmware, kernel에서 어떤 이름과 번호로 바뀌는지 아래에서 위로 연결합니다.

02 · CONTRACT

입력과 완료 조건

구분확인할 내용
입력legacy IRQ pin 또는 MSI address/data write, 활성 Local APIC, 유효한 IDT gate와 Linux vector allocation
출력대상 logical CPU가 vector를 수신해 common_interrupt로 진입하고 Linux irq_desc와 device handler를 실행함
소유권장치 원인은 driver, IOAPIC/MSI route는 irqdomain과 PCI/MSI core, vector는 x86 vector allocator, in-service/EOI는 Local APIC이 관리
완료 조건장치 원인이 제거되고 Local APIC EOI 뒤 ISR bit가 내려가며 vector와 Linux IRQ affinity가 의도한 CPU에서 안정적으로 처리됨

03 · BOTTOM-UP

완전히 아래에서 시작하는 8단계

그림 1. 아래에서 Linux까지 올라오는 8개 검증 경계위 계층의 로그보다 아래 계층의 물리 증거를 먼저 확정합니다.
01
1. Legacy baseline

8259A PIC의 IRQ0~15, cascade, edge/level 한계를 출발점으로 잡습니다. 최신 시스템에서도 early boot와 호환 경로를 이해해야 APIC 전환 로그를 해석할 수 있습니다.

START
02
2. Local APIC

각 logical CPU의 APIC ID, SVR, TPR, IRR, ISR, TMR, EOI, LVT를 확인합니다. xAPIC MMIO와 x2APIC MSR 접근 방식은 다르지만 vector와 priority 모델은 이어집니다.

CHECKPOINT
03
3. IOAPIC

핀 기반 장치 interrupt는 redirection table의 vector, delivery mode, polarity, trigger, mask, destination으로 변환됩니다. ACPI MADT interrupt source override가 legacy IRQ와 GSI 관계를 바꿀 수 있습니다.

CHECKPOINT
04
4. IDT and vector

x86 IDT의 32~255 vector 중 Linux가 device IRQ용 vector를 CPU별로 할당합니다. Linux IRQ 번호, GSI, IOAPIC pin, APIC vector는 서로 다른 번호 공간입니다.

CHECKPOINT
05
5. Entry path

CPU가 IDT gate를 따라 entry stub로 들어오면 pt_regs를 만들고 common_interrupt가 vector를 Linux irqdomain/irq_desc로 변환합니다. handler 전후 Local APIC EOI 시점도 chip flow handler에 달려 있습니다.

CHECKPOINT
06
6. MSI/MSI-X

PCIe 장치는 물리 핀 대신 APIC 대상 주소와 vector data를 Memory Write TLP로 보냅니다. MSI-X table entry마다 address/data/mask가 있어 queue별 affinity를 구성할 수 있습니다.

CHECKPOINT
07
7. Interrupt remapping

IOMMU의 interrupt remapping이 켜지면 장치 message는 직접 APIC 목적지를 담기보다 IRTE index/subhandle을 참조합니다. 보안과 CPU 수 확장성이 좋아지지만 검증 경계가 하나 늘어납니다.

CHECKPOINT
08
8. Linux runtime

ACPI/MADT, APIC mode 선택, irqdomain, vector allocation, request_irq, affinity 변경까지 연결합니다. `/proc/interrupts`만 보지 말고 장치 status와 APIC vector trace를 함께 대응시킵니다.

LINUX / RESULT
01

1. Legacy baseline

8259A PIC의 IRQ0~15, cascade, edge/level 한계를 출발점으로 잡습니다. 최신 시스템에서도 early boot와 호환 경로를 이해해야 APIC 전환 로그를 해석할 수 있습니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 2. Local APIC로 이동합니다.

02

2. Local APIC

각 logical CPU의 APIC ID, SVR, TPR, IRR, ISR, TMR, EOI, LVT를 확인합니다. xAPIC MMIO와 x2APIC MSR 접근 방식은 다르지만 vector와 priority 모델은 이어집니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 3. IOAPIC로 이동합니다.

03

3. IOAPIC

핀 기반 장치 interrupt는 redirection table의 vector, delivery mode, polarity, trigger, mask, destination으로 변환됩니다. ACPI MADT interrupt source override가 legacy IRQ와 GSI 관계를 바꿀 수 있습니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 4. IDT and vector로 이동합니다.

04

4. IDT and vector

x86 IDT의 32~255 vector 중 Linux가 device IRQ용 vector를 CPU별로 할당합니다. Linux IRQ 번호, GSI, IOAPIC pin, APIC vector는 서로 다른 번호 공간입니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 5. Entry path로 이동합니다.

05

5. Entry path

CPU가 IDT gate를 따라 entry stub로 들어오면 pt_regs를 만들고 common_interrupt가 vector를 Linux irqdomain/irq_desc로 변환합니다. handler 전후 Local APIC EOI 시점도 chip flow handler에 달려 있습니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 6. MSI/MSI-X로 이동합니다.

06

6. MSI/MSI-X

PCIe 장치는 물리 핀 대신 APIC 대상 주소와 vector data를 Memory Write TLP로 보냅니다. MSI-X table entry마다 address/data/mask가 있어 queue별 affinity를 구성할 수 있습니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 7. Interrupt remapping로 이동합니다.

07

7. Interrupt remapping

IOMMU의 interrupt remapping이 켜지면 장치 message는 직접 APIC 목적지를 담기보다 IRTE index/subhandle을 참조합니다. 보안과 CPU 수 확장성이 좋아지지만 검증 경계가 하나 늘어납니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 8. Linux runtime로 이동합니다.

08

8. Linux runtime

ACPI/MADT, APIC mode 선택, irqdomain, vector allocation, request_irq, affinity 변경까지 연결합니다. `/proc/interrupts`만 보지 말고 장치 status와 APIC vector trace를 함께 대응시킵니다.

이 단계의 통과 증거를 저장한 뒤에만 최종 사용자 결과를 판정합니다.

04 · DIAGRAMS

주소와 상태가 이동하는 모습

그림 2. Interrupt 실행 경로왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 Device pin
02 GSI/IOAPIC pin
03 redirection entry
04 Local APIC IRR
05 IDT vector
06 common_interrupt
07 irq_desc
08 handler+EOI
그림 3. firmware와 Linux 번호 공간의 연결왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 PCIe queue
02 MSI-X table address/data
03 Memory Write TLP
04 IRTE optional
05 Local APIC vector
06 Linux handler
그림 4. 입력, 소유권, 완료 조건의 경계완료 조건을 충족하기 전에는 다음 계층이 해당 자원을 재사용하면 안 됩니다.
INPUT

legacy IRQ pin 또는 MSI address/data write, 활성 Local APIC, 유효한 IDT gate와 Linux vector allocation

OWNER

장치 원인은 driver, IOAPIC/MSI route는 irqdomain과 PCI/MSI core, vector는 x86 vector allocator, in-service/EOI는 Local APIC이 관리

OUTPUT

대상 logical CPU가 vector를 수신해 common_interrupt로 진입하고 Linux irq_desc와 device handler를 실행함

DONE

장치 원인이 제거되고 Local APIC EOI 뒤 ISR bit가 내려가며 vector와 Linux IRQ affinity가 의도한 CPU에서 안정적으로 처리됨

05 · DETAILED MANUAL

x86 PIC, APIC, IOAPIC와 MSI를 번호 공간별로 읽는 상세 매뉴얼

legacy IRQ/PIC, ACPI GSI와 IOAPIC input, Linux virq, Local APIC vector, MSI message address/data를 분리합니다. MADT topology에서 시작해 IRR/ISR/TMR, IDT entry, vector allocation, interrupt remapping과 EOI까지 연결합니다.

공개적으로 다시 확인할 수 있는 자료

공개되지 않은 vendor register나 integration별 offset을 추측해서 채우지 않았습니다. 아래 제조사 자료, architecture 문서와 Linux v6.18.37 원본에서 다시 확인할 수 있는 범위만 사용했습니다.

06 · REGISTERS

읽어야 할 상태와 해석

값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.

레지스터/상태소유 블록검증할 의미
IA32_APIC_BASECPU MSRLocal APIC enable, x2APIC mode, base
SVR / TPR / PPRLocal APICsoftware enable과 현재 priority mask
IRR / ISR / TMR / EOILocal APICpending, servicing, trigger mode, completion
ICR / LVTLocal APICIPI와 timer/error/local interrupt routing
IOAPIC RTE[n]IOAPICvector, polarity, trigger, mask, destination
IDTR / IDT gateCPUvector별 entry address와 gate attributes
MSI/MSI-X address/dataPCI functiondestination/APIC message와 vector
IRTEVT-d/AMD IOMMUsource validation, destination, vector remap

07 · LINUX SOURCE

Linux v6.18.37 원본 코드와 줄별 해설

라인 번호를 고정해 외우는 대신 함수 선언을 기준으로 Linux v6.18.37 tree에서 다시 찾았습니다. 로컬 tree에 있는 파일은 실제 코드를 직접 싣고, 나머지는 원본 파일과 읽을 함수 좌표를 연결했습니다.

Linux v6.18.37 · 실제 원본 코드

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로

arch/x86/kernel/apic/apic.c:1345-1413

1345	if (!lapic_is_integrated())		/* 82489DX */
1346		value |= APIC_LVT_LEVEL_TRIGGER;
1347	if (apic_extnmi == APIC_EXTNMI_NONE)
1348		value |= APIC_LVT_MASKED;
1349	apic_write(APIC_LVT1, value);
1350}
1351 
1352static void __init apic_bsp_setup(bool upmode);
1353 
1354/* Init the interrupt delivery mode for the BSP */
1355void __init apic_intr_mode_init(void)
1356{
1357	bool upmode = IS_ENABLED(CONFIG_UP_LATE_INIT);
1358 
1359	switch (apic_intr_mode) {
1360	case APIC_PIC:
1361		pr_info("APIC: Keep in PIC mode(8259)\n");
1362		return;
1363	case APIC_VIRTUAL_WIRE:
1364		pr_info("APIC: Switch to virtual wire mode setup\n");
1365		break;
1366	case APIC_VIRTUAL_WIRE_NO_CONFIG:
1367		pr_info("APIC: Switch to virtual wire mode setup with no configuration\n");
1368		upmode = true;
1369		break;
1370	case APIC_SYMMETRIC_IO:
1371		pr_info("APIC: Switch to symmetric I/O mode setup\n");
1372		break;
1373	case APIC_SYMMETRIC_IO_NO_ROUTING:
1374		pr_info("APIC: Switch to symmetric I/O mode setup in no SMP routine\n");
1375		break;
1376	}
1377 
1378	x86_64_probe_apic();
1379 
1380	if (x86_platform.apic_post_init)
1381		x86_platform.apic_post_init();
1382 
1383	apic_bsp_setup(upmode);
1384}
1385 
1386static void lapic_setup_esr(void)
1387{
1388	unsigned int oldvalue, value, maxlvt;
1389 
1390	if (!lapic_is_integrated()) {
1391		pr_info("No ESR for 82489DX.\n");
1392		return;
1393	}
1394 
1395	if (apic->disable_esr) {
1396		/*
1397		 * Something untraceable is creating bad interrupts on
1398		 * secondary quads ... for the moment, just leave the
1399		 * ESR disabled - we can't do anything useful with the
1400		 * errors anyway - mbligh
1401		 */
1402		pr_info("Leaving ESR disabled.\n");
1403		return;
1404	}
1405 
1406	maxlvt = lapic_get_maxlvt();
1407	if (maxlvt > 3)		/* Due to the Pentium erratum 3AP. */
1408		apic_write(APIC_ESR, 0);
1409	oldvalue = apic_read(APIC_ESR);
1410 
1411	/* enables sending errors */
1412	value = ERROR_APIC_VECTOR;
1413	apic_write(APIC_LVTERR, value);
L1345 if (!lapic_is_integrated()) /* 82489DX */

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1346 value |= APIC_LVT_LEVEL_TRIGGER;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1347 if (apic_extnmi == APIC_EXTNMI_NONE)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1348 value |= APIC_LVT_MASKED;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1349 apic_write(APIC_LVT1, value);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1350 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1351 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1352 static void __init apic_bsp_setup(bool upmode);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 입력 계약을 만족하는지 확인합니다.

L1353 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1354 /* Init the interrupt delivery mode for the BSP */

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1355 void __init apic_intr_mode_init(void)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 입력 계약을 만족하는지 확인합니다.

L1356 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1357 bool upmode = IS_ENABLED(CONFIG_UP_LATE_INIT);

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 입력 계약을 만족하는지 확인합니다.

L1358 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1359 switch (apic_intr_mode) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1360 case APIC_PIC:

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1361 pr_info("APIC: Keep in PIC mode(8259)\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1362 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1363 case APIC_VIRTUAL_WIRE:

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1364 pr_info("APIC: Switch to virtual wire mode setup\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1365 break;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1366 case APIC_VIRTUAL_WIRE_NO_CONFIG:

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1367 pr_info("APIC: Switch to virtual wire mode setup with no configuration\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1368 upmode = true;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1369 break;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1370 case APIC_SYMMETRIC_IO:

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1371 pr_info("APIC: Switch to symmetric I/O mode setup\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1372 break;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1373 case APIC_SYMMETRIC_IO_NO_ROUTING:

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1374 pr_info("APIC: Switch to symmetric I/O mode setup in no SMP routine\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1375 break;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1376 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1377 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1378 x86_64_probe_apic();

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1379 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1380 if (x86_platform.apic_post_init)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1381 x86_platform.apic_post_init();

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1382 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1383 apic_bsp_setup(upmode);

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1384 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1385 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1386 static void lapic_setup_esr(void)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 입력 계약을 만족하는지 확인합니다.

L1387 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1388 unsigned int oldvalue, value, maxlvt;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1389 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1390 if (!lapic_is_integrated()) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1391 pr_info("No ESR for 82489DX.\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1392 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1393 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1394 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1395 if (apic->disable_esr) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1396 /*

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1397 * Something untraceable is creating bad interrupts on

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1398 * secondary quads ... for the moment, just leave the

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1399 * ESR disabled - we can't do anything useful with the

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1400 * errors anyway - mbligh

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1401 */

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1402 pr_info("Leaving ESR disabled.\n");

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1403 return;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1404 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1405 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1406 maxlvt = lapic_get_maxlvt();

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1407 if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1408 apic_write(APIC_ESR, 0);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1409 oldvalue = apic_read(APIC_ESR);

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L1410 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1411 /* enables sending errors */

원본 주석입니다. 부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1412 value = ERROR_APIC_VECTOR;

부팅 중 PIC, virtual wire, symmetric I/O APIC interrupt mode를 선택하고 초기화하는 경로의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1413 apic_write(APIC_LVTERR, value);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

Linux v6.18.37 · 실제 원본 코드

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry

arch/x86/kernel/irq.c:310-356

310	handle_irq(desc, regs);
311	return true;
312}
313 
314/*
315 * common_interrupt() handles all normal device IRQ's (the special SMP
316 * cross-CPU interrupts have their own entry points).
317 */
318DEFINE_IDTENTRY_IRQ(common_interrupt)
319{
320	struct pt_regs *old_regs = set_irq_regs(regs);
321 
322	/* entry code tells RCU that we're not quiescent.  Check it. */
323	RCU_LOCKDEP_WARN(!rcu_is_watching(), "IRQ failed to wake up RCU");
324 
325	if (unlikely(!call_irq_handler(vector, regs)))
326		apic_eoi();
327 
328	set_irq_regs(old_regs);
329}
330 
331#ifdef CONFIG_X86_LOCAL_APIC
332/* Function pointer for generic interrupt vector handling */
333void (*x86_platform_ipi_callback)(void) = NULL;
334/*
335 * Handler for X86_PLATFORM_IPI_VECTOR.
336 */
337DEFINE_IDTENTRY_SYSVEC(sysvec_x86_platform_ipi)
338{
339	struct pt_regs *old_regs = set_irq_regs(regs);
340 
341	apic_eoi();
342	trace_x86_platform_ipi_entry(X86_PLATFORM_IPI_VECTOR);
343	inc_irq_stat(x86_platform_ipis);
344	if (x86_platform_ipi_callback)
345		x86_platform_ipi_callback();
346	trace_x86_platform_ipi_exit(X86_PLATFORM_IPI_VECTOR);
347	set_irq_regs(old_regs);
348}
349#endif
350 
351#if IS_ENABLED(CONFIG_KVM)
352static void dummy_handler(void) {}
353static void (*kvm_posted_intr_wakeup_handler)(void) = dummy_handler;
354 
355void kvm_set_posted_intr_wakeup_handler(void (*handler)(void))
356{
L310 handle_irq(desc, regs);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L311 return true;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L312 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L313 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L314 /*

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L315 * common_interrupt() handles all normal device IRQ's (the special SMP

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L316 * cross-CPU interrupts have their own entry points).

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L317 */

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L318 DEFINE_IDTENTRY_IRQ(common_interrupt)

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L319 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L320 struct pt_regs *old_regs = set_irq_regs(regs);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L321 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L322 /* entry code tells RCU that we're not quiescent. Check it. */

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L323 RCU_LOCKDEP_WARN(!rcu_is_watching(), "IRQ failed to wake up RCU");

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L324 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L325 if (unlikely(!call_irq_handler(vector, regs)))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L326 apic_eoi();

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L327 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L328 set_irq_regs(old_regs);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L329 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L330 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L331 #ifdef CONFIG_X86_LOCAL_APIC

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L332 /* Function pointer for generic interrupt vector handling */

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L333 void (*x86_platform_ipi_callback)(void) = NULL;

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 입력 계약을 만족하는지 확인합니다.

L334 /*

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L335 * Handler for X86_PLATFORM_IPI_VECTOR.

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L336 */

원본 주석입니다. IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L337 DEFINE_IDTENTRY_SYSVEC(sysvec_x86_platform_ipi)

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L338 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L339 struct pt_regs *old_regs = set_irq_regs(regs);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L340 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L341 apic_eoi();

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L342 trace_x86_platform_ipi_entry(X86_PLATFORM_IPI_VECTOR);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L343 inc_irq_stat(x86_platform_ipis);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L344 if (x86_platform_ipi_callback)

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L345 x86_platform_ipi_callback();

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L346 trace_x86_platform_ipi_exit(X86_PLATFORM_IPI_VECTOR);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L347 set_irq_regs(old_regs);

IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L348 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L349 #endif

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L350 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L351 #if IS_ENABLED(CONFIG_KVM)

빌드 설정에 따라 포함되는 경로입니다. 실행 중 분기가 아니므로 실제 커널의 .config와 전처리 결과를 함께 봐야 합니다.

L352 static void dummy_handler(void) {}

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 입력 계약을 만족하는지 확인합니다.

L353 static void (*kvm_posted_intr_wakeup_handler)(void) = dummy_handler;

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 입력 계약을 만족하는지 확인합니다.

L354 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L355 void kvm_set_posted_intr_wakeup_handler(void (*handler)(void))

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IDT device vector 진입을 Linux IRQ dispatch로 연결하는 공통 entry의 입력 계약을 만족하는지 확인합니다.

L356 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

Linux v6.18.37 · 원본 좌표

arch/x86/kernel/apic/vector.c

Linux IRQ에 CPU별 APIC vector를 할당하고 이동시키는 구현

읽을 함수
x86_vector_alloc_irqs()
읽을 함수
assign_irq_vector()
읽을 함수
apic_ack_edge()

Linux v6.18.37 원본 파일 열기 →

08 · VERIFICATION

실제 장비에서 확인하는 순서

  1. Firmware topology

    실행 ACPI MADT에서 Local APIC/x2APIC, IOAPIC, interrupt source override 항목을 확인합니다.

    통과 기준 OS가 선택한 APIC ID, GSI base, override가 firmware table과 일치해야 합니다.

  2. Route

    실행 핀 방식이면 IOAPIC RTE, MSI면 PCI capability와 MSI-X table의 mask/address/data를 기록합니다.

    통과 기준 unmask된 entry가 Linux affinity와 같은 목적지/vector를 가리켜야 합니다.

  3. APIC state

    실행 debugger 또는 kernel trace로 해당 vector의 IRR→ISR 이동과 EOI 후 clear를 확인합니다.

    통과 기준 발생 전후 vector 한 개의 상태 변화가 설명 가능해야 합니다.

  4. Linux mapping

    실행 `/proc/interrupts`, `/proc/irq/N/smp_affinity_list`, irq vector trace를 함께 수집합니다.

    통과 기준 Linux IRQ와 실제 처리 CPU/vector의 관계가 affinity 변경 후에도 일관되어야 합니다.

  5. Load and migration

    실행 queue별 traffic을 만들며 CPU hotplug와 affinity 이동을 반복합니다.

    통과 기준 old vector가 quiesce된 뒤 새 CPU vector로 손실·중복 없이 이동해야 합니다.

09 · FAILURE MATRIX

처음 끊긴 경계로 원인을 좁히기

관측 결과우선 의심할 경계다음 증거
장치 status 없음장치 자체 동작 또는 interrupt enable 문제device status/mask
status 있음, IOAPIC/MSI 미전달잘못된 GSI, masked RTE/table, MSI capability 설정 오류RTE 또는 MSI-X entry
message 전달, APIC IRR 없음destination APIC ID, interrupt remap/IRTE 오류IOMMU fault와 APIC state
IRR 있음, entry 없음TPR/PPR priority, IF mask, IDT/vector 설정 문제RFLAGS.IF, TPR, IDT gate
entry 있음, handler/완료 오류vector→virq mapping, shared IRQ, EOI 또는 장치 clear 문제common_interrupt trace와 ISR

핵심은 마지막 오류 메시지가 아니라 처음 기대값과 달라진 checkpoint입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.