AI 加速器内存管理单元深度实战:IOMMU、SMMU、ATS 在生产集群中的工程实践
引言
在现代 AI 训练和推理集群中,GPU、NPU、FPGA 等加速器通过 DMA(Direct Memory Access)与宿主机内存直接交换数据。早期系统对 DMA 的使用相对简单,设备可以直接读写物理内存。但随着多租户虚拟化、内存加密、安全隔离等需求的出现,IOMMU(Input-Output Memory Management Unit) 成为了 AI 基础设施中不可或缺的关键组件。
IOMMU 不仅提供了设备地址到主机物理地址的翻译能力,更构建了一道安全防线——防止恶意或错误的设备进行越界内存访问。然而,这一额外抽象层也引入了地址翻译开销,在追求极致吞吐的 AI 负载中,如何平衡安全隔离与性能,是每一位基础设施工程师必须深入理解的核心课题。
本文将从底层原理出发,深入剖析 IOMMU 架构、ARM SMMU 实现、ATS/PRI 协议、虚拟化场景下的 VFIO 集成,并提供生产环境的性能调优实战经验。
一、DMA 与 IOMMU:从直读到安全抽象
1.1 早期 DMA 的裸访问模式
在没有 IOMMU 时代,设备执行 DMA 操作时使用的是主机物理地址(HPA, Host Physical Address)。PCIe 设备发起一个 TLP(Transaction Layer Packet),其中的地址字段直接指向物理内存:
[PCIe 设备] --TLP(0x7F123456)--> [内存控制器] --> RAM
这种模式简单直接,但存在严重隐患:
- 安全隔离缺失:任何有 DMA 权限的设备都可以读写整机内存
- 虚拟化困境:Guest OS 无法正确告知设备 DMA 地址(GPA ≠ HPA)
- 大页非对齐访问:4KB 页粒度下的跨页访问导致设备需要多次 DMA 事务
1.2 IOMMU 的翻译机制
IOMMU 在 CPU 和设备之间引入地址翻译层。设备发出的地址不再直接是物理地址,而是 IOVA(I/O Virtual Address),IOMMU 负责将其翻译成 HPA:
[PCIe 设备] --TLP(IOVA:0x1000)--> [IOMMU: 页表翻译] --TLP(HPA:0x7F12000)--> [内存控制器]
CPU 通过 Context Bank(或 VT-d 中的 Root Entry / Context Entry)关联到设备标识(BDF, Bus/Device/Function),每个设备拥有独立的页表基址。
1.3 IOMMU 的核心能力
| 能力 | AI 集群中的价值 |
|---|---|
| 地址空间隔离 | 多租户 GPU 设备各自独立 IOAS,防止越权访问 |
| GPA→HPA 二级翻译 | VFIO 直通场景下设备使用 GPA,IOMMU 完成最终翻译 |
| 中断重映射 | 防止设备发起恶意 MSI/MSI-X 中断 |
| 超级页(1GB/2MB)支持 | 减少 TLB miss,加速大模型参数的 DMA 搬运 |
| PASID/Process Address Space | 进程级 DMA 隔离,单设备多进程并发 |
| 页面错误处理 | ATS/PRI 支持设备侧分页,按需分配物理内存 |
二、Intel VT-d 与 ARM SMMU 架构对比
2.1 Intel VT-d 硬件架构
Intel VT-d 在 PCH(Platform Controller Hub)中实现,包含以下关键组件:
Root Table:位于内存中,由编程寄存器指向,每个 PCIe Bus 对应一个 Root Entry,指向 Context Table。
Root Table
├── Bus 0x00: Context Entry (Dev0, Func0) → PML4 页表
├── Bus 0x00: Context Entry (Dev0, Func1) → PML4 页表
├── Bus 0x01: Context Entry (Dev2, Func0) → PML4 页表
└── ...
多级页表:VT-d 2.5 支持 4-level 和 5-level 页表(从 PML4→PDP→PD→PT),与 CPU 页表结构兼容,可直接复用 CPU 页表 walker 逻辑。
Second-Level Translation(SLT):虚拟化场景下,第一阶段 Guest 页表(GVA→GPA)和第二阶段 IOMMU 页表(GPA→HPA)共同完成翻译。VT-d 1.x 需要两次查表,VT-d 2+ 开始支持嵌套翻译和 Scalable Mode(单一页表完成 GVA→HPA)。
关键寄存器:
// DMA 物理基址寄存器 (DMAR)
#define DMAR_CAP_REG 0x00 // 能力寄存器
#define DMAR_ECAP_REG 0x04 // 扩展能力寄存器
#define DMAR_GCMD_REG 0x08 // 全局命令
#define DMAR_GSTS_REG 0x0C // 全局状态
// 示例:启用翻译模式
// gcmd |= GCMD_TE (Translation Enable)
// gcmd |= GCMD_SRTP (Set Root Table Pointer)
2.2 ARM SMMU 架构
ARM SMMU(System Memory Management Unit)是 ARM 生态中的 IOMMU 实现,当前主流版本为 SMMUv3。
SMMUv3 架构分层
SMMUv3 采用队列(Queue)和命令(Command)模型,与 CPU MMU 的页表遍历完全不同:
┌─────────────────┐
Request Stream │ SMMU_ID_STR │ ← 来自设备的 IOMMU 请求
────────►│ (STE 选择) │
└────────┬────────┘
│
┌────────▼────────┐
│ Stream Table │ 内存中的表项 (STE)
│ Entry (STE) │ 每个 PCIe 端点一个
└────────┬────────┘
│
┌────────▼────────┐
│ CD (Context │ 描述符:指向实际页表
│ Descriptor) │
└────────┬────────┘
│
┌────────▼────────┐
│ Stage-1/2 │ S1=虚拟化翻译,S2=第二阶段翻译
│ Page Table │
└─────────────────┘
STE 的关键字段
struct smmu_ste {
uint64_t config; // V(有效) S1CDMax S1Fmt S1DSS
uint64_t s1_context; // S1ContextPtr → CD pointer
uint64_t s2_context; // TTB0/TTB1 for stage-2
uint64_t s1_cir; // 中断重映射
uint64_t s1_cpr; // 配置寄存器
};
// STE 中的关键位
#define STE_V (1ULL << 0)
#define STE_CONFIG_S1_BYPASS (0ULL << 0)
#define STE_CONFIG_S1_STAGE1_TRANSLATE (1ULL << 0)
#define STE_CONFIG_S1_STAGE2_TRANSLATE (2ULL << 0)
#define STE_CONFIG_S1_BOTH_TRANSLATE (3ULL << 0)
Stage 配置模式
SMMUv3 支持三种 Stage 配置,覆盖不同场景:
- Stage-1 Only:驱动直接使用 GVA,SMMU 翻译 GVA→HPA(单级)
- Stage-2 Only:某些场景使用 GPA,翻译 GPA→HPA(如某些嵌入式)
- Stage-1 + Stage-2(嵌套):虚拟化标准方案,驱动用 GVA → S1 翻译为 GPA → S2 翻译为 HPA
对于生产 AI 集群的 GPU 直通场景,通常使用 S1+S2 嵌套翻译:驱动(Guest IB 驱动)使用 GVA(实质是 GPA),SMMU 完成两级翻译。
三、ATS 与 PRI:设备侧的分页协议
3.1 Address Translation Services (ATS)
传统 IOMMU 模式下,设备每个 DMA 请求都要经过 IOMMU 翻译,翻译未命中时会产生大量页表遍历,形成性能瓶颈。ATS 协议允许设备缓存翻译结果,绕过 IOMMU:
传统模式(无 ATS):
[设备] --DMA(IOVA)--> [IOMMU] --翻译--> [内存]
每次 PCIe TLP 都经过 IOMMU
ATS 模式:
① 设备发送 Translation Request → IOMMU 返回 Translation Completion(含 HPA)
② 设备在本地 IOTLB 缓存翻译
③ 后续 DMA 直接携带 HPA(AT=Translation in TLP 首部)
[设备] --DMA(HPA, AT=1)--> [内存]
IOMMU 验证后直接放行(不打断)
PCIe ATS 在 TLP 首部使用 AT(Address Type)字段:
AT=00(Untranslated):IOVA,需要 IOMMU 翻译AT=01(Translation Request):请求翻译AT=10(Translated):已翻译的 IOVA,IOMMU 需验证有效性
3.2 Page Request Interface (PRI)
ATS 虽然将翻译请求从 IOMMU 卸载到设备,但页缺失时仍由 IOMMU 发起,设备无法在分页流程中参与。PRI 协议让设备可以主动请求操作系统为 IOVA 分配物理页:
① 设备尝试通过 ATS 访问一个未映射的 IOVA
② IOMMU 检测到页未分配(U/S=n),发起 Page Request:
- PRI Page Request TLP → 包含 IOVA 和 PASID
③ 驱动/操作系统:
- alloc_pages() 分配物理页
- iommu_map() 建立映射
- 通过 PRI Response TLP 告诉设备
④ 设备重试原 DMA
PRI 的关键意义:支持真正的按需分页。对于 AI 训练,模型参数可以分块加载,只有实际计算的物料才被映射并 DMA,避免了全量预分配。
3.3 PASID:进程地址空间标识
在 AI 集群中,单块 GPU 可能同时被多个进程通过 MIG(Multi-Instance GPU)或 vGPU 共享。PASID(Process Address Space Identifier)为每个进程分配独立的 IOAS:
PASID 在设备端的层级:
┌──────────────────────────────┐
│ RID (Root Complex ID) │
│ └── BDF (Bus:Dev.Func) │
│ └── PASID │ ← 进程级隔离
│ └── IO Address │
└──────────────────────────────┘
SMMUv3 的 Substream ID 对应 PASID,匹配到不同的 CD(Context Descriptor),CD 指向该进程的 Stage-1 页表。
四、VFIO 与 IOMMU 集成:GPU 直通实战
4.1 VFIO 框架概述
Linux VFIO 是设备直通的标准框架。通过 VFIO,用户空间可以直接配置设备的 DMA、中断、I/O:
+--------------------------------------------------+
| 用户空间 │
│ QEMU / llama.cpp / GPU Driver │
│ │ │
│ ▼ ioctl VFIO_DEVICE_SET_IOMMU │
+--------------------------------------------------+
| 内核空间 │
│ VFIO Container → IOMMU Domain → IOMMU Group │
│ │ │
│ ▼ iommu_map / iommu_unmap │
+--------------------------------------------------+
| 硬件 │
│ IOMMU / SMMU │
+--------------------------------------------------+
核心概念:
- IOMMU Group:共享 IOMMU 隔离域的设备的最小单位,无法跨 Group 隔离
- IOMMU Domain:地址翻译的上下文范围,Domain 内共享页表
- VFIO Container:一个或多个 Domain 的集合,用户空间接口的顶层
4.2 VFIO Type1 IOMMU:映射建立
VFIO 的 Type1 IOMMU 后端通过 VFIO_IOMMU_MAP_DMA ioctl 建立映射:
struct vfio_iommu_type1_dma_map {
__u32 argsz;
__u32 flags;
#define VFIO_DMA_MAP_FLAG_READ (1 << 0)
#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1)
__u64 vaddr; // 用户空间虚拟地址 (GVA/GPA)
__u64 iova; // 设备看到的 DMA 地址 (IOVA)
__u64 size; // 映射长度
};
// 典型调用(QEMU 中 Pin 内存):
int ret = ioctl(container_fd, VFIO_IOMMU_MAP_DMA, &map);
iommu_map() 的最终实现(SMMUv3 后端):
// drivers/iommu/arm/arm-smmu-v3.c
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
// 1. 确定页表格式 (AArch64 / Two-stage)
// 2. 写入 PTE (设置 AF / SH / AP / ATTR)
// 3. TLB 失效 arm_smmu_tlb_inv_domain()
// 4. 同步 CMDQ
// PTE 关键字段:
// - Valid bit
// - AF (Access Flag) - 用于区分首次访问
// - SH (Shareability) - Inner/Outer Shareable
// - AP (Access Permissions) - R/W
// - ATTR (Memory Attributes) - Device / Normal
}
4.3 GPU 直通的 VFIO 配置
在生产 Kubernetes 中,GPU Device Plugin 通过 VFIO 直通:
# 1. 解绑 GPU 原驱动
echo "0000:01:00.0" > /sys/bus/pci/devices/0000:01:00.0/driver/unbind
# 2. 加载 VFIO-PCI 驱动
modprobe vfio-pci
echo "10de 2206" > /sys/bus/pci/drivers/vfio-pci/new_id
# 3. 绑定到 VFIO
echo "0000:01:00.0" > /sys/bus/pci/drivers/vfio-pci/bind
# 4. 验证 IOMMU Group
ls /sys/bus/pci/devices/0000:01:00.0/iommu_group/devices/
# 确保 GPU 和音频在同一个 Group,或已隔离
# 5. 检查 SMMU 状态(ARM SMMUv3)
cat /sys/class/iommu/arm-smmu-v3.0.auto/ias
# 查看页表配置
4.4 大页映射优化
GPU 内存访问通常以大块连续区域为主。利用 IOMMU 大页(1GB Huge Page / 2MB Super Page)可以显著减少 TLB 压力:
// 内核侧:设备映射时的 IOMMU 超级页探测
// drivers/iommu/iommu.c
static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
{
// IOMMU 从 CPU 侧获取支持的页尺寸
// 若 [iova, iova+size] 和 [paddr, paddr+size] 对齐
// 且 IOMMU 支持对应页大小,直接使用超级页 PTE
// SMMUv3 支持的超级页:
// - 4KB granule: 2MB block, 1GB block, 512GB (5-level)
// - 16KB granule: 32MB block, 16GB block
// - 64KB granule: 512MB block, 64GB block
}
对齐规则示例(2MB 超级页):
- IOVA 必须对齐到 2MB 边界
- 物理地址必须对齐到 2MB 边界
- 映射大小必须是 2MB 的整数倍
五、生产集群中的性能调优
5.1 测量 IOMMU 开销
IOMMU 引入的开销主要有三个来源:
- 页表遍历延迟(PTW):IOVA→HPA 翻译消耗的延迟
- TLB Miss 惩罚:IOTLB 未命中时的多级查表
- TLB 失效同步:`iommu_unmap()` 后广播 TLB invalidation
- GPU 计算核心和 HBM 控制器在独立 Group
- 或利用 ACS(Access Control Services)实现 Peer-to-Peer 隔离
- 确定 stall 来源设备:从 stall 事件日志中提取 BDF(Bus/Device/Function)
- 检查映射有效性:确认驱动正确调用了 `iommu_map()`
- 检查 IOVA 范围:确认泄漏的 IOVA 是否在已注册的地址空间内
- 检查 TLB 一致性:确认 `iommu_unmap()` 后有对应的 TLB invalidation
- SMMUv3 的 STE(Stream Table Entry)配置错误
- 两个租户的 BDF 映射到同一个 Context Descriptor
- GPU 使用不同 PASID,但 CD 共享了 Stage-1 页表
- 内存热插拔:CXL 支持运行时添加/移除内存,IOMMU 页表需动态调整
- RCH(Root Complex Integrated Endpoint):直接在 PCH 中的 CXL 设备,IOMMU 路径更短
- 多级 Switch:CXL 网络中的 Switch 设备需要级联 IOMMU 上下文
- 设备直接访问 CPU 缓存(类似 AMD hUMA 的显式缓存推送)
- SMMU 页表项的缓存属性在线协商
- 基于 CHAI 的动态 TLB 预取策略
- 必须开启 IOMMU:现代 AI 集群均应启用 VT-d 或 SMMUv3,性能损失可控(通常 <5%)
- 大页优化优先:对 HBM 或 BAR 映射使用 1GB 超级页,对系统内存使用 2MB 超级页;配合 THP 确保物理连续
- 启用 ATS/PRI:对大厂 GPU(NVIDIA Hopper、AMD MI300)确保 ATS 和 PRI 功能正常;前者减少翻译延迟,后者支持真正的按需分页
- PASID 多进程:MIG/vGPU 场景下为每个 PASID 配置独立 Context Descriptor,实现地址空间隔离
- stall 事件监控:部署 SMMU stall 事件的持续监控(基于 `eventq`),及时发现错误设备或驱动缺陷
- IOMMU Group 规划:购买硬件时确认 GPU 计算流所在的 IOMMU Group,避免跨 Group 的 escalate 问题
测量方法:
// 使用 Linux perf 追踪 IOMMU 事件
// Intel IOMMU perf events:
// - iommu/tlb_invalidate
// - iommu/mem_pass_through
// - iommu/num_ptw_invalidations
perf stat -e 'iommu/*' -p $gpu_training_pid
// ARM SMMUv3 统计(通过 sysfs)
cat /sys/kernel/debug/arm-smmu-v3/perf/miss_rate
5.2 IOMMU 页表模式选择
针对 AI 训练负载,有两种主要配置范式:
模式 A:Identity Mapping(1:1 直通)
最简单也最快的模式,IOVA == HPA,但无安全隔离。适用场景:单租户裸金属 AI 训练集群,设备驱动可信。
┌────────────────────────────────────────────────┐
│ Identity Mapping(IOVA = HPA) │
│ │
│ 优点:零翻译开销,DMA 延迟接近直连内存 │
│ 缺点:无隔离,不适合多租户 │
│ 配置:iommu.passthrough=1 (内核参数) │
└────────────────────────────────────────────────┘
模式 B:完整 Translation + 大页优化
在安全优先的多租户环境,使用 IOMMU 翻译但最大化大页使用率:
# 启用透明大页(THP)供 VFIO 使用
echo "always" > /sys/kernel/mm/transparent_hugepage/enabled
# SMMUv3 超级页配置
# 在 UEFI/ACPI 中选择合适的页粒度
# 4KB 页 + 2MB 超级页:混合模式
echo "arm-smmu-v3.disable_bypass=0" >> /boot/cmdline.txt
5.3 多函数设备的隔离(ARI与MFD)
现代 GPU 设备包含多个 PCIe Function:图形、音频、USB-C 控制器、串行端口等。每个 Function 各自独立,但可能共享同一个 IOMMU Group。
对于 AI 计算 Function(Function 0),需要确保:
# 检查 ACS 能力
lspci -vvv -s 01:00.0 | grep -i acs
# ACS: Access Control (支持 ACS 时支持更细粒度隔离)
# 若硬件不支持,使用 pcie_acs_override 内核参数(多厂商设备不一致)
# GRUB_CMDLINE_LINUX="pcie_acs_override=downstream,multifunction"
5.4 IOMMU 集群调优清单
#!/bin/bash
# IOMMU 性能检查清单(AI 集群部署脚本片段)
echo "=== IOMMU 配置检查 ==="
# 1. 确认 IOMMU 开启
if [ -d /sys/class/iommu ]; then
echo "[OK] IOMMU 已启用"
else
echo "[FAIL] IOMMU 未启用!性能和安全均受限"
fi
# 2. 检查 SMMUv3 状态
if [ -d /sys/class/iommu/arm-smmu-v3.* ]; then
echo "[OK] ARM SMMUv3 已激活"
cat /sys/kernel/debug/arm-smmu-v3/*/stall_events 2>/dev/null
fi
# 3. 确认 ATS 启用
for dev in /sys/bus/pci/devices/*; do
if [ -f "${dev}/ats" ]; then
echo "[OK] $(basename ${dev}) 支持 ATS"
fi
done
# 4. 页表模式
if grep -q "iommu.passthrough=1" /proc/cmdline 2>/dev/null; then
echo "[INFO] IOMMU 直通模式 (Identity Mapping)"
else
echo "[INFO] IOMMU 翻译模式"
fi
# 5. 大页配置
echo "[INFO] 透明大页: $(cat /sys/kernel/mm/transparent_hugepage/enabled)"
# 6. PASID 支持
echo "[INFO] PASID: $(cat /sys/module/vfio/parameters/enable_unsafe_noiommu_mode 2>/dev/null)"
# 7. SMMU stall 事件监控
# stall 事件:设备尝试访问未映射 IOVA,SMMU 触发 stall
# 需要检查 stall 日志判断是否存在恶意或错误设备
dmesg | grep -i "smmu.*stall" | tail -5
# 8. IOVA 空间碎片化监控
echo "[INFO] IOVA 空间:"
cat /sys/kernel/debug/iommu/*/iotlb 2>/dev/null | head -20
六、安全隔离与故障排查实战
6.1 SMMU Stall 事件分析
当设备尝试访问未分配的 IOVA 时,SMMUv3 会记录 stall 事件:
# dmesg 输出示例:
[ 42.134567] arm-smmu-v3 arm-smmu-v3.0.auto: eventq: [0x00] PRI_PAGE_RESP
[ 42.134568] arm-smmu-v3 arm-smmu-v3.0.auto: eventq: [0x02] F_STE_FETCH
[ 42.134569] arm-smmu-v3 arm-smmu-v3.0.auto: unhandled context: iova=0xdeadbe000
# stall 类型:
# - F_UUT: Unsupported Upstream Transaction
# - F_ACCESS: Access flag fault
# - F_PERMISSION: Permission fault
# - EVENTQ_OVERFLOW: 事件队列溢出(性能问题)
排查步骤:
6.2 多租户隔离失效案例
在 Kubernetes 多租户 GPU 场景下,曾发生过因为 PASID 配错导致跨租户 DMA 的案例:
根因分析:
修复措施:
# DevicePlugin 配置:确保每租户独立 IOMMU Domain
apiVersion: v1
kind: Pod
metadata:
annotations:
nvidia.com/gpu.smmu.domain: "isolated" # 自定义调度约束
spec:
containers:
- name: ai-worker
resources:
limits:
nvidia.com/gpu: 1
// 内核 VFIO 侧修复:绑定 PASID 到独立 Domain
// drivers/vfio/vfio_iommu_type1.c
static int vfio_iommu_enable_nesting(struct vfio_iommu *iommu)
{
// 确保每个 PASID 对应独立 Stage-1 页表
// PASID 0 → default domain
// PASID N → per-process domain(可能需要 ATS 配合)
}
七、前沿趋势:CXL、CHAI 与 IOMMU 协作
7.1 CXL.mem 对 IOMMU 的影响
CXL 2.0/3.0 引入了 CXL.mem(内存扩展)。CXL 连接的内存设备可以被 CPU 像访问本地 PCIe BAR 一样访问,但其物理地址需要经 CPU 内存控制器映射。在 IOMMU 视角下,CXL 设备属于 PCIe 端点,需要相同的地址翻译。
关键挑战:
7.2 ARM CHAI(Cache Hardware Attribute Interface)
CHAI 是 ARM 提出的缓存属性接口,允许 SMMU 和设备直接查询缓存配置,实现更高效的一致性协议。在 AI 集群中,CHAI 的潜在应用包括:
7.3 未来方向:IOMMU 与 Accelerator Coherency
随着 GPU Direct Async 和 CXL 链路级一致性(Cache Coherency)的发展,IOMMU 的角色将从"翻译+隔离"向"一致性协调节点"演进。
Nvidia 的 Magnum IO 和 NVSwitch 已实现 GPU-GPU 的 peer-to-peer 免 CPU 干预传输,IOMMU 在未来的定位可能是:
┌──────────────────────────────────────────────┐
│ 未来 IOMMU 愿景 │
│ │
│ 1. 地址翻译(当前核心) │
│ 2. 一致性协调(CXL 3.0 CHI 链路) │
│ 3. 中断与 PRI 虚拟化 │
│ 4. 内存加密(TSM/Confidential Computing) │
│ 5. QoS 控制(PASID-level 带宽分配) │
└──────────────────────────────────────────────┘
八、总结与最佳实践
在 AI 生产环境中,IOMMU 的配置直接影响训练吞吐、推理延迟和多租户安全。本文总结了以下核心实践:
IOMMU 不是简单的"开关",而是一个需要在硬件拓扑、操作系统配置和加速器驱动三者间精确对齐的系统工程。理解其底层原理,才能在追求极值性能时做出正确取舍。
*作者:ybb.press | 发布于 Linux与内核频道 | 2026-10-07*

发表评论 取消回复