ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PCIe直通与SR-IOV底层机制及AER掉卡排查实战

PCIe直通与SR-IOV底层机制及AER掉卡排查实战 1. 从一个让人抓狂的掉卡问题说起如果你在服务器或者高性能工作站上折腾过虚拟化大概率遇到过这样的场景一台宿主机上插了四块网卡分配给四台虚拟机做业务隔离跑着跑着某台虚拟机突然网络断了dmesg里刷出一串 AER 报错设备从lspci列表里直接消失。重启虚拟机没用得重启宿主机才能恢复。更诡异的是有时候设备还在但链路速率从 Gen3 x8 掉到了 Gen1 x1带宽直接砍到十分之一。这类问题背后牵扯的东西非常多——PCIe 枚举、IOMMU 地址翻译、ACS 访问控制、AER 错误上报、VFIO 设备直通每一个环节出问题都可能导致上面这些症状。而 PCIe 直通和 SR-IOV 恰好是把这些底层机制全部串起来的两个核心应用场景。前者是把一个完整的物理 PCIe 设备交给虚拟机独占使用后者是把一个物理设备虚拟出多个轻量级功能单元VF分给不同虚拟机共享。这篇文章不打算写成教科书式的协议解读而是从实际运维和开发中踩过的坑出发把 PCIe 直通和 SR-IOV 的底层机制拆开揉碎讲清楚。适合已经有一定 Linux 基础、正在做虚拟化性能优化或者被直通设备稳定性问题折磨的读者。读完你应该能理解为什么直通需要 IOMMUACS 到底在卡什么SR-IOV 的 VF 是怎么变出来的以及遇到掉卡、降速、AER 报错时该从哪里下手排查。2. PCIe 直通的本质把物理设备完整交给虚拟机2.1 直通到底在做什么很多人第一次接触 PCIe 直通PCI Passthrough的时候理解停留在“把设备分配给虚拟机”这个层面。但这句话太笼统了真正要搞清楚的是宿主机是怎么做到让一个物理 PCIe 设备被虚拟机直接驱动的核心思路其实很直接——虚拟机监控器Hypervisor通过硬件辅助的 I/O 虚拟化技术把物理设备的配置空间、MMIO 区域和中断全部映射到虚拟机的地址空间中。虚拟机里的驱动程序发出的 MMIO 读写请求经过 IOMMU 的地址翻译后直接落到物理设备上。整个过程 CPU 不需要陷入 Hypervisor 做模拟数据通路几乎和裸机一样。这就是为什么直通的性能远好于 virtio 这类半虚拟化方案。virtio 虽然也能做到很高的吞吐但每个 I/O 请求都需要前后端协商CPU 开销和延迟都比直通高一个档次。对于 25G/100G 网卡、NVMe SSD 这类高速设备直通几乎是唯一能跑满线速的方案。2.2 VFIO用户态驱动框架的关键角色Linux 内核里负责直通的核心框架叫VFIOVirtual Function I/O。它的设计哲学很有意思内核只负责最危险的部分——IOMMU 的配置和 DMA 重映射设备的具体驱动逻辑全部放到用户态也就是 QEMU 里去跑。这样做的好处是安全性和灵活性的平衡。内核通过 IOMMU 确保设备只能 DMA 到所属虚拟机的内存区域不会越界写到宿主机或其他虚拟机的内存。而用户态的 QEMU 可以灵活地模拟各种 PCIe 配置空间行为不需要为每种设备写内核驱动。VFIO 把设备暴露给用户态的接口主要有这么几类container 和 groupIOMMU 的最小隔离单位是 group一个 group 里的设备互相之间无法隔离 DMA。VFIO container 是 group 的容器代表一个 IOMMU 地址空间。device fd通过/dev/vfio/vfio和/dev/vfio/group_id操作获取设备的各种区域信息MMIO、配置空间、中断。IOMMU 映射QEMU 通过VFIO_IOMMU_MAP_DMA把虚拟机的物理地址映射到宿主机的虚拟地址IOMMU 硬件负责在设备 DMA 时做地址翻译。注意VFIO 要求设备所在的 IOMMU group 里所有设备要么都绑定到 vfio-pci 驱动要么都不绑定。如果 group 里有其他设备还在用原生驱动VFIO 会拒绝接管。这是新手最常踩的坑之一。2.3 直通配置的完整操作链路下面走一遍典型的 PCIe 直通配置流程以 Intel 平台 QEMU/KVM 为例。第一步确认 CPU 和主板支持 IOMMU。Intel 平台叫 VT-dAMD 平台叫 AMD-Vi。BIOS 里要打开对应选项内核启动参数加上# Intel 平台 intel_iommuon iommupt # AMD 平台 amd_iommuon iommuptiommupt是 pass-through 模式只对需要直通的设备启用 IOMMU 翻译其他设备走恒等映射能减少一点性能开销。第二步确认设备所在的 IOMMU groupfor d in /sys/kernel/iommu_groups/*/devices/*; do n${d#*/iommu_groups/*}; n${n%%/*} printf IOMMU Group %s $n lspci -nns ${d##*/} done输出会告诉你每个 group 里有哪些设备。如果目标设备和其他关键设备比如桥、SATA 控制器在同一个 group那就没法单独直通需要考虑 ACS 拆分后面会详细讲。第三步把设备绑定到 vfio-pci 驱动。有两种方式一种是在内核启动参数里用vfio-pci.ids vendor:device指定另一种是运行时通过 sysfs 解绑原驱动再绑定 vfio-pci# 解绑原驱动 echo 0000:01:00.0 /sys/bus/pci/devices/0000:01:00.0/driver/unbind # 绑定 vfio-pci echo 8086 10fb /sys/bus/pci/drivers/vfio-pci/new_id第四步QEMU 命令行里加上-device vfio-pci,host01:00.0把设备传给虚拟机。这套流程看起来不复杂但实际跑起来问题往往出在细节上——group 隔离不干净、ACS 没开导致 P2P DMA 穿透、AER 错误处理不当导致设备掉线。下面逐个拆。3. IOMMU 与 ACS直通隔离性的两道闸门3.1 IOMMU 不只是地址翻译IOMMU 最常被提到的功能是 DMA 重映射——设备发出的 DMA 地址经过 IOMMU 翻译后才能到达物理内存。但它的作用远不止于此。IOMMU 还负责中断重映射Interrupt Remapping。在没有中断重映射的年代设备可以直接往任意 CPU 发送 MSI/MSI-X 中断恶意设备可以伪造中断向量攻击系统。中断重映射让 IOMMU 拦截设备的中断请求根据中断重映射表决定是否放行以及投递到哪个 CPU。对于直通场景IOMMU 还提供了DMA 保护即使虚拟机里的驱动有 bug 或者被恶意代码控制设备也只能 DMA 到分配给该虚拟机的内存区域无法触碰宿主机内核或其他虚拟机的内存。这是直通安全性的基石。3.2 ACS被大多数人忽略的隔离关键ACSAccess Control Services是 PCIe 协议里的一组可选功能用来控制 PCIe 交换机或 Root Complex 内部的路由行为。为什么直通需要关心 ACS因为 PCIe 拓扑里存在P2PPeer-to-PeerDMA的可能。举个典型例子CPU 的 Root Complex 下面挂了两个 PCIe 端口一个接了网卡 A一个接了网卡 B。如果 ACS 没启用网卡 A 可以直接 DMA 到网卡 B 的 MMIO 空间或者更危险的是网卡 A 可以伪装成网卡 B 向 CPU 发请求。这种情况下即使 IOMMU 把两个设备分到了不同的 group隔离也是不完整的。ACS 的作用就是在 PCIe 交换机和 Root Complex 的端口上强制所有 TLPTransaction Layer Packet都向上转发到 Root Complex不允许端口之间直接转发。这样 IOMMU 就能拦截并检查每一个请求。检查 ACS 是否启用lspci -vvv -s 01:00.0 | grep -i acs如果输出里有ACSCtl: SrcValid- TransBlk- ReqRedir- CmpltRedir- UpstreamFwd- EgressCtrl- DirectTrans-说明 ACS 能力存在但全部关闭。如果连ACSCtl都没有说明这个设备/端口根本不支持 ACS。3.3 ACS 覆盖与 IOMMU group 拆分很多消费级主板和部分服务器主板的 PCIe 端口不支持 ACS导致多个设备被塞进同一个 IOMMU group。这时候可以用内核的ACS Override补丁强制启用 ACS。这个补丁不在主线内核里需要自己编译或者用发行版提供的带补丁内核。启用方式是在内核启动参数加pcie_acs_overridedownstream,multifunctiondownstream会覆盖所有下游端口的 ACSmultifunction会额外拆分多功能设备。但要注意ACS Override 是绕过硬件限制的做法在某些平台上可能导致不可预期的行为比如设备无法正常工作或者系统不稳定。生产环境慎用测试环境可以拿来验证隔离性。实操心得判断一个设备能不能安全直通最直接的方法是看它所在的 IOMMU group 里有没有其他设备。如果 group 里只有它自己基本可以放心直通。如果 group 里有其他设备先尝试用 ACS Override 拆分拆不开就考虑换插槽或者换主板。4. SR-IOV一个物理设备变出多个虚拟功能4.1 SR-IOV 的架构逻辑SR-IOVSingle Root I/O Virtualization解决的是另一个问题直通虽然性能好但一个物理设备只能给一台虚拟机用。如果我有 8 台虚拟机都需要网卡难道要插 8 块物理网卡SR-IOV 的思路是让一个物理设备PFPhysical Function虚拟出多个轻量级功能单元VFVirtual Function每个 VF 可以独立分配给一台虚拟机。PF 是完整功能的 PCIe 设备拥有配置和管理 VF 的能力。VF 是从 PF 派生出来的拥有独立的配置空间、BAR 空间和中断但功能相对精简。从 PCIe 枚举的角度看VF 就是标准的 PCIe 设备有自己的 BDFBus/Device/Function号。SR-IOV 的硬件实现依赖设备本身的支持。网卡里通常有一个嵌入式交换机或者分类器负责把每个 VF 的流量隔离并路由到对应的物理端口队列。这也是为什么 SR-IOV 网卡的 VF 之间能保证线速转发和流量隔离。4.2 启用 VF 的完整流程以 Intel X710 网卡为例走一遍 VF 创建和分配流程。首先确认网卡支持 SR-IOVlspci -vvv -s 01:00.0 | grep -i Single Root I/O输出里应该有Single Root I/O Virtualization (SR-IOV)字样并且Total VFs显示支持的最大 VF 数量。然后通过 sysfs 创建 VF# 创建 4 个 VF echo 4 /sys/class/net/ens1f0/device/sriov_numvfs创建完成后lspci里会多出几个设备BDF 号通常是 PF 的 Function 号递增lspci | grep X710 01:00.0 Ethernet controller: Intel X710 01:00.1 Ethernet controller: Intel X710 01:10.0 Ethernet controller: Intel X710 Virtual Function 01:10.1 Ethernet controller: Intel X710 Virtual Function 01:10.2 Ethernet controller: Intel X710 Virtual Function 01:10.3 Ethernet controller: Intel X710 Virtual Function每个 VF 都可以像普通 PCIe 设备一样绑定到 vfio-pci 然后直通给虚拟机。VF 的 IOMMU group 通常是独立的不需要 ACS Override 就能干净隔离。4.3 VF 的 MAC 地址与 VLAN 配置VF 默认没有固定的 MAC 地址每次创建时由 PF 驱动随机分配或者从池子里取。如果虚拟机需要固定的 MAC可以通过 iproute2 工具设置ip link set ens1f0 vf 0 mac 52:54:00:11:22:33 ip link set ens1f0 vf 0 vlan 100VLAN 配置在 VF 层面做掉的好处是虚拟机里不需要再配 VLAN 子接口简化了网络配置。但要注意如果虚拟机里也配了 VLAN可能会出现双重打标的问题。踩坑记录某些网卡的 VF 在虚拟机重启后 MAC 地址会变导致 DHCP 拿到不同的 IP。解决办法是在 PF 侧固定 VF 的 MAC或者在虚拟机里配静态 MAC。另外VF 的 spoofchk 默认是开启的如果虚拟机里需要改 MAC 或者发带 VLAN 的包需要关掉ip link set ens1f0 vf 0 spoofchk off。5. 掉卡、降速与 AERPCIe 稳定性问题的排查链路5.1 AER 报错到底在说什么AERAdvanced Error Reporting是 PCIe 协议里的错误上报机制。当 PCIe 链路上发生物理层或者数据链路层错误时设备或者 Root Complex 会通过 AER 机制上报。常见的 AER 错误类型包括错误类型含义常见原因Correctable Error可纠正错误信号质量差、链路训练不稳定Uncorrectable Non-Fatal不可纠正但非致命TLP 校验失败、超时Uncorrectable Fatal不可纠正且致命链路断开、设备无响应Bad TLPTLP 校验错误信号完整性问题、硬件故障Bad DLLPDLLP 校验错误链路层问题Receiver Error接收端错误物理层信号问题Timeout请求超时设备无响应、链路挂死当 AER 报出 Uncorrectable Fatal 错误时内核的 PCIe 错误处理流程会尝试恢复设备。如果恢复失败设备会被从总线上移除这就是“掉卡”的直接原因。5.2 从 dmesg 到 lspci 的完整排查路径遇到掉卡问题第一步永远是看dmesgdmesg -T | grep -i -E aer|pcie|vfio|iommu | tail -50典型的 AER 报错长这样pcieport 0000:00:1c.0: AER: Corrected error received: 0000:01:00.0 pcieport 0000:00:1c.0: AER: PCIe Bus Error: severityCorrected, typePhysical Layer pcieport 0000:00:1c.0: AER: device [8086:10fb] error status/mask00000001/00002000 pcieport 0000:00:1c.0: AER: [ 0] Receiver Error如果看到severityCorrected说明链路在自动纠正错误暂时不影响功能但频繁出现说明信号质量有问题。如果看到severityUncorrectable那就比较严重了设备可能已经掉线。第二步确认设备是否还在总线上lspci -s 01:00.0如果设备消失说明已经被内核移除。这时候可以尝试通过 PCIe 热插拔机制重新扫描echo 1 /sys/bus/pci/rescan但如果是硬件层面的链路断开rescan 也找不回来只能重启。第三步检查链路状态lspci -vvv -s 01:00.0 | grep -i LnkSta输出类似LnkSta: Speed 8GT/s, Width x8。如果速度或宽度低于预期说明链路训练有问题。常见原因包括金手指氧化、插槽灰尘、线缆质量差、主板走线问题。5.3 降速问题的硬件与软件因素PCIe 降速从 Gen3 降到 Gen1或者从 x8 降到 x1通常有以下几个原因信号完整性问题最常见。金手指脏了、插槽接触不良、PCB 走线阻抗不匹配都会导致链路训练时协商到较低的速率。电源管理ASPMActive State Power Management在某些平台上会导致链路进入低功耗状态后无法恢复到高速率。可以尝试在内核参数加pcie_aspmoff关闭 ASPM。设备兼容性某些老设备和新主板搭配时链路训练会失败并降速。可以尝试在 BIOS 里强制设置 PCIe 速率。Retimer 或 Redriver 问题长距离走线或者经过扩展卡时信号衰减严重需要 Retimer 补偿。如果 Retimer 配置不当也会导致降速。排查降速问题时可以对比设备在 BIOS 里显示的链路速率和操作系统里看到的速率。如果 BIOS 里是 Gen3 而系统里是 Gen1那大概率是软件配置问题如果 BIOS 里就是 Gen1那就是硬件链路问题。5.4 直通场景下的 AER 处理策略在直通场景下AER 错误的处理会更复杂。因为设备被分配给虚拟机后宿主机仍然会收到 AER 中断但设备的驱动在虚拟机里。如果宿主机直接移除设备虚拟机会突然失去设备可能导致虚拟机崩溃或者业务中断。比较稳妥的做法是在宿主机侧配置 AER 错误处理策略对于可纠正错误只记录不处理对于不可纠正错误先尝试恢复恢复失败再通知虚拟机。Linux 内核提供了pcinoaer参数可以完全关闭 AER 上报但这会掩盖硬件问题不建议在生产环境使用。更好的方案是使用pcie_portsnative让内核使用原生 PCIe 端口服务配合pcihpiosize等参数调整热插拔行为。具体参数需要根据平台和内核版本调整。实操心得如果直通设备频繁出现 AER 可纠正错误但不影响功能可以先观察一段时间。如果错误频率很高比如每秒几十次说明链路质量确实有问题建议换插槽或者换线缆。不要轻易用pcinoaer掩盖问题因为可纠正错误积累到一定程度会变成不可纠正错误到时候就是直接掉卡。6. PCIe 枚举与热插拔设备是怎么被系统发现的6.1 枚举过程的底层逻辑PCIe 枚举是系统启动时发现和配置所有 PCIe 设备的过程。它从 Root Complex 开始逐级扫描每个总线上的设备读取配置空间的 Vendor ID 和 Device ID然后分配总线号和地址空间。枚举的核心步骤扫描总线 0从 Root Complex 开始读取每个可能的 Device/Function 的配置空间。发现桥设备如果发现 PCIe 桥Bridge读取桥的 Secondary Bus Number 和 Subordinate Bus Number然后递归扫描下游总线。分配资源为每个设备的 BAR 分配 MMIO 地址空间和 I/O 端口空间。配置中断分配 MSI/MSI-X 中断向量。枚举过程中如果遇到设备无响应比如链路没训练成功会跳过该设备。这就是为什么有时候设备在lspci里看不到——枚举时链路还没准备好。6.2 热插拔的软件栈PCIe 热插拔允许在系统运行时插入或移除设备。它依赖几个组件热插拔控制器主板上的 PCIe 插槽通常有热插拔控制器负责检测设备插入/移除并产生中断。ACPI 热插拔通过 ACPI 的_EJ0和_STA方法控制插槽电源和状态。内核 PCIe 热插拔驱动pciehp驱动负责处理热插拔事件调用pci_rescan_bus重新枚举总线。在直通场景下热插拔可以用来动态添加或移除直通设备。QEMU 支持通过 QMP 接口热插拔 vfio-pci 设备但需要虚拟机内核支持 PCIe 热插拔。注意不是所有平台都支持 PCIe 热插拔。消费级主板通常不支持服务器主板一般支持。可以通过lspci -vvv查看插槽的SlotCapabilities里是否有HotPlug标志。6.3 枚举失败的常见原因如果设备在lspci里看不到可能的原因包括链路未训练成功设备供电正常但链路没建立通常是硬件问题。BIOS 未分配资源某些 BIOS 在资源不足时会跳过部分设备。设备被隐藏某些服务器 BIOS 有选项可以隐藏 PCIe 设备。枚举顺序问题如果设备依赖的桥还没枚举设备可能被跳过。排查枚举问题可以从dmesg里搜索pci相关的日志看看内核在枚举时有没有报错。另外可以用lspci -t查看 PCIe 拓扑树确认设备挂在哪条总线上。7. 性能调优与实战建议7.1 直通设备的性能调优直通设备的性能调优主要围绕减少 CPU 开销和降低延迟。几个关键点CPU 亲和性把虚拟机的 vCPU 绑定到固定的物理核心避免跨 NUMA 节点访问。可以用virsh vcpupin或者 QEMU 的-numa参数配置。大页内存虚拟机使用 2MB 或 1GB 大页减少 TLB miss。QEMU 参数-mem-path /dev/hugepages。中断亲和性把设备的中断绑定到和 vCPU 同一个 NUMA 节点的 CPU 上减少跨节点中断延迟。关闭不必要的特性比如 ASPM、电源管理避免链路进入低功耗状态。7.2 SR-IOV 的 VF 数量与性能权衡VF 数量不是越多越好。每个 VF 都会占用 PF 的资源队列、中断向量、MAC 表项VF 太多会导致 PF 资源耗尽反而影响性能。一般来说网卡的 VF 数量建议不超过物理队列数的一半。另外VF 之间的流量隔离依赖硬件交换机如果硬件交换机性能不足VF 之间的通信会成为瓶颈。测试时可以用iperf3在 VF 之间打流观察是否达到线速。7.3 常见问题速查表问题现象可能原因排查方法设备在 lspci 里消失链路断开、AER 致命错误dmesg 查 AER检查物理连接链路速率低于预期信号质量差、ASPMlspci -vvv 查 LnkSta关闭 ASPMVF 创建失败PF 驱动不支持、资源不足dmesg 查 sriov 相关日志直通设备无法启动IOMMU group 隔离不干净检查 group 内其他设备虚拟机网络不通VF MAC 冲突、VLAN 配置错误检查 PF 侧 VF 配置AER 可纠正错误频繁信号完整性问题换插槽、换线缆、清洁金手指7.4 生产环境的稳定性建议在生产环境使用 PCIe 直通和 SR-IOV 时有几个经验性的建议第一尽量选择服务器平台而不是消费级平台。服务器主板的 PCIe 走线、供电和散热都更好ACS 支持也更完整。第二直通设备尽量选择同一型号同一批次。不同型号的设备在 IOMMU group 划分和 AER 处理上可能有差异混用会增加排查难度。第三做好监控。AER 错误计数、链路速率、VF 状态都应该纳入监控。Linux 提供了/sys/bus/pci/devices/*/aer_dev_correctable等接口可以读取 AER 统计。第四定期更新固件。网卡、主板 BIOS、BMC 固件都可能影响 PCIe 稳定性。厂商的固件更新日志里经常会提到 PCIe 兼容性修复。第五测试环境充分验证。直通和 SR-IOV 的配置在不同平台上差异很大生产环境部署前一定要在相同硬件上充分测试。8. 个人实操中的几点体会折腾 PCIe 直通和 SR-IOV 这些年最大的感受是文档里写的都是理想情况实际环境里全是意外。同样一块网卡在这块主板上直通没问题换到另一块主板上就频繁 AER 报错。同样一个 VF 配置内核 5.4 上跑得好好的升级到 5.15 就出问题。我的经验是遇到问题先别急着改配置先把现象记录下来dmesg完整日志、lspci -vvv输出、/sys/kernel/iommu_groups的拓扑、AER 统计。这些信息比任何猜测都有用。很多时候问题就藏在某一行不起眼的日志里。另外ACS Override 虽然好用但不要把它当成万能药。它本质上是绕过硬件限制在某些平台上会导致设备行为异常。如果 ACS Override 之后设备能识别但性能异常大概率是 P2P DMA 路径出了问题这时候还是得从硬件拓扑上想办法。最后说一个容易被忽略的点PCIe 插槽的供电能力。某些高性能网卡或者 FPGA 卡功耗较高如果插槽供电不足链路训练时可能失败或者降速。服务器主板通常有辅助供电接口消费级主板就要注意插槽的供电规格了。
返回列表