[커널 17차] 43주차

2021.06.27 01:57

ㅇㅇㅇ 조회 수:138

numa_init()
- init_fuc() 까지 진행
- node bitmask가 0이면 numa_free_distance()로 distance 정보 초기화하고 에러리턴
- numa_register_nodes() 함수로 numa node 등록을 수행한다. 등록 실패하면 numa_free_distance()로 distance 정보 초기화하고 에러리턴
- setup_node_to_cpumask_map() 함수로 nr_node_ids를 설정하고 cpu mask map 메모리를 할당, 초기화한다

 

__nodes_empty()
- 노드 bitmask가 empty인지 체크한다
- 함수 bitmap_empty() 사용

 

bitmap_empty()
- bitmap이 empty이면 true 아니면 false 리턴
- 함수 find_first_bit(), small_const_nbits() 사용

 

find_first_bit()
- first set bit의 index를 리턴하고 set bit이 없으면 size를 리턴한다

 

small_const_nbits()
- bitmap 길이가 long 보다 작은지 체크한다

 

BITMAP_FIRST_WORD_MASK
- 0xFFFF_0000 식으로 마스크

 

BITMAP_LAST_WORD_MASK
- 0x0000_FFFF 식으로 마스크

 

numa_free_distance()
- numa_distance 배열이 NULL이면 그냥 리턴
- numa_distance 물리주소를 memblock_free()하여 물리 메모리 해제

 

numa_register_nodes()
- memblock memory 중 node id가 NUMA_NO_NODE이거나 MAX_NUMNODES 이상인 memory가 있으면 에러 리턴
- for_each_node_mask() 매크로로 numa_nodes_parsed의 각 node id를 순회하면서 다음을 수행
- get_pfn_range_for_nid() 함수로 각 node id의 start pfn과 end pfn을 구함
- setup_node_data() 함수로 각 node id에 메모리를 추가 할당하고 node_data 전역 변수 배열에 관련 노드 정보를 저장한다
- node_data는 pglist_data 구조체 배열이고 pglist_data 구조체는 NUMA system의  메모리 layout을 표현한다
- node_set_online() 함수로 각 node id를 online으로 설정한다. 이는 node_states[N_ONLINE] bitmap을 set함으로써 이루어진다.
- node_possible_map bitmap을 numa_nodes_parsed bitmap과 동일하게 세팅한다

 

for_each_node_mask()
- find_first_bit(), _find_next_bit() 함수를 이용해서 next node를 iterate한다

 

_find_next_bit()
- 먼저 long 단위로 루핑하고 bit가 존재하는 long 변수를 찾으면 해당 long 변수에서 첫 번째 set bit 위치를 찾아서 next bit index를 계산, 리턴한다

 

get_pfn_range_for_nid()
- 각 memblock.memory를 루프를 돌면서 node id가 일치하는 memblock.meory region을 찾아서 해당 region의 start pfn와 end pfn를 리턴한다
- 매크로 __next_mem_pfn_range()를 이용해서 iteration 수행
- node id가 memblock별로 0/1/0/1/0/1이면 pfn가 겹치는 방식으로 리턴된다
- 예를 들어 memblock 하나가 한 개 page인 경우
- nid 0 : start pfn 0, end pfn 4
- nid 0 : start pfn 1, end pfn 5

 

__next_mem_pfn_range() 매크로
- start pfn과 end pfn가 같으면 다음 region으로 넘어가야 정확하게 계산된다

 

setup_node_data()
- 각 nid에 대해서 pglist_data size 만큼의 물리메모리를 memblock으로 할당받는다
- 할당받은 물리 메모리의 pfn에 대해 함수 early_pfn_to_nid()를 수행한다. 이 때 리턴된 tnid가 인자로 들어온 nid와 다르면 로그를 출력한다
- 할당받은 메모리의 가상주소를 node_data[nid]가 가리키도록 한다
- node_data[nid]를 memset해서 0으로 초기화한다
- node_data[nid]의 node_id, node_start_pfn, node_spanned_pages를 초기화한다
- 각각 nid, start_pfn, end_pfn - start_pfn으로 초기화 수행
- 따라서 node id가 다른 memblock이 서로 섞여 있으면 안되는 것으로 보임

 

early_pfn_to_nid()
- early_pfn_lock spinlock을 건다
- __early_pfn_to_nid() 함수로 pfn에 해당하는 nid를 구한다
- nid가 invalid하면 first_online_node를 리턴한다
- https://github.com/torvalds/linux/commit/e4568d3803852d00effd41dcdd489e726b998879
- early_pfn_lock spinlock을 푼다
- 구한 nid를 리턴한다

 

__early_pfn_to_nid()
- SPARSEMEM에서 사용되며, PFN에 해당하는 node id를 리턴한다
- mminit_pfnnid_cache 구조체를 인수로 받으며, 이는 마지막에 검색했던 pfn to nid의 데이터를 저장하는 캐시 구조체이다. last start, last end, last nid를 저장한다.
- 들어온 pfn이 last start와 last end 사이에 있으면 그냥 last nid를 리턴한다
- memblock_search_pfn_nid() 함수로 nid를 찾고 start pfn과 end pfn을 구한다
- nid가 NUMA_NO_NODE가 아니면 (유효한 node이면) 캐시 구조체에 검색 데이터를 저장한다 (start pfn, last pfn, nid)
- 찾은 nid를 리턴한다

 

memblock_search_pfn_nid()
- memblock_search() 함수로 pfn에 해당하는 물리주소를 가지는 memblock을 구한다
- 해당 memblock의 시작 주소, 마지막 주소를 가지고 start pfn, end pfn을 구한다
- 구한 start pfn, end pfn과 검색한 memblock의 node id를 리턴한다

 

memblock_search()
- 인자로 들어온 물리주소에 해당하는 memblock을 검색해서 리턴
- Binary search를 수행한다

 

first_online_node() 매크로
- first_node(node_states[N_ONLINE])
- online node bitmap 중 첫 번째 setbit을 리턴한다

 

early_page_uninitialised()
- 인자로 들어온 pfn에 해당하는 nid를 구하고, nid가 online이고, nid에 해당하는 first_deferred_pfn보다 크거나 같으면 true를 리턴
- 아니면 false를 리턴한다

 

node_set_online()
- node_states[N_ONLINE] bitmap에 nid bit을 set한다
- node_states[N_ONLINE] bitmap의 weight를 이용해서 nr_online_nodes 전역 변수에 online node의 수를 기록한다

 

setup_node_to_cpumask_map()
- nr_node_ids == MAX_NUMNODES이면 setup_nr_node_ids() 함수로 nr_node_ids를 setup
- node = 0 ~ nr_node_ids-1 에 대해서 루프를 돌면서 아래를 수행함
1) alloc_bootmem_cpumask_var() 함수로 cpumask_size() 만큼 memblock에서 메모리를 할당받고 이를 node_to_cpumask_map[node]가 가리키게 한다
2) cpumask_clear() 함수로 node_to_cpumask_map[node] bitmask를 clear한다

 

setup_nr_node_ids()
- node_possible_map bitmap의 마지막 setbit index를 구한다
- nr_node_ids를 위에서 구한 index + 1로 변경한다

 

alloc_bootmem_cpumask_var()
- 구조체 cpumask의 포인터 배열인 node_to_cpumask_map[MAX_NUMNODES]가 존재함
- memblock_alloc() 함수로 cpumask_size() 만큼 memblock에서 메모리를 할당받아서 mask 포인터가 기리키게 한다

 

cpumask_size()
- cpu bitmask 만큼의 사이즈를 리턴한다

 

dummy_numa_init()
- 모든 memblock.memory에 대해서 nid를 0으로 세팅하고 numa_nodes_parsed에 0번 nid를 set한다
- 이후 numa_off = true로 한다

 

======================================================

 

HugeTLBpage on ARM64
- https://www.kernel.org/doc/html/latest/arm64/hugetlbpage.html


현재 CONFIG_HUGETLB_PAGE & CONFIG_CMA 모두 켜져 있음

 

arm64_hugetlb_cma_reserve()
- order = PUD_SHIFT - PAGE_SHIFT를 계산하고 hugetlb_cma_reserve(order)를 호출
- 현재 세팅 기준으로 order = 18임

 

hugetlb_cma도 early_param이 존재한다
early_param("hugetlb_cma", cmdline_parse_hugetlb_cma);

 

cmdline_parse_hugetlb_cma()
- 전역변수 hugetlb_cma_size를 업데이트하고 종료

 

hugetlb_cma_reserve()
- 전역변수 hugetlb_cma_size가 setting 안되어 있으면 그냥 종료
- hugetlb_cma_size가 1GB 이상이 아니면 종료
- hugetlb_cma_size를 노드 개수(nr_online_nodes)로 나누고 round up해서 per_node 계산
- 즉 per_node는 node 당 담당해야 하는 hugetlb_cma_size 크기임
- online node의 nid에 대해서 루프를 돌면서 아래 수행
1) per_node랑 남은 크기 (처음엔 hugetlb_cma_size 전체 크기) 중 작은 것을 size로 설정
2) size를 1GB로 round up
3) size에 대해 cma_declare_contiguous_nid() 함수로 cma 메모리를 할당하고 cma 관리 자료구조를 설정한다
4) 할당한 size만큼 남은 크기에서 차감한다

 

======================================================

 

Sparse_mem 복습
- http://jake.dothome.co.kr/mm-1/
- http://jake.dothome.co.kr/sparsemem/
- SPARSEMEM_EXTREME 시나리오로 분석함

 

sparse_init()
- memblocks_present() 호출

 

memblocks_present()
- memblock을 돌면서 memory_present()를 호출
- memblock의 nid와 시작 주소의 pfn, 마지막 주소의 pfn를 memory_present()에 넘겨준다

 

memory_present()
- mem_section 구조체를 가리키는 2중 포인터 mem_section 전역 변수가 존재함
- mem_section 구조체는 unsigned long section_mem_map과 mem_section_usage 포인터 usage 멤버 변수로 구성됨 --> 16B 구조체임
- 물리주소 48bit 시스템 기준 첫 번째 배열은 1024개 존재함
- 두 번째 배열은 entry 256개이고 이는 한 페이지가 16B를 256개 담을 수 있기 때문 (4KB/16B = 256)
- 두 번째 배열 entry 1개가 메모리 section 1개, 즉 1GB를 표현한다
- 따라서 4KB 페이지 기준 두 번째 배열 entry 1개가 2^18 = 256K개의 페이지를 나타낸다

- 처음에 mem_sectio 더블 포인터가 NULL이므로 메모리 할당 수행
- mem_section 포인터 1024개 사이즈 만큼 memblock_alloc() 함수로 메모리 할당한다

- 넘겨받은 start pfn를 section 단위로 align한다 (30 - 12 = 18bit align 수행)
- start pfn과 end pfn이 최대 물리 메모리 주소를 넘어가는지 체크한다
- section 단위로 pfn에 대해 루프를 돌면서 아래를 수행한다 (2^18 만큼 step)
1) pfn으로 부터 section index를 계산한다
2) section index를 256으로 나누어 pfn에 해당하는 root = mem_section 1st level index를 계산한다
3) mem_section 구조체 256개에 해당하는 메모리를 할당받고 mem_section[root]가 가리키게 한다
4) 함수 set_section_nid()로 section id에서 nid를 구할 수 있는 전역변수 배열 section_to_node_table을 현재 section index와 nid를 이용해서 업데이트한다
5) __nr_to_section() 함수를 이용해서 section index에서 2nd level entry mem_section[][]를 구한다
6) 2nd level entry의 section_mem_map이 0이면 nid와 SECTION_IS_ONLINE 비트로 flag를 만들어 업데이트한다
7) 함수 section_mark_present()로 __highest_present_section_nr를 업데이트하고 section_mem_map에 SECTION_MARKED_PRESENT bit을 추가한다

 

- 2) ~3)은 함수 sparse_index_init()에서 수행

 

mminit_validate_memmodel_limits()
- start pfn, end pfn이 최대 물리 메모리 주소를 넘어가지는 체크

 

sparse_index_init()
- section index를 256으로 나누어 pfn에 해당하는 mem_section 1st level index = root를 계산한다
- 이미 mem_section[root]에 메모리가 할당되어 있으면 그냥 리턴한다 (두 번째 이상의 entry의 경우임)
- sparse_index_alloc() 함수로 mem_section 구조체 256개에 해당하는 메모리를 할당받고 mem_section[root]가 가리키게 한다

 

sparse_index_alloc()
- mem_section 구조체 256개에 해당하는 메모리를 할당한다
- 할당된 메모리 영역의 memblock에 인자 nid를 부여한다
- 할당된 메모리를 리턴

 

__nr_to_section()
- section index에서 2nd level entry mem_section[][]를 구해서 리턴한다

 

section_mark_present()
- 함수 __section_nr()으로 mem_section로부터 section index를 계산한다
- 지금까지 나온 section index를 가장 큰 값을 __highest_present_section_nr로 업데이트한다
- section_mem_map에 SECTION_MARKED_PRESENT bit을 추가한다

 

======================================================

 

of_platform_default_populate_init()
- 플랫폼 다바이스 등록하는 함수
- DT를 traverse하면서 device를 등록한다
- arch_init_call_sync로 등록되어 있다 --> initcall 섹션에 올라간다
- do_initcalls()에서 불린다
- do_basic_setup()에서 불림 (여기서 device_driver도 설정하는 듯)
- start_kernel()의 가장 마지막에 처리된다
- http://jake.dothome.co.kr/device-driver-3/

번호 제목 글쓴이 날짜 조회 수
공지 [공지] 스터디 정리 노트 공간입니다. woos 2016.05.14 617
82 [커널 17차] 49주차 ㅇㅇㅇ 2021.08.07 215
81 [커널 17차] 48주차 ㅇㅇㅇ 2021.08.01 166
80 [커널 18차] 9주차 Runixs 2021.07.29 256
79 [커널 17차] 47주차 ㅇㅇㅇ 2021.07.24 82
78 [커널 18차] 8주차 Runixs 2021.07.22 168
77 [커널 17차] 46주차 ㅇㅇㅇ 2021.07.18 60
76 [커널 18차] 7주차 Runixs 2021.07.12 129
75 [커널 17차] 45주차 ㅇㅇㅇ 2021.07.10 127
74 [커널 17차] 44주차 ㅇㅇㅇ 2021.07.04 76
73 [커널 18차] 6주차 V4bel 2021.07.03 75
» [커널 17차] 43주차 ㅇㅇㅇ 2021.06.27 138
71 [커널 18차] 5주차 V4bel 2021.06.26 102
70 [커널 17차] 42주차 ㅇㅇㅇ 2021.06.20 290
69 [커널 18차] 4주차 V4bel 2021.06.19 92
68 [커널 17차] 41주차 ㅇㅇㅇ 2021.06.13 36508
67 [커널 18차] 3주차 V4bel 2021.06.12 103
66 [커널 17차] 40주차 ㅇㅇㅇ 2021.06.05 280
65 [커널 18차] 2주차 V4bel 2021.06.05 269
64 [커널 17차] 39주차 ㅇㅇㅇ 2021.05.29 177
63 [커널 18차] 1주차 V4bel 2021.05.29 3596
XE Login