memory memblock으로 부터 lowmem 영역의 경계를 파악하여 각각의 zone으로 경계를 나누어 설정하고 0번 노드의 빈 페이지들을 초기화한다. 또한 Sparse 메모리 모델을 지원하는 경우에는 sparse memory의 초기화도 수행한다.
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을 나누는 경계로도 사용된다.
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만 사용한다.
참고
- Memory Model -1- (Basic) | 문c
- Memory Model -2- (mem_map) | 문c
- Memory Model -3- (Sparse Memory) | 문c
- Memory Model -4- (APIs) | 문c
- ZONE 타입 | 문c
- bootmem_init() | 문c – 현재 글
- zone_size_init() | 문c
- NUMA -1- (ARM64 초기화) | 문c
- build_all_zonelists() | 문c