QEMU 虚拟化完全指南 / 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 计算/渲染 | GPU | CUDA/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) |
|---|---|---|
| IOPS | 500K+ | 100-200K |
| 延迟 | 10-50μs | 100-500μs |
| 带宽 | 3-7 GB/s | 1-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
要点回顾
| 要点 | 核心内容 |
|---|---|
| IOMMU | VT-d / AMD-Vi,设备直通的基础 |
| VFIO | Linux 内核框架,管理设备直通 |
| GPU 直通 | 需要隐藏 VM 特征,IOMMU 分组一致 |
| NVMe 直通 | 最高性能,但失去快照和迁移能力 |
| USB 直通 | 支持控制器级和设备级直通 |
注意事项
IOMMU 分组: 同一个 IOMMU 分组中的设备必须一起直通。如果 GPU 和音频设备在同一个分组,两者都要直通。
NVIDIA 限制: NVIDIA 消费级 GPU 驱动会检测虚拟化环境,需要使用
hv_vendor_id和kvm=off来隐藏。
独占设备: 直通的设备宿主机将无法使用。如果宿主机只有 1 个 GPU,直通后宿主机只能使用文本控制台。
扩展阅读
下一步
→ 11 - SPICE 远程桌面:学习使用 SPICE 协议实现高性能远程桌面与 USB 重定向。