#loongarch架构介绍# [五] TLB维护 原创 精华
作者:蒋卫峰 李涛
前言
上一篇文章中介绍了loongarch中TLB相关的异常处理,这一篇文章继续介绍TLB相关的维护操作。
1. TLB硬件组织机构
首先补充loongarch中TLB相关的硬件背景知识。
loongarch架构中,TLB分为两个部分:一个是所有表项的页大小都相同的Singular-Page-Size TLB,简称STLB;一个是支持不同表项的页大小可以不同的Multiple-Page-Size TLB,简称MTLB。在进行虚实地址转换时,STLB和MTLB同时查找。同时软件上需保证不会出现MTLB和STLB同时命中的情况。
STLB和MTLB的表项格式基本一致,区别仅在于MTLB每个表项中均包含了一个页大小的信息,而STLB每个表项中无此信息。
回顾上一篇文章中介绍了的TLB表项格式:
其中PS(page size)域仅存在于MTLB。
2. 虚拟内存系统与硬件维护
虚拟地址空间的引入为程序提供了方便,但同时也带来了其他问题。
其中,有一种称为homonym的问题,其指的是单个虚拟地址指向多个物理地址的情况。例如,在多个进程中使用了相同的虚拟地址,但这些虚拟地址实际上指向了不同的物理地址。当进程切换、虚拟地址空间切换、页表修改等情况时,硬件上的缓存数据不一定会自动进行同步,此时就会有数据不一致的问题。
因此,操作系统在软件上需要手动去维护相关的硬件数据一致性:
-
TLB维护:TLB中缓存的页表项就有可能因为软件上对页表的修改,出现数据不一致的问题。操作系统需要进行invalidate(或者称为flush)TLB的操作,将相应的表项无效。
-
cache维护:某些类型的cache也可能会因页表的修改,出现数据不一致的问题。对于这些cache,操作系统同样需要进行clean或invalidate操作。
2.1 TLB维护与ASID
在一些早期的架构中,invalidate TLB操作实际上是将整个TLB中的数据无效,因为TLB因homonym问题无法判断其缓存的表项属于哪个进程。
但实际上,刷新整个TLB的数据是一件相当浪费性能的事,既会影响上下文切换时的速度,也会降低TLB加速的作用。因此,后来的架构一般都会在TLB中加入其他标识以识别不同的进程或者虚拟地址空间,这样TLB不需要每次切换上下文都进行invalidate操作。
ASID(Address Space ID)就是这样的标识。loongarch中支持ASID,见上文中TLB表项图,其中就有ASID域。每个TLB表项都有ASID,ASID由操作系统在软件上进行分配,一般一个虚拟地址空间有一个唯一的ASID,这样就有效减少了TLB invalidate操作的次数。
loongarch中,CSR.ASID寄存器可以控制当前TLB使用的ASID,如下图:
同时loongarch中TLB相关维护指令也支持根据ASID,只无效部分ASID匹配的表项。见后文介绍。
2.2 cache维护
相较于cache维护,TLB相关的维护是本文着重介绍的内容。主要是因为目前loongarch中cache相关的资料较少。
一般来说,在上下文切换、页表修改等情况时,是否需要在软件上对cache进行维护,与具体的架构和cache类型有关。以ARM架构为例,其中VIVT类型的cache在涉及页表切换等操作时需要进行维护。
3. TLB相关维护指令
-
tlbclr
:tlbclr指令根据TLB相关CSR中的信息无效TLB中的内容。-
当CSR.TLBIDX.Index落在MTLB范围内时,执行tlbclr,将MTLB中所有G=0且ASID等于CSR.ASID.ASID的表项无效。其中G、ASID为表项中的域。
-
当CSR.TLBIDX.Index落在STLB范围内时,执行tlbclr,将STLB中CSR.TLBIDX.Index对应的且G=0且ASID等于CSR.ASID.ASID的表项无效。
-
-
tlbflush
:tlbflush指令同样根据TLB相关CSR中的信息无效TLB中的内容,但作用范围较tlbclr指令更广。-
当CSR.TLBIDX.Index落在MTLB范围内时,执行tlbflush,将MTLB中所有的表项无效。
-
当CSR.TLBIDX.Index落在STLB范围内时,执行tlbflush,将STLB中CSR.TLBIDX.Index对应的表项无效。
-
-
invtlb op, rj, rk
:invtlb指令同样用于无效TLB中的内容,但相较于tlbclr和tlbflush指令更加灵活。-
op表示操作类型,下面是loongarch手册中列出的op类型:
-
op=0:清除所有表项
-
op=1:清除所有表项。效果和op=0完全一致
-
op=2:清除所有G=1的表项
-
op=3:清除所有G=0的表项
-
op=4:清除所有G=0,且ASID等于寄存器指定ASID的表项
-
op=5:清除所有G=0,ASID等于寄存器指定ASID,且VA等于寄存器指定VA的表项
-
op=6:清除所有G=1或ASID等于寄存器指定ASID,且VA等于寄存器指定VA的表项
-
-
通用寄存器rj中存放ASID信息。当op对应的操作不需要ASID时,rj应设置为r0
-
通用寄存器rk中存放VA虚拟地址信息。当op对应的操作不需要VA时,rk应设置为r0
-
下面用linux中loongarch下TLB flush相关API对invtlb指令举例说明。
注:目前loongarch手册中的op操作类型似乎不全
4. 上下文切换和TLB维护
本节结合linux中上下文切换部分代码对TLB invalidate操作进行分析。
以下为linux中context_switch上下文切换函数的流程:
在进行任务上下文切换函数switch_to之前,如果涉及进程虚拟地址空间改变,则需要切换mmu上下文。上面switch_mm函数的作用就是切换mmu上下文。
loongarch架构代码中switch_mm及相关函数的分析如下:
其中,switch_mm函数主要完成两个任务:
- 维护TLB:如前文所述,loongarch中上下文切换时需要维护TLB数据一致性。在上面的代码中,是结合ASID进行实现:
- 每次检测ASID是否变化,如果变化则说明虚拟地址空间需要进行切换,CSR.ASID寄存器需要重新设置
- 上面代码实现中是通过循环递增的方式分配新的ASID,当ASID溢出时需要使用invalidate TLB操作来保证数据一致性
- 切换页表:不同的用户虚拟地址空间有不同的页表,需通过设置相关寄存器进行切换。页表相关的配置可参考前面的文章。
上面的asid_valid函数检测ASID是否变化,get_new_mmu_context函数负责重新分配ASID和ASID溢出时调用local_flush_tlb_user函数进行invalidate TLB操作。
local_flush_tlb_user函数分析如下:
另外,上面代码中ASID管理部分可以进一步改进,因为每次检测到ASID变化后,mm结构体被设置了一个新分配的ASID,这样实际上未能利用mm结构体中原来的ASID和TLB中对应缓存数据。
总结
本文介绍了TLB维护操作和相关指令,并结合linux中代码进行了分析。这篇文章之后,本系列文章暂时告一段落。主要是目前loongarch相关的资料有限,描述二进制翻译扩展等扩展内容的loongarch手册第二、三卷也还没有出。。
最后,在查询loongarch资料(主要是基于龙芯手册第一卷1.02和linux中loongarch部分源码)的过程中也发现了一些不足点或者是不够详细的地方,这里一并列出:
-
指令的介绍有信息遗漏:如第一篇文章中列出的一部分手册上没有,但在代码中出现的move等指令;又如前文中的invtlb指令中的op操作类型相比代码中缺少一些
-
内存一致性模型,内存访存类型的信息不够详细:第二篇文章中提到了这点,如手册中出现的一致可缓存等术语没有解释
-
中断机制相关信息不够详细
-
cache相关信息不够详细
更多原创内容请关注:深开鸿技术团队
入门到精通、技巧到案例,系统化分享OpenHarmony开发技术,欢迎投稿和订阅,让我们一起携手前行共建生态。
感谢老师的解读,希望第二、三卷手册能尽快发布
从第一节读过来,希望龙芯和OpenHarmony都能走更远吧
默默点个赞
芯片了解的不多,跟老师从头学下
文章很优秀,希望龙芯能越来越好
老师解读的太详细了