强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

10 - 设备直通

10 - 设备直通

掌握 VFIO 框架下 PCI/GPU/NVMe/USB 设备直通配置,实现虚拟机接近原生的硬件性能。


10.1 设备直通概述

设备直通(Device Passthrough / PCI Passthrough)允许将物理硬件设备直接分配给虚拟机使用,绕过 Hypervisor 的设备模拟层,获得接近原生的性能。

设备直通架构:
  ┌────────────────────────────┐
  │        Virtual Machine      │
  │   ┌──────────────────┐     │
  │   │  物理设备驱动      │     │  ← VM 直接使用原生驱动
  │   └────────┬─────────┘     │
  ├────────────┼────────────────┤
  │   ┌────────┴─────────┐     │
  │   │     VFIO          │     │  ← 内核 VFIO 框架
  │   └────────┬─────────┘     │
  │   ┌────────┴─────────┐     │
  │   │     IOMMU         │     │  ← VT-d / AMD-Vi
  │   └────────┬─────────┘     │
  ├────────────┼────────────────┤
  │            │                │
  │   ┌────────┴─────────┐     │
  │   │  物理硬件设备      │     │  ← 网卡 / GPU / NVMe
  │   └──────────────────┘     │
  └────────────────────────────┘

适用场景

场景设备类型说明
高性能网络网卡 (NIC)10/25/100Gbps 网卡直通
GPU 计算/渲染GPUCUDA/ML 训练、游戏、图形渲染
高速存储NVMe SSD数据库、高 IOPS 工作负载
USB 设备USB 控制器专业外设、加密狗
声卡音频设备专业音频处理

直通 vs 模拟

对比维度设备模拟半虚拟化 (virtio)设备直通 (VFIO)
性能低 (10-50%)高 (70-90%)最高 (95-99%)
兼容性最好需要 virtio 驱动需要原生驱动
热插拔❌ 有限
迁移❌ 不支持
设备共享✅ 多 VM 共享✅ 多 VM 共享❌ 独占
配置难度简单简单复杂

10.2 IOMMU 配置

启用 IOMMU

IOMMU(I/O Memory Management Unit)是设备直通的基础:

# 检查 IOMMU 是否启用
dmesg | grep -i iommu

# Intel VT-d: 在 GRUB 中启用
# 编辑 /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt"

# AMD-Vi: 在 GRUB 中启用
GRUB_CMDLINE_LINUX_DEFAULT="amd_iommu=on iommu=pt"

# 更新 GRUB
sudo update-grub  # Debian/Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg  # Fedora/RHEL

# 重启
sudo reboot

验证 IOMMU

# 重启后验证
dmesg | grep -i iommu
# 预期输出:
# DMAR: IOMMU enabled
# DMAR: Intel(R) Virtualization Technology for Directed I/O

# 列出 IOMMU 分组
for d in /sys/kernel/iommu_groups/*/devices/*; do
    n=${d#*/iommu_groups/*}; n=${n%%/*}
    printf 'IOMMU Group %s: ' "$n"
    lspci -nns "${d##*/}"
done | sort -V

IOMMU 分组说明

IOMMU 分组规则:
  同一个 IOMMU 分组中的设备必须全部直通给同一个虚拟机
  
  IOMMU Group 0:
    00:00.0 Host bridge
    00:01.0 PCI bridge
    00:01.1 PCI bridge
  
  IOMMU Group 1:
    00:02.0 VGA compatible controller [Intel UHD 630]
  
  IOMMU Group 2:
    00:1f.0 ISA bridge
    00:1f.3 Audio device
    00:1f.4 SMBus
  
  IOMMU Group 14:
    03:00.0 Ethernet controller [Intel I350]
  
  → 可以单独直通 Group 14 的网卡
  → 如果要直通 Group 1 中的 GPU,需要同时直通 Audio 设备

10.3 VFIO 基础配置

加载 VFIO 模块

# 确保 VFIO 模块已加载
sudo modprobe vfio-pci
sudo modprobe vfio_iommu_type1
sudo modprobe vfio

# 验证
lsmod | grep vfio

# 设置开机自动加载
cat << 'EOF' | sudo tee /etc/modules-load.d/vfio-pci.conf
vfio-pci
vfio_iommu_type1
vfio
EOF

绑定设备到 vfio-pci 驱动

# 查找设备的 Vendor:Device ID
lspci -nn | grep -i ethernet
# 输出示例: 03:00.0 Ethernet controller [0200]: Intel Corporation I350 Gigabit Network Connection [8086:1521] (rev 01)

# 使用 PCI 地址绑定
# 方法 1: 使用 driver_override
echo "0000:03:00.0" | sudo tee /sys/bus/pci/devices/0000:03:00.0/driver/unbind
echo "vfio-pci" | sudo tee /sys/bus/pci/devices/0000:03:00.0/driver_override
echo "0000:03:00.0" | sudo tee /sys/bus/pci/drivers/vfio-pci/bind

# 方法 2: 使用 modprobe 配置(推荐,持久化)
# 获取设备 ID
VENDOR=$(lspci -nn -s 03:00.0 | grep -oP '\[8086:\K[0-9a-f]+')

# 配置 vfio-pci 选项
cat << EOF | sudo tee /etc/modprobe.d/vfio.conf
options vfio-pci ids=8086:${VENDOR}
softdep igb pre: vfio-pci
EOF

# 更新 initramfs
sudo update-initramfs -u  # Debian/Ubuntu
sudo dracut -f             # Fedora/RHEL

验证绑定

# 检查设备使用的驱动
lspci -k -s 03:00.0
# 预期输出:
# Kernel driver in use: vfio-pci

# 检查 VFIO 设备
ls -la /dev/vfio/
# 预期输出:
# vfio
# 14  (对应 IOMMU 分组号)

10.4 PCI 网卡直通

# 使用 QEMU 直通网卡
qemu-system-x86_64 \
  -enable-kvm -cpu host -m 4G \
  -machine q35,accel=kvm \
  -device vfio-pci,host=03:00.0 \
  -drive file=vm.qcow2,format=qcow2,if=virtio \
  -nographic

# 使用 libvirt XML
# <hostdev mode='subsystem' type='pci' managed='yes'>
#   <source>
#     <address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
#   </source>
# </hostdev>

SR-IOV(单根 I/O 虚拟化)

SR-IOV 允许一个物理网卡创建多个虚拟功能(VF),每个 VF 可以直通给不同的虚拟机:

# 检查网卡是否支持 SR-IOV
lspci -v -s 03:00.0 | grep -i sriov

# 启用 SR-IOV 虚拟功能
echo 4 | sudo tee /sys/class/net/ens3f0/device/sriov_numvfs

# 查看创建的 VF
lspci | grep "Virtual Function"
# 预期输出:
# 03:10.0 Ethernet controller: Intel Corporation I350 Virtual Function
# 03:10.4 Ethernet controller: Intel Corporation I350 Virtual Function
# 03:11.0 Ethernet controller: Intel Corporation I350 Virtual Function
# 03:11.4 Ethernet controller: Intel Corporation I350 Virtual Function

# 将 VF 绑定到 vfio-pci
echo "vfio-pci" | sudo tee /sys/bus/pci/devices/0000:03:10.0/driver_override
echo "0000:03:10.0" | sudo tee /sys/bus/pci/drivers/vfio-pci/bind

# 分配给虚拟机
qemu-system-x86_64 \
  -device vfio-pci,host=03:10.0 \
  -device vfio-pci,host=03:10.4

10.5 GPU 直通

GPU 直通是最复杂的设备直通场景,需要注意 IOMMU 分组和驱动隔离。

准备工作

# 1. 检查 GPU 型号
lspci | grep -i vga
lspci | grep -i audio

# 2. 检查 IOMMU 分组
# GPU 和其音频设备必须在同一个分组中

# 3. 如果使用 NVIDIA 消费级 GPU,需要添加隐藏标志
# NVIDIA 驱动会检测是否在 VM 中运行,需要隐藏 VM 特征

NVIDIA GPU 直通

# 配置内核参数
# /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="intel_iommu=on iommu=pt vfio-pci.ids=10de:2484,10de:228b"

# 10de:2484 = NVIDIA RTX 3060 GPU
# 10de:228b = NVIDIA RTX 3060 Audio

# 更新 initramfs 和 GRUB
sudo update-initramfs -u
sudo update-grub
sudo reboot

GPU 直通 QEMU 配置

qemu-system-x86_64 \
  -enable-kvm \
  -cpu host,kvm=off,hv_vendor_id=randomid \
  -m 8G \
  -machine q35,accel=kvm,kernel-irqchip=on \
  -smp 8,sockets=1,cores=8,threads=1 \
  -device vfio-pci,host=01:00.0,x-vga=on \
  -device vfio-pci,host=01:00.1 \
  -drive file=windows-vm.qcow2,format=qcow2,if=virtio \
  -device virtio-net-pci,netdev=net0 \
  -netdev user,id=net0,hostfwd=tcp::3389-:3389 \
  -bios /usr/share/OVMF/OVMF_CODE.fd \
  -nographic

NVIDIA 隐藏虚拟机特征

# NVIDIA 消费级 GPU 驱动会检测虚拟化环境
# 需要隐藏以下特征:

# 方法 1: 使用 hv_vendor_id
-cpu host,kvm=off,hv_vendor_id=randomid

# 方法 2: 隐藏 KVM 签名
-cpu host,kvm=off \
-global ICH9-LPC.disable_s3=1 \
-global ICH9-LPC.disable_s4=1

# 方法 3: 使用 libvirt XML
# <cpu mode='host-passthrough'>
#   <feature policy='disable' name='hypervisor'/>
# </cpu>

GPU 直通 libvirt XML

<hostdev mode='subsystem' type='pci' managed='yes'>
  <source>
    <address domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
  </source>
  <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0' multifunction='on'/>
</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='0x1'/>
</hostdev>

10.6 NVMe 直通

# 查找 NVMe 设备
lspci | grep -i nvme

# 绑定到 vfio-pci
echo "vfio-pci" | sudo tee /sys/bus/pci/devices/0000:04:00.0/driver_override
echo "0000:04:00.0" | sudo tee /sys/bus/pci/drivers/nvme/unbind
echo "0000:04:00.0" | sudo tee /sys/bus/pci/drivers/vfio-pci/bind

# 启动虚拟机
qemu-system-x86_64 \
  -enable-kvm -cpu host -m 4G \
  -machine q35,accel=kvm \
  -device vfio-pci,host=04:00.0 \
  -drive file=vm.qcow2,format=qcow2,if=virtio \
  -nographic

NVMe 直通 vs 虚拟磁盘

对比NVMe 直通虚拟磁盘 (virtio-blk)
IOPS500K+100-200K
延迟10-50μs100-500μs
带宽3-7 GB/s1-3 GB/s
快照
迁移
配置复杂简单

10.7 USB 直通

直通 USB 控制器

# 直通整个 USB 控制器
lspci | grep -i usb
# 00:14.0 USB controller: Intel Corporation Cannon Lake PCH USB 3.1 xHCI Host Controller

qemu-system-x86_64 \
  -enable-kvm -cpu host -m 4G \
  -device vfio-pci,host=00:14.0 \
  -drive file=vm.qcow2,format=qcow2 \
  -nographic

直通单个 USB 设备

# 查看 USB 设备
lsusb

# 使用 QEMU 的 USB 直通(不需要 VFIO)
qemu-system-x86_64 \
  -enable-kvm -cpu host -m 4G \
  -drive file=vm.qcow2,format=qcow2 \
  -device usb-ehci,id=usb \
  -device usb-host,vendorid=0x046d,productid=0xc077 \
  -nographic

libvirt USB 直通 XML

<hostdev mode='subsystem' type='usb' managed='yes'>
  <source>
    <vendor id='0x046d'/>
    <product id='0xc077'/>
  </source>
</hostdev>

10.8 VFIO 故障排查

常见问题

问题原因解决方案
设备绑定失败驱动冲突先 unbind 原驱动
IOMMU 分组冲突多设备同组ACS 覆写补丁或换槽
启动黑屏GPU 未初始化使用 VNC 先配置系统
代码 43 (Windows)NVIDIA 检测到 VM添加隐藏标志
性能差中断亲和性绑定 CPU 亲和性

ACS 覆写补丁

# 对于 IOMMU 分组不理想的主板,可以使用 ACS 覆写补丁
# 需要自定义内核或使用 DKMS 模块

# 检查 ACS 支持
sudo lspci -vvv | grep -i "acs"

# 添加内核参数(不推荐用于生产环境)
pcie_acs_override=downstream,multifunction

中断亲和性优化

# 查看设备中断
cat /proc/interrupts | grep vfio

# 设置中断 CPU 亲和性
# 假设 VFIO 设备使用中断 42-45
echo 4 > /proc/irq/42/smp_affinity
echo 8 > /proc/irq/43/smp_affinity
echo 2 > /proc/irq/44/smp_affinity
echo 1 > /proc/irq/45/smp_affinity

要点回顾

要点核心内容
IOMMUVT-d / AMD-Vi,设备直通的基础
VFIOLinux 内核框架,管理设备直通
GPU 直通需要隐藏 VM 特征,IOMMU 分组一致
NVMe 直通最高性能,但失去快照和迁移能力
USB 直通支持控制器级和设备级直通

注意事项

IOMMU 分组: 同一个 IOMMU 分组中的设备必须一起直通。如果 GPU 和音频设备在同一个分组,两者都要直通。

NVIDIA 限制: NVIDIA 消费级 GPU 驱动会检测虚拟化环境,需要使用 hv_vendor_idkvm=off 来隐藏。

独占设备: 直通的设备宿主机将无法使用。如果宿主机只有 1 个 GPU,直通后宿主机只能使用文本控制台。


扩展阅读


下一步

11 - SPICE 远程桌面:学习使用 SPICE 协议实现高性能远程桌面与 USB 重定向。