Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

在此记录一下如何在 Linux 下启用 PCIe Passthrough 给虚拟机提供宿主 PCIe 设备。

我用的是 Ubuntu 22.04,现在新的系统内核一般内置了 vfio-pci 模块,可以直接使用。以 Intel Xeon E5-2670 为例子。

通过内核启动参数使用

需要修改 /etc/defaults/grub,给 GRUB_CMDLINE_LINUX_DEFAULT 增加如下参数:

1
iommu=pt intel_iommu=on vfio-pci.ids={DEVICE_ID_1},{DEVICE_ID_N} vfio-pci.disable_idle_d3=1 kvm_intel.nested=1 kvm_intel.emulate_invalid_guest_state=0

intel 相关的参数是 Intel 专用,amd 的需要参阅参考资料对应修改。

vfio-pci.ids= 后填设备的 ID,可以通过 lspci -nnv 看到,例如我的 GTX560:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
04:00.0 VGA compatible controller [0300]: NVIDIA Corporation GF114 [GeForce GTX 560] [10de:1201] (rev a1) (prog-if 00 [VGA controller])
Subsystem: NVIDIA Corporation GF114 [GeForce GTX 560] [10de:0895]
Physical Slot: 5
Flags: fast devsel, IRQ 11, IOMMU group 29
Memory at cc000000 (32-bit, non-prefetchable) [disabled] [size=32M]
Memory at d0000000 (64-bit, prefetchable) [disabled] [size=128M]
Memory at d8000000 (64-bit, prefetchable) [disabled] [size=64M]
I/O ports at c000 [disabled] [size=128]
Expansion ROM at ce000000 [disabled] [size=512K]
Capabilities: <access denied>
Kernel driver in use: nouveau
Kernel modules: nvidiafb, nouveau

04:00.1 Audio device [0403]: NVIDIA Corporation GF114 HDMI Audio Controller [10de:0e0c] (rev a1)
Subsystem: NVIDIA Corporation GF114 HDMI Audio Controller [10de:0895]
Physical Slot: 5
Flags: fast devsel, IRQ 7, IOMMU group 29
Memory at ce080000 (32-bit, non-prefetchable) [disabled] [size=16K]
Capabilities: <access denied>
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intel

04:00.0 是设备物理位置,也就是插槽的位置,只要不换插槽就不会变。设备名称后面的 10de:1201 就是设备 ID。如果显卡带音频控制器,需要一同 passthrough。这里我就应该填 vfio-pci.ids=10de:1201,10de:0e0c

修改完成后执行 sudo update-grub 更新 GRUB 配置。重启之后通过 lspci -nnv 查,就能看到对应设备的 Kernel driver in use 变成了 vfio-pci 了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
04:00.0 VGA compatible controller [0300]: NVIDIA Corporation GF114 [GeForce GTX 560] [10de:1201] (rev a1) (prog-if 00 [VGA controller])
Subsystem: NVIDIA Corporation GF114 [GeForce GTX 560] [10de:0895]
Physical Slot: 5
Flags: fast devsel, IRQ 11, IOMMU group 29
Memory at cc000000 (32-bit, non-prefetchable) [disabled] [size=32M]
Memory at d0000000 (64-bit, prefetchable) [disabled] [size=128M]
Memory at d8000000 (64-bit, prefetchable) [disabled] [size=64M]
I/O ports at c000 [disabled] [size=128]
Expansion ROM at ce000000 [disabled] [size=512K]
Capabilities: <access denied>
Kernel driver in use: vfio-pci
Kernel modules: nvidiafb, nouveau

04:00.1 Audio device [0403]: NVIDIA Corporation GF114 HDMI Audio Controller [10de:0e0c] (rev a1)
Subsystem: NVIDIA Corporation GF114 HDMI Audio Controller [10de:0895]
Physical Slot: 5
Flags: fast devsel, IRQ 7, IOMMU group 29
Memory at ce080000 (32-bit, non-prefetchable) [disabled] [size=16K]
Capabilities: <access denied>
Kernel driver in use: vfio-pci
Kernel modules: snd_hda_intel

当即启用

当即启用其实就是更改设备当前的驱动。更改之前需要关闭所有用到目的显卡的软件例如 xserver 以及 nVidia 相关的服务。先进入到其他的控制台例如 Ctrl + Alt + F2 进入 tty2,之后关闭对应的服务,更换驱动。

或许还需要卸载掉 nVidia 相关的驱动,可以参考这个链接的问题:VGA passthrough with QEMU and KVM - Nothing on screen

更换驱动需要知道当前设备在使用什么驱动。可以通过 ls -nnv 查看,例如我的 NVS 450:

1
2
3
4
5
6
7
8
9
10
11
07:00.0 3D controller [0302]: NVIDIA Corporation G98 [Quadro NVS 450] [10de:06fa] (rev a1)
Subsystem: NVIDIA Corporation G98 [Quadro NVS 450] [10de:0619]
...
Kernel driver in use: nouveau
Kernel modules: nvidiafb, nouveau

08:00.0 VGA compatible controller [0300]: NVIDIA Corporation G98 [Quadro NVS 450] [10de:06fa] (rev a1) (prog-if 00 [VGA controller])
Subsystem: NVIDIA Corporation G98 [Quadro NVS 450] [10de:0619]
...
Kernel driver in use: nouveau <<< 这个
Kernel modules: nvidiafb, nouveau

它正在使用 nouveau,且需要记下显卡的设备物理地址(也就是 bus id),这里同时带了 3D controller,所以是 07:00.008:00.0

然后到 /sys/bus/pci/drivers 下,进入驱动名称的文件夹。我这里是 nueveau 在用显卡,所以完整路径为 /sys/bus/pci/drivers/nueveau。可以看到以上 id 都在这个文件夹下:

1
2
cattenlinger@Z420:/sys/bus/pci/drivers/nouveau$ ls
0000:07:00.0 0000:08:00.0 bind module new_id remove_id uevent unbind

需要做的就是解绑驱动,给 unbind 这个文件喂对应的 ID:

1
2
3
cd /sys/bus/pci/drivers/nueveau
echo 0000:07:00.0 > ./unbind
echo 0000:08:00.0 > ./unbind

这样就能卸载驱动了。然后得挂上 vfio-pci 的驱动。进入 vfio-pci 的驱动文件夹,记下显卡的设备 ID,并喂给 new_id

1
2
3
cd /sys/bus/pci/drivers/vfio-pci
# 第一个是 vendor id,第二个是 product id。
echo 10de 06fa > ./new_id

这样就挂上了。

把设备分配给 VM

我使用的是 QEMU,virt-manager 一样,只是换成了图形化界面。

给 QEMU 命令添加上这些个设备:

1
2
-device vfio-pci,host=04:00.0,multifunction=on,romfile="$FIRMWARE_DIR/vbios.bin"
-device vfio-pci,host=04:00.1

小插曲

如果显卡比较老(10XX 系列以下,amd 的我不熟不知道),还需要 vbios 文件,就是上面第一条命令的 romfile 参数。这个操作比较麻烦,可以参考这个连接:Preparation and placing of the ROM file。如果的确不方便用 Windows,也没法通过对应的 firmware upgrader 获取 vbios,可以尝试去 TECHPOWERUP - VGA VIOS 下载,但其实不建议,因为每台机子的 vbios 可能都不太相同。

如果不方便用 GPU-Z ,也可以用 CPU-Z。CPU-Z 出来的文件是 ASCII 的 dump,还需要手动转换为 binary。这里建议使用这个命令:

1
xxd -r -p vbios.txt vbios.bin

我的显卡就比较老,所以需要一个 vbios 来让 OVMF 去驱使显卡。

听说 N 卡比较难伺候,即便 Passthrough 进去了也很大可能用不了,我就一时能用一时不能,老显卡的话建议 AMD。

评论