Linux 内核 GICv3 ITS 翻译表实战指南:把海量 PCIe MSI 中断精准投到目标 CPU 📅 发布时间:2026/8/29 22:13:39 👁 浏览次数: Linux 内核 GICv3 ITS 翻译表实战指南把海量 PCIe MSI 中断精准投到目标 CPU【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux在 Linux 内核中GICv3 的ITSInterrupt Translation Service中断翻译服务负责把海量 PCIe 设备发来的 MSI 消息翻译成 CPU 能处理的 LPI 中断——如果你是在 ARM 服务器或嵌入式平台上和中断号不够用网卡中断全压死一个核较真的工程师这块内容值得你花二十分钟搞懂。一个网卡把 CPU0 打到怀疑人生的现场想象这样一个深夜一台 ARM64 服务器跑了 16 条队列的 PCIe 网卡cat /proc/softirqs发现 CPU0 的 NET_RX 计数一路狂飙其余核心看着它表演。你试着用echo改写/proc/irq/nr/smp_affinity_list把负载匀开结果越查越不对劲老式线中断SPI的号码池只有 INTID 32~1019 这不到一千个位置PCIe 设备一多MSI 根本排不上号只能退化成几个设备共享一条线中断一触发就得挨个轮询排查线中断的路由由 Distributor 统管改绑定就像改布线又笨重又不直观。问题出在哪消息型中断MSI的地址 → 中断这一步硬件上根本没有对应的翻译器。ARM 的解法是在 GICv3 里加一个专职翻译官ITS。一句话点破ITS 就是让 PCIe 设备往固定地址写一笔 MSI 消息硬件自动查表换成 LPILocality-specific Peripheral Interrupt再投到你指定 CPU 的中断路由器。读完你能带走的东西看穿 ITS 三张翻译表Device Table / ITT / Collection各自的职责以及Devid EventID → INTID这一步到底在哪发生搞懂内核里 ITS 的初始化三步走以及命令队列中 MAPD/MAPC/MAPTI/MOV/SYNC 各是什么角色掌握设备树里 ITS 节点的正确写法以及一个ITS 在设备树里配了却完全不生效的经典坑学会在开机日志里验证翻译链路并在运行时调整 LPI 亲和性。翻译的核心三张表和一条命令队列先建立直觉PCIe 设备发 MSI 时其实是在做一件事——往一个 64 位地址写一小段数据。ITS 的巧妙在于这个消息地址本身就是翻译的钥匙。低地址位被拆成 DeviceID哪个设备和 EventID设备里的哪个事件源高地址位则指向某个 ITS 实例的基址。拿到 {Devid, EventID} 之后硬件做两次查表Device Table用 DeviceID 索引找到该设备自己的 ITT 基址ITTInterrupt Translation Table用 EventID 索引查出真正的 LPI INTID再往下Redistributor 侧的Collection决定这个 LPI 最终投给哪个 CPU——而 Collection 是可以运行时改的。 整个消息 → 中断 → 目标核的链路如下注意表格是内核提前建好的数据通路上不碰软件那这些表是谁写的不是直接写内存而是走 ITS 的命令队列GITS_CWRITER指向的一段 4K~64K 缓冲区。内核把命令块塞进队列硬件按序消费MAPD在 Device Table 里绑定一个 DeviceIDMAPC把某个 Collection 绑到具体 CPU 集合MAPTI建立 EventID → LPI INTID 的映射MOV改 LPI 的目标 Collection也就是改亲和性SYNC广播 DVM 消息让所有ITS 实例失效缓存确保前面的改表动作全局可见。对应源码里的命令构造函数就是 drivers/irqchip/irq-gic-v3-its.c 里的its_send_mapd()、its_send_mapc()、its_send_mapti()、its_send_movi()、its_build_sync_cmd()这一族队列满了its_queue_full()会阻塞等待完成——这就是为什么改表是命令而不是写内存。内核里的 ITS 初始化三步走第一步先全局复位再逐个探测入口是its_of_probe()它先用of_find_matching_node()把设备树里所有匹配的 ITS 节点全部复位its_reset_one()——因为多个 ITS 可能共享内存任何一个复位失败就直接放弃宁可启动失败也不带病工作。之后才逐个调用its_probe_one()做真正的初始化。所以开机时你在 dmesg 里能看到的ITS0x...: Using ITS number 0这类日志就是这一步打出来的也是排查内核到底认没认出我的 ITS的第一现场。第二步分配命令队列与翻译表拨动使能开关its_probe_one()里发生的事按顺序是its_alloc_pages_node()按 NUMA 节点分配命令队列页写入GITS_CBASER带 RaWaWb、InnerShareable、VALID 标志回读校验共享性——如果硬件只支持 Non-shareable就退化成每次写命令前手动刷缓存日志里会出现ITS: using cache flushing for cmd queue写GITS_CTLR_ENABLEITS 正式开门营业。Redistributor 那一侧则是在每个 CPU 上由its_cpu_init_lpis()写GICR_PROPBASER把 LPI 属性表挂上去。每个 Redistributor 管着 8192 个 LPI 槽位INTID 从 8192 起这就是中断号几乎用不完的来源。第三步注册 MSI 域把设备驱动藏到幕后最后its_init_domain()注册 MSI 域操作表是its_msi_domain_ops。从此设备驱动完全不用知道 ITS 的存在pci_alloc_irq_vectors()要几条 MSI 向量内核就按MAPD → MAPC → MAPTI → MOV → SYNC的顺序把命令发一遍翻译项就建好了。表结构与命令顺序的全貌如下动手实操从设备树到亲和性调优ITS 节点的正确写法含经典坑its: msi-controller3f100000 { compatible arm,gic-v3-its; msi-controller; #msi-cells 1; reg 0x3f100000 0x10000; }; pcie... { msi-parent its; };msi-parent让 PCIe 根复合体的 MSI 地址直接指向 ITS 基址而msi-controller这个布尔属性是一道闸门——its_of_probe()遍历时发现它缺失会打印no msi-controller property, ITS ignored然后直接跳过。 也就是说节点写全了、reg 也没错唯独少这一行ITS 就静静地躺在那永远不被使用而且不报任何错误码只能靠日志发现。开机验证翻译链路# 确认 ITS 被识别、命令队列是否走了 cache-flush 降级模式 dmesg | grep -iE ITS|LPIs|cache flushing # 看 LPI 号段8192 及以上当前的分布与目标 CPU cat /proc/interrupts | awk NR1 || $10 8192看到Using ITS number N说明探测成功看到CPUx: LPIs not supported则是 Redistributor 硬件不支持 LPI属于硬件/SoC 层面的问题软件无法绕过。运行时把 LPI 匀到指定核心# 先看某个 LPI比如 1024现在被谁处理 cat /proc/irq/1024/smp_affinity_list # 改成由 CPU 4 和 CPU 5 处理 echo 4-5 /proc/irq/1024/smp_affinity_list这一句背后内核走的是its_set_affinity()→ 发一条MOV命令把该 LPI 的目标 Collection 换掉之后新来的中断就投往新核不需要重启驱动。排坑清单与调优建议设备树漏了msi-controller属性最隐蔽的坑ITS 被静默忽略只留一行 warn 日志。写 DTS 时把它和#msi-cells一起检查。LPI 槽位是全系统共享的每个 Redistributor 只有 8192 个 LPI设备多、每设备多向量时可能耗尽分配失败会落在 dmesg 里规划中断资源时先算总账。命令队列降级为 cache-flush 模式ITS: using cache flushing for cmd queue意味着建表路径变慢多设备同时枚举时明显一般不影响运行时性能但在设备热插拔密集的场景要留意。亲和性别设太早网卡多队列驱动通常在队列使能时才绑定中断抢在驱动初始化前手改 affinity可能被驱动自己的策略覆盖最好在驱动完全 up 之后再调。多 ITS 实例注意 NUMAACPI 系统下 SRAT 表会把 ITS 关联到节点日志SRAT: PXM %d - ITS %d命令队列和翻译表都优先分配在本地节点跨 NUMA 查表会拖慢建表。虚拟化别漏了 GICv4如果要在 VM 里直接转发 LPI 到 vCPUdoorbell 机制看 drivers/irqchip/irq-gic-v4.cITS 驱动里its_vlpi_*一族函数就是它的底座。源码入口与延伸阅读ITS 主驱动本文主角命令编码、探测、MSI 域都在这里drivers/irqchip/irq-gic-v3-its.cGICv3 核心Distributor/Redistributor、LPI 使能drivers/irqchip/irq-gic-v3.cMSI 域桥接把 ITS 暴露成标准 MSI 父域drivers/irqchip/irq-gic-its-msi-parent.cGICv4 doorbell / VLPIdrivers/irqchip/irq-gic-v4.c设备树绑定文档reg 布局、属性要求Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml想继续深挖的话推荐从its_probe_one()读起一路跟到its_send_single_command()你会发现整个翻译世界其实就是往队列里塞 64 字节的哲学——硬件把脏活全干了。你在自己的板子上遇到过 ITS 不生效、LPI 分配失败之类的幺蛾子吗踩过的坑欢迎在留言区贴出来尤其是那些只有 dmesg 里一行日志提示的整理出来对后来人会非常有帮助。觉得这份指南有用的话也顺手收藏一下方便下次配设备树时翻出来对照。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考