参考了很多文章,主要是:KVM GPU passthrough Ubuntu 20.04、virt-install工具创建虚拟机命令介绍(详细)和Virtual machines with PCI passthrough on Ubuntu 20.04, straightforward guide for gaming on a virtual machine。因为我的环境是无GUI的服务器, 所以不能用Virtual Machine Manager安装,必须用virt-install命令,以下主机相关操作全部在命令行以root用户完成,显卡为N卡。
准备环境首先确认主机BIOS开启了VT/d等虚拟化设置。安装软件包:
apt install qemu-kvm qemu-utils libvirt-daemon-system libvirt-clients bridge-utils ovmf
修改“/etc/default/grub”文件,开启IOMMU:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash modeset=0 intel_iommu=on"
参考文章里,最简单的方法是在这里后面加上“vfio-pci.ids=显卡所在IOMMU组所有PCI设备的vender ID:device ID,以逗号分隔”,但是很不幸,服务器上有多张同型号的显卡,他们的vender ID和device ID是一样的,这么搞一下就全屏蔽了。
更新grub:
update-grub
确认要给虚拟机分配的显卡的PCI主线ID,一般是一组,比如我这里是4个:显卡本身、音频设备、USB 3.1控制器和Type-C控制器,用
lspci -nn | grep -i NVIDIA
命令可以查看。然后就可以用
vi /etc/initramfs-tools/scripts/init-top/vfio.sh
命令创建一个新的脚本,用于屏蔽指定显卡组PCI设备:
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
for dev in 0000:1a:00.0 0000:1a:00.1 0000:1a:00.2 0000:1a:00.3
do
echo "vfio-pci" > /sys/bus/pci/devices/$dev/driver_override
echo "$dev" > /sys/bus/pci/drivers/vfio-pci/bind
done
exit 0
接着修改“/etc/initramfs-tools/modules”文件,在最后添加一行:
options kvm ignore_msrs=1
然后更新initramfs:
update-initramfs -u -k all
重启
重启后确认一下IOMMU是否开启:
dmesg | grep IOMMU
正常应该会显示诸如这样的输出:
[ 0.763744] DMAR: IOMMU enabled [ 1.330541] DMAR-IR: IOAPIC id 12 under DRHD base 0xc5ffc000 IOMMU 6 [ 1.330542] DMAR-IR: IOAPIC id 11 under DRHD base 0xb87fc000 IOMMU 5 [ 1.330542] DMAR-IR: IOAPIC id 10 under DRHD base 0xaaffc000 IOMMU 4 [ 1.330543] DMAR-IR: IOAPIC id 18 under DRHD base 0xfbffc000 IOMMU 3 [ 1.330544] DMAR-IR: IOAPIC id 17 under DRHD base 0xee7fc000 IOMMU 2 [ 1.330544] DMAR-IR: IOAPIC id 16 under DRHD base 0xe0ffc000 IOMMU 1 [ 1.330545] DMAR-IR: IOAPIC id 15 under DRHD base 0xd37fc000 IOMMU 0 [ 1.330546] DMAR-IR: IOAPIC id 8 under DRHD base 0x9d7fc000 IOMMU 7 [ 1.330546] DMAR-IR: IOAPIC id 9 under DRHD base 0x9d7fc000 IOMMU 7
这时再用nvidia-smi命令查看,正常应该会发现要给虚拟机的那张卡消失了,或者用
lspci -vv -s 1a:00.0 | grep driver
命令,如果属于宿主机,则应该是用的NVIDIA的驱动:
Kernel driver in use: nvidia
如果已经解绑,准备送给虚拟机,那应该是vfio的驱动:
Kernel driver in use: vfio-pci创建和修改虚拟机
用virt-install命令:
virt-install --connect qemu:///system -n "虚拟机名称" -r 内存大小(MB) --vcpus 总CPU数,socket=插槽数,cores=每个CPU核心数,threads=每个核心线程数 --host-device 1a:00.0 --host-device 1a:00.1 --host-device 1a:00.2 --host-device 1a:00.3 --features kvm_hidden=on --disk 新建硬盘路径(带文件名),size=新建硬盘大小(GB) -w bridge=桥接网卡1名称,mac=52:54:00:自定义MAC地址1后半 -w bridge=桥接网卡2名称,mac=52:54:00:自定义MAC地址2后半 --virt-type kvm --graphics vnc,listen=0.0.0.0 -c 系统安装镜像.iso --os-variant win10
注意把四个PCI设备要全加进去。
- 参考文章说自定义MAC地址前半必须是52:54:00,我照做了,能不能改没有试。
- VNC默认监听地址是127.0.0.1,这样非主机就无法访问了,因此改为0.0.0.0。
- VNC的子端口可以使用“virsh vncdisplay 虚拟机名称”命令查询,结果应该是“:子端口号”这样,如果在同一局域网内就无所谓,如果主机外接了防火墙/路由器,记得要开放端口,规则是5900+子端口号,比如如果虚拟机VNC子端口是12,那防火墙要转发给主机5912的端口。
- 如果虚拟机是Windows,安装过程需要用VNC来可视化操作,装一个VNC浏览器就可以。
如果创建虚拟机的时候配置写错了,可以用“virsh edit 虚拟机名称”进入虚拟机XML配置修改。比如CPU的配置:
显卡直通的配置:
关于网络virtio的bus号必须大于hostdev,要加新的hostdev可以把virtio的bus号往大了改。
这里我两个虚拟网卡全部用了桥接模式,要桥接主机网卡(主机还得上网),就得在主机上创建网桥,修改“/etc/network/interfaces”:
# 物理网卡1,使用手动模式 auto eno1np0 iface eno1np0 inet manual # 物理网卡2,使用手动模式 auto eno2np1 iface eno2np1 inet manual # 物理网卡1的网桥,使用手动模式 auto vmbr0 iface vmbr0 inet manual bridge_ports eno1np0 bridge_stp off # 物理网卡2的网桥,使用DHCP自动获取IP地址 auto vmbr1 iface vmbr1 inet dhcp bridge_ports eno2np1 bridge_stp off # PPPoEConf自动生成的配置,运行在物理网卡1的网桥上 auto dsl-provider iface dsl-provider inet ppp pre-up /bin/ip link set vmbr0 up # line maintained by pppoeconf provider dsl-provider
我这里配置比较复(离)杂(谱),两个网卡,网卡1拨号上千兆网,是主力网卡;网卡2自动获取地址,运行在百兆内网,用于网卡1寄了之后的救急修复(不想去机房,噪音折寿)。
而虚拟机需要注意,Windows系统自带的驱动可以识别e1000和VMXNet3模式的虚拟网卡,但不会识别virtio模式的虚拟网卡,而后者是更为推荐的方式,性能更好。一个办法就是先用前两者开机,去Fedora People库下载驱动后安装,再关闭虚拟机,网卡模式改为virtio即可。
完成至此,虚拟机即安装完成。在虚拟机上安装驱动后,一切正常:



