KVM+PCIE Passthrough 虚拟化Windows进行游戏云

2022-11-18

需求背景

我们可以在一台电脑上运行多个操作系统,比如可以同时安装一个Windows和一个Linux在某一硬件设备上,但是这样做有个缺点,双系统无法同时运行,对于希望用Linux作为日常应用,但是需要使用Windows玩游戏的人来说,切换系统并不友好。 因此出现许多种在Linux上运行Windows应用程序的方案,诸如Wine(好像也没别的办法了)或者虚拟机,但是Wine用来运行游行往往是有非常多问题,所以要想有一个完美的游戏运行环境,还是需要虚拟机。 出于虚拟化的原因虚拟机的图形性能并不好,解决这一点就需要进行PCIE直通,讲图形设备直接分配给虚拟机来使用。

实验设备

硬件: CPU: Intel Core i7 12700F GPU: RTX 3060Ti (client) /RX550 (host) 软件: OS: Windows 10 (client) /Fedora Workstation 36 (host) VM: KVM/QEMU

实验原理

建立Windows虚拟机,并将PCIE显卡直通给虚拟机使用,以达到图形性能充足的Windows虚拟机。

操作步骤

开启VT-d

PCIE直通需要处理器的I/O虚拟化支持,这项功能需要在BIOS中进行开启,其中Intel称为VT-d,AMD称为IOMMU,参见这里。不同的BOIS需要不同的开启方式,需要自行搜索。

开启Linux IOMMU系统功能

上面开启了硬件功能,但是操作系统的IOMMU还没有开启,因为内核参数CONFIG_INTEL_IOMMU_DEFAULT_ON在linux中并未启用。(当然,AMD默认开启了,AMD Yes)。开启方法为这里或者参考下面: 1、编辑 /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="nouveau.modeset=0 rd.driver.blacklist=nouveau nomodeset rhgb quiet xhci-hcd.quirks=262144 kvm-intel.nested=1 intel_iommu=on"
GRUB_DISABLE_RECOVERY="true"

2、重新生成引导配置

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

3、重启 执行reboot就好。 4、关于如何验证IOMMU是否开启,可以返回这里参考下。

隔离(分离)GPU

在参考了多种方案之后,我发现实现GPU隔离似乎有两种方案:

方案一:使用vfio

1、使用脚本打印出IOMMU Group(这一部分摘抄自这篇博客):

#!/bin/bash
shopt -s nullglob
for g in $(find /sys/kernel/iommu_groups/* -maxdepth 0 -type d | sort -V); do
    echo "IOMMU Group ${g##*/}:"
    for d in $g/devices/*; do
        echo -e "t$(lspci -nns ${d##*/})"
    done;
done;

一般输出为:

IOMMU Group 1:
       00:01.0 PCI bridge: Intel Corporation Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port [8086:0151] (rev 09)
IOMMU Group 2:
   00:14.0 USB controller: Intel Corporation 7 Series/C210 Series Chipset Family USB xHCI Host Controller [8086:0e31] (rev 04)
IOMMU Group 4:
       00:1a.0 USB controller: Intel Corporation 7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 [8086:0e2d] (rev 04)
IOMMU Group 10:
   00:1d.0 USB controller: Intel Corporation 7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 [8086:0e26] (rev 04)
IOMMU Group 13:
       06:00.0 VGA compatible controller: NVIDIA Corporation GM204 [GeForce GTX 970] [10de:13c2] (rev a1)
        06:00.1 Audio device: NVIDIA Corporation GM204 High Definition Audio Controller [10de:0fbb] (rev a1)

2、添加vfio配置到 /etc/modprobe.d/vfio.conf:

softdep nouveau pre: vfio-pci
options vfio-pci ids=8086:0151,8086:0e31,10de:13c2

3、重新生成启动镜像:

sudo update-initramfs -u
方案二 直接禁用物理机显卡驱动,感觉方式比较暴力,参考这篇博客,

1、查找显卡硬件ID和对应驱动:

lspci -nn | grep NVIDIA                          查看ID
lspci -vv -s 03:00.0 | grep driver      查看驱动

2、禁用显卡驱动: 编辑 vim /etc/modprobe.d/blacklist.conf

blacklist nouveau
blacklist snd_hda_intel
blacklist xhci_hcd
blacklist nvidia-gpu

3、重启机器并解除显卡绑定:

modprobe pci_stub
echo " 10de 1e84" >/ sys/bus/pci/drivers/pci-stub/new_id
echo 0800:03:08.0 > /sys/bus/pci/devices/e888:03:00.B/driver/unbindecho eeee:03:06.0 >/sys/bus/pci/drivers/pci-stub/bind
echo " 1ode 10f8”>/sys/bus/pci/drivers/pci-stub/new_id
echo e800:03:00.1 >/sys/bus/pci/devices/0880:03:00.1/driver/unbindecho 8e80:03:0e.1 > i sys/bus/ pci/drivers/pci-stub/bind
echo " 1ede 1ad8" >/sys/bus/pci/drivers/pci-stub/new_id
echo 0800:03:00.2 >/sys/bus/pci/devices/0800:03:00.2/driver/unbindecho 0ee0:63:86.2 >/ sys/bus/pci/drivers/pci-stub/bind
echo " 1ede 1ad9" >/sys/bus/pci/drivers/pci-stub/new_id
echo e800:03:00.3 > /sys/bus/pci/devices/ee80:03:00.3/driver/unbindecho 8ee0:03:00.3 > lsys/bus/pci/drivers/pci-stub/bind

安装虚拟机

可以选择使用OVMF或者不使用,两者在此环境下均可通过PCIE Passthrough。两种方式在网络上均可查找到。需要注意的是一定要使用virtio,可以提升IO效率。 具体步骤(非OVMF)可以参考Winapps的安装教程,这里不是重点,所以不进行赘述。

添加PCIE设备

现在就可以正常使用QEMU的GUI来添加宿主机直通设备了,一定要选择显卡所有相关设备进行穿透,一般会包括一个显示设备和一个声音设备。 也可以手动添加xml,类似于:

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

CPU性能优化(绑定CPU)

这里可以参考这篇博客,具体要参考自己的CPU核心数量来进行调整优化,核心xml类似于:

  <vcpu placement='static' cpuset='8-15'>8</vcpu>
  <cputune>
    <vcpupin vcpu='0' cpuset='8'/>
    <vcpupin vcpu='1' cpuset='9'/>
    <vcpupin vcpu='2' cpuset='10'/>
    <vcpupin vcpu='3' cpuset='11'/>
    <vcpupin vcpu='4' cpuset='12'/>
    <vcpupin vcpu='5' cpuset='13'/>
    <vcpupin vcpu='6' cpuset='14'/>
    <vcpupin vcpu='7' cpuset='15'/>
    <emulatorpin cpuset='9'/>
  </cputune>

内存优化(禁用内存Ballon)

在虚拟机的设备XML中,添加:

<memballoon model='none'>

AntiCheat,反游戏虚拟机检测

这里有两个方案: 方案一:禁用虚拟机CPU的hypervisor特性,可以参考这篇博客,关键代码为 在cpu段落中,将mode设置为host-passthrough

<feature policy="disable" name="hypervisor"/>

在os段落

<smbios mode="host"/>

在features段落

<hyperv>
  <vendor_id state="on" value="random"/>
</hyperv>
<kvm>
  <hidden state="on"/>
</kvm>

方案二:在Windows客户机中开启hyper-v,这个方案可能需要条件,但是我没有研究透,我的直接就能开启,可能需要的条件可以参考这篇博客,Windows开启hyper-v可以参考这篇博客

转接客户机视频输出

这里存在多种方案,常见的方案这里简单讲两种。 方案一:如果仅仅为了玩游戏的话,可以使用NVIDIA SHIELD来进行游戏串流,而宿主机使用Moonlight客户端来接受串流,这种操作非常简单方便,如果仅仅是玩游戏体验还是非常不错的。具体教程可以参考这篇博客,关于网络设置的部分不需要关心,直接填写虚拟机的ip地址就可以了。 方案二:使用looking-glass转接显卡输出,这个方案比较复杂,不过阿里的无影云桌面好像也是采用的这个方案~~(感觉就是抄的looking-glass)~~,需要在host与client双机上都安装客户端,然后在虚拟机的xml中添加shmem设备,具体流程可以参考官方安装步骤,需要特别指出的是,似乎需要将默认显卡删除掉之后才可以正常使用,也就是仅仅留下PCIE穿透的那张显卡,此时启动虚拟机是不能在宿主机看到显示输出的(除非使用looking-glass)。

实验效果

经过一个国庆假期的折腾,最后的效果是可以相当流畅的运行Genshin Impact了,显示效果相当完美,但是鼠标的IO总感觉会有延迟,这点可能需要进一步优化,看阿里的云桌面感觉延迟已经相当低了,kvm的可定制化实在过于强大,我时间有限,就没有进行进一步的优化。

结论

通过不断地尝试,最终还是成功在Linux上创建Windows游戏云主机,并取得了不错的效果,但是虚拟机玩游戏还是感觉怪怪的,并且不符合游戏的玩家规范,所以最终还是选择了双系统。但这个实验在技术范畴上算是成功了。