













Per-Vector Masking,逐向量屏蔽,用于单独屏蔽某一个 MSI 或 MSI-X 中断向量,而不影响设备的其他中断向量。
例如,一个 PCIe 网卡申请了 4 个中断向量:
如果只屏蔽 Vector 1:
这比直接关闭整个设备的 MSI/MSI-X 更精细。
MSI-X 天然支持逐向量屏蔽。每一个 MSI-X Table Entry 都有独立的 Vector Control 字段。
一个 MSI-X Table Entry 通常为 16 字节:
| 偏移 | 字段 | 作用 |
|---|---|---|
0x00 |
Message Address Low | MSI 写入地址低 32 位 |
0x04 |
Message Address High | MSI 写入地址高 32 位 |
0x08 |
Message Data | MSI 中断数据 |
0x0C |
Vector Control | 向量控制字段 |
Vector Control 的 bit 0 是:
bit 0: Mask Bit
含义:
0 = 该 MSI-X Vector 未屏蔽
1 = 该 MSI-X Vector 被屏蔽
其他位为保留位。
例如:
Vector Control = 0x00000001
表示这个 MSI-X 向量已经被屏蔽。
MSI-X Capability 中还有一个 Function Mask。
| 屏蔽方式 | 作用范围 |
|---|---|
| Per-Vector Mask | 只屏蔽一个 MSI-X 向量 |
| Function Mask | 屏蔽该 PCI Function 的所有 MSI-X 向量 |
| MSI-X Enable 清零 | 完全禁用 MSI-X 功能 |
有效屏蔽条件可以理解为:
Effective Mask =
Function Mask
OR
当前 Vector 的 Mask Bit
只要任意一个为 1,该 Vector 就不能发送 MSI-X 中断。
例如:
Function Mask = 0
Vector 0 Mask = 0 -> Vector 0 可以触发中断
Vector 1 Mask = 1 -> Vector 1 被屏蔽
Vector 2 Mask = 0 -> Vector 2 可以触发中断
如果:
Function Mask = 1
则无论各 Vector 的 Mask Bit 是多少,所有 MSI-X Vector 都被屏蔽。
MSI-X 中断被屏蔽后,设备不能向 Host 发出对应的 MSI-X Memory Write。
如果屏蔽期间出现了对应的中断事件,设备通常需要设置 MSI-X Pending Bit Array,也就是 PBA 中对应的 bit。
Vector N 被屏蔽
|
v
设备产生 Vector N 对应事件
|
v
不发送 MSI-X Message
|
v
设置 PBA[N] = 1
之后软件解除屏蔽:
Vector N Mask Bit: 1 -> 0
如果此时:
PBA[N] = 1
设备应当发送该 Vector 的 MSI-X Message,并清除或更新对应的 Pending 状态。
需要注意,PBA 记录的是“存在待处理中断”,不是事件计数器。屏蔽期间同一个 Vector 发生多次事件,PBA 通常仍然只是一个 bit:
发生 1 次事件:PBA[N] = 1
发生 10 次事件:PBA[N] 仍然是 1
实际发生了多少个事件,需要设备内部队列、状态寄存器或描述符环保存。
普通 MSI 也可以支持 Per-Vector Masking,但它是可选功能。
PCI MSI Capability 中的 Message Control 寄存器包含:
Per-Vector Masking Capable
如果这个 bit 为 1,MSI Capability 结构中会额外存在:
每个 MSI Vector 对应一个 Mask Bit:
Mask Bits[N] = 1 -> MSI Vector N 被屏蔽
Mask Bits[N] = 0 -> MSI Vector N 未屏蔽
Pending Bits 表示该 Vector 屏蔽期间是否发生了中断请求。
| 特性 | MSI | MSI-X |
|---|---|---|
| Per-Vector Masking | 可选 | 必须支持 |
| Mask 存放位置 | MSI Capability 配置空间 | MSI-X Table |
| Pending 状态 | Pending Bits 寄存器 | PBA |
| 最大 Vector 数量 | 通常最多 32 个 | 最多 2048 个 |
| 每个 Vector 独立地址和数据 | 通常不独立 | 独立 |
Linux 驱动通常不需要直接操作 MSI-X Table 的 Mask Bit,而是通过 IRQ 子系统操作:
disable_irq(irq);
作用:
如果不能等待当前 Handler 完成,可以使用:
disable_irq_nosync(irq);
重新启用:
enable_irq(irq);
disable_irq() 的作用void disable_irq(unsigned int irq);
主要作用:
disable_irq_nosync() 的作用void disable_irq_nosync(unsigned int irq);
主要作用:
查看设备使用的中断:
lspci -s <BDF> -vv
例如:
lspci -s 0000:01:00.0 -vv
可能看到:
Capabilities: [b0] MSI-X: Enable+ Count=16 Masked-
Vector table: BAR=4 offset=00000000
PBA: BAR=4 offset=00002000
字段含义:
| 字段 | 含义 |
|---|---|
Enable+ |
MSI-X 已启用 |
Count=16 |
设备支持 16 个 MSI-X Vector |
Masked- |
Function Mask 没有置位 |
Vector table |
MSI-X Table 所在 BAR 和偏移 |
PBA |
Pending Bit Array 所在 BAR 和偏移 |
这里的:
Masked+
通常表示 MSI-X Capability 中的 Function Mask 被置位,不代表某一个 Table Entry 的 Per-Vector Mask Bit。
lspci 默认不会逐项打印每个 MSI-X Table Entry 的 Mask Bit。
查看设备实际使用的 IRQ:
grep -i '<设备驱动或者设备名称>' /proc/interrupts
例如:
grep -i nvme /proc/interrupts
如果底层软件需要直接修改 MSI-X Table,通常采用以下流程:
1. 找到目标 MSI-X Table Entry
2. 设置 Vector Control.Mask = 1
3. 执行必要的 MMIO 写屏障或读回
4. 修改 Message Address 或 Message Data
5. 执行必要的 MMIO 写屏障或读回
6. 清除 Vector Control.Mask
7. 如果 PBA 对应 bit 为 1,设备发送 pending interrupt
核心目的是避免在修改 MSI-X Entry 的过程中,设备使用“部分更新”的地址或数据发送中断。
伪代码:
u32 ctrl;
/* 屏蔽该 Vector */
ctrl = readl(entry + MSI_X_VECTOR_CTRL);
ctrl |= BIT(0);
writel(ctrl, entry + MSI_X_VECTOR_CTRL);
/* 确保 Mask 写入已经到达设备 */
readl(entry + MSI_X_VECTOR_CTRL);
/* 更新 MSI-X Message */
writel(msg_addr_lo, entry + MSI_X_ADDR_LO);
writel(msg_addr_hi, entry + MSI_X_ADDR_HI);
writel(msg_data, entry + MSI_X_DATA);
/* 确保配置写入已经完成 */
readl(entry + MSI_X_DATA);
/* 解除屏蔽 */
ctrl &= ~BIT(0);
writel(ctrl, entry + MSI_X_VECTOR_CTRL);
实际 Linux PCI 驱动不建议直接这样操作,因为 MSI-X Table 一般由 PCI/MSI 子系统管理。驱动优先使用:
disable_irq(irq);
enable_irq(irq);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。