3. 多级缓存架构:TLB与PTC的分级设计

说到SMMU的性能,缓存架构是绕不开的核心。我个人习惯把SMMU的缓存体系比作一个「三级火箭」——微TLB、主TLB、页表缓存PTC,每一级都有自己的使命。

你想想看,CPU发一个DMA请求过来,虚拟地址要转成物理地址。如果每次都去内存里翻页表,那延迟谁都受不了。所以SMMU必须用缓存来加速。但缓存怎么分级、每级存什么、命中率怎么平衡,这里面门道不少。

3.1 微TLB(Micro TLB)——第一道防线

微TLB,也叫ITLB(Instruction TLB)或DTLB(Data TLB)的微型版本。它位于SMMU流水线的最前端,紧挨着翻译引擎。

核心特点:

  • 容量小:通常只有8~32个条目
  • 全相联:查找速度快,一个时钟周期就能出结果
  • 只缓存最近使用的映射:说白了就是热数据

我在项目中遇到过一个问题:某个GPU驱动频繁切换上下文,微TLB的命中率掉到了30%以下。结果就是每次翻译都要穿透到主TLB,延迟从1个周期飙升到10几个周期。后来我们做了个微TLB的上下文标记优化,命中率才回到80%以上。

实战建议:微TLB的条目数不是越多越好。我见过有人把微TLB做到128条,结果面积翻倍,时序反而跑不上去。一般来说,16~32条是性价比最高的区间。

3.2 主TLB(Main TLB)——中坚力量

微TLB没命中,就会走到主TLB。主TLB的容量大得多,通常是512~1024个条目,采用组相联结构。

特性 微TLB 主TLB
容量 8~32条目 512~1024条目
关联度 全相联 4~8路组相联
查找延迟 1周期 2~4周期
功耗 低 中等
替换策略 LRU或FIFO 伪LRU

主TLB的设计有个关键点——页粒度。我记得有一次调试,发现主TLB的命中率波动很大。查了半天,原来是4KB小页和2MB大页混用,导致TLB条目被频繁替换。后来我们强制对大页做了专门的TLB分区,问题才解决。

避坑指南:我曾经在某个项目中,主TLB的替换策略用了最简单的FIFO。结果遇到一个流式访问模式,命中率惨不忍睹。后来换成伪LRU,命中率提升了15%。替换策略的选择,真的不能偷懒。

3.3 PTC(Page Table Cache)——最后的堡垒

如果主TLB也没命中,那就得走页表漫步(Page Table Walk)了。但页表漫步需要多次访问内存,延迟很高。这时候PTC就派上用场了。

PTC缓存的是页表中间级别的条目,而不是最终的物理地址。说白了,它存的是「页表的页表」。

PTC的分级结构:

  • L1 PTC:缓存PGD/PUD级别的条目,容量最小,速度最快
  • L2 PTC:缓存PMD级别的条目,容量中等
  • L3 PTC:缓存PTE级别的条目,容量最大,但速度最慢

你可能会问:为什么不直接用主TLB覆盖所有情况?原因很简单——面积和功耗。主TLB做到1024条已经很大了,但页表可能有几百万条。PTC用更小的代价,缓存了页表漫步过程中的热点数据。

3.4 三级缓存的协同工作流程

下面这张图展示了整个翻译流程:

SMMU多级缓存翻译流程 DMA请求(VA) 微TLB(8~32条目) 全相联 · 1周期 命中 输出PA 未命中 主TLB(512~1024条目) 组相联 · 2~4周期 命中 输出PA 未命中 PTC(L1/L2/L3页表缓存) 缓存页表中间条目 命中 输出PA 未命中 页表漫步(Page Table Walk) 多次内存访问 · 高延迟 输出PA 说明:每一级缓存未命中时,都会向下一级发起查询,直到页表漫步完成 命中率目标:微TLB > 80%,主TLB > 95%,PTC > 99%

3.5 缓存一致性维护

多级缓存带来的最大挑战就是一致性。当软件修改了页表,硬件必须知道哪些缓存条目失效了。

我记得在一个项目中,驱动更新了页表但没有做TLB invalidation,结果DMA读到了旧数据。排查了整整两天才发现是缓存一致性问题。

关键机制:

  • TLB Invalidation:通过SMMU的INVAL寄存器,可以按ASID、VA范围或全局方式刷TLB
  • PTC Invalidation:页表修改后,需要同步刷PTC,否则页表漫步会读到旧数据
  • 广播机制:在多SMMU系统中,一个SMMU的TLB刷需要广播到其他SMMU

3.6 性能调优实战要点

基于我多年的调试经验,这里给出几个调优方向:

调优项 方法 预期效果
微TLB命中率 增加条目数、优化上下文切换频率 减少主TLB访问次数
主TLB命中率 使用大页、优化替换策略 减少页表漫步次数
PTC命中率 增加PTC容量、优化页表布局 减少内存访问延迟
TLB刷效率 按ASID刷、批量刷 减少刷TLB的开销

我的经验:调优时不要只看单个缓存的命中率。我习惯用perf工具同时监控微TLB、主TLB和PTC的命中率,看整体流水线的延迟分布。有时候微TLB命中率低一点没关系,只要主TLB能兜住就行。