KVM多显卡直通指南

在虚拟化环境中,将物理显卡直接分配给虚拟机(即显卡直通)是提升虚拟机图形性能的重要手段。本文将详细介绍如何在Linux系统中实现KVM多显卡直通,特别适用于需要在虚拟机中获得接近原生性能的用户。

前提条件

  • 支持虚拟化的CPU(大多数现代CPU都支持)
  • 支持IOMMU的主板(Intel VT-d或AMD-Vi)
  • 至少一张可用于直通的独立显卡
  • 运行Linux的宿主机系统

详细步骤

1. 启用BIOS中的虚拟化功能

首先需要在主板BIOS中启用虚拟化功能。根据不同的主板和CPU,这个选项可能被称为:

  • Intel系统:VT-x和VT-d
  • AMD系统:AMD-V和AMD-Vi

注意:部分老旧CPU可能不支持这些功能

2. 在宿主机系统中启用IOMMU组

IOMMU(Input/Output Memory Management Unit)是实现设备直通的关键技术。我们需要在Linux系统中启用它:

  1. 打开GRUB配置文件:
1
sudo nano /etc/default/grub
  1. 找到GRUB_CMDLINE_LINUX_DEFAULT行,修改为:
1
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on"

注意:如果使用AMD处理器,请使用amd_iommu=on替代intel_iommu=on

  1. 更新GRUB配置:
1
2
3
4
5
# Arch Linux
sudo grub-mkconfig -o /boot/grub/grub.cfg

# Ubuntu
sudo update-grub
  1. 重启系统后,验证IOMMU是否正确启用:
1
dmesg | grep -i iommu

如果看到类似pci xxxx:xx:xx.x: Adding to iommu group xx的输出,说明IOMMU已成功启用。

重要说明:系统中的所有设备只能按照IOMMU组为单位分配给宿主机或虚拟机。如果两个不相关的设备在同一个组内,它们必须一起分配。Linux系统不支持预留IOMMU组,因此我们需要阻止Linux内核在启动时初始化特定组内的硬件。

3. 寻找需要预留的IOMMU组

  1. 首先确认显卡所在的PCIe总线和产品型号:
1
lspci -nnv | grep -i nvidia

输出示例:

1
2
3
4
5
6
7
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2203] (rev a1) (prog-if 00 [VGA controller])
Kernel driver in use: nvidia
Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
01:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
05:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2203] (rev a1) (prog-if 00 [VGA controller])
Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
05:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)

解读PCI设备信息

  • 01:00.001:00.1等编号通过冒号分为三部分:PCIe的domain ID、bus ID和device id.function id
  • [0300]是设备类型编号,不同类型设备(网络、存储、显卡等)有不同编号
  • [10de:2203]中,10de是NVIDIA的厂商ID,2203表示这是一个3090Ti显卡
  1. 找出设备所在的IOMMU组:
1
2
sudo dmesg | grep iommu | grep 01:00.0
sudo dmesg | grep iommu | grep 05:00.0

输出示例:

1
2
[    0.536462] pci 0000:01:00.0: Adding to iommu group 17
[ 0.536491] pci 0000:05:00.0: Adding to iommu group 20
  1. 确认IOMMU组中的所有设备:
1
sudo dmesg | grep "iommu group 17"

输出示例:

1
2
[    0.536462] pci 0000:01:00.0: Adding to iommu group 17
[ 0.536467] pci 0000:01:00.1: Adding to iommu group 17

4. 屏蔽显卡驱动

有两种方法可以屏蔽显卡,使其不被宿主机系统使用:

方法一:型号屏蔽法

适用于只有一张显卡或多张显卡ID不同的情况:

  1. 修改GRUB配置:
1
sudo nvim /etc/default/grub
  1. GRUB_CMDLINE_LINUX_DEFAULT中添加设备ID:
1
2
3
4
5
# 使用pci-stub方法
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on pci-stub.ids=10de:2203,10de:1aef"

# 或使用vfio-pci方法
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on vfio-pci.ids=10de:2203,10de:1aef"
  1. 更新GRUB并重启:
1
2
3
4
5
# Arch Linux
sudo grub-mkconfig -o /boot/grub/grub.cfg

# Ubuntu
sudo update-grub
  1. 验证屏蔽是否成功:
1
lspci -nnv | grep -E "(^\S|Kernel driver in use)" | grep 01:00 -A 1

成功屏蔽后的输出:

1
2
3
4
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2203] (rev a1) (prog-if 00 [VGA controller])
Kernel driver in use: vfio-pci
01:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
Kernel driver in use: vfio-pci

方法二:脚本屏蔽法

适用于多张相同型号显卡的情况:

  1. 创建VFIO脚本:
1
sudo nano /etc/initramfs-tools/scripts/init-top/vfio.sh
  1. 添加以下内容:
1
2
3
4
5
6
#!/bin/sh
echo "vfio-pci" > /sys/bus/pci/devices/0000:01:00.0/driver_override
echo "vfio-pci" > /sys/bus/pci/devices/0000:01:00.1/driver_override
#echo "0000:01:00.0" > /sys/bus/pci/drivers/vfio-pci/bind # 这里我没有加这个绑定,有人加了这两句,但我注释了,因为本地找不到这个文件夹
#echo "0000:01:00.1" > /sys/bus/pci/drivers/vfio-pci/bind # 可选,需要modprobe -i vfio-pci后才有此文件夹,但不加也能正常工作
exit 0
  1. 给脚本添加执行权限:
1
sudo chmod 744 /etc/initramfs-tools/scripts/init-top/vfio.sh
  1. 更新initramfs:
1
sudo update-initramfs -u -k all
  1. 重启系统后验证显卡驱动是否被vfio-pci接管

恢复显卡挂载:如需恢复显卡使用,只需注释掉脚本中的两行echo命令,刷新initramfs并重启即可。

5. 在KVM中使用直通显卡

完成以上步骤后,您可以在KVM虚拟机配置中添加PCI设备,选择已屏蔽的显卡,即可实现显卡直通。

参考资料