关于MIPS平台的asid_cache debug的总结
来源:互联网 发布:淘宝店铺花呗被关闭 编辑:程序博客网 时间:2024/05/17 09:27
最近遇到一个非常有意思的问题,MIPS平台的CPUHOTPLUG会有一定的概率导致应用程序出现段错误,花了几天的时间排除了一大堆问题后,最后定位到了MIPS公司的一段代码上,竟然是在trap_init里又对asid_cache进行了初始化!
在网上很少有关于asid的内容,有也讲的不详不尽,今天写完了文档,决定把它贴出来!欢迎一起讨论。
===============================================================================================
动态开关核TLB异常问题总结
问题产生的背景:
mips的MMU使用asid+vpfn作为主键索引TLB表项,而asid只有8bits大小,如果使用asid作为进程ID,则最多只能索引256个不同进程的TLB项。
而linux的进程数目不可能局限于256个,所以linux使用了一种分组的方式,将asid进行了扩展,扩展后的asid已经和进程ID完全无关,只和进程的上下文环境有关。
要了解这次TLB异常的详细原因,就一定要明白linux是如何解决asid扩展问题的。
KERNEL的解决方案:
linux将进程ID和asid剥离开来,使asid和进程ID完全成了两个概念,linux的asid由一个32位的长整数来表示,我们可以将它的低8位看作实际要填写到TLB表项里的asid号,而高24位则用做分组号。每个进程在不同的CPU上都有一个asid变量用于标识该进程在不同CPU上的不同asid。这个asid在linux中使用cpu_context(cpu, mm)来进行访问。每个CPU还有一个单独的变量asid_cache,这个变量的作用稍后会提到。
首先我们来说明linux解决的第一个问题:
为了方便说明,我们这里假设所有进程都要对0x0000 0000这个虚拟地址进行访问,而这个地址被映射到了不同的物理页面上。
假设存在以下几个进程,并且它们的地址映射关系为:
进程A : asid 0x001 , vpfn = 0 , ppfn =1
进程B : asid 0x101 , vpfn = 0 , ppfn =2
进程C : asid 0x301 , vpfn = 0 , ppfn =3
这里的asid代表linux扩展后的asid,vpfn代表虚拟页的页号,ppfn代表其映射到的实际物理页面的页号。当进程A首先被调度,tlb refill异常处理代码会读取kernel页表项,将生成的TLB表项填充到CPU TLB里面去,因为TLB表项的asid只有8bits大小,所以生成的TLB表项的映射关系为:
[ (进程A asid & 0xff), vpfn ,ppfn]= [0x001& 0xff,0,1] =[ 0x01 , 0 , 1 ]
这时候再调度到进程B,因为CPU会用[ (进程B asid & 0xff ) + vpfn ] 去索引,既查找:
[ (进程B asid & 0xff), vpfn ,ppfn]= [0x101& 0xff,0,1]=[ 0x01 , 0 , 1 ]
是否有效,这时候它发现TLB中已存在有效映射,但实际进程B的0号虚拟页面对应的是2号物理页面,但CPU却取到了1号物理页面的内容,这时候就出错了。要解决这个问题,只需要在调度到进程B之前,将现有CPU内的TLB表项全部清空即可。
kernel的实现方法就用到了前面提到的asid_cache,asid_cache实际上保存的是当前进程在CPU上的asid,当准备切换进程上下文时,对比asid_cache与即将调度到的进程的asid,如果存在换组的情况,则说明可能会存在asid低8位重叠,所以执行清空TLB。
这段kernel代码为:
169 /* Check if our ASID is ofan older version and thus invalid */
170 if ((cpu_context(cpu, next)^ asid_cache(cpu)) & ASID_VERSION_MASK)
171 get_new_mmu_context(next, cpu);
我们用前面提到的进程A和B来加深理解:
进程A的asid为0x001,当执行进程A时,cpu的asid_cache值为0x001,当要调度进程B,切换其上下文时,asid_cache ^ 进程B的asid既 (0x001 ^0x101) = 0x100,说明切换进程就要切换进程的asid组,所以需要清空TLB。这时候再执行进程B,因为TLB中已经不存在进程A的映射关系,所以不会再出现前面提到的错误。
注意:换组操作不一定会引发真正的清空TLB操作,考虑下面这种情况:
组1 组2
asid 0x000,vpfn 0,ppfn0 asid 0x110,vpfn 0,ppfn 16
asid 0x001,vpfn 0,ppfn1 asid 0x111,vpfn 0,ppfn 17
…..... …...
asid 0x00e,vpfn 0,ppfn14 asid0x11e,vpfn 0,ppfn 30
asid 0x00f,vpfn 0,ppfn15 asid0x11f,vpfn 0,ppfn 31
由组1切换到组2,虽然发生了asid组切换,但组内的进程asid低8位没有相同的情况,所以也
没必要清空TLB。解决了一个问题,还有个问题需要解决:
假设进程A和进程B一直被频繁调度,现在的代码会频繁的判断asid组是否改变,清空tlb项的操作就会频繁运行,大大的影响了系统运行的效率——我们只因为两个冲突的TLB项而频繁清空TLB,32个TLB表项我们只用到了其中一项,剩余的31个完全没有被利用起来。为了解决这个问题,kernel的思路是在刷新TLB时,改变引起刷新操作的进程的asid:
首先我们看看代码:
118 static inline void
119 get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
120 {
121 unsigned long asid =asid_cache(cpu);
122
123 if (! ((asid += ASID_INC)& ASID_MASK) ) {
124 if (cpu_has_vtag_icache)
125 flush_icache_all();
126 local_flush_tlb_all(); /* start new asid cycle */
127 if(!asid) /*fix version if needed */
128 asid = ASID_FIRST_VERSION;
129 }
130 cpu_context(cpu, mm) =asid_cache(cpu) = asid;
131 }
这段代码的思路是这样的:当一个进程被调度到时,如果需要清空TLB,则将asid_cache的值加1,然后将这个新的asid赋值给即将调度到的进程asid,这样就实现了进程asid组迁移。因为所有的调度全部遵循这个规则,所以asid_cache的值始终为该cpu上asid的最大值,这样就保证asid_cache+1不被别的进程asid占用,而实现了asid组迁移。
用进程A和B来加深理解:
进程B在运行,asid_cache = 0x101
调度到进程A,触发TLB清空操作
A(asid) = asid_cache++ = 0x102
再调度到B,同组,不会再清空TLB,再调度到A同理。
BUG:
smp在开关核的时候会执行traps_init,这段函数会重新初始化 asid_cache的值,这就引发了
BUG,解释如下:
假设asid_cached初始化值为256,某进程在多次调度后,其asid值变为了257,这时候进行一次开关核操作,另一个进程被调度到的时候,asid=asid_cached+1,既也变为了257,这样就存在不同的进程asid值却一样,当这两个进程被轮番调度时,因为asid相同,所以会出现TLB索引出错的情况。
BUG解决方法有两个:
1)关CPU核时,将相对与这个核的所有进程的asid清空为0.
2)开核时,不对asid_cache进行重新初始化,而使用上一次的最大值。
第2个方法更好更直接。
--
转载 http://www.newsmth.net/nForum/#!article/KernelTech/67424
- 关于MIPS平台的asid_cache debug的总结
- 关于debug使用的总结
- mips平台的u-boot启动流程
- 移植webkit到c2的mips平台
- mips平台下的button按键驱动程序
- MIPS平台前端显示移动硬盘的使用率
- MIPS 平台出现的 Bus Error
- 关于mips结构中地址窗口的分析(基于loongson3A平台)
- 关于龙芯拿MIPS授权的一点理解
- 关于ARM和MIPS的架构讨论
- 关于Python的Debug...
- 关于Cookies的Debug
- 关于debug的问题
- 编译MIPS平台的远程调试工器gdb/gdbserver
- Android平台上GDB for MIPS芯片的构建
- MIPS平台OpenWrt路由器系统内的Rust应用程序开发
- MIPS平台OpenWrt路由器系统内的Rust应用程序开发
- curl 7.42.1在mips平台上的移植
- Lucene的工作原理
- NSNotificationCenter 编写日志组件
- Long Time Operations in ASP.NET
- Android 多按钮Button 动态批量监听
- hdu 2562
- 关于MIPS平台的asid_cache debug的总结
- 使用android DDMS
- Android HAL实例解析
- spring 取bean对象
- EGOImageView的使用方法及注意事项
- 解决VirtualBox rc=1908错误
- 动态数组
- 深入分析 Java 中的中文编码问题
- 数论概论笔记 第10章 同余式、幂与欧拉函数