想了解更多关于开源的内容,请访问:
成都创新互联公司总部坐落于成都市区,致力网站建设服务有网站建设、网站制作、网络营销策划、网页设计、网站维护、公众号搭建、小程序设计、软件开发等为企业提供一整套的信息化建设解决方案。创造真正意义上的网站建设,为互联网品牌在互动行销领域创造价值而不懈努力!
开源基础软件社区
https://ost.51cto.com
上一篇文章中介绍了loongarch中TLB相关的异常处理,这一篇文章继续介绍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。
虚拟地址空间的引入为程序提供了方便,但同时也带来了其他问题。
其中,有一种称为homonym的问题,其指的是单个虚拟地址指向多个物理地址的情况。例如,在多个进程中使用了相同的虚拟地址,但这些虚拟地址实际上指向了不同的物理地址。当进程切换、虚拟地址空间切换、页表修改等情况时,硬件上的缓存数据不一定会自动进行同步,此时就会有数据不一致的问题。
因此,操作系统在软件上需要手动去维护相关的硬件数据一致性:
在一些早期的架构中,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匹配的表项。见后文介绍。
相较于cache维护,TLB相关的维护是本文着重介绍的内容。主要是因为目前loongarch中cache相关的资料较少。
一般来说,在上下文切换、页表修改等情况时,是否需要在软件上对cache进行维护,与具体的架构和cache类型有关。以ARM架构为例,其中VIVT类型的cache在涉及页表切换等操作时需要进行维护。
下面用linux中loongarch下TLB flush相关API对invtlb指令举例说明。
注:目前loongarch手册中的op操作类型似乎不全。
// invtlb指令中的op
enum invtlb_ops {
/* Invalid all tlb */
INVTLB_ALL = 0x0,
/* Invalid current tlb */
INVTLB_CURRENT_ALL = 0x1,
/* Invalid all global=1 lines in current tlb */
INVTLB_CURRENT_GTRUE = 0x2,
/* Invalid all global=0 lines in current tlb */
INVTLB_CURRENT_GFALSE = 0x3,
/* Invalid global=0 and matched asid lines in current tlb */
INVTLB_GFALSE_AND_ASID = 0x4,
/* Invalid addr with global=0 and matched asid in current tlb */
INVTLB_ADDR_GFALSE_AND_ASID = 0x5,
/* Invalid addr with global=1 or matched asid in current tlb */
INVTLB_ADDR_GTRUE_OR_ASID = 0x6,
/* Invalid matched gid in guest tlb */
INVGTLB_GID = 0x9,
/* Invalid global=1, matched gid in guest tlb */
INVGTLB_GID_GTRUE = 0xa,
/* Invalid global=0, matched gid in guest tlb */
INVGTLB_GID_GFALSE = 0xb,
/* Invalid global=0, matched gid and asid in guest tlb */
INVGTLB_GID_GFALSE_ASID = 0xc,
/* Invalid global=0 , matched gid, asid and addr in guest tlb */
INVGTLB_GID_GFALSE_ASID_ADDR = 0xd,
/* Invalid global=1 , matched gid, asid and addr in guest tlb */
INVGTLB_GID_GTRUE_ASID_ADDR = 0xe,
/* Invalid all gid gva-->gpa guest tlb */
INVGTLB_ALLGID_GVA_TO_GPA = 0x10,
/* Invalid all gid gpa-->hpa tlb */
INVTLB_ALLGID_GPA_TO_HPA = 0x11,
/* Invalid all gid tlb, including gva-->gpa and gpa-->hpa */
INVTLB_ALLGID = 0x12,
/* Invalid matched gid gva-->gpa guest tlb */
INVGTLB_GID_GVA_TO_GPA = 0x13,
/* Invalid matched gid gpa-->hpa tlb */
INVTLB_GID_GPA_TO_HPA = 0x14,
/* Invalid matched gid tlb,including gva-->gpa and gpa-->hpa */
INVTLB_GID_ALL = 0x15,
/* Invalid matched gid and addr gpa-->hpa tlb */
INVTLB_GID_ADDR = 0x16,
};
/*
* invtlb op info addr
* (0x1 << 26) | (0x24 << 20) | (0x13 << 15) |
* (addr << 10) | (info << 5) | op
*/
// 基于机器码封装了invtlb op info addr格式的指令
static inline void invtlb(u32 op, u32 info, u64 addr)
{
__asm__ __volatile__(
"parse_r addr,%0\n\t"
"parse_r info,%1\n\t"
".word ((0x6498000) | (addr << 10) | (info << 5) | %2)\n\t"
:
: "r"(addr), "r"(info), "i"(op)
:
);
}
// invtlb op 0 0指令
static inline void invtlb_all(u32 op, u32 info, u64 addr)
{
__asm__ __volatile__(
".word ((0x6498000) | (0 << 10) | (0 << 5) | %0)\n\t"
:
: "i"(op)
:
);
}
本节结合linux中上下文切换部分代码对TLB invalidate操作进行分析。
以下为linux中context_switch上下文切换函数的流程:
context_switch(struct rq *rq, struct task_struct *prev,
| struct task_struct *next)
| // 更新时间片、最近进队时间等调度信息以及其他准备工作
|-> prepare_task_switch(rq, prev, next);
|
| // 虚拟地址空间切换相关
|-> mm = next->mm;
| oldmm = prev->active_mm;
| if (!mm) {
| next->active_mm = oldmm;
| atomic_inc(&oldmm->mm_count); // 增加oldmm的引用计数
| // 其他架构(x86)相关,这里不关注
| enter_lazy_tlb(oldmm, next);
| } else
| // 切换到用户进程,需切换进程虚拟空间
| switch_mm(oldmm, mm, next);
|
|-> ...
|
| // 切换任务上下文
|-> switch_to(prev, next, prev);
| // 切换后再次被调度时向下执行
|
| // 用barrier机制同步,
| // 保证switch_to和finish_task_switch的执行顺序
|-> barrier();
| // 使用gcc内联汇编:::"memory"语法实现,
| // 这样编译器不会优化此语句前后的访存顺序
|-> #define barrier() __asm__ __volatile__("": : :"memory")
|
| // 再次被调度时,进行清理工作
|-> finish_task_switch(this_rq(), prev);
在进行任务上下文切换函数switch_to之前,如果涉及进程虚拟地址空间改变,则需要切换mmu上下文。上面switch_mm函数的作用就是切换mmu上下文。
loongarch架构代码中switch_mm及相关函数的分析如下:
switch_mm(struct mm_struct *prev, struct mm_struct *next,
| struct task_struct *tsk)
|-> switch_mm_irqs_off(prev, next, tsk);
|-> unsigned int cpu = smp_processor_id();
|
| /* Check if our ASID is of an older version and thus invalid */
| // 如果asid不同,则需重新分配
|-> if (!asid_valid(next, cpu))
| get_new_mmu_context(next, cpu);
|
| // 写入asid到寄存器CSR.ASID
|-> write_csr_asid(cpu_asid(cpu, next));
|
| // 切换页表
|-> if (next != &init_mm)
| csr_writeq((unsigned long)next->pgd, LOONGARCH_CSR_PGDL);
| else
| csr_writeq((unsigned long)invalid_pg_dir, LOONGARCH_CSR_PGDL);
|
|-> ...
#define cpu_context(cpu, mm) ((mm)->context.asid[cpu])
#define asid_cache(cpu) (cpu_data[cpu].asid_cache)
static inline int asid_valid(struct mm_struct *mm, unsigned int cpu)
{
// 如果mm中的asid和当前asid_cache不同,则返回无效
if ((cpu_context(cpu, mm) ^ asid_cache(cpu)) & asid_version_mask(cpu))
return 0;
return 1;
}
static inline void
get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
{
| // 循环递增地分配新的asid,为旧的asid_cache + 1
|-> u64 asid = asid_cache(cpu);
| if (!((++asid) & cpu_asid_mask(&cpu_data[cpu])))
| // 当asid溢出时开始新的分配循环,此时需刷新TLB中所有用户部分
| local_flush_tlb_user(); /* start new asid cycle */
|
| // 将mm中asid和当前asid_cache设为新分配的asid
|-> cpu_context(cpu, mm) = asid_cache(cpu) = asid;
}
其中,switch_mm函数主要完成两个任务:
上面的asid_valid函数检测ASID是否变化,get_new_mmu_context函数负责重新分配ASID和ASID溢出时调用local_flush_tlb_user函数进行invalidate TLB操作。
local_flush_tlb_user函数分析如下:
local_flush_tlb_user(void)
| // 刷新所有global=0的TLB表项
|-> invtlb_all(INVTLB_CURRENT_GFALSE, 0, 0);
另外,上面代码中ASID管理部分可以进一步改进,因为每次检测到ASID变化后,mm结构体被设置了一个新分配的ASID,这样实际上未能利用mm结构体中原来的ASID和TLB中对应缓存数据。
本文介绍了TLB维护操作和相关指令,并结合linux中代码进行了分析。这篇文章之后,本系列文章暂时告一段落。主要是目前loongarch相关的资料有限,描述二进制翻译扩展等扩展内容的loongarch手册第二、三卷也还没有出。
最后,在查询loongarch资料(主要是基于龙芯手册第一卷1.02和linux中loongarch部分源码)的过程中也发现了一些不足点或者是不够详细的地方,这里一并列出:
想了解更多关于开源的内容,请访问:
开源基础软件社区
https://ost.51cto.com
当前标题:loongarch架构介绍—(五)TLB维护
本文来源:http://www.shufengxianlan.com/qtweb/news42/63042.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联