Intel GVT-g
Intel GVT-g 是一项为 Intel GPU(Broadwell 及更新型号)提供中介设备直通的技术。它可以用于为多个客户虚拟机虚拟化 GPU,从而在虚拟机中有效地提供接近原生的图形性能,同时仍然允许您的主机正常使用虚拟化的 GPU。如果您希望在没有专用 GPU 的超极本上运行的 Windows 虚拟机中获得加速图形,而无需 完全设备直通,这将非常有用。(NVIDIA 和 AMD GPU 也存在类似的技术,但它们仅在“专业”GPU 产品线中提供,如 Quadro、Radeon Pro 等。)
还有一种称为 GVT-d 的技术变体 - 它本质上是 Intel 对使用 vfio-pci 驱动程序的完全设备直通的称呼。使用 GVT-d,主机无法使用虚拟化的 GPU。
先决条件
Intel GVT-g 目前仅适用于 Intel Broadwell(第 5 代)到 Comet Lake(第 10 代),这是因为 i915 驱动程序缺少对 Ice Lake(第 10 代移动处理器)、Rocket Lake(第 11 代桌面处理器)及更新型号的支持。有关详细信息,请参阅此 Intel 支持帖子 和此 Github Issue。
目前,Ice Lake 仅支持 GVT-d。对于基于 Xe 架构 (Gen12) 的 GPU,则需要 SR-IOV 功能。有关更多详细信息,请参阅 QEMU/Guest graphics acceleration#SR-IOV。
您必须先创建一个虚拟 GPU,然后将其分配给您的虚拟机。带有虚拟 GPU 的客户机将其视为“常规”GPU - 只需安装最新的原生驱动程序即可。(虚拟 GPU 实际上确实需要专门的驱动程序才能正常工作,但最新的上游 Linux/Windows 驱动程序中已存在所有必需的更改。)
您将需要:
- 使用至少 Linux 4.16 和 QEMU 2.12。
- 通过将
intel_iommu=on
添加到您的 内核参数 来启用 IOMMU。 - 启用 内核模块:
kvmgt
、vfio-iommu-type1
和mdev
。 - 设置 i915 内核模块参数
enable_gvt=1
以启用 GPU 虚拟化。 - 将
i915.enable_guc=0
添加到 内核参数,请参阅 Intel graphics#Enable GuC / HuC firmware loading 中的警告。 - 通过运行
lspci -D -nn
找到您的 GPU 的 PCI 地址$GVT_PCI
(例如0000:00:02.0
)。 - 生成一个虚拟 GPU GUID(以下命令中的
$GVT_GUID
),您将使用它来创建和分配虚拟 GPU。单个虚拟 GPU 只能分配给单个虚拟机 - 根据您想要的虚拟 GPU 数量创建尽可能多的 GUID。(您可以通过运行uuidgen
来做到这一点。)
使用 i915.enable_gvt=1
标志重新启动后,您应该能够创建虚拟 GPU。
您可以像这样列出可以在您的系统上创建的虚拟 GPU 类型
$ ls /sys/devices/pci0000\:00/$GVT_PCI/mdev_supported_types
编号较小的类型 能够支持更高的分辨率、更大的视频内存分配,并且能够使用更多的 GPU 时间片。
可以像这样访问每种类型的功能描述
$ cat /sys/devices/pci0000\:00/$GVT_PCI/mdev_supported_types/$GVT_TYPE/description
选择您想要使用的类型 - 我们将在下面将其称为 $GVT_TYPE
。
使用您创建的 GUID 创建具有所选类型的虚拟 GPU
# echo "$GVT_GUID" > "/sys/devices/pci0000\:00/$GVT_PCI/mdev_supported_types/$GVT_TYPE/create"
您可以根据需要使用不同的 GUID 重复此操作多次。所有创建的虚拟 GPU 都将位于 /sys/bus/pci/devices/$GVT_PCI/
中 - 如果您想删除虚拟 GPU,您可以执行
# echo 1 > /sys/devices/pci0000\:00/$GVT_PCI/$GVT_GUID/remove
libvirt QEMU 钩子
使用 libvirt,可以libvirt QEMU 钩子 在机器启动时自动创建虚拟 GPU,并在机器停止时将其删除。将变量替换为您在上面找到的值,并将 DOMAIN 替换为机器的名称。
/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
不要忘记使文件 可执行,并引用每个变量值,例如 GVT_PCI="0000:00:02.0"
。您还需要重新启动 libvirtd 守护程序,以便它知道新的钩子。
启动时 systemd 服务
除了 QEMU 钩子之外,您还可以让 systemd 在启动时创建虚拟 GPU。只要虚拟机不使用虚拟 GPU,这就不依赖于 libvirt,并且似乎对主机上的 GPU 性能没有影响。
创建一个 bash 脚本,并将 echo 命令放在 先决条件 中确定的位置以创建虚拟 GPU。使其 可执行。确保非特权用户无法修改该脚本,因为它将在启动时以 root 身份运行。
现在 创建一个 systemd 服务 来运行该脚本,并为其提供以下属性
After=graphical.target Type=oneshot User=root
为虚拟机分配虚拟 GPU
如果您以普通用户身份运行 qemu
或 libvirtd
,它可能会抱怨某些路径 /dev/vfio/number
不可写。您需要使用 chmod
或 setfacl
为该帐户启用对该路径的写入访问权限。
QEMU CLI
要创建具有虚拟化 GPU 的虚拟机,请将此参数添加到 QEMU 命令行
-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$GVT_GUID
-enable-kvm
启用 KVM。libvirt
将以下设备添加到虚拟机定义的 devices
元素中
$ virsh edit vmname
... <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-pci' display='off'> <source> <address uuid=GVT_GUID/> </source> </hostdev> ...
将 GVT_GUID
替换为您的虚拟 GPU 的 UUID。
获取虚拟 GPU 显示内容
有几种可能的方法可以从虚拟 GPU 检索显示内容。
使用 DMA-BUF 显示
QEMU CLI
将 display=on,x-igd-opregion=on
添加到 -device vfio-pci
参数的末尾,例如
-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$GVT_GUID,display=on,x-igd-opregion=on
libvirt
首先,修改虚拟机定义的 XML 架构,以便我们可以稍后使用 QEMU 特定的元素。更改
$ virsh edit vmname
<domain type='kvm'>
为
$ virsh edit vmname
<domain xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' type='kvm'>
然后将此配置添加到 <domain>
元素的末尾,即,将此文本插入到结束标记 </domain>
的正上方
$ virsh edit vmname
... <qemu:override> <qemu:device alias="hostdev0"> <qemu:frontend> ... <qemu:property name="x-igd-opregion" type="bool" value="true"/> </qemu:frontend> </qemu:device> </qemu:override> ...
将 DMA-BUF 与 UEFI/OVMF 结合使用
如上所述,DMA-BUF 显示不适用于使用(未修改的)OVMF 的基于 UEFI 的客户机,因为它不会创建必要的 ACPI OpRegion,该 OpRegion 通过 QEMU 的非标准 fw_cfg 接口公开。有关此问题的详细信息,请参阅 此 OVMF 错误[死链接 2025-01-19 ⓘ]。
根据 此 GitHub 评论,OVMF 错误报告提出了几个解决问题的方案。可以:
- 补丁[死链接 2025-01-19 ⓘ] OVMF (详细信息[死链接 2025-01-19 ⓘ]) 以添加 Intel 特定的怪癖(最直接但不可向上游的解决方案);
- 补丁[死链接 2025-01-19 ⓘ] 主机内核 (详细信息[死链接 2025-01-19 ⓘ]) 以自动为虚拟 GPU 提供选项 ROM,其中包含基本相同的代码,但采用选项 ROM 格式;
- 从内核补丁中提取 OpROM (来源) 并将其作为覆盖提供给 QEMU。
我们将选择最后一个选项,因为它不涉及任何修补。(注意:如果链接和 存档 都失效,则可以手动从内核补丁中提取 OpROM。)
i915ovmfAUR 可用,它是已存档的 i915ovmfPkg 的 最新分支,它是当前用于 OVMF 的最先进的 GVT-g ROM,请参阅 此讨论。AUR 软件包将 UEFI rom 文件安装为 /var/lib/libvirt/qemu/drivers/i915ovmf.rom
。首次启动可能需要一段时间,尤其是当客户机操作系统需要安装驱动程序时,Windows 10 就是这种情况。
下载 vbios_gvt_uefi.rom
并将其放置在世界可访问的位置(我们将使用 /
作为示例)。
- 如果使用 SELinux,它将拒绝访问 .rom 文件,从而导致 QEMU 无法找到它的消息。
- 要安全地授予对 .rom 文件的访问权限,而无需禁用 SELinux 或切换到被动模式,请将
vbios_gvt_uefi.rom
文件放在/var/lib/libvirt
中,然后以 root 身份运行restorecon -Rv /var/lib/libvirt
。然后 SELinux 将应用正确的标签,从而授予对 QEMU 的 svirt_t 策略的访问权限。
QEMU CLI
要指定 vBIOS ROM 文件,请将 ,romfile=/path/to/vbios_gvt_uefi.rom
附加到 -device vfio-pci,...
。
libvirt
然后编辑虚拟机定义,将此配置附加到我们之前添加的 <qemu:override>
元素
$ virsh edit vmname
... <qemu:override> <qemu:device alias="hostdev0"> <qemu:frontend> ... <qemu:property name="romfile" type="string" value="/path/to/vbios_gvt_uefi.rom"/> ... </qemu:frontend> </qemu:device> </qemu:override> ...
启用 RAMFB 显示(可选)
这应与上述 DMA-BUF 配置结合使用,以便也显示在客户机 Intel 驱动程序加载之前发生的所有事情(即 POST、固件界面和客户机初始化)。
QEMU CLI
将 ramfb=on,driver=vfio-pci-nohotplug
添加到 -device vfio-pci
参数的末尾,例如
-device vfio-pci,sysfsdev=/sys/bus/mdev/devices/$GVT_GUID,display=on,x-igd-opregion=on,ramfb=on,driver=vfio-pci-nohotplug
libvirt
首先,按照 本节 的第一步修改 XML 架构。
然后将此配置添加到 <domain>
元素的末尾,即,将此文本插入到结束标记 </domain>
的正上方
$ virsh edit vmname
... <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> ...
显示虚拟 GPU 输出
由于 spice-gtk 的 问题,配置因 SPICE 客户端 EGL 实现而异。
使用 QEMU GTK 显示输出
至少对于 Windows 客户机而言,此方法将为您提供比弱 CPU 上的 SPICE 显示更高的刷新率和更少的延迟/输入延迟。此外,它的 CPU 密集程度低于 Looking Glass。但是您会失去有用的 SPICE 显示功能,例如:
- 共享剪贴板(可以重新获得,请参阅 QEMU#qemu-vdagent)
- 实时 USB 重定向(您需要在启动客户机之前分配 USB 设备)
- 鼠标光标可以自由进出虚拟机(将被捕获,除非您使用 USB 平板电脑输入设备)
- 将显示输出集成到 virt-manager 中(将为 GTK 显示生成单独的窗口)
只有当客户机启动其正确的 Intel GPU 驱动程序(通常在登录屏幕上)时,显示输出才会开始工作。这意味着:
- 最好预先安装 Intel GPU 驱动程序,或使用不同的虚拟显示适配器(如 -vga std 或 libvirt)与 Intel vGPU 一起安装 Intel GPU 驱动程序,然后删除 std 视频适配器。
- 您永远不会看到操作系统启动,如果它在登录之前崩溃,您需要切换到不同的虚拟显示适配器。
- 如果您需要访问 BIOS,则需要 启用 RAMFB 显示。
Ctrl+Alt+G
捕获或释放鼠标光标,Ctrl+Alt+F
在窗口和全屏模式之间切换。QEMU CLI
将 -display gtk,gl=on
添加到命令行。可以通过添加 -vga none
来禁用 QEMU VGA 适配器,或者您有两个虚拟屏幕,连接到 QEMU VGA 适配器的那个屏幕是空白的。
libvirt
- 确保上面添加的
<hostdev>
设备已将display
属性设置为'off'
。 - 确保您已将虚拟行
xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'
添加到您的domain
(来自步骤 使用 DMA-BUF 显示)。 - 删除所有
<graphics>
和<video>
设备。
QEMU GTK 显示窗口需要被告知它应该使用哪个显示输出运行 OpenGL。在笔记本电脑上,首先断开所有外部显示器,以便您只有一个笔记本电脑屏幕作为显示器。通过粘贴到终端来获取 GPU 输出到的显示器的编号:echo $DISPLAY
。一个示例是 :0
。您现在可以重新连接您之前使用的任何显示器。将您刚刚确定的数字插入到下面的 env name='DISPLAY'
行中。
- 添加以下 QEMU 命令行参数
$ virsh edit vmname
... <qemu:commandline> <qemu:arg value="-display"/> <qemu:arg value="gtk,gl=on,zoom-to-fit=off"/> <qemu:env name="DISPLAY" value=":0"/> </qemu:commandline> <qemu:override> <qemu:device alias="hostdev0"> <qemu:frontend> <qemu:property name="display" type="string" value="on"/> ... </qemu:frontend> </qemu:device> </qemu:override> ...
缩放
在窗口模式下,-display gtk,gl=on,zoom-to-fit=off
使 GTK 显示窗口大小与客户机显示的分辨率一样大,这为您提供 1:1 像素纵横比。打开此选项或将其忽略会使客户机显示拉伸/收缩到 GTK 窗口的实际大小(难看的缩放)。
在全屏模式下,无论如何您都会获得缩放,并且当您更改客户机分辨率时,缩放仅在降低分辨率时更新。当您增加分辨率时,图像会变得比您的显示器更大,因此您需要退出全屏并再次进入。
GTK 显示 CPU 负载
这是将客户机帧缓冲区复制到 GTK 显示窗口的方法之间的权衡。
用 gl=es
代替 gl=on
会将复制客户机帧缓冲区的任务委托给 GPU(通过 DMA)。
这降低了 GTK 显示的 CPU 负载,并且可以产生响应更快的客户机,尤其是在弱 CPU 上。
当此帧缓冲区复制操作与加载 GPU 的应用程序共享 GPU 资源时,显示的 FPS 可能会下降,并且很可能发生卡顿(显示的帧之间的时间间隔不规则)。
使用 SPICE 与 MESA EGL 输出
QEMU CLI
将 -display spice-app,gl=on
添加到命令行。必须安装 virt-viewer
。
libvirt
- 确保上面添加的
<hostdev>
设备已将display
属性设置为'on'
。 - 删除所有
<graphics>
和<video>
设备。 - 添加以下设备
$ virsh edit vmname
... <graphics type='spice'> <listen type='none'/> <gl enable='yes'/> </graphics> <video> <model type='none'/> </video> ...
在 gl
标记中有一个可选属性 rendernode
,用于允许指定渲染器,例如
<gl enable='yes' rendernode='/dev/dri/by-path/pci-0000:00:02.0-render'/>
使用 SPICE 与 NVIDIA EGL 或 VNC 输出
libvirt
- 确保上面添加的 <hostdev> 设备已将 display 属性设置为 'on'。
- 删除所有 <graphics> 和 <video> 设备。
- 添加以下设备
$ virsh edit vmname
... <graphics type='spice' autoport='yes'> <listen type='address'/> </graphics> <graphics type='egl-headless'/> <video> <model type='none'/> </video> ...
可以将 <graphics type='spice'>
类型更改为 'vnc' 以使用 VNC 代替。
此外,在 <graphics type='egl-headless'> 标记内有一个可选标记 <gl>,用于强制使用特定的渲染器,不要将其放在 'spice' 图形中,因为提到了错误,例如
<graphics type='egl-headless'> <gl rendernode='/dev/dri/by-path/pci-0000:00:02.0-render'/> </graphics>
禁用所有输出
如果禁用所有输出,则查看显示输出的唯一方法是使用 RDP、VNC 或 Looking Glass 等软件服务器。有关详细信息,请参阅 PCI passthrough via OVMF#Using Looking Glass to stream guest screen to the host。
QEMU CLI
在 -device vfio-pci
参数中,删除 ramfb=on
并更改为 display=off
。添加 -vga none
以禁用 QEMU VGA 适配器。
libvirt
为了确保不添加模拟 GPU,可以编辑虚拟机配置并进行以下更改
- 删除所有 <graphics> 设备。
- 将 <video> 设备的类型更改为 'none'。
- 确保上面添加的 <hostdev> 设备已将
display
属性设置为 'off'。
故障排除
缺少 mdev_supported_types 目录
如果您已按照说明添加了 i915.enable_gvt=1
内核参数,但仍然没有 /sys/bus/pci/devices/0000:02:00.0/mdev_supported_types
目录,请首先仔细检查是否已加载 kvmgt
模块。
您还应该检查您的硬件是否受支持。检查 dmesg 的输出以查找此消息
# dmesg | grep -i gvt
[ 4.227468] [drm] Unsupported device. GVT-g is disabled
如果是这种情况,您可能需要向上游检查支持计划。例如,对于“Coffee Lake”(CFL) 平台的支持,请参阅 https://github.com/intel/gvt-linux/issues/53
Windows 挂起并出现错误内存错误
如果 Windows 由于错误内存错误而挂起,请通过 dmesg 查找更多详细信息。如果主机内核日志显示类似 rlimit memory exceeded 的内容,您可能需要增加 Linux 允许 QEMU 分配的最大内存。假设您在 kvm
组中,请将以下内容添加到 /etc/security/limits.d/42-intel-gvtg.conf
并重新启动系统。
# qemu kvm, need high memlock to allocate memory for vga-passthrough @kvm - memlock 8388608
将 Intel GVT-G 与 PRIME render offload 结合使用
在主机上使用 Intel GVT-G 的同时使用 NVIDIA 的 PRIME render offload 会在客户机上导致一些 问题。建议使用 bbswitch 来保持显卡断电,或将其与 Bumblebee、nvidia-xrun 或 optimus-manager 结合使用。
无显示
如果您的虚拟机在使用 RAMFB 显示时未显示任何内容,请尝试将以下附加选项添加到现有的 <qemu:commandline> 标记
$ virsh edit vmname
... <qemu:commandline> <qemu:arg value="-set"/> <qemu:arg value="device.hostdev0.display=on"/> </qemu:commandline> ...
qemu: eglCreateImageKHR failed
。降级 到 8.0.4 以进行临时修复。图形错乱
如果您的虚拟机在鼠标进入虚拟机屏幕时显示伪影,则以下 解决方法 可能会奏效。
首先,按照 #libvirt 2 上所示修改 XML 架构。
然后,将此插入到结束标记 </domain> 的正上方,注意添加到现有的 <qemu:commandline> 标记(如果存在)
$ virsh edit vmname
... <qemu:commandline> <qemu:env name="MESA_LOADER_DRIVER_OVERRIDE" value="i965"/> </qemu:commandline> ...
尝试挂起时主机挂起
创建 GVT-g 虚拟 GPU 后,主机在尝试挂起时可能会挂起。请参阅 github 以跟踪此错误。
一种解决方法是在挂起之前删除创建的 GVT-g 虚拟 GPU,并在从挂起唤醒后重新创建 GVT-g 虚拟 GPU。您可以安装 gvtg_vgpu-gitAUR 软件包来自动为您执行此操作。
更改虚拟 GPU 的显示分辨率
默认情况下,vGPU 的显示分辨率是 vGPU 能够达到的最大分辨率。无论客户机操作系统设置什么分辨率,vGPU 都会将显示内容缩放到此分辨率。这将在查看器中产生质量较差的图片。
要更改显示分辨率,请将 xres
和 yres
配置附加到 <qemu:override>
元素中
$ virsh edit vmname
... <qemu:override> <qemu:device alias="hostdev0"> <qemu:frontend> ... <qemu:property name="xres" type="unsigned" value="800"/> <qemu:property name="yres" type="unsigned" value="600"/> ... </qemu:frontend> </qemu:device> </qemu:override> ...