01 · QUESTION
무엇을 확인할 것인가
MSI message의 address/data, PCI requester identity, Linux IRQ와 target CPU가 어떤 domain hierarchy로 연결되는가?
PCI MSI core가 vector를 할당하면 hierarchical irqdomain이 device ID와 event/vector를 parent controller hwirq로 변환하고 compose_msi_msg가 device table에 쓸 address/data를 만든다. affinity 변경은 message 또는 remapping table을 갱신한다.
device MSI-X table programming, interrupt-remapper/ITS/IMSIC table update, CPU vector allocation과 irq_desc activation을 구분한다. affinity 변경 절차는 irqchip마다 다르다. ITS의 MOVI, x86 vector 이동, IMSIC의 MSI message 갱신과 이전 vector 정리를 구분한다.
02 · CONTRACT
공통 규약과 아키텍처별 구현
| architecture | 핵심 mechanism | 실패 형태 | 확인할 상태 |
|---|---|---|---|
| arm64 | GICv3 ITS device/event translation과 LPI | 잘못된 DeviceID alias, collection affinity 또는 table cache invalidation 누락은 MSI가 엉뚱한 CPU로 가거나 사라지는 원인이 된다. | BDF/DeviceID, EventID, LPI INTID, collection target, ITS command queue read/write pointer와 pending table을 본다. |
| x86-64 | MSI address/data의 APIC vector 또는 interrupt-remapping IRTE | xAPIC destination width 한계, stale IRTE, source validation 실패 또는 vector migration race가 DMA-remapping fault와 lost interrupt를 만든다. | BDF, MSI address/data, vector, destination APIC ID, IRTE index/source-id, remapping fault와 vector cleanup state를 확인한다. |
| RISC-V | IMSIC per-hart interrupt file과 AIA MSI domain | hart/guest index 계산, MSI page address 또는 EIID width가 틀리면 다른 interrupt file이 pending되거나 write가 fault난다. | hart index, guest index, MSI page PA, EIID, EIP/EIE, parent APLIC source와 irqdomain mapping을 기록한다. |
03 · DIAGRAMS
세 그림으로 먼저 읽기
arm64
- mechanism
- GICv3 ITS device/event translation과 LPI
- state
- PCI requester ID를 DeviceID로, MSI data의 event를 EventID로 사용해 ITS가 collection/target redistributor의 LPI로 변환한다. command queue의 MAPD, MAPTI, MOVI와 SYNC가 device/collection table 상태를 관리한다.
- checkpoint
- BDF/DeviceID, EventID, LPI INTID, collection target, ITS command queue read/write pointer와 pending table을 본다.
x86-64
- mechanism
- MSI address/data의 APIC vector 또는 interrupt-remapping IRTE
- state
- remapping이 없으면 message에 destination APIC ID와 vector를 직접 encode한다. VT-d/AMD remapping이 켜지면 message는 IRTE index를 가리키고 IOMMU가 requester 검증, destination과 vector를 선택한다.
- checkpoint
- BDF, MSI address/data, vector, destination APIC ID, IRTE index/source-id, remapping fault와 vector cleanup state를 확인한다.
RISC-V
- mechanism
- IMSIC per-hart interrupt file과 AIA MSI domain
- state
- MSI write가 target hart/guest interrupt-file address와 EIID data를 사용한다. IMSIC domain이 EIID를 Linux IRQ에 연결하고 APLIC가 wired source를 MSI로 변환해 같은 interrupt file에 전달할 수도 있다.
- checkpoint
- hart index, guest index, MSI page PA, EIID, EIP/EIE, parent APLIC source와 irqdomain mapping을 기록한다.
그림 3 코드 근거: arm64 · x86-64 · RISC-V · x86 MSI message 갱신 · x86 이전 vector 정리
04 · SOURCE
Linux 6.18.37 원본 코드와 주요 구문 해설
소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 원문 전체 발췌를 보존하고, 개별 해설은 근거가 있는 구문에만 붙였습니다.
arm64 · Linux 6.18.37
GICv3 ITS device/event translation과 LPI
PCI requester ID를 DeviceID로, MSI data의 event를 EventID로 사용해 ITS가 collection/target redistributor의 LPI로 변환한다. command queue의 MAPD, MAPTI, MOVI와 SYNC가 device/collection table 상태를 관리한다.
원본 코드: drivers/irqchip/irq-gic-v3-its.c:3677-3757
3677 fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
3678 } else {
3679 return -EINVAL;
3680 }
3681
3682 return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
3683}
3684
3685static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
3686 unsigned int nr_irqs, void *args)
3687{
3688 msi_alloc_info_t *info = args;
3689 struct its_device *its_dev = info->scratchpad[0].ptr;
3690 struct its_node *its = its_dev->its;
3691 struct irq_data *irqd;
3692 irq_hw_number_t hwirq;
3693 int err;
3694 int i;
3695
3696 err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq);
3697 if (err)
3698 return err;
3699
3700 err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev));
3701 if (err)
3702 return err;
3703
3704 for (i = 0; i < nr_irqs; i++) {
3705 err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
3706 if (err)
3707 return err;
3708
3709 irq_domain_set_hwirq_and_chip(domain, virq + i,
3710 hwirq + i, &its_irq_chip, its_dev);
3711 irqd = irq_get_irq_data(virq + i);
3712 irqd_set_single_target(irqd);
3713 irqd_set_affinity_on_activate(irqd);
3714 irqd_set_resend_when_in_progress(irqd);
3715 pr_debug("ID:%d pID:%d vID:%d\n",
3716 (int)(hwirq + i - its_dev->event_map.lpi_base),
3717 (int)(hwirq + i), virq + i);
3718 }
3719
3720 return 0;
3721}
3722
3723static int its_irq_domain_activate(struct irq_domain *domain,
3724 struct irq_data *d, bool reserve)
3725{
3726 struct its_device *its_dev = irq_data_get_irq_chip_data(d);
3727 u32 event = its_get_event_id(d);
3728 int cpu;
3729
3730 cpu = its_select_cpu(d, cpu_online_mask);
3731 if (cpu < 0 || cpu >= nr_cpu_ids)
3732 return -EINVAL;
3733
3734 its_inc_lpi_count(d, cpu);
3735 its_dev->event_map.col_map[event] = cpu;
3736 irq_data_update_effective_affinity(d, cpumask_of(cpu));
3737
3738 /* Map the GIC IRQ and event to the device */
3739 its_send_mapti(its_dev, d->hwirq, event);
3740 return 0;
3741}
3742
3743static void its_irq_domain_deactivate(struct irq_domain *domain,
3744 struct irq_data *d)
3745{
3746 struct its_device *its_dev = irq_data_get_irq_chip_data(d);
3747 u32 event = its_get_event_id(d);
3748
3749 its_dec_lpi_count(d, its_dev->event_map.col_map[event]);
3750 /* Stop the delivery of interrupts */
3751 its_send_discard(its_dev, event);
3752}
3753
3754static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
3755 unsigned int nr_irqs)
3756{
3757 struct irq_data *d = irq_domain_get_irq_data(domain, virq);주요 구문 해설
원본 81줄은 그대로 표시하며, 검토한 55개 구문에 설명을 붙였습니다.
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;irqchip용 firmware node 형식의 두 번째 인자에 상승 에지 트리거를 지정합니다. LPI는 상태를 계속 유지하는 선이 아니라 메시지로 발생시키는 인터럽트입니다.
} else {부모 domain이 장치 트리 형식도 irqchip firmware node 형식도 아니면 지원하는 IRQ 명세를 만들 수 없는 경로로 갑니다.
return -EINVAL;부모 domain의 firmware node 형식을 해석할 수 없어 -EINVAL을 반환합니다. 불완전한 명세로 GIC 자원을 할당하지 않습니다.
return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);만든 LPI 명세로 부모 GIC domain에 IRQ 하나의 할당을 요청하고 그 결과를 반환합니다. ITS의 장치 이벤트가 부모 GIC의 LPI와 연결되는 단계입니다.
static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,ITS domain에서 연속된 Linux IRQ들을 할당하는 함수입니다. 시작 IRQ virq를 장치에 확보한 LPI 번호들과 연결합니다.
unsigned int nr_irqs, void *args)할당할 IRQ 개수와 MSI 공통 계층이 준비한 요청 자료를 받습니다. 요청 자료에서 ITS 장치와 MSI descriptor를 꺼냅니다.
msi_alloc_info_t *info = args;공통 할당 인자 포인터를 MSI 전용 요청 자료로 해석합니다. 여기에는 descriptor와 앞 단계가 남긴 임시 정보가 있습니다.
struct its_device *its_dev = info->scratchpad[0].ptr;MSI 준비 단계가 scratchpad에 남긴 ITS 장치 포인터를 얻습니다. 이 장치의 DeviceID와 LPI 할당 범위를 사용합니다.
struct its_node *its = its_dev->its;해당 장치를 담당하는 ITS 컨트롤러를 가져옵니다. MSI를 기록할 주소와 명령 전송 대상이 장치가 속한 ITS에 따라 결정됩니다.
struct irq_data *irqd;각 Linux IRQ의 정책 비트를 설정할 irq_data 포인터입니다. 단일 CPU 대상·활성화 시 affinity 선택 등을 여기에 기록합니다.
irq_hw_number_t hwirq;이번 장치 요청에 할당되는 첫 LPI 하드웨어 번호를 받습니다. Linux IRQ 시작 번호 virq와는 다른 번호 공간입니다.
int err;LPI 확보·IOMMU 준비·부모 domain 할당의 성공 또는 오류를 보관합니다. 실패 시 뒤의 연결 단계로 진행하지 않게 합니다.
int i;요청한 IRQ 범위에서 몇 번째 항목을 연결하는지 나타냅니다. virq+i와 hwirq+i를 같은 항목으로 대응시킵니다.
err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq);device event 공간에서 연속 hwirq를 확보해 LPI mapping 준비를 한다.
if (err)장치에서 요청한 개수의 LPI를 확보하지 못했는지 확인합니다. 실패하면 IOMMU나 부모 domain을 준비하기 전에 종료합니다.
return err;LPI 할당 오류를 그대로 호출자에 돌려줍니다. 실제 장치 IRQ 연결은 아직 시작하지 않았습니다.
err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev));장치가 MSI를 기록할 ITS 주소를 IOMMU 경로에서도 접근할 수 있도록 준비합니다. MSI descriptor와 장치의 MSI 기준 주소를 넘기며, 필요한 매핑을 만들지 못하면 오류를 반환하고 이후 IRQ 연결을 진행하지 않습니다.
if (err)MSI 쓰기 대상의 IOMMU 준비가 실패했는지 확인합니다. 장치의 메시지가 ITS에 도달할 수 없는 상태로 IRQ 연결을 계속하지 않습니다.
return err;IOMMU MSI 준비에서 받은 오류를 반환합니다. 이 줄 자체가 LPI 비트맵이나 부모 자원을 직접 되돌리는 코드는 아닙니다.
for (i = 0; i < nr_irqs; i++) {요청된 IRQ 개수만큼 Linux IRQ와 LPI를 하나씩 연결합니다. 각 항목에 부모 GIC 자원과 ITS chip 정보를 설정합니다.
err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);할당한 각 Linux IRQ와 LPI 하드웨어 번호를 부모 GIC domain에 연결합니다. ITS의 장치별 이벤트가 GIC의 실제 LPI 전달 경로까지 이어지도록 계층의 상위 IRQ 자원을 준비합니다.
if (err)이번 LPI의 부모 GIC domain 할당이 실패했는지 확인합니다. 실패한 항목에 ITS 처리기를 붙이기 전에 중단합니다.
return err;부모 domain 할당 오류를 상위 IRQ 계층에 반환합니다. 남은 항목의 연결을 진행하지 않습니다.
irq_domain_set_hwirq_and_chip(domain, virq + i,Linux virq에 ITS hwirq와 irq_chip callback을 연결한다.
hwirq + i, &its_irq_chip, its_dev);현재 LPI 번호, ITS irq_chip, 해당 장치 포인터를 Linux IRQ에 묶습니다. 후속 mask·affinity·활성화 콜백이 이 장치와 이벤트를 찾게 됩니다.
irqd = irq_get_irq_data(virq + i);방금 연결한 Linux IRQ의 irq_data를 얻습니다. 이 자료에 단일 CPU 대상과 활성화 시 affinity 결정 등의 정책을 기록하여 IRQ 코어가 ITS의 제약에 맞게 동작하도록 합니다.
irqd_set_single_target(irqd);이 IRQ는 한 번에 하나의 CPU를 대상으로 한다고 IRQ 코어에 표시합니다. affinity에 여러 CPU가 허용되어 있어도 실제 LPI 전달 대상은 그중 한 CPU로 선택됩니다.
irqd_set_affinity_on_activate(irqd);실제 IRQ 활성화 시점에 affinity를 정하도록 표시합니다. 할당할 때의 임시 상태보다 IRQ를 켤 때의 CPU 상태를 기준으로 전달 대상을 고를 수 있게 합니다.
irqd_set_resend_when_in_progress(irqd);처리 중인 IRQ에도 재전송이 필요할 수 있다는 IRQ 코어 정책 비트를 설정합니다. 이 줄이 MSI를 즉시 보내는 것은 아니며, IRQ 코어의 resend 판단 방식을 지정합니다.
pr_debug("ID:%d pID:%d vID:%d\n",장치 이벤트 번호와 물리 LPI 번호, Linux IRQ 번호의 대응을 디버그 로그에 출력합니다. 서로 다른 세 번호 공간을 추적하기 위한 정보입니다.
(int)(hwirq + i - its_dev->event_map.lpi_base),LPI 번호에서 장치의 LPI 시작 번호를 빼 이벤트 ID를 구해 로그에 넣습니다. 이 드라이버에서 장치별 이벤트 인덱스를 얻는 방식입니다.
(int)(hwirq + i), virq + i);실제 LPI 번호와 그에 연결한 Linux IRQ 번호를 함께 기록합니다. 숫자가 같을 필요가 없는 두 계층의 연결을 보여줍니다.
return 0;요청한 모든 IRQ의 부모 연결과 ITS 정책 설정을 마쳤음을 0으로 반환합니다. 이벤트를 실제 CPU로 매핑하는 활성화는 별도 콜백에서 진행합니다.
static int its_irq_domain_activate(struct irq_domain *domain,할당해 둔 ITS IRQ를 실제 전달 가능한 상태로 활성화하는 함수입니다. CPU를 고르고 장치 이벤트와 LPI 사이에 MAPTI 매핑을 만듭니다.
struct irq_data *d, bool reserve)활성화 대상 irq_data와 IRQ 코어의 예약 여부 인자를 받습니다. 이 구현의 본문은 d로 장치·이벤트를 찾고 reserve 값으로 별도 분기하지 않습니다.
struct its_device *its_dev = irq_data_get_irq_chip_data(d);irq_data에 저장해 둔 ITS 장치 정보를 가져옵니다. 이 장치의 이벤트 번호와 LPI 매핑을 사용해 곧 활성화할 IRQ의 전달 경로를 구성합니다.
u32 event = its_get_event_id(d);장치에 할당된 LPI 범위에서 이번 IRQ가 차지하는 위치를 이벤트 ID로 구합니다. ITS에 보낼 MAPTI에서 DeviceID와 함께 MSI 요청을 식별할 값입니다.
int cpu;이번 LPI를 실제로 전달할 논리 CPU 번호를 저장합니다. 선택 결과가 유효해야 collection 매핑을 만들 수 있습니다.
cpu = its_select_cpu(d, cpu_online_mask);현재 온라인 CPU 중 이 IRQ를 받을 CPU를 선택합니다. 선택할 대상이 없으면 활성화가 실패하므로, 실제 매핑을 만들기 전에 유효한 전달 대상을 확보합니다.
if (cpu < 0 || cpu >= nr_cpu_ids)CPU 선택이 실패했거나 번호가 시스템의 유효 범위를 벗어났는지 확인합니다. 유효하지 않으면 배치 통계와 ITS 매핑을 변경하지 않습니다.
return -EINVAL;유효한 전달 CPU가 없으므로 활성화를 -EINVAL로 실패시킵니다.
its_inc_lpi_count(d, cpu);선택한 CPU가 맡은 LPI 수를 증가시킵니다. ITS 드라이버가 CPU별 LPI 배치를 추적하는 장부를 실제 활성화될 인터럽트에 맞추는 처리입니다.
its_dev->event_map.col_map[event] = cpu;이벤트별 collection 배치 표에 선택한 CPU를 기록합니다. MAPTI를 만들 때와 이후 비활성화·affinity 처리에서 이 이벤트의 대상을 찾는 기준입니다.
irq_data_update_effective_affinity(d, cpumask_of(cpu));선택한 CPU 하나를 이 IRQ의 실제 affinity로 기록합니다. 요청된 허용 CPU 집합과 구분하여, 현재 하드웨어가 인터럽트를 보낼 CPU를 IRQ 코어에 알립니다.
its_send_mapti(its_dev, d->hwirq, event);ITS에 MAPTI 명령을 보내 DeviceID·EventID를 이번 LPI 번호와 선택한 collection에 연결합니다. 이후 장치의 해당 MSI 쓰기가 지정 CPU의 LPI로 전달될 수 있게 됩니다.
return 0;이벤트와 LPI의 CPU 매핑 명령을 보냈으므로 활성화 성공을 반환합니다.
static void its_irq_domain_deactivate(struct irq_domain *domain,ITS IRQ의 이벤트 매핑을 제거해 전달을 중단하는 함수입니다. CPU별 LPI 수를 줄이고 DISCARD 명령을 보냅니다.
struct irq_data *d)비활성화할 IRQ 자료를 받습니다. 여기에 저장된 ITS 장치와 이벤트 번호를 사용하여 해당 이벤트만 제거합니다.
struct its_device *its_dev = irq_data_get_irq_chip_data(d);비활성화할 IRQ에서 ITS 장치 정보를 다시 얻습니다. 활성화 때 만든 이벤트 매핑과 CPU별 배치 기록을 찾아 되돌리는 데 사용합니다.
u32 event = its_get_event_id(d);비활성화할 LPI에 대응하는 장치 이벤트 번호를 구합니다. 이 번호로 col_map의 CPU 기록을 찾고 ITS의 해당 이벤트 매핑만 제거합니다.
its_dec_lpi_count(d, its_dev->event_map.col_map[event]);이 이벤트를 맡았던 CPU의 LPI 수를 감소시킵니다. 이어 하드웨어 매핑을 제거하므로, CPU별 배치 통계에서도 이 IRQ를 제외합니다.
its_send_discard(its_dev, event);ITS에 DISCARD 명령을 보내 해당 장치 이벤트의 LPI 매핑을 제거합니다. 이 이벤트를 통한 인터럽트 전달을 비활성화하는 단계이며, 장치 전체의 모든 MSI 매핑을 지우지는 않습니다.
static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,ITS domain에 할당했던 Linux IRQ 범위를 해제하는 함수입니다. 활성화 해제와 별도로 LPI 비트맵·domain 항목·부모 자원을 반환합니다.
unsigned int nr_irqs)해제할 연속 IRQ의 개수를 받습니다. 시작 번호 virq부터 이 범위에 대응하는 장치 LPI 자원들을 정리합니다.
struct irq_data *d = irq_domain_get_irq_data(domain, virq);해제할 Linux IRQ 번호를 이 ITS domain의 irq_data로 찾습니다. 그 자료에서 장치와 LPI 할당 정보를 얻어 비트맵 및 부모 domain 자원을 반환할 준비를 합니다.
x86-64 · Linux 6.18.37
MSI address/data의 APIC vector 또는 interrupt-remapping IRTE
remapping이 없으면 message에 destination APIC ID와 vector를 직접 encode한다. VT-d/AMD remapping이 켜지면 message는 IRTE index를 가리키고 IOMMU가 requester 검증, destination과 vector를 선택한다.
원본 코드: arch/x86/kernel/apic/msi.c:258-304
258
259static const struct msi_parent_ops x86_vector_msi_parent_ops = {
260 .supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED,
261 .init_dev_msi_info = x86_init_dev_msi_info,
262};
263
264struct irq_domain * __init native_create_pci_msi_domain(void)
265{
266 if (apic_is_disabled)
267 return NULL;
268
269 x86_vector_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
270 x86_vector_domain->msi_parent_ops = &x86_vector_msi_parent_ops;
271 return x86_vector_domain;
272}
273
274void __init x86_create_pci_msi_domain(void)
275{
276 x86_pci_msi_default_domain = x86_init.irqs.create_pci_msi_domain();
277}
278
279/* Keep around for hyperV */
280int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
281 msi_alloc_info_t *arg)
282{
283 init_irq_alloc_info(arg, NULL);
284
285 if (to_pci_dev(dev)->msix_enabled)
286 arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
287 else
288 arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
289 return 0;
290}
291EXPORT_SYMBOL_GPL(pci_msi_prepare);
292
293#ifdef CONFIG_DMAR_TABLE
294/*
295 * The Intel IOMMU (ab)uses the high bits of the MSI address to contain the
296 * high bits of the destination APIC ID. This can't be done in the general
297 * case for MSIs as it would be targeting real memory above 4GiB not the
298 * APIC.
299 */
300static void dmar_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
301{
302 __irq_msi_compose_msg(irqd_cfg(data), msg, true);
303}
304 주요 구문 해설
원본 47줄은 그대로 표시하며, 검토한 23개 구문에 설명을 붙였습니다.
static const struct msi_parent_ops x86_vector_msi_parent_ops = {x86 vector domain을 MSI의 부모로 사용할 때 제공할 기능과 초기화 동작을 정의합니다. 장치별 MSI domain이 이 표의 지원 범위를 참고합니다.
.supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED,vector 부모가 허용하는 MSI 기능 비트 집합을 등록합니다. 하위 장치 domain의 요청 중 부모가 처리할 수 없는 기능은 초기화 과정에서 제외됩니다.
.init_dev_msi_info = x86_init_dev_msi_info,장치별 MSI 정보의 x86 초기화 함수 주소를 등록합니다. 실제 장치 domain을 만들 때 IRQ 처리기·affinity·필수 플래그를 x86 방식에 맞춥니다.
struct irq_domain * __init native_create_pci_msi_domain(void)부팅 중 native PCI MSI 부모 domain을 준비하는 함수입니다. APIC를 사용할 수 있으면 기존 vector domain에 MSI 부모 역할을 부여하고 그 포인터를 반환합니다.
if (apic_is_disabled)APIC가 비활성화됐는지 확인합니다. CPU의 APIC 벡터로 MSI를 받을 기반이 없으면 native MSI domain을 제공하지 않습니다.
return NULL;APIC를 사용할 수 없어 기본 PCI MSI domain을 만들지 못했음을 NULL로 반환합니다.
x86_vector_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;최종 local APIC vector와 target CPU를 관리하는 parent IRQ domain이다.
x86_vector_domain->msi_parent_ops = &x86_vector_msi_parent_ops;최종 local APIC vector와 target CPU를 관리하는 parent IRQ domain이다.
return x86_vector_domain;최종 local APIC vector와 target CPU를 관리하는 parent IRQ domain이다.
void __init x86_create_pci_msi_domain(void)플랫폼이 제공하는 PCI MSI domain 생성 함수를 호출하고 결과를 기본 domain으로 저장하는 부팅 함수입니다. native나 가상화 플랫폼의 선택을 공통 초기화와 분리합니다.
x86_pci_msi_default_domain = x86_init.irqs.create_pci_msi_domain();플랫폼이 지정한 생성 함수로 기본 PCI MSI domain을 만들고 결과를 보관합니다. 이후 PCI MSI 할당이 이 domain을 통해 CPU 벡터와 연결되며, 기본 native 구현은 APIC 사용 여부를 확인하고 vector domain을 MSI 부모로 준비합니다.
int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,Hyper-V에서도 사용하는 PCI MSI 할당 준비 함수입니다. 대상 장치와 요청 정보를 받아 x86의 MSI 또는 MSI-X 종류를 정합니다.
msi_alloc_info_t *arg)준비 결과를 써 넣을 x86 MSI 할당 자료를 받습니다. 아래에서 초기화한 뒤 장치의 MSI-X 활성 여부에 맞는 type을 저장합니다.
init_irq_alloc_info(arg, NULL);MSI 할당 요청 자료를 기본값으로 초기화합니다. 뒤에서 MSI인지 MSI-X인지와 장치 식별 정보를 채우기 전에 이전 값이 섞이지 않도록 하는 공통 준비입니다.
if (to_pci_dev(dev)->msix_enabled)이 PCI 장치가 MSI-X 방식으로 설정되어 있는지 확인합니다. MSI-X와 일반 MSI는 하위 벡터 할당 제약이 달라 서로 다른 요청 종류로 표시합니다.
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;MSI-X 장치임을 할당 요청에 기록합니다. 뒤의 x86 IRQ 할당 계층이 그 방식에 맞춰 벡터를 준비하도록 합니다.
else장치가 MSI-X로 설정되지 않았으면 일반 PCI MSI 요청으로 분류합니다.
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;할당 요청의 종류를 PCI MSI로 기록합니다. MSI-X와 구분된 벡터 할당 규칙을 적용할 기준입니다.
return 0;할당 인자 초기화와 MSI/MSI-X 분류를 성공적으로 마쳤음을 반환합니다. 실제 벡터 할당이나 장치 MSI 레지스터 쓰기는 이후 단계입니다.
EXPORT_SYMBOL_GPL(pci_msi_prepare);pci_msi_prepare를 GPL 호환 모듈이 참조할 수 있는 공개 심벌로 등록합니다. 모듈 로더가 함수 주소를 찾도록 만드는 빌드 정보이며, 이 줄에서 MSI를 준비하거나 함수를 실행하지 않습니다.
#ifdef CONFIG_DMAR_TABLEIntel DMAR/IOMMU를 위한 MSI 메시지 구성 지원입니다. 이 구성은 DMAR 장치의 메시지 주소 상위 부분으로 목적지 APIC ID의 상위 비트도 전달하므로, 일반 PCI MSI와 별도의 메시지 생성 함수를 둡니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
static void dmar_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)DMAR, 즉 Intel IOMMU용 인터럽트의 MSI 메시지를 만드는 함수입니다. IRQ의 APIC 대상·벡터를 받아 일반 PCI MSI와 구분되는 확장 대상 주소 형식으로 출력합니다.
__irq_msi_compose_msg(irqd_cfg(data), msg, true);이 IRQ에 배정된 x86 벡터와 APIC 대상 정보로 DMAR용 MSI 메시지의 주소·데이터를 구성합니다. 마지막 true는 DMAR 메시지 형식을 선택하여 확장 APIC 대상 정보를 담을 수 있게 합니다.
RISC-V · Linux 6.18.37
IMSIC per-hart interrupt file과 AIA MSI domain
MSI write가 target hart/guest interrupt-file address와 EIID data를 사용한다. IMSIC domain이 EIID를 Linux IRQ에 연결하고 APLIC가 wired source를 MSI로 변환해 같은 interrupt file에 전달할 수도 있다.
원본 코드: drivers/irqchip/irq-riscv-imsic-platform.c:217-283
217 .irq_force_complete_move = imsic_irq_force_complete_move,
218#endif
219 .irq_retrigger = imsic_irq_retrigger,
220 .irq_ack = imsic_irq_ack,
221 .irq_compose_msi_msg = imsic_irq_compose_msg,
222 .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
223};
224
225static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
226 unsigned int nr_irqs, void *args)
227{
228 struct imsic_vector *vec;
229
230 /* Multi-MSI is not supported yet. */
231 if (nr_irqs > 1)
232 return -EOPNOTSUPP;
233
234 vec = imsic_vector_alloc(virq, cpu_online_mask);
235 if (!vec)
236 return -ENOSPC;
237
238 irq_domain_set_info(domain, virq, virq, &imsic_irq_base_chip, vec,
239 handle_edge_irq, NULL, NULL);
240 irq_set_noprobe(virq);
241 irq_set_affinity(virq, cpu_online_mask);
242 irq_data_update_effective_affinity(irq_get_irq_data(virq), cpumask_of(vec->cpu));
243
244 return 0;
245}
246
247static void imsic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
248 unsigned int nr_irqs)
249{
250 struct irq_data *d = irq_domain_get_irq_data(domain, virq);
251
252 imsic_vector_free(irq_data_get_irq_chip_data(d));
253 irq_domain_free_irqs_parent(domain, virq, nr_irqs);
254}
255
256#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
257static void imsic_irq_debug_show(struct seq_file *m, struct irq_domain *d,
258 struct irq_data *irqd, int ind)
259{
260 if (!irqd) {
261 imsic_vector_debug_show_summary(m, ind);
262 return;
263 }
264
265 imsic_vector_debug_show(m, irq_data_get_irq_chip_data(irqd), ind);
266}
267#endif
268
269static const struct irq_domain_ops imsic_base_domain_ops = {
270 .alloc = imsic_irq_domain_alloc,
271 .free = imsic_irq_domain_free,
272 .select = msi_lib_irq_domain_select,
273#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
274 .debug_show = imsic_irq_debug_show,
275#endif
276};
277
278static bool imsic_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
279 struct irq_domain *real_parent, struct msi_domain_info *info)
280{
281 if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
282 return false;
283 주요 구문 해설
원본 67줄은 그대로 표시하며, 검토한 44개 구문에 설명을 붙였습니다.
.irq_force_complete_move = imsic_irq_force_complete_move,진행 중인 IRQ의 CPU 이동을 강제로 마무리하는 콜백을 등록합니다. 이전 CPU에 남은 IMSIC vector를 정리해야 할 때 사용합니다.
#endif215행에서 시작한 선택 구간을 마칩니다. 여러 hart가 있는 SMP 빌드에서는 IRQ의 대상 hart를 바꾸는 affinity와 이동 완료 동작을 irq_chip에 등록합니다. 단일 CPU 구성에는 CPU 사이의 인터럽트 이동이 필요 없습니다.
.irq_retrigger = imsic_irq_retrigger,IRQ를 다시 발생시키는 콜백으로 imsic_irq_retrigger를 등록합니다. 이 함수는 대상 CPU의 MSI 페이지에 로컬 인터럽트 ID를 써 재전송합니다.
.irq_ack = imsic_irq_ack,IRQ 승인 시 affinity 이동의 후속 처리를 진행하는 imsic_irq_ack를 등록합니다. 이 구현은 irq_move_irq를 호출하여 지연된 이동을 처리합니다.
.irq_compose_msi_msg = imsic_irq_compose_msg,장치에 설정할 MSI 주소와 데이터를 조립하는 콜백을 등록합니다. 주소는 대상 CPU의 IMSIC 페이지, 데이터는 그 CPU의 로컬 ID로 만들어집니다.
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,별도 wake 설정 콜백을 요구하지 않고 suspend 중에는 이 IRQ를 마스크하도록 IRQ 코어에 지정합니다. 칩의 전원 관리 처리 방식을 나타내는 플래그입니다.
static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,IMSIC 기본 domain에서 Linux IRQ에 CPU별 인터럽트 vector를 배정하는 함수입니다. virq는 상위 계층에서 정한 Linux IRQ 번호입니다.
unsigned int nr_irqs, void *args)한 번에 요청할 IRQ 수와 상위 계층 인자를 받습니다. 이 버전은 다중 MSI 요청을 지원하지 않아 수가 1을 넘으면 거부합니다.
struct imsic_vector *vec;할당한 IMSIC vector의 포인터입니다. 선택한 CPU와 그 CPU 안의 로컬 인터럽트 ID, 이동 관리 정보 등을 연결합니다.
if (nr_irqs > 1)한 요청이 둘 이상의 IRQ를 요구하는지 확인합니다. 이 버전에서 지원하지 않는 multi-MSI를 단일 IRQ처럼 잘못 할당하지 않도록 합니다.
return -EOPNOTSUPP;한 번에 여러 MSI를 할당하는 동작은 지원하지 않으므로 -EOPNOTSUPP를 반환합니다.
vec = imsic_vector_alloc(virq, cpu_online_mask);온라인 CPU들 가운데 사용할 수 있는 IMSIC 인터럽트 파일과 로컬 ID를 골라 이 Linux IRQ에 할당합니다. 반환된 vector 자료에는 실제 대상 CPU와 ID가 담기며, 자리가 없으면 할당을 실패시킵니다.
if (!vec)온라인 CPU에서 사용할 IMSIC vector를 확보하지 못했는지 확인합니다. 실패한 포인터를 IRQ의 chip_data로 등록하지 않습니다.
return -ENOSPC;CPU별 로컬 인터럽트 ID 자리가 부족하므로 -ENOSPC를 반환합니다.
irq_domain_set_info(domain, virq, virq, &imsic_irq_base_chip, vec,Linux IRQ에 IMSIC 기본 chip과 할당한 vector를 연결합니다. 이 domain의 hwirq에도 virq를 사용하지만 실제 하드웨어의 CPU·로컬 ID는 vector에 별도로 저장합니다.
handle_edge_irq, NULL, NULL);IRQ의 공통 흐름 처리기로 에지 IRQ 처리기를 지정합니다. MSI 쓰기로 생기는 요청을 에지 이벤트로 처리하고 추가 handler 자료는 두지 않습니다.
irq_set_noprobe(virq);이 IRQ를 레거시 IRQ 자동 탐색 대상에서 제외합니다. MSI처럼 명시적으로 할당하고 전달 경로를 구성한 IRQ에 임의의 probe 동작이 적용되지 않도록 하는 설정입니다.
irq_set_affinity(virq, cpu_online_mask);새 MSI IRQ의 허용 affinity를 온라인 CPU 집합으로 설정합니다. 아래에서는 그중 IMSIC vector를 실제로 배정받은 CPU를 effective affinity로 별도 기록합니다.
irq_data_update_effective_affinity(irq_get_irq_data(virq), cpumask_of(vec->cpu));vector가 실제로 속한 CPU 하나를 IRQ의 effective affinity로 기록합니다. 여러 CPU가 허용되어 있더라도 현재 MSI가 도착하는 인터럽트 파일의 CPU는 이 값으로 분명히 나타납니다.
return 0;vector 확보와 IRQ chip·처리기·affinity 설정을 마쳤음을 반환합니다.
static void imsic_irq_domain_free(struct irq_domain *domain, unsigned int virq,IMSIC 기본 domain의 IRQ 자원을 해제하는 함수입니다. 시작 Linux IRQ로 저장된 vector를 찾아 돌려주고 부모 domain도 정리합니다.
unsigned int nr_irqs)부모 domain에서 함께 해제할 IRQ 범위의 개수를 받습니다. 실제 vector 해제는 이 구현의 단일 IRQ 할당 정책에 맞춰 수행합니다.
struct irq_data *d = irq_domain_get_irq_data(domain, virq);해제 요청의 Linux IRQ를 이 IMSIC domain의 irq_data로 찾습니다. 저장된 chip_data에서 앞서 할당한 IMSIC vector를 꺼내기 위한 조회입니다.
imsic_vector_free(irq_data_get_irq_chip_data(d));irq_data에 연결된 IMSIC vector를 반환합니다. 해당 CPU의 로컬 인터럽트 ID를 더 이상 이 IRQ가 차지하지 않게 하여 이후 다른 MSI 할당에서 재사용할 수 있도록 합니다.
irq_domain_free_irqs_parent(domain, virq, nr_irqs);이 IRQ 범위가 부모 domain에서 확보했던 자원을 함께 해제합니다. IMSIC vector뿐 아니라 계층형 IRQ 할당의 상위 연결도 남기지 않게 하는 정리입니다.
#ifdef CONFIG_GENERIC_IRQ_DEBUGFSIRQ debugfs 지원은 인터럽트 도메인과 벡터 배치를 진단 파일로 보여 주는 기능입니다. 켜지면 IMSIC 전체 또는 개별 벡터 정보를 출력할 함수를 포함하며, 꺼지면 출력용 구현을 생략합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
static void imsic_irq_debug_show(struct seq_file *m, struct irq_domain *d,IRQ domain의 디버그 정보를 seq_file 출력에 기록하는 콜백입니다. 특정 IRQ가 지정됐는지에 따라 전체 vector 요약과 개별 정보를 나눕니다.
struct irq_data *irqd, int ind)특정 IRQ 자료와 출력 들여쓰기 깊이를 받습니다. irqd가 NULL이면 domain 전체 요약, 있으면 그 IRQ의 CPU·vector 정보를 보여줍니다.
if (!irqd) {특정 IRQ 자료가 없는 domain 전체 조회인지 확인합니다. 개별 chip_data를 읽지 않고 전체 배치 요약을 출력하는 경로입니다.
imsic_vector_debug_show_summary(m, ind);특정 IRQ 자료가 없으면 IMSIC vector의 전체 배치 요약을 디버그 출력에 기록합니다. m은 출력 대상이고 ind는 들여쓰기 깊이이며, 인터럽트의 실제 전달 상태를 변경하지 않습니다.
return;전체 요약 출력이 끝났으므로 반환합니다. NULL인 irqd를 아래의 개별 vector 출력에 넘기지 않습니다.
imsic_vector_debug_show(m, irq_data_get_irq_chip_data(irqd), ind);특정 IRQ의 chip_data에 담긴 IMSIC vector 정보를 디버그 출력에 표시합니다. 그 IRQ가 어떤 CPU와 로컬 ID에 배치되었는지 살펴보기 위한 출력이며, vector를 새로 할당하는 동작은 아닙니다.
#endif256행에서 시작한 선택 구간을 마칩니다. IRQ debugfs 지원은 인터럽트 도메인과 벡터 배치를 진단 파일로 보여 주는 기능입니다. 켜지면 IMSIC 전체 또는 개별 벡터 정보를 출력할 함수를 포함하며, 꺼지면 출력용 구현을 생략합니다.
static const struct irq_domain_ops imsic_base_domain_ops = {IMSIC 기본 IRQ domain이 제공할 작업 표를 정의합니다. 자원 할당·해제·domain 선택·디버그 출력을 공통 IRQ 계층에 연결합니다.
.alloc = imsic_irq_domain_alloc,domain의 IRQ 할당 요청을 IMSIC vector 배정 함수에 연결합니다. 함수 주소 등록이며 여기서 vector를 즉시 할당하지 않습니다.
.free = imsic_irq_domain_free,domain의 IRQ 해제 요청에 vector와 부모 자원을 돌려주는 함수를 연결합니다.
.select = msi_lib_irq_domain_select,MSI 요청에 맞는 domain인지 판단하는 작업을 공통 MSI 선택 helper에 맡깁니다. firmware 명세와 bus 종류를 적절한 계층에 연결할 때 사용합니다.
#ifdef CONFIG_GENERIC_IRQ_DEBUGFSIRQ debugfs가 켜진 경우 IMSIC 벡터 정보를 보여 줄 함수를 도메인의 debug_show 항목에 연결합니다. 디버깅 출력을 추가하는 설정이며 실제 IRQ 할당·해제 기능을 켜는 조건은 아닙니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
.debug_show = imsic_irq_debug_show,IRQ 디버그 조회를 IMSIC vector 정보 출력 함수에 연결합니다. GENERIC_IRQ_DEBUGFS가 있는 빌드에서 제공되는 진단 동작입니다.
#endif273행에서 시작한 선택 구간을 마칩니다. IRQ debugfs가 켜진 경우 IMSIC 벡터 정보를 보여 줄 함수를 도메인의 debug_show 항목에 연결합니다. 디버깅 출력을 추가하는 설정이며 실제 IRQ 할당·해제 기능을 켜는 조건은 아닙니다.
static bool imsic_init_dev_msi_info(struct device *dev, struct irq_domain *domain,장치별 MSI domain을 만들 때 공통 설정과 IMSIC 제약을 적용하는 함수입니다. 장치와 그 domain을 받아 PCI MSI 계열의 이동 정책 등을 준비합니다.
struct irq_domain *real_parent, struct msi_domain_info *info)실제 부모 domain과 채워 넣을 MSI 정보 구조체를 받습니다. 부모의 지원 기능을 반영하고 장치별 chip 동작을 구성할 대상입니다.
if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))공통 MSI 라이브러리의 장치 정보 초기화가 성공했는지 확인합니다. 실패하면 IMSIC의 추가 affinity 정책을 덧붙이지 않습니다.
return false;부모·장치 MSI 정보 초기화 실패를 거짓으로 돌려줍니다. 불완전한 정보로 domain 생성을 계속하지 않게 합니다.
05 · WORKED EXAMPLE
숫자로 검산하기
MSI-X vector를 CPU2에서 CPU7로 이동
device MSI-X table entry 하나가 RX queue 3에 연결되어 있고 interrupt remapping이 활성화됐다고 가정한다.
- GIC ITSITS가 CPU7 collection을 선택하면 MOVI로 이벤트를 옮기고 effective affinity를 갱신한다. 매 이동에 device MSI를 다시 쓰거나 mask하지 않는다.
- x86-64CPU7 vector를 할당하고 상위 MSI domain에서 message 또는 IRTE를 갱신한다. CPU2의 이전 vector 정리는 새 interrupt 도착과 pending 확인을 따라간다.
- RISC-V IMSICCPU7의 새 vector를 할당한다.
!irq_can_move_in_process_context(d)이고 새·옛 local ID가 다를 때만 이전 address와 새 data의 임시 message를 쓴 뒤 최종 message를 기록한다. 그 밖에는 최종 message만 기록한다.
결론affinity 변경은 irqchip·MSI domain의 조건부 경로다. 장치 message, remapper, controller가 항상 동시에 바뀌는 것은 아니다.
06 · DEEP DIVE
주요 동작 상세 분석
공통 커널 코드와 아키텍처별 함수의 역할 구분
PCI MSI core가 vector를 할당하면 hierarchical irqdomain이 device ID와 event/vector를 parent controller hwirq로 변환하고 compose_msi_msg가 device table에 쓸 address/data를 만든다. affinity 변경은 message 또는 remapping table을 갱신한다.
device MSI-X table programming, interrupt-remapper/ITS/IMSIC table update, CPU vector allocation과 irq_desc activation을 구분한다. affinity 변경 절차는 irqchip마다 다르다. ITS의 MOVI, x86 vector 이동, IMSIC의 MSI message 갱신과 이전 vector 정리를 구분한다.
arm64: GICv3 ITS device/event translation과 LPI
PCI requester ID를 DeviceID로, MSI data의 event를 EventID로 사용해 ITS가 collection/target redistributor의 LPI로 변환한다. command queue의 MAPD, MAPTI, MOVI와 SYNC가 device/collection table 상태를 관리한다.
ITS affinity 변경은 새 collection에 MOVI를 보내고 effective affinity를 갱신한다. 기존 MSI를 매번 mask·unmask하는 절차는 아니다. 디버깅할 때는 BDF/DeviceID, EventID, LPI INTID, collection target, ITS command queue read/write pointer와 pending table을 본다.
x86-64: MSI address/data의 APIC vector 또는 interrupt-remapping IRTE
remapping이 없으면 message에 destination APIC ID와 vector를 직접 encode한다. VT-d/AMD remapping이 켜지면 message는 IRTE index를 가리키고 IOMMU가 requester 검증, destination과 vector를 선택한다.
새 APIC vector를 할당하고 MSI message 또는 IRTE를 갱신한다. 이전 vector는 pending interrupt를 고려해 나중에 정리한다. 디버깅할 때는 BDF, MSI address/data, vector, destination APIC ID, IRTE index/source-id, remapping fault와 vector cleanup state를 확인한다.
RISC-V: IMSIC per-hart interrupt file과 AIA MSI domain
MSI write가 target hart/guest interrupt-file address와 EIID data를 사용한다. IMSIC domain이 EIID를 Linux IRQ에 연결하고 APLIC가 wired source를 MSI로 변환해 같은 interrupt file에 전달할 수도 있다.
IMSIC는 새 vector를 먼저 할당하고 최종 MSI message를 갱신한다. process-context 이동이 불가하고 새·옛 local ID가 다를 때만 이전 address·새 data의 임시 message를 먼저 쓴다. 이후 이전 vector의 pending 상태를 이동 절차에서 정리한다. 디버깅할 때는 hart index, guest index, MSI page PA, EIID, EIP/EIE, parent APLIC source와 irqdomain mapping을 기록한다.
객체를 사용하고 해제하는 주체와 시점을 확인한다
MSI descriptor, vector와 remapping entry는 device가 bus mastering과 MSI를 중지하고 synchronize_irq가 끝날 때까지 유지된다. vector 조기 재사용은 다른 device handler를 실행시킨다.
주소나 레지스터 값만 확인하면 갱신되지 않은 상태를 놓칠 수 있다. 상태를 만드는 코드, 다른 코드에 전달하는 시점, 사용하는 코드, 해제하는 시점을 함께 기록한다.
최대 지연 시간은 명령 하나의 실행 시간만으로 결정되지 않는다
MSI write의 PCIe fabric 지연, translation table cache miss, target interrupt controller delivery, CPU irq-off와 handler 시간이 합쳐진다. affinity가 NUMA/cluster 경계를 넘으면 data와 IRQ locality가 달라진다.
평균값뿐 아니라 인터럽트를 비활성화한 시간, 다른 CPU의 응답 시간, 펌웨어 호출 시간, 재시도 횟수를 따로 확인해야 최대 지연의 원인을 찾을 수 있다.
07 · FAILURE
실패를 어떤 증거로 나눌 것인가
| 분류 | 관찰되는 결과 | 첫 확인값 |
|---|---|---|
| arm64 | 잘못된 DeviceID alias, collection affinity 또는 table cache invalidation 누락은 MSI가 엉뚱한 CPU로 가거나 사라지는 원인이 된다. | BDF/DeviceID, EventID, LPI INTID, collection target, ITS command queue read/write pointer와 pending table을 본다. |
| x86-64 | xAPIC destination width 한계, stale IRTE, source validation 실패 또는 vector migration race가 DMA-remapping fault와 lost interrupt를 만든다. | BDF, MSI address/data, vector, destination APIC ID, IRTE index/source-id, remapping fault와 vector cleanup state를 확인한다. |
| RISC-V | hart/guest index 계산, MSI page address 또는 EIID width가 틀리면 다른 interrupt file이 pending되거나 write가 fault난다. | hart index, guest index, MSI page PA, EIID, EIP/EIE, parent APLIC source와 irqdomain mapping을 기록한다. |
08 · LAB
재현과 계측 절차
- MSI-X table과 kernel irqdomain mapping을 동시에 dump해 BDF에서 CPU vector까지 역추적한다.
- IRQ affinity를 부하 중 이동시키며 lost/duplicate sequence number와 route-update latency를 측정한다.
- 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
- 소스만 읽고 끝내지 않고 최종
vmlinux의objdump -dr,readelf -SW결과로 선택된 alternative와 section 배치를 확인한다.
09 · REFERENCES
원문 위치
- arm64drivers/irqchip/irq-gic-v3-its.c:3677-3757
- x86-64arch/x86/kernel/apic/msi.c:258-304
- RISC-Vdrivers/irqchip/irq-riscv-imsic-platform.c:217-283
Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.