Linux解锁NVIDIA消费级显卡vGPU功能的入坑记录

发布于 2023-04-22 2,968 次阅读


请注意,此教程大部分步骤可能已经过时,仅供参考

运行 Linux 虚拟机可能会遇到一些关于显卡的问题: qemu/vmware/virtualbox 的虚拟显卡无法正常运行一些渲染程序和游戏。这些软件要想正常运行,解决方法只有物理显卡直通和显卡虚拟化。显卡有限的情况下,想要每个虚拟机都分配一个物理显卡是不现实的,对于显卡虚拟化,非专业显卡无法开启显卡虚拟化, Linux 下也并没有像微软 Hyper-V + GPU-PV 之类的黑科技。

vgpu-unlock 是解锁 NVIDIA 消费级显卡虚拟化功能的 Linux 补丁,目前已经支持以下架构的大多数显卡,对应相同架构的计算卡。

  • Maxwell 1代架构,支持大多数的GTX 700系显卡(对应Tesla M10)
  • Maxwell 2代架构,支持大多数的GTX 900系显卡(对应Tesla M60)
  • Pascal架构,支持大多数的GTX 1000系显卡(对应Tesla P40)
  • Turing架构,支持大多数的GTX 1600系和RTX 2000系显卡(对应Quadro RTX 6000)

注意: vgpu-unlock 暂时不支持Ampere(RTX 3000系)架构及以上显卡

准备

vgpu_unlock 默认仅支持 ≥5.10 (5.11~5.13版本的内核需要额外补丁)版本内核,而 VGPU-Community-Drivers 项目似乎支持更高版本的内核,但为了兼容性最好用小于5.10的内核。

本文使用的环境如下:

  • Debian 11 (Bullseye) on Linux 5.10.0
  • Intel Core i3-10100F
  • NVIDIA GeForce GTX 1050 2G

下载和安装相关的工具和依赖

apt update && apt upgrade -y
apt install -y git build-essential dkms unzip python3-pip mdevctl jq -y
pip3 install frida --break-system-packages

vGPU-Unlock-patcher 是 vgpu_unlock 注入 nvidia 驱动的一个解决方案:

git clone https://github.com/VGPU-Community-Drivers/vGPU-Unlock-patcher.git -b 525.105 --recursive

vgpu_unlock-rs 是设置 vGPU 配置的工具:

git clone https://github.com/mbilker/vgpu_unlock-rs.git

开启 IOMMU

编辑 /etc/default/grub ,添加下面的参数开启 IOMMU :

# Intel 处理器写入以下内容
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on"
# AMD 处理器写入以下内容
GRUB_CMDLINE_LINUX_DEFAULT="amd_iommu=on"

然后使用 update-grub 更新系统引导并重启。

禁用开源驱动

在安装驱动之前需要禁用 nouveau 驱动,否则 vGPU 驱动将会报错无法安装:

echo "blacklist nouveau" >> /etc/modprobe.d/disable-nouveau.conf
echo "options nouveau modeset=0" >> /etc/modprobe.d/disable-nouveau.conf

使用 update-initramfs -u 更新 initrd 并重启以应用更改。

单N卡用户请注意:在禁用驱动后就无法进入桌面环境,请确保开启 SSH 服务器或者使用 Ctrl+Alt+F2~F6 进入 TTY

加载模块

/etc/modules 写入以下内容:

vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd

安装Rust编译环境

因为 Debian 11 软件包源的 Rust 版本太老,直接编译 vgpu_unlock-rs 会报错,所以使用官方的安装方法:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装和配置

下载 vGPU-KVM 驱动

官方下载 vGPU 驱动的前提需要用一个非个人邮箱(可以使用自己的域名和未关闭25端口的vps自建邮箱服务端进行注册,亲测可用)注册 NVIDIA 企业账户,申请90天的 vGPU 体验通过后,才会提供 vGPU 宿主机和虚拟机的GRID驱动下载渠道和 vGPU 的使用许可。

Github 仓库有本文对应的 NVIDIA GRID 15.2(525.105) 驱动,下载 Linux-KVM 后缀的驱动包。 仓库已删,本人已上传部分版本vGPU驱动到此仓库,随缘更新

解压下载下来的驱动包,将解压后的 Host_Drivers 文件夹里面的 NVIDIA-Linux-x86_64-525.105.14-vgpu-kvm.run 移动到 vGPU-Unlock-patcher 下。

下载消费级显卡驱动

由于计算卡本身也没有显示输出接口,因此用的 vGPU 驱动既不支持 DRM (图像输出),也不支持在宿主机上进行OpenGL/CUDA之类的渲染运算,如果只有NVIDIA显卡的电脑直接安装 vGPU 驱动,将无法正常使用显卡。

针对这个问题,可以通过合并游戏显卡驱动来解决, vGPU-Unlock-patcher 的 patch.sh 脚本也有这个功能。

如果你并不需要上面的功能(比如当云计算/游戏虚拟机平台),可以跳过这个步骤。

wget https://download.nvidia.com/XFree86/Linux-x86_64/525.105.17/NVIDIA-Linux-x86_64-525.105.17.run

同上,将下载的文件移动到 vGPU-Unlock-patcher 下。

  • 需要注意的是,如果使用合并后的驱动,宿主机的GPU本身也将会视为一个vGPU,对于我这个低显存的1050 2G就只能给虚拟机用一个vGPU,多分配会报IO错误,因此下面划分vGPU时就需要谨慎划分。

使用 vGPU-Unlock-patcher 脚本

首先进入 vGPU-Unlock-patcher 目录,输入以下命令:

./patch.sh general-merge

默认不支持 6.x 版本的内核,如果内核版本是 6.1~6.3 ,尝试以下选项

./patch.sh --lk6-patches general-merge
  • 实测大多数 6.x 内核无法编译通过,不建议使用

等待命令执行完成,检查当前目录下是否出现 NVIDIA-Linux-x86_64-525.105.17-vgpu-kvm-merge-patched 目录,若有则进入这个目录并输入以下命令安装驱动:

./nvidia-installer --dkms

出现任何选项,选 yes 即可。

如果出现 Installation Error ,通常是 Linux 内核不兼容导致的,可以尝试降级内核或者更换内核。

如果是搭建云虚拟机平台,把上文中的 ./patch.sh general-merge 替换成 ./patch.sh vgpu-kvm,且对应的文件夹应为 NVIDIA-Linux-x86_64-525.105.17-vgpu-kvm-patched

安装和配置 vgpu_unlock-rs

安装 vgpu_unlock-rs 后就可以通过修改配置文件来自定义默认的vGPU配置。(例如把限制45fps的B类vGPU改到60fps或更高)

进入到 vgpu_unlock-rs 目录后,输入下面命令:

cargo build --release
cp './target/release/libvgpu_unlock_rs.so' /usr/local/lib

为了让 NVIDIA 驱动使用 vgpu_unlock-rs ,这里修改 NVIDIA 驱动的 Systemd 服务文件:

mkdir /etc/systemd/system/nvidia-{vgpu-mgr,vgpud}.service.d
echo '[Service]
Environment=LD_PRELOAD=/usr/local/lib/libvgpu_unlock_rs.so' | tee /etc/systemd/system/nvidia-{vgpu-mgr,vgpud}.service.d/vgpu_unlock-rs.conf > /dev/null

将服务设置为开机运行:

systemctl enable nvidia-{vgpu-mgr,vgpud}.service

设置完成后重启,重启后输入 mdevctl types ,如果输出像下面这样,那么便配置成功了:

...
nvidia-283
Available instances: 0
Device API: vfio-pci
Name: GRID P40-4C
Description: num_heads=1, frl_config=60, framebuffer=4096M, max_resolution=4096x2160, max_instance=6
...

如果没有任何输出,尝试重启两个服务,然后再输入上面的命令。

systemctl restart nvidia-{vgpu-mgr,vgpud}.service

vGPU 类型

NVIDIA的vGPU是靠显存和功能来划分的,例如:

我的 GTX1050 只有 2GB 显存,而对应架构的 Tesla P40 的一个 vGPU 最少也需要 1GB 的显存,也就是说,我的 GTX1050 最多只能划分 1+1 也就是两个 vGPU 。

如果是 GTX1060 6GB ,则可以划分成 1+1+1+1+1+1 总共6个vGPU ,或者 2+2+2 总共3个vGPU ,或者 3+3 总共2个vGPU,但是不能像 1+2+3 这样划分。

更具体的vGPU信息可以在NVIDIA官网的GRID文档,与自己的显卡架构对应的计算卡vGPU支持列表里找到。

执行 mdevctl types 命令后的输出:

0000:01:00.0
nvidia-156
Available instances: 0
Device API: vfio-pci
Name: GRID P40-2B
Description: num_heads=4, frl_config=45, framebuffer=2048M, max_resolution=5120x2880, max_instance=12
nvidia-215
Available instances: 0
Device API: vfio-pci
Name: GRID P40-2B4
Description: num_heads=4, frl_config=45, framebuffer=2048M, max_resolution=5120x2880, max_instance=12
...

0000:01:00.0 是显卡的 PCI 物理地址

nvidia-156 是 vGPU 的类型名

Name: GRID P40-2Bnvidia-156 的 vGPU 的虚拟机显示的显卡名

其中, P40-2B 中的 2B2 代表显存容量,这里表示显存为 2G ,而 2B 中的 B 表示的是vGPU功能类型,下面写出了各字母代表的vGPU功能:

vGPU类型支持的操作系统一般的使用场合许可证支持的显示参数备注
AWindows, LinuxVirtual ApplicationsvApps1280x1024,1 displayGood for RDSH
BWindows, LinuxBasic PC workvPCUp to 5K, 2 displays45 FPS max
CLinuxCompute servervCSUp to 4K, 1 displayCUDA Only
QWindows, LinuxProfessional WorkstationvDWSUp to 8K, 4 displaysCUDA, OpenGL

看不懂可以直接选Q,Q是专业工作站版本,功能最全。

  • 目前已知Q类型vGPU的OpenGL性能非常糟糕,在跑OpenGL软件渲染时甚至会带动桌面一起卡顿,如果需要使用OpenGL可以使用A类型和B类型vGPU,通过修改 /etc/vgpu_unlock/profile_override.toml 配置文件替代Q类型vGPU,可以参考此项目的README
  • 同时,A类型vGPU在Linux下不生效,它们只能在Windows上使用。

vGPU 创建

首先用 uuidgen 生成 UUID ,然后选择合适的 vGPU 类型,比如 nvidia-46

mdevctl start -u 451dcd1d-d464-49a3-b0c6-3cf2c7c1f8c4 -p 0000:01:00.0 --type nvidia-46
mdevctl define -a -u 451dcd1d-d464-49a3-b0c6-3cf2c7c1f8c4

注意:不要盲目复制粘贴!

创建 Libvirt 虚拟机和测试

安装和配置 Libvirt 虚拟机

安装 Qemu Libvirt 和 Virt-Manager :

apt install libvirt0 virt-manager qemu-system-x86 qemu-kvm qemu-utils libvirt-clients libvirt-daemon-system virtinst -y

设置 libvirt 开机自动启动

systemctl enable --now libvirtd

设置 Libvirt 虚拟机权限(将 $USER 替换为自己的用户名):

sudo usermod -a -G input $USER
sudo usermod -a -G kvm $USER
sudo usermod -a -G libvirt $USER

启动虚拟机 NAT 网络并设置自动启动

virsh net-start default
virsh net-autostart default

虚拟机添加 vGPU

在应用程序菜单里面找到虚拟系统管理器并打开,创建虚拟机最后一步时选择在安装前自定义配置

左下角添加硬件,在弹出的左侧列表里找到 MDEV Host Device ,右边的主机设备就是你生成的 vGPU 设备。

安装 vGPU-GRID 驱动

在前面下载和解压 vGPU-KVM 显卡驱动步骤时,解压出来 Guset_Drivers 的文件夹里的后缀 .exe 文件就是 Windows 的 GRID 驱动。

.deb 和 .rpm 分别是 Debian 系和 Red Hat 系的 GRID 虚拟显卡驱动, .run 是通用的 Linux GRID 虚拟显卡驱动。

可以用下面的命令把文件夹里面的所有驱动打包成 GRID.iso 文件,在虚拟机里用光驱加载运行:

mkisofs -J -l -r -o GRID.iso Guest_Drivers/*

获取GRID驱动授权

前面说过,只有注册 NVIDIA 企业账号并申请90天 vGPU 试用,通过后才可以拿到 vGPU 的驱动和授权服务,否则 GRID 驱动只能用20分钟,超过20分钟后就会限制性能。

那怎么办呢?

Docker Hub 上有一个容器项目 collinwebdesigns/fastapi-dls ,可以伪装成 Nvidia GRID License Server 获取授权,不过需要一个OpenSSL证书,所以这里使用已经封好证书的容器(此容器已非最新版本,不推荐使用)你也可以根据源码的README来自己创建一个证书。

Docker 安装与 fastapi-dls 部署

使用下面命令安装 docker :

apt install docker -y

首先,打开你的 Windows/Linux 虚拟机,查看虚拟机 NAT 网络的路由地址,我这边是 192.168.122.1,然后我要容器监听 1234 端口,那么docker容器的命令如下:

docker run --name dls -d -e DLS_URL=192.168.122.1 -e DLS_PORT=1234 -p 1234:443 makedie/fastapi-dls

DLS_URL 和 DLS_PORT 告诉 GRID 驱动访问哪个地址和端口获取授权,请确保没有填错。

使用下面的命令设置容器开机自动启动:

docker update --restart=always dls

让虚拟机的 GRID 驱动获取许可

在虚拟机内使用刚才设置的 DLS_URL 和 DLS_PORT 访问容器并下载 Token ,例如 https://192.168.122.1:1234/-/client-token

注意:地址要加上 HTTPS 前缀,否则无法访问

Windows 把下载的 token 文件放进虚拟机的 C:\Program Files\NVIDIA Corporation\vGPU Licensing\ClientConfigToken 文件夹内,然后重启 NvContainerLocalSystem 服务。

Linux 使用 curl --insecure -X GET https://192.168.122.1:1234/-/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token.tok

或者 wget https://192.168.122.1:1234/-/client-token -O /etc/nvidia/ClientConfigToken/client_configuration_token.tok --no-check-certificate

随后用 systemctl restart nvidia-gridd 重启服务

然后用 nvidia-smi -q 查看 License Status ,如果为 Licensed 则已成功获取许可。

结尾

虽然 vgpu_unlock 对绝大多数消费级显卡(包括超入门级的 GTX1050 和“亮机”级的 MX150/GT1030 )都提供了支持,但 NVIDIA vGPU 仍然有一些限制,例如:

  • vGPU 无法输出画面,本地只能通过像 parsec 和 moonlight 之类的串流远程桌面软件才能完全正常使用即时渲染的应用(比如游戏和3D渲染软件)
  • 也许是驱动兼容性问题,直通 vGPU 的Windows虚拟机无法开启Hyper-V虚拟化,但仍然可以使用VMware Workstation Pro和VirtualBox等虚拟化软件进行套娃

由于笔者没有条件测试,所以无法得知 vGPU 驱动和消费级 GPU 驱动合并的 merge 驱动对核显+独显的笔记本设备的兼容性。

实测笔记本(环境是ArchLinux 自编译内核,版本5.15-xanmod HD530核显+GTX1050Ti独显)对合并的GRID驱动兼容性良好,可以正常使用 Bumblebee 来切换显卡。

TIPS

  1. 建议全程挂代理
  2. 请尽量不要更新内核,建议将内核软件包设置为手动升级