bootmem_init()

memory memblock으로 부터 lowmem 영역의 경계를 파악하여 각각의 zone으로 경계를 나누어 설정하고 0번 노드의 빈 페이지들을 초기화한다. 또한 Sparse 메모리 모델을 지원하는 경우에는 sparse memory의 초기화도 수행한다.

bootmem_init-2a

 

bootmem_init()

arch/arm/mm/init.c

void __init bootmem_init(void)
{
        unsigned long min, max_low, max_high;

        memblock_allow_resize();
        max_low = max_high = 0;

        find_limits(&min, &max_low, &max_high);

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

        /*
         * sparse_init() needs the bootmem allocator up and running.
         */
        sparse_init();

        /*
         * Now free the memory - free_area_init_node needs
         * the sparse mem_map arrays initialized by sparse_init()
         * for memmap_init_zone(), otherwise all PFNs are invalid.
         */
        zone_sizes_init(min, max_low, max_high);

        /*
         * This doesn't seem to be used by the Linux memory manager any
         * more, but is used by ll_rw_block.  If we can get rid of it, we
         * also get rid of some of the stuff above as well.
         */
        min_low_pfn = min;
        max_low_pfn = max_low;
        max_pfn = max_high;
}
  • memblock_allow_resize();
    • memblock_can_resize = 1로 만들어 향후 memblock 관리 영역이 모자랄 때 2배 단위로 커질 수 있도록 한다.
  • find_limits(&min, &max_low, &max_high);
    • memblock 정보로 max_pfn, max_low_pfn, min_low_pfn 값을 얻어온다.
  • arm_memory_present();
    • Sparsemem의 경우 내부에서 mem_section[] 매핑을 위한 allocation이 수행되기 때문에 반드시 fixed reservation이 끝난 후에 이 함수가 호출되어야 한다.
    • 몇 개 32bit ARM 머신을 제외하고 대부분의 32bit ARM에서는 CONFIG_SPARSEMEM 옵션을 사용하지 않는다.
    • 참고: Sparse Memory | 문c
  • sparse_init();
    • Sparse memory 모델을 사용하는 시스템을 위해 관리 영역을 할당받고 매핑 초기화한다.
    • 참고: Sparse Memory | 문c
  • zone_sizes_init(min, max_low, max_high);
    • zone  영역을 나누고 초기화한다.

아래와 같이 memblock 설정 값을 읽어 lowmem 영역의 경계를 나눈다. 이 값은 zone을 나누는 경계로도 사용된다.

bootmem_init-1b

 

memblock_allow_resize()

mm/memblock.c

void __init memblock_allow_resize(void)
{
        memblock_can_resize = 1;
}
  • memblock_can_resize 플래그를 enable 시켜 향후 memblock 관리 영역이 모자랄 때 2배 단위로 커질 수 있도록 한다.

 

find_limits()

arch/arm/mm/init.c

static void __init find_limits(unsigned long *min, unsigned long *max_low,
                               unsigned long *max_high)
{
        *max_low = PFN_DOWN(memblock_get_current_limit());
        *min = PFN_UP(memblock_start_of_DRAM());
        *max_high = PFN_DOWN(memblock_end_of_DRAM());
}
  • memblock 정보를 읽어 다음을 설정한다.
  • max_low
    • lowmem/highmem 영역의 경계 pfn
  • min
    • 메모리 영역의 최하 pfn
  • max_high
    • 메모리 영역의 최상 pfn

 

zone_sizes_init()

arch/arm/mm/init.c

static void __init zone_sizes_init(unsigned long min, unsigned long max_low,
        unsigned long max_high)
{
        unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
        struct memblock_region *reg;

        /*  
         * initialise the zones.
         */
        memset(zone_size, 0, sizeof(zone_size));

        /*  
         * The memory size has already been determined.  If we need
         * to do anything fancy with the allocation of this memory
         * to the zones, now is the time to do it.
         */
        zone_size[0] = max_low - min;
#ifdef CONFIG_HIGHMEM
        zone_size[ZONE_HIGHMEM] = max_high - max_low;
#endif

        /*  
         * Calculate the size of the holes.
         *  holes = node_size - sum(bank_sizes)
         */
        memcpy(zhole_size, zone_size, sizeof(zhole_size));
        for_each_memblock(memory, reg) {
                unsigned long start = memblock_region_memory_base_pfn(reg);
                unsigned long end = memblock_region_memory_end_pfn(reg);

                if (start < max_low) {
                        unsigned long low_end = min(end, max_low);
                        zhole_size[0] -= low_end - start;
                }   
#ifdef CONFIG_HIGHMEM
                if (end > max_low) {
                        unsigned long high_start = max(start, max_low);
                        zhole_size[ZONE_HIGHMEM] -= end - high_start;
                }
#endif
        }

#ifdef CONFIG_ZONE_DMA
        /*  
         * Adjust the sizes according to any special requirements for
         * this machine type.
         */
        if (arm_dma_zone_size)
                arm_adjust_dma_zone(zone_size, zhole_size,
                        arm_dma_zone_size >> PAGE_SHIFT);
#endif

        free_area_init_node(0, zone_size, min, zhole_size);
}
  • zone_size[0] = max_low – min;
    • lowmem 영역 사이즈 설정
  • zone_size[ZONE_HIGHMEM] = max_high – max_low;
    • highmem이 동작하는 경우 highmem 영역 사이즈 설정
  • memcpy(zhole_size, zone_size, sizeof(zhole_size));
    • zone_size[]를 zhole_size[]에 복사
  • for_each_memblock(memory, reg) {
    • memory memblock을 루프를 돌며 하나씩 얻어온다.
  • if (start < max_low) {
    • memory memblock이 lowmem 영역에 들어있거나 일부 겹치는 경우
  • unsigned long low_end = min(end, max_low);
    • 끝 주소가 lowmem을 초과하지 않게 한다.
  • zhole_size[0] -= low_end – start;
    • hole 사이즈를 감소시킨다. 이 값이 0이되면 hole이 없는 것이다.
  • highmem 영역에 대해서도 위와 똑같은 방법으로 hole 사이즈를 감소시킨다.
  • if (arm_dma_zone_size)
    • DMA zone 사이즈가 존재하는 경우 dma zone 사이즈만큼 ZONE_DMA에 사이즈를 구성하고 ZONE_NORMAL 영역에서 그 사이즈만큼 뺀다.
  • free_area_init_node(0, zone_size, min, zhole_size);
    • 0번 노드에 zone_size[]와, zhole_size[] 정보를 사용하여 빈 페이지들을 초기화한다.
    • 참고: free_area_init_node() | 문c

아래 그림과 같이 highmem 영역을 사용하는 경우 ZONE_HIGHMEM에 대한 사이즈가 지정되고, 아키텍처가 dma 영역을 별도로 지정하여 사용하는 경우 ZONE_NORMAL에 대한 size를 dma 사이즈 만큼 감소시키고 ZONE_DMA에 대한 사이즈를 dma 사이즈로 지정한다.

  • rpi2: ZONE_NORMAL만 사용한다.

bootmem_init-3b

참고

댓글 남기기