virt-report虚拟化社区动态

VFIO 设备直通的 DMA 地址转换与隔离

本文由 OpenAI Codex (GPT-6-Astra) 辅助编写。

一、让设备访问客户机内存并限制范围

设备直通把物理设备的功能交给客户机驱动使用。驱动向设备提交缓冲区地址后,设备可以通过 DMA 读写客户机内存。宿主机需要把这些地址转换成实际的物理页,同时限制设备能够访问的范围。VFIO 管理设备访问与 DMA 资源,宿主 IOMMU 执行地址转换和权限检查。

本文分析 x86 / KVM 下普通 PCIe 设备访问客户机 RAM 的过程:客户机不配置虚拟 IOMMU,设备使用 GPA 作为 DMA 地址,宿主启用 IOMMU 隔离;内存采用常规可固定的 RAM。本例不涉及 PCIe 对等访问、ATS、PASID 和可恢复页故障。设备寄存器与中断仅作为请求提交和完成的上下文。

源码版本:QEMU 10.2.0、Linux 6.18。

二、CPU 与 DMA 的地址转换路径

同一块客户机内存,客户机 CPU、QEMU 进程和物理设备使用的地址不同。建映射时,QEMU 提供设备侧地址与用户态内存位置;运行时,IOMMU 根据设备所属的转换域处理 DMA。

地址含义本例中的用途
GPA客户机物理地址客户机 RAM 中缓冲区的位置
IOVA设备发出的 I/O 虚拟地址交给宿主 IOMMU 转换;本例数值等于 GPA
HVAQEMU 进程的宿主虚拟地址向内核描述 RAM 的后备内存
HPA宿主物理地址DMA 最终访问的宿主物理页

例如,客户机缓冲区位于 GPA 0x00400080,设备发出同值的 IOVA;映射覆盖 0x00400000–0x00400fff,对应宿主页 HPA 0x21000000。IOMMU 保留页内偏移 0x80,得到 HPA 0x21000080。QEMU 映射这块页时可以使用完全不同的 HVA。连续的 IOVA 区间也可以对应多个不连续的宿主物理页。

CPU 与设备 DMA 的地址转换
两条访问路径指向同一后备页。虚线表示映射配置,实线表示地址转换关系;本例未配置客户机 IOMMU,设备 IOVA 与 GPA 数值相同。
两条访问路径指向同一后备页。虚线表示映射配置,实线表示地址转换关系;本例未配置客户机 IOMMU,设备 IOVA 与 GPA 数值相同。 打开原图
Mermaid 源码
flowchart TB
    accTitle: CPU 与设备 DMA 访问同一客户机后备页
    accDescr: 客户机 CPU 经客户机页表与 EPT 或 NPT 完成地址转换,物理设备经宿主 IOMMU 完成 DMA 地址转换。QEMU 的 HVA 用于配置后备内存,内核固定页面并建立 IOVA 到 HPA 的映射,HVA 不随设备 DMA 请求传输。
    subgraph CPU["CPU 访存路径"]
        GVA["客户机虚拟地址 GVA"]
        GPA["客户机物理地址 GPA<br/>0x00400080"]
        EPT["EPT / NPT<br/>CPU 第二阶段转换"]
        GVA -->|客户机页表| GPA
        GPA --> EPT
    end
    subgraph DMA["设备 DMA 路径"]
        DEV["PCIe 设备标识 + IOVA<br/>0x00400080"]
        IOMMU["宿主 IOMMU<br/>转换上下文、I/O 页表与权限"]
        DEV -->|设备读或设备写| IOMMU
    end
    subgraph MAP["映射配置路径"]
        Q["QEMU 提交 MAP<br/>IOVA = 0x00400000<br/>HVA = 0x70001000<br/>长度 = 4 KiB · 权限 = READ / WRITE"]
        K["内核验证并固定后备页<br/>页基址 HPA = 0x21000000"]
        Q --> K
    end
    P["同一客户机 RAM 后备页<br/>访问 HPA 0x21000080"]
    EPT --> P
    IOMMU -->|保留页内偏移 0x80| P
    K -. 建立 IOVA → HPA 映射 .-> IOMMU
    classDef address fill:#e4f1ec,stroke:#438575,color:#173e34
    classDef translation fill:#eaf0f6,stroke:#8198ac,color:#263c4c
    classDef control fill:#f5f4f1,stroke:#a6aaa7,color:#3c4542
    class GVA,GPA,DEV,P address
    class EPT,IOMMU translation
    class Q,K control

EPT / NPT 负责客户机 CPU 的第二阶段地址转换,具体建图过程见KVM 内存映射与 EPT 缺页处理。PCIe 设备发起的普通 DMA 使用宿主 IOMMU 的转换上下文,QEMU 的 HVA 不随 DMA 请求发送。两条路径需要指向相同的客户机后备页,但页表管理、权限和缓存失效是各自的机制。

客户机配置虚拟 IOMMU 后,IOVA 与 GPA 可以不同,宿主还需跟踪客户机的 DMA 映射变化或使用硬件嵌套转换。QEMU 的内存监听代码为 IOMMU 区域和普通 RAM 分别处理;本文沿普通 RAM 分支继续。IOMMU 与 RAM 的映射分支

三、建立 IOMMU 映射,再提交 DMA

先建立设备与地址空间的关联,再映射客户机 RAM。之后客户机提交请求,设备发出 DMA,IOMMU 检查目标地址与权限。下面可以切换设备读、设备写以及三种映射状态,观察目标内存是否被访问。

DMA 地址与访问权限

设备访问一个 4 KiB 页内的 64 B 缓冲区。先建立映射,再逐步提交和处理请求。

QEMU / 宿主内核 · 配置路径关联设备与地址空间 → 固定 RAM 后备页 → 设置 I/O 页表IOVA 0x00400000 · HVA 0x70001000 · 长度 4 KiB

IOMMU_IOAS_MAP · 映射输入

ioas_id
10
iova
0x00400000
user_va
0x70001000 · QEMU HVA
length
4096 B
flags
FIXED_IOVA | READABLE | WRITEABLE
标志省略共同的 IOMMU_IOAS_MAP_ 前缀。HPA 由内核解析后备页得到,不在此输入结构中。

设备与页表关联

设备尚未绑定 IOMMUFD

IOAS 10
逻辑 IOVA 映射
HWPT 20
本例设备使用的硬件页表
HPA 页
等待内核建立映射
向 IOAS 附着时可复用兼容的 HWPT,或创建一个;一个 IOAS 可以对应多个 HWPT。
客户机驱动缓冲区 GPA 0x00400080,长度 64 B设备请求地址采用相同数值的 IOVA
寄存器 / 队列提交请求

物理 PCIe 设备

IOVA 0x00400080

等待请求

长度
64 B
方向
设备读 / 写
设备标识选择转换上下文
DMA 请求设备读 / 写

宿主 IOMMU

IOVA 页 → HPA 页

IOVA
0x00400000
HPA
0x21000000
权限
由所选映射决定

检查映射及读写权限

64 B 数据页内偏移 0x80

宿主物理内存

HPA 0x21000080

客户机 RAM 的后备页

缓冲区内容原有数据 A
尚未发生 DMA 数据访问

配置路径设置转换关系;正常数据访问由设备与 IOMMU 硬件处理。示例地址用于展示页号和偏移,不对应实际机器。故障仅表示本次数据访问被阻止,不模拟特定设备的错误恢复。

  1. QEMU 向内核提供 IOVA、后备内存与访问权限,建立硬件映射。
  2. 客户机驱动提交缓冲区地址,设备发出 DMA。
  3. IOMMU 检查映射和权限,通过后访问 HPA 对应的 RAM。
  4. 正常请求完成后,驱动消费或回收缓冲区;缺失映射和越权写入不能产生正常数据完成。

启用 JavaScript 可切换权限和方向,逐步查看状态变化。

1. 配置阶段

  1. 建立 DMA 所有权。VFIO 检查设备所在 IOMMU group 的所有权,并把设备接入受控的 I/O 地址空间。传统接口通过 group 与 container 建立关系,IOMMUFD 路径通过设备文件描述符绑定并附着 IOAS 或硬件页表。
  2. 接收内存布局。QEMU 的 MemoryListener 获得可访问的 RAM 区间,计算设备侧 IOVA、进程侧 HVA、长度和只读属性。
  3. 建立映射。内核验证范围、权限与页粒度,固定相应后备页,在设备关联的 IOMMU domain 中建立 IOVA 到 HPA 的映射。硬件随后可缓存转换结果。

无客户机 IOMMU 时,常规配置通常映射可 DMA 访问的客户机 RAM,映射生命周期长于单个 I/O 请求。客户机驱动每次填写一个缓冲区地址,并不会使 QEMU 重新执行一遍 DMA map ioctl。

2. 数据阶段

  1. 客户机驱动准备缓冲区和设备所需的描述符,按照设备协议与内存顺序要求提交请求。寄存器访问可通过受支持的 BAR 映射或 VFIO 访问路径到达设备。
  2. 物理设备发出携带 DMA 地址的读写事务,宿主根据设备标识选择对应 IOMMU 转换上下文。
  3. IOMMU 检查映射及读写权限。转换成功时访问目标宿主页;本例缺失映射或写只读页时产生故障,目标数据访问被阻止。
  4. 正常完成的数据请求由设备按其协议报告完成,驱动随后回收或消费缓冲区。DMA 故障后的错误报告、超时或恢复过程依赖设备及平台,不能当作成功完成。

读写权限以设备视角命名:设备读取 RAM 使用 READ,设备把接收数据写入 RAM 使用 WRITE。驱动的“发送”通常包含设备读,“接收”通常包含设备写。内核的权限解释

四、隔离与映射生命周期

设备访问时的上下文选择、IOTLB 命中和撤销同步,在IOMMU:DMA 地址转换、缓存与失效中沿同一个接收缓冲区展开。

1. IOMMU group 的隔离边界

I/O 页表限制设备到内存的访问,但实际隔离还取决于 PCIe 拓扑。某些多功能设备、桥或缺少合适 ACS 能力的路径,可能允许事务在到达 IOMMU 前发生重定向。内核据此把无法独立隔离的设备归入同一 IOMMU group,作为 DMA 所有权的边界。

IOMMUFD 的设备 cdev 接口按设备提供文件描述符,同时检查整个 group 的所有权。同组设备共享硬件隔离边界,由同一 DMA 所有者管理。映射接口接收用户态提供的后备内存,由内核验证并固定页面,再建立设备可访问的范围。VFIO group 与设备 cdev

2. 撤销映射与在途 DMA 的协调

释放或复用 DMA 缓冲区前,需要确保设备已经停止使用它。删除 I/O 页表项还要与硬件转换缓存失效配合,避免缓存中的旧转换继续指向被回收的页。设备队列停止、在途事务处理与映射撤销共同决定安全的回收顺序;单纯删除 QEMU 的用户态指针并不完成这些工作。

QEMU 收到 RAM 区域删除通知后会调用后端的 DMA unmap。内核负责撤销其维护的 IOMMU 映射并释放固定页的引用。大范围映射可减少频繁的 map / unmap,但长期固定内存会约束内存回收、气球回收和热插拔;频繁更新小范围映射则会增加页表管理与 IOTLB 失效开销。区域删除、RAM discard 与固定页的关系

设备 DMA 写入的脏页还需要独立跟踪。vCPU 的 KVM 脏页日志无法覆盖物理设备绕过 CPU 发出的全部内存写入。因此,直通设备热迁移同时依赖设备状态保存、DMA 停止与设备或 IOMMU 的脏页跟踪能力,不能仅凭 CPU 内存迁移成功判断设备可迁移。

五、源码:RAM 区域到 DMA 映射

1. QEMU 从 RAM 布局生成 DMA 映射

vfio_listener_region_add()
  vfio_container_region_add()
    vfio_get_section_iova_range()     # 设备地址区间及页对齐
    memory_region_get_ram_ptr()       # RAM 在 QEMU 中的地址
    vfio_container_dma_map()
      legacy: vfio_legacy_dma_map()
        VFIO_IOMMU_MAP_DMA
      iommufd: iommufd_cdev_map()
        iommufd_backend_map_dma()
          IOMMU_IOAS_MAP

vfio_container_region_add()利用 MemoryRegionSection 的地址空间偏移计算 IOVA,同时组合 RAM 基址、区域内偏移和对齐偏移得到 HVA;再把 section->readonly传入映射后端。传统后端构造 vfio_iommu_type1_dma_map,IOMMUFD 后端构造 iommu_ioas_map。两者都区分设备读写权限。地址计算、传统映射请求、IOMMUFD 映射请求

这条调用链采用用户虚拟地址提供后备内存。QEMU 还支持另一条分支:当 RAM 有文件后备且后端提供 dma_map_file时,使用内存文件描述符和偏移,走 IOMMU_IOAS_MAP_FILE。文件映射改变后备页的描述方式,设备侧仍使用 IOVA。映射分派、文件后备映射

2. Linux 固定后备页并设置 I/O 页表

传统 Type1:vfio_dma_do_map()
  验证范围、对齐、权限与已有映射
  vfio_pin_map_dma()
    vfio_pin_pages_remote()          # 获取并固定后备页
    vfio_iommu_map()
      iommu_map(domain, iova, paddr, length, prot, ...)

IOMMUFD:iommufd_ioas_map()
  获取 IOAS,验证用户参数
  iopt_map_user_pages()              # 管理页及 IOAS 映射
    将映射同步到该 IOAS 关联的硬件页表

Type1 的 vfio_pin_map_dma()分批固定页面,将物理页号交给 vfio_iommu_map()。后者调用 IOMMU 子系统的映射接口,由平台驱动建立硬件所需的转换。IOMMUFD 把地址空间及硬件页表作为独立对象管理。Type1 页固定与映射、IOAS 映射入口

这些函数负责建立或更新转换关系。正常 DMA 由设备和 IOMMU 硬件处理:IOTLB 命中时复用缓存的转换,未命中时可遍历硬件页表;缺失映射或权限不符才形成访问故障。QEMU 参与映射配置,硬件执行随后各次 DMA 的地址转换。

六、直通减少的开销与保留的成本

回到 GPA 0x00400080 的例子,映射建立后,设备通过 IOMMU 访问对应宿主页,无需 QEMU 为这次数据搬运解析一条 virtqueue 或复制报文。省下的是软件设备数据路径的一部分工作;IOMMU 转换、硬件队列、设备中断以及客户机驱动仍要运行。BAR 是否能够直接映射、设备是否需要其他模拟,也会影响实际退出次数。

对本例可以先核对地址和方向:IOVA 0x00400080 应命中映射页,读写权限应与设备操作相符,页内偏移仍为 0x80。映射缺失或越权出现的是 IOMMU 访问故障;性能低但没有故障时,则继续检查队列、中断、DMA 带宽与 NUMA,不能把两种现象都归为地址转换错误。

评估应分开一次性的接管与映射成本、正常运行的吞吐和尾延迟,以及停止、复位和迁移的管理成本。大内存虚拟机还要观察页固定规模与映射耗时;高频小 I/O 则更容易暴露中断和调度开销。若增加虚拟 IOMMU 来细化客户机 DMA 权限,还需评估动态映射和 IOTLB 更新,不能沿用本例长期映射全部可访问 RAM 的开销结论。

七、从 VFIO Container 到 IOMMUFD

本节比较接口分工。设备绑定、IOAS 与 HWPT 的创建、附着及释放过程,见IOMMUFD:设备绑定与 I/O 地址空间管理。

1. 以 group 和 container 组织 DMA

传统 VFIO 将设备访问、硬件隔离组和 IOMMU 管理组织成 device、group、container 三层。应用先把 group 加入 container,再配置 IOMMU 后端,最后通过 group 获取设备文件描述符。多个可兼容的 group 可以共享 container 中的映射,减少重复的页表维护。

这种结构支撑了长期使用的设备直通路径,但 I/O 地址空间管理与 VFIO container 接口紧密绑定。随着嵌套转换、不同硬件页表类型和更细粒度设备绑定的需求出现,用户态需要更直接地表达地址空间、硬件页表及它们与设备的关联。

2. 把地址空间管理交给 IOMMUFD

IOMMUFD 通过 /dev/iommu提供通用 I/O 地址空间接口。IOAS 保存用户态建立的地址映射;HWPT 表示关联的硬件页表。设备经 VFIO cdev 绑定 IOMMUFD,再附着到 IOAS 或 HWPT。向 IOAS 附着时,内核可以复用兼容的 HWPT,也可以新建 HWPT;同一个 IOAS 的映射可以同步到多个硬件页表。VFIO 继续提供设备区域、配置空间和中断访问,DMA 地址空间管理则使用 IOMMUFD 的对象与操作。

QEMU 在 2023 年合入 IOMMUFD 后端实现。公共 VFIO 内存监听逻辑保留,后端分别完成传统 container 或 IOMMUFD 的绑定与 map / unmap。现在的 iommufd_cdev_connect_and_bind()与 iommufd_cdev_attach_ioas_hwpt()对应绑定和附着两个步骤。绑定实现、附着实现

接口拆分为后续页表能力提供了扩展位置,同时引入对象生命周期、能力协商和多种页表关系的管理。切换后端不会改变设备的 DMA 协议,也不会自动补齐设备迁移、复位或硬件隔离能力。比较两条路径时,应分别检查地址空间管理能力与具体设备支持的功能。

3. 与其他设备虚拟化方式的关系

SR-IOV 让一个物理功能提供多个 VF,VF 可以再通过 VFIO 分配;mdev 由宿主设备驱动提供受管理的设备实例;vDPA 则面向 virtio 数据面加速。它们分别解决功能划分、设备实例管理或 virtio 加速的问题。此处讨论的物理 PCIe 设备 DMA 路径,不能不加区分地套用到所有 mdev 或 vDPA 实现。

八、参考资料