GICv3 ITS 翻译表实战:搞懂 MSI 中断路由的 5 个关键点
GICv3 ITS 翻译表实战:搞懂 MSI 中断路由的 5 个关键点
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
某台 ARM 服务器的 16 队列 NVMe,softirq 却全压在 CPU0 上,其余核心几乎闲着。排查中断风暴时,这类现场很常见。问题多半不在盘,而在于 MSI 中断路由没按预期工作:GICv3 下 PCIe 设备发出的不是传统 SPI,而是 LPI(Locality-specific Peripheral Interrupt),必须先经 ITS 翻译表把消息翻成一个具体的 LPI 编号才能投递。看懂这张表,你就看懂了中断为什么"找不上门"。
30 秒速通:GICv3 各部件分工 ⚙️
| 部件 | 一句话职责 |
|---|---|
| GICD(Distributor) | 总前台:管 SPI/PPI 的优先级与全局路由 |
| GICR(Redistributor) | 每核一个的收发口,LPI 最终都从这里进 CPU |
| ITS(Translation Service) | 翻译窗口:把 MSI 消息里的 (DeviceID, EventID) 翻成 LPI 编号 |
| GICV/GICH | 虚拟化代理,GICv4 在此之上增强中断注入 |
一句话记住分工:SPI 是外设"拉线"拉出来的,LPI 是设备往 PCIe 总线"发包"发出来的;包要能送到,就得先查地址簿——这本地址簿就是 ITS 翻译表。
类比:设备 ID + 事件 ID 如何送到目标 CPU
把 ITS 想成一个国际包裹转运站。设备发一条 MSI,等于往转运站扔进一个包裹,面单上有两个编号:寄件方编号(DeviceID)和商品编号(EventID)。
ITS 手里有三本"册子":
- Device Table:按寄件方编号索引的公司名录,告诉你这家公司的包裹清单放在哪;
- ITT(中断翻译表):该公司的包裹清单,按商品编号索引,查出最终投递编号(LPI INTID);
- Collection:收件方登记簿——每个投递编号绑定一个 Collection,由它决定中断投到哪个 CPU 的 Redistributor。
所以一条 MSI 走完 Device Table → ITT → Collection 三次查表,就成了"LPI 编号 + 目标 CPU"。表是内核提前用命令队列填好的,运行时硬件纯查表,速度快且不占 CPU。
内核落点:从 ITS 探测到 MSI 控制器注册
内核把 ITS 拉起来,用一条链就能串完:
- 探测:
its_of_probe遍历设备树,对每个 ITS 节点调用its_probe_one复位硬件、读尺寸寄存器,再由its_alloc_tables和its_alloc_collections分配三张表; - 填表:内核按 MAPD → MAPC → MAPTI → MOV → SYNC 的顺序往命令队列里塞命令——MAPD 绑设备、MAPC 把 Collection 绑到 CPU、MAPTI 写入 EventID→LPI 映射、MOV 改亲和性、SYNC 让硬件失效本地缓存;
- 注册:最后挂上一个标准 MSI domain 交给 PCIe 栈,此后驱动调
pci_enable_msi_range即可,翻译细节全透明。
源码入口在 drivers/irqchip/irq-gic-v3-its.c,约 5900 行,本文主线都在这一个文件里。
3 步配好 ITS 设备树 📌
第 1 步,GICv3 父节点:compatible = "arm,gic-v3",reg给两条——GICD 基址0x3e000000长 0x10000,GICR 区0x3e100000长 0x200000;再写#interrupt-cells = <3>和interrupt-controller;两行属性。
第 2 步,ITS 节点:
its: msi-controller@3e200000 { compatible = "arm,gic-v3-its"; msi-controller; #msi-cells = <1>; reg = <0x3e200000 0x20000>; };逐字段说:compatible让内核按its_device_id匹配;msi-controller是硬开关,漏了它its_of_probe会打印 "no msi-controller property, ITS ignored" 直接跳过;#msi-cells = <1>表示 MSI 数据是一个 32 位 EventID;reg是 ITS 寄存器空间物理地址和大小。
第 3 步,把设备指给 ITS:在 PCIe 主机桥(或外设)节点上加一条 phandle:
pcie@80000000 { compatible = "arm,pcie-host"; msi-parent = <&its>; reg = <0x80000000 0x100000>; };这样该桥下所有设备的 MSI 都走这个 ITS 翻译。
避坑清单:亲和性与多 ITS 的三个常见坑
| 现象 | 原因 | 对策 |
|---|---|---|
写smp_affinity后smp_affinity_list纹丝不动 | 写入的掩码里一个在线 CPU 都没有,内核保留原亲和 | 先cat /sys/devices/system/cpu/online,只写在线位 |
| 亲和改了,中断却总在同一个 CPU 上跑 | 目标 CPU 的 Redistributor 未在线、Collection 未注册,LPI 落到默认投递路径 | 只指向在线 CPU;用dmesg \| grep -i gic确认各 CPU 初始化完成 |
| dmesg 出现 "ITS ignored",部分设备收不到中断 | ITS 节点漏写msi-controller属性;多 ITS 时 GICv4 还要求全局编号唯一,撞号会报 "Duplicate ITSList" | 每个 ITS 节点都补齐属性;多实例时错开物理地址、别共用 ITSList 编号 |
延伸阅读
- 源码:drivers/irqchip/irq-gic-v3.c、drivers/irqchip/irq-gic-v3-its.c
- 设备树绑定:Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
- 规范:ARM IHI 0069(GICv3/GICv4 架构规范)
下一步建议:先在目标机上跑dmesg | grep -E "GICv3: (Using LPIs|ITS)"确认 ITS 已拉起,再对照/proc/interrupts里 LPI 列在 16 个 CPU 上是否均匀分布——不均匀,就回到上面的避坑表逐条查。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
