2. IOMMU与SMMU的关系:通用架构、异同与演进
好,我们进入第二章。这一章我打算把IOMMU和SMMU的关系彻底讲清楚。很多刚接触这个领域的工程师,包括我当年,都会困惑:这两个东西到底是不是一回事?为什么ARM体系里叫SMMU,而x86那边叫IOMMU?它们之间到底差在哪?
说白了,SMMU就是ARM体系下的IOMMU。但事情没这么简单。ARM为了在移动端、服务器端都能玩转,给SMMU加了不少私货。咱们今天就把这些私货一一拆开。
2.1 IOMMU通用架构
先看一张通用的IOMMU架构图。不管哪个厂商,核心思想都一样:让DMA设备也能访问虚拟地址。
核心思想:CPU有MMU,进程用虚拟地址。DMA设备没有MMU,直接物理地址。IOMMU就是给DMA设备配的“MMU”。
IOMMU的核心组件其实不多,我列个表大家就清楚了:
| 组件 | 功能 | 我踩过的坑 |
|---|---|---|
| DMA重映射 | 将设备DMA请求中的地址翻译成物理地址 | 翻译表没配好,DMA直接写飞了 |
| 中断重映射 | 将设备中断路由到正确的CPU | 中断路由错乱,系统直接panic |
| 页表缓存(TLB) | 缓存最近用过的地址翻译 | TLB没刷干净,读到脏数据 |
| 权限检查 | 检查设备是否有权访问某段内存 | 权限设太松,设备乱读写 |
2.2 SMMU与IOMMU的异同
好,现在咱们来对比SMMU和IOMMU。我直接说结论:功能上,SMMU ≈ IOMMU + 缓存一致性 + ARM体系特有功能。
我的理解:SMMU就是ARM版的IOMMU,但ARM给它加了“缓存一致性”这个杀手锏。x86的IOMMU(VT-d/AMD-Vi)没有这个能力。
具体差异,我整理了一张表:
| 对比维度 | IOMMU (x86) | SMMU (ARM) |
|---|---|---|
| 架构标准 | VT-d (Intel) / AMD-Vi | ARM SMMU架构规范 |
| 缓存一致性 | 不支持(需要软件维护) | 支持(通过ACE/CHI总线) |
| 页表格式 | 兼容x86页表 | 兼容ARM页表(支持2级/3级) |
| 中断重映射 | 支持(MSI重映射) | 支持(GICv3/v4集成) |
| 虚拟化支持 | 支持(VT-d扩展) | 支持(SMMUv3强化) |
| 多级流控 | 有限 | 支持(Stream ID + Substream ID) |
这里我要重点说一下缓存一致性。我在做某个项目时,x86平台下DMA写内存后,CPU读到的数据竟然是旧的。为什么?因为DMA绕过了CPU的Cache,直接写到了内存。CPU的Cache里还是老数据。这就是缓存不一致。
ARM的SMMU怎么解决?它通过ACE/CHI总线协议,让SMMU在DMA访问时,先通知CPU的Cache做窥探(Snoop),保证Cache和内存数据一致。说白了,就是DMA写内存时,CPU的Cache也跟着更新。
避坑指南:我曾经在调试SMMU时,发现DMA数据总是对不上。查了三天,最后发现是SMMU的缓存一致性没使能。ARM默认是关闭的,需要你在驱动里显式打开。这个坑,我替你们踩过了。
2.3 SMMU的演进历史
SMMU不是一天建成的。ARM从移动端杀到服务器端,SMMU也跟着一路进化。我按版本给大家捋一捋:
2.3.1 SMMUv1:初代目
- 发布时间:ARMv7时代
- 核心能力:基本地址翻译、权限控制
- 局限:不支持虚拟化、TLB管理原始
- 我的评价:能用,但别指望太多
2.3.2 SMMUv2:小步快跑
- 改进点:支持2级页表、多Stream ID
- 新增:TLB无效化命令、性能计数器
- 应用:早期服务器芯片、网络设备
- 我遇到的:TLB无效化命令有bug,需要workaround
2.3.3 SMMUv3:大版本升级
- 发布时间:ARMv8.2时代
- 核心变化:完全重写架构,支持虚拟化
- 关键特性:
- HTTU(硬件页表更新)
- PRI(页请求接口)
- MPAM(内存分区和监控)
- Substream ID(细粒度流控)
- 我的感受:这才是真正能打的东西
演进路线总结:SMMUv1(能用)→ SMMUv2(好用)→ SMMUv3(强大)。如果你现在做新设计,直接上SMMUv3,别犹豫。
我画了一张演进图,大家感受一下:
最后,我给大家一个选型建议:
- 移动端/嵌入式:SMMUv2够用,资源占用少
- 服务器/数据中心:必须SMMUv3,虚拟化和缓存一致性是刚需
- 网络设备/加速器:SMMUv3 + PRI,支持页请求和按需映射
个人经验:我见过不少团队在选型时贪便宜用SMMUv2,结果后面虚拟化需求一来,整个架构要重做。我的建议是:一步到位SMMUv3,省得后面折腾。