最近遇到一个非常有意思的问题,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的总结相关推荐

  1. Froyo(Android2.2)移植到Mips平台经验总结

    经过以前已经移植Eclair(Android2.1)移植到MIPS平台的 经验(请参考我博客中的相关文章),移植Froyo相比移植Eclair简单多了.在此把移植Froyo的一些经验在本文做一个总结, ...

  2. mips平台下使用jiffies_to_msecs差值计算rtt不准确问题

    我们业务模块实现了rtt计算机制,通过发送探测request时,使用jiffies_to_msecs(jiffies)记录下发送时间值.收到探测reply时,再使用jiffies_to_msecs(j ...

  3. 龙芯Mips平台vmlinux文件分析

    龙芯Mips平台vmlinux文件分析 一.文件格式 1. 文件类型 2. 使用场景 二.结构内容描述 1. ELF header(ELF 头) 2. 字符串表表项 Entry 3. 读取字符串表 S ...

  4. 搭建MIPS平台GDB调试环境

    概述:    目标平台:MIPS板子-broadcom 7214平台    主机: ubuntu10.10  192.168.99.1    目标:建立gdb-server gdb 在目标板上调试(1 ...

  5. minigui:mips平台交叉编译报错error: include location /usr/include/ is unsafe for cross-compilation

    今天在对minigui做交叉编译,下面是编译的部分脚本 ./configure \--host=$host \--with-runmode=ths \--prefix=$_prefix \|| exi ...

  6. mips平台编译rtl8822cs驱动报错问题

    1.平台: JZ4760+Linux 3.10 2.问题: 将rtl8822cs驱动编译进内核时,出现重复定义的错误,报错信息部分截图如下: 但在代码中,并没有发现有重复定义,内核其他地方也没有定义. ...

  7. linux-kernel编译过程——mips平台示例

    一.选板级 1.扳级目录:arch/mips/configs (arch/平台/configs) 2.将选定的扳级复制到.config中cp halley5_v20_linux_sfc_nand_de ...

  8. ubuntu 12.04 交叉编译 arm/mips 平台的 strace

    为了定位段错误问题,需要移植strace到嵌入式平台上.从git hub上拉取代码下来编译运行,实测可用 编译步骤包括如下几个步骤 1. 下载源代码 2. 编译 //下载源代码 1. mkdir st ...

  9. msm8996平台的一些debug方法

    本文中基于一些基于msm8996平台的bsp和kernel调试的一些节点和方法,有些与高通平台相关,但大部分应该是linux通用的调试信息,未经仔细整理,只当随笔记录了.android版本是7.1,k ...

最新文章

  1. python中正确的输入语句x、y=input_语句x=input()执行时,如果从键盘输入12并按回车键,则x的值是( )。_学小易找答案...
  2. Matlab实用程序--图形应用-条形图形
  3. app.vue里使用data_Yeoman自定义Generator使用案例及Plop的使用
  4. Linux基础常用运维操作
  5. FreeRTOS 之五 动态内存管理(heap_1.c)详解
  6. php多线程 static变量,private static和public static的比较:多线程间
  7. 移动端html5广告的优势,h5手机端开发的优势都有哪些呢
  8. python实现找出1000以内的所有完美数
  9. Red Hat 6 安装 Yum源
  10. Atitit  发帖机实现(1)-----UsrQBm2008 页面上下文规范
  11. GCC vector 叠加示例
  12. 3dmax蒙皮详细教程
  13. 如何进入DOS系统 | 常用DOS系统命令
  14. 弹性公网IP ping不通?
  15. 任务管理器已被系统管理员停用的解决方法
  16. VScode+latex+Sumatra PDF环境配置(步步到位)
  17. Verilog仿真器
  18. 线下活动 | 聚焦分布式高可用的消息队列
  19. 超级计算机 人脑,迄今为止没有一部超级计算机的综合能力超过人脑
  20. java音乐播放器视频_java 实现音乐播放器的简单实例

热门文章

  1. 知识图谱(三):Neo4j数据导入与多库切换
  2. 电气CAD(即ACE)将自建块导入系统元件库中,学习笔记
  3. HBase RegionServer宕机恢复
  4. Cray 推出开源大数据一体机 Urika-GX
  5. jlszyy(吉林省中医药管理局官网)
  6. Jmeter学习文档/使用
  7. MySql·设置字符集编码
  8. 用Java写贪吃蛇小游戏
  9. Bootstrap前端框架学习(一):Bootstrap在Vue项目中的安装及可视化布局
  10. MobaXterm常用使用功能设置