← ARMv9 해설DUJINLABS.COM

Source deep dive 07 · GICv3 · irq_domain · vGIC · ITS

ARM GICv3·vGIC·ITS interrupt 소스 분석

장치가 CPU에 알림을 보낸다는 설명에서 시작해 ICC_IAR1_EL1에서 INTID를 읽고 Linux IRQ 번호로 변환하며, VM에는 ICH_LR_EL2를 통해 virtual interrupt를 넣는 실제 흐름을 분석한다.

Kernel
Linux v6.18.37
Physical
GICv3
Virtual
vGICv3
MSI
ITS · LPI

Interrupt controller는 “누가, 어느 CPU에, 얼마나 급하게”를 정한다

장치가 interrupt line이나 MSI를 발생시키면 GIC는 INTID, priority, group, target affinity를 바탕으로 처리할 PE를 고른다. CPU가 IRQ exception으로 들어간 뒤 Linux가 INTID를 driver의 handler와 연결한다.

Distributor

SPI의 전역 enable·priority·routing을 관리한다.

Redistributor

CPU별 SGI·PPI·LPI 상태와 power를 관리한다.

CPU interface

system register로 acknowledge, priority와 EOI를 처리한다.

ITS

PCIe MSI의 DeviceID·EventID를 LPI와 collection으로 변환한다.

INTID 종류가 routing과 Linux handler를 바꾼다

종류범위·대상대표 용도
SGIsoftware-generated, CPU별reschedule, TLB shootdown 같은 kernel IPI.
PPIprivate peripheral, CPU별architected timer, local maintenance IRQ.
SPIshared peripheral, routing 가능UART, network, storage controller.
LPImemory-backed property/pending tableITS가 변환한 대규모 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 action

gic_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를 보유한다.

  1. Device Tree의 interrupts 또는 ACPI GSI를 domain specifier로 해석한다.
  2. gic_irq_domain_translate()가 hwirq와 trigger type을 얻는다.
  3. gic_irq_domain_alloc()이 Linux IRQ descriptor를 할당한다.
  4. gic_irq_domain_map()이 SGI/PPI/SPI별 flow handler와 chip을 설정한다.
  5. 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이 난다.

단계해야 할 일실패 증상
AcknowledgeIAR에서 INTID 읽기spurious ID와 실제 IRQ를 구분하지 못함.
Device clearstatus read·write로 interrupt 원인 제거같은 IRQ가 즉시 재발.
EOIpriority drop낮은 priority IRQ가 막힘.
Deactivateactive 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로 acknowledge

kvm_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_LRvirtual INTID, priority, pending/active, HW mapping을 encoding.
vgic_v3_fold_lr_state()ICH_LR → softwareguest EOI·active 변화와 pending 상태를 vgic_irq에 반영.
vgic_queue_irq_unlock()queue 관리target vCPU와 LR 가용성에 따라 pending queue를 갱신.

실제 구현은 foldpopulate에서 볼 수 있다.

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 PE

its_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 table

vgic_its_inject_msi()가 virtual translation을 시작한다.

Linux v6.18.37 소스 지도

파일심볼책임
drivers/irqchip/irq-gic-v3.cgic_handle_irq, gic_irq_domain_mapphysical GICv3 CPU interface와 IRQ domain
irq-gic-v3-its.cits_irq_domain_activateMSI·LPI와 physical ITS
kvm/vgic/vgic.ckvm_vgic_inject_irq, vgic_queue_irq_unlockvirtual IRQ software state
kvm/vgic/vgic-v3.cpopulate_lr, fold_lr_stateList Register 변환
kvm/vgic/vgic-its.cvgic_its_inject_msivirtual ITS command와 LPI
include/linux/irqdomain.hstruct irq_domainhwirq↔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/null

physical INTID, Linux IRQ, irqchip, affinity, handler 실행 시간과 vGIC injection을 같은 사건으로 연결한다. VM 안의 IRQ 번호와 host IRQ 번호가 같을 것이라 기대하면 안 된다.

근거 자료