SMMUv3 嵌套翻译、PCIe ATS、PRI 与共享虚拟寻址(SVA)深度解析¶
1. SMMUv3 两级流表与上下文描述符(CD)硬件查找架构¶
在系统级 IOMMU(ARM SMMUv3)中,外设发起的每一个 DMA 事务都携带 StreamID(标识具体物理外设,通常由 PCIe RequesterID/BDF 派生) 与可选的 PASID(Process Address Space ID,标识进程)。
flowchart TD
DMA_Tx["外设发起 DMA 事务: StreamID + PASID + IOVA/VA"] --> ST_Lookup["SMMU 查流表: Stream Table (线性表或 2 级树状表)"]
ST_Lookup --> STE["选中 Stream Table Entry (STE)"]
subgraph STE_Config ["STE 配置模式判定 (STE.Config)"]
STE -->|Config == 0b100| Bypass["Bypass: 物理地址直通 (不翻译)"]
STE -->|Config == 0b101| S1_Only["Stage-1 Only: 主机普通 OS DMA 翻译"]
STE -->|Config == 0b110| S2_Only["Stage-2 Only: 虚拟机传统设备直通"]
STE -->|Config == 0b111| Nested["Nested (Stage-1 + Stage-2): 虚拟机 SVA 共享地址"]
end
STE -->|S1ContextPtr| CD_Table["Context Descriptor (CD) 表 (由 PASID 索引)"]
CD_Table --> CD["选中 Context Descriptor (CD)"]
CD -->|CD.TTBR0| S1_PT["Stage-1 页表: VA/IOVA → 中间物理地址 IPA"]
STE -->|STE.S2TTB| S2_PT["Stage-2 页表: IPA → 最终宿主机物理地址 PA"]
S1_PT & S2_PT --> Final_PA["输出合法 PA 发送至 NoC / DDR"]
STE.Config 的编码为:0b000 Abort、0b100 Bypass、0b101 仅 Stage-1、0b110 仅 Stage-2、0b111 两阶段翻译。图中省略了 Abort 分支。编码见 Arm SMMUv3 规范 G.b §5.2 的 Config 字段,可与 Linux 的 STRTAB_STE_0_CFG_* 定义交叉核对。
2. 虚拟机直通两阶段嵌套翻译(Nested Translation)的硬件开销¶
在云计算虚拟化场景下(KVM / VFIO),当虚拟机内部运行的 Guest OS 试图让直通网卡直接使用用户进程虚拟地址(VA)时,SMMU 必须进行 Stage-1(Guest 虚拟化)与 Stage-2(Hypervisor 隔离)的嵌套翻译:
sequenceDiagram
participant Dev as PCIe Endpoint (网卡/GPU)
participant SMMU as SMMUv3 硬件翻译引擎
participant GuestPT as Guest Stage-1 页表 (GPA/IPA 视图)
participant HostPT as Host Stage-2 页表 (PA 视图)
participant Memory as 真实 Host DDR 物理内存
Dev->>SMMU: 发起 DMA 读写请求: GVA (Guest 虚拟地址)
Note over SMMU: 阶段 1 走表: 查询 Guest L0~L3 页表
SMMU->>HostPT: 读取 Guest L0 页表基地址 (其本身为 IPA, 需经 Stage-2 翻译)
HostPT-->>SMMU: 返回 Guest L0 页表所在 Host PA
SMMU->>Memory: 读取 Guest L0 表项
Note over SMMU: ... 依次递归 4 级 ...
Note over SMMU: 阶段 2 走表: 将 Stage-1 输出的 IPA 翻译为最终 PA
SMMU->>HostPT: 翻译目标数据页 IPA → PA
HostPT-->>SMMU: 返回最终数据 Host PA
SMMU->>Memory: 执行真实 DMA 数据搬运
- 嵌套走表的开销:若两阶段都采用 4 级页表,且相关翻译与页表项均未命中缓存,可按 $(4+1) \times (4+1) - 1 = 24$ 次页表项读取估算这个简化模型的开销。这不是每次 DMA 都发生的固定访问次数。
- IOTLB 与 Walk Cache:缓存命中可减少重复翻译和走表访问,实际收益取决于页大小、访问局部性、缓存容量和实现方式,不能统一写成平均 2~3 次。嵌套翻译与缓存行为可参阅 Arm SMMUv3 架构规范。
3. PCIe ATS(地址翻译服务)与 Device-TLB 硬件协议¶
ATS(Address Translation Services)允许支持该能力的 PCIe 设备请求地址翻译,并在本地 Device-TLB / ATC 中缓存结果,减少重复请求。设备、Root Complex 和 IOMMU 都需要支持并正确配置相应能力;缓存失效、权限和隔离检查仍然存在,命中路径也有实际时延。参见 Arm SMMUv3 规范的 PCIe、PASID、PRI 与 ATS 章节。
flowchart LR
subgraph PCIe_Endpoint ["PCIe 外设 (集成 Device-TLB)"]
Req_Gen["DMA 引擎准备发起 4KB 传输"]
Dev_TLB{"本地 Device-TLB"}
Req_Gen --> Dev_TLB
Dev_TLB -->|Hit 命中| Fast_DMA["以 AT=0b10 (Translated) 标记直接发送带 PA 的 Memory Write TLP"]
end
subgraph Host_Root_Complex ["Host 侧 SMMUv3"]
Dev_TLB -->|Miss 缺失| ATS_Req["发送 AT=0b01 的 ATS Translation Request TLP (带 IOVA/VA)"]
ATS_Req --> SMMU_Walk["SMMU 查表翻译"]
SMMU_Walk --> ATS_Cpl["返回 ATS Translation Completion TLP (带 PA & 权限)"]
ATS_Cpl --> Dev_TLB
Fast_DMA --> Check_Attr["按 SMMU 配置处理已翻译事务, 继续访问内存"]
end
PCIe TLP 的 AT(Address Type)是两位字段:0b00 表示 Untranslated,0b01 表示 Translation Request,0b10 表示 Translated。因此图中的已翻译 Memory Write 使用 0b10。编码见 Intel VT-d 规范 Rev. 5.0 §4.1、§4.1.1 与 §4.1.3;SMMU 对 Translation Request 与 Translated 事务的处理见 Arm SMMUv3 规范 G.b §3.9.1。
ATS Invalidation 协议与失联死锁¶
- 移除设备可能缓存的映射时,需要让相关设备的地址翻译缓存失效,再按协议完成同步。
- 设备处理 ATS Invalidation Request TLP 后回复 ATS Invalidation Completion TLP。
- 若设备未按要求回复,应检查设备状态、链路和驱动的超时处理。超时上限与故障后果取决于平台和实现,不能据此断言整条总线必然死锁。映射失效与设备缓存的关系可参阅 Linux SVA 说明。
4. 共享虚拟寻址(SVA)与 PRI(Page Request Interface)缺页握手¶
对需要长期访问用户缓冲区的传统 DMA 方案,驱动通常需要固定相关页面并建立设备可用的映射。SVA(Shared Virtual Addressing)让支持它的设备与 CPU 使用进程的虚拟地址空间;配合 ATS 和 PRI,可在支持可恢复缺页的平台上减少预先固定全部页面的需要。驱动仍要绑定设备与进程地址空间、管理 PASID 和队列,不能把图中的直接提交理解为完全无需驱动。参见 Linux SVA 文档。
sequenceDiagram
participant App as 用户态应用程序
participant Acc as 硬件加速器 (NPU / GPU)
participant PRI as SMMUv3 PRI 队列
participant OS as Linux 缺页处理 (handle_mm_fault)
App->>Acc: 设备与进程绑定后, 提交包含用户 VA 的任务
Acc->>Acc: 访问该 VA, 本地与 SMMU 查表均发现 PTE.Valid == 0
Acc->>PRI: 发送 PCIe Page Request TLP (请求分配内存页)
PRI->>OS: SMMU 触发 PRI 中断, 将请求推入 PRI Queue
OS->>OS: 内核执行标准 do_page_fault(), 分配物理页并更新进程页表
OS->>PRI: 向 SMMU CMDQ 写入 PRI_RESP 命令 (Response Code: SUCCESS)
PRI->>Acc: 发送 PCIe Page Response TLP
Acc->>Acc: 重新请求翻译并尝试 DMA 访存
5. SMMUv3 三大硬件环形队列管理与命令¶
SMMUv3 使用命令队列、事件队列和可选的 PRI 队列传递命令与事件。驱动还要配置寄存器、流表和上下文描述符;队列并不是唯一接口。PRI 能力应按实现检查,相关寄存器与能力位可对照 Linux arm-smmu-v3 定义。
flowchart TD
subgraph SMMU_Queues ["SMMUv3 三大物理队列"]
CMDQ["1. Command Queue (CMDQ: CPU写, SMMU读)\n下发配置同步与 TLB 失效命令\n(CMD_CFGI_STE, CMD_TLBI_NH_VA, CMD_SYNC)"]
EVENTQ["2. Event Queue (EVENTQ: SMMU写, CPU读)\n硬件上报翻译错误、权限违例与安全阻断\n(F_TRANSLATION, F_PERMISSION, F_ADDR_SIZE)"]
PRIQ["3. PRI Queue (PRIQ: SMMU写, CPU读)\n接收 PCIe 设备发来的缺页请求并在处理后应答"]
end
6. 常见关键直通陷阱与排查手册¶
陷阱 1:VFIO 直通网卡触发 F_PERMISSION 导致网卡死锁¶
- 故障现象:在 KVM 虚拟机中将物理网卡通过 VFIO 直通给虚拟机,驱动加载时网卡直接超时,宿主机
dmesg疯狂报错:arm-smmu-v3: event 0x07: F_PERMISSION for StreamID 0x1800, IOVA 0x82000000。 - 微架构根因:
- Event
0x07为 Permission Fault。 - KVM 在初始化该直通设备的 Stage-2 映射时,错误地将 DMA 描述符环形缓冲区所在的内存页映射为了 只读(
IOMMU_READ)。 - 网卡在接收到网络数据包后,DMA 试图将写回状态(Descriptor Status)写入该物理页,触发 SMMU 权限阻断,DMA 事务被丢弃。
- 排查法则:检查 VFIO 分配映射时的标志位,确保包含
IOMMU_WRITE读写双向权限。
陷阱 2:DMA 寻址超出硬件 DMA Mask¶
- 故障现象:设备在某些 64GB 内存服务器上运行正常,在 512GB 内存服务器上随机触发
F_ADDR_SIZE错误。 - 根因:
- 设备硬件只支持 32 位或 36 位 DMA 寻址能力,但驱动未调用
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36))显式声明; - 操作系统给设备分配了超出 36 位物理边界(
> 64GB)的高位物理页,STE 检测到地址越界直接抛出F_ADDR_SIZE严重异常事件。