硬件环境确认

首先要确定主板和 CPU 都支持虚拟化技术,在 BIOS 将 VT-d、VT-x 设置成 enable。
以 Intel 为例:
VT:  Intel Virtualization Technology
VT-d:  Intel VT for Directed I/O

软件环境配置

开启 iommu

关于 iomuu 和 vfio 参考这里:VFIO 简介
使用如下命令确认是否支持 iommu

cat /proc/cmdline | grep iommu

如果没有输出,说明不支持 iommu,需要手动开启。编辑/etc/default/grub,在 GRUB_CMDLINE_LINUX 中添加 intel_iommu=on 配置项:

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="intel_iommu=on crashkernel=auto rhgb quiet"
GRUB_DISABLE_RECOVERY="true"

更新 grub,并重启设备:

grub2-mkconfig -o /boot/grub2/grub.cfg
shutdown -r now

重启以后如下确认是否正常:

#使用下面命令
dmesg | grep -i iommu
#如果输出类似下面的内容,说明正确
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-3.10.0-862.6.3.el7.x86_64 root=UUID=4e83b2b5-5ff1-4b1b-af0f-3f6a7f8275ea ro intel_iommu=on crashkernel=auto rhgb quiet
[    0.000000] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-3.10.0-862.6.3.el7.x86_64 root=UUID=4e83b2b5-5ff1-4b1b-af0f-3f6a7f8275ea ro intel_iommu=on crashkernel=auto rhgb quiet
[    0.000000] DMAR: IOMMU enabled
[    0.257808] DMAR-IR: IOAPIC id 3 under DRHD base  0xfbffc000 IOMMU 0
[    0.257810] DMAR-IR: IOAPIC id 1 under DRHD base  0xd7ffc000 IOMMU 1
[    0.257812] DMAR-IR: IOAPIC id 2 under DRHD base  0xd7ffc000 IOMMU 1

确认虚拟化支持

egrep -c '(vmx|svm)' /proc/cpuinfo

如果输入非 0,则说明支持

配置 vfio

检查当前显卡设备信息

lspci -nn
......
02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [GeForce GTX 1080 Ti] [10de:1b06] (rev a1)
02:00.1 Audio device [0403]: NVIDIA Corporation GP102 HDMI Audio Controller [10de:10ef] (rev a1)
03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [GeForce GTX 1080 Ti] [10de:1b06] (rev a1)
03:00.1 Audio device [0403]: NVIDIA Corporation GP102 HDMI Audio Controller [10de:10ef] (rev a1)
05:00.0 PCI bridge [0604]: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge [1a03:1150] (rev 03)
06:00.0 VGA compatible controller [0300]: ASPEED Technology, Inc. ASPEED Graphics Family [1a03:2000] (rev 30)

可以看到,其实我的这台设备上有三个 vga 设备,两个 1080Ti,一个 ASPEED(这是服务器经常见到的集成显卡),1080Ti 显卡同时带了声卡

确认设备使用的驱动

lspci -vv -s 02:00.0 | grep driver
	Kernel driver in use: nvidia
lspci -vv -s 02:00.1 | grep driver
	Kernel driver in use: snd_hda_intel
lspci -vv -s 03:00.0 | grep driver
	Kernel driver in use: nvidia
lspci -vv -s 03:00.1 | grep driver
	Kernel driver in use: snd_hda_intel
lspci -vv -s 06:00.0 | grep driver
	Kernel driver in use: ast

可以看到,两块 N 卡使用的驱动为 nvidia(显卡驱动)和 snd_hda_intel(声卡驱动),ASPEED 使用的是 ast 驱动

配置 vfio

为了保证设备不被宿主机使用,我们建议将上面查到的两个驱动禁用,编辑/etc/modprobe.d/blacklist.conf,添加如下配置(似乎并不需要此步骤):

blacklist nvidia
blacklist snd_hda_intel

配置加载 vfio-pci 模块,编辑/etc/modules-load.d/vfio-pci.conf,添加如下内容:

vfio-pci

配置使用 vfio 驱动的设备(这里的设备就是上面我们查到的设备的)编辑/etc/modprobe.d/vfio.conf,添加如下配置:

# GTX 1080Ti and its audio controller
options vfio-pci ids=10de:1b06,10de:10ef

重新生成内核

重新生成内核文件之前,我们先备份已有的:

mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r)-nouveau.img

重新生成:
dracut /boot/initramfs-$(uname -r).img $(uname -r)

重启设备

shutdown -r now

验证 vfio 是否生效

查看启动信息,确认 vfio 模块是否加载:

dmesg | grep -i vfio
[    6.755346] VFIO - User Level meta-driver version: 0.3
[    6.803197] vfio_pci: add [10de:1b06[ffff:ffff]] class 0x000000/00000000
[    6.803306] vfio_pci: add [10de:10ef[ffff:ffff]] class 0x000000/00000000

查看配置以后,显卡使用的驱动:

lspci -nnk -d 10de:1b06

02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [GeForce GTX 1080 Ti] [10de:1b06] (rev a1)
	Subsystem: ZOTAC International (MCO) Ltd. Device [19da:1470]
	Kernel driver in use: vfio-pci
	Kernel modules: nouveau, nvidia_drm, nvidia
03:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [GeForce GTX 1080 Ti] [10de:1b06] (rev a1)
	Subsystem: ZOTAC International (MCO) Ltd. Device [19da:1470]
	Kernel driver in use: vfio-pci
	Kernel modules: nouveau, nvidia_drm, nvidia

从 Kernel driver in use 的结果可以看出,vfio 已经加载成功。

配置虚拟机使用宿主机的 GPU

配置之前,我们首先需要查看 pci 设备的 pci 标识,virsh nodedev-list 可以列出宿主机设备的所有 pci 设备的标识,结果如下:

virsh nodedev-list | grep pci | grep 03
pci_0000_00_03_0
pci_0000_03_00_0
pci_0000_03_00_1

查看 pci_0000_03_00_0 设备的详细信息:

#使用下面的命令查看
virsh nodedev-dumpxml pci_0000_03_00_0
#输出结果为如下
<device>
  <name>pci_0000_03_00_0</name>
  <path>/sys/devices/pci0000:00/0000:00:03.0/0000:03:00.0</path>
  <parent>pci_0000_00_03_0</parent>
  <driver>
    <name>vfio-pci</name>
  </driver>
  <capability type='pci'>
    <domain>0</domain>
    <bus>3</bus>
    <slot>0</slot>
    <function>0</function>
    <product id='0x1b06'>GP102 [GeForce GTX 1080 Ti]</product>
    <vendor id='0x10de'>NVIDIA Corporation</vendor>
    <iommuGroup number='47'>
      <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
      <address domain='0x0000' bus='0x03' slot='0x00' function='0x1'/>
    </iommuGroup>
    <numa node='0'/>
    <pci-express>
      <link validity='cap' port='0' speed='8' width='16'/>
      <link validity='sta' speed='8' width='16'/>
    </pci-express>
  </capability>
</device>

从结果中,我们可以看到,该设备在宿主机中的硬件信息为:

<iommuGroup number='47'>
  <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
  <address domain='0x0000' bus='0x03' slot='0x00' function='0x1'/>
</iommuGroup>

编辑虚拟机配置文件(virsh edit xxxx),在 devices 代码块中增加如下内容:

<hostdev mode='subsystem' type='pci' managed='yes'>
  <driver name='vfio'/>
  <source>
    <address domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
  <driver name='vfio'/>
  <source>
    <address domain='0x0000' bus='0x06' slot='0x00' function='0x1'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</hostdev>

重启虚拟机:

virsh destroy xxx;virsh start xxx

进入系统以后,可以使用 lspci 查看虚拟机的 pci 设备,会发现设备已经在虚拟机中显示

可能问题解决

ERROR CODE 43(尚未验证)

#编辑VM XML
virsh edit win

#修改第一行
<domain type='kvm'>
#为
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>

#找到<features>节点,添加内容
<features>
...
  <kvm>
    <hidden state='on'/>
  </kvm>
...
</features>

#找到</device>节点,在device后添加内容
<qemu:commandline>
  <qemu:arg value='-cpu'/>
  <qemu:arg value='host,hv_time,kvm=off,hv_vendor_id=null'/>
</qemu:commandline>

#重启VM
virsh destroy win
virsh start win

参考:GPU PassThrough in KVM
参考:kvm 显卡透传(centos)
参考:centos 7.3 下为虚拟机透传 GPU.md