关于MIPS平台的asid_cache debug的总结

最近遇到一个非常有意思的问题,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
将进程IDasid剥离开来,使asid和进程ID完全成了两个概念,linuxasid由一个32 位的长整数来表示,我们可以将它的低8位看作实际要填写到TLB表项里的asid号,而高24 位则用做分组号。每个进程在不同的CPU上都有一个asid变量用于标识该进程在不同CPU上的不同asid。这个asidlinux中使用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扩展后的asidvpfn代表虚拟页的页号,ppfn代表其映射到的实际物理页面的页号。当进程A首先被调度,tlb refill异常处理代码会读取kernel页表项,将生成的TLB 表项填充到CPU TLB里面去,因为TLB表项的asid只有8bits大小,所以生成的TLB表项的映射关系为:
(进程A asid & 0xff) vpfn ppfn = 0x001 0xff01 = 0x01 , 0 , 1
  
这时候再调度到进程B,因为CPU会用[ ( 进程B asid & 0xff ) + vpfn ] 去索引,既查找:
  
(进程B asid & 0xff) vpfn ppfn = 0x101 0xff01= 0x01 , 0 , 1 ]  
  
是否有效,这时候它发现TLB中已存在有效映射,但实际进程B0号虚拟页面对应的是2号物理页面,但CPU却取到了1号物理页面的内容,这时候就出错了。要解决这个问题,只需要在调度到进程B之前,将现有CPU内的TLB表项全部清空即可。
  
kernel
的实现方法就用到了前面提到的asid_cacheasid_cache实际上保存的是当前进程在CPU 上的asid,当准备切换进程上下文时,对比asid_cache与即将调度到的进程的asid,如果存在换组的情况,则说明可能会存在asid8位重叠,所以执行清空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);
  
我们用前面提到的进程AB来加深理解:
进程Aasid0x001,当执行进程A时,cpuasid_cache值为0x001,当要调度进程B,切换其上下文时,asid_cache ^ 进程Basid (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组切换,但组内的进程asid8位没有相同的情况,所以也
没必要清空TLB。解决了一个问题,还有个问题需要解决:
假设进程A和进程B一直被频繁调度,现在的代码会频繁的判断asid组是否改变,清空tlb项的操作就会频繁运行,大大的影响了系统运行的效率——我们只因为两个冲突的TLB项而频繁清空TLB32TLB表项我们只用到了其中一项,剩余的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的值始终为该cpuasid的最大值,这样就保证asid_cache+1不被别的进程asid占用,而实现了asid组迁移。
  
用进程AB来加深理解:
  
进程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 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值