美文网首页
PCIe AER及错误处理原理

PCIe AER及错误处理原理

作者: itsenlin | 来源:发表于2024-12-09 15:59 被阅读0次

一、错误分类

image.png

如上图,pci传输过程中,以及pcie设备自身发现的错误,可以两大类

  1. 可恢复错误: 表示硬件会自动恢复的错误,无需软件参与
  2. 不可恢复错误,不可恢复错误分为2小类:
    • Non-Fatal错误: 表示传输事务出错不可靠,但是物理层link还OK
    • Fatal错误: 表示物理层link已经出问题

从错误出现在PCIe所属层级来分,可以分为4类:

  1. 物理层
  2. 数据链路层
  3. 传输层
  4. pcie设备内部错误

二、错误检测机制

Completion Status

传输过程中,如果接收端发现有问题,会在回复的TLP包中的completion status位置相应的错误值,如下图

image.png

cpl.status占了3bit,不同取值含义如下:

image.png

其中最主要的有2个:

  • cpl.status=0b001,表示UR
  • cpl.status=0b100,表示CA
    不在表中的取值当前都是保留状态

下图是对tlp的一种判断逻辑:


image.png

Error转发

在规范中,这种错误还被称为:data poisoning,还是很形象的。这种错误只针对发送TLP或者回复的TLP中携带有数据的情况。并且是中间接收节点(像switch的UP/DP口)检测到TLP包中携带的数据有问题时,会置TLP中“EP”位为1,表示此TLP包携带的数据有问题


image.png

Error Message

device发现错误,通过特殊的Message类型的TLP发送给RC进行处理,TLP如下图所示


image.png

其中type类型低3bit必须是0,表示发给RC,Message Code表示错误级别,取值如下图所示:


image.png

这里有几个需要注意的点:

  • 错误消息是需要按错误级别发送给RC进行统一处理
  • 错误消息来自PCIe设备、传统PCI设备、RC自己
  • 一个Request ID检测到多条同级别的错误消息,多条消息可能合并,同一级别的错- 误消息至少发送一条
  • 对于错误消息,RC根据Request ID来标识消息来源,并且将消息转换成平台级别的事件,像中断

三、错误列表

General PCIe error list

image.png

内部错误是与PCIe接口相关的错误,它发生在组件内,可能不是由于PCIe接口本身的数据包或事件,也不是由于在PCI上发起的事务表达。

  • 举例1:数据包缓冲区错误被ECC纠正,上报corrected Internal Errror
  • 举例2:数据包缓冲区错误无法被ECC纠正, 上报un corrected Internal Errror

Physical Layer Error List

image.png

规范中只有Receiver Error一种
在网上还看到,Training Error也是一种物理层错误,参见:这篇文章

Data Link Layer Error List

image.png

Transaction Layer Error List

image.png image.png image.png

四、错误记录

不支持AER

不支持AER的设备,有两个地方会记录部分错误信息:

  1. PCI兼容的配置空间中的status寄存器,由command寄存器控制


    image.png
image.png

对于Bridge,像UP/DP/RP,还有Secondary Status/Bridge Control两个寄存器控制,与Status/Command寄存器类似,是记录和控制下层bus下设备相关错误


image.png
  1. PCIe CAP下的Device Status寄存器,由device control寄存器控制


    image.png
image.png

支持AER

支持AER的设备,会将之前列的Error list记录在AER CAP的相应bit位


image.png
image.png

部分错误携带TLP header相关信息,存放在AER CAP的Hear Log/TLP Prefix Log寄存器

image.png image.png

Multiple Error处理

在AER CAP中有一个Advanced Error Capabilites and Control Register寄存器:

  • First Error Pointer指向最先发生的Uncorrectable错误所在bit位的值;Header Log寄存器记录对应错误的报头信息
  • 当第一个Uncorrectable错误被处理并清除之后, First Error Pointer/Header Log会指向下一个Uncorrectable错误的相关信息
  • 当所有Uncorrectable错误被处理并清除之后, First Error Pointer/Header Log指向无效值
  • 当错误报头记录数量超过实现所支持的最大数量时,会报Header Log Overflow错误
image.png

RC

RC除了记录上面描述的Device相关的AER信息以外,还需要记录接收到从下层或RC自己上报的AER信息
通过source寄存器标识错误来源,如下图


image.png

Root Error Status寄存器中的位表示收到了什么类型的错误,如下图


image.png

注意:Bit5/bit6只是为了区分收到的uncorrectable错误是Fatal的还是Non-Fatal的

RC收到AER信息之后,可以:

  • 配置触发一个中断(AER CAP中的Root Error Command Register来控制),软件来进行处理AER


    image.png
  • 或者产生一个System Error(PCIe CAP里的Root Control Register来控制)


    image.png

Advisory Non-Fatal Error

在某些情况下,非致命错误的检测器不是确定错误是否可恢复的最合适的代理,或者它甚至不需要任何恢复操作。主要由检测代理(请求者、完成者、接收者)的角色和特定错误确定。

基于此,提出了Advisory Non-Fatal Error场景,在此场景下收到的Non-Fatal错误换成Correctable错误(如果使能了AER),或者不上报错误消息(没有使能AER)

  • Completer Sending a Completion with UR/CA Status
  • Intermediate Receiver
  • Ultimate PCI Express Receiver of a Poisoned TLP
  • Requester with Completion Timeout
  • Receiver of an Unexpected Completion

五、设备错误记录以及上报的流程

image.png

六、错误消息上报控制

image.png

七、linux内核对AER的处理

RC的AER功能初始化

/**
 * aer_enable_rootport - enable Root Port's interrupts when receiving messages
 * @rpc: pointer to a Root Port data structure
 *
 * Invoked when PCIe bus loads AER service driver.
 */
static void aer_enable_rootport(struct aer_rpc *rpc)
{
    struct pci_dev *pdev = rpc->rpd;
    int aer = pdev->aer_cap;
    u16 reg16;
    u32 reg32;

    /* Clear PCIe Capability's Device Status */
    pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, &reg16);
    pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16);

    /* Disable system error generation in response to error messages */
    pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
                   SYSTEM_ERROR_INTR_ON_MESG_MASK);

    /* Clear error status */
    pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, &reg32);
    pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32);
    pci_read_config_dword(pdev, aer + PCI_ERR_COR_STATUS, &reg32);
    pci_write_config_dword(pdev, aer + PCI_ERR_COR_STATUS, reg32);
    pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, &reg32);
    pci_write_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, reg32);

    aer_enable_irq(pdev);
}

static void aer_enable_irq(struct pci_dev *pdev)
{
    int aer = pdev->aer_cap;
    u32 reg32;

    /* Enable Root Port's interrupt in response to error messages */
    pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, &reg32);
    reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
    pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
}

设备的AER初始化

void pci_aer_init(struct pci_dev *dev)
{
    int n;

    dev->aer_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
    if (!dev->aer_cap)
        return;

    dev->aer_stats = kzalloc(sizeof(struct aer_stats), GFP_KERNEL);

    /*
     * We save/restore PCI_ERR_UNCOR_MASK, PCI_ERR_UNCOR_SEVER,
     * PCI_ERR_COR_MASK, and PCI_ERR_CAP.  Root and Root Complex Event
     * Collectors also implement PCI_ERR_ROOT_COMMAND (PCIe r5.0, sec
     * 7.8.4).
     */
    n = pcie_cap_has_rtctl(dev) ? 5 : 4;
    pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n);

    pci_aer_clear_status(dev);

    if (pci_aer_available())
        pci_enable_pcie_error_reporting(dev);

    pcie_set_ecrc_checking(dev);
}
  • pci_aer_clear_status(dev);对应的定义如下
int pci_aer_clear_status(struct pci_dev *dev)
{
    if (!pcie_aer_is_native(dev))
        return -EIO;

    return pci_aer_raw_clear_status(dev);
}

/**
 * pci_aer_raw_clear_status - Clear AER error registers.
 * @dev: the PCI device
 *
 * Clearing AER error status registers unconditionally, regardless of
 * whether they're owned by firmware or the OS.
 *
 * Returns 0 on success, or negative on failure.
 */
int pci_aer_raw_clear_status(struct pci_dev *dev)
{
    int aer = dev->aer_cap;
    u32 status;
    int port_type;

    if (!aer)
        return -EIO;

    port_type = pci_pcie_type(dev);
    if (port_type == PCI_EXP_TYPE_ROOT_PORT ||
        port_type == PCI_EXP_TYPE_RC_EC) {
        pci_read_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, &status);
        pci_write_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, status);
    }

    pci_read_config_dword(dev, aer + PCI_ERR_COR_STATUS, &status);
    pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, status);

    pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &status);
    pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, status);

    return 0;
}
  • pci_enable_pcie_error_reporting(dev);对应的定义如下
static int pci_enable_pcie_error_reporting(struct pci_dev *dev)
{
    int rc;

    if (!pcie_aer_is_native(dev))
        return -EIO;

    rc = pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
    return pcibios_err_to_errno(rc);
}
  • 对于Bridge来说,还需要配置Bridge Control寄存器的SEER,这一位是控制是否转发从下面设备接收到的错误消息到上层
static void pci_configure_serr(struct pci_dev *dev)
{
    u16 control;

    if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {

        /*
         * A bridge will not forward ERR_ messages coming from an
         * endpoint unless SERR# forwarding is enabled.
         */
        pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &control);
        if (!(control & PCI_BRIDGE_CTL_SERR)) {
            control |= PCI_BRIDGE_CTL_SERR;
            pci_write_config_word(dev, PCI_BRIDGE_CONTROL, control);
        }
    }
}

设备AER初始化在设备枚举中的调用层次如下图


image.png

RC对AER的处理

  • 接收到AER中断之后,会进入AER的中断处理函数,读取status/source等信息
/**
 * aer_irq - Root Port's ISR
 * @irq: IRQ assigned to Root Port
 * @context: pointer to Root Port data structure
 *
 * Invoked when Root Port detects AER messages.
 */
static irqreturn_t aer_irq(int irq, void *context)
{
    struct pcie_device *pdev = (struct pcie_device *)context;
    struct aer_rpc *rpc = get_service_data(pdev);
    struct pci_dev *rp = rpc->rpd;
    int aer = rp->aer_cap;
    struct aer_err_source e_src = {};

    pci_read_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, &e_src.status);
    if (!(e_src.status & AER_ERR_STATUS_MASK))
        return IRQ_NONE;

    pci_read_config_dword(rp, aer + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
    pci_write_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, e_src.status);

    if (!kfifo_put(&rpc->aer_fifo, e_src))
        return IRQ_HANDLED;

    return IRQ_WAKE_THREAD;
}
  • 中断处理函数会将AER相关信息通过FIFO队列传送给后端处理线程isr,isr出队一个一个处理AER
static irqreturn_t aer_isr(int irq, void *context)
{
    struct pcie_device *dev = (struct pcie_device *)context;
    struct aer_rpc *rpc = get_service_data(dev);
    struct aer_err_source e_src;

    if (kfifo_is_empty(&rpc->aer_fifo))
        return IRQ_NONE;

    while (kfifo_get(&rpc->aer_fifo, &e_src))
        aer_isr_one_error(rpc, &e_src);
    return IRQ_HANDLED;
}
  • 处理AER的主要逻辑在aer_isr_one_error(rpc, &e_src)中,定义如下:
/**
 * aer_isr_one_error - consume an error detected by root port
 * @rpc: pointer to the root port which holds an error
 * @e_src: pointer to an error source
 */
static void aer_isr_one_error(struct aer_rpc *rpc,
        struct aer_err_source *e_src)
{
    struct pci_dev *pdev = rpc->rpd;
    struct aer_err_info e_info;

    pci_rootport_aer_stats_incr(pdev, e_src);

    /*
     * There is a possibility that both correctable error and
     * uncorrectable error being logged. Report correctable error first.
     */
    if (e_src->status & PCI_ERR_ROOT_COR_RCV) {
        e_info.id = ERR_COR_ID(e_src->id);
        e_info.severity = AER_CORRECTABLE;

        if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV)
            e_info.multi_error_valid = 1;
        else
            e_info.multi_error_valid = 0;
        aer_print_port_info(pdev, &e_info);

        if (find_source_device(pdev, &e_info))
            aer_process_err_devices(&e_info);
    }

    if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) {
        e_info.id = ERR_UNCOR_ID(e_src->id);

        if (e_src->status & PCI_ERR_ROOT_FATAL_RCV)
            e_info.severity = AER_FATAL;
        else
            e_info.severity = AER_NONFATAL;

        if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
            e_info.multi_error_valid = 1;
        else
            e_info.multi_error_valid = 0;

        aer_print_port_info(pdev, &e_info);

        if (find_source_device(pdev, &e_info))
            aer_process_err_devices(&e_info);
    }
}

主要分三块内容:

  • 统计RC收到各类型错误的计数
  • 处理此AER中的可恢复中断
  • 处理此AER中的不可恢复中断

而后两块内容处理逻辑类似,都包含三方面:

  • 找到AER来源,即此AER是从哪个设备发上来的
  • 统计此设备上出现的各类错误的计数
  • 对此错误进行处理

相关文章

网友评论

      本文标题:PCIe AER及错误处理原理

      本文链接:https://www.haomeiwen.com/subject/ldinsjtx.html