从 PLIC 到 IMSIC——RISC-V 中断投递的 Linux 实践
在单核系统里,中断处理的路由问题简单到近乎不存在:所有中断都送到唯一的一个核,软件的全部职责就是"及时响应"。而一旦进入多核 SoC,每一个中断都变成一道寻址题——发给哪个核?以什么优先级?由谁屏蔽?在哪个上下文处理?投递路径的每一次含糊,要么退化为"所有中断挤在一个核上处理"的单点瓶颈,要么演变为"中断在核间流浪"的缓存行乒乓。
中断投递之所以值得 RISC-V Linux 开发者系统性梳理,原因有二:
其一,它是实时性的地基。中断从设备拉到 CPU 的路径上串联了控制器仲裁、总线投递、CSR 状态、内核 irq 层、softirq 软中断五个环节;每个环节的延迟预算叠加起来,才是系统对外的响应能力承诺——cyclictest 测出的最小延迟,就是这条路径的裸代价。
其二,它是扩展性的暗渠。多队列网卡每个队列一个 MSI,NVMe 每个队列一个中断,音频、看门狗、GPU 各有各的投递需求——中断在核间的分布策略,直接决定前几篇文章反复讨论的"跨簇流量"是发生在数据面还是中断面。一个 affinity 配置不当的 16 队列网卡,可以让精心调优的数据通路重新坍缩回单核。本文围绕"中断投递的机制形态、RISC-V 规范如何定义投递语义、Linux 上如何落地与排查"三个层面展开。
一、中断投递问题从哪里来
1.1 三个软件可见的场景
多核系统中,中断投递需要回答的问题可以归纳为三类:
设备中断的核间分布:同一个设备(尤其是多队列网卡)的多个中断源,应该分发给哪些核?全部集中在一个核意味着串行化瓶颈;任意分散则可能让中断处理与业务线程落在不同簇,缓存行在"收包核"与"处理核"之间弹跳。这是运维侧 /proc/irq/N/smp_affinity 调优的全部出发点。
核间中断(IPI):调度器唤醒(reschedule)、TLB shootdown(本系列第二篇的主角)、停止 CPU(stop_cpu)……IPI 是内核自己制造的中断,它的投递机制完全不走设备控制器。IPI 的代价(发送方的等待、接收方的上下文切换)是多核扩展性的隐性天花板。
虚拟化中断直通:直通给虚拟机的设备,其中断必须送达正在运行该 VCPU 的物理核,且要带上正确的"身份"(属于哪个虚拟中断文件);定时器等虚拟中断还需要 hypervisor 拦截后注入。中断子系统的架构直接决定直通路径是"硬件直达"还是"hypervisor 转手"——两者性能差距可达一个量级。
1.2 一个关键的认知框架:中断投递是"事件到核的寻址问题"
进入具体机制之前,先确立一个对后文所有讨论都成立的框架:
中断子系统的演进史,就是事件寻址方式从"集中仲裁、软件认领"到"直接写邮箱、硬件路由"的迁移史。
PLIC 代表旧范式:一个集中的仲裁器接收所有外设的线中断,软件查询"是谁在响"、认领(claim)、处理完毕再确认(complete)——一次投递是三次控制器往返。AIA/IMSIC 代表新范式:每个核拥有一个内存映射的"邮箱"(中断文件),投递本身就是一次普通的内存写(MSI 写到目标核的邮箱地址即完成路由)——集中仲裁与软件认领被彻底删除。这个框架与数据缓存世界的"嗅探到目录"迁移、IOMMU 的"物理直连到队列化翻译"高度同构:系统的每一次扩展,都伴随着控制面的去中心化。识别这个范式,是理解为什么内核社区如此迫切推动 AIA 迁移的钥匙。
二、机制层:PLIC、AIA 与两种投递形态
尽管实现细节属于各 SoC 厂商,两种机制的软件可见行为值得逐一拆解。
2.1 PLIC:集中仲裁与 claim/complete 往返
PLIC(Platform-Level Interrupt Controller)管理所有线中断(wired interrupt)源:每个源有优先级寄存器与使能位(全局一份 + 每上下文一份),仲裁器在挂起的源中挑出各上下文可见的最高优先级者,置位 CPU 的中断 pending 位。软件侧的处理流程是其代价所在:
- trap 入口读 claim 寄存器——这一读同时完成"清除控制器 pending"并返回中断源 ID;
- 按源 ID 分发到内核 irq 层的处理函数;
- 处理完毕后写 complete 寄存器,通知控制器"可以再投这个源了"。
三点结构性短板值得注意:其一,claim 与 complete 之间,同源中断被阻塞——对高频中断源,这决定了处理串行化的下界;其二,电平触发中断的时序约束微妙(complete 过早可能导致同一源重复认领,过晚则丢失新事件),驱动层的边沿/电平选择必须与控制器配置精确对齐;其三,"发给哪个核"由各上下文的使能位集合决定,粒度粗、更新慢,本质上是共享信箱模型——中断一旦被某个核 claim,就永久归属它,与缓存一致性问题里的"写者归属"异曲同工:归属错了核,数据就得搬家。
2.2 AIA 与 IMSIC:中断即内存写
AIA(Advanced Interrupt Architecture)的核心组件 IMSIC(Incoming MSI Controller)把每个 hart 变成一个邮箱集合:每个 hart 拥有若干"中断文件"(interrupt file),每个文件是一个 MSI 目标地址 + 一组 pending/enable 位。外设(或另一个核发 IPI,或 IOMMU 转发 MSI——呼应第三篇的 MSI 重映射)只需向目标文件的地址写一个值,投递即完成。
新形态的收益是全方位的:投递路径变成一次普通的内存写,与 DMA 同路,天然享受互连的优化;路由粒度细化到"每个中断源每次投递都可指定目标核",affinity 是逐中断文件的;软件开销上 claim/complete 往返消失——pending 位在邮箱里,trap 入口由 CSR(topei,top external interrupt)一次性读出"最高优先级源 + 其向量",连读控制器的总线往返都省了。代价则是生态迁移成本:设备树描述、固件初始化、内核 irqchip 驱动,全链条都要就位,这也是当前市面上 PLIC 与 IMSIC 两代机制并存的原因。
2.3 优先级与嵌套:软件可见的行为契约
两种机制的另一个分野在优先级处理:PLIC 提供每源的优先级寄存器,但不支持中断嵌套抢占——处理外部中断时 mip 的 EIE 位被清,更高优先级源只能等当前处理完。AIA 定义了嵌套机制(配合 hideleg/hedeleg 与优先级 CSR 组合),允许更高优先级中断打断低优先级处理。对实时工作负载,这是架构层面的能力差异;对吞吐工作负载,嵌套能力则几乎不影响结果——又一个"规范定义能力、负载决定价值"的例证。
三、RISC-V 的立场:CSR 契约、SBI 抽象与生态位
3.1 软件可见行为的定义者
规范层面,中断子系统的软件可见契约由三组机制界定:CSR 状态机(mip/mie/mstatus 的 MIE 位、stvec/scause/sepc 构成 trap 入口,AIA 扩展了 topei/xiselect 一族间接 CSR);投递语义(PLIC 的 claim/complete 往返、IMSIC 的写即投递);虚拟化拆分(AIA 为每个 hart 提供 supervisor 与 VS 两级中断文件,hypervisor 可以把设备 MSI 直接路由进 guest 的虚拟文件,无需逐次注入)。与前几篇同理:规范规定"契约的最小集",实现可以更宽松(比如把 PLIC 的 claim 做得更快),软件论证必须建立在规范承诺之上。
3.2 SBI:固件之上的统一抽象
从内核视角看,一些中断操作(发送 IPI、屏蔽 S 模式外部中断、处理 Sstc 之外的定时器)经由 SBI 调用而非直接 CSR 访问——M 模式固件(OpenSBI 等)持有真正的中断控制器所有权,内核只获得委托后的接口。这层抽象让同一份内核可以跑在 M 模式初始化差异巨大的各代 SoC 上,也意味着 IPI 的真实路径是"内核 → SBI ecall → 固件 → 邮箱/控制器",比表面看多一跳。做 IPI 微基准时,这一跳的固定开销必须计入基线。
3.3 生态位:两代机制并存的过渡期
当前的产业现实是过渡期并存:绝大多数已量产 RISC-V SoC 沿用 PLIC(复用成熟的设备树描述与驱动),高性能与虚拟化导向的新平台(典型如服务器路线)转向 AIA/IMSIC,部分实现采取"PLIC 存在但 AIA 优先"的兼容姿态。内核通过设备树探测自动选择 irqchip 路径,但对 BSP 与性能工程师,"目标平台走哪条中断路径"是排障与调优的第一问——两代机制的观测接口、affinity 语义、微基准结果都不相同,混用经验是错误结论的高发区。以玄铁处理器为核心的平台在从 PLIC 向 AIA/IMSIC 的迁移路线上的适配进展,可以参考玄铁官网及其开放技术文档,具体核型与 SoC 的中断控制器配置以平台手册为准。
四、Linux 上的落地:把中断钉在正确的核上
中断机制在硬件里跑,但决定投递质量的是内核的 irq 层与运维的 affinity 策略。
4.1 认清拓扑:中断都挂在哪,谁能收到
# 每个中断源的计数与归属核
cat /proc/interrupts
# 软中断(后半段)的分布——hardirq 之外的第二战场
cat /proc/softirqs
# 某中断的当前亲和性与可选项
cat /proc/irq/38/smp_affinity_list
/proc/interrupts 的列即核、行即中断源,跑一段负载前后对比增量,是判断"中断是否集中单核"最直接的手段。注意 hardirq 计数只反映前半段,NAPI 之类的软中断处理在 /proc/softirqs 的 NET_RX 行——两个文件要一起看。
4.2 affinity:中断亲和性三原则
借鉴本系列第一篇的"同簇亲和"原则,中断亲和性的调优可归纳为三条:
中断跟随数据:网卡的队列中断应与消费该队列的线程同簇(最好同核),让 DMA 写入的缓存行直接落在处理者热缓存里——多队列网卡的 RSS 队列号、IRQ 号、处理线程三者的对齐,是网络调优的经典基本功。
避免 IRQ0 陷阱:默认配置下大量中断挤在 CPU0,使它成为全系统的隐性串行点;显式为高频中断源设置 affinity,或让 irqbalance 在 NUMA/簇拓扑上自动分布,二者择一,但不要同时手动干预又跑 irqbalance(互相打架,affinity 飘忽不定)。
管理式中断尊重拓扑:现代驱动的部分中断是"managed IRQ",亲和性由内核按设备 NUMA 本地性预置且不可手改——这类中断出现在 /proc/interrupts 里却没有 smp_affinity 写权限是正常现象,不是故障。
4.3 IPI:内核自产中断的成本核算
/proc/interrupts 里的 IPI 行(reschedule、call function、TLB shootdown)暴露了内核自产中断的流量。三个高频来源对应三篇前文:调度器唤醒(wakeup path 的 load_balance 与 wake_affine 策略)、TLB shootdown(第二篇的 munmap 风暴)、模块间 per-CPU 回调(smp_call_function 的滥用)。IPI 计数与业务操作次数的比例,是判断"多核税"最便宜的量化指标——不需要 PMU,watch -d cat /proc/interrupts 就够用。
4.4 虚拟化:直通路径的两级跳
IMSIC 的两级中断文件让设备直通的中断路径实现了"硬件直达":设备的 MSI 经 IOMMU 重映射(第三篇的 MRTW 机制)直接写入 guest 的 VS 文件,VCPU 所在物理核立即收到,全程无 hypervisor 参与。相比之下,PLIC 时代的直通中断要经 hypervisor 捕获、查询归属、注入 VCPU 的软件转手,每次多出微秒级的往返。评估虚拟化平台的中断能力时,"直通中断走哪条路径"是与 DMA 路径同等重要的选型指标(当前内核对 IMSIC 直通的支持状态以 Documentation 与 KVM 文档为准)。
五、验证:中断路径的代价要靠测出来
5.1 中断延迟:实时性的裸代价
# 经典工具(用户态测量调度+中断全路径延迟)
cyclictest -m -p95 -h 100 -a -t <核数> -D 60s
cyclictest 的 min 值近似"定时器中断 → trap → 调度回用户态"的裸路径代价;max 与_histogram 尾部反映中断被高优先级处理或关中断区间阻塞的最坏情形。对比同平台 PLIC 与 IMSIC 配置(若可切换)的尾部延迟,是评估 AIA 嵌套能力实际收益的直接手段。
5.2 投递吞吐微基准
测量中断投递的裸速率,方法是让设备(或另一个核经 IPI)以固定速率发中断、最小化处理逻辑,统计每秒实际完成的中断数:
- 同核绑定下测出的即"单核中断处理上限";
- 改变中断源数量与 affinity 分布,观察吞吐是线性扩展还是饱和——饱和点就是 claim/complete 串行化(PLIC)或邮箱写带宽(IMSIC)的实现上限。
5.3 一份示例对照(量级说明用)
| 网卡 16 队列 affinity 策略 | 有效吞吐 | CPU 占用特征 |
|---|---|---|
| 全部默认(挤 CPU0) | 明显受限 | CPU0 满载,其余核空转 |
| 按簇分散、与 RSS 队列错位 | 接近线性但有回退 | 跨簇缓存行乒乓(呼应第一篇) |
| 按簇分散、与处理线程同簇对齐 | 最高且近线性 | 中断、数据、处理同簇落位 |
(示例仅用于说明趋势:中断亲和性的收益本质是"把第二篇的广播税和第一篇的乒乓税一起消掉";具体数字因网卡、核数、拓扑而异,不可外推为普遍规律。)
5.4 排查残留问题
若中断路径仍不及预期,按顺序检查:/proc/interrupts 高频源是否集中单核(affinity 失效或未设置);/proc/softirqs 的 NET_RX 是否与 hardirq 分布脱节(软中断聚合到了别的核,ksoftirqd 成瓶颈);irqbalance 与手动 affinity 是否冲突(systemd 状态与 /proc/irq/*/smp_affinity 的稳定性);IPI 计数是否异常(关联 TLB shootdown 或调度风暴,回到前两篇的排查方法);虚拟化场景下直通中断是否走了 hypervisor 转手的慢路径(guest 内测延迟显著高于宿主机直测)。
六、结语:规范定义投递语义,拓扑决定落位收益
RISC-V 中断投递的工程全貌,可以压缩为一句话:规范(PLIC 的 claim 契约、AIA 的邮箱语义与 CSR 族)定义事件寻址的语义承诺,实现(仲裁延迟、邮箱带宽、嵌套能力)决定履行承诺的投递上限,而内核的 affinity 布局决定每一次投递落位之后引发的连带流量。
至此四篇成环:数据缓存一致性讲核间数据如何看见(第一篇),TLB 一致性讲核间翻译如何作废(第二篇),IOMMU 讲设备访存如何翻译与隔离(第三篇),中断投递讲事件如何找到正确的核(第四篇)。四者是同一个问题的四面——多核 SoC 上,任何跨界的访问(数据、翻译、设备访存、事件)都是一次需要付费的寻址。规范定语义,实测定代价,落位布局定成败——这套方法贯穿四篇,也是多核系统调试可迁移的通用心法。
浙公网安备 33010602011771号