GVT-g

Intel GVT-g是一项GPU虚拟化技术。 可以将一个Intel核心显卡划分为多个虚拟GPU,每个虚拟GPU可以分配给一个虚拟机。 提供接近原先显卡的性能。适合Windows和Linux的客户机,黑苹果没有成功驱动起来的案例。

适用CPU

适用于Intel Broadwell (5代) 到 Comet Lake (10代)的CPU,10代以后更换成了SR-IOV技术。

内核启用

内核增加intel_iommu=on,启用IOMMU。

内核启用kvmgt, vfio-iommu-type1 和 mdev 模块。

内核增加enable_gvt=1参数,开启GPU虚拟化。

内核增加i915.enable_guc=0参数,因为当 GuC/HuC 也启用时,不支持通过设置 enable_gvt=1 使用 GVT-g 图形虚拟化。

内核增加iommu=pt参数,只对穿透的设备启用IOMMU,可以避免宿主机的PCIe设备性能的转换浪费。此参数源码中的说明如下:

/*
 * This variable becomes 1 if iommu=pt is passed on the kernel command line.
 * If this variable is 1, IOMMU implementations do no DMA translation for
 * devices and allow every device to access to whole physical memory. This is
 * useful if a user wants to use an IOMMU only for KVM device assignment to
 * guests and not for driver dma translation.
 */
int iommu_pass_through __read_mostly = 1;

下面汇总,操作如下:

创建/etc/modules-load.d/intel-gvt-g.conf,写入

kvmgt
vfio-iommu-type1
mdev

修改内核参数以最常见的GRUB为例,编辑/etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt i915.enable_guc=0 i915.enable_gvt=1"

找到GRUB_CMDLINE_LINUX_DEFAULT,在原来可能是"quiet"或是"quiet splash"中加上新加的内核参数。 然后重新生成 grub.cfg 文件:

grub-mkconfig -o /boot/grub/grub.cfg

重启系统,使其生效。

可用类型

find /sys/devices -name "mdev_supported_types"

可能的路径是/sys/devices/pci0000:00/0000:00:02.0/mdev_supported_types,输出如下:

i915-GVTg_V5_1  # Video memory: <512MB, 2048MB>, resolution: up to 1920x1200
i915-GVTg_V5_2  # Video memory: <256MB, 1024MB>, resolution: up to 1920x1200
i915-GVTg_V5_4  # Video memory: <128MB, 512MB>, resolution: up to 1920x1200
i915-GVTg_V5_8  # Video memory: <64MB, 384MB>, resolution: up to 1024x768

可以看到最大分辨率为1920x1200,现在来看还是比较低的。

上述可用类型还和主板上分配给核显的内存大小有关。

创建虚拟GPU

echo "$GVT_GUID" > "/sys/devices/pci${GVT_DOM}/$GVT_PCI/mdev_supported_types/$GVT_TYPE/create"

$GVT_GUID可以用uuidgen命令生成一个随机的uuid。

移除虚拟GPU

echo 1 > /sys/devices/pci0000\:00/$GVT_PCI/$GVT_GUID/remove

qemu hook

使用qemu hook可以在虚拟机启动和关闭时自动创建和移除虚拟GPU。

创建/etc/libvirt/hooks/qemu,内容如下:

#!/bin/sh
GVT_PCI=<GVT_PCI>
GVT_GUID=<GVT_GUID>
MDEV_TYPE=<GVT_TYPE>
DOMAIN=<DOMAIN name>
if [ $# -ge 3 ]; then
    if [[ " $DOMAIN " =~ .*\ $1\ .* ]] && [ "$2" = "prepare" ] && [ "$3" = "begin" ]; then
        echo "$GVT_GUID" > "/sys/devices/pci0000:00/$GVT_PCI/mdev_supported_types/$MDEV_TYPE/create"
    elif [[ " $DOMAIN " =~ .*\ $1\ .* ]] && [ "$2" = "release" ] && [ "$3" = "end" ]; then
        echo 1 > "/sys/devices/pci0000:00/$GVT_PCI/$GVT_GUID/remove"
    fi
fi

变量位置根据自己电脑填上内容。最后别忘了给脚本加上可执行权限:

sudo chmod +x /etc/libvirt/hooks/qemu

分配虚拟GPU

最后在虚拟机的xml文件中加入如下内容:(加在所有其他hostdev之前,不然后面获取显示时hostdev0还要改)

    <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-pci' display='off'>
      <source>
        <address uuid=GVT_GUID/>
      </source>
    </hostdev>

GVT_GUID是之前创建虚拟GPU时的uuid。

获取显示

启用OpRegion,使虚拟机支持虚拟GPU。
修改虚拟机的xml配置,第一行改为如下内容:(否则后面的配置会保存不了)

<domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type='kvm'>

</domain>之前,加入如下内容:

  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
        ...
        <qemu:property name="x-igd-opregion" type="bool" value="true"/>
      </qemu:frontend>
    </qemu:device>
  </qemu:override>

UEFI

如果固件类型是UEFI的,需要使用修改版的vbios_gvt_uefi.rom才能支持显示内容。如果是BIOS类型的,可以跳过这一步。

固件可以放到/usr/share/vgabios/,随意放可能会提示找不到rom。下面是允许访问的目录:

  # access to firmware's etc
  /usr/share/kvm/** r,
  /usr/share/qemu/** r,
  /usr/share/qemu-kvm/** r,
  /usr/share/bochs/** r,
  /usr/share/openbios/** r,
  /usr/share/openhackware/** r,
  /usr/share/proll/** r,
  /usr/share/vgabios/** r,
  /usr/share/seabios/** r,
  /usr/share/misc/sgabios.bin r,
  /usr/share/ovmf/** r,
  /usr/share/OVMF/** r,
  /usr/share/AAVMF/** r,
  /usr/share/qemu-efi/** r,
  /usr/share/slof/** r,

然后修改xml,增加一行设置romfile:

  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
      ...
        <qemu:property name="romfile" type="string" value="/usr/share/vgabios/vbios_gvt_uefi.rom"/>
      ...
      </qemu:frontend>
    </qemu:device>
  </qemu:override>

之前的hostdev配置中display='off',在虚拟机里可以看到有显卡和显示器,但是虚拟机界面上看不到它输出的画面。 修改xml配置,override中把display改为on。并且将安装后自带的video改为None。

  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
      ...
        <qemu:property name="display" type="string" value="on"/>
      ...
      </qemu:frontend>
    </qemu:device>
  </qemu:override>
<video>
  <model type="none"/>
</video>

这样重启后,就可以看到虚拟机的画面了。但是看不到进入系统前的画面。

启用RAMFB,可以显示启动画面。修改xml配置:

  <qemu:override>
    <qemu:device alias="hostdev0">
      <qemu:frontend>
        ...
        <qemu:property name="driver" type="string" value="vfio-pci-nohotplug"/>
        <qemu:property name="ramfb" type="bool" value="true"/>
      </qemu:frontend>
    </qemu:device>
  </qemu:override>

至此,GVT-g的配置就完成了。