☰
SSD 的 MSI / MSI-X 中断机制解析
2026/10/7 2:44:21 网站建设 项目流程

本文主要针对PCIe NVMe SSD。
SATA/AHCI SSD 也可能使用 MSI/MSI-X,但其命令完成模型是 AHCI/NCQ,不完全适用 NVMe 的 SQ/CQ、Completion Queue Interrupt Vector 等概念。


目录

1. 先给结论

2. INTx、MSI、MSI-X 的区别

2.1 Legacy INTx

2.2 MSI

MSI 的关键特点

2.3 MSI-X

MSI-X 的优势

2.4 对比表

3. MSI/MSI-X 中的几个“编号”不要混淆

3.1 NVMe Completion Queue ID

3.2 MSI-X Vector Index

3.3 Linux IRQ Number

3.4 CPU Interrupt Vector

4. NVMe 中的队列和中断关系

4.1 Submission Queue 和 Completion Queue

4.2 Completion Queue 的 Interrupt Vector

5. NVMe MSI-X 的完整工作流程

5.1 PCIe 枚举阶段

5.2 MSI-X Table 初始化

5.3 NVMe 控制器初始化

5.4 主机提交 I/O

5.5 SSD 写入 Completion Queue

5.6 CPU 处理中断

6. 为什么有时“完成了但没有中断”

6.1 处于轮询模式

6.2 中断被 Mask

6.3 中断聚合

7. 常见应用场景

7.1 一队列一个 MSI-X 向量

7.2 多个 CQ 共享一个 MSI-X 向量

7.3 降级到单 MSI

7.4 轮询模式

7.5 虚拟化和 SR-IOV

8. Linux 下的检查方法

8.1 查看 MSI/MSI-X 是否启用

8.2 查看实际中断计数

8.3 查看 PCI 设备的 MSI IRQ 列表

8.4 查看 IRQ CPU 亲和性

8.5 查看内核日志

8.6 查看 PCIe 链路和 AER 状态

9. 常见故障现象和排查思路

9.1 现象一:I/O 超时,/proc/interrupts 不增长

优先检查

常见原因

9.2 现象二:IRQ 计数增长,但 I/O 仍然超时

9.3 现象三:中断风暴

9.4 现象四:只有一个 CPU 有中断,其他 CPU 没有

9.5 现象五:性能低、CPU 中断占用高

9.6 现象六:系统重置或热复位后中断失效

10. MSI-X 硬件或 FPGA SSD 的调试重点

10.1 MSI-X Capability 描述错误

10.2 没有使用主机写入的地址和数据

10.3 CQE 和 MSI-X 消息的顺序错误

10.4 Vector Index 和 CQ 配置不一致

10.5 Mask 和 Pending 处理错误

11. 建议的系统化排查流程

第一步:确认是否真的需要中断

第二步:确认 PCIe 中断模式

第三步:确认实际 IRQ

第四步:确认控制器和队列状态

第五步:确认所有 Mask 层

第六步:确认 PCIe、IOMMU 和中断路由

第七步:确认 CPU 和 NUMA

12. 需要特别记住的几个结论


1. 先给结论

NVMe SSD 的一次典型 I/O 完成过程可以概括为:

主机提交 SQ Entry │ ▼ SSD 控制器 DMA 读取 SQ │ ▼ SSD 执行命令 │ ▼ SSD DMA 写入 CQE │ ▼ SSD 发送 MSI/MSI-X 消息 │ ▼ Root Complex / IOMMU / APIC 或 GIC │ ▼ CPU 进入 NVMe 中断处理函数 │ ▼ 驱动读取 CQE、完成请求、更新 CQ Head Doorbell

最重要的一点:

MSI/MSI-X 只是“通知主机有完成事件”,中断消息本身不携带 NVMe Completion Entry。
真正的完成信息已经由 SSD 通过 DMA 写入主机内存中的 Completion Queue。


2. INTx、MSI、MSI-X 的区别

2.1 Legacy INTx

传统 PCI 中断通常使用 INTx 引脚:

设备拉低中断线 │ ▼ 主机响应中断 │ ▼ 驱动读取设备状态并清除中断

特点:

  • 物理或逻辑中断线;
  • 多个 PCI 设备可能共享同一条中断线;
  • 属于电平触发方式;
  • 需要设备和驱动显式清除中断状态;
  • 扩展性差,容易产生共享中断和中断抖动。

在现代 NVMe SSD 中,INTx 通常只是兼容性兜底方式。


2.2 MSI

MSI,即 Message Signaled Interrupt。

它不是通过独立的中断引脚,而是设备发起一个 PCIe Memory Write TLP,写入由操作系统配置好的中断消息地址和数据:

SSD │ │ PCIe Memory Write TLP ▼ MSI 地址 / 数据 │ ▼ Root Complex │ ▼ APIC / GIC / Interrupt Remapping │ ▼ CPU IRQ

MSI 的关键特点

  • 不使用共享中断线;
  • 中断通过 PCIe 总线事务传递;
  • 一般支持 1、2、4、8、16、32 个消息;
  • 多 MSI 数量通常要求是 2 的幂;
  • 所有 MSI 消息通常使用相同的 Message Address,仅 Message Data 有区别;
  • 每个 MSI 向量的独立性不如 MSI-X;
  • 支持能力受设备、操作系统和平台限制。

PCI MSI Capability 中通常包括:

  • MSI Enable;
  • Multiple Message Capable;
  • Multiple Message Enable;
  • 32 位或 64 位 Message Address;
  • Message Data;
  • 可选的 Per-Vector Mask 和 Pending Bits。

2.3 MSI-X

MSI-X 是为大量独立中断向量设计的扩展机制。

MSI-X 一般最多支持 2048 个向量,而且每个向量都有独立的:

Message Address Message Data Vector Mask

典型结构如下:

MSI-X Table ┌────────────────────────────┐ │ Entry 0: Addr/Data/Mask │ │ Entry 1: Addr/Data/Mask │ │ Entry 2: Addr/Data/Mask │ │ ... │ │ Entry N: Addr/Data/Mask │ └────────────────────────────┘ PBA: Pending Bit Array

MSI-X Table 和 Pending Bit Array 通常位于设备 BAR 指定的位置。

每个 MSI-X Table Entry 通常包含:

Message Address Low Message Address High Message Data Vector Control

其中 Vector Control 中通常有 Mask 位。

MSI-X 的优势

  • 每个队列可以使用独立向量;
  • 每个向量可以绑定到不同 CPU;
  • 适合 NVMe 多队列、高 IOPS 场景;
  • 支持任意数量的向量,不要求 2 的幂;
  • 向量间隔离性更好;
  • 可以将不同队列分散到不同 NUMA 节点和 CPU。

2.4 对比表

特性INTxMSIMSI-X
传递方式中断线PCIe Memory WritePCIe Memory Write
是否共享经常共享通常不共享通常不共享
向量数量少通常最多 32最多 2048
向量数量要求无通常为 2 的幂可任意分配
每向量独立地址无有限有
每向量独立 Mask无可选支持
适合 NVMe 多队列较差一般最适合
典型使用兼容性回退低队列或降级场景高性能默认方案

3. MSI/MSI-X 中的几个“编号”不要混淆

NVMe 调试中经常把以下几个概念混为一谈。

3.1 NVMe Completion Queue ID

例如:

CQ1、CQ2、CQ3

这是 NVMe 协议中的队列编号。


3.2 MSI-X Vector Index

例如:

MSI-X vector 0、vector 1、vector 2

这是 MSI-X Table 的索引,也是 NVMe Create I/O Completion Queue 命令中 IV 字段所引用的向量编号。


3.3 Linux IRQ Number

例如:

IRQ 123 IRQ 124

这是 Linux 内核分配给设备中断的 IRQ 号。


3.4 CPU Interrupt Vector

这是 CPU/APIC 层面的硬件中断向量,例如某个内部向量号。


它们之间并不一定相等:

NVMe IV = 3 │ ▼ MSI-X Table Entry 3 │ ▼ Linux IRQ 157 │ ▼ CPU APIC Vector 0xE1

因此:

NVMe 的 Interrupt Vector、MSI-X Table Index、Linux IRQ 号,不是同一个概念。


4. NVMe 中的队列和中断关系

4.1 Submission Queue 和 Completion Queue

NVMe 使用成对或多对队列:

Submission Queue,SQ:主机提交命令 Completion Queue,CQ:SSD 返回完成结果

典型关系:

SQ1 ───────┐ ├──> CQ1 ──> MSI-X Vector 1 SQ2 ───────┘ SQ3 ───────┐ ├──> CQ2 ──> MSI-X Vector 2 SQ4 ───────┘

多个 Submission Queue 可以关联到同一个 Completion Queue。

真正决定中断向量的是Completion Queue,而不是 Submission Queue。


4.2 Completion Queue 的 Interrupt Vector

创建 I/O Completion Queue 时,主机通常会指定:

  • Completion Queue ID;
  • 队列深度;
  • 队列物理地址;
  • Interrupt Enable,通常称为IEN;
  • Interrupt Vector,通常称为IV。

概念上相当于:

Create CQ1: IEN = 1 IV = 1 Create CQ2: IEN = 1 IV = 2

于是:

CQ1 的完成事件 -> MSI-X Vector 1 CQ2 的完成事件 -> MSI-X Vector 2

Admin Completion Queue 在规范和常见驱动实现中通常使用 vector 0,但具体仍以驱动和控制器实现为准。


5. NVMe MSI-X 的完整工作流程

5.1 PCIe 枚举阶段

系统启动时,PCIe Root Complex 枚举 SSD:

  1. 读取 PCI Configuration Space;
  2. 检查 MSI Capability;
  3. 检查 MSI-X Capability;
  4. 读取 MSI-X Table Size;
  5. 读取 MSI-X Table 的 BAR、偏移;
  6. 读取 PBA 的 BAR、偏移;
  7. 分配 PCI 资源;
  8. 建立中断路由。

Linux 中驱动通常会通过类似以下机制申请中断向量:

pci_alloc_irq_vectors(...) pci_irq_vector(...) request_irq(...)

实际函数和参数会随内核版本变化。

一般选择顺序是:

MSI-X ↓ 失败 MSI ↓ 失败 Legacy INTx

不过具体行为依赖驱动和操作系统。


5.2 MSI-X Table 初始化

操作系统或 PCI 子系统会为每个 MSI-X Table Entry 写入:

Message Address Message Data Vector Mask

例如:

MSI-X Entry 0 -> Linux IRQ 100 MSI-X Entry 1 -> Linux IRQ 101 MSI-X Entry 2 -> Linux IRQ 102

注意:

  • Table 中的 Message Address 不是固定值;
  • 不能把 x86 上常见的0xFEE...地址硬编码到设备中;
  • 在 IOMMU、Interrupt Remapping、虚拟化或 ARM GIC ITS 环境下,地址和数据可能完全不同;
  • 设备必须使用主机写入的地址和数据。

5.3 NVMe 控制器初始化

驱动随后初始化 NVMe 控制器:

  1. 创建 Admin Submission Queue;
  2. 创建 Admin Completion Queue;
  3. 使能控制器;
  4. 查询或设置 I/O Queue 数量;
  5. 创建 I/O Completion Queues;
  6. 为每个 CQ 指定IV;
  7. 创建与 CQ 关联的 Submission Queue;
  8. 开启相关中断;
  9. 绑定 IRQ affinity。

一个常见布局可能是:

MSI-X Vector 0 -> Admin CQ MSI-X Vector 1 -> I/O CQ 1 MSI-X Vector 2 -> I/O CQ 2 MSI-X Vector 3 -> I/O CQ 3 ...

但如果向量不够,也可能是:

MSI-X Vector 1 -> CQ1、CQ5 MSI-X Vector 2 -> CQ2、CQ6 MSI-X Vector 3 -> CQ3、CQ7

5.4 主机提交 I/O

主机驱动完成以下动作:

  1. 在 SQ 中填写 NVMe Command;
  2. 更新本地 SQ Tail;
  3. 通过 MMIO 写 SQ Tail Doorbell;
  4. SSD 看到 Doorbell 变化;
  5. SSD DMA 读取 SQ Entry。

5.5 SSD 写入 Completion Queue

命令完成后,SSD:

  1. 执行存储操作;
  2. 生成 Completion Entry;
  3. 通过 DMA 将 CQE 写入主机内存;
  4. 更新 CQ Entry 中的 Phase Bit;
  5. 根据中断聚合和 Mask 状态决定是否发送中断;
  6. 找到对应的 Interrupt Vector;
  7. 读取 MSI-X Table Entry;
  8. 生成 MSI-X PCIe Memory Write。

CQE 中通常包含:

  • Command Identifier,CID;
  • Submission Queue Head;
  • Submission Queue Identifier;
  • Status;
  • Phase Bit;
  • 其他状态信息。

5.6 CPU 处理中断

CPU 收到 MSI-X 后进入类似以下逻辑:

nvme_irq(vector) ├── 找到该 vector 关联的 CQ ├── 检查 CQ 中是否有新的 CQE ├── 检查 Phase Bit ├── 读取 CID 和 Status ├── 完成对应 Block Layer Request ├── 推进本地 CQ Head └── 写 CQ Head Doorbell

注意:

在 MSI-X 中,设备没有像 INTx 那样的“拉线保持”和明确的硬件 ACK 概念。
主机真正解除设备侧待处理状态的关键动作,是消费 CQE 并更新 CQ Head Doorbell。
CPU 的 APIC EOI 仍由操作系统中断子系统处理。


6. 为什么有时“完成了但没有中断”

以下情况都可能导致 CQE 已经写入,但暂时没有看到中断:

6.1 处于轮询模式

某些 NVMe 驱动、SPDK、DPDK 或高性能应用会使用 polling:

主机主动轮询 CQ 而不是等待 MSI-X

这种情况下:

  • I/O 可以正常完成;
  • /proc/interrupts中断计数可能不增长;
  • 没有中断并不代表异常。

Linux 也可能配置部分 polling queues。


6.2 中断被 Mask

可能存在多层 Mask:

  1. MSI-X Table Entry 的 Vector Mask;
  2. MSI-X Function Mask;
  3. NVMeINTMS寄存器;
  4. CQ 创建时IEN = 0;
  5. Linux IRQ 被禁用;
  6. 设备处于复位或电源管理状态。

这些 Mask 并不等价,需要逐层确认。


6.3 中断聚合

NVMe 支持 Interrupt Coalescing。

控制器可能不会在每个 CQE 写入后都立刻中断,而是满足以下条件之一后再中断:

  • 累计完成数达到阈值;
  • 等待时间达到定时器值;
  • 控制器内部触发强制刷新。

好处:

  • 减少中断次数;
  • 降低 CPU 消耗;
  • 提高高 IOPS 场景的吞吐。

代价:

  • 增加单个 I/O 的完成延迟;
  • 参数不合理时表现为“中断很少”或“延迟突然升高”。

NVMe 中通常有:

  • Interrupt Coalescing Feature;
  • Interrupt Vector Configuration Feature。

工具支持时可以查看类似:

nvme get-feature /dev/nvme0 -f 0x08

不同工具版本和控制器支持情况可能不同。


7. 常见应用场景

7.1 一队列一个 MSI-X 向量

CQ1 -> Vector 1 -> CPU 1 CQ2 -> Vector 2 -> CPU 2 CQ3 -> Vector 3 -> CPU 3

适合:

  • 高并发随机 I/O;
  • 多核系统;
  • 高端 PCIe SSD;
  • 每 CPU 或每 NUMA 节点绑定一个队列。

优点:

  • 低共享;
  • 中断处理并行;
  • 便于 CPU affinity;
  • 适合高 IOPS。

缺点:

  • 占用更多向量;
  • 每个队列都可能产生中断;
  • 中断频率过高时 CPU 消耗增加。

7.2 多个 CQ 共享一个 MSI-X 向量

CQ1 ─┐ CQ5 ─┴──> Vector 1 CQ2 ─┐ CQ6 ─┴──> Vector 2

适合:

  • MSI-X 向量数量不足;
  • 虚拟机或 SR-IOV VF;
  • 低端控制器;
  • 系统限制了最大 IRQ 数量。

中断处理函数通常需要扫描该 vector 关联的多个 CQ。

缺点:

  • 共享 vector 的队列相互影响;
  • 扫描队列增加处理开销;
  • 可能导致某些队列延迟增加。

7.3 降级到单 MSI

所有 CQ -> MSI Vector 0

常见原因:

  • MSI-X 分配失败;
  • 平台不支持 MSI-X;
  • 虚拟化环境限制;
  • 驱动参数或内核策略;
  • 控制器 MSI-X Capability 描述错误。

这种模式下 I/O 仍然可能正常,但:

  • 所有队列共享一个 IRQ;
  • 中断处理集中到一个 CPU;
  • 高 IOPS 场景吞吐和延迟可能明显变差。

7.4 轮询模式

应用或驱动主动读取 CQ

适合:

  • 极低延迟场景;
  • 用户态 NVMe;
  • SPDK;
  • 高速存储测试;
  • CPU 资源充足的环境。

优点:

  • 避免中断切换;
  • 延迟更稳定;
  • 高 IOPS 下性能好。

缺点:

  • 消耗 CPU;
  • 空闲时效率低;
  • 通用块设备场景不一定适合。

7.5 虚拟化和 SR-IOV

在虚拟化环境中,MSI-X 可能经过:

Guest MSI-X ↓ 虚拟机监控器 ↓ 物理 MSI-X / Posted Interrupt / vIOMMU ↓ 物理 CPU

可能遇到:

  • VF 的 MSI-X 向量数较少;
  • Guest 中看到的向量数与 PF 不同;
  • 中断经过虚拟化转发;
  • 虚拟机迁移或 reset 后向量重新分配;
  • IOMMU/Interrupt Remapping 配置导致消息地址不同。

8. Linux 下的检查方法

8.1 查看 MSI/MSI-X 是否启用

lspci -vv -s 0000:xx:yy.z

重点观察类似信息:

MSI: Enable+ Count=1/1 MSI-X: Enable+ Count=64 Masked-

含义:

  • Enable+:该模式当前启用;
  • Count=64:设备最多提供或报告 64 个 MSI-X 表项;
  • 不代表操作系统实际创建了 64 个 I/O 队列;
  • Masked-:通常表示没有被整体 Mask,但仍不能替代对 NVMe 和驱动状态的检查。

注意:

MSI-X Capability 中的 Count 通常是最大能力,不一定等于实际分配的 IRQ 数量。


8.2 查看实际中断计数

grep -i nvme /proc/interrupts

可能看到:

120: 10023 0 0 0 IR-PCI-MSI nvme0q0 121: 0 9821 0 0 IR-PCI-MSI nvme0q1 122: 0 0 9934 0 IR-PCI-MSI nvme0q2

可以观察:

  • 是否存在多个nvme0qX;
  • 每个 IRQ 是否都在增长;
  • 是否只有一个 IRQ 增长;
  • 是否只有 CPU0 处理;
  • IRQ 是否极度集中。

注意:

  • 没有 I/O 时计数不增长是正常的;
  • polling 模式下计数不增长也可能正常;
  • 中断计数增长不代表 CQE 一定被正确处理。

8.3 查看 PCI 设备的 MSI IRQ 列表

BDF=0000:xx:yy.z ls /sys/bus/pci/devices/$BDF/msi_irqs/ cat /sys/bus/pci/devices/$BDF/irq

不同内核版本中 sysfs 内容可能略有不同。


8.4 查看 IRQ CPU 亲和性

IRQ=120 cat /proc/irq/$IRQ/smp_affinity_list cat /proc/irq/$IRQ/effective_affinity_list

还可以查看设备 NUMA 节点:

cat /sys/bus/pci/devices/$BDF/numa_node

重点确认:

  • IRQ 是否运行在正确 CPU;
  • CPU 是否和 SSD 位于同一 NUMA 节点;
  • 是否被 irqbalance 自动迁移;
  • 是否所有 IRQ 集中在一个 CPU;
  • 是否某些 IRQ 被绑定到了不合适的 NUMA 节点。

8.5 查看内核日志

dmesg -T | grep -iE \ 'nvme|msi|msix|aer|iommu|dmar|amd-vi|timeout|reset'

重点关注:

  • I/O timeout;
  • reset controller;
  • failed to allocate IRQ;
  • MSI-X分配失败;
  • AER错误;
  • IOMMU DMA fault;
  • PCIe link down;
  • Completion Queue overflow;
  • controller fatal status。

8.6 查看 PCIe 链路和 AER 状态

lspci -vv -s 0000:xx:yy.z

关注:

  • LnkSta;
  • Link Speed;
  • Link Width;
  • Correctable Error;
  • Non-Fatal Error;
  • Fatal Error;
  • Unsupported Request;
  • Completion Timeout;
  • Receiver Error。

MSI-X 消息本身也是 PCIe 事务。如果 PCIe 链路、Root Port、IOMMU 或 Interrupt Remapping 有问题,设备可能已经完成命令,但中断消息没有正常抵达 CPU。


9. 常见故障现象和排查思路

9.1 现象一:I/O 超时,/proc/interrupts不增长

优先检查

  1. 是否真的产生了 I/O;
  2. 是否启用了 polling;
  3. lspci -vv中 MSI-X 是否为Enable+;
  4. 实际是否降级到了 MSI 或 INTx;
  5. NVMe 控制器是否处于CSTS.RDY=1;
  6. NVMeINTMS是否屏蔽;
  7. MSI-X Table Entry 是否被 Mask;
  8. CQ 是否创建时IEN=1;
  9. CQ 的 IV 是否指向正确向量;
  10. 设备是否真的写入了 CQE;
  11. 是否存在 IOMMU、AER、PCIe 链路错误。

常见原因

  • MSI-X 没有启用;
  • MSI-X Table 被错误配置;
  • CQ 使用了错误的 Interrupt Vector;
  • 控制器处于复位状态;
  • IRQ 被禁用;
  • IOMMU 拒绝了 MSI 或 DMA;
  • SSD 没有产生 CQE;
  • 设备写入 CQE 的 DMA 地址错误;
  • 使用 polling,误以为应该有中断。

9.2 现象二:IRQ 计数增长,但 I/O 仍然超时

这种情况通常说明中断消息已经到达 CPU,但 NVMe 完成处理链路有问题。

重点检查:

  1. 中断处理函数是否找到正确的 CQ;
  2. CQE 是否真的写入;
  3. CQE Phase Bit 是否正确;
  4. CID 是否能匹配到原始请求;
  5. CQ Head 是否正确推进;
  6. CQ Doorbell 是否正确写回;
  7. DMA 内存是否可见;
  8. CPU Cache 是否同步;
  9. 内核驱动是否出现锁竞争或异常退出。

常见原因:

  • CQE DMA 写入错误地址;
  • Phase Bit 处理错误;
  • CQ Head 没有更新;
  • CQE 中 CID 错误;
  • 设备生成了错误的 MSI-X vector;
  • ISR 进入了错误的队列处理路径;
  • 共享 vector 扫描逻辑存在问题。

9.3 现象三:中断风暴

表现为:

/proc/interrupts 中某个 nvme IRQ 快速增长 CPU 使用率升高 I/O 性能下降

常见原因:

  • ISR 没有消费 CQE;
  • CQ Head Doorbell 没有更新;
  • Phase Bit 判断错误;
  • 设备不断认为 CQ 中存在未处理 CQE;
  • MSI-X Pending Bit 没有正确清除;
  • 共享 vector 处理函数没有扫描到真正产生事件的 CQ;
  • 中断聚合参数不合理;
  • AER 或设备错误导致重复中断;
  • 控制器持续报告异步事件。

排查重点:

是否存在未消费 CQE? CQ Head 是否推进? CQ Tail/Phase 是否一致? ISR 退出前是否还有 pending CQE? 是否是共享 vector?

9.4 现象四:只有一个 CPU 有中断,其他 CPU 没有

可能原因:

  • 只分配了一个 MSI/MSI-X 向量;
  • 所有 CQ 共享一个 vector;
  • irqbalance 将 IRQ 集中到了一个 CPU;
  • IRQ affinity 没有正确配置;
  • NVMe 驱动根据 CPU 数量只创建了少量队列;
  • 虚拟化环境限制了向量数量;
  • 控制器 NUMA 拓扑和 CPU 绑定不合理。

可检查:

grep -i nvme /proc/interrupts cat /proc/irq/<IRQ>/effective_affinity_list

性能优化方向:

  • 增加 I/O Queue 数量;
  • 使用 MSI-X;
  • 让队列数与 CPU 或工作线程数匹配;
  • 设置合理 IRQ affinity;
  • 让 IRQ 与内存、SSD 位于同一 NUMA 节点;
  • 避免所有队列共享同一个 vector。

9.5 现象五:性能低、CPU 中断占用高

常见原因:

  • 降级到了单 MSI;
  • 多个 CQ 共享一个向量;
  • I/O 粒度很小;
  • 没有启用中断聚合;
  • 中断亲和性配置不合理;
  • 中断集中在一个 CPU;
  • 设备队列数少;
  • 使用了不合适的 NUMA 绑定;
  • 中断与软中断、块层处理产生竞争。

可以从以下角度优化:

  1. 确认 MSI-X 是否启用;
  2. 确认实际分配的 vector 数量;
  3. 检查 CQ 与 vector 的映射;
  4. 调整 CPU affinity;
  5. 检查 NUMA;
  6. 调整 NVMe Interrupt Coalescing;
  7. 对极低延迟业务评估 polling;
  8. 对高 IOPS 业务增加队列并避免过度共享。

9.6 现象六:系统重置或热复位后中断失效

常见原因:

  • FLR 后 MSI-X Table 内容被设备清除;
  • 驱动没有重新写入 MSI-X Table;
  • IRQ 已经重新分配,但设备仍使用旧的地址/数据;
  • CQ 和 SQ 已经被重新创建,但 IV 映射未更新;
  • 控制器尚未重新解除 Mask;
  • reset 流程中存在旧请求和新队列并发;
  • PCIe Link Recovery 后设备状态与驱动状态不一致。

正确的 reset 流程通常需要:

停止新 I/O 等待或取消旧请求 Mask 中断 停止控制器 重新初始化 MSI/MSI-X 重新创建 Admin Queue 重新创建 I/O CQ/SQ 重新配置 vector 映射 解除 Mask 恢复 I/O

10. MSI-X 硬件或 FPGA SSD 的调试重点

如果是自研 SSD 控制器、FPGA NVMe Endpoint 或定制 PCIe 设备,MSI-X 问题通常集中在以下位置。

10.1 MSI-X Capability 描述错误

检查:

  • Capability 链表是否正确;
  • Table Size 是否正确;
  • Table BIR 是否指向正确 BAR;
  • Table Offset 是否正确;
  • PBA BIR 是否正确;
  • PBA Offset 是否正确;
  • Table 是否按规范对齐;
  • Table Entry 是否为正确大小;
  • BAR 空间是否足够。

MSI-X Table Entry 通常为 16 字节。


10.2 没有使用主机写入的地址和数据

错误做法:

设备内部硬编码一个 MSI 地址 设备内部硬编码一个中断数据

正确做法:

主机通过 MSI-X Table 写入地址和数据 设备读取或使用对应 Table Entry 设备按照该 Entry 生成 MSI-X Message

特别是在以下环境中,硬编码几乎一定会失败:

  • IOMMU 开启;
  • Interrupt Remapping 开启;
  • ARM GIC ITS;
  • 虚拟机;
  • SR-IOV;
  • 不同 BIOS 或不同内核;
  • 多 Socket 系统。

10.3 CQE 和 MSI-X 消息的顺序错误

设备必须保证:

先让 CQE 对主机可见 再发送 MSI-X Message

错误顺序可能是:

先发送 MSI-X 后完成 CQE DMA 写入

此时 CPU 进入 ISR 后可能读不到有效 CQE,表现为:

  • 中断计数增长;
  • 驱动找不到完成项;
  • I/O 长时间 pending;
  • 后续可能产生中断风暴。

需要关注:

  • PCIe TLP 顺序;
  • 设备内部 DMA 写入完成标志;
  • 内存屏障;
  • DMA 一致性;
  • 非一致性 ARM 平台上的 Cache Sync;
  • CQE 写入和 MSI 写入之间的排序保证。

10.4 Vector Index 和 CQ 配置不一致

例如:

主机创建 CQ3,指定 IV=3 设备实际向 Table Entry 2 发送中断

可能导致:

  • 中断进入错误的 ISR;
  • ISR 找不到待处理 CQ;
  • 某个 CQ 一直不处理;
  • 另一个 CQ 产生大量“伪中断”。

测试时建议:

  1. 先只启用一个 CQ;
  2. 只使用 vector 0 或 vector 1;
  3. 确认单队列路径;
  4. 再逐步增加 CQ 和 MSI-X vector;
  5. 最后测试共享 vector。

10.5 Mask 和 Pending 处理错误

需要同时检查:

  • MSI-X Function Mask;
  • MSI-X Vector Control Mask;
  • PBA Pending Bit;
  • NVMe INTMS;
  • CQ 的 IEN;
  • 控制器内部中断 pending 状态;
  • Reset 后是否清理旧状态。

11. 建议的系统化排查流程

可以按以下顺序排查。

第一步:确认是否真的需要中断

是否使用 polling? 是否有实际 I/O? 是否只是空闲状态?

第二步:确认 PCIe 中断模式

lspci -vv -s <BDF>

确认:

MSI-X: Enable+

如果是:

MSI-X: Enable- MSI: Enable+

说明实际使用的是 MSI,而不是 MSI-X。


第三步:确认实际 IRQ

grep -i nvme /proc/interrupts ls /sys/bus/pci/devices/<BDF>/msi_irqs/

判断:

  • 是否只有一个 IRQ;
  • 是否有多个 nvme 队列 IRQ;
  • 是否有 IRQ 计数;
  • 是否 IRQ 分布在多个 CPU。

第四步:确认控制器和队列状态

关注:

  • Controller 是否 Ready;
  • Admin Queue 是否正常;
  • I/O CQ 是否创建成功;
  • CQ 的 IV 是否正确;
  • CQ 的 IEN 是否打开;
  • CQ 是否有有效 CQE;
  • Phase Bit 是否变化;
  • CQ Head Doorbell 是否推进。

第五步:确认所有 Mask 层

PCI MSI/MSI-X Enable MSI-X Function Mask MSI-X Vector Mask NVMe INTMS CQ IEN Linux IRQ enable 状态

第六步:确认 PCIe、IOMMU 和中断路由

检查:

dmesg -T | grep -iE 'aer|iommu|dmar|amd-vi|nvme|pci'

排除:

  • PCIe 链路异常;
  • Completion Timeout;
  • IOMMU DMA Fault;
  • Interrupt Remapping 异常;
  • Root Port 错误;
  • 虚拟化中断注入异常。

第七步:确认 CPU 和 NUMA

检查:

cat /proc/irq/<IRQ>/effective_affinity_list cat /sys/bus/pci/devices/<BDF>/numa_node

避免:

  • 所有中断集中在 CPU0;
  • IRQ 位于远端 NUMA 节点;
  • irqbalance 与手工 affinity 冲突;
  • I/O 线程、内存和中断位于不同 NUMA 节点。

12. 需要特别记住的几个结论

  1. MSI/MSI-X 是 PCIe 消息,不是传统中断线。
  2. MSI/MSI-X 只传递通知,不携带 NVMe CQE 内容。
  3. CQE 由 SSD DMA 写入主机内存。
  4. NVMe 的中断向量绑定在 Completion Queue 上。
  5. Submission Queue 不直接决定中断向量。
  6. MSI-X Count 是设备最大能力,不等于实际使用的 IRQ 数量。
  7. NVMe IV、MSI-X Table Index、Linux IRQ、CPU Vector 不是同一个编号。
  8. MSI-X Enable+ 不代表一定已经有中断产生。
  9. 中断不增长可能是 polling,也可能是 Mask、路由、设备或 DMA 故障。
  10. 中断增长但 I/O 不完成,问题可能在 CQE、Phase、CID、Doorbell 或 DMA,而不一定在 MSI-X。
  11. 高性能 NVMe 通常优先使用 MSI-X,多队列和 CPU affinity 是性能关键。
  12. 自研硬件不能硬编码 MSI 地址和数据,必须使用主机配置到 MSI-X Table 的内容。

整体上,可以把 NVMe MSI-X 理解为:

Completion Queue │ │ 通过 IV 指定 ▼ MSI-X Vector │ │ 通过 Table Entry 指定地址和数据 ▼ PCIe MSI-X Message │ ▼ Linux IRQ / CPU │ ▼ 驱动消费 CQE

而排查问题时,最有效的思路是把链路拆成四段:

SSD 是否写入 CQE? ↓ SSD 是否生成 MSI/MSI-X? ↓ PCIe/平台是否把 MSI 送到 CPU? ↓ 驱动是否正确消费 CQE?

这四段分别对应设备数据通路、设备中断生成、PCIe/平台中断路由、操作系统驱动处理。只要逐段确认,绝大多数“NVMe 中断不工作、IRQ 风暴、I/O 超时或 MSI-X 性能异常”都可以定位。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询