关于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
将进程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 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机浏览器支付页面弹不出来怎么办 新手机登录微信需要验证怎么办 手机淘宝注册账号换手机号后怎么办 冒充微信好友诈骗被骗了怎么办 刚刚激活的电信手机号码忘了怎么办 用手机充话费充错了怎么办 王者荣耀号丢了换成了别的号怎么办 微信给空号q币怎么办 qq密码被别人改了怎么办怎么登 qq刚改了密码忘了怎么办 qq密码忘了想改密码怎么办 qq账号和密码都忘了怎么办 我的微信被别人改了密码怎么办 微信被盗密码被改了怎么办 微信被盗了密码都被改了怎么办 微信被别人改密码改手机号了怎么办 微信号手机号码换了密码改了怎么办 电信充值卡的密码刮花了怎么办 qq充了q币不到账怎么办 开通黄钻不支持手机话费开通怎么办 银行卡给红包充值迟迟不到账怎么办 我想用信用卡往微信零钱充钱怎么办 修改密保手机跳过原手机验证怎么办 骏卡有用但是充不进去怎么办 在云南移动上充错话费了怎么办 手机版本太低安装不了支付宝怎么办 手机下载支付宝后找不到了怎么办 王者荣耀充值成功游戏未到账怎么办 银行卡号和密码都忘了怎么办 微信支付密码和银行卡号忘了怎么办 请问5位数的密码忘记了怎么办 苹果手机四位数密码忘记了怎么办 锡山教育app登陆密码忘了怎么办 新商盟手机订烟登录忘记密码怎么办 在qq上修改支付密码忘记怎么办 微信红包支付密码忘了怎么办 qq钱包绑定了别人的银行卡怎么办 用支付宝充手机话费怎么退款怎么办 我有个qq号丢了怎么办 微信红包密码输错了怎么办 qq钱包支付限额付不了款怎么办