参考文献:
- Ubuntu配置GPU直传kvm虚拟机 - CodeAntenna
- KVM虚拟机GPU直通,step by step - 机械意志 (mechanical-consciousness.com)
- lspci的输出简单分析 - 成蹊0xc000 - 博客园 (cnblogs.com)
- PCI passthrough via OVMF - Arch Linux 中文维基 (archlinuxcn.org)
- Win10/11 如何开启 第二屏幕/副屏/虚拟显示器,让平板成为副屏 - 知乎 (zhihu.com)
首先声明,本文的背景是在两张同型号的3090ti中选择一张进行直通,在整个直通过程中,上面的参考文献给与了我很大的帮助,本篇内容是我基于他们描述完整操作后的记录,其中不乏对原文的直接摘抄,但也结合了我个人的机器情况进行了重新梳理,并进行了多次可行性验证,因为每个人的机器情况可能都大不相同,希望我的记录能给和我相同配置的同学一些帮助。如果有不懂的命令,建议大家查一下GPT,可能理解起来会快很多。
直通步骤:
1. 确定是否支持虚拟化:
确定本机是否支持VT-P虚拟化,一般需要主板BIOS开启VT-P。如果不能进BIOS确认,也可以在终端里确认。
检查CPU是否支持虚拟化,运行如下命令:
$ egrep -c '(svm|vm)' /proc/cpuinfo
如果显示为0,则不支持虚拟化。
系统配置:Ubuntu22.04.1 LTS,GPU 3090 Ti*2,CPU i7 13700K,主板 华硕Z790-P
2. 在宿主机系统中启用iommu组:
打开/etc/default/grub,找到GRUB_CMDLINE_LINUX_DE FAULT,修改为下面内容:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on"
接着我们需要更新grub:
$ sudo update-grub -u
然后重启电脑,重启完后我们需要检查iommu有没有被正确启用:
$ dmesg | grep -i iommu
如果你在输出中看到很多pci xxxx:xx:xx.x: Adding to iommu group xx
,那么恭喜你,第一步完成了。
系统中的所有设备只能按照iommu group为单位分配给宿主机或者虚拟机。因此如果你看到两个不相关的设备在一个group里,那也没招,你只能给这俩设备一起丢虚拟机里去。
很可惜的是,Linux系统并不支持预留iommu group。
iommu group是硬件实现上的分组。在Linux的实现中,只认得各个在总线上的硬件,并挨个挨个的启动起来。
我们要做的就是阻止Linux内核在启动时初始化某些组里的硬件。
3. 寻找我们需要预留的iommu组:
首先确认我们显卡所在的PCIE总线和产品型号:
$ lspci -nnv | grep -i nvidia
我的电脑上输出如下:
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)
可以看到输出分成两部分,编号加设备描述。01:00.0
和01:00.1
是编号通过冒号 “:” 又分成了三部分,第一个部分是PCIe的 domain ID,第二个部分是 bus ID,第三个部分是 device id.function id。PCI设备的组织形式是一个树形,这表示一个domain可以包含多个bus,一个bus又包含了多个device,一个device又包含多个function。
0300
是一个用来区分不同设备的编号,网络设备、存储设备、多媒体设备、显卡都有自己的编号。
10de:2203
这是一个厂商特有的编号,10de是 Nvidia的编号(Vendor ID),Nvidia所有的设备都是使用这一编号,2203 表示这是一个3090Ti显卡。
以上参考:lspci的输出简单分析 - 成蹊0xc000 - 博客园 (cnblogs.com),可以在这个博客里继续查找自己的显卡信息。
接下来我们需要找出我们的设备所在的iommu group:
$ sudo dmesg | grep iommu | grep 01:00.0
输出如下:
[ 0.536462] pci 0000:01:00.0: Adding to iommu group 17
$ sudo dmesg | grep iommu | grep 05:00.0
[ 0.536491] pci 0000:05:00.0: Adding to iommu group 20
确认iommu group中的所有设备:
$ sudo dmesg | grep "iommu group 17"
输出如下:
[ 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. 屏蔽显卡
4.1 型号屏蔽法:
如果只有一张显卡,或者两张显卡的ID不同,可以直接用pci-stub或者vfio-pci的方法来屏蔽即可,修改/etc/default/grub
:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on pci-stub.ids=10de:2203,10de:1aef"
或者
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on vfio-pci.ids=10de:2203,10de:1aef"
更新内核,并重启:
$ sudo update-grub -u
如果屏蔽成功,内核驱动就会显示如下:
$ lspci -nnv | grep -E "(^\S|Kernel driver in use)" | grep 01:00 -A 1
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
否则下面还是驱动被nvidia接管的状态(其实nvidia-smi就能看了):
$ lspci -nnv | grep -E "(^\S|Kernel driver in use)" | grep 01:00 -A 1
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:2203] (rev a1) (prog-if 00 [VGA controller])
Kernel driver in use: nvidia
01:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
Kernel driver in use: snd_hda_intel
4.2 脚本屏蔽法
当然如果两张显卡都是同一个型号,是不能用上面的方法屏蔽的。
这个时候我们要为显卡指定驱动:
$ sudo vim /etc/initramfs-tools/scripts/init-top/vfio.sh
脚本内容为:
#!/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
给脚本增加权限,否则无法执行:
$ sudo chmod 744 /etc/initramfs-tools/scripts/init-top/vfio.sh
完成后刷新initramfs,这个命令会执行上面的脚本:
$ sudo update-initramfs -u -k all
输出下面的结果,重启就可以正常屏蔽了。
update-initramfs: Generating /boot/initrd.img-6.2.0-31-generic
update-initramfs: Generating /boot/initrd.img-6.2.0-26-generic
重启配置成功后按照上一节的命令就能看到显卡的驱动被vfio-pci接管了,nvidia-smi也能发现显卡已经无法识别了。
如果想恢复显卡的挂载,就把脚本中的两行echo...
注释掉,并刷新initramfs
,重启机器即可。
4.3 开机脚本屏蔽法
有些地方用下面的方法成功了,但我试了没有效果,我把方法留在这里供大家尝试:
$ sudo vim /usr/bin/vfio-pci-override.sh
脚本的内容:
#!/bin/sh
if [ ! -z "$(ls -A /sys/class/iommu)" ]; then
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
fi
modprobe -i vfio-pci
再给脚本增加运行权限,否则启动时是不能运行的:
$ sudo chmod 744 /usr/bin/vfio-pci-override.sh
最后在/etc/modprobe.d/vfio.conf
内指定我们的脚本路径。
$ sudo vim /etc/modprobe.d/vfio.conf
install vfio-pci /usr/bin/vfio-pci-override.sh
Arch Linux的官方中文文档也有详细的解释,但实际操作和Ubuntu有差异,放在这里作为一个参考资料。PCI passthrough via OVMF - Arch Linux 中文维基 (archlinuxcn.org)。
因为用了 modprobe,所以 vfio 也能正常工作了,上面说的bind文件也能找到了。
$ lsmod | grep vfio
输出如下:
vfio_pci 16384 0
vfio_pci_core 94208 1 vfio_pci
vfio_iommu_type1 49152 0
vfio 61440 3 vfio_pci_core,vfio_iommu_type1,vfio_pci
iommufd 73728 1 vfio
irqbypass 16384 2 vfio_pci_core,kvm
5. 安装KVM虚拟机并配置显卡:
至此显卡已经被成功虚拟化,接下来就是加载到KVM之中,KVM的安装很简单,GUI界面的安装网上有很多资料,这里留下安装命令。
$ sudo apt-get install qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager ovmf
启用libvirt服务和开机自启
sudo systemctl start libvirtd.service
sudo systemctl enable libvirtd.service
如果你装完上面的命令后不想再重启更新用户组之类的,可以直接sudo virt-manager
。
创建Win10镜像,这里就不赘述了,操作很简单,找好ISO镜像,安装即可,这里备注几个重要的点。
首先,如果这里没有自动识别ISO中的系统可以输入后自己手动选择一下,比如这里选择win10就可以了。
其次,创建成功后再把显卡的PCIE添加进来,注意Nividia的声卡也要加进来。
进入系统后安装Nvidia官方的驱动就可以在设备管理器里找到显卡了,但还需要继续配置才能使用显卡接管显示输出。
6. 高清显示与Parsec远程
parsec和其他软件都是需要启动显卡才能高清输出的,所以这里还要创建一个虚拟高分辨率显示器,创建步骤很简单,参考这个博客即可,Win10/11 如何开启 第二屏幕/副屏/虚拟显示器,让平板成为副屏 - 知乎 (zhihu.com)。
安装里面的脚本,在CMD里执行下面的命令:
deviceinstaller64 install usbmmidd.inf usbmmidd
deviceinstaller64 enableidd 1 # 创建屏幕
deviceinstaller64 enableidd 0 # 用来关闭屏幕
完成后就能在windows的设立里看到第二个屏幕可用了,再把第二块屏的分辨率调到3840x2160(16:9)的4k,并设置为主屏。
最后打开parsec之类的软件就能够正常高清输出了,当然parsec需要网络能够满足p2p的环境,如果连接不了报错,可以在B站搜索相关错误代码,确认自己的情况,我这里尝试了同一个路由内虚拟机默认的网络配置是可以连接的,但设备在不同路由下面的时候会跨网段,虚拟机就不能建立p2p连接了(例如宿主机在172.19.1.10,虚拟机在宿主机中但没有相关172.19的网段,测试用的笔记本在172.20.1.20),可能的思路是需要修改虚拟机的网卡让其转到172.19网段下面再尝试。文章来源:https://www.toymoban.com/news/detail-780921.html
ToDesk里也会显示当前是否为p2p的连接,可以利用ToDesk来辅助检测网络环境。文章来源地址https://www.toymoban.com/news/detail-780921.html
到了这里,关于【完整详细教程】Ubuntu22.04 双显卡 3090Ti*2 KVM虚拟机多显卡直通与Parsec高清远程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!