PVE 8.3 直通 Tesla P40 给 Ubuntu 22.04 详细教程(含 BAR0 is 0M 根治方案)
1. 适用范围与环境确认
本教程针对如下环境编写(不同版本参数可能略有差异,请注意核对):
| 项目 | 值 |
|---|---|
| 宿主机 | Proxmox VE 8.3.0(内核 6.8.4-2-pve,QEMU 9.0.2) |
| CPU 平台 | Intel(需 VT-d;AMD 平台请将参数中的 intel_iommu 改为 amd_iommu) |
| GPU | NVIDIA Tesla P40(GP102,24GB,PCI ID 10de:1b38,Pascal 架构,Compute Capability 6.1) |
| 虚拟机 | machine=q35,bios=ovmf(UEFI),内存 64GB |
| 客户机 | Ubuntu 22.04.5 LTS,内核 5.15.0-generic |
| 驱动 | nvidia-driver-580-server(580.173.02,DKMS) |
| CUDA | CUDA Toolkit 12.6(不可用 CUDA 13,见 §7) |
关键前提:直通设备必须在独立的 IOMMU 组中(组内只有该 GPU 及其功能设备),否则无法直通或需要额外拆分策略。
2. 宿主机准备(PVE 侧)
2.1 BIOS 开启虚拟化与 IOMMU
进主板 BIOS,开启:
Intel Virtualization Technology (VT-x)
Intel VT-d (Directed I/O) / AMD IOMMU
部分主板还有 "Above 4G Decoding"(影响大显存 BAR,建议开启)
2.2 内核参数启用 IOMMU
# GRUB 方式 nano /etc/default/grub # 修改: GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt" update-grub # systemd-boot 用户则编辑 /etc/kernel/cmdline 后执行 proxmox-boot-tool refresh reboot
重启后确认 IOMMU 已启用:
dmesg | grep -e DMAR -e IOMMU # 期望看到:DMAR: IOMMU enabled
2.3 确认设备 BDF 与 IOMMU 组
lspci | grep -i nvidia # 示例输出:82:00.0 3D controller: NVIDIA Corporation GP102GL [Tesla P40] [10de:1b38]
检查该设备是否独占 IOMMU 组(PVE 官方脚本):
#!/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理想结果:P40 所在组内只有 GPU 自己(有时还带 Audio Controller,需要一并直通)。
2.4 绑定 vfio-pci 并屏蔽 nouveau
# 将 GPU 绑定到 vfio-pci echo "options vfio-pci ids=10de:1b38" > /etc/modprobe.d/vfio.conf # 屏蔽 nouveau(若存在) echo "blacklist nouveau" > /etc/modprobe.d/blacklist-nouveau.conf echo "options nouveau modeset=0" >> /etc/modprobe.d/blacklist-nouveau.conf # 保证 vfio-pci 优先加载 echo "softdep nvidia pre: vfio-pci" > /etc/modprobe.d/nvidia.conf # 宿主机有 N 卡驱动时 update-initramfs -u reboot
重启后确认 P40 已被 vfio-pci 接管:
lspci -k -s 82:00 # Kernel driver in use: vfio-pci lsmod | grep -E 'vfio' # 应看到 vfio_pci / vfio_iommu_type1
3. 创建并配置虚拟机
3.1 基础 VM 创建建议
# 以创建 VM120 为例 qm create 120 --name ubuntu22-p40 --memory 65536 --cores 16 --sockets 1 --net0 virtio,bridge=vmbr0 --scsi0 local-lvm:vmdisk,size=500G --ide2 local-lvm:cloudinit,media=cdrom --boot c --ostype l26 --machine q35 --bios ovmf --efidisk0 local-lvm:1,efitype=4m,pre-enrolled-keys=0
要点:
machine=q35、bios=ovmf:P40 大 BAR 直通需要 UEFI;
在BIOS里关闭安全启动(不然后安装驱动时会提示输入密码)
pre-enrolled-keys=0:避免 Secure Boot 干扰第三方驱动(NVIDIA DKMS);内存按需(本例 64GB)。内存越大,64 位 PCI 地址窗口越紧张,越需要 §5 的修复参数。
*** 以下是一个真实配置 ***
args: -global q35-pcihost.pci-hole64-size=96G -fw_cfg name=opt/ovmf/X-PciMmio64Mb,string=65536 bios: ovmf boot: order=scsi0;ide2;net0 cores: 10 cpu: x86-64-v2-AES efidisk0: GreatWall:vm-120-disk-0,efitype=4m,pre-enrolled-keys=1,size=4M hostpci0: 0000:82:00,pcie=1 ide2: local:iso/ubuntu-22.04.5-live-server-amd64.iso,media=cdrom,size=2086842K machine: q35 memory: 65536 meta: creation-qemu=9.0.2,ctime=1787823223 name: ubuntu22-p40 net0: virtio=BC:24:11:67:54:94,bridge=vmbr0,firewall=1 numa: 1 onboot: 1 ostype: l26 scsi0: GreatWall:vm-120-disk-1,iothread=1,size=500G,ssd=1 scsihw: virtio-scsi-single smbios1: uuid=d8a818f2-41e3-403f-bdeb-48c51d010364 sockets: 2 vmgenid: 3829d72e-6451-4352-9816-896d2bdea67f
3.2 添加 GPU 直通
qm set 120 --hostpci0 '0000:82:00,pcie=1'
pcie=1:以 PCIe 端点方式直通(P40 首选,避免降级传统 PCI 导致性能/兼容问题);若 GPU 还带独立 Audio Controller,另加一行
hostpci1。
3.3 先不加 args,完成系统安装
安装 Ubuntu 22.04 到虚拟磁盘,配置好静态 IP(本例 192.168.8.24),SSH 可达后继续。
4. 客户机驱动安装(Ubuntu 22.04)
4.1 安装 580-server 驱动(apt + DKMS)
sudo apt update sudo apt install -y nvidia-driver-580-server sudo reboot
4.2 确认驱动编译与加载
dkms status # 期望:nvidia-srv/580.173.02, 5.15.0-190-generic: installed lsmod | grep nvidia # 应能看到 nvidia 模块 ls -l /dev/nvidia* # nvidia0/nvidiactl/nvidia-uvm 等节点 nvidia-smi # 若正常则直接跳到 §6
注意:不要用 NVIDIA 官网 runfile 安装(容易覆盖 DKMS 链路、与 apt 包冲突)。
5. 核心排障:NVRM: BAR0 is 0M @ 0x0 根治
5.1 症状
nvidia-smi:NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driverdmesg:
NVRM: This PCI I/O region assigned to your NVIDIA device is invalid: NVRM: BAR0 is 0M @ 0x0 (PCI:0000:01:00.0) nvidia: probe of 0000:01:00.0 failed with error -1
5.2 快速定位(不要先重装驱动)
# guest 内:看 BAR 是否分配(全 0 即为未分配) cat /sys/bus/pci/devices/0000:01:00.0/resource # guest 内:确认设备 BDF 与 BAR 声明 lspci -v -s 01:00.0 | grep -E 'Memory at|Region' # host 内:核对 VM 配置与生成的 QEMU 命令行 cat /etc/pve/qemu-server/120.conf qm showcmd 120
排障经验:先看 dmesg | grep NVRM 是否有 BAR 报错,再核对宿主机直通参数,而不是盲目卸载重装驱动。若 DKMS installed、nvidia.ko 存在、nouveau 已屏蔽、Secure Boot 已关,问题几乎都在 PCI BAR 分配层。
5.3 根因
P40 的 BAR 布局为:BAR0=16M(32 位非预取)、BAR1=32G(64 位预取)、BAR3=32M。当 guest 内存大(本例 64GB)且显存 BAR 大时,需要两层地址空间都足够:
QEMU 底层 64 位 PCI hole:默认 32G,被 64GB 内存 + 32GB BAR 挤爆;
OVMF 固件内的 64 位 MMIO 窗口:默认仅 32GB(对应 edk2 的
opt/ovmf/X-PciMmio64Mb),同样装不下。
只修一层不够(实测仅扩 QEMU hole 后 BAR1/3 恢复,但 BAR0 仍为 0M);改 pcie=0 传统 PCI 直通同样无效。
5.4 修复(在宿主机执行,两步合一)
qm set 120 --hostpci0 '0000:82:00,pcie=1' # 保持 PCIe 直通 qm set 120 --args '-global q35-pcihost.pci-hole64-size=96G -fw_cfg name=opt/ovmf/X-PciMmio64Mb,string=65536' qm start 120
最终 /etc/pve/qemu-server/120.conf 关键三行:
machine: q35 hostpci0: 0000:82:00,pcie=1 args: -global q35-pcihost.pci-hole64-size=96G -fw_cfg name=opt/ovmf/X-PciMmio64Mb,string=65536
参数含义:
| 参数 | 作用 |
|---|---|
-global q35-pcihost.pci-hole64-size=96G | 把 QEMU 底层 64 位 PCI hole 扩到 96GB(容纳 64G 内存后的大 BAR) |
-fw_cfg name=opt/ovmf/X-PciMmio64Mb,string=65536 | 把 OVMF 固件的 64 位 MMIO 窗口扩到 65536MB=64GB(关键) |
说明:PVE 的
machine配置项只接受白名单内的选项,直接写q35,pci-hole64-size=96G会被拒绝;因此这类参数统一走args透传给 QEMU。修改前建议先备份cp /etc/pve/qemu-server/120.conf /root/120.conf.bak,改后用qm showcmd 120校验参数已生效。
窗口大小经验值:X-PciMmio64Mb 取 ≥ 内存 + 最大 BAR 对齐值,64GB 内存给 65536 即可;更大组合(128GB 内存)可给 131072。重启后即可恢复到 §4 的驱动状态,若此前驱动已装好则直接 nvidia-smi 应正常。
6. 验证清单
# guest 内 lspci -v -s 01:00.0 | grep -E 'Memory at' # 期望:BAR0(16M)、BAR1(32G)、BAR3(32M) 均为有效地址,不再是 <ignored>/0x0 cat /sys/bus/pci/devices/0000:01:00.0/resource # 前三行含非零地址 nvidia-smi # 期望输出 Tesla P40、Driver 580.173.02、显存 23040MiB 全部可见
7. CUDA 安装(注意版本限制)
兼容性关键点:CUDA 13.0 已移除对 Pascal 架构的支持,Tesla P40 只能使用 CUDA 12.x 工具链。
驱动 580.173.02 向下兼容 CUDA 12.x(12.x 最低要求驱动 ≥ 525),所以组合为:驱动 580 + CUDA Toolkit 12.6。
# guest 内(官方 apt 仓库路线,只装工具链、不覆盖现有驱动) cd /tmp wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update sudo apt-get install -y cuda-toolkit-12-6 # 环境变量 echo 'export PATH=/usr/local/cuda/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc # 验证 nvcc --version
编译运行样例验证计算可用:
# 编译 deviceQuery(nvcc 自带样例需单独装 cuda-samples,或直接 nvcc 编译测试内核)
cat > /tmp/t.cu <<'EOF'
#include <cstdio>
__global__ void k(int *a){ *a = 42; }
int main(){
int h=0, *d; cudaMalloc(&d, sizeof(int));
k<<<1,1>>>(d); cudaMemcpy(&h,d,sizeof(int),cudaMemcpyDeviceToHost);
printf("CUDA OK, result=%d
", h); return 0;
}
EOF
nvcc -o /tmp/t /tmp/t.cu && /tmp/t
# 期望:CUDA OK, result=428. 常见问题速查表
| 现象 | 排查方向 | 处理 |
|---|---|---|
BAR0 is 0M @ 0x0 / probe -1 | guest dmesg + qm showcmd | 按 §5 加 pci-hole64-size + X-PciMmio64Mb 并重启 VM |
宿主机 vfio-pci 0000:82:00.0: BAR 3: can't reserve | 宿主机显存占用 | 宿主机内核加 video=efifb:off |
| Secure Boot 拦截 DKMS | 固件设置 | 关 Secure Boot 或给模块签名 |
| nouveau 抢占设备 | lspci -k 看驱动 | 确认 blacklist + update-initramfs -u |
| VM 无法启动 | qm start 报错 | 检查 IOMMU 组是否独立、设备是否被其他 VM 占用 |
| 大显存 BAR 仍分配异常 | 确认内存配置 | 视内存大小把 X-PciMmio64Mb 调到 131072 或更大 |
9. 命令速查(宿主机)
# 诊断 lspci -k -s 82:00 # 设备驱动接管情况 cat /etc/pve/qemu-server/120.conf # VM 配置 qm showcmd 120 # 实际生成的 QEMU 命令行 qm status 120 # 修改与重启 qm set 120 --hostpci0 '0000:82:00,pcie=1' qm set 120 --args '-global q35-pcihost.pci-hole64-size=96G -fw_cfg name=opt/ovmf/X-PciMmio64Mb,string=65536' qm shutdown 120 && qm start 120 # 或 qm stop 强制
10. 总结
直通链路共三层:宿主 IOMMU/vfio → QEMU 地址空间 → OVMF 固件窗口,P40 这类大显存 GPU 三处都要配到位;
排障优先级:
dmesg定位 BAR → 核对 VM 直通参数 → 必要时加pci-hole64-size+X-PciMmio64Mb→ 再考虑驱动层;版本红线:Pascal(P40) 不支持 CUDA 13,工具链固定用 CUDA 12.x;
一切修改先备份
120.conf,重启前用qm showcmd校验。(内容由小泥巴根据一次实际经历整理)