[커널 17차] 45주차

2021.07.10 23:13

ㅇㅇㅇ 조회 수:127

zone 개념
- http://jake.dothome.co.kr/zone-types/

 

HMM
- https://www.kernel.org/doc/html/v4.18/vm/hmm.html

- CMA는 movable zone에 할당하여 사용
- 원래 GPU 메모리 할당을 하려면 cuda_malloc 등을 사용해야 하나 HMM device_zone을 사용하면 해당 영역을 일반 malloc을 이용해서 할당받을 수 있다?

 

ARM64 5.9v 기준
- ZONE_DMA와 ZONE_DMA32가 모두 켜져 있다
- ZONE_DMA : 하위 1GB (하위 1GB 주소만 접근할 수 있는 디바이스를 위함)
- ZONE_DMA32 : 나머지 32bit 영역 (1GB ~ 4GB까지)
- 즉 ZONE_DMA는 32bit 보다 작은 제각각의 address limitation을 가지는 DMA 지원을 할 수 있도록 함
- ZONE_DMA32는 32bit 전체를 사용할 수 있는 DMA device를 커버함

- sparse_buffer_init()에서 메모리 할당할 때 MAX_DMA_ADDRESS가 PAGE_OFFSET으로 따로 DMA 영역을 고려하지 않고 할당했다

 

zone_sizes_init()
- argument로 min pfn, max pfn을 입력 받음
- max_zone_pfns 배열을 만들어서 각 zone 별 max pfn을 구함

- max_zone_pfns[ZONE_DMA] : zone_dma 영역의 최대 pfn, arm64_memblock_init()에서 정한 arm64_dma_phys_limit 사용. DRAM 시작 주소에서 최대 1GB까지로 설정.

- max_zone_pfns[ZONE_DMA32] : zone_dma32 영역의 최대 pfn, arm64_memblock_init()에서 정한 arm64_dma32_phys_limit 사용. DRAM 시작 주소에서 최대 4GB까지로 설정.

- max_zone_pfns[ZONE_NORMAL] : zone_normal 영역의 최대 pfn. 물리적인 max_pfn 사용
- free_area_init() 함수 호출하여 각 zone의 시작 pfn과 종료 pfn을 설정한다


매크로 MAX_NR_ZONES
- kernel/bounds.c 코드로부터 kernel/bounds.s를 만들고 여기서 bounds.h를 생성해서 매크로를 만든다
- Kbuild로 script/Makefile.lib 사용
- scripts/Makefile.lib로 bounds.s를 sed로 parsing해서 bounds.h로 만든다
- scripts/Makefile.build의 116번째 줄에서 bounds.s를 생성함
- https://github.com/torvalds/linux/commit/1cdf25d704f7951d02a04064c97db547d6021872
- 커밋 1cdf25d704f79에서 MAX_NR_ZONES를 알아낼 수 없기 때문에 이런 방식으로 매크로를 생성하는 것으로 보임
- MAX_NR_ZONES를 preprocessor로 넘길 수 없는 이유는 이게 enumeration으로 정의되는데 이것이 config 및 #if에서 사용되기 때문임


free_area_init()
- 아래 두 전역변수 배열은 각 zone별로 시작 pfn와 종료 pfn를 저장한다
+- arch_zone_lowest_possible_pfn[MAX_NR_ZONES]
+- arch_zone_highest_possible_pfn[MAX_NR_ZONES]
- 위 두 배열을 모두 zeroing 수행
- 함수 find_min_pfn_with_active_regions()로 물리주소로 첫 번째 pfn을 계산
- 함수 arch_has_descending_max_zone_pfns()로 zone 처리 순서가 ascending인지 descending인지 결정. 현 설정에서는 ascending
- 루프를 돌면서 movable zone을 제외한 zone_dma, zone_dma32, zone_normal, zone_device 등에 대해 차례대로 arch_zone_lowest_possible_pfn[zone]/arch_zone_highest_possible_pfn[zone]을 설정한다
- 전역변수 배열 zone_movable_pfn[MAX_NUMNODES]는 각 노드의 movable zone의 시작 pfn을 저장한다
- zone_movable_pfn을 0으로 설정
- 함수 find_zone_movable_pfns_for_nodes()으로 각 노드의 movable zone이 시작되는 pfn을 계산한다 --> 분석 중


find_zone_movable_pfns_for_nodes()
- saved_node_state에 node_states[N_MEMORY]를 저장
- 함수 early_calculate_totalpages()로 모든 노드의 총 page 수 합계를 계산하여 totalpages에 저장
- nodes_weight() 함수로 메모리가 존재하는 총 노드의 수를 계산하여 usable_nodes에 저장
- 함수 find_usable_zone_for_movable()으로 movable_zone을 설정한다
- 다음 3가지 path 중 하나를 타게 된다

 

1) movable_node_is_enabled()이 참인 경우
- 전역 변수 movable_node_enabled가 참이면 이 path를 탄다
- 각 memblock에 대해 hotpluggable인 memblock의 base pfn 중 가장 낮은 pfn을 zone_movable_pfn[nid]로 잡는다
- 0번 노드는 hotplug 설정 불가능하다
- out2 label로 점프한다

 

2) mirrored_kernelcore가 참인 경우
- early_param("kernelcore", cmdline_parse_kernelcore);
- 함수 cmdline_parse_kernelcore()에서 early_param으로 option에 "mirror"가 있으면 mirrored_kernelcore가 참이 된다
- kernelcore는 non-movable kernel 영역이 자리잡는 메모리 영역임
- mirrored_kernelcore는 mirroring으로 보호받는 메모리 영역에 kernelcore를 할당하라는 의미
- x86 시스템에서만 사용 가능하다
- mirror 외 나머지 영역은 movable zone이 된다
- 본 옵션 사용시 4GB 이하 메모리 영역은 mirror 플래그 설정해야 함
- 각 memblock에 대해서 mirror가 아닌 memblock의 base pfn 중 가낭 낮은 pfn을 zone_movable_pfn[nid]로 잡는다
- mirror가 아닌 memblock 중 base pfn이 4GB 보다 낮으면 경고를 출력한다
- out2 label로 점프한다

 

3) required_kernelcore_percent, required_movablecore_percent, required_kernelcore, required_movablecore 중 하나가 설정되어 있거나 1), 2), 3) 모두 아무것도 설정 안된 경우
- early_param("kernelcore", cmdline_parse_kernelcore);
- early_param("movablecore", cmdline_parse_movablecore);
- 위 early_param에서 위 4개 파라미터가 설정된다
- %로 option을 주면 percent 파라미터가 설정되고 아니면 page 수 파라미터가 설정된다
- % 파라미터가 설정되어 있으면 required_kernelcore, required_movablecore의 page 수를 계산한다
- required_movablecore가 설정되어 있으면 1024 page로 roundup을 하고 나머지를 required_kernelcore로 설정한다. 그러나 required_kernelcore가 cmdline으로 설정되어 있으면 cmdline으로 설정된 required_kernelcore가 우선 적용된다
- required_kernelcore가 설정되지 않았거나 total page 수보다 같거나 크면 out label로 이동한다
- usable_startpfn을 movable_zone의 가장 낮은 pfn으로 설정한다
- 여기까지 진행


early_calculate_totalpages()
- 시스템의 총 페이지 수를 계산하여 반환한다
- 매크로 for_each_mem_pfn_range()를 이용하여 iterate를 수행하고
- 각 iterate마다 각 노드의 시작 pfn/종료 pfn을 구해서 총 페이지 수에 더함
- 그리고 각 노드의 page 수가 0이 아니면 함수 node_set_state()로 현 노드(nid)의 N_MEMORY bitmap을 set한다 (node_states[N_MEMORY] bitmask에서 nid 번째 bit를 1로 설정)
- 총 페이지 수를 리턴

 

for_each_mem_pfn_range() 매크로
- 함수 __next_mem_pfn_range()를 이용하여 모든 노드에 대해서 iteration을 수행한다
- 각 iteration마다 각 노드의 nid/시작 pfn/종료 pfn를 구한다
- loop 변수 i가 -1이면 iterate 종료

 

__next_mem_pfn_range()
- memblock.memory의 region에 대해서 루프를 돌면서
- nid, base의 pfn, base + size의 pfn을 구해서 리턴한다
- region에서 base pfn과 base + size pfn이 같거나 역전되면 루프에서 pass --> 동일 nid, 동일 pfn이므로 중복임

 

nodes_weight()
- bitmask 중에서 1로 set되어 있는 bit의 총 수를 리턴한다

 

find_usable_zone_for_movable()
- movable zone으로 사용될 수 있는 zone 중에서 가장 high zone을 선택한다
- 실제 물리 pfn이 있는 최상위 zone을 movable_zone으로 설정한다
- movable_zone은 전역변수임

 

movable_node_enabled
- false로 초기화되어 있음
- early_param으로 cmd_line에서 세팅
- 함수 cmdline_parse_movable_node()
- early_param("movable_node", cmdline_parse_movable_node);

 

out2 label
- 각 노드에 대해 zone_movable_pfn[nid]를 1024로 roundup 수행한다

 

out lable
- node_states[N_MEMORY]를 saved_node_state로 복원한다

 

구조체 assign하면 compiler가 지정한 memcpy가 된다
compiler 의존도를 줄이고 싶으면 각 member에 대해 assign해야 한다

 

커널 파라미터
- https://www.kernel.org/doc/html/v4.14/admin-guide/kernel-parameters.html

 

참조
- http://jake.dothome.co.kr/free_area_init_node/
- https://github.com/torvalds/linux/commit/342332e6a925e9ed015e5465062c38d2b86ec8f9

XE Login