第4章 PCIe接口连接故障排查(硬件识别/中断冲突)
PCIe接口,说白了就是个高速串行总线。但真出故障的时候,它可一点都不「高速」——你经常会被卡在设备不识别、驱动加载失败、或者莫名其妙的中断风暴里。
我做了这么多年HSM开发,PCIe相关的问题占了将近四成。硬件识别和中断冲突是两个最常见的坑。咱们一个一个讲。
4.1 硬件识别:设备为什么没出现?
先说硬件识别。PCIe设备插上去以后,系统怎么发现它?流程其实很清晰:
- 复位与链路训练——上下电后,Root Complex和Endpoint之间会自动做链路训练(LTSSM)。
- 配置空间枚举——BIOS或OS通过配置空间读写,给每个设备分配Bus/Device/Function号。
- 资源分配——BAR空间、MSI中断号等资源的分配与映射。
我记得有个项目,PCIe SSD盘怎么插都不认。lspci看不到,dmesg也没报错。物理链路看起来好好的,灯也亮。后来我用示波器卡了一下差分信号——发现接收端焊盘虚焊。所以我的习惯是:硬件识别出问题,先别急着查驱动,先确认链路训练是否成功。
检查链路状态的小技巧
在Linux下直接用 lspci -vvv 查看 LnkSta 字段。如果显示 Speed 2.5GT/s, Width x1 而不是你期望的 8GT/s, x4,那说明链路训练没跑到最高性能——可能信号完整性有问题。
实战命令清单
# 查看所有PCIe设备
lspci -nn
# 查看指定设备(例如00:01.0)的详细链路状态
lspci -s 00:01.0 -vvv | grep -i "lnk"
# 触发重新扫描(热插拔场景)
echo 1 > /sys/bus/pci/rescan
还有一个经典问题:设备端口挂在了错误的PCIe switch下面。你想想看,如果上游Switch的端口没有正确配置Downstream Port,甚至没上电,下游设备当然找不到。我会在系统起来前先对照原理图,确认物理slot对应的BDF号。
4.2 中断冲突:性能杀手与神秘崩溃
硬件识别过了,设备也枚举出来了,但一跑吞吐量测试就丢包,或者内核疯狂打印中断相关的错误——这一般就是中断冲突。
PCIe支持三种中断机制:INTx(传统边带信号)、MSI(消息信号中断)、MSI-X(扩展版)。现在基本都用MSI或MSI-X,因为它可以不共享中断线,每个设备有自己的中断向量。但如果你内核配置或者BIOS有问题,退回到INTx模式,那就可能和其他设备抢中断号。
我曾经遇到过一台服务器,插了两张HSM加速卡,结果只有一张能正常工作。 dmesg看到大量“irq xx: nobody cared”和“spurious interrupt”。查了一下午才发现,两张卡都要求分配同样的MSI-X向量,但BIOS没有给它们分配到不同的CPU核心组。解决办法是在kernel cmdline里加上pci=noacpi或者手动指定irq affinity。
| 特性 | INTx | MSI | MSI-X |
|---|---|---|---|
| 中断线数量 | 每个设备最多4条(共享) | 最多32个向量 | 最多2048个向量 |
| 是否共享 | 是(容易冲突) | 否(每个向量独立) | 否(每个向量独立) |
| 性能开销 | 高(需轮询ISR) | 低 | 极低(支持每个向量单独Mask) |
| 硬件支持 | 所有PCIe设备 | PCI 2.2+ | PCI 3.0+ |
注意:INTx共享中断的坑
如果你的设备工作在INTx模式(或者你手动降级了),而其他设备也用了同一条中断线,那么中断处理程序必须判断中断是否属于自己。如果两个设备同时触发中断,且优先级低的ISR返回IRQ_NONE,很容易产生“spurious interrupt”导致系统panic。
排查中断冲突的一般步骤:
- 查看
/proc/interrupts确认每个中断号绑定了哪些设备。如果两个设备共享同一个IRQ号且都在使用INTx,就要小心了。 - 用
cat /proc/irq/<IRQ>/smp_affinity看亲和性设置。建议把关键HSM设备的中断绑定到隔离的CPU核上。 - 如果设备支持MSI-X,检查驱动是否有参数强制启用(比如
module_param(msix_enable, int, 0))。
亲测有效的快速诊断命令
# 查看中断分布
watch -n 1 cat /proc/interrupts
# 查看PCIe设备当前使用的中断模式
lspci -s 03:00.0 -vvv | grep -i "msi"
# 手动更改中断亲和性
echo 2 > /proc/irq/92/smp_affinity
4.3 一张图看懂PCIe故障排查流程
我把硬件识别和中断冲突两部分的核心逻辑画成了流程图。遇到问题时,照着这个顺序走,基本能快速定位。
我个人用这张图处理过不下二十次PCIe故障。基本上,顺着左边走是硬件链路问题,右边是枚举和资源分配问题,中间的分支专门针对中断。你记住一个原则:先硬件再软件,先识别再中断。
避坑指南:我曾经被BIOS的PCIe ASPM坑过一次
那是在一个低功耗设计的板子上,BIOS默认开启了ASPM L1。结果HSM卡在休眠状态下,链路直接断开。驱动rescan根本没用,必须把设备下电再上电。后来我在kernel参数里加了 pcie_aspm=off 才解决。如果你发现设备间歇性丢失,先关掉ASPM试试。
4.4 实战案例:HSM加速卡中断风暴
最后讲一个真实案例。有一款HSM卡,插在PCIe x16槽位上,驱动加载后CPU软锁。我查了下/proc/interrupts,发现中断号95被两个设备共享:一个是板载网卡,另一个就是HSM卡。而且两者都工作在INTx模式。
为什么?因为该HSM卡的固件bug——它没有正确报告MSI能力。BIOS于是退回到INTx。又因为PCIe槽位与网卡同属一个PCI桥,中断线被路由到了同一根线上。
解决办法很简单:给HSM卡打一个驱动补丁,强制告知固件支持MSI。然后通过setpci把中断线重新分配。具体步骤:
# 查看设备当前中断线
lspci -s 05:00.0 -vvv | grep "Interrupt"
# 强制写中断线寄存器(需要先确认硬件设计)
setpci -s 05:00.0 INTERRUPT_LINE=0x0b
# 重启后检查中断共享情况
cat /proc/interrupts | grep "pci-msi"
嗯,这不算标准做法,但有时候应急只能用这种手段。当然,最终还是要联系固件厂商修复。
总之,PCIe故障排查说难也不难——链路训练、设备枚举、中断配置,这三层一层层剥开,大多数问题都能定位。养成看 lspci -vvv 和 /proc/interrupts 的习惯,能省下你大量抓头发的时间。