免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3181 | 回复: 3
打印 上一主题 下一主题

[内存管理] early_pte_alloc函数的疑问 [复制链接]

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-04-14 20:48 |只看该作者 |倒序浏览
本帖最后由 arm-linux-gcc 于 2013-04-14 20:52 编辑

3.4的内核

  1. static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
  2. {
  3.         if (pmd_none(*pmd)) {
  4.                 pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
  5.                 __pmd_populate(pmd, __pa(pte), prot);
  6.         }
  7.         BUG_ON(pmd_bad(*pmd));
  8.         return pte_offset_kernel(pmd, addr);
  9. }
复制代码
对于这个函数我的理解如下:
当内核启动时,在做map_desc[]数组中的设备固定映射时,一般来说需要用到二级页表,于是就会调用到这个函数进行二级页表分配
在arm里面,一级页表中一个项对应1M的映射关系,然后这个项指向了二级页表,而二级页表中每一个项是对应了一个page(4k),于是二级页表中的项数就是(1M/4K)
而一个项是4字节,于是一份二级页表就是1kB

而这里的early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);分配了4k,我觉得这里是有浪费的

并且__pmd_populate函数中

  1. static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,
  2.                                   pmdval_t prot)
  3. {
  4.         pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot;
  5.         pmdp[0] = __pmd(pmdval);
  6. #ifndef CONFIG_ARM_LPAE
  7.         pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
  8. #endif
  9.         flush_pmd_entry(pmdp);
  10. }
复制代码
为了减少浪费,所以有pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));,这句是把后面那个1M的二级页表也给准备好了
于是这里实际上是准备了两份二级页表(各1k),但是仍然还是浪费了2k

疑问1,
为什么这里不把后续的两个1M全都映射了呢,这样就不会浪费了

疑问2,
如果map_desc[]数组中的写死的那些虚拟地址是按照从小到大排列的,那么当跨越1M的时候,不会发生问题,因为会此时后面那个1M会判断到没有二级页表,于是会调用early_pte_alloc去分配
但是如果map_desc[]数组中的写死的那些虚拟地址是按照从大到小排列的,那么当跨越1M的时候,低地址的那个1M判断到没有二级页表,于是也会去调用early_pte_alloc去分配,但是问题就来了,因为分配之后是写了两个1M的一级页表项的(指向的二级页表),于是pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));这句就会把相邻的高地址的1M的一级页表项给覆盖了,但是此时那个高地址1M的一级页表项已经有内容了(之前做高地址1M的映射时分配的)。这样的话map_desc[]数组中的虚拟地址必须从小到大了,感觉这是个bug啊,是不是我哪里看漏了?




论坛徽章:
0
2 [报告]
发表于 2013-04-14 21:31 |只看该作者
回复 1# arm-linux-gcc
疑问1,
为什么这里不把后续的两个1M全都映射了呢,这样就不会浪费了

arm下面会使用两份页表来保存页表内容,原因是arm的页表格式定义与LINUX的标准定义相差颇大,为了方便管理,在使用linux标准接口设置页表属性时,会将其页表项值保存在一份“Linux页表”备份上,之后调用架构相关的页表项填写函数时会从这份linux页表里读取值,转换成相应的硬件标志位。供硬件解析的页表位置存放在MMU解析得到的地址,linux页表备份则放在物理页表加2k偏移的位置,之所以这样设置的原因是为了不浪费空间。因为arm二级页表的大小为2^8 * 4 = 1K,再加一份linux拷贝也才2k,会导致大量的页内碎片;所以将两个连续的一级页表项映射到连续的一段空间,这样就能保证在一个两级页表的存放刚好占满一个页了。arm原来的页表解析是12+8+12,这样修改后的结果可以看做是,arm的页表解析变为了11+9+12,一级页表项中两项被合为一项了。
下面是arm下的二级页表存放格式,在arch/arm/include/asm/pgtable.h里有详细介绍
*
*    pgd             pte
* |        |
* +--------+ +0
* |        |-----> +------------+ +0
* +- - - - + +4    |  h/w pt 0  |
* |        |-----> +------------+ +1024
* +--------+ +8    |  h/w pt 1  |
* |        |       +------------+ +2048
* +- - - - +       | Linux pt 0 |
* |        |       +------------+ +3072
* +--------+       | Linux pt 1 |
* |        |       +------------+ +4096
这个是arm v7架构的页表属性设置函数:
  1. ENTRY(cpu_v7_set_pte_ext)
  2. #ifdef CONFIG_MMU
  3. ARM(        str        r1, [r0], #-2048        )        @ linux version [b][color=Red] /*取偏移2k的位置的linux页表备份*/[/color][/b]
  4. THUMB(        str        r1, [r0]                )        @ linux version
  5. THUMB(        sub        r0, r0, #2048                )

  6.         bic        r3, r1, #0x000003f0
  7.         bic        r3, r3, #PTE_TYPE_MASK
  8.         orr        r3, r3, r2
  9.         orr        r3, r3, #PTE_EXT_AP0 | 2

  10.         tst        r1, #1 << 4
  11.         orrne        r3, r3, #PTE_EXT_TEX(1)

  12.         tst        r1, #L_PTE_WRITE
  13.         tstne        r1, #L_PTE_DIRTY
  14.         orreq        r3, r3, #PTE_EXT_APX

  15.         tst        r1, #L_PTE_USER
  16.         orrne        r3, r3, #PTE_EXT_AP1
  17.         tstne        r3, #PTE_EXT_APX
  18.         bicne        r3, r3, #PTE_EXT_APX | PTE_EXT_AP0

  19.         tst        r1, #L_PTE_EXEC
  20.         orreq        r3, r3, #PTE_EXT_XN

  21.         tst        r1, #L_PTE_YOUNG
  22.         tstne        r1, #L_PTE_PRESENT
  23.         moveq        r3, #0

  24.         str        r3, [r0]
  25.         mcr        p15, 0, r0, c7, c10, 1                @ flush_pte
  26. #endif
  27.         mov        pc, lr
  28. ENDPROC(cpu_v7_set_pte_ext)
复制代码
疑问2,
如果map_desc[]数组中的写死的那些虚拟地址是按照从小到大排列的,那么当跨越1M的时候,不会发生问题,因为会此时后面那个1M会判断到没有二级页表,于是会调用early_pte_alloc去分配
但是如果map_desc[]数组中的写死的那些虚拟地址是按照从大到小排列的,那么当跨越1M的时候,低地址的那个1M判断到没有二级页表,于是也会去调用early_pte_alloc去分配,但是问题就来了,因为分配之后是写了两个1M的一级页表项的(指向的二级页表),于是pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));这句就会把相邻的高地址的1M的一级页表项给覆盖了,但是此时那个高地址1M的一级页表项已经有内容了(之前做高地址1M的映射时分配的)。这样的话map_desc[]数组中的虚拟地址必须从小到大了,感觉这是个bug啊,是不是我哪里看漏了?


   

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
3 [报告]
发表于 2013-04-14 22:03 |只看该作者
本帖最后由 arm-linux-gcc 于 2013-04-14 22:03 编辑

第二个问题我搞清楚了,__pmd_populate的参数pmdp是按照2M对齐的,所以创建二级页表都是从偶数section开始的,所以刚好错开

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
4 [报告]
发表于 2013-04-14 22:05 |只看该作者
非常感谢
:wink:

回复 2# junnyg


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP