3. AER错误处理概述:AER Capability结构、错误分类与严重性
高级错误报告(Advanced Error Reporting, AER)是PCIe规范中定义的一套标准化错误报告与处理机制,它扩展了传统PCI的INTx/MSI中断错误上报方式,为驱动开发者提供了更精细的错误分类、更丰富的错误状态寄存器以及可配置的错误严重性等级。在将传统MSI中断驱动移植到支持AER的PCIe驱动时,理解AER Capability结构、错误分类及严重性映射是正确配置错误处理流程的前提。
3.1 AER Capability结构
每个支持AER的PCIe功能(Function)在其配置空间中包含一个独立的AER Capability结构,该结构通过Capability ID(0x0001)和Next Capability Pointer串联在PCIe能力链表中。AER Capability结构由一组标准寄存器组成,用于控制错误报告行为、记录错误状态以及提供错误源标识。
3.1.1 AER Capability寄存器布局
| 偏移地址 | 寄存器名称 | 宽度 | 功能描述 |
|---|---|---|---|
| 0x00 | PCIe Capability ID + Next Cap Pointer | 16位 + 8位 | 标识AER Capability,指向下一个Capability |
| 0x04 | Uncorrectable Error Status Register (UESR) | 32位 | 记录已发生的不可纠正错误类型(写1清零) |
| 0x08 | Uncorrectable Error Mask Register (UEMR) | 32位 | 屏蔽特定不可纠正错误的报告(1=屏蔽) |
| 0x0C | Uncorrectable Error Severity Register (UESR) | 32位 | 定义不可纠正错误的严重性(0=非致命,1=致命) |
| 0x10 | Correctable Error Status Register (CESR) | 32位 | 记录已发生的可纠正错误类型(写1清零) |
| 0x14 | Correctable Error Mask Register (CEMR) | 32位 | 屏蔽特定可纠正错误的报告(1=屏蔽) |
| 0x18 | Advanced Error Capabilities and Control Register (AECCR) | 32位 | 控制AER功能,如First Error Pointer、ECRC Generation/Check等 |
| 0x1C | Header Log Register (HLR) | 128位 | 记录导致错误的TLP头部信息(用于诊断) |
| 0x2C | Root Error Command Register (RECR) | 32位 | 仅Root Complex使用,控制错误转发行为 |
| 0x30 | Root Error Status Register (RESR) | 32位 | 仅Root Complex使用,汇总下游设备错误状态 |
| 0x38 | Error Source Identification Register (ESIR) | 32位 | 标识产生错误的Requester ID(Bus/Device/Function) |
| 0x3C | TLP Prefix Log Register (TPL) | 128位 | 记录错误TLP的前缀信息(可选) |
pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) 获取。驱动在初始化阶段需要读取UESR/CESR清零历史错误状态,并配置UEMR/CEMR以决定哪些错误需要上报。
3.1.2 AER Capability结构SVG图
3.2 错误分类:可纠正错误与不可纠正错误
PCIe AER将错误分为两大类:可纠正错误(Correctable Errors)和不可纠正错误(Uncorrectable Errors)。这种分类决定了驱动在收到错误通知后应采取的处理策略——是仅记录日志并继续运行,还是需要触发复位或热复位流程。
3.2.1 可纠正错误(Correctable Errors)
可纠正错误是指硬件能够通过重传、ECC纠错等机制自动恢复的错误,不会导致数据损坏或链路中断。驱动通常只需记录错误计数,无需采取恢复动作。
| 错误类型 | 错误码(Bit位置) | 描述 | 典型触发场景 |
|---|---|---|---|
| Receiver Error | Bit 0 | 接收端检测到物理层错误(如8b/10b解码错误) | 信号完整性差、链路噪声 |
| Bad TLP | Bit 6 | 接收到的TLP格式错误或CRC校验失败 | 链路瞬态错误、发送端逻辑错误 |
| Bad DLLP | Bit 7 | 接收到的DLLP格式错误或CRC校验失败 | 链路层协议错误 |
| REPLAY_NUM Rollover | Bit 8 | 重试计数器溢出,表明链路持续错误 | 链路严重不稳定 |
| Replay Timer Timeout | Bit 12 | 重传定时器超时,TLP未得到确认 | 链路拥塞或对端无响应 |
| Advisory Non-Fatal | Bit 13 | 指示一个非致命不可纠正错误已被纠正 | ECC纠正了单比特错误 |
| Corrected Internal Error | Bit 14 | 设备内部错误已被内部机制纠正 | 内部RAM ECC纠正 |
| Header Log Overflow | Bit 15 | Header Log寄存器溢出,新错误覆盖旧记录 | 连续多个错误未及时读取 |
3.2.2 不可纠正错误(Uncorrectable Errors)
不可纠正错误是指硬件无法自动恢复的错误,可能导致数据损坏或链路功能异常。根据严重性,不可纠正错误又分为非致命(Non-Fatal)和致命(Fatal)两类。
| 错误类型 | 错误码(Bit位置) | 默认严重性 | 描述 |
|---|---|---|---|
| Undefined | Bit 0 | 致命 | 未定义的内部错误 |
| Data Link Protocol Error | Bit 4 | 致命 | 数据链路层协议序列错误 |
| Surprise Down Error | Bit 5 | 致命 | 设备意外断开(热插拔异常) |
| Poisoned TLP | Bit 12 | 非致命 | TLP被标记为中毒(数据损坏) |
| Flow Control Protocol Error | Bit 13 | 致命 | 流控制协议错误 |
| Completion Timeout | Bit 14 | 非致命 | 请求者等待Completion超时 |
| Completer Abort | Bit 15 | 非致命 | 完成者主动中止请求 |
| Unexpected Completion | Bit 16 | 非致命 | 收到与未完成请求不匹配的Completion |
| Receiver Overflow | Bit 17 | 致命 | 接收缓冲区溢出 |
| Malformed TLP | Bit 18 | 致命 | TLP格式违反PCIe规范 |
| ECRC Error | Bit 19 | 非致命 | 端到端CRC校验失败 |
| Unsupported Request | Bit 20 | 非致命 | 请求类型不被设备支持 |
| ACS Violation | Bit 21 | 非致命 | 访问控制服务(ACS)权限违规 |
| Uncorrectable Internal Error | Bit 22 | 致命 | 设备内部不可纠正错误 |
| MC Blocked TLP | Bit 23 | 非致命 | 多播TLP被阻塞 |
| AtomicOp Egress Blocked | Bit 24 | 非致命 | 原子操作被出口阻塞 |
| TLP Prefix Blocked | Bit 25 | 非致命 | TLP前缀被阻塞 |
3.3 错误严重性:非致命与致命
不可纠正错误的严重性决定了驱动和系统软件在检测到错误后的响应策略。AER规范定义了两种严重性等级,并通过UESR寄存器中的对应位来配置。
3.3.1 非致命错误(Non-Fatal Error)
- 定义: 错误影响了当前事务,但设备功能仍然可用,链路保持正常。
- 驱动行为: 记录错误日志,尝试恢复受影响的事务(如重发请求),继续提供服务。
- 系统影响: 通常不会触发系统复位,仅上报到OS错误处理框架(如Linux的AER驱动)。
- 典型例子: Completion Timeout、Poisoned TLP、Unsupported Request。
3.3.2 致命错误(Fatal Error)
- 定义: 错误导致设备功能不可用或链路完整性受损,需要复位恢复。
- 驱动行为: 立即停止设备I/O操作,触发链路复位或功能级复位(FLR),重新初始化设备。
- 系统影响: 可能导致整个PCIe域的重置,影响同一总线上的其他设备。
- 典型例子: Malformed TLP、Receiver Overflow、Surprise Down Error。
3.3.3 错误严重性处理流程SVG图
3.4 AER错误处理与MSI中断的协同
在PCIe驱动中,AER错误处理并非独立的中断源,而是通过MSI/MSI-X中断向量来通知驱动。驱动在初始化时需要完成以下配置步骤:
- 分配MSI/MSI-X中断向量: 调用
pci_alloc_irq_vectors()分配至少一个中断向量用于AER错误通知。 - 注册AER回调: 通过
pci_enable_pcie_error_reporting()启用设备AER功能,并注册struct pci_error_handlers结构体中的回调函数(如error_detected、slot_reset、resume)。 - 配置错误掩码: 根据驱动需求设置UEMR和CEMR,屏蔽不需要上报的错误类型。
- 清零历史状态: 写入UESR和CESR清零遗留错误状态位。
static const struct pci_error_handlers mydev_err_handler = {
.error_detected = mydev_error_detected,
.slot_reset = mydev_slot_reset,
.resume = mydev_resume,
};
static int mydev_probe(struct pci_dev *pdev, ...)
{
// 1. 启用PCIe AER
pci_enable_pcie_error_reporting(pdev);
// 2. 分配MSI中断向量
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
// 3. 注册错误处理回调
pdev->error_handler = &mydev_err_handler;
// 4. 清零AER状态寄存器
pci_write_config_dword(pdev, pdev->aer_cap + PCI_ERR_UNCOR_STATUS, 0xFFFFFFFF);
pci_write_config_dword(pdev, pdev->aer_cap + PCI_ERR_COR_STATUS, 0xFFFFFFFF);
}
3.5 本章小结
| 知识点 | 核心内容 | 驱动移植关注点 |
|---|---|---|
| AER Capability结构 | 包含UESR/UEMR/UESR、CESR/CEMR、HLR等寄存器 | 通过 pci_find_ext_capability() 获取基地址 |
| 可纠正错误 | 硬件自动恢复,驱动只需记录计数 | 配置CEMR屏蔽不关心的错误,避免中断风暴 |
| 不可纠正错误 | 分为非致命和致命,通过UESR配置 | 非致命:记录+恢复;致命:复位+重新初始化 |
| 严重性可编程 | 驱动可修改UESR改变错误严重性 | 谨慎修改,避免系统不稳定 |
| 与MSI协同 | AER通过MSI中断通知驱动 | 先分配MSI向量,再注册AER回调 |