[커널 17차] 46주차

2021.07.18 00:13

ㅇㅇㅇ 조회 수:60

free_area_init() 계속
- find_zone_movable_pfns_for_nodes() 부터 시작
- 각 zone 별로 이름, start pfn과 end pfn을 출력한다
- 각 node 별로 노드 id와 movable 영역 시작 pfn을 출력한다
- 각 노드의 memblock별로 노드 id, 시작 pfn, 종료 pfn을 출력하고 각 memblock의 시작 pfn부터 마지막 pfn까지 영역에 대해 subsection_map_init()을 수행하여 subsection bitmask를 set한다
- mminit_verify_pageflags_layout()을 수행하여 page 구조체의 pageflags의 layout을 커널 로그로 출력한다
- setup_nr_node_ids()를 수행하여 nr_node_ids 전역변수를 possible node의 최대 index + 1로 설정한다
- init_unavailable_mem()를 수행 --> 여기까지 진행

 

find_zone_movable_pfns_for_nodes()
- 각 노드 별로 movable 영역이 시작되는 pfn을 계산한다
- restart: 부분부터 진행
- required_kernelcore (kernelcore 영역으로 할당, 즉 non-movable로 설정해야 하는 pfn 수)를 usable_nodes로 나누어서 각 노드별로 할당 받아야 하는 kernel pfn 수를 계산한다 (kernelcore_node 값)
- 물리 메모리가 존재하는 노드 별로 loop를 돌면서 다음을 수행한다


1) kernelcore_remaining에 현재 노드에 할당해야 하는 커널 페이지를 입력

2) 현재 노드의 각 memblock 별로 루프를 돌면서 아래를 수행한다


-- zone_movable_pfn[nid] 이하의 page들은 이미 커널에 할당된 것이므로 패스한다
-- usable_startpfn보다 낮은 pfn들은 무조건 커널에 할당하고 그 사이즈만큼 kernelcore_remaining 및 required_kernelcore에서 차감한다
-- 해당 memblock의 end_pfn이 usable_startpfn보다 낮거나 같으면 memblock이 통째로 커널에 할당되었으므로 zone_movable_pfn[nid]를 현재 memblock의 end_pfn로 업데이트하고 다음 memblock으로 넘어간다
-- 아니면 usable_startpfn부터 시작해서 memblock에 남은 메모리 영역을 남은 커널 할당량만큼 커널에 할당해야 한다
-- 또는 usable_startpfn이 memblock의 start_pfn보다 낮으면 무조건 커널에 할당해야 하는 페이지는 없으므로 할당된 커널 사이즈만큼을 현재 memblock에서 할당하면 된다
-- 남은 memblock 영역이 남은 커널 할당량보다 크면 남은 커널 할당량을 이번 memblock에 모두 할당하고 zone_movable_pfn[nid]를 usable_startpfn/start_pfn부터 해당 사이즈만큼 올려서 업데이트한다
-- 남은 memblock 영역이 남은 커널 할당량보다 작으면 남은 memblock 영역을 모두 커널에 할당하고 그 사이즈만큼 usable_startpfn/start_pfn부터 올려서 zone_movable_pfn[nid]를 업데이트한다
-- 커널에 할당된 만큼 required_kernelcore/kernelcore_remaining을 차감한다
-- 남은 커널 할당량이 0이면 memblock 루프를 break한다

 

3) 노드 별 할당해야 하는 커널 사이즈를 남은 커널 할당량을 이용해서 재계산한다

 

- usable_nodes를 1 감소시킨다
- usable_nodes가 0보다 크고 required_kernelcore > usable_nodes이면 restart로 다시 점프해서 남은 커널 할당량은 재분배한다
- 여기서 남은 커널 할당량 페이지 수가 남은 노드 수보다 같거나 작으면 남은 커널 할당량을 굳이 할당하지 않고 무시하고 넘어간다

- out2 : 각 노드에 대해 zone_movable_pfn[nid]를 1024로 roundup 수행한다
- out : node_states[N_MEMORY]를 saved_node_state로 복원한다


subsection_map_init()
- 시작 pfn과 페이지 수를 인수로 받아서 subsection bitmap을 업데이트하는 함수

- 시작 pfn과 페이지수를 인수로 받아서 시작 section 및 끝 section 번호를 구한다
- page 수가 0이면 그냥 리턴
- 시작 section 번호부터 마지막 section 번호까지 루프를 돌면서 아래를 수행한다
1) 시작 pfn과 남은 페이지 수(nr_pages)를 이용해서 현재 section에서 처리해야 할 page 수를 계산한다 (pfns)
2) 현재 section에 해당하는 mem_section 2nd level 구조체를 가져온다 (ms)
3) 함수 subsection_mask_set()로 ms->usage->subsection_map을 시작 pfn부터 page 개수 만큼의 subsection에 대해서 1로 setting한다. 즉 mem_section 자료 구조에 어떤 subsection이 존재하는지 계산해서 bitmap에 반영한다.
4) section number, 페이지 수, section에 할당된 subsection의 시작과 마지막 index를 출력한다
5) 처리한 페이지 수 만큼 시작 pfn과 처리해야 할 페이지 수 (nr_pages)를 업데이트한다


subsection_mask_set()
- 인수 pfn부터 nr_pages 만큼의 페이지를 subsection index로 변환해서 인수 map의 bitmap에 반영한다
- subsection index는 subsection 당 page 512개가 포함되고, section 당 subsection이 512개가 포함되는 단위이다
- 따라서 pfn의 하위 9bit를 없애고 하위 18bit 보다 상위의 bit를 제거해서 그 사이의 9bit를 구해서 이를 index로 계산한다
- 이렇게 계산한 index를 mem_section_usage의 subsection_map(512bit)의 bitmap에 대응시킨다


mminit_verify_pageflags_layout()
- config 설정에 따라 struct page의 flags의 각 bit의 layout이 달라진다
- 본 함수는 현재 설정에서 page flags의 layout을 커널 로그로 출력한다
- 출력을 위해 매크로 mminit_dprintk()를 사용하여 "mminit::" 및 prefix를 앞에 출력하고 그 뒤에 로그를 출력한다


init_unavailable_mem()
- 각 memblock.memory에 대해서 루프를 돌면서 memblock.memory에 포함되지 않는 영역에 대해 init_unavailable_range() 함수를 수행하여 초기화하고 초기화된 페이지의 총 수를 계산한다
- 이후 마지막 memblock.memory 다음부터 마지막 pfn(section 단위로 align up)까지 영역을 init_unavailable_range() 함수로 다시 초기화하고 초기화된 페이지를 더한다
- 초기화된 페이지 총 수를 로그에 출력한다

 

init_unavailable_range()
- 현재 설정으로 pageblock_nr_pages = 1024임
- 시작 pfn부터 end pfn까지 루프를 돌면서 아래를 수행한다
1) 현재 pfn을 pageblock 단위로 align한 pfn이 valid하지 않으면 pfn을 pageblock_nr_pages(1024) 만큼 더하고 continue
- pfn valid check를 pageblock(1024개 page) 단위로 하는 것으로 보임
2) 현재 pfn을 pageblock 단위로 align한 pfn이 valid하면 현재 page에 대해서 __init_single_page()로 초기화하고 __SetPageReserved()로 reserve 처리한다. 또한 본 함수에서 초기화한 page 수를 카운트한다
- 루프가 끝나면 초기화한 page 수를 리턴한다

 

- 현재로써는 memblock.memory에 포함되지 않는 pfn을 넘겨주었는데, valid_pfn()은 memblock.memory에 포함되어야 valid하므로 대부분의 경우 그냥 skip하는 코드가 된다
- 다만 pageblock 단위로 align down한 다음 pfn_valid() 체크를 하기 때문에 memblock 마지막에 pageblock align이 맞지 않는 pfn들의 경우에는 일부 초기화가 된다 (한 pageblock 이하 만큼만)
- 그러나 이것이 의도된 동작인지는 의문이다
- memblock 앞의 pageblock 만큼은 초기화가 안되는데 뒤의 pageblock 만큼은 초기화를 하고 있다

 

pfn_valid()
- 인수 pfn이 valid한지 판정
- pfn의 section number가 NR_MEM_SECTIONS (1 << 18)보다 크면 invalid
- pfn의 section이 valid하지 않으면 invalid (valid_section() 함수 사용)
- memblock_is_map_memory() 함수로 pfn에 해당하는 물리 주소가 memblock.memory에 존재하는지, 또한 nomap인지 체크해서 memblock.memory에 존재하지 않거나 nomap이면  invalid이고 아니면 valid로 리턴한다

 

valid_section()
- 인수 mem_section의 section_mem_map flag가 SECTION_HAS_MEM_MAP이 0이면 invalid로 리턴

 

memblock_is_map_memory()
- 인수 addr에 해당하는 memblock.memory가 없으면 0 리턴
- 있어도 해당 memblock이 nomap이면 0 리턴 
- 아니면 1 리턴

 

관련 패치
- https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1550457.html
- https://github.com/torvalds/linux/commit/e8c24773d6b2cd9bc8b36bd6e60beff599be14be
- https://lore.kernel.org/lkml/20171201095048.GA3084@dhcp-128-65.nay.redhat.com/#t
- https://github.com/torvalds/linux/commit/720e14ebec642bc56c44e5e60a2d595900e5bbf0
- https://github.com/torvalds/linux/commit/e822969cab48b
- https://github.com/torvalds/linux/commit/a4a3ede2132ae0863e2d43e06f9b5697c51a7a3b

 

5.14-rc1에서의 모습
- https://elixir.bootlin.com/linux/v5.14-rc1/source/mm/page_alloc.c

XE Login