一、整体架构层次
┌─────────────────────────────────────────────────────────────────────┐
│ Guest VM (User Space) │
│ App ──► Guest Driver ──► Guest OS Kernel │
└──────────────────────────────┬──────────────────────────────────────┘
│ virtio / VFIO uAPI
┌──────────────────────────────▼──────────────────────────────────────┐
│ Hypervisor / VMM Layer │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ QEMU/KVM │ │ vIOMMU │ │ VFIO Container/Group │ │
│ │ Device │ │ (emulated) │ │ /dev/vfio/X │ │
│ │ Models │ │ virtio-iommu│ │ │ │
│ └──────┬──────┘ └──────┬───────┘ └────────────┬───────────┘ │
│ │ │ │ │
└─────────┼─────────────────┼─────────────────────────┼───────────────┘
│ │ │
┌─────────▼─────────────────▼─────────────────────────▼───────────────┐
│ Host Kernel (KVM + IOMMU subsystem) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ KVM Module │ │ IOMMU Core │ │ VFIO Driver │ │
│ │ (EPT/NPT) │ │ (iommu.c) │ │ vfio_iommu_type1 │ │
│ └──────┬──────┘ └──────┬───────┘ └────────────┬───────────┘ │
│ │ │ │ │
│ ┌──────▼─────────────────▼─────────────────────────▼───────────┐ │
│ │ IOMMU Domain / IO Page Table │ │
│ │ IOVA ──► [PT L4][PT L3][PT L2][PT L1] ──► HPA │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────────┐
│ Hardware Layer │
│ │
│ ┌────────────┐ ┌────────────┐ ┌──────────────────────────────┐ │
│ │ Intel VT-d │ │ AMD-Vi │ │ ARM SMMU v3 │ │
│ │ DMAR Units │ │ IOMMU HW │ │ STE / CD Tables │ │
│ └────────────┘ └────────────┘ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
二、核心子系统及关系
2.1 IOMMU Domain 模型
IOMMU Domain (per-VM or per-device)
┌────────────────────────────────────┐
PCIe Device │ IO Page Table (IOPT) │
BDF: 00:02.0 ──┤ IOVA Space: [0, 2^48) │──► HPA (Host Physical)
BDF: 00:03.0 ──┤ │
│ Fault Queue (PRI / ATS) │
│ Pasid Table (if SVA enabled) │
└────────────────────────────────────┘
Domain Types:
├── DMA_DOMAIN : Normal passthrough/mapping (VFIO primary use)
├── IDENTITY_DOMAIN : IOVA == PA, used for trusted devices
├── BLOCKED_DOMAIN : All DMA blocked (default for unmanaged device)
└── SVA_DOMAIN : Shared Virtual Address (process ASID)2.2 VFIO 软件栈(Passthrough 核心路径)
User Space (QEMU) Kernel (VFIO) IOMMU HW
────────────────── ────────────── ──────────
open(/dev/vfio/vfio)
open(/dev/vfio/N) ───────► vfio_group_open()
check group viable
VFIO_GROUP_SET_CONTAINER ────► vfio_group_set_container()
└─ iommu_group_attach()
VFIO_SET_IOMMU ────────► vfio_iommu_type1_attach()
└─ iommu_domain_alloc() ──► alloc PT
VFIO_IOMMU_MAP_DMA ────────► vfio_iommu_type1_map()
├─ pin_user_pages(GPA→HPA)
└─ iommu_map(domain, ──► write PTE
iova, hpa, size, prot)
VFIO_DEVICE_GET_REGION ──────► expose BAR regions
mmap(BAR) ────────► vfio_pci_mmap() / KVM memslot
└─ EPT mapping for MMIO BAR
[Device DMA]
Device issues DMA(IOVA) ───────────────────────────────► IOMMU translates
IOVA → HPA ✓
or FAULT ✗ ──► IRQ
2.3 vIOMMU 架构(Guest 视角的 IOMMU)
┌────────────────────── Guest VM ──────────────────────┐
│ Guest Driver Guest IOMMU Driver │
│ (e.g. nvme-pci) (viommu / smmuv3-pmcg) │
│ │ │ │
│ │ DMA API │ map/unmap │
│ ▼ ▼ │
│ [Guest IOVA] [Guest IO Page Table] │
│ │ GPA of PTE │
└───────┼─────────────────────┼────────────────────────┘
│ KVM Trap │ MMIO write to vIOMMU regs
▼ ▼
┌─────────────── VMM (QEMU + KVM) ───────────────────┐
│ │
│ virtio-iommu backend ◄── vIOMMU emulation │
│ OR ARM vSMMU │
│ │ │
│ │ translates Guest IOVA → GPA │
│ │ then maps GPA → HPA via real VFIO domain │
│ ▼ │
│ vfio_iommu_map(GPA→HPA) ──────────────────────► │
└───────────────────────────────────────────────────-─┘
│
▼ (physical IOMMU sees HPA directly)
┌────────────────────────────────┐
│ Physical IOMMU PT │
│ IOVA(=GPA or remapped) → HPA │
└────────────────────────────────┘三、关键工作流程
3.1 VFIO Passthrough 设备启动流程
Phase 1: Host 侧 IOMMU Group 绑定
─────────────────────────────────
1. unbind device from native driver
echo 0000:01:00.0 > /sys/bus/pci/drivers/nvme/unbind
2. bind to vfio-pci stub driver
echo vfio-pci > /sys/bus/pci/devices/0000:01:00.0/driver_override
echo 0000:01:00.0 > /sys/bus/pci/drivers/vfio-pci/bind
3. kernel: iommu_group_get_by_id(N)
└─ all devices in group must be under VFIO control
(ACS isolation requirement)
Phase 2: QEMU 建立 DMA Domain
──────────────────────────────
4. QEMU opens /dev/vfio/N (group fd)
5. QEMU calls VFIO_GROUP_SET_CONTAINER
└─ kernel: vfio_iommu_type1_attach_group()
└─ iommu_domain_alloc(bus) → alloc Intel DMAR context entry
6. QEMU calls VFIO_SET_IOMMU (TYPE1 or TYPE1v2)
└─ kernel: iommu_domain_set_attr(DOMAIN_ATTR_GEOMETRY)
Phase 3: DMA Mapping (per mmap region)
───────────────────────────────────────
7. QEMU calls VFIO_IOMMU_MAP_DMA
struct vfio_iommu_type1_dma_map {
argsz, flags,
vaddr = host virtual addr (GPA mapped by KVM memslot)
iova = guest physical addr (what device will DMA to)
size = region size
}
└─ kernel flow:
pin_user_pages_fast(vaddr) → pfn[] (HPA)
iommu_map(domain, iova, hpa, size, IOMMU_READ|IOMMU_WRITE)
└─ intel_iommu_map() → __domain_mapping()
└─ pfn_to_dma_pte() → write IO page table entries
└─ __iommu_flush_iotlb() → invalidate IOTLB
Phase 4: 设备运行期 DMA
────────────────────────
8. Guest driver programs device with IOVA (== GPA in type1)
9. PCIe device issues MRd/MWr TLP with IOVA as DMA address
10. IOMMU intercepts, walks IO page table
IOVA → HPA (DDR physical) ✓ → DMA proceeds
or IOVA not mapped → DMAR Fault → MSI to CPU → iommu_fault_handler3.2 SVA(Shared Virtual Address)流程
Process VA Space IOMMU Device
───────────────── ────── ──────
mmap(file, VA)
│
├─ iommu_sva_bind_device()
│ └─ alloc PASID (e.g. 5)
│ └─ install process CR3 into PASID table
│ PASID[5].pgd = current->mm->pgd
│
├─ submit work to device
│ with PASID=5, VA=0x7fff0000
│
│ Device issues TLP:
│ PASID TLP Prefix(5) + addr(0x7fff0000)
│ IOMMU looks up PASID[5].pgd
│ walks CPU page table
│ VA → HPA (shared with CPU MMU)
│
├─ page fault (if page not present)
│ IOMMU PRI (Page Request Interface) → host kernel
│ └─ handle_mm_fault() → populate page
│ └─ PRI Response → device retries四、Debug 方法体系
4.1 内核 IOMMU Debug 接口
# ── 1. 基础信息 ──────────────────────────────────────────
# 查看 IOMMU 硬件拓扑
cat /sys/kernel/iommu_groups/*/devices
# 查看 device 所属 group
readlink /sys/bus/pci/devices/0000:01:00.0/iommu_group
# dmesg IOMMU 初始化信息
dmesg | grep -E 'DMAR|IOMMU|iommu|smmu' | head -40
# ── 2. Intel VT-d 专用 debugfs ─────────────────────────
mount -t debugfs none /sys/kernel/debug
# 查看所有 DMAR 硬件单元
cat /sys/kernel/debug/iommu/intel/dmar_translation_struct
# 查看 domain 的 IO 页表 (dump PTE)
cat /sys/kernel/debug/iommu/intel/domain_translation_struct
# 查看 IOTLB 统计
cat /sys/kernel/debug/iommu/intel/io_tlb
# 查看 IR (Interrupt Remapping) 表
cat /sys/kernel/debug/iommu/intel/ir_translation_struct
# ── 3. AMD-Vi / ARM SMMU ───────────────────────────────
# AMD
cat /sys/kernel/debug/iommu/amd/amd_iommu_device_state
# ARM SMMU v3
ls /sys/kernel/debug/iommu/smmuv3/
cat /sys/kernel/debug/iommu/smmuv3/smmuv3.0/events # fault events
# ── 4. VFIO debug ──────────────────────────────────────
# 查看 container 的所有 DMA mappings
cat /sys/kernel/debug/vfio/<container_id>/iommu
# 查看 group 状态
ls /dev/vfio/
cat /proc/$(pgrep qemu)/maps | grep vfio4.2 IOMMU Fault 分析
# ── Fault 类型速查 ────────────────────────────────────
# Intel VT-d Fault Reason Codes (key ones):
# 0x01 - Root Entry not present
# 0x02 - Context Entry not present
# 0x05 - Write access to RO page
# 0x06 - IOVA outside MGAW
# 0x0d - Transfer buffer DMA error
# 0x26 - Page-walk read error (broken IO PT)
# dmesg fault 信息格式:
# DMAR: DRHD: handling fault status reg 2
# DMAR: [DMA Write] Request device [01:00.0]
# fault addr 7f000000 [fault reason 05]
# ── Fault 捕获方法 ─────────────────────────────────────
# 方法1: dmesg 实时监控
dmesg -w | grep -E 'DMAR|fault|iommu'
# 方法2: perf + tracepoints
perf trace -e iommu:* sleep 10
# tracepoint 列表
ls /sys/kernel/tracing/events/iommu/
# io_page_fault map unmap attach_device detach_device
# 开启详细 trace
echo 1 > /sys/kernel/tracing/events/iommu/io_page_fault/enable
echo 1 > /sys/kernel/tracing/events/iommu/map/enable
cat /sys/kernel/tracing/trace_pipe
# 方法3: ftrace 追踪 iommu_map 调用链
echo function_graph > /sys/kernel/tracing/current_tracer
echo 'iommu_map iommu_unmap intel_iommu_map' \
> /sys/kernel/tracing/set_graph_function
echo 1 > /sys/kernel/tracing/tracing_on
# ... trigger workload ...
cat /sys/kernel/tracing/trace | head -2004.3 QEMU / VFIO 层 Debug
# ── QEMU IOMMU 日志 ───────────────────────────────────
# 启动参数开启 trace
qemu-system-x86_64 \
--trace 'vfio*' \
--trace 'iommu*' \
--trace 'vtd*' \
-object iommu,id=smmu,type=virtio \
...
# QEMU monitor 查看 iommu 状态
(qemu) info iommu
(qemu) info pci
# ── vIOMMU (Intel VTD emulation) 验证 ─────────────────
# Guest 内验证 vIOMMU 是否正常
dmesg | grep -i 'DMAR\|iommu\|vIOMMU'
cat /sys/class/iommu/*/devices
# Guest 内查看 IOMMU group(应与 passthrough 设备一致)
find /sys/kernel/iommu_groups -type l
# ── VFIO DMA map 验证 ─────────────────────────────────
# 通过 /proc/iomem 确认 BAR 映射
cat /proc/iomem | grep -A2 "0000:01:00"
# 用 iommu_check 工具验证映射完整性(需要 iommu-test 模块)
modprobe iommu_v2_test
echo "check" > /sys/kernel/debug/iommu_test/run4.4 性能分析
# ── IOTLB Miss 分析 ───────────────────────────────────
# Intel PMU: IOTLB miss 事件
perf stat -e \
uncore_imc/cas_count_read/,\
uncore_imc/cas_count_write/ \
-a sleep 5
# VT-d specific PMU (需要 intel_vtd_pmu 驱动)
perf stat -e dmar0/iotlb_miss/ -a sleep 5
# ── Address Space 碎片 ────────────────────────────────
# 查看 iova 分配器状态(rcache)
cat /sys/kernel/debug/iommu/iova_rcaches
# ── 大页映射验证 ──────────────────────────────────────
# 检查是否使用了 1G/2M IOMMU 大页
cat /sys/kernel/debug/iommu/intel/domain_translation_struct \
| grep -E 'large|huge|level'4.5 典型问题 Checklist
Problem Diagnosis Fix
───────────────────────────────────────────────────────────────────────────
DMA Fault (reason 0x02) Context entry missing check BDF binding,
→ device not in IOMMU group iommu_group_add_device()
DMA Fault (reason 0x05) Write to read-only mapping check VFIO_IOMMU_MAP_DMA
flags: VFIO_DMA_MAP_FLAG_WRITE
Guest hangs on DMA IOTLB not flushed after remap iommu_flush_tlb_all() or
QEMU vtd_context_cache_reset()
vIOMMU map rejected GPA not covered by memslot extend KVM memslot,
check memory-backend object
IOMMU group isolation fail Missing ACS capability check PCIe switch ACS,
(can't assign device) on upstream port or use acs_override (debug only!)
SVA page fault loop PASID table not updated check iommu_sva_handle_iopf()
after fork/munmap and mm_notifier registration
ARM SMMU STE fault Stream Table Entry invalid check smmu_write_strtab_ent()
PASID/SID match五、架构决策关键点总结
| 场景 | 推荐方案 | 关键接口 |
|---|---|---|
| 设备直通给单 VM | VFIO type1 + physical IOMMU | VFIO_IOMMU_MAP_DMA |
| 多 VM 共享设备 | SR-IOV VF + 各自 IOMMU domain | iommu_domain_alloc per VF |
| Guest 内 DMA 隔离 | virtio-iommu / vSMMU | VFIO_IOMMU_MAP_DMA 二级映射 |
| AI 训练大模型 P2P DMA | ACS-aware group + PCIe P2P | iommu_map with peer domain |
| 进程级设备隔离 | SVA / PASID | iommu_sva_bind_device |
IOMMU 在虚拟化中本质上扮演两个角色:安全边界(防止 DMA 越界破坏 Host 内存)和地址翻译器(将 Guest 物理地址透明转换为 Host 物理地址)。两者通过同一套 IO 页表机制实现,这是整个软件栈设计的核心约束。


夜雨聆风