pcie aer howto
https://github.com/torvalds/linux/blob/master/Documentation/PCI/pcieaer-howto.txt
pcieaer-howto.txt里面有一句话,比较有意思。
有些系统的firmware支持AER功能,如果linux使能AER的同时,firmware也来处理AER,将可能导致无法预知的行为。因此,linux不会处理AER事件,除非firmware确保有OS来控制AER,firmware和OS交接AER控制权是通过APCI的_OSC的方式实现的。
这里存在一个蒙代尔不可能三角,简单、通用、详细这三角最多只能选择两个。
(1)FFM(firmware first model):这种通过SMI中断到BIOS,然后BIOS通过ACPI到OS的机制。选择了简单和通用,就得放弃详细。
(2)OS native model分两种:用linux kernel自带的PCI系统和自己开发PCI子系统。
选择linux kernel自带的PCI子系统选择了通用,一定程度上选择了简单(相对自己开发而言),就放弃了详细。
根据自己的系统情况自研PCI子系统,就选择了详细,从而放弃了简单和通用。
三种处理方式谈不上谁好谁坏,需要根据具体项目情况计算项目的最佳投入收益比。
比如下面这个例子,选择了FFM当出现fatal error的错误,系统会直接panic,当你看到下面的打印时就是pcie错误导致的panic
{23}[Hardware Error]: event severity: fatal
Kernel panic - not syncing: Fatal hardware error!
[ 891.997065] Kernel panic - not syncing: Fatal hardware error!
[ 891.997069] CPU: 0 PID: 5311 Comm: llvmpipe-11 Kdump: loaded Not tainted 4.18.0-147.el8.x86_64 #1
[ 891.997072] Call Trace:
[ 891.997076]
[ 891.997087] dump_stack+0x5c/0x80
[ 891.997092] panic+0xe7/0x247
[ 891.997097] ? ghes_copy_tofrom_phys+0x1ab/0x260
[ 891.997100] ? ghes_irq_func+0x20/0x20
[ 891.997103] __ghes_panic.cold.16+0x21/0x21
[ 891.997106] ghes_proc+0x95/0x130
[ 891.997109] ghes_poll_func+0x16/0x30
[ 891.997113] call_timer_fn+0x2b/0x130
[ 891.997117] run_timer_softirq+0x376/0x410
[ 891.997120] ? enqueue_hrtimer+0x38/0x90
[ 891.997123] ? __hrtimer_run_queues+0x130/0x280
[ 891.997127] ? hpet_mask_rtc_irq_bit+0x60/0x60
[ 891.997130] __do_softirq+0xe3/0x30a
[ 891.997134] irq_exit+0x100/0x110
[ 891.997137] smp_apic_timer_interrupt+0x74/0x140
[ 891.997140] apic_timer_interrupt+0xf/0x20
[ 891.997142]
这里我们先研究一下 linux PCIEAER servicer的作者对这几种机制的思考。
linux PCIEAER driver的作者写了一篇文章,enable pci express advance error report in kernel,
enable pci express advance report in kernel
感兴趣的可以自己下载看看。文中指出在2.6.18内核之前由于没有AER服务,BIOS做了一些基础的错误处理机制,但是这种机制不能配合相应设备得到更详细的错误信息,更重要的没有recovery的功能,因此linux kernel引入的AER driver
大部分的BIOS提供了非标准的错误处理机制,AER driver为了防止处理PCIe错误时与BIOS冲突,必须要通过APCI 的**_OSC机制**,从BIOS那里获取PCIE AER的控制权。
一旦PCIEAER driver获取了PCIe错误的控制权,BIOS必须停止PCIE错误处理。AER driver配置RP和特定设备的的 AER CAP 寄存器来支持PCIe native AER。
当前,大部分的BIOS没有遵循PCI FW 3.0,来支持ACPI_OSC处理。如果BIOS不遵循 PCI FW 3.0规定的ACPI _OSC。PCIE AER driver有一种规避措施就是在bootgrub中加入aerdriver.forceload=y,不过这个aerdriver.forceload已经被移除了
https://lore.kernel.org/patchwork/patch/714628/
intel的白皮书对于FFM和OS native model处理的规定
A Tour beyond BIOS Implementing the ACPI Platform Error Interface with the Unified Extensible Firmware Interface
Intel BIOS APCI白皮书
APEI提供了两种方式,Firmware first model(FFM)和OS native model。
(1)FFM
所有的错误都通过SMI中断或者GPI中断通知到host firmware。然后Firmware分析错误,最后创建详细的有FRU信息的APEI 错误log(实际上firmware是很难做到的)。最后firmware通SCI、NMI或者其他中断通知OS。
(2)OS native model
OS或者OS级别的软件提供直接访问hardware reg和分析错误的机制。这需要标准的硬件标准架构提供错误信息和上报功能。例如AER 和MCA(MCE architecture)
硬件平台可以使用并发模式,即FFM和OS native都存在,也就是某些错误使用FFM,某些错误使用OS native model(个人建议是PCIEAER 使用OS native mode,因为PCI spec越来越复杂后,拓扑管理、热插拔、aer、DPC功能是相互影响的,把几个功能割裂开会出现各种各种的问题。这点linux kernel自带的PCI子系统处理的也不好。出现错误时,linux kernel的PCI子系统经常会出现Oops,panic之类的问题)
在X86架构下,错误通过硬件pin脚、硬件错误寄存器或者error msg通知host firmware。这些错误通常会触发SMI,这种host firmware就可以在SMM环境处理错误了。
error也可以通过X86架构下的中断通知OS,包括MCE/CMC或者AER MSI/INTx(基本上没有人再使用了)或者平台级的NMI/ACPI SCI。
前面说了很多理论,下面我们看看intel的框图
怎么关闭FFM呢?
intel平台
下面介绍的比较详细,把MISCCTRLSTS 这个reg可以覆盖标准配置空间Root ctrl reg的system error enable bit的设置,因此在intel平台上要关闭firmware first model,首先把MISCCTRLSTS的bit35-33清零。防止MISCCTRLSTS覆盖了root ctrl reg对应bit。
MISCCTRLSTS 的bit4也需要清理,防止MSI和INTx中断被disable。
ROOT CNT和其他一些AER reg(这些reg都不用手动操作,OS的AER driver和pci_enable_msi函数会帮你搞定,具体见linux 的AER driver)就可以了,详细介绍见
/**
* 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_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);
}
寄存器详细见:Intel® Xeon® Processor D-1500 Product Family External Design
Specification (EDS), Volume Two: Core and Uncore Registers
MISCCTRLSTS 0的bit4比较清楚的说明了,当该bit 为1,不管MSI或者INTx使能与否,MSI或者INTx都不会触发的,也就是说MISCCTRLSTS 0的bit4可以覆盖PCIe标准寄存器对于MSI或者INTx的使能或者禁止。
至于PCIe错误是否会导致如NMI/SMI/PMI/CPEI等system event,则依赖于对应的system error使能bit或者覆盖system error使能bit是否设置。
AMD 平台
IOHCRASx0000017C (IOHC::PCIE0PortASerr_ACTION_CONTROL)到IOHCRASx00000378 (IOHC::PCIE1PortHParityErr_ACTION_CONTROL)清零,就可以关闭firmware first model了。
下面是OS的一下patch,这几个patch没啥太大意义,只是表明内核维护者对这两种机制怎么共存也没想好怎么处理
https://patchwork.kernel.org/patch/10871703/
https://patchwork.ozlabs.org/cover/998635/
https://patchwork.kernel.org/patch/10871705/