PCIe常见问题定位 与错误类型

基于Linux系统的PCIe常见问题定位总结

建链问题

PCIe的拓扑和Linux的PCIe ID

PCIe的建链问题比较常见,由于各种厂商定位PCIe的方式方法不同,这里我们只介绍协议的方式 如何判断当前链路状态。在介绍建链前需要补充一下PCIe设备如何查看ID。Linux系统下通过以下命令,可以查到当前 PCIe 的拓扑图。

lspci –vt

下图是执行该命令后某个编译服务器的部分拓扑截图。关键信息是各个设备的ID即DBDF(Domain,Bus,Deivce,Function)号,识别到ID后才能针对某个设备做精确的查询和控制。

图1

图中有个IEP设备就是0000:00:00.0, 这里的几个域的含义是第一个0000是domain域基本上都是0,第二个00是Bus总线号,第三个00是Device 设备号,第四个0是Function 功能号。这个RCiEP(Root Complex Integrated Endpoint)设备是Intel CPU自带的内部的模块,我们不需要关注,我们需要关注的是RP(Root Port)和EP(Endpoint),这里是借这个设备说明一下如何查看PCIe 设备的DBDF号,图中红字也有说明。

这个图里面能看到有3 RP,即00:01.0, 00:02.0, 00:03.0。同时也能看到00:02.0下挂了一个EP,但这个EP有两个PF(Physical Function),一个是02:00.0 WX7100 一个是02:00.1 RX580。同时00:03.0 下挂了一个EP,这个EP只有一个PF 03:00.0 NVMe SSD

自此,该拓扑解析完成,目前看到有3个RP 2个EP,且有个EP有2个PF,还有1个RCiEP。

注:绝大部分场景下,设备都在一个domain域内,所以lspci工具使用时通常会省略domain域,即domain默认值为0。

查看当前PCIe建链状态

当我们已经知道拓扑后,我们就知道哪个设备挂载哪个RP下了。假设我们有个设备挂载上图的0:01.0 这个RP上,但是没有看到EP设备怎么办。这时需要确认RP的建链状态

使用命令查看当前RP设备的配置空间解析,用于查看当前RP状态

sudo lspci -vvs 0:1.0

执行结果如下图所示

图2

其中红框中的DLActive- 就代表该Port并未建链。

无法建链问题

如果出现无法建链的情况需要优先排查下列三种情况

  1. 卡是否有插入对应插槽,是否插槽插错了或者没有插好
  2. EP卡是否有问题,在其他设备上是否能正常使用
  3. RP对应的槽位是否能正常,别的正常卡能否插在该槽位正常工作

如果这三点都排查了,都工作正常,那有可能就是从RP到EP的链路质量不好。需要硬件先测一下眼图,看看是否真的信号质量太差。如果确实信号质量差,那就要按照协议调整PCIe的一些硬件参数。如果是自研芯片或者能看到寄存器信息的芯片,则可以通过读取芯片PCIe的状态机状态来判断是在那个阶段就出现问题了。还可以排查一些PCIe的配置,比如时钟来源等等。

建链的问题较多,这里只是给了一些思路,具体还要根据实际的场景实际分析。

注:可能有些BIOS/UBOOT在启动阶段如果不能建链, BIOS/UBOOT就会关闭对应RP用于节能。如果实在调试EP设备,需要注意启动时间与BIOS对应。

带宽速率不符合预期

如图3 所示DLActive状态正常,红框中的代表当前建链的状态即与对端协商到的带宽和速率是多少。蓝框中的表示当前我这个设备支持的单宽和速率是多少。

图3

图3是EP的配置空间,如果这里红框中的speed和蓝框中speed需要对应的话,那这会儿的建链是有问题的,需要确认为什么speed明明支持到8G/s(GEN3)但是当前只建链到2.5Gb/s(GEN1)。这里需要先确认RP的配置空间是否支持到8G/s(GEN3),如果RP只支持则建链到2.5Gb/s(GEN1)是符合协议的。

图4

从图4中能看到RP是支持8G/s(GEN3),这里就需要确认为什么链路只建链到2.5Gb/s(GEN1)。当然这里的原因是因为显卡根据负载会自动切换速率,当前我们没用显卡,所以显卡驱动时为了省电故意将速率切换到2.5Gb/s(GEN1)。但如果我们对接的卡并不支持动态切换速率的话,就需要检查为什么不能建链到更高速度。一般的原因还是因为信号质量不好,尝试切速的时候误码过多,无法recovery 成功,所以速率无法上去。

当然还有些是width 与预期不匹配,一般原因仍然是跟物理通路有关,可能某条通路不通或者信号质量太差,但是其他部分通路是通的,则有可能建链到与预期不匹配的带宽上。

枚举问题

枚举问题一般比较少。一般我理解的枚举问题就是,上面那个例子中,RP的DLActive+ 但是仍然看不到对接的EP设备,可以尝试用以下命令重新枚举设备。

echo 1 > /sys/bus/pci/rescan

注意老的Linux版本如果当前设备有其他PCI设备在正常运行,可能会受影响。如果不放心可以指定某个RP进行rescan如下命令

echo 1 > /sys/bus/pci/devices/0000\:00\:01.0/rescan

执行命令后如果能看到设备,那么很有可能就是EP启动较慢,导致在HOST BIOS建链枚举阶段并未真正枚举到。在OS启动阶段也就看不到对应设备了。但在HOST启动OS后又建链了,可能才会出现这种现象。

当然如果上面的命令做了以后还是无法看到设备,可能需要怀疑EP设备是否能正常工作了,因为这个枚举动作实际上也是逻辑在参与,如果枚举不到大概率还是EP芯片逻辑出问题了。

AER问题

AER (Advanced Error Reporting) 是PCIe设备的高级功能,PCIe设备如果出现问题可以通过这个机制上报给系统,然后系统来处理异常。

AER里面主要分为三种类型的故障,分别是:CE(Correctable Error), UE(Uncorrectable Error),UE中根据Severity寄存器又分为NFE(Non-Fatal Error), FE(Fatal Error)。一般来说CE 不会上报OS,PCIe设备自己处理,UE会上报OS。如果HOST为x86可能也不会管NEF,因为NFE的默认处理是断链,然后重新建链,但X86不只是热插拔,所以NEF要么不处理,要么不上报。FE的处理一般都是复位系统。

这里笔者列出较常见的故障有哪些,并说明一下出错原因。没有列出的,笔者暂时没有遇到过,还不太清楚问题出现原因,就不单独描述了。如果对这个AER有兴趣可以看PCIe协议。

Correctable Errors

  • Receiver Error:可能是协商过程中出现误码,只要不影响功能可以不用关注
  • Bad TLP Status:TLP 出现过错包,由于PCIe协议有重传机制,只要不一直出现,不会有大影响但如果持续出现会影响PCIe的通信效率。
  • Bad DLLP Status:同上,只是改成了DLLP出现过错包。
  • REPLAY_NUM Rollover Status:即错包过多,内部计数已经翻转,这个错误不常见,如果出现需要考虑物理通路上是否有太多误码而影响PCIe通信。

Uncorrectable Errors

  • Data Link Protocol Error Status:链路异常,笔者从未遇到过,暂时不知产生原因,但这个是fatal,如果真的产生可能需要通过PCIe协议分析仪看看为什么会有这种故障。
  • Surprise Down Error Status: 即链路突然断链,fatal故障,x86默认重启。AARCH64 可以支持热插拔的场景下,可以通过修改Slot Capabilities Register 中的Hot-Plug Surprise 位,热插拔的时候可以不再上报这个故障。该故障的原因也有很多,比如真正的热插拔,或者插槽松动引起的Linkdown,误码过多引起的Linkdown,还有软件配置了Link Control Register中的Link Disable导致的Linkdown等等都有可能。
  • Poisoned TLP Received Status:TLP中毒可以理解为一个错误的TLP报文,根据TLP报文的类型会导致的异常是未知的,一般建议是芯片内部不处理该报文并丢弃,但这个设计整个芯片处理逻辑。产生原因目前仍然可能是因为链路原因或RAM的位跳变引起,该异常不太常见,该异常出现后可能伴随着其他故障出现如UC 或CPL Timeout等。
  • Completion Timeout Status:即发送的请求对端没有及时相应导致超时。该现象比较常见,较大可能是因为CPL 超时时间配置不合理,导致对端的请求还没处理完成就超时了,还有可能是对端逻辑出现故障了无法返回导致超时。
  • Unsupported Request Error Status& Completer Abort Status:UR和CA,UR就是对端发了一个我处理不了的请求,CA就是这个请求被终止了,一般来说出现这种异常可能有其他异常出现,需要结合TLP Header来进一步分析异常原因。截图如下,记录的是第一次异常出现的TLP Header。

  • Unexpected Completion Status:即收到一个多余的completion 报文,对接收端没有多影响,但是需要排查发送端是否有其他异常引起多个completion报文。
  • Malformed TLP Status:即畸形TLP,TLP出现问题,需要优先排查RP和EP的MPS和MRRS是否匹配,如果都匹配,再怀疑对端设备异常,也可能是链路出现故障。需要看对端设备是否有其他信息可以查看,否则该异常默认是Fatal故障,会导致整系统复位。

参考资料

1、NCB-PCI_Express_Base_4.0r1.0_September-27-2017

  • 简介

AER 即 Advanced Error Reporting高级错误报告,是PCIe高级特性,用于报告PCIe 错误信息,是PCIe RAS特性最重要的部分,本文从PCIe AER协议、固件、linux内核实现讲述PCIe AER知识。

  • AER 协议介绍
  1. AER错误分类

分为可纠正错误和不可纠正错误 , Correctable errors and Uncorrectable errors

不可纠正错误分为ERR_FATAL和ERR_NONFATAL。

Correctable Errors可纠正错误是指错误发生后,硬件可以自动恢复。

Uncorrectable errors错误发生后,影响设备功能,硬件不能自动恢复。

ERR_FATAL错误是致命错误,此错误类型影响了PCIe link链路。

ERR_NONFATAL错误是指影响了设备功能,但是PCIe link还是稳定的。

2. AER寄存器

包含了AER 状态、掩码、级别等寄存器。

3.错误控制上报路径

这里最终有两条路径: MSI/INTx中断上报给OS AER驱动程序,还有一个系统错误的中断上报给固件firmware。

  • AER软件实现

PCIe AER驱动属于PCIe port driver, 其绑定的是PCIe root port。

drivers/pci/pcie/aer.c
static struct pcie_port_service_driver aerdriver = {
    .name       = "aer",
    .port_type  = PCIE_ANY_PORT,
    .service    = PCIE_PORT_SERVICE_AER,

    .probe      = aer_probe,
    .remove     = aer_remove,
};

/**
 * pcie_aer_init - register AER root service driver
 *
 * Invoked when AER root service driver is loaded.
 */
int __init pcie_aer_init(void)
{
    if (!pci_aer_available())
        return -ENXIO;
    return pcie_port_service_register(&aerdriver);
}

注:port_type = PCIE_ANY_PORT, AER绑定的不是ROOT PORT么,怎么是PCIE_ANY_PORT,这个澄清内核增加了PCIE RCEC支持,除了ROOT PORT针对RCEC设备也可以AER处理。

/**
 * aer_probe - initialize resources
 * @dev: pointer to the pcie_dev data structure
 *
 * Invoked when PCI Express bus loads AER service driver.
 */
static int aer_probe(struct pcie_device *dev)
{
    int status;
    struct aer_rpc *rpc;
    struct device *device = &dev->device;
    struct pci_dev *port = dev->port;

    /* Limit to Root Ports or Root Complex Event Collectors */
    if ((pci_pcie_type(port) != PCI_EXP_TYPE_RC_EC) &&
        (pci_pcie_type(port) != PCI_EXP_TYPE_ROOT_PORT))
        return -ENODEV;
...
}

PCIe AER错误上报流程。

EP设备发生AER错误,通过error msg上报到root port, root port上报中断给CPU处理。

PCIe Correctable Errors处理流程

1)、获取出错的设备和清状态

2)、读取设备详细错误信息。

Error Severity : Corrected
PCIE Bus Error type : Physical Layer
Receiver Error : Multiple
Receiver ID : 0020
VendorID=8086h, DeviceID=3597h, Bus=00h, Device=04h,
Function=00h

PCIe NON-FATAL Errors处理流程

1)、获取出错的设备和清状态

2)、读取设备详细错误信息。

3) 、错误恢复处理

Error Severity : Uncorrected (Non-Fatal)
PCIE Bus Error type : Transaction Layer
Completion Timeout : Multiple
Requester ID : 0018
VendorID=8086h, DeviceID=3596h, Bus=00h, Device=03h,
Function=00h

PCIe FATAL Errors处理流程

FATAL错误处理流程大体类似non-fatal,只是错误恢复的时候有差异,fatal错误影响pcie link链路,会做链路的恢复。

Error Severity : Uncorrected (Fatal)
PCIE Bus Error type : Transaction Layer
Unsupported Request : First
Requester ID : 0200
VendorID=8086h, DeviceID=0329h, Bus=02h, Device=00h,
Function=00h
TLB Header:
04000001 00180003 02040000 00020400

关键结构

设备支持AER错误恢复,需要设备驱动注册pci_error_handlers

struct pci_error_handlers
{
        int (*error_detected)(struct pci_dev *dev, pci_channel_state_t);
        int (*mmio_enabled)(struct pci_dev *dev);
        int (*slot_reset)(struct pci_dev *dev);
        void (*resume)(struct pci_dev *dev);
};
The possible channel states are:

typedef enum {
        pci_channel_io_normal,  /* I/O channel is in normal state */
        pci_channel_io_frozen,  /* I/O to channel is blocked */
        pci_channel_io_perm_failure, /* PCI card is dead */
} pci_channel_state_t;
Possible return values are:

enum pci_ers_result {
        PCI_ERS_RESULT_NONE,        /* no result/none/not supported in device driver */
        PCI_ERS_RESULT_CAN_RECOVER, /* Device driver can recover without slot reset */
        PCI_ERS_RESULT_NEED_RESET,  /* Device driver wants slot to be reset. */
        PCI_ERS_RESULT_DISCONNECT,  /* Device has completely failed, is unrecoverable */
        PCI_ERS_RESULT_RECOVERED,   /* Device driver is fully recovered and operational */
};

nvme驱动nvme_err_handler注册

drivers/nvme/host/pci.c

static const struct pci_error_handlers nvme_err_handler = {
    .error_detected = nvme_error_detected,
    .slot_reset = nvme_slot_reset,
    .resume     = nvme_error_resume,
    .reset_prepare  = nvme_reset_prepare,
    .reset_done = nvme_reset_done,
};
......

static struct pci_driver nvme_driver = {
    .name       = "nvme",
    .id_table   = nvme_id_table,
    .probe      = nvme_probe,
    .remove     = nvme_remove,
    .shutdown   = nvme_shutdown,
#ifdef CONFIG_PM_SLEEP
    .driver     = {  
        .pm = &nvme_dev_pm_ops,
    },   
#endif
    .sriov_configure = pci_sriov_configure_simple,
    .err_handler    = &nvme_err_handler,
};
  • AER和firmware关系

  1. AER错误可以先上报给firmware,叫做firmware-first机制。固件错误收集处理后再通过ACPI APEI接口上报给OS处理,用于服务器和PC场景。
  2. 前面介绍AER上报还有机制MSI/INTx中断上报。 需要通过ACPI _OSC方法控制是固件接管还是OS接管.
  • PCIe AER错误注入

PCIe AER硬件错误较难触发,如何调试AER软件处理的正确,提供了AER注错工具aer-inject

Linux AER Test Suite

  • 小结

本文主要介绍下PCIe AER知识,从协议、linux内核软件实现原理、和固件关系,错误注入工具介绍,能够对PCIe AER知识有个系统性认识。

  • 4
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值