错误处理概述

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寄存器以获得详细的错误信息
  • 实现分层的错误恢复策略
  • 定期进行错误注入测试验证系统鲁棒性
  • 监控错误趋势,进行预测性维护

参考资源