Interrupt controller는 “누가, 어느 CPU에, 얼마나 급하게”를 정한다
장치가 interrupt line이나 MSI를 발생시키면 GIC는 INTID, priority, group, target affinity를 바탕으로 처리할 PE를 고른다. CPU가 IRQ exception으로 들어간 뒤 Linux가 INTID를 driver의 handler와 연결한다.
SPI의 전역 enable·priority·routing을 관리한다.
CPU별 SGI·PPI·LPI 상태와 power를 관리한다.
system register로 acknowledge, priority와 EOI를 처리한다.
PCIe MSI의 DeviceID·EventID를 LPI와 collection으로 변환한다.
INTID 종류가 routing과 Linux handler를 바꾼다
| 종류 | 범위·대상 | 대표 용도 |
|---|---|---|
| SGI | software-generated, CPU별 | reschedule, TLB shootdown 같은 kernel IPI. |
| PPI | private peripheral, CPU별 | architected timer, local maintenance IRQ. |
| SPI | shared peripheral, routing 가능 | UART, network, storage controller. |
| LPI | memory-backed property/pending table | ITS가 변환한 대규모 MSI. |
Physical IRQ가 driver handler에 도달하는 경로
GICv3 IRQ flow
device asserts IRQ / sends MSI
→ GIC selects target PE
→ arm64 IRQ vector entry
→ gic_handle_irq(regs)
→ ICC_IAR1_EL1에서 INTID acknowledge
→ __gic_handle_irq(INTID, regs)
→ generic_handle_domain_irq(domain, INTID)
→ irq_desc flow handler
→ registered device actiongic_handle_irq()는 interrupt mask 상태에 맞는 helper를 선택하고, __gic_handle_irq()이 domain dispatch를 수행한다.
irq_domain이 hardware INTID를 Linux IRQ로 바꾼다
driver는 보통 GIC INTID를 직접 배열 index로 쓰지 않는다. irq_domain이 firmware specifier를 hardware IRQ와 Linux virtual IRQ에 연결하고 struct irq_desc가 chip method와 action list를 보유한다.
- Device Tree의
interrupts또는 ACPI GSI를 domain specifier로 해석한다. gic_irq_domain_translate()가 hwirq와 trigger type을 얻는다.gic_irq_domain_alloc()이 Linux IRQ descriptor를 할당한다.gic_irq_domain_map()이 SGI/PPI/SPI별 flow handler와 chip을 설정한다.- runtime에
generic_handle_domain_irq()가 reverse mapping을 찾아 handler를 실행한다.
실제 map 지점은 gic_irq_domain_map()이다.
EOI와 deactivate는 level interrupt에서 특히 중요하다
ICC_EOIR1_EL1 write는 priority drop을 수행하고 mode에 따라 deactivate가 분리될 수 있다. level-triggered device의 원인을 지우지 않은 채 GIC만 deactivate하면 line이 다시 assert되어 interrupt storm이 난다.
| 단계 | 해야 할 일 | 실패 증상 |
|---|---|---|
| Acknowledge | IAR에서 INTID 읽기 | spurious ID와 실제 IRQ를 구분하지 못함. |
| Device clear | status read·write로 interrupt 원인 제거 | 같은 IRQ가 즉시 재발. |
| EOI | priority drop | 낮은 priority IRQ가 막힘. |
| Deactivate | active state 종료 | active bit가 남거나 잘못된 재주입. |
Linux IPI는 GIC SGI를 사용한다
gic_ipi_send_mask()은 cpumask를 affinity cluster별로 묶어 SGI system register write를 만든다. scheduler reschedule, call-function와 TLB shootdown처럼 CPU 사이 동기화에 쓰인다.
IPI가 많으면 GIC 문제로 단정하지 말고 어떤 kernel subsystem이 보냈는지 /proc/interrupts의 IPI 항목과 tracepoint를 함께 본다.
Virtual IRQ는 software state를 거쳐 guest List Register로 들어간다
KVM vGIC injection
irqfd / emulated device / virtual timer
→ kvm_vgic_inject_irq
→ vgic_irq pending state 갱신
→ vgic_queue_irq_unlock
→ target vCPU wakeup
→ guest entry 전에 vgic_v3_populate_lr
→ ICH_LR*_EL2
→ guest가 virtual ICC_IAR1_EL1로 acknowledgekvm_vgic_inject_irq()은 level과 target을 갱신하고 queue 여부를 결정한다.
List Register는 pending virtual interrupt의 hardware window다
vGIC는 모든 virtual IRQ를 항상 LR에 넣을 수 없다. LR 수가 제한되어 있어 우선순위가 높은 pending IRQ를 골라 넣고, guest가 처리한 결과를 exit 때 software state로 접는다.
| 함수 | 방향 | 역할 |
|---|---|---|
| vgic_v3_populate_lr() | software → ICH_LR | virtual INTID, priority, pending/active, HW mapping을 encoding. |
| vgic_v3_fold_lr_state() | ICH_LR → software | guest EOI·active 변화와 pending 상태를 vgic_irq에 반영. |
| vgic_queue_irq_unlock() | queue 관리 | target vCPU와 LR 가용성에 따라 pending queue를 갱신. |
ITS는 MSI의 두 번호를 LPI로 번역한다
PCIe device가 GITS_TRANSLATER에 MSI write를 보내면 ITS는 Device Table에서 DeviceID, Interrupt Translation Table에서 EventID를 찾아 LPI와 target collection을 결정한다.
Physical ITS
PCIe requester emits MSI
→ DeviceID + EventID
→ GITS_TRANSLATER
→ Device Table / ITT lookup
→ LPI + Collection
→ Redistributor pending table
→ target PEits_irq_domain_activate()이 MSI domain activation과 device/event mapping을 연결한다.
Virtual ITS
QEMU/KVM MSI
→ vgic_its_inject_msi
→ virtual DeviceID/EventID resolve
→ virtual LPI pending
→ target vCPU vGIC queue
→ ICH_LR 또는 virtual pending tablevgic_its_inject_msi()가 virtual translation을 시작한다.
Linux v6.18.37 소스 지도
| 파일 | 심볼 | 책임 |
|---|---|---|
| drivers/irqchip/irq-gic-v3.c | gic_handle_irq, gic_irq_domain_map | physical GICv3 CPU interface와 IRQ domain |
| irq-gic-v3-its.c | its_irq_domain_activate | MSI·LPI와 physical ITS |
| kvm/vgic/vgic.c | kvm_vgic_inject_irq, vgic_queue_irq_unlock | virtual IRQ software state |
| kvm/vgic/vgic-v3.c | populate_lr, fold_lr_state | List Register 변환 |
| kvm/vgic/vgic-its.c | vgic_its_inject_msi | virtual ITS command와 LPI |
| include/linux/irqdomain.h | struct irq_domain | hwirq↔Linux IRQ mapping |
Interrupt storm과 VM latency를 추적한다
Host 확인
watch -n1 cat /proc/interrupts
grep . /proc/irq/$IRQ/{smp_affinity_list,effective_affinity_list}
sudo trace-cmd record -e irq -e irq_vectors -e kvm
sudo perf kvm stat live
cat /sys/kernel/debug/irq/irqs/$IRQ 2>/dev/nullphysical INTID, Linux IRQ, irqchip, affinity, handler 실행 시간과 vGIC injection을 같은 사건으로 연결한다. VM 안의 IRQ 번호와 host IRQ 번호가 같을 것이라 기대하면 안 된다.
근거 자료
- Local sourceLinux v6.18.37 commit
0c503cf3dde2 - GICArm Generic Interrupt Controller Architecture Specification
- GICv5Introducing GICv5
- LinuxLinux generic IRQ documentation