一、错误分类
image.png
如上图,pci传输过程中,以及pcie设备自身发现的错误,可以两大类
- 可恢复错误: 表示硬件会自动恢复的错误,无需软件参与
- 不可恢复错误,不可恢复错误分为2小类:
- Non-Fatal错误: 表示传输事务出错不可靠,但是物理层link还OK
- Fatal错误: 表示物理层link已经出问题
从错误出现在PCIe所属层级来分,可以分为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的设备,有两个地方会记录部分错误信息:
-
PCI兼容的配置空间中的status寄存器,由command寄存器控制
image.png
image.png
对于Bridge,像UP/DP/RP,还有Secondary Status/Bridge Control两个寄存器控制,与Status/Command寄存器类似,是记录和控制下层bus下设备相关错误
image.png
-
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, ®16);
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, ®32);
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32);
pci_read_config_dword(pdev, aer + PCI_ERR_COR_STATUS, ®32);
pci_write_config_dword(pdev, aer + PCI_ERR_COR_STATUS, reg32);
pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, ®32);
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, ®32);
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是从哪个设备发上来的
- 统计此设备上出现的各类错误的计数
- 对此错误进行处理













网友评论