免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: platinum
打印 上一主题 下一主题

关于 vmalloc 的几点疑惑…… [复制链接]

论坛徽章:
0
21 [报告]
发表于 2009-05-19 00:35 |只看该作者
原帖由 eexplorer 于 2009-5-18 14:28 发表


ZONE_DMA 是16M以下的物理内存,只是给一些旧的设备,之能访问16M以内的物理内存。现在的pci设备的dma engine都能访问4G以内的内存。
所以分配dma able的内存不需要指定GFP_DMA,否则你可能很快就分配不出 ...



恩,补充的对。因为前面没有提级具体的硬件设备,没有说分配的 DMA 内存,是否为 ISA 设备使用。考虑到兼容性的问题,所以我说“可以使用”,而非“必须使用”。并指明了 GFP_DMA 标志是从 zonelist 的 ZONE_DMA 分配的内存,因为在这个区域中仅包含物理内存的 0-16M 前 16M。而这前 16M 正是给那些无法在整个 32 BIT 地址空间中进行 DMA 的设备提供的,如 ISA 这种只能访问物理内存前 16M 的设备。当然这里所说的 ZONE_DMA 的作用仅是指 x86 而言,因为在某些体系下 ZONE_DMA 本就为空。

论坛徽章:
0
22 [报告]
发表于 2009-05-19 09:35 |只看该作者
原帖由 raise_sail 于 2009-5-18 20:12 发表
嗯......

按照我的理解,从代码上看,如果kernel memory space只有一个 pgd 的话,也不会产生 vmalloc()相关的页错的。


会的。
一个物理页地址可以同时有N个虚拟地址. kmalloc分配的虚拟(CPU只能操作虚拟地址)地址是固定映射的。而vmalloc分配的虚拟地址不是固定映射范围的地址。需要缺页来建立。

论坛徽章:
0
23 [报告]
发表于 2009-05-19 14:32 |只看该作者

回复 #22 思一克 的帖子

vmalloc()时已经修改init_mm中的页表了呀,而进程页表的最后一个PGD与init_mm的最后一个PGD完全相同哦(我指32bit x86, 3 page levels),也就是说共享所有PMD。这种情况下,我理解,没有理由再有页错了呀,因为页表已经填好了。

论坛徽章:
0
24 [报告]
发表于 2009-05-19 15:15 |只看该作者
我做过实验, 在KERNEL 6.2.13中。
难道我记错了? 也有可能。

原帖由 raise_sail 于 2009-5-19 14:32 发表
vmalloc()时已经修改init_mm中的页表了呀,而进程页表的最后一个PGD与init_mm的最后一个PGD完全相同哦(我指32bit x86, 3 page levels),也就是说共享所有PMD。这种情况下,我理解,没有理由再有页错了呀,因为页 ...

[ 本帖最后由 思一克 于 2009-5-19 15:28 编辑 ]

论坛徽章:
0
25 [报告]
发表于 2009-05-19 15:18 |只看该作者


  1. /*
  2. * Handle a fault on the vmalloc or module mapping area
  3. *
  4. * This assumes no large pages in there.
  5. */
  6. static inline int vmalloc_fault(unsigned long address)
  7. {
  8.         unsigned long pgd_paddr;
  9.         pmd_t *pmd_k;
  10.         pte_t *pte_k;
  11.         /*
  12.          * Synchronize this task's top level page-table
  13.          * with the 'reference' page table.
  14.          *
  15.          * Do _not_ use "current" here. We might be inside
  16.          * an interrupt in the middle of a task switch..
  17.          */
  18.         pgd_paddr = read_cr3();
  19.         pmd_k = vmalloc_sync_one(__va(pgd_paddr), address);
  20.         if (!pmd_k)
  21.                 return -1;
  22.         pte_k = pte_offset_kernel(pmd_k, address);
  23.         if (!pte_present(*pte_k))
  24.                 return -1;
  25.         return 0;
  26. }



复制代码


do_page_fault() 调用这个vmalloc_fault。
所以,vmalloc应该是用do_page_fault的。

论坛徽章:
0
26 [报告]
发表于 2009-05-19 15:27 |只看该作者
这是KERNEL 6.2.13的do_page_fault. 我实验的就是这版本:



  1. ........

  2. do_sigbus:
  3.         up_read(&mm->mmap_sem);

  4.         /* Kernel mode? Handle exceptions or die */
  5.         if (!(error_code & 4))
  6.                 goto no_context;

  7.         /* User space => ok to do another page fault */
  8.         if (is_prefetch(regs, address, error_code))
  9.                 return;

  10.         tsk->thread.cr2 = address;
  11.         tsk->thread.error_code = error_code;
  12.         tsk->thread.trap_no = 14;
  13.         info.si_signo = SIGBUS;
  14.         info.si_errno = 0;
  15.         info.si_code = BUS_ADRERR;
  16.         info.si_addr = (void __user *)address;
  17.         force_sig_info(SIGBUS, &info, tsk);
  18.         return;


  19. vmalloc_fault:
  20.         {
  21.                 /*
  22.                  * Synchronize this task's top level page-table
  23.                  * with the 'reference' page table.
  24.                  *
  25.                  * Do _not_ use "tsk" here. We might be inside
  26.                  * an interrupt in the middle of a task switch..
  27.                  */
  28.                 int index = pgd_index(address);
  29.                 unsigned long pgd_paddr;
  30.                 pgd_t *pgd, *pgd_k;
  31.                 pud_t *pud, *pud_k;
  32.                 pmd_t *pmd, *pmd_k;
  33.                 pte_t *pte_k;

  34.                 //printk("vmalloc_fault %p ii %d\n", address, in_interrupt());
  35.                 vmalloc_fault_count++;

  36.                 asm("movl %%cr3,%0":"=r" (pgd_paddr));
  37.                 pgd = index + (pgd_t *)__va(pgd_paddr);
  38.                 pgd_k = init_mm.pgd + index;

  39.                 if (!pgd_present(*pgd_k))
  40.                         goto no_context;

  41.                 /*
  42.                  * set_pgd(pgd, *pgd_k); here would be useless on PAE
  43.                  * and redundant with the set_pmd() on non-PAE. As would
  44.                  * set_pud.
  45.                  */

  46.                 pud = pud_offset(pgd, address);
  47.                 pud_k = pud_offset(pgd_k, address);
  48.                 if (!pud_present(*pud_k))

  49. ..................
复制代码


明显告诉了, vmalloc是通过page_fault的。

论坛徽章:
0
27 [报告]
发表于 2009-05-19 15:43 |只看该作者

回复 #26 思一克 的帖子

嗯,好方法,回去我也在虚拟机里试试!

我看着pgd_alloc()里复制页表的,也有可能是你我考虑的内核配置不同,因为pgd_alloc()里复制内核空间页表是有条件的。

论坛徽章:
0
28 [报告]
发表于 2009-05-19 15:45 |只看该作者
原帖由 raise_sail 于 2009-5-19 15:43 发表
嗯,好方法,回去我也在虚拟机里试试!

我看着pgd_alloc()里复制页表的,也有可能是你我考虑的内核配置不同,因为pgd_alloc()里复制内核空间页表是有条件的。


你研究吧。我觉得vmalloc应该是用缺页的。
有结果了贴出。

论坛徽章:
0
29 [报告]
发表于 2009-05-19 21:20 |只看该作者
关于vmalloc后,用户进程的页表需要在缺页异常中向init_mm进行同步的问题,我一直以来也是很疑惑……
记得ulk3上面好像就是这么写的,是要在缺页异常中去同步的。

但是我觉得这样做好像有点问题,比如vmalloc将虚拟地址A映射到了物理地址a,之后由于一次缺页异常,这个映射被同步到了1号进程的页表中;再之后,vfree被调用,虚拟地址A到物理地址a的映射被撤销;再这后,vmalloc又被调用,将虚拟地址A映射到了物理地址b;再之后在使用1号进程页表的情况下(active_mm等于1号进程的mm),A是映射到a的,但是此时A却应该是映射到b的。由于此时虚拟地址A有映射,不会发生缺页异常,A到b的映射不会被更新……这样不是出问题了吗?

这个问题一直没想明白。
后来看了看pgd_alloc的实现,发现它是体系结构相关的。有些情况下,新的pgd是引用swapper_pg_dir的pmd(pud)的(大多数情况好像都是这样),这时应该不存在上面说到的同步问题;而有些情况下,新的pgd对swapper_pg_dir做了深拷贝(比如X86),pmd(pud)都是新的了,这时候就存在上面的同步问题。

更让我疑惑的是,为什么体系结构不同,会存在这样的实现差异呢?

[ 本帖最后由 kouu 于 2009-5-19 21:40 编辑 ]

论坛徽章:
0
30 [报告]
发表于 2009-05-19 22:37 |只看该作者
我看的是2.6.29.1,其中x86体系结构下,pgd_ctor()部分代码如下.

如果不是共享页表项的话,就调用pgd_list_add(),它会把所有这种pgd加到一个链表里,然后vmalloc()会处理同步所有pgd的工作。

static void pgd_ctor(pgd_t *pgd)
{
    /* If the pgd points to a shared pagetable level (either the
       ptes in non-PAE, or shared PMD in PAE), then just copy the
       references from swapper_pg_dir. */

    if (PAGETABLE_LEVELS == 2 ||
        (PAGETABLE_LEVELS == 3 && SHARED_KERNEL_PMD) ||
        PAGETABLE_LEVELS == 4) {
        clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY,
                swapper_pg_dir + KERNEL_PGD_BOUNDARY,
                KERNEL_PGD_PTRS);
    }
    /* list required to sync kernel mapping updates */
    if (!SHARED_KERNEL_PMD)
        pgd_list_add(pgd);
}
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP