[커널 17차] 47주차

2021.07.24 23:41

ㅇㅇㅇ 조회 수:83

init_unavailable_range()
- pfn_valid 체크가 pageblock이 아니고 section 단위로 하는 것이 의도가 아닌가 생각됨
- 원래는 memory에 없지만 reserve에 있는 page를 체크하는 것이었음

 

__SetPageReserved() 매크로
- page flag의 다양한 bit을 test/set/clear하는 매크로들이 있다
- TESTPAGEFLAG() / SETPAGEFLAG() / CLEARPAGEFLAG()
- 이 매크로들은 bit set/clear시 atomic operation을 사용한다
- 이 매크로들은 policy가 있어서 page flag에 따라서 버그 체크를 할 수 있다
- 버그 체크 중 PF_NO_COMPOUND 같은 것은 page가 compound page인가를 체크한다
(page 할당시 1개가 아닌 연속된 여러 개의 page를 할당받으면 compound page가 된다)
- 공통된 버그 체크는 PF_POISONED_CHECK() 가 있음
- __SETPAGEFLAG() / __CLEARPAGEFLAG() 매크로는 기능은 동일한데 bit set/clear 동작 시 atomic operation을 이용하지 않아 더 빠르다

 

free_area_init()
- init_unavailable_mem() 이후부터
- for_each_online_node() 매크로로 각 online node에 대해 루프를 돌면서
- NODE_DATA(nid)로 nid에 대한 pg_data_t 구조체 pgdat을 구하고
- free_area_init_node(nid) 함수로 해당 노드를 초기화 한다
- pgdat->node_present_pages를 체크해서 노드에 메모리가 존재하면 node_set_state(nid, N_MEMORY) 함수로 N_MEMORY 비트맵을 set한다
- check_for_memory(pgdat, nid) 함수로 해당 노드에 highmem이 있는지 normal memory가 있는지 체크해서 노드 비트맵을 업데이트한다

 

free_area_init_node()
- arm64_numa_init()에서 함수 get_pfn_range_for_nid()를 사용해서 각 노드의 start pfn과 spanned pfn을 계산했었는데 여기서도 같은 방식으로 현 노드의 start pfn과 end pfn을 계산하고 있다
- arm64_numa_init()에서는 이 정보로 pg_data_t 배열 node_data를 만들었다
- 이는 sparse_init()에서 pgdat 정보를 이용하기 때문에 중복 계산하고 있는 것으로 보인다
- pgdat에 현 노드의 nid, start pfn을 저장하고 per_cpu_nodestats을 NULL로 초기화한다

- calculate_node_totalpages() 함수를 이용해서 각 노드 별 zone 영역의 start pfn, spanned pfn, present pfn을 계산하여 저장한다. 그리고 각 node의 총 spanned page 수와 present page 수를 저장한다. 이 정보들은 pgdat 구조체 배열 node_data[nid]에 저장된다
- spanned pfn은 각 노드/존에 할당된 페이지 수 전체이고 present pfn은 그 중 hole을 제외한 진짜 page 수를 나타낸다
- CONFIG_FLAT_NODE_MEM_MAP가 n이므로 alloc_node_mem_map()는 빈 함수
- 함수 pgdat_set_deferred_range()로 pgdat의 first_deferred_pfn을 0xFFF..FF로 설정
- 현재 CONFIG_DEFERRED_STRUCT_PAGE_INIT는 off임
- 함수 free_area_init_core()로 노드의 각 zone 자료구조(pgdat)를 초기화한다
1) 모든 페이지를 reverse
2) 모든 메모리 큐를 empty
3) 메모리 비트맵을 clear

 

calculate_node_totalpages()
- zone_spanned_pages_in_node(), zone_absent_pages_in_node() 함수를 이용하여 각 노드 별 zone 영역의 start pfn, spanned pfn, present pfn을 계산하여 저장한다. 그리고 각 node의 총 spanned page 수와 present page 수를 저장한다. 이 정보들은 pgdat 구조체 배열 node_data[nid]에 저장된다

 

zone_spanned_pages_in_node()
- 각 노드내의 각 존에서의 zone start pfn과 zone end pfn을 계산하고 그 차이를 spanned pfn으로 리턴한다

 

zone_absent_pages_in_node()
- 각 노드내의 각 존에서의 zone start pfn과 zone end pfn 사이의 전체 페이지 수에서 memblock이 실제 존재하는 page 수를 빼서 해당 노드의 각 존에서 hole의 페이지 수를 계산한다
- 만약 mirrored_kernelcore인 경우에는 다음을 실행한다
1) zone movable인 경우 mirror 영역을 hole 페이지 수에 포함한다
2) zone normal인 경우 mirrror가 아닌 영역을 hole 페이지 수에 포함한다
- 이렇게 되면 mirrored_kernelcore인 경우에도 hole 페이지 수가 정확하게 계산된다

 

adjust_zone_range_for_zone_movable()
- 각 노드의 존들의 start pfn과 end pfn을 조정한다


- movable zone의 경우
1) zone start pfn을 zone_movable_pfn[nid]로 조정
2) zone end pfn을 node_end_pfn으로 조정하여 노드의 끝으로 조정


- movable zone이 아니고 mirrored_kernelcore이 아닌 경우에서 zone의 movable zone 아래에서 시작해서 movable zone 경계 안으로 들어오는 경우
1) zone start pfn은 유지
2) zone end pfn은 zone_movable_pfn[nid]로 조정


- zone이 movable zone안에 포함되는 경우
1) zone start pfn을 zone end pfn으로 조정 --> 사실상 zone 제거
2) zone end pfn은 유지

- 따라서 mirrored_kernelcore인 경우에는 movable_zone이 없는 것처럼 normal zone의 start pfn과 end pfn이 계산된다

 

관련 패치
- https://github.com/torvalds/linux/commit/e506b99696a296e9aba2e5f3bc5768aa7d8e2396

 

free_area_init_core()
- pgdat 자료구조를 받아서 해당 노드의 모든 존 메타데이터를 초기화한다
- pgdat_init_internals() 함수로 pgdat의 내부 변수를 초기화한다
- pgdat->per_cpu_nodestats을 &boot_nodestats 전역변수로 초기화 (per_cpu_nodestat 구조체)
- 이후 노드의 각 존에 대해서 루프를 돌면서 아래를 수행한다
- calc_memmap_size()으로 memmap에 사용된 struct page 메모리의 페이지 수를 계산한다 (memmap_pages)
- highmem은 없으므로 함수 is_highmem_idx()는 항상 false이다
- freesize(present_pages)에서 memmap_pages를 차감하고 커널 로그로 출력한다
- dma_reserve는 x86에만 있으므로 무시
- 전역변수 nr_kernel_pages, nr_all_pages에 freesize를 더한다
- zone_init_internals()으로 pgdat->node_zones를 초기화한다
- set_pageblock_order()는 빈 함수이고, pageblock_order는 9이다
- setup_usemap()은 빈 함수
- init_currently_empty_zone()으로 pgdat->node_zones을 업데이트
- memmap_init()으로 struct page들을 초기화한다


pgdat_init_internals()
- pgdat_resize_init()으로 pgdat->node_size_lock 스핀락을 초기화
- pgdat_init_split_queue()으로 pgdat->deferred_split_queue를 초기화
- pgdat_init_kcompactd()으로 pgdat->kcompactd_wait wait_queue_head를 초기화
- init_waitqueue_head()로 pgdat->kswapd_wait를 초기화
- init_waitqueue_head()로 pgdat->pfmemalloc_wait를 초기화
- 함수 pgdat_page_ext_init()는 빈 함수 : CONFIG_PAGE_EXTENSION은 sparsemem쓰면 false
- spin_lock_init()으로 pgdat->lru_lock 스핀락 초기화
- lruvec_init()으로 pgdat->__lruvec 구조체를 초기화

 

pgdat_init_split_queue()
- pgdat->deferred_split_queue에 들어있는 아래 변수들을 초기화한다
- split_queue_lock 스핀락을 초기화
- split_queue list_head를 초기화
- split_queue_len = 0

 

init_waitqueue_head()
- wait_queue_head는 스핀락(lock)과 list_head(head)로 구성됨
- list_head가 queue를 구성하고 이를 변경할 때 synchronization을 위해 spinlock을 사용함
- lock 스핀락을 초기화
- head list_head를 초기화

 

lruvec_init()
- memset으로 lruvec 구조체 초기화
- 이후 for_each_lru() 매크로로 lruvec->lists[lru] list_head들을 모두 초기화

 

calc_memmap_size()
- memmap에 사용된 struct page의 페이지 수를 계산해서 리턴한다
- spanned_pages와 present_pages 보다 많이 크면 present_pages 기준으로 계산
- 아니면 spanned_pages 기준으로 계산한다

 

zone_init_internals()
- pgdat의 node_zones를 초기화한다
- zone->managed_pages를 freesize로 초기화
- zone->node, zone->name, zone->zone_pgdat을 초기화
- spin_lock_init()으로 zone->lock 스핀락을 초기화
- zone_seqlock_init()으로 zone->span_seqlock seqlock을 초기화
- zone_pcp_init()으로 zone의 percpu 자료구조를 초기화

 

seqlock_init()
- seqlock의 스핀락(lock)을 초기화
- sequence 값(seqcount)을 0으로 초기화

 

lock 종류
- https://blog.daum.net/tlos6733/162

 

zone_pcp_init()
- zone->pageset을 전역변수 per_cpu_pageset 구조체 &boot_pageset으로 초기화
- zone_batchsize() 함수로 zone의 batch 사이즈를 계산하고 zone name, present_pages 수와 함께 커널 로그로 출력한다

 

init_currently_empty_zone()
- pgdat->nr_zones를 업데이트한다
- zone_init_free_lists()로 zone->free_area[order].free_list[t] list_head를 초기화하고 zone->free_area[order].nr_free도 0으로 초기화한다
- zone->initialized = 1로 업데이트한다

 

memmap_init()
- 각 노드의 각 존의 start pfn 및 end pfn을 계산하고 memmap_init_zone()으로 struct page를 초기화한다

 

memmap_init_zone()
- 전역 변수 highest_memmap_pfn을 end pfn으로부터 업데이트한다
- altmap = NULL이므로 ZONE_DEVICE인 경우 그냥 리턴 <-- 여기까지 진행

XE Login