免费注册 查看新帖 |

Chinaunix

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

[内存管理] flush_pmd_entry 的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-11-19 13:04 |只看该作者 |倒序浏览
arm linux 2.6.35

内核初始化内存管理的时候,初始化pgd的代码是这样的:

if (((addr | end | phys) & ~SECTION_MASK) == 0) {
                pmd_t *p = pmd;

                if (addr & SECTION_SIZE)
                        pmd++;

                do {
                        *pmd = __pmd(phys | type->prot_sect);
                        phys += SECTION_SIZE;
                } while (pmd++, addr += SECTION_SIZE, addr != end);

                flush_pmd_entry(p);
        }

SECTION_SIZE是1MB,而内核每次都初始化2MB的内存,就是说上面的do while循环会执行两次,会填写连续的两个pmd的内容,但是flush_pmd_entry(p)却在循环外,只运行了一次,看起来只写入了第一个pmd的数据,这是为什么?

论坛徽章:
0
2 [报告]
发表于 2012-11-19 15:39 |只看该作者
leslielg 发表于 2012-11-19 13:04
arm linux 2.6.35

   而内核每次都初始化2MB的内存,就是说上面的do while循环会执行两次,会填写连续的两个pmd的内容


从哪里看到是执行2次?

  1. 620         if (((addr | end | phys) & ~SECTION_MASK) == 0 && !force_pages) {
  2. 621                 pmd_t *p = pmd;
  3. 622
  4. 623 #ifndef CONFIG_ARM_LPAE
  5. 624                 if (addr & SECTION_SIZE)
  6. 625                         pmd++;
  7. 626 #endif
  8. 627
  9. 628                 do {
  10. 629                         *pmd = __pmd(phys | type->prot_sect);
  11. 630                         phys += SECTION_SIZE;
  12. 631                 } while (pmd++, addr += SECTION_SIZE, addr != end);
  13. 632
  14. 633                 flush_pmd_entry(p);
  15. 634         } else {
  16. 635                 /*
  17. 636                  * No need to loop; pte's aren't interested in the
  18. 637                  * individual L1 entries.
  19. 638                  */
  20. 639                 alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
  21. 640         }
复制代码

论坛徽章:
0
3 [报告]
发表于 2012-11-19 15:46 |只看该作者
回复 2# omycle


    while (pmd++, addr += SECTION_SIZE, addr != end); // end = addr + SECTION_SIZE * 2

论坛徽章:
0
4 [报告]
发表于 2012-11-20 10:00 |只看该作者
本帖最后由 omycle 于 2012-11-20 10:00 编辑

回复 3# leslielg


    恩。对。flush_pmd_entry,的目的是:“Clean data or unified cache line by MVA to PoC”.

在cache中,每个cache line是32字节。而一个pmd entry是4字节。因此,对于两个pmd(pmd 1, pmd2),刷pmd2,Pmd1 没必要重复刷新了。

这里,可能会问:pmd1和pmd2如果不在同一个cache line中,怎么办? 一个页表所占的页是4KB,因此,如果成对刷页表项的话,这个页表项对一定落在同一个cache line中。

  1. 463 static inline void flush_pmd_entry(void *pmd)
  2. 464 {
  3. 465         const unsigned int __tlb_flag = __cpu_tlb_flags;
  4. 466
  5. 467         tlb_op(TLB_DCLEAN, "c7, c10, 1  @ flush_pmd", pmd);
  6. 468         tlb_l2_op(TLB_L2CLEAN_FR, "c15, c9, 1  @ L2 flush_pmd", pmd);
  7. 469
  8. 470         if (tlb_flag(TLB_WB))
  9. 471                 dsb();
  10. 472 }
复制代码
以ARM V7为例467行做的事情是:“mcr p15,0,pmd,c7,c10,1”
468行是空操作。

论坛徽章:
0
5 [报告]
发表于 2012-11-20 13:13 |只看该作者
回复 4# omycle


    看到你的说法,flush这个操作是一次刷写8个页表项? MCR p15, 0, pmd, c7, c10, 1 这条语句会自动载入pmd + 1, pmd + 2...?

    另外根据代码
    if (addr & SECTION_SIZE)
              pmd++;

    pmd只能从偶数页表项开始映射,所以一定会在同一条cache line里

论坛徽章:
0
6 [报告]
发表于 2012-11-20 13:33 |只看该作者
回复 5# leslielg


    “Clean”所做的事情是将cache中的数据写到memory中。clean的最小单位是“cache line”。

在Kernel中,flush的意思是不明确的,需要根据具体指令来看,flush是clean 或者 invalidate 或者 clean And invalidate.

“invalidate”是标记某个cache line或者tlb entry为无效。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP