[커널 17차] 86~87주차

2022.04.30 23:03

ㅇㅇㅇ 조회 수:101

alloc_pages()
- 일단 NUMA가 아닌 경우에 대해서 분석 시작
- 인자로 gfp_mask와 order를 받아서 함수 alloc_pages_node()를 호출하며, alloc_pages_node()에 넣어줄 node id로는 numa_node_id()를 사용함

 

alloc_pages_node()
- 인자로 nid, gfp_mask, order를 받는다
- nid == NUMA_NO_NODE이면 nid = numa_mem_id()로 하여 가장 가까운 memory가 있는 node로 세팅한다
- 함수 __alloc_pages_node()를 호출한다

 

__alloc_pages_node()
- 인자로 nid, gfp_mask, order를 받는다
- 0 <= nid < MAX_NUMNODES 를 체크한다
- gfp == GFP_THISNODE이고 nid가 online이 아니면 경고한다
- GFP_THISNODE : 요청된 nid에서 할당을 강제하는 플래그 (no fallback)
- 함수 __alloc_pages()를 호출한다. 이 때 인자 nodemask == NULL로 호출한다

 

__alloc_pages()
- 인자로 gfp, order, preferred_nid, nodemask를 받는다
- L5347 : alloc_flags를 우선 ALLOC_WMARK_LOW로 설정한다
- L5355 : order가 MAX_ORDER보다 같거나 크면 경고하고 NULL 리턴한다
- L5360 : gfp를 gfp_allowed_mask로 마스킹한다. gfp_allowed_mask는 GFP_BOOT_MASK로 초기화되어 있고 GFP_BOOT_MASK는 GFP mask 중에서 __GFP_RECLAIM, __GFP_IO, __GFP_FS가 제외되어 있다. 나중에 kernel_init_freeable() 함수에서 모든 bit flag를 허용하는 것으로 변경된다.
- L5368 : 함수 current_gfp_context()로 task 설정에 따라 gfp를 조정한다
- L5369 : alloc_gfp에 gfp를 대입
- L5370 : 함수 prepare_alloc_pages()로 alloc_gfp, alloc_flags, alloc_context ac를 설정한다. 실패하면 NULL 리턴
- L5378 : 함수 alloc_flags_nofragment()로 fast path에서 사용할 flag를 조정한다. 이는 최대한 fragment를 일으키지 않도록 하기 위해서이다
- L5381 : 함수 get_page_from_freelist()로 page 할당을 시도한다. Fastpath이며, 위에서 변경된 alloc_flags로 할당 시도한다

 

current_gfp_context()
- 현재 task 설정에 따라 인자로 받은 gfp 값을 조정한다
- L158 : current->flags를 가져온다
- L160 : 현 task의 flags에 PF_MEMALLOC_NOIO, PF_MEMALLOC_NOFS, PF_MEMALLOC_PIN 가 있으면 조정에 들어간다
- L165 : PF_MEMALLOC_NOIO가 있으면 __GFP_IO, __GFP_FS를 제거한다
- L168 : PF_MEMALLOC_NOFS가 있으면 __GFP_FS를 제거한다
- L170 : PF_MEMALLOC_PIN이 있으면 __GFP_MOVABLE을 제거한다
- L173 : 변경된 flags를 리턴한다

 

prepare_alloc_pages()
- 인자로 gfp_mask, order, preferred nid, nodemask를 받아서 alloc_context ac, alloc_gfp, alloc_flags를 리턴한다
- L5136 ~ L5139 : alloc context ac에 highest zone index, zone list, node mask, migrate type을 채워준다
- L5141 : cpuset이 설정되어 있으면 alloc_gfp에 __GFP_HARDWALL을 추가한다. 그리고 현재 context가 task context이고 nodemask가 따로 요청되지 않은 경우 ac->nodemask를 current->mems-allowed로 변경한다. 아니면 alloc_flags에 ALLOC_CPUSET을 추가한다
- L5153 ~ 5154 : CONFIG_LOCKDEP = n 이므로 빈 함수이다
- L5156 : gfp_mask에 __GFP_DIRECT_RECLAIM 플래그가 있으면 함수 might_sleep_if()로 양보한다
- L5158 : config 설정에 fault injection이 있는 경우 메모리 요청이 실패해야 하는 케이스인지 판단하고 맞으면 false를 리턴한다
- L5161 : migrate type이 MOVABLE이면 alloc_flags에 ALLOC_CMA를 추가한다
- L5164 : gfp_mask에 __GFP_WRITE가 있으면 ac->spread_dirty_pages에 플래그를 추가한다
- L5171 : ac->zonelist, ac->highest_zoneidx, ac->nodemask로부터 함수 first_zones_zonelist()를 이용해서 첫 번째 zone을 계산해서 ac->preferred_zoneref에 달아준다
- L5174 : true를 리턴한다

 

get_migratetype()
- gfp_flags를 받아서 0 / __GFP_RECLAIMABLE / __GFP_MOVABLE 셋 중 하나를 리턴한다
- __GFP_RECLAIMABLE / __GFP_MOVABLE 둘 다 설정되면 경고를 내보낸다

 

alloc_flags_nofragment()
- 인자로 zone, gfp_mask를 받는다
- L4000 : gfp_mask를 __GFP_KSWAPD_RECLAIM로 필터링해서 alloc_flags를 계산한다
- L4003 : zone이 NULL이면 alloc_flags 리턴
- L4006 : zone이 ZONE_NORMAL이 아니면 alloc_flags 리턴
- L4014 : ZONE_NORMAL이 ZONE_DMA32 다음 zone이 아니면 빌드 버그
- L4015 : online node가 2개 이상이고 ZONE_DMA32가 populated가 아니면 alloc_flags 리턴
- L4018 : alloc_flags에 ALLOC_NOFRAGMENT를 추가해서 리턴한다. 이는 pageblock type이 섞이는 것을 막는다. 이는 allocation을 pageblock 단위로 수행하도록 하기 때문이다 (min order 변경)
- 주석에 따르면 preferred zone이 highmem이면 lowmem으로 fallback하면 low mem pressure 때문에 더 안좋고 ZONE_NORMAL 다음이 ZONE_DMA이면 메모리가 너무 작아서 다른 노드로 fallback되므로 그냥 현재 노드에서 fragment가 일으키는게 차라리 낫다. 그리고 node가 2개 이상인데 DMA32가 없으면 애초에 lowmem으로 fallback할 수 없으므로 ALLOC_NOFRAGMENT 하지 않는다

 

get_page_from_freelist()
- 인자로 gfp_mask, order, alloc_flags, alloc_context ac를 받는다
- L4044 : pglist_data last_pgdat_dirty_limit = NULL로 초기화
- L4052 : alloc_flags에 ALLOC_NOFRAGMENT 플래그가 있으면 no_fallback을 true로 한다. 아니면 false
- L4053 : ac->preferred_zoneref를 iteration 첫 zoneref로 잡는다
- L4054 : 매크로 for_next_zone_zonelist_nodemask()로 ac->highest_zoneidx, ac->nodemask로 zone 들을 iterate 한다
- L4059 : cpuset이 enabled이고 alloc_flags가 ALLOC_CPUSET으로 체크되어 있고 현재 존이 __cpuset_node_allowed() 함수로 cpuset에 허용되지 않는 경우면 다음 zone으로 넘어간다
- L4082 : ac->spread_dirty_pages == true이면 현재 루프 zone의 node가 dirty limit을 넘어가는지 함수 node_dirty_ok()로 체크한다. 만약 dirty limit에 걸리면 현재 node를 last_pgdat_dirty_limit에 저장하고 continue로 다음 zone으로 넘어간다. 만약 다음 루프에서 노드가 last_pgdat_dirty_limit와 동일하면 역시 continue로 다음 zone으로 넘어가도록 한다.
- L4092 : no_fallback 상태이고 노드가 여러 개이며 현재 zone이 preferred_zoneref가 아닌 경우 현재 node가 preferred zone의 node가 아니면 alloc_flags에서 ALLOC_NOFRAGMENT를 지우고 L4052로 돌아간다. 루프를 다시 처음부터 시작. 즉 ALLOC_NOFRAGMENT인 상태에서 할당을 시도했는데 preferred zone node가 아닌 다른 node로 넘어가버리면 locality가 깨지므로 ALLOC_NOFRAGMENT 제약을 풀고 다시 시도한다
- L4108 : 현재 zone과 alloc_flags에 따른 watermark를 가져온다. 현재는 LOW
- L4109 : <— 진행 예정

 

__cpuset_node_allowed()
- 인자로 node 및 gfp_mask를 받는다
- L3519 : 인터럽트 context이면 true
- L3521 : node가 current->mems_allowed이면 true
- L3527 : 현 task가 out of memory가 곧 죽을 task이면 true
- L3529 : gfp_mask에 __GFP_HARDWALL이 set 되어 있으면 false
- L3532 : 현재 task가 죽는 중이면 (current->flag == PF_EXITING) true
- L3536 : callback_lock spin lock을 건다
- L3538 : rcu_read_lock()
- L3539 : 함수 nearest_hardwall_ancestor()로 현재 task의 cpuset hardwall ancestor를 구한다
- L3540 : 현재 cpuset에서 node가 mems_allowed 인지 체크한다
- L3541 : rcu_read_unlock()
- L3543 : callback_lock spin lock을 푼다
- L3544 : 체크된 allowed를 리턴한다

 

node_dirty_ok()
- L497 : 함수 node_dirty_limit()로 현재 노드의 dirty limit을 계산한다
- L500 : 현 노드의 NR_FILE_DIRTY/NR_WRITEBACK 인 page 수를 더해서 (dirty page 수) limit 이하인지 리턴한다

 

node_dirty_limit()
- L472 : 함수 node_dirtyable_memory()로 현 노드의 dirtyable memory 양을 계산한다
- L480 : 현 노드의 dirtyable memory의 20%를 dirty limit으로 계산한다
- L482 : RT인 경우 25%로 limit 상향
- L485 : dirty limit 리턴

 

node_dirtyable_memory()
- 현 노드의 모든 populated page 수 - totalreserve_pages + NR_INACTIVE_FILE + NR_ACTIVE_FILE page 수를 리턴한다

 

Watermark
- http://jake.dothome.co.kr/zonned-allocator-watermark/

 

Watermark boost
- 1c30844d2dfe2 : external fragmentation을 줄이기 위해 일시적으로 sysctl로 watermark 값을 올릴 수 있도록 boost 개념을 도입하였음

 

boost_watermark()
- 호출되면 watermark high의 watermark_boost_factor (초기값 1.5배) 만큼 곱해서 max_boost를 구함
- max_boost와 pageblock_nr_pages 중 큰 값을 max_boost로 업데이트
- zone->watermark_boost를 zone->watermark_boost + pageblock_nr_pages와 max_boost 중 작은 값으로 세팅한다

 

steal_suitable_fallback()
- 이 함수에서 boost_watermark() 호출하여 watermark boost를 설정함

init_per_zone_wmark_min()
- 이 함수는 부팅 한참 뒤에서 사용된다
- L8466 : 함수 nr_free_buffer_pages()로 watermark high 이상의 페이지를 더해서 4로 나눈값을 lowmem_kbytes로 계산
- L8467 : lowmem_kbytes에 16을 곱해서 루트를 씌운 값을 new_min_free_kbytes으로 계산
- L8470 : min_free_kbytes = new_min_free_kbytes으로 놓고 128 ~ 262144 범위로 조정한다
- L8479 : 함수 __setup_per_zone_wmarks()로 zone 별 watermark 설정
- …

 

__setup_per_zone_wmarks()
- 각 zone의 watermark를 설정하는 함수
- L8353 : 전역 변수 min_free_kbytes를 4로 나누어서 pages_min 계산
- L8359 : 각 zone의 managed pages를 모아서 lowmem_pages에 더한다
- L8364 : 각 zone 별로 루프를 돈다
- L8367 : zone->lock spinlock을 건다
- L8368 : pages_min에 현재 zone의 managed pages / lowmem_pages를 곱해서 tmp를 구한다. 즉 모든 managed pages에서 현재 zone의 managed pages의 비율을 pages_min에 곱해준다
- L8370 : highmem 없으므로 패스
- L8390 : zone->_watermark[WMARK_MIN] = tmp
- L8398 : tmp를 tmp/4와 zone의 managed pages x 10 / 10000 중 큰 것으로 세팅한다
- L8403 : zone->_watermark[WMARK_LOW] = zone->_watermark[WMARK_MIN] + tmp
- L8404 : zone->_watermark[WMARK_HIGH] = zone->_watermark[WMARK_MIN] + 2 * tmp
- L8406 : zone->lock spinlock을 푼다
- L8410 : 함수 calculate_totalreserve_pages()로 totalreserve_pages를 업데이트한다

 

 calculate_totalreserve_pages()
- L8287 : 각 online node에 대해서 루프를 돈다
- L8289 : node의 totalreserve_pages를 0으로 설정
- L8291 : 현재 node의 zone 별로 루프를 돈다
- L8294 : 현재 zone의 managed_pages를 구한다
- L8293 : max = 0
- L8297 : 현재 zone부터 MAX ZONE 중에서 zone->lowmem_reserve가 max보다 크면 max를 lowmem_reserve로 세팅
- L8303 : max에 zone의 high watermark를 더해준다
- L8305 : max > managed_pages이면 managed_pages로 조정
- L8308 : max를 node의 totalreserve_pages에 더해준다
- L8310 : totalreserve_pages에 max를 더해줌

 

setup_per_zone_lowmem_reserve()
- lowmem_reserve 배열을 생성해준다
- http://jake.dothome.co.kr/zonned-allocator-watermark/ 에서 lowmem reserve 참조
- 배열 생성이 끝나면 함수 calculate_totalreserve_pages() 재호출

 

zone_watermark_fast()
- L3924 : 함수 zone_page_state()로 zone의 free pages 수를 가져온다
- L3930 : order == 0이면 if 문 진입
- L3934 : free pages에서 __zone_watermark_unusable_free() 만큼 unusable free page를 감소시킨 fast_free 값을 구함
- L3935 : fast_free > watermark + z->lowmemreserve[highest_zoneidx] 이면 메모리가 충분하므로 true 리턴
- L3939 : order != 0 이거나 order == 0인데 fast check 실패하면 함수 __zone_watermark_ok()로 메모리 체크 수행하여 충분하면 true 리턴
- L3948 : order == 0이고 gfp_mask에 __GFP_ATOMIC이 있고 z->watermark_boost가 존재하고, alloc_flags에 ALLOC_WMARK_MASK가 있는 경우, watermark를 min으로 낮추고 다시 함수 __zone_watermark_ok()를 호출하여 메모리 체크를 시도한다
- L3955 : L3939에서 실패하고 L3948 조건에 안걸리면 false 리턴한다

 

__zone_watermark_unusable_free()
- 인자로 zone z, order, alloc_flags를 받는다
- z의 free memory 중 쓸 수 없는 메모리양을 계산해서 반환한다
- L3821 : alloc_flags 중 ALLOC_HARDER 및 ALLOC_OOM이 있으면 alloc_harder를 참으로 세팅
- L3822 : unusable_free를 요청한 페이지 수 - 1 만큼으로 설정한다 (요청한만큼 감소)
- L3829 : alloc_harder가 참이 아니면 unusable_free에 z->nr_reserved_highatomic을 더해 준다 (high atomic 영역 사용 불가)
- L3834 : alloc_flags에 ALLOC_CMA가 없으면 unusable_free에 free CMA page 만큼 더해준다 (CMA 영역 사용 불가)
- L3838 : unusable_free를 반환한다

 

__zone_watermark_ok()
- 인자로 zone z, order, mark, highest_zoneidx, alloc_flags, free_pages를 받는다
- L3851 : min = mark
- L3853 : alloc_flags 중 ALLOC_HARDER 및 ALLOC_OOM이 있으면 alloc_harder를 참으로 세팅
- L3856 : 함수 __zone_watermark_unusable_free()로 free_pages에서 unusable free page 수를 빼준다
- L3858 : ALLOC_HIGH 요청인 경우 min (watermark)를 반으로 줄여준다
- L3861 : alloc_harder가 참인 경우 ALLOC_OOM 이면 min (watermark)를 다시 반으로 감소시키고, 아니면 25% 감소시킨다
- L3879 : free_pages가 min + z->lowmem_reserve[highest_zoneidx] 보다 크지 않으면 false 리턴
- L3883 : order == 0 이면 true 리턴
- L3887 : order != 0 이면 요청된 order부터 MAX_ORDER 이하로 루프를 돌면서 watermark 체크 수행
- L3888 : area = z->free_area[order]를 가져온다
- L3891 : area에 free page가 없으면 continue하여 상위 order로 이동
- L3894 : migrate type에 대해서 루프를 돌면서 area->freelist[migrate_type]이 empty가 아닌 것이 있으면 true 리턴
- L3899 : CONFIG_CMA = y이고, alloc_flags에 ALLOC_CMA가 있고, area->freelist[MIGRATE_CMA]가 empty가 아니면 true를 리턴한다
- L3905 : alloc_harder가 true이고 area->freelist[MIGRATE_HIGHATOMIC]이 empty가 아니면 true를 리턴한다
- L3908 : 아니면 false를 리턴한다

번호 제목 글쓴이 날짜 조회 수
공지 [공지] 스터디 정리 노트 공간입니다. woos 2016.05.14 627
128 [커널 17차] 94주차 ㅇㅇㅇ 2022.06.19 80
127 [커널 18차] 56주차 kkr 2022.06.18 71
126 [커널 17차] 92~93주차 ㅇㅇㅇ 2022.06.11 93
125 [커널 18차] 54주차 kkr 2022.06.04 82
124 [커널 19차] 3주차 리턴 2022.06.04 217
123 [커널 18차] 53주차 kkr 2022.05.29 93
122 [커널 17차] 91주차 ㅇㅇㅇ 2022.05.28 64
121 [커널 19차] 2주차 리턴 2022.05.28 169
120 [커널 17차] 90주차 ㅇㅇㅇ 2022.05.22 149
119 [커널 18차] 52주차 kkr 2022.05.21 124
118 [커널 19차] 1주차 리턴 2022.05.16 456
117 [커널 17차] 89주차 ㅇㅇㅇ 2022.05.15 65
116 [커널 18차] 51주차 kkr 2022.05.14 159
115 [커널 18차] 50주차 kkr 2022.05.10 210
114 [커널 17차] 88주차 ㅇㅇㅇ 2022.05.08 101
113 [커널 19차] 0주차 - 오리엔테이션 리턴 2022.05.07 600
» [커널 17차] 86~87주차 ㅇㅇㅇ 2022.04.30 101
111 [커널 17차] 84~85주차 JSYoo5B 2022.04.16 86
110 [커널 17차] 83주차 ㅇㅇㅇ 2022.04.03 92
109 [커널 17차] 82주차 ㅇㅇㅇ 2022.03.27 65
XE Login