本页目录
IOMMU 与 DMA API
覆盖: IOMMU 硬件 → DMA API (coherent/streaming) → swiotlb → IOMMU domain → VFIO → 虚拟化中的 IOMMU 内核版本: 2.6 ~ 6.x
IOMMU 原理
IOMMU 为设备提供 IO 地址转换 (类比 CPU 的 MMU):
设备视角地址 (IOVA) → IOMMU 页表 → 物理地址
功能:
1. 地址转换: 设备只能"看到" IOVA → 不知道真实物理地址
2. 隔离: 每个设备/domain 有独立页表 → A 不能访问 B 的内存
3. 中断重映射: MSI 地址经 IOMMU 翻译 → 防止 MSI 注入攻击
4. 大块连续: 分散物理页 → 映射为设备视角的连续 IOVA → 简化 DMA 缓冲分配
实现:
Intel: VT-d (DMAR table)
AMD: AMD-Vi (IVRS table)
ARM: SMMU (System MMU)
DMA API
// include/linux/dma-mapping.h
// 设备驱动不应手动分配/释放物理内存用于 DMA → 必须使用 DMA API
// 用途: descriptor ring, 设备状态页 (CPU 和设备频繁读写同一区域)
// 特性: CPU 和设备看到的永远一致 → 缓存自动同步
void *;
void ;
// 用途: 数据 buffer (一次性, 方向固定)
// 特性: 需要显式 sync → 性能优于 coherent
dma_addr_t ;
void ;
// direction: DMA_TO_DEVICE (写), DMA_FROM_DEVICE (读), DMA_BIDIRECTIONAL
int ;
内部实现: 根据平台选择
有 IOMMU:
dma_map_single → iommu_dma_map_page() → 在 IOMMU domain 中建映射
→ 零拷贝 (IOVA 到物理地址的映射, 不需要拷贝数据)
无 IOMMU:
dma_map_single → 如果 DMA 可寻址范围 < 物理地址范围 → SWIOTLB bounce buffer
→ 在低地址区分配临时 buffer → 拷贝数据 (性能损失)
SWIOTLB: 软件 Bounce Buffer
当设备 DMA 寻址范围有限 (如 32-bit PCI 设备, 只能访问 0~4GB)...:
SWIOTLB 在低地址区 (DMA zone) 预留 bounce buffer
dma_map: 数据从高地址 → copy → bounce buffer (在 DMA 可及范围)
dma_unmap: bounce buffer → copy → 用户 buffer (如果是 DMA_FROM_DEVICE)
启动参数: swiotlb=65536 (分配 64MB bounce buffer)
IOMMU Domain 与 Group
// drivers/iommu/
// domain: IOMMU 页表的抽象 (一组 IOVA → 物理的映射)
// group: 硬件隔离单元 (同一 group 的设备可以互相访问)
// IOMMU group 是 VFIO 的隔离边界:
// 一个 group 内的设备不能互相隔离 → 必须一起分配给同一个 VM
// /sys/kernel/iommu_groups/*/devices/*
VFIO: 用户态设备驱动
// drivers/vfio/
// 允许用户态程序直接 (但安全地) 访问硬件:
// 1. IOMMU 隔离: 设备映射到自己的 IOMMU domain → 不能访问 host 内存
// 2. MMIO 直通: 映射设备的 BAR → 用户态可直接 mmap
// 3. 中断转发: 设备中断 → IOMMU interrupt remapping → eventfd → QEMU/user
// QEMU: vfio-pci → PCI passthrough → guest 直接访问硬件
// SPDK: 用户态 NVMe 驱动 → 数据路径零拷贝
参考
- 源码:
kernel/dma/,drivers/iommu/intel/,drivers/iommu/arm/,drivers/vfio/ - 内核文档:
Documentation/core-api/dma-api.rst - LWN: "The DMA API", "IOMMU groups and VFIO"
关键词: IOMMU, DMA API, dma_map_single, coherent DMA, swiotlb, IOMMU domain, VFIO