Ubuntu 22 + QEMU KVM + OVMF 环境下无法按需在nvidia与vfio-pci间正确绑定/解绑GPU(无需重启)
Ubuntu 22 + QEMU KVM + OVMF 环境下无法按需在nvidia与vfio-pci间正确绑定/解绑GPU(无需重启)
我之前也碰到过几乎一模一样的问题,折腾了好几天才摸清楚门道,给你梳理下可能的原因和解决步骤:
一、核心问题拆解
1. 从VFIO切回nvidia后PyTorch失效的原因
虽然nvidia-smi能正常显示,但这只说明驱动加载成功了,PyTorch依赖的CUDA runtime没有正确初始化——VFIO接管设备时会重置GPU,切回nvidia驱动后,内核层面的CUDA上下文或设备状态没被彻底重置,导致PyTorch无法识别可用CUDA设备。
2. 从nvidia手动切到VFIO后VM报Error 43的原因
大概率是nvidia驱动没完全释放GPU的BAR内存,或是GPU的硬件状态(比如电源管理、显存锁定)还被nvidia驱动占用,导致QEMU无法正确映射BAR区域,触发Nvidia针对虚拟机的保护机制(Error 43)。
二、分场景解决步骤
场景1:从VFIO切回nvidia驱动,让PyTorch正常工作
- 先关闭所有VM,解绑VFIO设备:
# 替换为你的GPU PCI地址,比如0000:01:00.0和0000:01:00.1 echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/unbind echo 0000:01:00.1 > /sys/bus/pci/drivers/vfio-pci/unbind - 加载nvidia相关驱动模块(未自动加载时执行):
modprobe nvidia nvidia_uvm nvidia_modeset nvidia_drm - 强制重置CUDA runtime状态(关键步骤):
# 杀掉所有占用GPU的进程 pkill -9 python pkill -9 nvidia-smi # 重新初始化nvidia设备节点 rm -rf /dev/nvidia* nvidia-smi -r - 测试PyTorch:
现在应该能正常返回import torch print(torch.cuda.is_available())True了。
场景2:从nvidia驱动切到VFIO,让VM正常启动不报错
- 杀掉所有使用GPU的进程,停掉桌面服务(如果是桌面环境):
pkill -9 python systemctl stop display-manager pkill -9 nvidia* - 解绑nvidia驱动的设备,并绑定到vfio-pci:
echo 0000:01:00.0 > /sys/bus/pci/drivers/nvidia/unbind echo 0000:01:00.1 > /sys/bus/pci/drivers/nvidia/unbind # 绑定GPU和音频控制器到vfio-pci(替换为你的设备ID) echo 10de:2684 > /sys/bus/pci/drivers/vfio-pci/new_id echo 10de:22ba > /sys/bus/pci/drivers/vfio-pci/new_id - 重置GPU的PCI状态,强制释放BAR内存:
echo 1 > /sys/bus/pci/devices/0000:01:00.0/reset echo 1 > /sys/bus/pci/devices/0000:01:00.1/reset - 现在启动VM,应该不会再出现Error 43和BAR映射失败的问题。
三、额外优化建议
写切换脚本替代开机绑定,实现按需切换(无需重启):
比如switch-gpu-to-vfio.sh:#!/bin/bash # 停桌面服务和占用GPU的进程 systemctl stop display-manager pkill -9 python nvidia* # 解绑nvidia echo 0000:01:00.0 > /sys/bus/pci/drivers/nvidia/unbind echo 0000:01:00.1 > /sys/bus/pci/drivers/nvidia/unbind # 绑定vfio-pci echo 10de:2684 > /sys/bus/pci/drivers/vfio-pci/new_id echo 10de:22ba > /sys/bus/pci/drivers/vfio-pci/new_id # 重置GPU echo 1 > /sys/bus/pci/devices/0000:01:00.0/reset echo 1 > /sys/bus/pci/devices/0000:01:00.1/reset再写
switch-gpu-to-nvidia.sh:#!/bin/bash # 关闭VM(替换为你的VM名称) virsh shutdown win11-vm # 解绑vfio-pci echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/unbind echo 0000:01:00.1 > /sys/bus/pci/drivers/vfio-pci/unbind # 加载nvidia模块 modprobe nvidia nvidia_uvm nvidia_modeset nvidia_drm # 重置CUDA状态 rm -rf /dev/nvidia* nvidia-smi -r # 重启桌面服务 systemctl start display-manager记得给脚本加执行权限:
chmod +x switch-gpu-*.sh确保内核开启IOMMU相关参数:
在/etc/default/grub的GRUB_CMDLINE_LINUX_DEFAULT中添加:GRUB_CMDLINE_LINUX_DEFAULT="quiet splash iommu=pt vfio_iommu_type1.allow_unsafe_interrupts=1"然后更新grub:
update-grub
备注:内容来源于stack exchange,提问作者SpacemanSanchez




