ARM SMMU与设备直通:QEMU/KVM下VFIO DMA路径全解析

发布时间:2026/10/14 1:24:54
ARM SMMU与设备直通:QEMU/KVM下VFIO DMA路径全解析 前段时间在给一台 ARM64 服务器做四端口网卡直通QEMU 命令行里加了-device vfio-pci,host0003:01:00.0guest 里lspci能看到设备但网卡驱动一加载宿主机dmesg就开始疯狂刷 SMMU event。折腾了大半天才弄明白直通这件事从来不是把 PCIe 设备“塞”给虚拟机这么简单从 PCIe Requester ID 到 IOMMU domain再到 Stage2 页表、中断路由整条链路任何一个环节断了设备在 guest 里都只能当摆设。这篇文章就把 QEMU KVM 环境下的 ARM SMMU IO passthrough 实现链路完整拆一遍重点讲清楚物理 SMMU 在这个过程里到底做了什么、QEMU 和 KVM 各自站在哪一层、以及我在实际配置和排查时踩过的坑。内容主要面向正在折腾 ARM 虚拟化直通、或者想把网卡/NVMe 直通给 KVM guest 却总被 DMA fault 困扰的同行。1. 一次DMA fault引出的问题直通设备为什么绕不开SMMU1.1 现象guest里能看到设备却一跑就崩直通的一个典型误区是只要 guest 里能lspci看到设备就以为直通成功了。实际上这只说明 PCIe 配置空间被正确暴露给了 guest真正的数据面还没打通。我当时在 guest 里执行ethtool -i eth0是正常的驱动也加载了但一跑iperf或dd这类真实 IOguest 立刻卡死host 侧出现大量类似arm-smmu-v3 ... event 0x的报错。这种故障的典型特征就是设备管理和控制通道正常DMA 数据通道不通。控制通道走的是 PIO/MMIO由 QEMU 截获后转发给物理设备数据通道走的是 DMA物理设备直接访问内存这时候就必须有地址翻译和权限检查的中间层。1.2 地址错位设备DMA写的是GPA不是HPA再看深一层直通设备发起的 DMA 地址到底是谁的地址guest 里的设备驱动调用 DMA API 时拿到的是 guest 物理地址在 ARM 虚拟化语境下也叫 IPAIntermediate Physical Address。设备并不知道自己是运行在虚拟机里的它只会把这个地址当作普通总线地址发到 PCIe 上。如果宿主机的 SMMU 不做任何翻译那么这个 GPA 会被当成 HPA 直接访问宿主机物理内存。结果有两种要么访问到错误的内存位置造成数据损坏要么被 SMMU 的权限检查拦下来产生 DMA fault。这就是为什么直通必须依赖 SMMU 的根本原因需要一个硬件单元把设备侧可见的“假物理地址”翻译成真正的宿主物理地址同时按 IOMMU domain 做隔离。1.3 没有SMMU时的“假直通”有人会问如果我在 guest 里把设备驱动收到的 DMA 地址直接配成 HPA是不是就不需要 SMMU 了理论上是但没有任何正经项目会这么干。首先guest 里的操作系统不知道也不应该知道 HPA否则内存热插拔、KASLR、overcommit 全部失效。其次设备直通要求多个 guest 之间内存严格隔离如果设备可以随意 DMA 到任意 HPA一个 guest 里的恶意驱动就能读另一个 guest 的内存这在安全上是不可接受的。SMMU 在这里的作用相当于给每个直通设备发了一张“受限通行证”只允许它访问映射过的 DMA 窗口其他地址一律拒绝。所以结论很明确SMMU 不是可选项而是直通链路的必选项。哪怕某些测试环境关闭了 SMMU 能跑通也只是侥幸一旦涉及多虚机隔离或者真实负载问题马上会暴露。2. SMMU在直通链路里的真正分工谁翻译、怎么翻译2.1 从PCIe Requester ID到IOMMU domainSMMU 要完成翻译第一步是识别“谁在发起 DMA”。PCIe 上每个设备都有一个唯一的 Requester ID由 Bus/Device/Function 三者组成。SMMU 通过 Stream ID可能还带 Pasid/Substream ID把这个设备关联到一个具体的 IOMMU domain。在 Linux 内核里每个 IOMMU domain 对应一组地址映射关系和权限配置。同一个 PCIe 拓扑下如果多个设备共享一个 IOMMU group那么它们必须属于同一个 domain无法被拆开单独直通。这是后面排查 IOMMU group 隔离问题的基础。实际直通时VFIO 会打开/dev/vfio/group把设备所在的 IOMMU group 绑定到一个 container然后通过 ioctl 向这个 container 注册 DMA 映射。QEMU 做的就是把 guest 内存区域同步成这些映射关系。这一层对用户是透明的但理解它有助于排障。2.2 Stage1/Stage2两阶段guest管一半host管一半ARM SMMUv3 最核心的设计是两阶段翻译概念上类似 CPU 的 Stage1 MMU 和 Stage2 MMU。Stage1负责把设备侧看到的 IOVAIO Virtual Address翻译成 IPA通常由 guest 自己的 IOMMU 驱动管理。Stage2负责把 IPA 翻译成 HPA由宿主机 hypervisor/VFIO 管理。在典型的直通场景里guest 内核如果没感知到 IOMMU设备驱动直接拿 GPA 当 DMA 地址那么宿主机只需要配置 Stage2 把 GPA 翻译成 HPA 就够了。如果 guest 里也有一套 IOMMU无论是虚拟 SMMU 还是物理 SMMU 的嵌套直通设备驱动会先申请 IOVA 并建立 Stage1 映射然后 SMMU 再做 Stage2 翻译。这也是很多人对“SMMU 直通”理解混乱的地方直通不一定需要在 guest 里开 SMMU。绝大多数生产环境用的是“宿主 SMMU 做 Stage2”的方案guest 是无感直通。那些在 QEMU 里开启虚拟 SMMU 的用法更多是为了让 guest 能主动管理 DMA 隔离或者在混合设备场景下做更细粒度的权限控制跟“直通必需”不是一回事。2.3 与Intel VT-d的映射关系如果你之前熟悉 x86 平台的 VT-d那么可以把 SMMU 看成 ARM 版的 IOMMU。两者的核心逻辑高度相似都有设备表/Stream table 识别设备、都有地址翻译缓存TLB、都支持中断重映射。区别主要体现在细节上SMMUv3 的两阶段翻译更灵活Stage1/Stage2 可以分别使能也支持嵌套。PCIe 的 PRI/PASID 能力在 SMMUv3 里有对应的 Substream 机制。中断重映射在 ARM 平台上通常和 GIC ITS 绑定x86 则是 Interrupt Remapping Table。理解这些对应关系后很多 x86 上的直通经验可以直接迁移到 ARM 平台只是排查报错时关键字从dmar换成了arm-smmu-v3。3. QEMU侧的实现细节vfio-pci如何把设备“递”给guest3.1 QEMU对直通设备的模拟边界QEMU 对直通设备的模拟并不是“把整个设备模拟一遍”而是只做必要的“外形”工作模拟 PCIe 配置空间让 guest 能枚举到设备把设备的 BARMMIO 资源映射到 guest 物理地址空间处理 MSI-X 表把真实中断通过 KVM 注入 guest数据面 DMA 不经过 QEMU直接由物理设备经过 SMMU 访问 guest 内存。这是直通性能好的根本原因QEMU 只出现在慢路径上快路径完全由硬件旁路。也正因为如此对 QEMU 进程的 memory footprint 和 MMIO 访问开销不那么敏感。但代价是任何 DMA 层面的错误都无法由 QEMU 兜底只能靠 SMMU 和内核日志排查。3.2 虚拟SMMU与直通设备的“兼容性陷阱”这里必须单独拎出来讲因为非常容易踩坑。QEMU 的 ARMvirtmachine 支持通过smmuon创建一个模拟的 SMMUv3 设备让 guest 内核看到一颗 SMMU。但它主要服务于 VirtIO 这类 QEMU 模拟设备不建议直接和 vfio-pci 直通设备混在一起用。原因也很直接QEMU 模拟的 SMMU 只能翻译它自己模拟设备发起的 DMA而 vfio-pci 直通设备发起的 DMA 在物理 SMMU 里走的是硬件翻译路径。两套 SMMU 语义并不一致强行叠加容易出现 mapped address 对不上、fault 满天飞的问题。我在实验中的做法是直通场景下 QEMU 侧不需要开smmuonguest 里也不需要启动 IOMMU 驱动。宿主机的物理 SMMU 配合 VFIO 已经完成了最关键的 Stage2 翻译。如果你确实需要在 guest 里开 IOMMU先确认你的 SMMU 硬件支持 nesting并且内核、QEMU、VFIO 版本都满足要求否则不要轻易尝试。3.3 MemoryListener与VFIO DMA映射的联动QEMU 内部用 MemoryListener 监听 guest 物理内存的变化。当 guest 内存区域被创建、删除或属性变化时QEMU 会调用 VFIO 的接口做VFIO_IOMMU_MAP_DMA或VFIO_IOMMU_UNMAP_DMA。这个机制是直通 DMA 能工作的关键。例如 guest RAM 如果使用普通匿名内存VFIO 默认可能无法映射因为物理页可能在内存压力下被换出或迁移。生产环境通常需要让 guest 内存使用mlock锁定内存或者用mem-path/dev/hugepages的 HugeTLB 后端。这样 VFIO 才能保证 DMA 期间页表稳定。所以如果你看到 guest 里的直通设备间歇性 DMA fault先想想内存后端是不是换了或者内存热插拔时有没有重新映射好。4. KVM侧不是看客中断直达与GIC/ITS的协同4.1 DMA翻译不经过KVM中断却要经过KVM直通路径上SMMU 负责 DMA 翻译QEMU 负责设备展示和内存映射那 KVM 到底在做什么KVM 最大的贡献在中断路径上。直通设备产生 MSI/MSI-X 中断时中断信号需要被送进正确的 vCPU并让 guest 的 GIC 看到合理的中断状态。这个过程如果让 QEMU 全程模拟延迟会非常高。所以 KVM 提供了 irqfd 机制物理中断到达宿主机后通过 VFIO 与 eventfd 关联KVM 直接把中断注入到 guest 的 vGICQEMU 不参与热路径。4.2 irqfd/ioeventfd把物理MSI变成虚拟中断具体实现大概是这样的物理设备触发 MSI-X中断到达 GIC ITS宿主机内核识别为直通设备中断触发绑定的 eventfdKVM 根据 irqfd 路由信息把中断写进 vGIC 的 pending 状态vCPU 退出 guest 模式后重新进入在 guest 里看到该中断。这里面如果 GIC 版本不对或者 ITS 模型没配好直通设备中断就会在第一步或第三步断开。常见现象是设备驱动 probe 时注册中断成功但一收包就丢中断或者cat /proc/interrupts里的计数完全不变。我建议直通时优先使用-machine virt,gic-version3并确保内核启用了CONFIG_ARM_GIC_V3_ITS。对于某些老内核ITS 的虚拟化支持还不完善直接退回到 GICv2 模式虽然少了不少特性但兼容性更好。4.3 缓存一致性、TLB失效与KVM的内存标脏还有一个容易忽视的点DMA 和 CPU 之间的缓存一致性。在 ARM 平台上如果 guest 内存属性配置不当设备 DMA 写入的数据可能无法被 CPU 及时看到或者 CPU 写过的数据没有回写就被设备读走。KVM 会负责把 guest 内存区域设置为一致的共享属性但如果你用了自定义的 memory attributes或者开启了某些 device memory 类型的映射就要特别小心。另外 SMMU 也有自己的 TLB。每次 guest 的 DMA 映射变化后需要发送 TLB invalidate 命令。宿主机 VFIO 通常会在UNMAP_DMA时自动做 invalidate但如果你在 guest 里频繁做 IOMMU map/unmapinvalidate 的开销会直接影响到吞吐。这也是为什么直通性能调优时大家更推荐使用大页、减少映射切换的原因。5. 实操复盘在qemu-system-aarch64上跑通一次SMMU直通5.1 宿主侧的检查清单与内核参数开始直通前先确认宿主机内核具备以下能力CONFIG_ARM_SMMU_V3CONFIG_VFIOCONFIG_VFIO_PCICONFIG_VFIO_IOMMU_TYPE1CONFIG_ARM_GIC_V3_ITS检查 SMMU 是否识别dmesg | grep -i smmu正常会看到类似arm-smmu-v3 ... iommu_ops的输出。如果没有先查固件/SMMU 驱动是否加载再看设备树或 ACPI 表里有没有 IOMMU 节点。查看设备所在 IOMMU groupfor g in /sys/kernel/iommu_groups/*; do echo Group $(basename $g): $(ls $g/devices) done这一步非常重要。如果目标网卡所在 group 里还有其他设备比如同一个 PCIe bridge 下的兄弟设备那么单独直通这张网卡会失败。要么把整个 group 的设备一起直通要么从硬件拓扑上换一个独立组。5.2 VFIO设备绑定与QEMU启动参数确认 group 后把设备从原驱动解绑再绑定到 vfio-pcimodprobe vfio-pci driverctl set-override 0003:01:00.0 vfio-pci这里用driverctl比手动 echo 到 sysfs 更稳妥它会自动处理 bind/unbind 顺序。绑定成功后/dev/vfio/group应该存在。QEMU 启动关键参数大致如下qemu-system-aarch64 \ -machine virt,gic-version3 \ -cpu cortex-a72 \ -m 4G \ -smp 4 \ -kernel /path/to/Image \ -initrd /path/to/initrd.img \ -append root/dev/vda consolettyAMA0 \ -netdev user,idnet0 \ -device virtio-net-pci,netdevnet0 \ -device vfio-pci,host0003:01:00.0 \ -nographic注意这里我没有加smmuon。理由前面讲过宿主物理 SMMU 已经承担了直通设备的 DMA 翻译QEMU 模拟 SMMU 在这里不是必需品加了反而可能引入额外复杂度。如果 guest 内存要使用大页可以追加-mem-prealloc -mem-path /dev/hugepages这样 VFIO 的 DMA 映射会更稳定性能也更好。5.3 guest内验证与host侧观测guest 起来后先确认设备枚举正常lspci -nnk然后看驱动是否绑定成功尝试给网卡分配 IP 或跑一轮 IO。不要只看 guest 内部状态同时开一个终端观察 host 侧日志dmesg -wT | grep -i smmu如果一切正常dmesg不会出现 event 报错guest 内的吞吐也会达到接近物理机的水平。如果出现报错优先记录event的类型和 Stream ID这比直接猜原因有效得多。6. 我踩过的坑和对应的排查链路6.1 直通网卡DMA fault风暴先查host dmesg我遇到最多的就是用网卡直通时 guest 卡死host 上刷arm-smmu-v3fault。后来发现根因是 guest 内存用了普通匿名页QEMU 内存压力大的时候某个内存区域被换出VFIO 的 DMA 映射实际上已经失效。治本的办法是给直通 guest 配上 HugeTLB 后端并-mem-prealloc同时限制宿主机的内存 overcommit。治标但也好用的排查手段是先看 host dmesg 里的 fault 地址和 stage 信息确认到底是 Stage1 还是 Stage2 的问题。Stage2 fault 大概率是 VFIO 映射和 guest RAM 不一致Stage1 fault 则要往 guest 侧 IOMMU 设置上查。6.2 中断注册失败多半是GIC/ITS模型问题直通设备能跑 DMA但中断起不来通常不是设备本身的问题而是 KVM 虚拟 ITS 和物理 ITS 的衔接出了问题。常见表现是 guest 里ethtool -S看到的 rx 中断一直是 0或者驱动 probe 时报request_irq失败。排查链路先看 guest 的/proc/interrupts里有没有对应设备的中断号再确认 QEMU 的gic-version是否支持 MSI/LPI最后看 host dmesg 里有没有 ITS 相关错误。如果是 GICv3 但 ITS 设备模型不稳定可以试试用gic-version3配合内核irqchip.gicv3_pseudo_nmi0这类参数排除干扰。有些时候纯粹是内核版本太老升级内核比调参数更快。6.3 IOMMU group不隔离整组设备一起直通很多 PCIe 网卡是 multi-function 设备一张卡上有四个 Physical Function它们很可能默认在同一个 IOMMU group 里。你想直通其中一个内核会直接拒绝因为同一个 group 内其它设备没有被隔离。这不是 SMMU 的问题而是 PCIe ACS 能力没有生效。解决办法不是硬开 override而是要评估能把整组设备都直通给同一个 guest 吗能的话就在 QEMU 命令里把该 group 的所有设备都写上guest 里会看到多张网卡不能的话就换硬件拓扑或者调整 PCIe Root Port 配置。6.4 性能低于线速的两个隐藏开销最后说说性能。直通设备理论上应该接近物理机但实际经常跑不到线速。除了 SMMU TLB invalidate 之外还有一个容易被忽略的点guest 没有开启 MSI-X 的多队列中断或者中断 CPU 亲和性没有绑对。KVM 的 irqfd 注入延迟虽然低但如果 vCPU 被宿主机的其他负载抢占中断响应会抖动。建议把直通设备的物理中断绑到空闲 CPU 上同时给 guest 里对应网卡队列的 vCPU 做 pinning。另一个性能杀手是 SMMU 的 caching mode 或 strict mode 设置某些平台为了安全性默认关闭了 TLB caching 或采用 strict 模式表现就是吞吐掉一半甚至更多。这个需要结合具体 SMMU 驱动参数微调不能一概而论。直通这套东西真正跑通一次之后你会发现原理和细节其实是互相印证的。SMMU 负责隔离和翻译VFIO 负责把设备安全和灵活地交给 guestQEMU 和 KVM 各自守住慢路径和中断路径。以后再遇到直通问题先分清楚是路径哪一段断了控制面、数据面、还是中断面然后按日志一步步往下查基本不会被表象带偏。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询