Linuxias
Developer's Delight
Linuxias
  • Category
    • AI
      • Deep Learning
      • Machine Learning
      • Data Science
      • Framework
      • MLOps
      • Paper-Review
      • Tips
    • Android
      • Kotlin
      • Component
      • Compose
      • Compose UI
      • Material
      • Testing
    • Software Architecture
      • Architecture Pattern
      • Design Pattern
      • Requirement Engineering
    • Linux
      • Compile & Link
      • Command & Tool
      • Container
      • Debugging & Testing
      • Profiling
      • Kernel Analysis
      • Server
      • Shell Script
      • System Programming
    • Language
      • Carbon
      • C,C++
      • C#
      • Java
      • Python
    • ETC
      • Data Struct | Algorithm
      • git
      • Security
    • Book
    • 경제공부
      • 세금
      • 부동산
hELLO · Designed By 정상우.
Linuxias

Developer's Delight

Linux/Kernel Analysis

bootmem_init() 부트 메모리 초기화

2018. 10. 6. 17:31
반응형

[분석 기준] 

 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();
}
 
Colored by Color Scripter
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);
}
 
Colored by Color Scripter
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;
}
 
Colored by Color Scripter
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));
    }
}
 
Colored by Color Scripter
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);
        }
    }
}
 
Colored by Color Scripter
cs

line 6 -14
CONFIG_SPARSEMEM_EXTREME은 ARM64에서 Default로 설정되어 있습니다. mem_section은 부트업 타임에 아직 초기화 되지 않았으므로 해당 조건문이 수행되고 memblock_virt_alloc에 의해 메모리가 할당됩니다.

line 18 - 31
각 섹션에 node id (UMA 에선 dummy로 초기화 하여 전체 node id가 0)를 설정해줍니다.


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);
}
 
Colored by Color Scripter
cs


line 17
전체 메모리에 대해 페이지 블록 크기 단위로 나누기 위한 pageblock_order를 설정합니다.

line 30
usemap_map에 사용할 사이즈를 계산합니다. MEM_SECTIONS의 개수 * 포인터 사이즈를 usemap_map의 사이즈로 사용합니다.
.....

[참고자료]

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
    'Linux/Kernel Analysis' 카테고리의 다른 글
    • zone_size_init
    • 메모리 모델 (FLATMEM, DISCONTIGMEM, SPARSEMEM)
    • void __init paging_init() - 커널 페이지 초기화 하기
    • [커널분석] arm64_memblock_init()
    Linuxias
    Linuxias
    I want to be a S/W developer who benefits people.

    티스토리툴바