[분석 기준]
kernel version : linux kernel 4.16
architecture : aarch64 (arm64)
bootmem_init() 함수는 함수네임 그대로 부트 메모리 초기화를 위한 함수입니다. 커널은 ZONE이라는 개념을 도입해 메모리를 관리하게 됩니다. bootmem_init() 함수에서 어떻게 부트 메모리를 초기화 하는지 분석해 보도록 하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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(); } | cs |
min, max 변수는 memblock 주소의 시작과 끝을 가진 변수입니다.
line 8 :
memtest=count 파라미터를 이용하여 해당 count 만큼 메모리 패턴 테스트를 지정합니다. memtest는 dt에 설정된 값을 ealry_param() 을 이용해 가져오게 됩니다.
line 12:
numa 시스템을 사용하는 경우 초기화를 진행합니다.
line 17:
sparse memory model인 경우(ARM64인 경우 sparse가 default) memblock에 등록된 모든 메모리타입에 대해 mem_section을 초기화 합니다. 그 후 초기화된 mem_section 에 전체 메모리의 node id를 설정하여 줍니다.
line 18:
sparse memory 모델에서 사용하는 mem_map을 초기화합니다.
arm64_numa_init()
numa 시스템을 초기화하기 위한 함수입니다.
1 2 3 4 5 6 7 8 9 10 11 12 | void __init arm64_numa_init(void) { if (!numa_off) { if (!acpi_disabled && !numa_init(arm64_acpi_numa_init)) return; if (acpi_disabled && !numa_init(of_numa_init)) return; } numa_init(dummy_numa_init); } | cs |
line 10
numa_init() 함수는 파라미터로 함수포인터를 전달받습니다. 전달된 함수는 dummy_numa_init() 함수이며, 해당 함수는 아래와 같습니다. SMP 시스템이라고 생각하고 분석을 했을 때 간단히 dummy로 numa를 초기화 하게 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static int __init dummy_numa_init(void) { int ret; struct memblock_region *mblk; if (numa_off) pr_info("NUMA disabled\n"); /* Forced off on command line. */ pr_info("Faking a node at [mem %#018Lx-%#018Lx]\n", 0LLU, PFN_PHYS(max_pfn) - 1); for_each_memblock(memory, mblk) { ret = numa_add_memblk(0, mblk->base, mblk->base + mblk->size); if (!ret) continue; pr_err("NUMA init failed\n"); return ret; } numa_off = true; return 0; } | cs |
line 11 - 18
memblock의 모든 memory region을 interator하며 모든 nid(node id)를 0으로 설정하게 됩니다.
arm64_memory_present()
SPARSEMEM 메모리 모델인 경우에는 memblock에 등록된 모든 memory 타입 블록에 대해 섹션별로 메모리가 존재하는 곳의 mem_section을 초기화합니다.
1 2 3 4 5 6 7 8 9 10 11 12 | static void __init arm64_memory_present(void) { struct memblock_region *reg; for_each_memblock(memory, reg) { int nid = memblock_get_region_node(reg); memory_present(nid, memblock_region_memory_base_pfn(reg), memblock_region_memory_end_pfn(reg)); } } | cs |
line 5 - 10
foreach로 모든 memory 타입의 region에 대해 memblock_get_region_node()를 통해 nid를 구해오고, memory_present()함수를 통해 mem_section을 초기화합니다.
memory_present() - mm/sparse.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /* Record a memory area against a node. */ 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; 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); } } } | cs |
sparse_init() - mm/sparce.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | void __init sparse_init(void) { unsigned long pnum; struct page *map; unsigned long *usemap; unsigned long **usemap_map; int size; #ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER int size2; struct page **map_map; #endif /* see include/linux/mmzone.h 'struct mem_section' definition */ BUILD_BUG_ON(!is_power_of_2(sizeof(struct mem_section))); /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */ set_pageblock_order(); /* * map is using big page (aka 2M in x86 64 bit) * usemap is less one page (aka 24 bytes) * so alloc 2M (with 2M align) and 24 bytes in turn will * make next 2M slip to one more 2M later. * then in big system, the memory will have a lot of holes... * here try to allocate 2M pages continuously. * * powerpc need to call sparse_init_one_section right after each * sparse_early_mem_map_alloc, so allocate usemap_map at first. */ size = sizeof(unsigned long *) * NR_MEM_SECTIONS; usemap_map = memblock_virt_alloc(size, 0); if (!usemap_map) panic("can not allocate usemap_map\n"); alloc_usemap_and_memmap(sparse_early_usemaps_alloc_node, (void *)usemap_map); #ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER size2 = sizeof(struct page *) * NR_MEM_SECTIONS; map_map = memblock_virt_alloc(size2, 0); if (!map_map) panic("can not allocate map_map\n"); alloc_usemap_and_memmap(sparse_early_mem_maps_alloc_node, (void *)map_map); #endif for_each_present_section_nr(0, pnum) { usemap = usemap_map[pnum]; if (!usemap) continue; #ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER map = map_map[pnum]; #else map = sparse_early_mem_map_alloc(pnum); #endif if (!map) continue; sparse_init_one_section(__nr_to_section(pnum), pnum, map, usemap); } vmemmap_populate_print_last(); #ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER memblock_free_early(__pa(map_map), size2); #endif memblock_free_early(__pa(usemap_map), size); } | cs |
[참고자료]
http://jake.dothome.co.kr/
도서 : 코드로 알아보는 ARM 리눅스 커널
'Linux > Kernel Analysis' 카테고리의 다른 글
zone_size_init (0) | 2018.11.03 |
---|---|
메모리 모델 (FLATMEM, DISCONTIGMEM, SPARSEMEM) (0) | 2018.10.06 |
void __init paging_init() - 커널 페이지 초기화 하기 (2) | 2018.09.29 |
[커널분석] arm64_memblock_init() (1) | 2018.09.15 |
[ARMv8] aarch64 프로세서 상태 레지스터(PSTATE) (0) | 2018.09.08 |