N卡单显卡直通
总结只要能用vfio把显卡隔离出来就算成功了, 剩下都是调试问题
安装一个启用UEFI的windows虚拟机
- 在安装准备时勾选”安装前手动配置”, (Customize configuration before install)
- 在Overview栏, Firmware(固件)项中选择UEFI, 没有UEFI选项的arch用户可以安装ovmf包后再尝试
启动vnc功能
virt-manager安装的虚拟机默认会启动vnc, 不需要多于操作。后面我们会用另一台电脑通过vnc
安装完成后调整一下vnc的配置, 配置端口(默认5900), 开启all interfaces
(可选)安装virtio驱动
启动宿主机的iommu功能
grub用户可以通过修改grub配置文件(/etc/default/grub
)添加内核命令行参数, 其他bootloader用户可以搜添加内核命令行参数的方法。
- 修改
/etc/default/grub
配置文件, 在GRUB_CMDLINE_LINUX_DEFAULT
添加参数启用iommu,intel_iommu=on iommu=pt
, amd的CPU就用amd_iommu=on
1 | $ vim /etc/default/grub |
- 使用
grub-mkconfig
应用修改
1 | $ grub-mkconfig -o /boot/grub/grub.cfg |
重启后生效
使用如下脚本获取IOMMU分组信息:
1 |
|
记录显卡相关设备的id, 如我这里的分别是10de:2482
和10de:228b
。(不过我们但显卡直通用不到:)
)
不需要配置vfio内核模块
archwiki中vfio的相关操作是双显卡直通的操作, 所以需要配置vfio以在GPU被使用前将GPU隔离出来。但我们单显卡直通的思路是关掉所有图像界面, 卸载NVIDIA驱动, 手动使用vfio驱动, 所以我们不需要对vfio做其他配置。
配置virt manager钩子脚本
每个虚拟机的启动和关闭都会调用一个hook脚本(如果脚本存在的话), 根据官方文档的描述, qemu虚拟机在启动和关闭时都会调用/etc/libvirt/hooks/qemu
这个脚本, 所以我们编写以下hook脚本:
- 启动虚拟机时关闭所有图形界面(关闭所有使用NVIDIA驱动的程序), 换上vfio驱动做设备隔离
- 而要关掉nvidia驱动则需要关闭所有正在使用的程序, 如窗口管理器, 桌面管理器等
- 关闭虚拟机时再将NVIDIA驱动恢复, vfio可以选择不卸载(这里测试不卸载vfio驱动也没有影响)
你可以将本仓库提供的hook脚本拷贝到/etc/libvirt/hooks
目录下: sudo cp /path/to/this/repo/scripts/hooks/* /etc/libvirt/hooks/
。然后将启动钩子和恢复钩子软连接到/bin
目录下: sudo ln -s /path/to/repo/vfio-startup.sh /bin/
, ln -s /path/to/repo/vfio-teardown.sh /bin/
1 | # /etc/libvirt/hooks/qemu |
启动前触发的命令, 调用/bin/vfio-startup.sh
脚本
1 | /etc/libvirt/hooks/qemu guest_name prepare begin - |
关闭后触发的命令, 调用/bin/vfio-teardown.sh
1 | /etc/libvirt/hooks/qemu guest_name release end - |
配置防止休眠
拷贝本仓库脚本:
sudo cp /path/to/repo/scripts/libvirt-nosleep@.service /etc/systemd/system/libvirt-nosleep@.service
添加一个systemd的守护进程, 创建文件/etc/systemd/system/libvirt-nosleep@.service
, 在其中添加以下内容:
1 | [Unit] |
配置启动(前)脚本
可以将本仓库的这个脚本软连接到/bin
目录下, 因为/etc/libvirt/hooks/qemu
里调用的是/bin
目录下的脚本
1 | ln -s /path/to/repo/vfio-startup.sh /bin/ |
启动钩子如下: 注释掉的部分时调试的结果, 后面会说如何调试
1 | set -x |
配置关闭(后)脚本
可以将本仓库的这个脚本软连接到/bin
目录下, 因为/etc/libvirt/hooks/qemu
里调用的是/bin
目录下的脚本
1 | ln -s /path/to/repo/vfio-teardown.sh /bin/ |
关闭钩子如下: 注释掉的部分时调试的结果, 后面会说如何调试
1 | set -x |
⭐调试⭐
最重要的是懂得怎么调试, 我们需要额外一台可以通过ssh连接到这里的设备(手机, 平板等)。
- 查询需要现在直通的主机的ip, archlinux使用
ip addr
- 然后使用ssh命令连接到主机
1 | $ ssh username@remote_host -p 22 |
直接运行/bin/vfio-startup.sh
和/bin/vfio-teardown.sh
查看报错信息, 或者逐行测试。这里总结了几种错误
- 内核模块无法卸载
virsh nodedev-detach
卡死- vfio没有启用
/bin/vfio-teardown.sh
恢复后无响应
逐行调试
逐行走一下vfio-startup.sh和vfio-teardown.sh中的命令
卸载module
直接运行如下命令会发现提示被占用, 这时需要先关闭所有的图形程序。
1 | modprobe -r nvidia_drm |
使用lsmod查看被几个程序占用, 这里关掉我的窗口管理器bspwm后就没有占用了, killall bspwm
1 | [ring@archlinux ~]% killall bspwm |
之后在逐个module卸载
1 | modprobe -r nvidia_drm |
vtcon unbind
测试unbind(写入0)再重新bind(写入1)的情况
接着测试vfio-startup.sh
里的命令。经过上一步关闭图形程序和卸载nvidia驱动, 你已经关闭了所有图形程序, 你面前只有一个命令行:
运行下面的命令后会发现你的光标不再跳动, 对应vfio-startup.sh
1 | echo 0 > /sys/class/vtconsole/vtcon0/bind |
运行下面的命令后会发现你的光标恢复跳动, 对应vfio-teardown.sh
1 | echo 1 > /sys/class/vtconsole/vtcon0/bind |
nodedev-detach
1 | virsh nodedev-detach pci_0000_01_00_0 |
这里的pci_0000_A_B_C
中的A, B, C是通过lspci命令查看的, 如我lspci后看到GPU设备对应的总线id如下
1 | $ lspci | grep NVID |
所以pci_0000_01_00_0
对应的就是01:00.0, pci_0000_01_00_0
对应的就是01:00.1
如果这时你没有卸载nvidia驱动会发现执行virsh nodedev-detach
无法完成执行, 因为设备被占用了
启用vfio
这时你卸载了nvidia驱动, unbind了console, detach了GPU。从console中看都光标不再跳动,但再ssh中仍然可以操作。
继续执行vfio-startup.sh
中的命令:
1 | modprobe vfio-pci |
再ssh中通过lspci -v | less
搜索NVIDIA可以看到这时GPU使用的是vfio驱动了, 说明设备隔离成功!
vfio teardown恢复原本状态
整个过程再ssh中测试, 因为主机的界面已经不更新了
vfio-startup.sh
执行完成后再测试vfio-teardown.sh
恢复,基本就是vfio-startup.sh
的一个逆过程。
卸载vfio模块, 不过我这里测试不卸载也没事, nvidia驱动也能挂载
1 | # modprobe -r vfio_pci |
virsh reattach GPU设备
1 | virsh nodedev-reattach pci_0000_01_00_0 |
重新挂载nvidia显卡驱动
1 | modprobe nvidia |
lspci -v
查看驱动使用情况, nvidia自动又使用上了
重新启用vconsole, 原本我们写入0关闭了两个vtcon, 现在想要写入1重启。但我在写入1重启vtcon1使就会出现coredump, 所有我不写了, 使用下来也没有问题。
1 | echo 1 > /sys/class/vtconsole/vtcon0/bind |
vfio-teardown执行完毕
至此, 把vfio-startup和vfio-teardown的命令都执行了一遍,按理说原本状态应该恢复了。但是你可能会发现你主机上的光标还是没有跳动。不要慌,这时你的主机其实已经可以接收命令了,盲打图形界面启动命令, 或者在teardown后自动启动图形界面即可。因为我这时没有使用任何dm, 直接通过startx启动的窗口管理器, 所以我盲打startx来启动我的窗口管理器。
配置直通并启动虚拟机
配置显卡直通设备
virt-manager中添加pci设备, 选中显卡进行添加。
配置usb设备直通
同样的在virt-manager中添加设备, 添加usb设备。
可以根据vendor和product添加设备, 这样的方式能够自动识别usb设备地址。
也可以手动写死usb设备地址进行添加, 像一些三无设备就没有vendor和product, 所以手动添加。通过在添加设备栏目里手动编写xml文件实现。
首先使用lsusb
命令查看设备地址
xml格式如下, bus和device除填lsusb中查到的结果, 保存后virt-manager会自动分配内部地址。
1 | <hostdev mode="subsystem" type="usb" managed="yes"> |
安装显卡驱动
- 你可以等windows自动检测然后安装NVIDIA的驱动
- 你可以可以通过vnc连接到主机然后手动安装
隐藏虚拟机信息
不让软件检测到自己跑在虚拟机上, 在xml的feature栏目中添加
1 | <kvm> |