KVM/QEMU GPU Passthrough cpu-pining

KVM/QEMU GPU Passthrough cpu-pining

Tags
kvm
Published
Published March 5, 2022
按我个人意识的重要性排序。

打开iommu

(此文本仅用于grub引导的情况下)
nano /etc/default/grub //修改引导设置 GRUB_CMDLINE_LINUX_DEFAULT=“” //找到这一行并添加 intel_iommu=on或amd_iommu=on // 取决于你的cpu厂商 grub-mkconfig -o /boot/grub/grub.cfg //生成grub引导文件并重启

找到GPU ID

dmesg | grep -i -e DMAR -e IMOOU //检查是imoou否启用
显示所有PCI硬件设备
#!/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;
找到所有与你显卡相关的组件 例如显卡自带的HDMI音频 可能有一个 也可能三个四个
复制他们 并保存起来 蛮重要

安装所有虚拟机相关

pacman -S qemu libvirt edk2-ovmf virt-manager ebtables dnsmasq //使用pacman安装相关 systemctl enable libvirtd.service //启用相关服务 systemctl start libvirtd.service //启动相关服务 systemctl enable virtlogd.socket systemctl start virtlogd.socket virsh net-autostart default //启用虚拟机的网络 virsh net-start default

修补GPU固件 (nvida需要)

(amd用户跳过, 2022.3.4 ndida用户也可以跳过了,如今驱动启用vifo)
nvida用户点击显示教程
notion image
使用GPU-Z工具导出你的显卡bios文件
pacman -S bless //安装十六进制编辑器
使用它打开你刚才保存的bios文件
notion image
不要惊慌 打开搜索
notion image
搜索VIDEO 设置搜索Text 然后点击Find Next
notion image
找到关键字符U...K7400.L.w.VIDEO 然后把U之前的所有文本选中删除
notion image
你会选中非常多的字符 不要惊慌 选到头后按下DELETE键 全部删除
notion image
删除后应该如图所示 到现在 你成功了 修复完成
接下来将你修改的文件另存为 不要覆盖原始文件 如果不幸翻车 原始文件能保你显卡不变砖头
notion image
文件准备好 我们开始下一步 安装windows虚拟机

安装Windows虚拟机

去微软官网下载win10镜像或者ITELLYOU下载
打开Virtul machine manager
新建虚拟机 选择你下载的镜像
打开customize configuration before install (安装前自定义配置)
选择Q35固件 选择UEFI_OVMF_CODE.fd 因为需要启用uefi以便劫持的你显卡
cpu选项sockets为插槽一般为1 cores核心 threads线程 看情况填写
(linux资源分配很烂所以一会儿我们搞一下手动拓扑CPU pingning)
内存设置根据自己的配置来吧
引导选项 记得启动CD-ROM 不然你会挠头想喵的发生了什么为啥没安装界面
接下来就是安装了。。。太基础了 不写这成人仪式了
关机 我们开始做hook :)

安装Hook管理

接下来我们做的 可以在虚拟机每次启动或关机状态时 把显卡拿过来或还给linux系统
参考这个网站 我们一步步来
mkdir /etc/libvirt/hooks //没有这个目录所以我们需要先创建 sudo wget 'https://raw.githubusercontent.com/PassthroughPOST/VFIO-Tools/master/libvirt_hooks/qemu' \ -O /etc/libvirt/hooks/qemu sudo chmod +x /etc/libvirt/hooks/qemu //赋予权限 sudo pacman -S tree //安装展示树状目录
mkdir /etc/libvirt/hooks/qemu.d mkdir /etc/libvirt/hooks/qemu.d/win10 mkdir /etc/libvirt/hooks/qemu.d/win10/prepare mkdir /etc/libvirt/hooks/qemu.d/win10/release mkdir /etc/libvirt/hooks/qemu.d/win10/prepare/begin mkdir /etc/libvirt/hooks/qemu.d/win10/release/end cd /etc/libvirt/hooks tree
notion image
nano /etc/libvirt/hooks/kvm.conf
_后面的内容是你之前查询的GPUID 例如01.00.1你就在0000_后填写把小数点替换成_即可
notion image
VIRSH_GPU_VIDEO=pci_0000_ VIRSH_GPU_AUDIO=pci_0000_
nano /etc/libvirt/hooks/qemu.d/win10/prepare/begin/start.sh nano /etc/libvirt/hooks/qemu.d/win10/release/end/revert.sh
下面是参考的脚本 你需要根据自己的需要来设置
AMD (amd不需要解除efi和VT绑定 而且和nvida所需要解除的驱动不同)
#!/bin/bash # debugging set -x # load variables we defined source "/etc/libvirt/hooks/kvm.conf" # stop display manager systemctl stop sddm.service pulse_pid=$(pgrep -u sylvester pulseaudio) pipewire_pid=$(pgrep -u sylvester pipewire-media) kill $pulse_pid kill $pipewire_pid # Unbind VTconsoles #echo 0 > /sys/class/vtconsole/vtcon0/bind # Unbind EFI-framebuffer #echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind # Avoid race conditions sleep 7 # unload amd drivers modprobe -r amdgpu modprobe -r drm_kms_helper modprobe -r pinctrl_amd modprobe -r drm # unbind gpu virsh nodedev-detach $VIRSH_GPU_VIDEO virsh nodedev-detach $VIRSH_GPU_AUDIO # load vfio modprobe vfio modprobe vfio_pci modprobe vfio_iommu_type1
#!/bin/bash # Helpful to read output when debugging set -x # Load the config file with our environmental variables source "/etc/libvirt/hooks/kvm.conf" # Unload all the vfio modules modprobe -r vfio_pci modprobe -r vfio_iommu_type1 modprobe -r vfio # Reattach the gpu virsh nodedev-reattach $VIRSH_GPU_VIDEO virsh nodedev-reattach $VIRSH_GPU_AUDIO #Sleep 5s rtcwake -m mem -s 5 # Load all Radeon drivers modprobe amdgpu modprobe gpu_sched modprobe ttm modprobe drm_kms_helper modprobe i2c_algo_bit modprobe drm modprobe snd_hda_intel modprobe pinctrl_amd # Start you display manager systemctl start sddm.service
NVIDA
#!/bin/bash # Helpful to read output when debugging set -x # Stop display manager systemctl stop display-manager.service ## Uncomment the following line if you use GDM #killall gdm-x-session # Unbind VTconsoles echo 0 > /sys/class/vtconsole/vtcon0/bind echo 0 > /sys/class/vtconsole/vtcon1/bind # Unbind EFI-Framebuffer echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind # Avoid a Race condition by waiting 2 seconds. This can be calibrated to be shorter or longer if required for your system sleep 2 #Unload Nvida modprobe -r nvida_drm modprobe -r nvida_modeset modprobe -r drm_kms_helper modprobe -r nvida modprobe -r i2c_nvida_gpu modprobe -r drm modprobe -r nvida_uvm # Unbind the GPU from display driver virsh nodedev-reattach $VIRSH_GPU_VIDEO virsh nodedev-reattach $VIRSH_GPU_AUDIO # load vfio modprobe vfio modprobe vfio_pci modprobe vfio_iommu_type1
#!/bin/bash set -x # Re-Bind GPU to Nvidia Driver virsh nodedev-reattach $VIRSH_GPU_VIDEO virsh nodedev-reattach $VIRSH_GPU_AUDIO # Reload nvidia modules modprobe -r nvida_drm modprobe -r nvida_modeset modprobe -r drm_kms_helper modprobe -r nvida modprobe -r i2c_nvida_gpu modprobe -r drm modprobe -r nvida_uvm # Rebind VT consoles echo 1 > /sys/class/vtconsole/vtcon0/bind # Some machines might have more than 1 virtual console. Add a line for each corresponding VTConsole #echo 1 > /sys/class/vtconsole/vtcon1/bind nvidia-xconfig --query-gpu-info > /dev/null 2>&1 echo "efi-framebuffer.0" > /sys/bus/platform/drivers/efi-framebuffer/bind # load vfio modprobe vfio modprobe vfio_pci modprobe vfio_iommu_type1 # Restart Display Manager systemctl start display-manager.service
另一种方案 来自bilibili LED
start
#!/bin/bash # Helpful to read output when debugging set -x long_delay=10 medium_delay=5 short_delay=1 echo "Beginning of startup!" function stop_display_manager_if_running { # Stop dm using systemd if command -v systemctl; then if systemctl is-active --quiet "$1.service" ; then echo $1 >> /tmp/vfio-store-display-manager systemctl stop "$1.service" fi while systemctl is-active --quiet "$1.service" ; do sleep "${medium_delay}" done return fi # Stop dm using runit if command -v sv; then if sv status $1 ; then echo $1 >> /tmp/vfio-store-display-manager sv stop $1 fi fi } # Stop currently running display manager if test -e "/tmp/vfio-store-display-manager" ; then rm -f /tmp/vfio-store-display-manager fi stop_display_manager_if_running sddm stop_display_manager_if_running gdm stop_display_manager_if_running lightdm stop_display_manager_if_running lxdm stop_display_manager_if_running xdm stop_display_manager_if_running mdm stop_display_manager_if_running display-manager sleep "${medium_delay}" # Unbind VTconsoles if currently bound (adapted from https://www.kernel.org/doc/Documentation/fb/fbcon.txt) if test -e "/tmp/vfio-bound-consoles" ; then rm -f /tmp/vfio-bound-consoles fi for (( i = 0; i < 16; i++)) do if test -x /sys/class/vtconsole/vtcon${i}; then if [ `cat /sys/class/vtconsole/vtcon${i}/name | grep -c "frame buffer"` \ = 1 ]; then echo 0 > /sys/class/vtconsole/vtcon${i}/bind echo "Unbinding console ${i}" echo $i >> /tmp/vfio-bound-consoles fi fi done # Unbind EFI-Framebuffer if test -e "/tmp/vfio-is-nvidia" ; then rm -f /tmp/vfio-is-nvidia fi if lsmod | grep "nvidia" &> /dev/null ; then echo "true" >> /tmp/vfio-is-nvidia echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind fi echo "End of startup!"
revert
#!/bin/bash set -x echo "Beginning of teardown!" sleep 10 # Restart Display Manager input="/tmp/vfio-store-display-manager" while read displayManager; do if command -v systemctl; then systemctl start "$displayManager.service" else if command -v sv; then sv start $displayManager fi fi done < "$input" # Rebind VT consoles (adapted from https://www.kernel.org/doc/Documentation/fb/fbcon.txt) input="/tmp/vfio-bound-consoles" while read consoleNumber; do if test -x /sys/class/vtconsole/vtcon${consoleNumber}; then if [ `cat /sys/class/vtconsole/vtcon${consoleNumber}/name | grep -c "frame buffer"` \ = 1 ]; then echo "Rebinding console ${consoleNumber}" echo 1 > /sys/class/vtconsole/vtcon${consoleNumber}/bind fi fi done < "$input" # Rebind framebuffer for nvidia if test -e "/tmp/vfio-is-nvidia" ; then echo "efi-framebuffer.0" > /sys/bus/platform/drivers/efi-framebuffer/bind fi echo "End of teardown!"
qemu (负责判断虚拟机开启或关闭)
#!/bin/bash OBJECT="$1" OPERATION="$2" if [[ $OBJECT == "win10" ]]; then case "$OPERATION" in "prepare") systemctl start libvirt-nosleep@"$OBJECT" 2>&1 | tee -a /var/log/libvirt/custom_hooks.log /bin/vfio-startup.sh 2>&1 | tee -a /var/log/libvirt/custom_hooks.log ;; "release") systemctl stop libvirt-nosleep@"$OBJECT" 2>&1 | tee -a /var/log/libvirt/custom_hooks.log /bin/vfio-teardown.sh 2>&1 | tee -a /var/log/libvirt/custom_hooks.log ;; esac fi
 
接下来赋予他们权限
cd /etc/libvirt/hooks/qemu.d chmod 777 * -R
恭喜靓仔 你已经搞掂了最困难的步骤

确保几个步骤已经完成

接下来对虚拟机进行一些设置
添加你显卡相关(一般有两个)到虚拟机并添加你的usb鼠标到直连
notion image
在Edit选项栏找到Preferences启用xml编辑
notion image
点击你其中一个显卡资源 打开rom bar单选框 然后打开xml编辑页面
notion image
在</source>行后回车输入
<rom bar="on" file="/u rom path/rx570.rom"/>
应用后给你的另一个显卡设备也如法炮制输入以上代码 并且应用
notion image
直到现在 你已经完成了显卡直通的设置 可以享受了 接下来是进阶

CPU Pinning

建立的虚拟机调用cpu是被linux调度的 所以导致纵使你虚拟机给了很多核心 但当某些高效集群软件或游戏时运行性能依旧明显不足 so 不如我们直接将大部分核心给予虚拟机
托VIFO-Tools的福 我们的操作会更加简便 讲下原理
VIFO-Tools直接hook了libvirt 可以在每次libvirt你的虚拟机程序运行的时候自动索取特定目录的文件来运行它 这就是刚才你显卡直通只需要写两个shell文件就可以享受的原因
首先 开始前 你需要了解一些小Tips 以便接下来的操作
notion image
这张图片展示了i9 9900K处理器的线程模式 一个core核心由#0真实内核和#8逻辑内核组成
我们要做的就是将linux限制在#0和#8组成的core核心中调用即可
lscpu -e //在终端输入以下指令展示cpu核心
notion image
CORE一栏代表核心 最左边CPU一栏表示的是内核
notion image
 
记住谁和谁是#CPU一栏中内核组成的的CORE核心 接下来要用
特定调用目录如下
/etc/libvirt/hooks/qemu $vm_name $hook_name $sub_name $extra
由于VFIO-Tools的加持 我们的cpu pinning也会更加方便 只需要创建两个脚本到特定目录
nano /etc/libvirt/hooks/qemu.d/win10/prepare/begin/isolstart.sh
其中x为你的cpu与#0内核组成核心的另一个内核 要确保linux使用的一个完整核心
systemctl set-property --runtime -- user.slice AllowedCPUs=0,x systemctl set-property --runtime -- system.slice AllowedCPUs=0.x systemctl set-property --runtime -- init.scope AllowedCPUs=0,x
当虚拟机结束时 我们也要把cpu还给linux 保证我们linux的体验
此时的x应为你最大的CPU一览中的内核名称例如#15 代表所有全部内核可用
nano /etc/libvirt/hooks/qemu.d/win10/release/end/isocpurevert.sh
systemctl set-property --runtime -- user.slice AllowedCPUs=0-x systemctl set-property --runtime -- system.slice AllowedCPUs=0-x systemctl set-property --runtime -- init.scope AllowedCPUs=0-x

过虚拟机检测

在显卡直通和CpuPinning的加持下也许你会玩一些游戏大作
但一些PVP的多人游戏为了阻止Cheater会加入虚拟机检测 例如 EAC BE
显然没有顾及无辜的你我
打开你的QENU 点击Overview一栏 打开xml编辑 在hperv标签输入以下内容
<hyperv mode="custom"> <relaxed state="on"/> <vapic state="on"/> <spinlocks state="on" retries="8191"/> <vpindex state="on"/> <runtime state="on"/> <synic state="on"/> <stimer state="on"/> <reset state="on"/> <vendor_id state="on" value="randomid"/> <frequencies state="on"/> </hyperv>
接着在</hyperv>标签后加入以下内容
<kvm> <hidden state="on"/> </kvm>
随后 在你的虚拟系统中 启用windows服务 hyper-v栏内两个选项都打勾 确定 重启
完成 😉
notion image

参考链接