添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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件事情:

  1. 为错误处理入口aer_irq,申请中断;request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
  2. 配置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, &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); aer_pos = pdev->aer_cap; /* Clear error status */ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32); 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, &reg32); 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.chinaaet.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...