PCIe 错误处理流程
AER与错误恢复机制详解
错误处理概述
PCIe提供了完善的错误检测、报告和恢复机制,确保数据传输的可靠性。AER(Advanced Error Reporting,高级错误报告)是PCIe错误处理的核心功能,提供了比基础错误报告更详细的错误信息。
为什么需要AER?
- 精确定位:识别错误发生的具体位置和类型
- 错误分类:区分可纠正错误和不可纠正错误
- 错误追溯:记录错误发生的次数和时间
- 系统恢复:支持软件介入进行错误恢复
错误类型分类
PCIe将错误分为两大类:可纠正错误(Correctable Errors)和不可纠正错误(Uncorrectable Errors)。
可纠正错误
可纠正错误是硬件可以自动恢复的错误,不需要软件干预。这类错误通常由链路噪声引起,通过重传机制解决。
| 错误类型 | 描述 | 处理方式 |
|---|---|---|
| Receiver Error | 接收端检测到物理层错误(8b/10b或128b/130b解码错误) | 物理层自动重传 |
| Bad TLP | TLP校验失败(LCRC错误或序列号错误) | 数据链路层重传 |
| Bad DLLP | DLLP CRC校验失败 | 丢弃并等待重传 |
| Replay Timeout | 重传缓冲区超时未收到ACK | 自动重传 |
| Replay Num Rollover | 重传次数超过阈值 | 链路重训练 |
| Advisory Non-Fatal | 非致命错误提示 | 记录日志,继续运行 |
不可纠正错误
不可纠正错误是硬件无法自动恢复的错误,需要软件介入处理。这类错误又分为非致命(Non-Fatal)和致命(Fatal)两种。
非致命错误
| 错误类型 | 描述 | 影响 |
|---|---|---|
| Poisoned TLP | 数据被标记为损坏(EP位设置) | 当前事务失败 |
| Unsupported Request | 接收到不支持的请求类型 | 返回UR完成状态 |
| Completer Abort | 完成方异常终止事务 | 返回CA完成状态 |
| Completion Timeout | 非Posted请求超时未收到完成包 | 事务失败 |
| Unexpected Completion | 收到未请求的完成包 | 丢弃完成包 |
| ACS Violation | 违反ACS访问控制规则 | 访问被拒绝 |
致命错误
| 错误类型 | 描述 | 影响 |
|---|---|---|
| Data Link Protocol Error | 数据链路层协议错误(序列号异常) | 链路需要复位 |
| Surprise Down | 链路意外断开 | 设备离线 |
| Malformed TLP | TLP格式错误(长度错误、保留位设置等) | 链路不稳定 |
| Flow Control Protocol Error | 流控制协议错误 | 数据传输中断 |
致命错误处理
致命错误通常表示链路或设备出现严重问题,需要:
- 禁用受影响的链路
- 进行链路重训练
- 可能需要系统重启
AER(高级错误报告)机制
AER能力结构
AER通过扩展配置空间提供详细的错误报告功能,主要包括以下寄存器组:
可纠正错误寄存器组
| 寄存器 | 偏移 | 功能 |
|---|---|---|
| Correctable Error Status | 0x10 | 可纠正错误状态(每位对应一种错误) |
| Correctable Error Mask | 0x14 | 可纠正错误屏蔽(1=屏蔽) |
不可纠正错误寄存器组
| 寄存器 | 偏移 | 功能 |
|---|---|---|
| Uncorrectable Error Status | 0x18 | 不可纠正错误状态 |
| Uncorrectable Error Mask | 0x1C | 不可纠正错误屏蔽 |
| Uncorrectable Error Severity | 0x20 | 错误严重级别(0=非致命, 1=致命) |
错误日志寄存器组
| 寄存器 | 偏移 | 功能 |
|---|---|---|
| Header Log | 0x24-0x34 | 记录错误TLP的Header(16字节) |
| Root Error Command | 0x38 | 控制错误报告方式(中断/日志) |
| Root Error Status | 0x3C | Root Complex错误状态 |
| Error Source ID | 0x40 | 报告错误的设备ID |
错误报告流程
1
错误检测
设备检测到错误
2
状态记录
更新Error Status寄存器
3
Header记录
记录错误TLP Header
4
中断通知
发送ERR_MSG或MSI中断
5
软件处理
驱动读取寄存器并处理
错误恢复流程
可纠正错误恢复
可纠正错误由硬件自动恢复,软件只需要记录和监控。
// 可纠正错误处理伪代码
void handle_correctable_error(struct pci_dev *pdev) {
u32 status = read_aer_status(pdev);
// 记录错误统计
if (status & RECEIVER_ERROR)
pdev->err_stats.receiver_err++;
if (status & BAD_TLP)
pdev->err_stats.bad_tlp++;
// 清除错误状态
write_aer_status(pdev, status);
// 如果错误频率过高,记录警告
if (pdev->err_stats.receiver_err > THRESHOLD) {
log_warning("High error rate on %s", pdev->name);
}
}
非致命错误恢复
非致命错误需要软件介入,但通常不需要复位链路。
// 非致命错误处理伪代码
void handle_nonfatal_error(struct pci_dev *pdev) {
u32 status = read_aer_uncorr_status(pdev);
if (status & COMPLETION_TIMEOUT) {
// 完成超时,需要重试事务
retry_transaction(pdev);
}
if (status & UNSUPPORTED_REQUEST) {
// 不支持的请求,记录并通知上层
log_error("Unsupported request from %s", pdev->name);
notify_upper_layer(pdev);
}
if (status & POISONED_TLP) {
// 数据损坏,需要重新获取数据
handle_poisoned_data(pdev);
}
// 清除错误状态
write_aer_uncorr_status(pdev, status);
}
致命错误恢复
致命错误需要链路复位,可能导致数据丢失。
// 致命错误处理伪代码
void handle_fatal_error(struct pci_dev *pdev) {
u32 status = read_aer_uncorr_status(pdev);
if (status & (SURPRISE_DOWN | DATA_LINK_PROTOCOL)) {
// 禁用设备
pci_disable_device(pdev);
// 尝试链路重训练
if (pci_link_retrain(pdev) == 0) {
// 重训练成功,重新初始化
pci_reinit_device(pdev);
} else {
// 重训练失败,标记设备离线
pdev->state = DEVICE_OFFLINE;
notify_system_admin(pdev);
}
}
// 清除错误状态
write_aer_uncorr_status(pdev, status);
}
Linux AER驱动
Linux内核提供了AER驱动(aerdrv),自动处理PCIe错误。
Linux AER相关命令
# 查看AER错误计数
$ lspci -vvv -s 00:01.0 | grep -A 20 "Advanced Error Reporting"
# 查看系统日志中的PCIe错误
$ dmesg | grep -i "pci.*error"
# 启用/禁用AER
$ echo 1 > /sys/bus/pci/devices/0000:00:01.0/aer/enable
# 查看AER统计
$ cat /sys/bus/pci/devices/0000:00:01.0/aer/count
错误注入测试
错误注入是验证系统错误处理能力的重要手段。PCIe提供了多种错误注入方法。
软件错误注入
通过写AER寄存器模拟错误。
// 注入可纠正错误
void inject_correctable_error(struct pci_dev *pdev, u32 error_type) {
// 设置错误注入寄存器
pci_write_config_dword(pdev, AER_INJECT_CORR, error_type);
// 触发错误
pci_write_config_dword(pdev, AER_INJECT_CONTROL, INJECT_ENABLE);
}
// 注入不可纠正错误
void inject_uncorrectable_error(struct pci_dev *pdev, u32 error_type) {
pci_write_config_dword(pdev, AER_INJECT_UNCORR, error_type);
pci_write_config_dword(pdev, AER_INJECT_CONTROL, INJECT_ENABLE);
}
硬件错误注入工具
PCIe Jammer
专业PCIe协议分析仪,支持错误注入功能
FPGA测试平台
使用FPGA实现自定义错误注入逻辑
Linux aer-inject
内核提供的AER错误注入工具
测试用例设计
| 测试项 | 注入错误类型 | 预期结果 |
|---|---|---|
| 可纠正错误恢复 | Receiver Error, Bad TLP | 硬件自动恢复,错误计数增加 |
| 完成超时处理 | Completion Timeout | 驱动重试事务 |
| 数据损坏处理 | Poisoned TLP | 上层应用收到错误通知 |
| 链路恢复 | Surprise Down | 链路重训练成功 |
| 错误屏蔽 | 设置Error Mask | 被屏蔽的错误不产生中断 |
错误处理最佳实践
监控与告警
- 定期检查AER错误计数器
- 设置错误率阈值告警
- 记录错误趋势用于预测性维护
错误屏蔽策略
- 屏蔽已知的良性错误(如热插拔期间的瞬态错误)
- 不要屏蔽致命错误
- 生产环境谨慎使用错误屏蔽
恢复策略
- 实现分层恢复(事务级→链路级→设备级)
- 设置恢复重试次数上限
- 记录恢复失败事件
测试验证
- 定期进行错误注入测试
- 验证错误恢复时间满足SLA
- 测试极端错误场景
总结
PCIe错误处理机制是系统可靠性的重要保障。通过AER提供的详细错误信息,结合合理的错误分类和恢复策略,可以构建高可用性的PCIe系统。
关键要点
- 理解可纠正错误和不可纠正错误的区别
- 正确配置AER寄存器以获得详细的错误信息
- 实现分层的错误恢复策略
- 定期进行错误注入测试验证系统鲁棒性
- 监控错误趋势,进行预测性维护