3. 传统 INTx 中断机制:INTx 引脚定义、中断路由、中断共享与 IRQ 分配

在 PCIe 体系结构中,传统 INTx 中断机制是兼容 PCI 时代的遗留中断方案。尽管 MSI/MSI-X 已成为主流,但大量 Legacy 设备、桥接芯片以及某些嵌入式平台仍依赖 INTx 完成中断传递。理解 INTx 的引脚定义、路由拓扑、共享模型及 IRQ 分配策略,是掌握 PCIe 中断处理全貌的基石。

3.1 INTx 引脚定义与信号映射

每个 PCIe 功能(Function)最多可支持 4 个独立的中断引脚:INTA#、INTB#、INTC#、INTD#。这些引脚为电平触发(Level-Triggered),低电平有效。单功能设备通常只使用 INTA#;多功能设备可为每个功能分配不同的 INTx 引脚,以减少共享冲突。

引脚名称 有效电平 触发方式 典型用途
INTA# 低电平 电平触发 单功能设备默认中断引脚
INTB# 低电平 电平触发 多功能设备 Function 1
INTC# 低电平 电平触发 多功能设备 Function 2
INTD# 低电平 电平触发 多功能设备 Function 3
关键区别:INTx 是电平触发,与 MSI/MSI-X 的边沿触发有本质不同。电平触发要求中断服务程序(ISR)必须清除设备侧的中断源,否则将导致中断风暴。

3.2 INTx 中断路由:从设备到 CPU

INTx 中断路由遵循 PCIe 层次拓扑,通过 中断控制器(Interrupt Controller) 逐级向上传递。路由路径涉及三个关键组件:

  1. Endpoint 设备:通过 INTx 引脚向上游发送中断请求。
  2. Switch / Root Port:将 INTx 信号转换为内部消息,通过 INTx 中断消息(INTx Interrupt Message) 在 PCIe 事务层传递。
  3. Root Complex:接收 INTx 消息,映射到 系统中断控制器(如 APIC / GIC),最终路由到目标 CPU 核心。
路由本质:INTx 在物理引脚上表现为电平信号,但在 PCIe 链路层被封装为 Assert_INTx / Deassert_INTx 消息包,通过虚拟通道传输。Root Complex 负责将消息转换为系统中断线。
INTx 中断路由拓扑图 INTx 中断路由拓扑图 Endpoint 1 INTA# Endpoint 2 INTB# PCIe Switch INTx 消息封装 Root Complex 中断映射 CPU / APIC 说明: INTx 在 PCIe 链路层以 Assert/Deassert 消息包传输,Root Complex 将其映射到系统中断控制器。 每个 Endpoint 的 INTx 引脚在拓扑中可能被多个设备共享。

3.3 中断共享机制

INTx 中断共享是传统 PCI/PCIe 体系的重要特征。由于系统中断线资源有限(通常 16 条 ISA IRQ 或 24 条 PCI IRQ),多个设备必须共享同一条中断线。共享机制依赖以下条件:

  • 电平触发特性:电平触发允许多个设备同时拉低中断线,中断控制器检测到电平有效后触发一次中断。
  • 中断服务链(Interrupt Service Chain):内核遍历注册在该 IRQ 上的所有中断处理程序,逐一调用直到某个处理程序声明“已处理”。
  • 设备必须支持中断状态寄存器:ISR 通过读取设备的中断状态寄存器判断是否为自己产生的中断,若不是则立即返回 IRQ_NONE。
共享中断的陷阱:如果某个设备的中断处理程序未正确清除中断源,或驱动程序存在 bug 导致死循环,将阻塞同 IRQ 上的所有其他设备,造成整个中断线“挂死”。调试时可通过 /proc/interrupts 观察各设备的中断计数是否增长。

3.4 IRQ 分配策略

系统固件(BIOS/UEFI)和操作系统共同完成 INTx 的 IRQ 分配。分配过程遵循以下步骤:

  1. 设备枚举:PCIe 枚举阶段,固件为每个功能分配 INTx 引脚(通过配置空间的 Interrupt Pin 寄存器)。
  2. 中断线路由:固件根据 中断路由表(Interrupt Routing Table) 将 INTx 引脚映射到系统中断控制器的一个 IRQ 编号。
  3. 操作系统注册:内核读取设备的 Interrupt Line 寄存器(配置空间偏移 0x3C),获取分配的 IRQ 号,并调用 request_irq() 注册中断处理程序。
寄存器 偏移 位宽 描述
Interrupt Pin 0x3D 8 bit 指示设备使用的 INTx 引脚(1=INTA#, 2=INTB#, 3=INTC#, 4=INTD#)
Interrupt Line 0x3C 8 bit 固件分配的 IRQ 编号,操作系统读取后用于注册中断
实践要点:在 Linux 内核中,驱动程序通常不直接修改 Interrupt Line 寄存器,而是通过 pci_alloc_irq_vectors() 或 pci_request_irq() 接口获取 IRQ。对于 INTx,内核会自动读取配置空间并建立共享中断链。
INTx IRQ 分配流程 INTx IRQ 分配流程 1. 设备枚举 读取 Interrupt Pin 2. 路由映射 中断路由表 3. 写入 IRQ Interrupt Line 寄存器 4. 注册 request_irq() 分配细节: • 固件通过 ACPI 或 MP 表获取中断路由表,将 INTx 引脚映射到 PIC/APIC 的 IRQ 输入。 • 操作系统在 PCI 子系统初始化时读取 Interrupt Line,并建立共享中断链(irq_desc->action)。 • 驱动程序调用 request_irq(irq, handler, IRQF_SHARED, ...) 加入共享链。

3.5 中断共享的 Linux 内核实现

在 Linux 内核中,INTx 共享中断通过 irq_desc 结构体的 action 链表实现。每个注册的中断处理程序以 irqaction 节点挂入链表。中断触发时,内核依次调用链表中的处理程序:

/* 内核中断处理核心流程(简化) */
unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
    struct irq_desc *desc = irq_to_desc(irq);
    struct irqaction *action;
    irqreturn_t ret;

    /* 遍历共享中断链 */
    for (action = desc->action; action; action = action->next) {
        ret = action->handler(irq, action->dev_id);
        if (ret == IRQ_HANDLED)
            break;  /* 已处理,退出循环 */
    }

    /* 若无人处理,记录 spurious interrupt */
    if (!action)
        note_interrupt(irq, desc, IRQ_NONE);
}
驱动编写建议:注册 INTx 中断时务必使用 IRQF_SHARED 标志,并在中断处理程序开头检查设备中断状态寄存器。若确认不是本设备中断,立即返回 IRQ_NONE,避免误处理影响其他设备。

3.6 INTx 与 MSI/MSI-X 的对比

特性 INTx MSI / MSI-X
触发方式 电平触发(低电平有效) 边沿触发(写内存消息)
中断线共享 必须支持共享 不共享,每个向量独立
中断数量 最多 4 个(INTA# ~ INTD#) MSI: 最多 32 个;MSI-X: 最多 2048 个
路由方式 通过中断控制器路由 直接写内存,无需中断控制器
性能 较低(共享链遍历、电平消抖) 高(直接投递、无共享开销)
兼容性 所有 PCIe 设备必须支持 可选功能,需操作系统支持
总结:INTx 是 PCIe 中断机制的“底线兼容方案”。在调试 Legacy 设备或分析中断风暴问题时,深入理解 INTx 的引脚定义、路由拓扑、共享链模型及 IRQ 分配流程,是快速定位问题的关键。对于新设计,应优先使用 MSI-X 以获得更好的性能和可扩展性。