3、IOMMU(SMMU)原理:SMMU硬件架构、页表与转换、TLB与缓存一致性
在SA8155平台中,SMMU(System Memory Management Unit,系统内存管理单元)是GPU与系统内存之间的关键桥梁。它负责将GPU等外设发起的设备虚拟地址(Device Virtual Address, DVA)转换为物理地址(Physical Address, PA),并提供内存保护与隔离机制。本章深入剖析SMMU的硬件架构、页表转换机制、TLB工作原理以及缓存一致性问题。
3.1 SMMU硬件架构
SMMU本质上是一种专为外设设计的IOMMU(Input-Output Memory Management Unit)。在SA8155中,SMMU集成在系统总线上,位于GPU、显示控制器等主设备与内存控制器之间。其核心架构包含以下组件:
核心组件:
- Translation Control Unit (TCU):负责执行页表遍历(Page Table Walk),是SMMU的运算核心。
- Translation Buffer Unit (TBU):包含TLB(Translation Lookaside Buffer),缓存最近使用的页表转换结果。
- Context Banks:每个Context Bank对应一个独立的地址空间,用于隔离不同设备或不同进程的地址映射。
- Stream ID (SID) 与 Substream ID (SSID):SID标识主设备,SSID标识设备内的不同上下文(如进程),用于路由转换请求。
- PMU (Performance Monitoring Unit):用于统计TLB命中率、页表遍历次数等性能指标。
3.2 页表(Page Table)与转换(Translation)
SMMU使用多级页表结构将设备虚拟地址(DVA)转换为物理地址(PA)。SA8155的SMMU支持多达4级页表,与ARMv8 CPU的页表格式兼容,便于统一管理。
3.2.1 页表层级结构
| 页表级别 | 粒度 | 覆盖范围 | 典型用途 |
|---|---|---|---|
| L0 (PGD) | 512GB | 整个地址空间 | 顶级页表,指向L1页表 |
| L1 (PUD) | 1GB | 大块内存区域 | 映射大块连续显存 |
| L2 (PMD) | 2MB | 中等粒度 | 纹理、帧缓冲 |
| L3 (PTE) | 4KB | 小粒度 | 细粒度分配、稀疏映射 |
实战提示:GPU驱动中,显存分配通常优先使用2MB或1GB大页(Huge Page),以减少页表级数和TLB压力,提升GPU访问性能。
3.2.2 地址转换流程
当GPU发起内存访问时,SMMU执行以下转换步骤:
- 输入:GPU发出设备虚拟地址(DVA)和Stream ID。
- TLB查找:SMMU首先在TLB中查找DVA对应的物理地址。
- TLB命中:直接返回物理地址,访问内存。
- TLB未命中:TCU启动页表遍历(Page Table Walk),从L0到L3逐级查找。
- 权限检查:验证访问权限(读/写/执行)和内存属性(可缓存、可共享等)。
- 返回结果:将转换结果写入TLB,并返回物理地址。
3.3 TLB(Translation Lookaside Buffer)与缓存一致性
TLB是SMMU中关键的硬件缓存,用于加速地址转换。然而,在多核异构系统中,TLB与CPU缓存、GPU缓存之间的数据一致性是必须解决的核心问题。
3.3.1 TLB结构与管理
| TLB类型 | 存储内容 | 特点 |
|---|---|---|
| 微TLB (Micro TLB) | 最近使用的少量条目 | 极低延迟,容量小(通常32-64条) |
| 主TLB (Main TLB) | 大量转换条目 | 容量大(512-1024条),命中率更高 |
| 共享TLB (Shared TLB) | 多Context共享 | 减少重复缓存,节省面积 |
3.3.2 TLB无效化(TLB Invalidation)
当页表被修改(如释放显存、更改映射)时,必须使对应的TLB条目失效,否则SMMU可能使用过时的转换结果,导致数据错误或安全漏洞。
关键风险:TLB未及时无效化是GPU驱动中常见的Bug来源。例如,在释放显存后未执行TLB无效化,可能导致后续分配的内存被错误映射,引发数据泄露或GPU崩溃。
TLB无效化操作分为两种粒度:
- 全局无效化:清除所有TLB条目,开销大,但简单可靠。
- 按地址空间无效化:仅清除特定Context Bank或特定地址范围的TLB条目,效率更高。
3.3.3 缓存一致性问题
在SA8155中,GPU、CPU、DSP等多个主设备共享系统内存。SMMU的页表属性中包含了内存类型和共享属性,用于控制缓存一致性:
| 内存属性 | 描述 | 适用场景 |
|---|---|---|
| Non-cacheable | 不缓存,直接访问内存 | 寄存器、同步变量 |
| Write-back Cacheable | 回写缓存,延迟写入 | 纹理、帧缓冲(性能优先) |
| Write-through Cacheable | 写直达缓存,立即写入内存 | 共享数据,需要实时可见 |
| Inner Shareable | 在CPU集群内保持一致 | CPU与GPU共享数据 |
| Outer Shareable | 在系统全局保持一致 | 多主设备共享(GPU+DPU+VPU) |
3.4 实战要点:SMMU配置与调试
在SA8155 GPU驱动开发中,SMMU配置涉及以下关键步骤:
SMMU初始化流程:
- 解析设备树:获取SMMU寄存器基地址、中断号、Stream ID映射表。
- 复位与使能:复位SMMU硬件,使能TCU和TBU。
- 配置Context Bank:为GPU分配独立的Context Bank,设置页表基地址寄存器(TTBR)。
- 设置Stream ID:将GPU的Stream ID绑定到对应的Context Bank。
- 分配页表:在内存中分配页表结构,建立DVA到PA的映射。
- 使能转换:设置SMMU_CRx寄存器,使能地址转换。
调试SMMU问题时,常用的手段包括:
- 读取SMMU故障寄存器:如SMMU_FSR(Fault Status Register),获取转换错误类型和地址。
- 检查页表内容:通过dump页表内存,验证映射是否正确。
- 使用PMU统计:监控TLB命中率,判断是否需要调整页表粒度。
- TLB无效化验证:在关键操作前后执行TLB无效化,观察是否解决数据不一致问题。
// 示例:SMMU故障寄存器读取(伪代码)
uint32_t fsr = readl(smmu_base + SMMU_FSR);
if (fsr & FSR_TF) {
uint32_t far = readl(smmu_base + SMMU_FAR); // 故障地址
uint32_t sid = readl(smmu_base + SMMU_FSYNR0); // Stream ID
printf("SMMU Fault: addr=0x%x, sid=0x%x\n", far, sid);
writel(fsr, smmu_base + SMMU_FSR); // 清除故障
}
性能优化建议:
- 使用2MB大页映射GPU频繁访问的显存区域,减少TLB压力。
- 在进程切换时,使用按地址空间TLB无效化而非全局无效化,降低开销。
- 对于只读数据(如纹理),设置页表属性为只读,防止意外写入。