AER及linux内核驱动简介:
https://blog.csdn.net/u010443710/article/details/104649179
下面是PCIe设备端错误记录和报告的详细流程图。
1. AER 中断
首先AER驱动作为错误上报和处理的机制,必须有一个错误上报的入口。
这个入口就是AER中断。linux的AER驱动只针对RC,也就是说软件需要处理RC的AER中断请求。
并在中断处理函数中通过AER 寄存器来判断错误类型并作出相应处理。
1.1 AER中断产生
在PCIe spec中定义了2种AER中断产生方式,类似于ep设备,可以选择legacy的INTx或者MSI/MSIx的方式来产生中断。
但对于RC而言,无论是INTx还是MSI/MSIx,都不需要像ep那样真的来触发INTx边带信号或发送MSI tlp来告知RC。
因为RC作为根节点,内部的中断就是报给自己,可以直接在chip内部处理,不需要在PCIe协议上走一圈。
这就涉及RC内部中断上报的机制,由于RC内部中断不仅限于AER中断,所以这部分单独开一篇进行阐述。
1.2 如何使能AER中断?
AER Capability -> Root Error Command Register (Offset 2Ch)
打开相应的报告使能bit位,当错误发生后,就会有中断产生。
2. AER驱动
AER驱动与PME、pciehp、pcie-dpc一样是作为pcie port的可选service。
service挂载在pcie port驱动上,由portdrv_core进行管理,service通过pcie_port_service_register进行注册。
具体来说,service的数据结构如下:
struct pcie_port_service_driver {
const char *name;
int (*probe) (struct pcie_device *dev);
void (*remove) (struct pcie_device *dev);
int (*suspend) (struct pcie_device *dev);
int (*resume) (struct pcie_device *dev);
/* Service Error Recovery Handler */
const struct pci_error_handlers *err_handler;
/* Link Reset Capability - AER service driver specific */
pci_ers_result_t (*reset_link) (struct pci_dev *dev);
int port_type; /* Type of the port this driver can handle */
u32 service; /* Port service this device represents */
struct device_driver driver;
以下是AER的service结构
static struct pcie_port_service_driver aerdriver = {
.name = "aer",
.port_type = PCI_EXP_TYPE_ROOT_PORT,
.service = PCIE_PORT_SERVICE_AER,
.probe = aer_probe,
.remove = aer_remove,
.err_handler = &aer_error_handlers,
.reset_link = aer_root_reset,
2.1 初始化(aer_probe)
AER初始化主要完成2件事情:
- 为错误处理入口aer_irq,申请中断;request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
- 配置AER功能相关的cap寄存器,打开AER能使,中断上报使能等;aer_enable_rootport(rpc);
* 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;
/* Alloc rpc data structure */
rpc = aer_alloc_rpc(dev);
if (!rpc) {
dev_printk(KERN_DEBUG, device, "alloc rpc failed\n");
aer_remove(dev);
return -ENOMEM;
/* Request IRQ ISR */
status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
if (status) {
dev_printk(KERN_DEBUG, device, "request IRQ failed\n");
aer_remove(dev);
return status;
rpc->isr = 1;
aer_enable_rootport(rpc);
return status;
这里的中断向量dev->irq,是pcie port驱动初始化时已经申请好了可能是lagecy的或者是MSI/MSIx,AER只需要再注册一个share中断上去。
看一下aer_enable_rootport,先清楚了所有device的状态位,再把上下游设备的错误上报全部使能。
* 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->port;
int aer_pos;
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);
aer_pos = pdev->aer_cap;
/* Clear error status */
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32);
pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
* Enable error reporting for the root port device and downstream port
* devices.
set_downstream_devices_error_reporting(pdev, true);
/* Enable Root Port's interrupt in response to error messages */
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32);
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32);
2.2 中断处理
AER中断分为上下半部,上半部aer_irq:
irqreturn_t aer_irq(int irq, void *context)
unsigned int status, id;
struct pcie_device *pdev = (struct pcie_device *)context;
struct aer_rpc *rpc = get_service_data(pdev);
int next_prod_idx;
unsigned long flags;
int pos;
pos = pdev->port->aer_cap;
* Must lock access to Root Error Status Reg, Root Error ID Reg,
* and Root error producer/consumer index
spin_lock_irqsave(&rpc->e_lock, flags);
/* Read error status */
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
spin_unlock_irqrestore(&rpc->e_lock, flags);
return IRQ_NONE;
/* Read error source and clear error status */
pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
/* Store error source for later DPC handler */
next_prod_idx = rpc->prod_idx + 1;
if (next_prod_idx == AER_ERROR_SOURCES_MAX)
next_prod_idx = 0;
if (next_prod_idx == rpc->cons_idx) {
* Error Storm Condition - possibly the same error occurred.
* Drop the error.
spin_unlock_irqrestore(&rpc->e_lock, flags);
return IRQ_HANDLED;
rpc->e_sources[rpc->prod_idx].status = status;
rpc->e_sources[rpc->prod_idx].id = id;
rpc->prod_idx = next_prod_idx;
spin_unlock_irqrestore(&rpc->e_lock, flags);
/* Invoke DPC handler */
schedule_work(&rpc->dpc_handler);
return IRQ_HANDLED;
irq中首先读取 Root Error Status Register,看看是不是有错误产生了。(PCI_ERR_ROOT_STATUS)
获取错误源ID,Error Source Identification Register,PCI_ERR_ROOT_ERR_SRC
并保存在rpc->e_sources数组里面。
中断处理下半部 :aer_isr,这是一个worker
* aer_isr - consume errors detected by root port
* @work: definition of this work item
* Invoked, as DPC, when root port records new detected error
void aer_isr(struct work_struct *work)
struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
struct pcie_device *p_device = rpc->rpd;
struct aer_err_source uninitialized_var(e_src);
mutex_lock(&rpc->rpc_mutex);
while (get_e_source(rpc, &e_src))
aer_isr_one_error(p_device, &e_src);
mutex_unlock(&rpc->rpc_mutex);
把刚才上半部保存在rpc->e_sources里面的错误源取出来,一个一个调用aer_isr_one_error进行处理
* aer_isr_one_error - consume an error detected by root port
* @p_device: pointer to error root port service device
* @e_src: pointer to an error source
static void aer_isr_one_error(struct pcie_device *p_device,
struct aer_err_source *e_src)
struct aer_rpc *rpc = get_service_data(p_device);
struct aer_err_info *e_info = &rpc->e_info;
* 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;
e_info->multi_error_valid = 0;
aer_print_port_info(p_device->port, e_info);
if (find_source_device(p_device->port, e_info))
aer_process_err_devices(p_device, 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;
e_info->severity = AER_NONFATAL;
if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV)
e_info->multi_error_valid = 1;
e_info->multi_error_valid = 0;
aer_print_port_info(p_device->port, e_info);
if (find_source_device(p_device->port, e_info))
aer_process_err_devices(p_device, e_info);
aer_isr_one_error就是处理错误的具体实现了,这里按照AER的不同类型进行错误报告或者恢复处理。
转载地址:http://blog.chinaaet.com/justlxy/p/5100057838
前面的文章提到过高级错误报告(Advanced Error Reporting,AER),接下来详细地介绍一下这一功能。在已有的PCIe错误报告机制上(之前文章介绍的),AER还支持以下特性:
· 在登记实际发生的错误类型时,有更好的粒度(Granularity,可以理解为区分...
转载地址:http://blog.china
aet.com/justlxy/p/5100057839
这一篇文章讲一讲,高级错误报告(Advanced Error Reporting,
AER)关于可校正和不可校正错误的相关寄存器,以及Root如何处理来自其他
PCIe设备的错误消息等内容。
Ø 高级可校正错误处理(Advanced Correctable Error Handling)
https://github.com/torvalds/linux/blob/master/Documentation/PCI/pcieaer-howto.txt
pcieaer-howto.txt里面有一句话,比较有意思。
有些系统的firmware支持AER功能,如果linux使能AER的同时,firmware也来处理AER,将可能导致无法预知的行为。因此,linux不会处理AER事件,除非f...
本文翻译自内核文档:
linux\Documentation\PCI\
pcieaer-howto.txt
《 PCI Express高级错误报告
驱动程序指南》 HOWTO
T.Long Nguyen <tom.l.nguyen@intel.com>
张艳敏<yanmin.zhang@intel.com>
2006年7月29日
1.1关于本指南
编写linux驱动先看一下驱动框架是什么样子的。
驱动编写和应用层编写有什么区别呢?
(一)首先 入口函数的问题。应用层编写我们的入口就是main函数,但在驱动编写时不是这样的,有两种情况,
1、缺省情况下
int __init init_module(void) 加载模块时的初始化函数,也就是驱动模块的入口函数
void __exit cleanup_module(void) 卸载模
想要换工作的时候,才想到把之前做的碎片化的性能分析进行系统化学习,这里参考的是陈志勇、马利伟、万龙著《全栈性能测试修炼宝典Jmeter实战》,感觉自己上了一个台阶。
1 评估标准
先看一下web项目性能测试通过的标准,有了评判标准,系统设计的是否优秀就有了依据。
2 瓶颈阈值分析
下图把什么方面使用什么样的命令进行了梳理。
2.1 CPU定位分析
2.2 内存定位分析
2.3 网络定位分析
2.4 IO定位分析
1. mpstat -P ALL 1 或 mpstat -I SUM -P ALL 1(有的机器得用这个命令),
2. sar -n DEV 1 显示 网络读写发生在eth1
3. cat /proc/interrupts | grep eth1 通过这个可以查看网卡队列数目,也可以查看哪个cpu占用的软中断
4.top命令查看
Cpu(s): 0...
一、模块背景
调试PCIe AER错误恢复代码非常困难,因为它很难触发真正的硬件错误。基于软件的错误注入可用于伪造各种PCIe错误。
首先,应该在内核中启用PCIe AER软件错误注入配置,即以下应位于.config中的项目。
CONFIG_PCIEAER_INJECT = y或CONFIG_PCIEAER_INJECT = m
使用新内核重新启动或insmod模块后,名为设备文件/ dev / aer_inject 会被创建。
然后,需要一个名为aer-inject的用户空间工具,
该工具可以从此获取:g
转载地址:http://blog.chinaaet.com/justlxy/p/5100057782
前面的文章提到过,PCI总线中定义两个边带信号(PERR#和SERR#)来处理总线错误。其中PERR#主要对应的是普通数据奇偶校检错误(Parity Error),而SERR#主要对应的是系统错误(System Error)。具体如下:
· 普通的数据奇偶校检错误——通过PER...