[커널 17차] 95~96주차

2022.07.10 01:08

ㅇㅇㅇ 조회 수:105

do_try_to_free_pages()
- 인자로 zonelist, scan control sc를 받는다
- L3390 : global reclaim인 경우 ALLOCSTALL 이벤트 카운트 증가
- L3393 : sc->nr_to_reclaim만큼 페이지 reclaim되거나 sc->compaction_ready가 될 때까지 sc->priority를 감소시키면서 reclaim을 수행한다.
- L3394 : 함수 vmpressure_prio()로 메모리 압력을 계산해서 업데이트한다
- L3396 : sc->nr_scanned = 0으로 초기화
- L3397 : 함수 shrink_zones()로 reclaim 수행
- L3399 : reclaim 결과 목표치 달성하면 break
- L3402 : compaction 가능한 경우 break
- L3409 : priority가 9 이하로 떨어지면 reclaim시 page write 가능하도록 설정
- L3411 : priority 1씩 감소시키면서 반복
- L3414 : zonelist에 대해서 루프 수행
- L3416 : 동일 노드면 다음 zone으로 skip
- L3418 : 현재 노드 기록
- L3420 : memcg 데이터에서 lruvec 데이터로 refault 데이터를 카피한다. 함수 snapshot_refaults() 사용
- L3422 : global reclaim이 아닌 memcg reclaim인 경우, 해당 memcg, node의 lruvec의 LRUVEC_CONGESTED bit를 클리어한다
- L3433 : reclaim된 페이지가 있으면 reclaim 페이지 수를 리턴
- L3437 : compaction ready이면 1을 리턴
- L3449 : 여기까지 오면 reclaim이 실패한 것으로, 설정을 변경하여 재시도한다. 여기서는 deactivate를 강제하여 reclaim을 재시도한다
- L3457 : memcg의 메모리 사용량이 min 이상 low 이하일 때도 reclaim 가능하게 하여 재시도한다
- L3465 : 모든 재시도 실패한 것으로 0 리턴하고 실패 선언한다

 

vmpressure_prio()
- 인자로 gfp, memcg, priority를 받아서 reclaim에 의한 메모리 압력을 accounting한다
- L333 : priority가 3 보다 크면 심각한 상황이 아니므로 그냥 리턴
- L343 : 함수 vmpressure() 메모리 압력 정보 업데이트. 인자로 tree = true, scanned = vmpressure_win, reclaimed = 0으로 준다
- vmpressure_win은 메모리 압력 측정을 위한 window 크기를 의미한다

 

vmpressure()
- 인자로 memcg, tree, scanned, reclaimed를 받아서 메모리 압력 정보에 따른 조치를 취하거나 관련 정보를 저장한다
- L247 : memcg로부터 vmpressure 구조체를 받는다
- L260 : 이 함수는 유저에게 메모리 압력 상황에 따라 help를 요청하는 것이므로, 유저가 도와줄 수 없는 경우, 즉 gfp가 highmem, movable, IO, FS 모두 해당 사항이 없으면 그냥 리턴한다
- L271 : scanned = 0이면 측정 안됨. 그냥 리턴
- L274 : tree가 참이면 유저에게 조치를 취해달라고 요청한다
- L275 : vmpressure lock을 잡는다
- L276 : vmpressure 구조체의 scanned 및 reclaimed를 업데이트하고 lock을 푼다
- L280 : scanned가 window 기준보다 작으면 그냥 리턴
- L282 : 함수 schedule_work()로 vmpressure 구초제의 work를 등록한다. 이 work는 유저 app이 등록한 것으로 보인다
- L283 : tree가 참이 아닌 경우 그냥 데이터만 업데이트
- L287 : memcg == NULL이거나 root memcg이면 그냥 나감
- L290 : 마찬가지로 vmpressure lock 잡고 scanned, reclaimed 값 업데이트함
- L293 : scanned가 window보다 작으면 unlock하고 나감
- L297 : 아니면 vmpressure scanned, reclaimed 값 0으로 초기화하고 unlock
- L300 : 함수 vmpressure_calc_level()로 scanned 대비 reclaimed 수를 계산해서 메모리 압력 수준을 구한다 (medium, low, critical)
- L302 : medium 이상이면 memcg->socket_pressure를 jiffies + HZ로 업데이트함. 무슨 의미인지는 모르겠음


shrink_zones()
- 인자로 zonelist, scan control sc를 받는다

- L3281 : buffer head를 시스템 한계보다 초과해서 사용하는 경우 sc->gfp_mask에 GFP_HIGHMEM을 추가하고 sc->reclaim_idx도 그에 따라 조정한다. 추후 L3349에서 원래 값으로 sc->gfp_mask를 복구한다. 이 이유는 user가 요청한 highmem 메모리 영역이 DMA device 같은 lowmem 영역의 메모리를 잡고 있을 수 있기 때문이다(?)
- L3287 : zonelist에 따라서 루프 수행
- L3293 : global reclaim이면 if 문 진입
- L3294 : 현재 node가 cpuset에서 허용되지 않으면 스킵
- L3307 : 요청 order가 3 초과 (costly order) 이고 reclaim 없이 compaction 수행 가능하면 sc->compaction_ready를 true로 만들고 다음 zone으로 continue. Compaction 수행 가능 여부는 함수 compaction_ready()로 판단한다
- L3320 : 이미 현재 노드에서 reclaim 수행했으면 continue로 스킵
- L3329 : memcg soft limit reclaim을 수행한다. 함수 mem_cgroup_soft_limit_reclaim()로 수행하며 memcg 구조를 알아야 해서 일단 스킵하고 이후에 분석 예정
- L3339 : if 문 끝난 이후 동일 노드면 zone 스킵
- L3341 : 현재 zone에 해당하는 node 기록하여 스킵할 때 사용
- L3342 : 함수 shrink_node()로 현 zone에 해당하는 node에 대해 reclaim을 수행한다

 

compaction_ready()
- 인자로 zone, scan control sc를 받는다
- L3237 : 함수 compaction_suitable() 현재 zone에서 compaction 가능한지 판단한다. 이 때 alloc flags는 min(0)으로 준다
- L3238 : 함수 결과가 COMPACT_SUCCESS이면 true 리턴
- L3241 : 결과가 COMPACT_SKIPPED이면 false 리턴
- L3254 : 결과가 COMPACT_CONTINUE이면 watermark를 high 기준으로 compaction gap만큼 마진 둬서 page 0 order 기준으로 체크한다. 성공하면 true 리턴, 실패하면 false 리턴한다. compaction gap은 원래 order size의 2배이다

 

compaction_suitable()
- 인자로 zone, order, alloc flags, highest zone idx를 받아서 해당 zone이 compaction이 가능한지 조사해서 리턴한다
- L2205 : 함수 __compaction_suitable()로 compaction 가능한지 계산한다
- L2223 : 결과가 COMPACT_CONTINUE인데 order가 costly order = 3보다 크면 fragmenration 정도를 판단하고 그 정도가 심하지 않은 경우 COMPACT_SKIPPED을 리턴한다
- L2233 : 그 외의 경우 결과를 그대로 리턴한다

 

__compaction_suitable()
- 인자로 zone, order, alloc flags, highest zone idx, watermark target을 받는다

- L2155 : 유저의 command로 compaction을 하고 있는 경우 그냥 COMPACT_CONTINUE 리턴
- L2163 : alloc flags에 해당하는 현재 zone의 watermark를 계산한다
- L2163 : 계산한 watermark로 현재 요청 order 만족이 되는지 판단하고 되면 COMPACT_SUCCESS 리턴
- L2181 : 안되면 order가 costly order = 3보다 크면 watermark를 low로 아니면 min으로 변경
- L2183 : watermark에 compact gap 추가 (order size 2배)
- L2184 : order 0으로 watermark 체크해서 안되면 COMPACT_SKIPPED 리턴하고 되면 COMPACT_CONTINUE 리턴

 

shrink_node()
- 인자로 node 정보 pgdat과 reclaim 설정값 scan_control sc를 받아 node reclaim을 수행한다

- L3044 : 함수 mem_cgroup_flush_stats()로 mem cgroup lurvec 정보들을 flush한다
- L3046 : sc->nr 메모리를 0으로 memset
- L3048 : sc->nr_reclaimed와 sc->nr_scanned를 로컬 변수로 가져온다
- L3054 : lru_lock을 걸고 memcg의 lruvec anon cost 및 file cost를 sc->anon_cost, sc->file_cost로 가져온다. 이후 lru lock을 해제한다
- L3063 : sc에 force_deactivate가 설정되어 있지 않으면 if 문 진입
- L3066 : refault가 발생하면 새로운 workingset이 설정되기 때문에 새 workingset에 속하지 않는 기존 stale active page들을 deactivate 시켜야 한다. 따라서 sc->may_deactivate를 DEACTIVATE_ANON/DEACTIVATE_FILE로 설정한다. 또는 inactive list가 너무 작으면 sc->may_deactivate를 DEACTIVATE_ANON/DEACTIVATE_FILE로 설정한다. refault도 아니고 inactive list가 작은 것도 아니면 DEACTIVATE_ANON/DEACTIVATE_FILE를 각각 클리어한다
- L3086 : sc에 force_deactivate가 설정되어 있으면 sc->may_deactivate를 DEACTIVATE_ANON/DEACTIVATE_FILE로 설정한다
- L3094 : file inactive list가 sc->priority에 견주어 크고 (file >> priority), DEACTIVATE_FILE이 클리어되어 있으면 sc->cache_trim_mode를 1로 설정한다. 아니면 0으로 설정한다
- L3109 : sc->target_mem_cgroup이 NULL이면 if문 진입
- L3110 : node의 free page와 file page 수를 계산하고 각 zone의 high watermark 총합을 계산한다. 또한 node의 inactive anon page수를 가져온다. 그리고 file + free page 수가 high watermark 총합보다 같거나 작고, DEACTIVATE_ANON가 클리어 되어 있고 anon inactive page 수가 priority에 비해 크면 sc->file_is_tiny를 set한다
- L3139 : shrink_node_memcgs() 함수로 해당 노드의 모든 memcg에 대해 reclaim을 수행한다 <— 분석 중

 

shrink_node_memcgs()
- 인자로 노드 정보 pgdat, scan control sc를 받아서 해당 노드의 모든 memcg에 대해 reclaim을 수행한다

- L2974 : sc->target_mem_cgroup에서 target_memcg를 가져온다
- L2977 : DFS로 target_memcg에서 시작해서 모든 child memcg에 대해 루프를 돈다
- L2979 : 현 노드/현 memcg에 대한 lruvec을 가져온다
- L2989 : 함수 cond_resched()으로 우선 순위 높은 task 있으면 양보
- L2991 : 함수 mem_cgroup_calculate_protection()로 현 memcg의 memcg->memory.emin, memcg->memory.elow를 계산한다
- L2993 : memcg memory가 emin보다 작으면 reclaim하지 않고 다음 memcg로 continue
- L2999 : memcg memory가 elow보다 작으면 sc->memcg_low_reclaim이 설정되어 있으면 reclaim하고 아니면 continue한다. 이 때 sc->memcg_low_skipped = 1로 로깅한다
- L3016 : 함수 shrink_lruvec()을 이용하여 reclaim 수행
- L3018 : 함수 shrink_slab()으로 reclaim 수행
- L3022 : reclaim 결과에 따라 함수 vmpressure() reclaim 효율을 업데이트한다
- L3026 : 다음 memcg로 이동한다

 

mem_cgroup_calculate_protection()
- 각 memcg의 emin/elow를 현재 상황에 따라 계산한다
- 함수 effective_protection()를 이용하여 남는 것은 sibling끼리 분배하고 모자라면 cap하여 분배한다

 

shrink_lruvec()
- 인자로 lruvec, scan control sc를 받는다

- L2797 : 함수 get_scan_count()로 현재 lruvec의 scan 값을 계산한다
- L2800 : get_scan_count()에서 계산한 scan값을 지역 변수 targets[]으로 복사
- L2813 : global & direct reclaim이고 sc->priority가 default이면 scan_adjusted를 true로 함 —> 스캔 요청된 만큼 reclaim 강제
- L2816 : 함수 blk_start_plug()로 여러 개의 IO를 요청할 것임을 알림
- L2817 : inactive anon / active file / inactive file의 scan 개수가 0이 될 때까지 while 루프 수행 (nr[])
- L2822 : 각 lru (active anon/inactive anon/active file/inactive file)에 대해서 함수 shrink_list()를 이용해서 reclaim을 수행한다. 이 때 수행할 scan 개수는 nr[lru]와 SWAP_CLUSTER_MAX = 32 중 작은 것으로 하고, 그 수만큼 nr[lru]를 감소시킨다. nr_reclaimed에 누적된 reclaim page 수를 기록한다
- L2832 : 더 높은 우선 순위를 가진 task에게 양보한다
- L2834 : reclaim된 페이지 수가 reclaim해야 할 페이지 수보다 작거나 scan_adjusted가 true이면 continue로 다시 while 문 수행 (계속 reclaim)
- L2844 : file과 anon에서 scan해야할 페이지 수를 계산한다
- L2853 : file이나 anon에서 scan해야 할 페이지 수가 0이면 while문에서 나간다
- L2856 : file과 anon 중 더 많이 스캔한 것을 골라서 scan target 대비 실제 scan한 비율을 계산하고, nr 값들을 0으로 만든다
- L2876 : 더 적게 스캔한 것의 nr 값을 위에서 계산한 scan 비율을 이용해서 조정하고, scan_adjusted을 true로 만들어서 다시 while 문을 수행한다. 결국 새로 조정된 nr값이 0이 될 때까지 reclaim하게 된다
- L2888 : blk_finish_plug() 함수로 IO 요청이 종료됨을 알림
- L2889 : sc->nr_reclaimed에 reclaim된 페이지 수를 누적한다
- L2895 : anon page를 evict할 수 있고 (함수 can_age_anon_pages()로 판단), LRU inactive anon list가 작으면 함수 shrink_active_list()로 active anon list에 대해서 SWAP_CLUSTER_MAX = 32 페이지 만큼 reclaim을 수행한다

 

get_scan_count()
- 인자로 lruvec, sc, nr을 받아서 scan을 수행할 각 페이지 수를 계산한다
- nr[0] : anon inactive pages to scan
- nr[1] : anon active pages to scan
- nr[2] : page inactive pages to scan
- nr[3] : page active pages to scan

- L2580 : memcg의 swappiness를 가져온다
- L2588 : sc->may_swap이 false이거나 현 memcg/node가 anon page를 reclaim할 수 없는 경우 scan_balance를 SCAN_FILE로 놓고 out으로 이동한다
- L2600 : sc->target_mem_cgroup이 있고 memcg swappiness가 0이면 scan_balance를 SCAN_FILE로 놓고 out으로 이동한다
- L2610 : sc->priority == 0이고 swappniess true이면 OOM에 가까운 상황이므로 scan_balance = SCAN_EQUAL으로 놓고 out으로 이동한다
- L2618 : sc->file_is_tiny이면 scan_balance = SCAN_ANON으로 놓고 out으로 이동한다
- L2627 : sc->cache_trim_mode이면 scan_balance = SCAN_FILE으로 놓고 out으로 이동한다
- L2632 : scan_balance = SCAN_FRACT로 세팅
- L2648 : anon 및 file page의 pressure를 계산한다. pressure는 cost의 역수에 비례한다.

 

anon cost = 2*sc->anon_cost + sc->file_cost
file_cost = sc->anon_cost + 2*sc->file_cost
total cost = 3*sc->anon_cost + 3*sc->file_cost
anon pressure = swappiness * (total_cost + 1) / (anon_cost + 1)
file pressure = (200 - swappiness) * (total_cost + 1) / (file_cost + 1)

 

- L2663 : anon inactive, active, file inactive, active에 대해서 루프를 돈다
- L2664 : 현재 lru가 file lru인지 기록
- L2669 : 현 lru에 속하는 페이지 총합을 구함 (존 별 page의 합)
- L2670 : 함수 mem_cgroup_protection()로 현 memcg의 emin, elow를 가져온다
- L2673 : min/low가 설정된 경우 if 문 진입
- L2703 : 현 memcg의 memory usage 양을 가져온다
- L2707 : sc->memcg_low_reclaim = false이고 low > min이면 protection을 low로 설정하고 sc->memcg_low_skipped = 1로 만든다. 아니면 protection을 min으로 설정한다
- L2715 : memcg memory와 protection 중 큰 값을 cgroup_size로 설정
- L2717 : scan 크기를 lruvec_size에서 protection / (cgroup_size + 1) 비율 만큼 제외한 값으로 설정한다
- L2725 : scan 크기를 SWAP_CLUSTER_MAX = 32보다 작으면 32로 설정
- L2727 : min/low 설정이 없으면 scan 크기를 lruvec_size 전체로 설정한다
- L2730 : scan = scan >> sc->priority
- L2736 : scan == 0이고 memcg가 online이 아니면 scan을 lruvec_size와 SWAP_CLUSTER_MAX = 32 중 작은 값으로 한다
- L2739 : scan_balance 값을 본다
- L2740 : scan_balance == SCAN_EQUAL이면 nr[lru] = scan
- L2743 : scan_balance == SCAN_FRACT이면 위에서 계산한 pressure에 비례해서 scan값을 설정한다

 

scan = scan * pressure / sum of pressure

 

- L2756 :  scan_balance == SCAN_FILE 또는 SCAN_ANON이면 type에 안맞는 lruvec인 경우 scan을 0으로 조정한다
- L2767 : nr[lru] = scan 값을 할당

 

scan 계산에서 offline memcg인 경우에만 roundup 하는 이유
- https://github.com/torvalds/linux/commit/76073c646f5f4999d763f471df9e38a5a912d70d
- 원래와 behavior가 바뀌어서 롤백하되 offline memory는 최대한 회수하는 것이 맞으므로 유지

 

blk_start_plug()
- 현재 caller가 여러 개의 IO request를 batch로 전송한다는 것을 block layer에 얄려줌
- block layer는 가급적 blk_finish_plug() 끝난 후 쌓여있는 IO를 한번에 전송하려하나 꼭 보장되는 것은 아님

scan 비율 조정하면서 reclaim 수행하는 패치
- https://github.com/torvalds/linux/commit/e82e0561dae9f3ae5a21fc2d3d3ccbe69d90be46

 

shrink_list()
- 인자로 lru, nr_to_scan, lruvec, scan_control sc를 받는다

- L2500 : lru가 active lru이면 sc->may_deactivate가 anon/file deactivate가 설정되어 있으면 shrink_active_list() 함수를 수행하고 0을 리턴한다. sc->may_deactivate가 설정 안되어 있으면 sc->may_deactivate = 1 설정하고 0을 리턴한다
- L2508 : lru가 active가 아니면 함수 shrink_inactive_list()를 수행하고 그 결과를 리턴한다


관련 링크
https://github.com/torvalds/linux/commit/aa48e47e3906
http://jake.dothome.co.kr/zonned-allocator-compaction/
https://pr0gr4m.tistory.com/entry/Linux-Kernel-5-Block-Device-Driver-Basic-Concept
https://scslab-intern.gitbooks.io/linux-kernel-hacking/content/chapter14.html
https://www.linuxjournal.com/article/7539
https://stackoverflow.com/questions/10828294/c-and-c-partial-initialization-of-automatic-structure

https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt

http://jake.dothome.co.kr/vmpressure/
https://github.com/torvalds/linux/commit/8e8ae645249b85c8ed6c178557f8db8613a6bcc7

https://lwn.net/Articles/897536/

번호 제목 글쓴이 날짜 조회 수
공지 [공지] 스터디 정리 노트 공간입니다. woos 2016.05.14 626
148 [커널 17차] 103주차 ㅇㅇㅇ 2022.08.28 35
147 [커널 18차] 66주차 kkr 2022.08.27 76
146 [커널 17차] 101~102주차 ㅇㅇㅇ 2022.08.21 47
145 [커널 18차] 65주차 kkr 2022.08.20 28
144 [커널 18차] 64주차 kkr 2022.08.13 75
143 [커널 17차] 100주차 [1] ㅇㅇㅇ 2022.08.06 100
142 [커널 18차] 63주차 kkr 2022.08.06 102
141 [커널 17차] 99주차 ㅇㅇㅇ 2022.07.31 35
140 [커널 18차] 62주차 kkr 2022.07.30 26
139 [커널 17차] 97~98주차 ㅇㅇㅇ 2022.07.24 52
138 [커널 18차] 61주차 kkr 2022.07.23 113
137 [커널 18차] 60주차 kkr 2022.07.16 129
» [커널 17차] 95~96주차 ㅇㅇㅇ 2022.07.10 105
135 [커널 18차] 59주차 kkr 2022.07.09 126
134 [커널 19차] 8주차 kanlee 2022.07.02 160
133 [커널 19차] 7주차 kanlee 2022.07.02 95
132 [커널 19차] 6주차 kanlee 2022.07.02 42
131 [커널 19차] 5주차 kanlee 2022.07.02 38
130 [커널 19차] 4주차 kanlee 2022.07.02 106
129 [커널 18차] 57주차 kkr 2022.06.25 129
XE Login