GPU穿透
通过IOMMU(IOMMU 是 Intel VT-d 和 AMD-Vi 的通用名称),并给主机添加VFIO模块。 使得GPU在主机上以vfio驱动,并将PCI穿透到虚拟机中使用。
开启IOMMU
在 BIOS 设置中启用 Intel VT-d 或 AMD-Vi,对于Intel的CPU,设置内核参数 intel_iommu=on iommu=pt
。
因为穿透以IOMMU分组作为最小单位。首先查看IOMMU分组和设备信息,通过以下脚本,确认要穿透的设备与主机要使用的设备不在同一个分组中。
iommu.sh
#!/bin/bash
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf 'IOMMU Group %s ' "$n"
lspci -nns "${d##*/}"
done
静态屏蔽
如果主机有2个或以上显卡,主机不使用该GPU,专给虚拟机使用,比较简单的方式是直接从主机静态绑定设备到vfio-pci。
具体执行有2种方式
优先绑定vfio-pci
在/etc/modprobe.d/vfio.conf
中写入以下内容
# Bind GPU device to vfio-pci for virtual machine.
options vfio-pci ids=10de:2882,10de:22be disable_vga=1
# Ensure that vfio-pci is initialized before GPU driver.
softdep nouveau pre: vfio-pci
softdep snd_hda_intel pre: vfio-pci
softdep xhci_pci pre: vfio-pci
第一行,将GPU设备绑定到vfio-pci,ids=10de:2882,10de:22be
是显卡的PCI ID,从iommu.sh执行结果可以看到,disable_vga=1
表示禁用VGA功能。
后面三行,确保vfio-pci在GPU驱动程序之前初始化。
屏蔽显卡驱动
还有一种做法是/etc/modprobe.d/blacklist.conf
blacklist nouveau
把显卡驱动加入黑名单。lspci -k | grep -A 3 "VGA"
可以查看显卡驱动。但是这种做法对于相同驱动的显卡则不好分辨。
动态分离
主机使用GPU
如果主机也要使用该GPU,可以使用动态分离设备的方式。
为了方便分离,显卡需要不被Xorg使用,否则切换穿透时会提示被占用,就无法分离给虚拟机使用。还要先停止Xorg,就不方便。
为此,NVIDIA显卡驱动安装的时候选择不要生成Xorg配置。如果已经安装了Xorg配置,可以删除/etc/X11/xorg.conf
和 /usr/share/X11/xorg.conf.d/nvidia-drm-outputclass.conf
。
如果还是不行,可以使用fuser
命令,或者lsof
命令,确定哪个程序占用了GPU
sudo fuser -v /dev/nvidia*
sudo lsof /dev/nvidia*
分离
#!/bin/bash
gpu="0000:01:00.0"
aud="0000:01:00.1"
gpu_vd="$(cat /sys/bus/pci/devices/$gpu/vendor) $(cat /sys/bus/pci/devices/$gpu/device)"
aud_vd="$(cat /sys/bus/pci/devices/$aud/vendor) $(cat /sys/bus/pci/devices/$aud/device)"
function bind_vfio {
echo "$gpu" > "/sys/bus/pci/devices/$gpu/driver/unbind"
echo "$aud" > "/sys/bus/pci/devices/$aud/driver/unbind"
echo "$gpu_vd" > /sys/bus/pci/drivers/vfio-pci/new_id
echo "$aud_vd" > /sys/bus/pci/drivers/vfio-pci/new_id
}
function unbind_vfio {
echo "$gpu_vd" > "/sys/bus/pci/drivers/vfio-pci/remove_id"
echo "$aud_vd" > "/sys/bus/pci/drivers/vfio-pci/remove_id"
echo 1 > "/sys/bus/pci/devices/$gpu/remove"
echo 1 > "/sys/bus/pci/devices/$aud/remove"
echo 1 > "/sys/bus/pci/rescan"
# 或者也可以像上面vfio-pci那样直接绑定,驱动可能是nvidia,声卡驱动可能是snd_hda_intel
}
还有一个virsh命令,已经把上面的步骤封装了,更直接
virsh nodedev-detach pci_0000_01_00_0
virsh nodedev-detach pci_0000_01_00_1
反过来
virsh nodedev-reattach pci_0000_01_00_0
virsh nodedev-reattach pci_0000_01_00_1
hooks
可以使用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
创建 /etc/libvirt/hooks/kvm.conf
## Virsh devices
VIRSH_GPU_VIDEO=pci_0000_01_00_0
VIRSH_GPU_AUDIO=pci_0000_01_00_1
假设虚拟机名为win10。
创建 /etc/libvirt/hooks/qemu.d/win10/prepare/begin/bind_vfio.sh
#!/bin/bash
## Load the config file
source "/etc/libvirt/hooks/kvm.conf"
## Load vfio
modprobe vfio
modprobe vfio_iommu_type1
modprobe vfio_pci
## Unbind gpu from nvidia and bind to vfio
virsh nodedev-detach $VIRSH_GPU_VIDEO
virsh nodedev-detach $VIRSH_GPU_AUDIO
创建 /
etc/libvirt/hooks/qemu.d/win10/prepare/end/unbind_vfio.sh`
#!/bin/bash
## Load the config file
source "/etc/libvirt/hooks/kvm.conf"
## Unbind gpu from vfio and bind to nvidia
virsh nodedev-reattach $VIRSH_GPU_VIDEO
virsh nodedev-reattach $VIRSH_GPU_AUDIO
## Unload vfio
modprobe -r vfio_pci
modprobe -r vfio_iommu_type1
modprobe -r vfio
虚拟机添加PCI
通过virt-manager和virsh edit win10
给虚拟机win10添加PCI设备,参考xml
<devices>
...
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
</source>
<address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0"/>
</hostdev>
<hostdev mode="subsystem" type="pci" managed="yes">
<source>
<address domain="0x0000" bus="0x01" slot="0x00" function="0x1"/>
</source>
<address type="pci" domain="0x0000" bus="0x06" slot="0x00" function="0x0"/>
</hostdev>
...
</devices>