☰
PCI Passthrough 实战:IOMMU、VFIO 与中断重映射配置指南
2026/10/15 23:09:38 网站建设 项目流程

1. 从一个真实需求说起:为什么要做PCI Passthrough

如果你在虚拟化环境里跑过对延迟敏感或者对硬件有强依赖的业务,大概率会遇到一个绕不开的坎:虚拟网卡再怎么调优,性能就是上不去。我最早接触这个需求,是帮一个做数据采集的朋友搭环境,他的采集卡是PCIe接口的,需要在虚拟机里直接访问这块物理设备。一开始想的是走常规的虚拟化方案,结果发现设备根本识别不到,更别提驱动了。后来折腾了一圈,才把PCI Passthrough这套东西跑通。

PCI Passthrough,中文一般叫“PCI设备直通”,核心思路是把宿主机上的一块物理PCI设备,直接分配给某台虚拟机独占使用。虚拟机看到的不再是一个模拟出来的虚拟设备,而是货真价实的物理硬件。这样做的好处很直接:性能接近裸机、延迟大幅降低、设备兼容性问题基本消失。代价也很明显:这块设备被虚拟机独占期间,宿主机自己就用不了了,而且配置过程涉及IOMMU、VFIO、中断重映射等一堆概念,稍有不慎就是各种报错。

这篇文章我打算把整个流程从头到尾捋一遍,包括底层原理、环境检查、配置步骤、常见报错和排查思路。适合两类人看:一类是刚接触虚拟化、想搞清楚直通到底怎么回事的;另一类是被性能问题折磨过、准备上直通方案的。我会尽量把“为什么这么做”讲清楚,而不是只丢一堆命令让你抄。

2. 直通的底层逻辑:IOMMU、VFIO与中断重映射

2.1 IOMMU到底在干什么

要理解直通,先得理解IOMMU。你可以把它想象成给设备用的“地址翻译官”。正常情况下,CPU访问内存用的是虚拟地址到物理地址的映射,这个由MMU负责。但PCI设备发起DMA(直接内存访问)的时候,它用的是物理地址,如果不管控,设备就能访问任意内存区域,这在虚拟化场景下是灾难性的。

IOMMU的作用就是给设备也加一层地址翻译。设备发出的DMA请求,先经过IOMMU翻译成宿主机认可的物理地址,这样就能把设备的内存访问限制在指定范围内。对于直通来说,IOMMU还负责把设备隔离到独立的IOMMU组里,保证不同设备之间的DMA互不干扰。没有IOMMU,直通根本无从谈起,因为虚拟机里的设备会直接踩到宿主机的内存。

在x86平台上,Intel的叫VT-d,AMD的叫AMD-Vi。名字不同,功能类似。检查是否开启,最直接的办法是看内核启动日志里有没有DMAR相关的条目,或者看/sys/class/iommu目录下有没有内容。

2.2 VFIO:把设备安全地交给虚拟机

有了IOMMU,还需要一套机制来管理设备的分配和隔离,这就是VFIO(Virtual Function I/O)。VFIO是一个内核框架,它允许把设备的安全访问权限暴露给用户空间,虚拟机管理器(比如QEMU)就是通过VFIO来接管直通设备的。

VFIO的工作方式是这样的:它把设备绑定到vfio-pci驱动上,而不是原来的物理驱动。这样一来,宿主机就不再使用这块设备,设备处于一种“待分配”状态。当虚拟机启动时,QEMU通过VFIO接口把设备映射到虚拟机地址空间,虚拟机里的驱动就能直接操作硬件了。

这里有个关键点:设备必须被隔离在独立的IOMMU组里。如果一块设备和别的设备共享IOMMU组,那要么整组一起直通,要么都别直通。这是因为IOMMU组是隔离的最小单位,组内设备之间的DMA无法互相隔离。实际配置中,经常遇到网卡和别的设备共享组的情况,这时候就需要用PCIe ACS(Access Control Services)或者pcie_acs_override来强行拆分,但后者有安全风险,生产环境慎用。

2.3 中断重映射:别让中断成为瓶颈

设备直通后,中断怎么处理?如果还是走传统的中断线,虚拟机里的设备中断会直接打到宿主机上,导致虚拟机无法正常响应。所以需要中断重映射(Interrupt Remapping),它允许把设备的中断重定向到虚拟机的虚拟中断控制器。

Intel平台叫VT-d Interrupt Remapping,AMD平台也有对应机制。开启后,设备产生的中断会先经过IOMMU的中断重映射表,找到对应的虚拟机,再注入到虚拟机的vCPU上。这样虚拟机就能像裸机一样处理中断了。检查是否开启,可以看内核日志里有没有“Interrupt remapping enabled”之类的提示。

3. 动手前的环境检查:别急着改配置

3.1 硬件与固件层面的确认

第一步永远是确认硬件支持。CPU要支持VT-d或AMD-Vi,主板BIOS里要能开启IOMMU。很多消费级主板默认是关闭的,甚至有些主板根本不提供这个选项。我踩过的坑是:一台机器CPU明明支持,但BIOS里死活找不到IOMMU选项,后来查资料才发现是主板厂商在固件里屏蔽了。这种情况只能换主板或者换平台。

确认方法很简单,Linux下执行:

dmesg | grep -i -e DMAR -e IOMMU

如果看到“DMAR: IOMMU enabled”之类的输出,说明IOMMU已经开启。如果什么都没看到,先去BIOS里找“VT-d”、“IOMMU”、“AMD-Vi”这些选项,开启后再回来检查。

另外,还要确认设备本身是否支持直通。大部分PCIe设备都支持,但有些设备(比如某些集成显卡)的直通会非常麻烦,因为它的驱动和固件设计就没考虑过虚拟化场景。网卡、HBA卡、采集卡这些通常没问题。

3.2 查看IOMMU组的分组情况

IOMMU组的分组情况决定了哪些设备能独立直通。查看方法:

for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*} printf 'IOMMU Group %s ' "$n" lspci -nns "${d##*/}" done

这个脚本会列出每个IOMMU组里的设备。理想情况下,你想直通的设备独占一个组。如果它和别的设备共享,就要考虑是否整组直通,或者用ACS覆盖来拆分。我一般建议先看分组,再决定直通哪块设备。如果一块网卡和USB控制器分在同一个组,那直通网卡就意味着USB控制器也得跟着走,这通常不是你想要的结果。

3.3 确认设备当前使用的驱动

设备当前绑定的驱动决定了你需不需要手动切换。比如一块网卡,如果它用的是igb或ixgbe驱动,直通前需要把它从这些驱动上解绑,改绑到vfio-pci。查看命令:

lspci -nnk -d 8086:10fb

输出里会显示“Kernel driver in use”和“Kernel modules”两行。记下设备的PCI ID(比如8086:10fb),后面配置VFIO的时候要用。

4. 配置VFIO:让设备准备好被接管

4.1 内核参数的调整

要让VFIO正常工作,内核启动参数里需要开启IOMMU。编辑GRUB配置:

sudo vi /etc/default/grub

找到GRUB_CMDLINE_LINUX这一行,Intel平台加上:

intel_iommu=on iommu=pt

AMD平台加上:

amd_iommu=on iommu=pt

iommu=pt的意思是“passthrough”,它让没有直通需求的设备继续用直接映射,减少性能开销。不加这个参数也能工作,但可能会有额外的地址翻译开销。

改完后更新GRUB并重启:

sudo update-grub sudo reboot

重启后再次检查dmesg | grep -i IOMMU,确认IOMMU已经启用。

4.2 绑定vfio-pci驱动

有两种方式把设备绑定到vfio-pci:一种是在内核启动时通过vfio-pci.ids参数指定,另一种是运行时动态绑定。前者适合设备固定的场景,后者适合临时调试。

内核参数方式,在GRUB_CMDLINE_LINUX里追加:

vfio-pci.ids=8086:10fb,8086:10fc

多个设备用逗号分隔。这样系统启动时就会自动把这些设备绑定到vfio-pci,不会加载原来的驱动。

动态绑定方式,先解绑原驱动:

echo 0000:03:00.0 > /sys/bus/pci/devices/0000:03:00.0/driver/unbind

然后绑定到vfio-pci:

echo 8086 10fb > /sys/bus/pci/drivers/vfio-pci/new_id

这种方式重启后就失效了,适合测试阶段。生产环境建议用内核参数方式,稳定且可重复。

4.3 验证绑定结果

绑定完成后,用lspci -nnk确认设备驱动已经变成vfio-pci:

lspci -nnk -d 8086:10fb

输出里应该看到“Kernel driver in use: vfio-pci”。如果还是原来的驱动,说明绑定没成功,检查PCI ID是否正确、IOMMU组是否独立。

注意:如果设备在IOMMU组里和其他设备共享,绑定vfio-pci后,同组的其他设备也会受到影响。所以绑定前一定要确认分组情况。

5. 虚拟机配置:把设备交给QEMU

5.1 QEMU命令行方式

如果你直接用QEMU启动虚拟机,添加直通设备的参数是这样的:

qemu-system-x86_64 \ -enable-kvm \ -m 4096 \ -cpu host \ -device vfio-pci,host=03:00.0,id=net0 \ ...

host=03:00.0是设备的PCI地址,id=net0是给这个设备起的名字,方便后续引用。如果设备有多个功能(比如网卡的多队列),可以用host=03:00.0指定整个设备,或者用host=03:00.0,function=0指定单个功能。

5.2 libvirt XML配置方式

用libvirt管理虚拟机的话,需要在XML里添加<hostdev>段:

<hostdev mode='subsystem' type='pci' managed='yes'> <source> <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/> </source> </hostdev>

managed='yes'表示让libvirt自动处理设备的解绑和绑定,省去手动操作。如果设备已经被绑定到vfio-pci,libvirt也能识别。添加后重启虚拟机,设备就会出现在虚拟机里。

5.3 虚拟机内的驱动安装

设备直通进去后,虚拟机里需要安装对应的驱动。比如直通的是Intel网卡,虚拟机里就要装igb或ixgbe驱动。如果是Windows虚拟机,可能需要手动安装厂商提供的驱动。这一步和裸机装驱动没区别,因为虚拟机看到的就是物理设备。

有个细节:如果直通的是显卡,虚拟机里可能需要关闭一些安全启动相关的选项,否则驱动加载会失败。另外,直通显卡时,宿主机最好用另一块显卡做控制台,否则宿主机自己没显示输出。

6. 常见问题与排查技巧实录

6.1 设备绑定失败:Device or resource busy

这个报错通常是因为设备还在被宿主机使用。比如网卡还在被网络管理器管理,或者设备被其他驱动占用。解决办法是先停掉相关服务,再解绑驱动。对于网卡,可以用:

ip link set dev eth0 down

然后再解绑。如果是存储控制器,要确保没有挂载它的文件系统。

6.2 虚拟机启动报错:Failed to bind PCI device

这个错误一般是IOMMU组的问题。检查设备是否在独立的IOMMU组里,如果和其他设备共享,要么整组直通,要么用ACS覆盖拆分。另外,确认vfio-pci模块已经加载:

lsmod | grep vfio

如果没有,手动加载:

modprobe vfio-pci

6.3 直通后性能不达预期

直通理论上性能接近裸机,但如果配置不当,可能会有额外开销。检查几点:一是iommu=pt是否加上,二是虚拟机的CPU模式是否设为host-passthrough,三是是否开启了大页内存。另外,如果直通的是网卡,虚拟机里的中断亲和性设置也会影响性能,可以把中断绑定到不同的vCPU上。

6.4 宿主机失去设备访问能力

这是直通的正常现象。设备被虚拟机独占后,宿主机就看不到它了。如果宿主机也需要用这块设备,那只能等虚拟机关机后再解绑。所以规划的时候要想清楚:哪些设备是宿主机必须保留的,哪些是可以完全交给虚拟机的。

6.5 常见问题速查表

问题现象可能原因排查方向
设备绑定vfio-pci失败设备被占用停掉相关服务,解绑原驱动
虚拟机启动报IOMMU错误IOMMU未开启或组不独立检查BIOS和内核参数,查看IOMMU组
虚拟机内设备无驱动驱动未安装或版本不匹配安装厂商驱动,检查设备ID
直通后性能差中断重映射未开启或CPU模式不对检查内核日志,调整虚拟机CPU配置
宿主机无法使用设备设备被虚拟机独占关机后解绑,或规划时预留设备

7. 一些实操心得与避坑建议

直通这东西,说难不难,说简单也不简单。我踩过的坑里,最折腾的是IOMMU组的问题。有一次直通一块网卡,发现它和USB控制器分在同一个组,结果直通网卡后USB键鼠全失灵了,虚拟机装系统的时候连键盘都用不了。后来只能换了一块主板,才把分组问题解决。所以选硬件的时候,最好提前查一下主板的IOMMU分组情况,有些主板厂商会做优化,分组比较干净。

另一个坑是驱动绑定。我习惯用内核参数方式绑定vfio-pci,但有一次忘了把设备ID写对,结果系统启动后设备还是被原驱动接管了。后来发现是PCI ID写错了,把8086:10fb写成了8086:10f8。这种低级错误排查起来很费时间,建议绑定后一定用lspci -nnk确认。

还有一点,直通设备的热插拔支持因平台而异。有些设备支持热插拔,可以在虚拟机运行时添加或移除;有些则必须关机操作。如果业务要求高可用,最好在规划阶段就确认清楚。

最后,直通不是万能的。如果只是想要高性能网络,SR-IOV可能是更好的选择,它能让一块物理网卡虚拟出多个VF,每个VF直通给不同虚拟机,宿主机自己还能保留PF。但SR-IOV需要网卡和主板都支持,配置也更复杂。PCI Passthrough更适合那些必须独占物理设备的场景,比如采集卡、HBA卡、特殊加速卡。

我个人在实际操作中的体会是:直通配置本身不难,难的是环境检查和问题排查。把IOMMU组、驱动绑定、中断重映射这三块搞清楚,大部分问题都能定位。另外,多准备一块网卡做管理口,别把管理口直通出去了,否则虚拟机一挂,宿主机都连不上,那就真抓瞎了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询