ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linux GICv3 ITS 翻译表:从 DeviceID 到 LPI 的一次完整走读

2026/8/29 11:40:21 拓冰建站 浏览量
Linux GICv3 ITS 翻译表:从 DeviceID 到 LPI 的一次完整走读 Linux GICv3 ITS 翻译表从 DeviceID 到 LPI 的一次完整走读【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux一块 NVMe 盘开满 8 个队列需要 8 个 MSI 向量而 SPI 中断号最多只有 992 个且路由基本绑死在固件上设备一多、队列一开中断号就不够分了。Linux 内核里 GICv3 的 ITSInterrupt Translation Service中断翻译服务就是为此设计的它把每条 MSI 消息翻译成 LPILocality-specific Peripheral Interrupt并动态路由到目标 CPU。下面按翻译原理 → 内核关键路径 → 设备树 → 运行时调优的顺序把 Linux GICv3 ITS 从头到尾讲一遍读完你可以直接对照自己的设备树和源码排查问题。 问题为什么 SPI 撑不住了GICv3 本身支持四类中断SGISoftware Generated Interrupt软件生成中断CPU 之间互发编号 0–15PPIPrivate Peripheral Interrupt私有外设中断每个 CPU 一份私有SPIShared Peripheral Interrupt共享外设中断外设共享编号 32–1023LPILocality-specific Peripheral Interrupt靠 MSI 消息触发从 8192 起步LPI 的存在本身就是答案。旧方案和它的对比一目了然传统 SPI 路由ITS LPI可用中断编号992 个32–1023全系统共享从 8192 到 2^32-1规模由硬件 IDBITS 字段决定中断到 CPU 的绑定固件阶段配死运行时改动麻烦每条 LPI 可单独改亲和性数字锚点LPI 编号起点 8192 来自源码里的硬编码SGI/PPI/SPI 占用前 8192 个编号优先级寄存器 8 位共 256 级。 翻译怎么发生三个步骤1. 消息到达设备发的 MSI 本质上是一条指向 ITS 的 TLP 消息目标地址就是 ITS 的 Translater 寄存器GITS_TRANSLATER偏移 0x10040。这条消息里藏着两个字段DeviceID 和 EventID。2. 查表翻译ITS 内部依次走三张表内核初始化时负责把它们填好表索引起点表项指向内核对应命令Device TableDeviceID该设备的中断翻译表ITT基址MAPDInterrupt Translation TableEventID一个 LPI 中断号MAPTICollectionLPI 中断号目标 CPU 的亲和性MAPC / MOVI举个具体数值DeviceID0x21、EventID3 的消息进来Device Table 第 0x21 条指向 ITTITT 第 3 条读出 LPI 中断号 8196Collection 表说 8196 归 CPU 5。3. 路由到目标 CPU重分布器Redistributor每个 CPU 一个按 Collection 指向把这条 LPI 投递出去目标核跑它的处理程序。翻译完成之后MSI 的后续处理完全在硬件里完成不占 CPU 周期。翻译表是内核建起来的靠的是 ITS 命令队列命令队列的固定顺序是 MAPD → MAPC → MAPTI → MOVI → SYNC先绑设备、再绑 Collection、然后填 EventID 映射、需要时改亲和性最后 SYNC 使缓存失效。队列满了内核会等它排空再写这一段逻辑全部在 drivers/irqchip/irq-gic-v3-its.c 里。 内核关键路径从探测到 MSI domain入口是 drivers/irqchip/irq-gic-v3-its.c 里的its_init()它按设备树或 ACPI 遍历所有 ITS 节点逐个走its_probe_one()——分配命令队列页、调its_alloc_tables()建三张表、写 CBASER 挂上队列、置位 GITS_CTLR_ENABLE 使能硬件随后allocate_lpi_tables()全局分配 LPI 属性表最后给每个 ITS 注册一个 MSI domain。值得单独看的是注册这一步父 domain 指回 GICv3 主 domain子 domain 承载 MSI 向量分配逻辑。没有这一段设备树里所有msi-parent指向 ITS 的设备都拿不到 MSI 向量。static int its_init_domain(struct its_node *its) { struct irq_domain_info dom_info { .fwnode its-fwnode_handle, .ops its_domain_ops, .domain_flags its-msi_domain_flags, .parent its_parent, }; struct msi_domain_info *info; info kzalloc_obj(*info); if (!info) return -ENOMEM; info-ops its_msi_domain_ops; info-data its; dom_info.host_data info; return msi_create_parent_irq_domain(dom_info, gic_v3_its_msi_parent_ops) ? 0 : -ENOMEM; }its_msi_domain_ops挂在一组操作后面设备pci_enable_msi_range()要 N 个向量时内核从 LPI 号池切一段连续的 LPI 给它逐条填好翻译表设备驱动全程只用标准 MSI 接口对 ITS 无感知。 设备树里怎么写 ITS 节点GICv3 节点和 ITS 节点的完整写法可以对照 Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml 的官方示例。精简版如下gic: interrupt-controller2cf00000 { compatible arm,gic-v3; #interrupt-cells 3; #address-cells 1; #size-cells 1; ranges; interrupt-controller; reg 0x2f000000 0x10000, /* GICD 分发器 */ 0x2f100000 0x200000; /* GICR 重分布器基址 */ /* ITS 作为子节点每个实例一个 */ its0: msi-controller2c200000 { compatible arm,gic-v3-its; msi-controller; /* 标记它是 MSI 控制器 */ #msi-cells 1; /* 恒为 1设备号 */ reg 0x2c200000 0x20000; /* ITS 寄存器空间 */ }; }; nvme: nvme01000000 { compatible pci-nvme; msi-parent its0; /* MSI 请求交给 ITS */ interrupts-msi 1 0; /* 1 个向量EventID 从 0 起 */ };逐段说明compatible arm,gic-v3-its是内核its_of_probe()匹配的钥匙#msi-cells必须写 1因为 MSI 父数据只有一个 DeviceID外设不再用interrupts改用msi-parent加interrupts-msi向量数量由 PCI 枚举阶段协商。属性细节比如mbi-ranges、redistributor-stride的写法以官方绑定文档为准链接见上文。⚙️ 跑起来之后还能调什么中断亲和性echo 3 /proc/irq/lpi号/smp_affinity把中断挪到 CPU 0、1。需要它的场景某个核被某设备的中断打满、邻居核却闲着时。优先级GICv3 的优先级寄存器 8 位、256 级数值越小越优先。LPI 的优先级在启动时统一设置SPI 可在 DTS 中断单元格第三位逐条指定。需要它的场景把关键设备如存储控制器的中断压过大量低优先级设备的噪声。多 ITS 实例并列写多个 ITS 节点即可内核会把设备分摊到不同实例。需要它的场景设备数上万的平台用多个实例分摊命令队列和翻译表压力小设备数的单板 SoC 通常一个 ITS 足够。一句话收尾Linux GICv3 ITS 做的事情很克制把 DeviceID EventID 翻译成 LPI再把这条 LPI 动态指向目标 CPU其余交给硬件。源码入口 drivers/irqchip/irq-gic-v3-its.c设备树写法看 Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml寄存器与命令定义以 ARM 官方 GICv3/v4 架构规范IHI 0069为准。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考