KVM虚机GPU直通实践总结

      相比于容器部署方式来说,虚机的GPU直通灵活性欠佳,但由于其较强的资源隔离性,可绕过宿主机诸多限制,以及其较好的稳定性,通常用于AI组件生产级部署。容器部署方式虽然简单易用,但容器与宿主机共享nvidia内核驱动,容器内的nvidia驱动、CUDA版本均不能高于宿主机,若宿主机的CUDA版本过低,就必须相应降低python和pytorch版本,难以满足各类科学实验环境的需要。这里主要探讨如何通过虚拟机的GPU直通方式,并寻求更高效的环境构建方法。

      这里使用的宿主机配置情况如下:48C/384G/2.0T、4 * tesla v100 GPU,驱动版本:418.152.00,CUDA版本: 10.1,操作系统版本:RHEL 7.5。目前宿主机部署了一些容器,希望留给容器2块显卡,另外2块显卡配置GPU直通给虚机使用。

1 GPU直通方式A

1.1 前置条件

1.1.1 CPU虚拟化支持

        首先,要满足能部署虚机的最小条件,CPU必须要支持虚拟化技术,通过如下命令检查CPU是否支持vmx或svm指令集,vmx是Intel处理器的虚拟化指令集,svm是AMD处理器的虚拟化指令集。

grep -E '(vmx|svm)' /proc/cpuinfo

lscpu | grep -E '(vmx|svm)'

若操作系统是ubuntu,还可以使用kvm-ok来检查CPU是否支持虚拟化:

sudo apt update

sudo apt install cpu-checker

kvm-ok

1.1.2 主板IOMMU支持

IOMMU(Input/Output Memory Management Unit)是一种硬件技术,用于管理输入/输出设备与系统内存之间的数据传输。它在主板上实现,通常与虚拟化技术一起使用。

GPU直通是一种虚拟化技术,它允许将物理GPU(图形处理单元)直接分配给虚拟机,以提供更好的图形性能和功能。在实现GPU直通时,主板的IOMMU技术起到了关键作用。当使用GPU直通时,虚拟机监视器(hypervisor)需要将物理GPU分配给特定的虚拟机。这样,虚拟机就可以直接访问GPU,并在虚拟机中运行需要图形处理能力的应用程序。主板的IOMMU技术允许虚拟机监视器为虚拟机分配独立的内存空间,并控制虚拟机对GPU的访问权限。通过使用IOMMU,虚拟机监视器可以确保虚拟机之间的内存隔离,并防止虚拟机之外的应用程序对GPU的非法访问。此外,IOMMU还提供了内存地址转换和DMA(Direct Memory Access)保护功能,以确保虚拟机可以正确地访问系统内存,并防止来自GPU的DMA请求对系统内存造成破坏。

主板是否支持IOMMU无法使用命令检查,需要重启进入BIOS。通常在"Advanced"、"Chipset"或"Security"等类似的菜单下。具体的菜单名称和位置会因不同的BIOS/UEFI版本和制造商而异。

1.1.3 GPU的UEFI支持

UEFI(Unified Extensible Firmware Interface)是一种新一代的固件接口标准,用于替代传统的 BIOS。

若要启用GPU直通(GPU Pass-through),GPU支持UEFI并不是必须的,但是它可以提供更好的兼容性和功能。

在使用GPU直通时,UEFI支持可以提供以下好处:

兼容性:某些虚拟化平台可能要求GPU支持UEFI才能启用直通功能。如果GPU不支持UEFI,则可能会遇到驱动程序或兼容性问题。

UEFI GOP:UEFI支持的GPU可以提供UEFI GOP(图形输出协议),这是一种在启动过程中提供图形输出的标准协议。使用UEFI GOP可以提供更好的启动画面和图形性能。

安全启动:UEFI支持的GPU可以与安全启动功能一起使用,确保只有经过验证的操作系统和驱动程序可以加载。

这里使用UEFITool检查GPU是否支持UEFI:

lspci -k | grep -i NVIDIA -A 3

lspci -v -s 62:00.0

【注意】在一些情况下,GPU设备的ROM可能被存储在系统的BIOS中,而不是设备本身。这意味着需要查找系统的BIOS文件来获取GPU设备的ROM。

1.2 宿主机启用IOMMU

步骤1、修改/etc/default/grub中GRUB_CMDLINE_LINUX选项的默认配置,/etc/default/grub文件在Linux系统中用于配置 GRUB2(GRand Unified Bootloader version 2)引导加载器的默认设置,而GRUB_CMDLINE_LINUX中的参数会被添加到所有内核的启动项中,包括 "recovery mode"。在GRUB_CMDLINE_LINUX选项后面添加intel_iommu=on iommu=pt,如下:

GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet intel_iommu=on iommu=pt"

【注意】intel_iommu=on参数适用于Intel的处理器,若AMD处理器,应为:amd_iommu=on。

此外,若在不支持IOMMU的主板上设置了intel_iommu=on,系统一般会忽略这个设置,而不会造成系统无法启动,因此风险较小。

【注意】iommu=pt不是必须的选项,pt即pass through,直通或透传的意思。若设置了该参数,IOMMU 会在尽可能的情况下为设备进行直接物理地址映射,而不是虚拟地址映射。这种模式下,DMA(直接内存访问)操作可以直接使用物理地址,而无需通过 IOMMU 进行地址转换。因此,该参数主要用于提高性能,因为它可以减少IOMMU的地址转换开销,缺点是无法使用IOMMU提供的一些高级特性,如设备隔离和DMA保护。

步骤2、生成新的GRUB配置文件:

grub2-mkconfig -o /boot/grub2/grub.cfg

步骤3、重启操作系统

步骤4、验证IOMMU是否已启用

dmesg | grep -E "DMAR|IOMMU"

[    0.000000] DMAR: IOMMU enabled

观察到这样的日志即说明IOMMU已成功启用。

1.3 启用vfio-pci内核模块

vfio-pci模块主要为虚机提供对物理设备的直接访问,可以使用它将设备从主机的驱动程序中分离出来,并将其分配给虚机。启用该模块的步骤如下:

步骤1、查询显卡的供应商ID和设备型号ID:

lspci -nn | grep -i nvidia

显示内容如下:

5a:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:1db6] (rev a1)

5e:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:1db6] (rev a1)

62:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:1db6] (rev a1)

66:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:1db6] (rev a1)

以第一行为例:5a:00.0即设备在PCI总线上的地址,格式为<总线号>:设备号.功能号;3D controller表示设备的类型;[0302]为设备代码,与设备类型一致;NVIDIA Corporation为设备制造商的名称;Device [10de:1db6]为设备具体型号(ID),格式为[厂商ID:设备型号ID];rev a1意思是设备的修订版本号为a1。

步骤2、创建并修改vfio配置文件,添加一行配置信息:

options vfio-pci ids=10de:1db6

意思是当vfio-pci模块被加载时,它应该接管所有设备ID为10de:1db6的设备。

【注意】这里的设备ID需要与步骤1查询到的GPU设备ID保持一致。

步骤3、配置系统自动加载vfio-pci模块:

echo 'vfio-pci' > /etc/modules-load.d/vfio-pci.conf

/etc/modules-load.d/ 目录下的配置文件用于定义在系统启动时需要自动加载的内核模块。每个文件中可以包含一行或多行的模块名称,每行一个模块。因此该命令的功能是让系统在下次启动时,自动加载vfio-pci模块。

步骤4、重启后检查vfio是否成功加载

dmesg | grep -i vfio

该小节执行完成后,在宿主机运行nvidia-smi发现所有GPU均无法使用,但在虚机可以正常绑定所有GPU,这不太符合最初的需求,即部分保留给宿主机,部分给虚机使用。

1.4 升级qemu

关于这一步的必要性,本人认为升级的理由可能是新版本的qemu往往包含了更多的特性和修复,可能对GPU直通操作至关重要。

步骤1、确认qemu的版本号,目前在RHEL 7.5上为1.5.3,可升级到2.12

/usr/libexec/qemu-kvm -version

步骤2、采用rpm包安装方式升级,涉及到的rpm可从pkgs.org自行下载

glusterfs-6.0-37.el7.x86_64.rpm

glusterfs-api-6.0-37.el7.x86_64.rpm

glusterfs-cli-6.0-37.el7.x86_64.rpm

glusterfs-client-xlators-6.0-37.el7.x86_64.rpm

glusterfs-libs-6.0-37.el7.x86_64.rpm

qemu-img-ev-2.12.0-44.1.el7_8.1.x86_64.rpm

qemu-kvm-common-ev-2.12.0-44.1.el7_8.1.x86_64.rpm

qemu-kvm-ev-2.12.0-44.1.el7_8.1.x86_64.rpm

【注意】安装qemu ev仓库配置包centos-release-qemu-ev是依赖于centos-release的,如果在RHEL环境中采用yum方式安装会造成版本冲突或依赖关系问题,因此采用rpm部署方式,虽然相对麻烦一点。

步骤3、重启libvirt守护进程以使qemu变更生效

systemctl restart libvirtd

步骤4、检查qemu升级后版本

/usr/libexec/qemu-kvm -version

2 GPU直通方式B

采用如上GPU直通方式A,vfio-pci模块的绑定配置匹配的是供应商与设备ID,这会导致绑定主机上所有同一型号的GPU,不符合我们期望的部分GPU给宿主机的容器使用,部分GPU做直通给虚机使用的情形,为此需要考虑绑定设备PCI地址的方式。

此外,1.2小节是前提条件,需要提前完成。

2.1 dracut发行版直通方式

【常识】dracut是一个用于创建initramfs(初始RAM文件系统)影像的工具,而initramfs是一个临时的根文件系统,在系统启动过程中被加载,然后启动真正的根文件系统。本小节针对使用dracut的发行版操作系统,如Ubuntu、Fedora、CentOS、RHEL和OpenSUSE等。

步骤1、确定需要配置直通的两块显卡

lspci -nnk | grep -i nvidia -A3

步骤2、覆盖指定PCI 设备的默认驱动程序,形如:

echo "vfio-pci" > /sys/bus/pci/devices/0000\:5a\:00.0/driver_override

我们提前准备好覆盖默认驱动程序的脚本:

cat >/usr/sbin/vfio-pci-override.sh<<EOF

#!/bin/sh

DEVS="0000:5a:00.0 0000:5e:00.0"

for DEV in \$DEVS; do

    echo "vfio-pci" > /sys/bus/pci/devices/\$DEV/driver_override

done

modprobe -i vfio-pci

EOF

赋予该脚本可执行属性:

chmod +x /usr/sbin/vfio-pci-override.sh

【注意】DEVS应指定完整的PCI设备地址,开头的0000为PCI域。

步骤3、创建一个新的dracut模块

mkdir /usr/lib/dracut/modules.d/20vfio

【注意】目录/usr/lib/dracut/modules.d/下的每一个子目录都代表一个 dracut模块,每个模块至少包含一个module-setup.sh脚本(用于模块的安装和配置设定),20这个数字设置的原则是要小于被覆盖的驱动程序模块nvidia。

cat >/usr/lib/dracut/modules.d/20vfio/module-setup.sh<<EOF

#!/bin/bash

check() {

    return 0

}

depends() {

    return 0

}

install() {

    declare moddir=\${moddir}

    inst_hook pre-udev 00 "\${moddir}/vfio-pci-override.sh"

}

EOF

【注意】module-setup.sh脚本中,check()、depends()和install()都是固定函数,dracut在处理模块时会调用这些函数。其中:

① check()函数用于检查是否应该安装这个模块,返回0表示应该被安装,1表示不应被安装,因此可在check()中检查某内核模块是否存在,因此可用以下脚本改写check():

check() {

if [ -d "/sys/module/vfio_pci" ]; then

return 0

else

return 1

fi

}

【注意】/sys/module/vfio_pci只有在vfio-pci模块已被加载时才存在,因此可做为判断条件。

② depends() 用于声明当前模块的依赖关系,该函数应返回一个空格分割的模块名列表,这些模块会先于当前模块安装,比如当前模块依赖udev和systemd,可返回udev systemd。

③ install() 用于实际安装模块。在这个函数中,可复制文件、创建链接、添加udev规则等,但应使用dracut提供的一些函数来执行这些操作,例如inst函数用于安装文件,inst_hook函数用于添加钩子。

【注意】udev规则通常位于/etc/udev/rules.d或/lib/udev/rules.d目录下,文件名以.rules结尾,如:ACTION=="add", KERNEL=="sda", SYMLINK+="my_disk"

install() 函数中引用的${moddir}变量在dracut框架中是预定义的,在本环境中即为/usr/lib/dracut/modules.d/20vfio。inst_hook函数会把脚本/usr/lib/dracut/modules.d/20vfio/vfio-pci-override.sh安装到生成的initramfs映像中,并在 udev 事件发生之前运行它。

步骤4、为module-setup.sh授予可执行权限、创建vfio-pci-override.sh的软链接

chmod +x /usr/lib/dracut/modules.d/20vfio/module-setup.sh

ln -s /usr/sbin/vfio-pci-override.sh /usr/lib/dracut/modules.d/20vfio/vfio-pci-override.sh

步骤5、指定dracut在生成initramfs时的特定参数

# 【注意】/etc/dracut.conf.d/*.conf通常用于为特定的dracut模块设置选项,或者更改Dracut的默认行为。

cat >/etc/dracut.conf.d/vfio.conf <<EOF

# 告知Dracut在生成initramfs时需要包含vfio模块

add_dracutmodules+=" vfio "

# 强制Dracut在生成initramfs时包含这些驱动:vfio(VFIO框架的核心驱动)、vfio_pci(特定的VFIO设备驱动,用于管理PCI设备)和vfio_iommu_type1(IOMMU驱动,控制设备如何访问物理内存)

force_drivers+=" vfio vfio_pci vfio_iommu_type1 "

# 通过在Dracut配置中使用install_items+=,可以确保在OS启动时,这些程序会被包含在initramfs中,无论它们是否是initramfs的默认组成部分。这对于需要在initramfs启动过程中运行特定脚本或命令的系统来说是非常重要的

install_items+="/usr/sbin/vfio-pci-override.sh /usr/bin/find /usr/bin/dirname"

EOF

步骤6、强制生成一个新的initramfs,并在这个过程中输出详细的信息

dracut -fv

步骤7、重启操作系统后检查显卡使用情况

modprobe vfio-pci

lspci -nnk | grep -i nvidia -A3

可以看出,前两块显卡的内核驱动已改为vfio-pci,而后两块显卡依旧为nvidia。

nvidia-smi

可以看出,宿主机上只能识别到后两块显卡。

【疑惑】在实践过程中发现:重启宿主机操作系统后,需要执行modprobe vfio-pci,否则lspci看到的Kernel Driver依旧是nvidia。

2.2 mkinitcpio发行版直通方式

mkinitcpio也是一个用于生成initramfs的工具,它通常用于Arch Linux和其衍生发行版,如:Arch Linux、Manjaro Linux、Antergos和EndeavourOS等,由于这一类型操作系统较少使用,缺少相应环境,不再详细说明。

步骤1、创建脚本/usr/bin/vfio-pci-override.sh绑定GPU

#!/bin/sh

GROUP="0000:00:03.0"

DEVS="0000:03:00.0 0000:03:00.1"

if [ ! -z "$(ls -A /sys/class/iommu)" ]; then

for DEV in $DEVS; do

echo "vfio-pci" > /sys/bus/pci/devices/$GROUP/$DEV/driver_override

done

fi

modprobe -i vfio-pci

步骤2、创建/etc/modprobe.d/vfio.conf

install vfio-pci /usr/bin/vfio-pci-override.sh

步骤3、修改/etc/mkinitcpio.conf,添加vfio-pci和vfio_iommu_type1驱动

MODULES=(ext4 vfat vfio-pci vfio_iommu_type1)

FILES=(/etc/modprobe.d/vfio.conf /usr/bin/vfio-pci-override.sh)

步骤4、重新生成initramfs

步骤5、重启和检查GPU

3 KVM配置

3.1 GPU设备绑定

在virt-manager界面中依次执行:“Add Hardware”--“PCI Host Device”--“选择GPU”:

【注意】如果在KVM虚拟机中错误地绑定了NVIDIA驱动的显卡,可能会导致宿主机的驱动程序冲突。这可能会导致内核层面被挂起,使得KVM虚拟机和宿主机所有运行nvidia-smi命令的进程无法终止。无法通过kill命令终止这些进程,即使执行reboot重启操作也会受影响。此时,唯一的解决方法是强制关机。因此在生产操作时要特别小心。

3.2 为虚机安装nvidia驱动

1、目前nvidia发布的CUDA Toolkit包含了nvidia驱动,因此推荐直接下载CUDA Toolkit:

wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run

2、在安装前务必安装编译环境和内核源码

yum -y install wget gcc cmake kernel-devel pciutils

3、禁用nouveau,nouveau为Linux开源的nvidia显卡驱动,与nvidia存在冲突:

lsmod | grep nouveau

cat >> /etc/modprobe.d/blacklist-nouveau.conf <<EOF

    blacklist nouveau

    options nouveau modeset=0

EOF

dracut --force

reboot

4、安装dkms,dkms组件可以确保nvidia的显卡驱动在内核更新后可重新构建并安装,以保证cuda功能正常工作

yum install epel-release

yum install dkms

5、安装cuda toolkit:

sh cuda_11.8.0_520.61.05_linux.run

【注意】无需安装nvidia-fs,它依赖于infiniband 适配器驱动

4 总结

本资料参考了热心网友的分享的资料:https://zhuanlan.zhihu.com/p/556536202、https://blog.csdn.net/u010099177/article/details/120709515,并结合chatgpt对每一步骤进行了解释,希望自己和他人对GPU直通的整体操作有个全面了解,也希望更多朋友提出宝贵意见。

  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值