关于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将进程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的总结相关推荐
- Froyo(Android2.2)移植到Mips平台经验总结
经过以前已经移植Eclair(Android2.1)移植到MIPS平台的 经验(请参考我博客中的相关文章),移植Froyo相比移植Eclair简单多了.在此把移植Froyo的一些经验在本文做一个总结, ...
- mips平台下使用jiffies_to_msecs差值计算rtt不准确问题
我们业务模块实现了rtt计算机制,通过发送探测request时,使用jiffies_to_msecs(jiffies)记录下发送时间值.收到探测reply时,再使用jiffies_to_msecs(j ...
- 龙芯Mips平台vmlinux文件分析
龙芯Mips平台vmlinux文件分析 一.文件格式 1. 文件类型 2. 使用场景 二.结构内容描述 1. ELF header(ELF 头) 2. 字符串表表项 Entry 3. 读取字符串表 S ...
- 搭建MIPS平台GDB调试环境
概述: 目标平台:MIPS板子-broadcom 7214平台 主机: ubuntu10.10 192.168.99.1 目标:建立gdb-server gdb 在目标板上调试(1 ...
- minigui:mips平台交叉编译报错error: include location /usr/include/ is unsafe for cross-compilation
今天在对minigui做交叉编译,下面是编译的部分脚本 ./configure \--host=$host \--with-runmode=ths \--prefix=$_prefix \|| exi ...
- mips平台编译rtl8822cs驱动报错问题
1.平台: JZ4760+Linux 3.10 2.问题: 将rtl8822cs驱动编译进内核时,出现重复定义的错误,报错信息部分截图如下: 但在代码中,并没有发现有重复定义,内核其他地方也没有定义. ...
- linux-kernel编译过程——mips平台示例
一.选板级 1.扳级目录:arch/mips/configs (arch/平台/configs) 2.将选定的扳级复制到.config中cp halley5_v20_linux_sfc_nand_de ...
- ubuntu 12.04 交叉编译 arm/mips 平台的 strace
为了定位段错误问题,需要移植strace到嵌入式平台上.从git hub上拉取代码下来编译运行,实测可用 编译步骤包括如下几个步骤 1. 下载源代码 2. 编译 //下载源代码 1. mkdir st ...
- msm8996平台的一些debug方法
本文中基于一些基于msm8996平台的bsp和kernel调试的一些节点和方法,有些与高通平台相关,但大部分应该是linux通用的调试信息,未经仔细整理,只当随笔记录了.android版本是7.1,k ...
最新文章
- python中正确的输入语句x、y=input_语句x=input()执行时,如果从键盘输入12并按回车键,则x的值是( )。_学小易找答案...
- Matlab实用程序--图形应用-条形图形
- app.vue里使用data_Yeoman自定义Generator使用案例及Plop的使用
- Linux基础常用运维操作
- FreeRTOS 之五 动态内存管理(heap_1.c)详解
- php多线程 static变量,private static和public static的比较:多线程间
- 移动端html5广告的优势,h5手机端开发的优势都有哪些呢
- python实现找出1000以内的所有完美数
- Red Hat 6 安装 Yum源
- Atitit 发帖机实现(1)-----UsrQBm2008 页面上下文规范
- GCC vector 叠加示例
- 3dmax蒙皮详细教程
- 如何进入DOS系统 | 常用DOS系统命令
- 弹性公网IP ping不通?
- 任务管理器已被系统管理员停用的解决方法
- VScode+latex+Sumatra PDF环境配置(步步到位)
- Verilog仿真器
- 线下活动 | 聚焦分布式高可用的消息队列
- 超级计算机 人脑,迄今为止没有一部超级计算机的综合能力超过人脑
- java音乐播放器视频_java 实现音乐播放器的简单实例