vmemmap VA与struct page阵列页表映射

本文原创,谢绝转载!

vmemmap VA与struct page阵列页表映射

 

一个struct mem_section对应PAGES_PER_SECTION个page,比如PAGES_PER_SECTION的值为18,即对应256KB个page,256K个struct page所占用的空间为256K * 64 = 16MB;如果page size为4KB,则一个mem_section对应256K*4KB = 1GB内存

 

vmemmap区虚拟地址与物理地址的页表映射

1.arm64_memory_present()

遍历所有的memory region,初始化struct mem_section数组,这些mem_section以二维数组的方式组织起来,二维数组的一行元素数量为SECTIONS_PER_ROOT,这个二维数组名字是struct mem_section **mem_section。

初始化每个struct mem_section,将这些struct的section_mem_map成员或上一个SECTION_MARKED_PRESENT flag。

2.sparse_init()

根据1中初始化的mem_section,如果这个mem_section有SECTION_MARKED_PRESENT flag(present),则为这个mem_section进行虚拟地址到物理地址的页表映射,虚拟地址的起始地址为vmemmap(它为struct page *类型指针),物理地址为存放struct page的内存区域,这个内存区域由sparse_buffer_init()来分配,它所分配的内存的物理地址会大于等于__pa(MAX_DMA_ADDRESS),MAX_DMA_ADDRESS等于PAGE_OFFSET,PAGE_OFFSET为linear mapping的起始地址,所以这里分配内存是在线性映射区里分配的。这个页表映射的工作在sparse_mem_map_populate()里进行。这个页表映射是以PMD_SIZE为单位进行的,所以不会进行PTE level的映射,所以struct page的虚拟地址(在vmemmap区)对应的PA是PMD entry base PA + VA的(PTE&page) offset。以4KB page size为例,PMD_SIZE为2MB,所以如果一个mem_section对应16M的struct page阵列,需要进行16M/2M = 8次的PMD entry映射。

如果为nid为-1(即只有一个node),在sparse_init()里for_each_present_section_nr将计算map_count,即计算所有是present状态的mem_section的总和,最后将不满足循环条件从而break循环,此时pnum_end为-1。在这个循环里因为nid均为-1,所以均相等,所以不会call sparse_init_nid()。在这个循环之后将会call sparse_init_nid(),此时的map_count为唯一的一个node里对应的present mem_section数量,在sparse_init_nid()里调用sparse_buffer_init()去分配map_count * section_map_size()大小的内存,section_map_size()即一个mem_section所对应的page数量,此时分配的内存因为虚拟地址是连续的,又因为是在linear mapping区,所以对应PA也是连续的。

所以sparse_init_nid()是为一个memory node的内存大小分配用来存储对应数量的struct page空间,并建立vmemmap虚拟地址到这个struct page阵列物理地址的页表映射。

如果系统中有多个memory node,则所分配的struct page阵列之间物理地址可以不连续,但是一个node的struct page阵列物理地址是连续的,这也许就是sparse memory(稀疏内存)的含义吧

 

note:

1. struct page阵列(PA)页表映射的VA是vmemmap区而非struct page阵列本身所在的虚拟地址linear mapping区。用__pa(struct page*)获取struct page的物理地址将不会得到正确的PA,如果想获取struct page的PA,可以使用查kernel page table的方式,查到PMD level止。

2. sparse_buffer_init()是通过memblock调用memblock_reserve() reserve一块memory region

 

疑问:存在一个mem_section里实际只有对应几个struct page的情况,此时还是会分配一个mem_section完整数量的struct page空间,而这个mem_section也是完整地被reserve的,所以这看起来存在内存浪费?

 

相关函数

 

void __init bootmem_init(void)
{
    unsigned long min, max;

    min = PFN_UP(memblock_start_of_DRAM());
    max = PFN_DOWN(memblock_end_of_DRAM());

    early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT);

    max_pfn = max_low_pfn = max;

    arm64_numa_init();
    /*
     * Sparsemem tries to allocate bootmem in memory_present(), so must be
     * done after the fixed reservations.
     */
    arm64_memory_present();

    sparse_init();
    zone_sizes_init(min, max);

    memblock_dump_all();
}

 

 

void __init memory_present(int nid, unsigned long start, unsigned long end)
{
    unsigned long pfn;

#ifdef CONFIG_SPARSEMEM_EXTREME
    if (unlikely(!mem_section)) {
        unsigned long size, align;

        size = sizeof(struct mem_section*) * NR_SECTION_ROOTS;
        align = 1 << (INTERNODE_CACHE_SHIFT);
        mem_section = memblock_virt_alloc(size, align);
    }
#endif

    start &= PAGE_SECTION_MASK; //对齐到PAGE_SECTION_MASK,这将使start可能变小
    mminit_validate_memmodel_limits(&start, &end);
    for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) {
        unsigned long section = pfn_to_section_nr(pfn);
        struct mem_section *ms;

        sparse_index_init(section, nid);
        set_section_nid(section, nid);

        ms = __nr_to_section(section);
        if (!ms->section_mem_map) {
            ms->section_mem_map = sparse_encode_early_nid(nid) |
                            SECTION_IS_ONLINE;
            section_mark_present(ms);
        }
    }
}

 

posted @ 2022-02-23 17:12  aspirs  阅读(520)  评论(0编辑  收藏  举报