[커널 17차] 97~98주차

2022.07.24 01:17

ㅇㅇㅇ 조회 수:52

shrink_lruvec()에서 nr[]값 조정하여 루프돌리는 부분 (L:2876)

 

cost : anon/file cost, cost 낮은거 우선
swappiness : 높으면 anon 우선
lurvec_size : size 큰 놈을 더 많이

 

anon :    100 ->     30     -> 70% -> nr[anon] = 0
file :        200 ->     30     -> 200 * 30% = 60 -> nr[file] = 60 - 30 = 30

 

Reclaim 슬라이드
- https://lpc.events/event/11/contributions/896/attachments/793/1493/slides-r2.pdf

 

문C블로그
- http://jake.dothome.co.kr/lru-lists-pagevecs/

 

vmscan: make mapped executable pages the first class citizen
- https://github.com/torvalds/linux/commit/8cab4754d24a0f2e05920170c845bd84472814c6

 

SwapCache & SwapBacked
- https://www.kernel.org/doc/html/latest/admin-guide/mm/pagemap.html?highlight=kpageflags


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()를 수행하고 그 결과를 리턴한다

 

shrink_active_list()
- 인자로 nr_to_scan, lruvec, scan control sc, lru type을 받는다

 

- L2351 : 함수 내부에서 사용할 list head l_hold, l_active, l_inactive를 선언한다
- L2360 : 함수 lru_add_drain()으로 lruvec에 대한 pcpu 자료구조인 pagevec을 lruvec으로 flush한다
- L2362 : spinlock lruvec->lru_lock을 잡는다
- L2364 : 함수 isolate_lru_pages()를 이용해서 lruvec에서 l_hold로 page들을 이동시킨다. 이 때 이동시킨 페이지 수는 nr_taken에 반환된다. 이 때 함수 내에서 scan된 page 수도 nr_scanned에 업데이트된다
- L2367 : 함수 __mod_node_page_state로 현재 isolation된 page수를 accounting한다
- L2369 : Global reclaim이면 함수 __count_vm_events()로 PGREFILL 이벤트에 nr_scanned 만큼 더한다
- L2371 : 함수 __count_memcg_events()로 현재 lruvec의 memcg에서 PGREFILL 이벤트가 nr_scanned 만큼 발생했음을 기록한다
- L2373 : spinlock lruvec->lru_lock을 푼다
- L2375 : list head l_hold가 빌 때까지 while 문을 반복한다
- L2376 : 함수 cond_resched()로 우선 순위 높은 task에 양보
- L2377 : l_hold의 tail에 있는 페이지를 가져온다
- L2378 : page를 l_hold에서 제거한다
- L2380 : 함수 page_evictable()로 page가 evictable인지 판별하고 아니면 함수 putback_lru_page()로 다시 lru list로 보내고 continue
- L2385 : buffer_heads_over_limit = 1이고 page가 private로 설정되어 있고 (함수 page_has_private()) 함수 trylock_page()로 page lock을 얻는데 성공하면 함수 try_to_release_page()로 page를 버디 시스템으로 되돌리고 함수 unlock_page()로 unlock한다
- L2393 : page가 reference된 경우 페이지가 코드 영역이고 파일(즉 실행 파일)이면 l_active에 페이지를 추가하고 continue
- L2411 : page가 reference되지 않은 경우 ClearPageActive() 수행
- L2412 : SetPageWorkingset()으로 page의 working set 설정
- L2413 : 현 page를 l_inactive에 추가한다

 

- L2419 : spinlock lruvec->lru_lock을 잡는다
- L2421 : 함수 move_pages_to_lru() l_active, l_inactive에 있는 페이지들을 각각 알맞는 lruvec으로 이동시킨다
- L2424 : 함수 list_splice()로 l_inactive에 있는 페이지 중 free 가능한 페이지들을 l_active로 이동시킨다
- L2426 : accounting 수행
- L2430 : spinlock lruvec->lru_lock을 푼다
- L2432 : mem_cgroup_uncharge_list()로 l_active 관련 memcg 처리 수행
- L2433 : 함수 free_unref_page_list() l_active에 있던 free 가능한 페이지를 버디로 반환한다
- L2434 : trace 관련 처리 수행 후 리턴


lru_add_drain()
- 함수 lru_add_drain_cpu()로 현재 cpu의 pagevec을 lruvec으로 drain한다

 

lru_add_drain_cpu()
- L594 : percpu pagevec lru_pvecs.lru_add를 가져온다
- L596 : pagevec에 page가 존재하면 __pagevec_lru_add() 함수로 pagevec을 flush한다 <— 진행 중

 

__pagevec_lru_add()
- 인자로 pagevec을 받아서 각 page들의 refcount를 감소시킨다. 그리고 pagevec에서 제거하고 lruvec으로 보낸다
- 만약 refcount == 0이 되어버리면 사용하고 있지 않는 page이므로 버디로 반환한다
- 이유는 lruvec에서 pagevec으로 올 때 refcount를 1씩 증가시켰기 때문으로 판단됨

 

- L1048 : 루프를 돌면서 pagevec의 각 page들을 처리한다
- L1049 : pagevec의 page를 가져온다
- L1051 : 현재 page의 lruvec lock을 잡고 동시에 IRQ disable & save flags를 수행한다. 현 page의 lruvec과 이전 페이지의 lruvec이 같으면 LRU lock 및 IRQ 처리를 하지 않고 이전 lruvec을 그대로 사용한다.
- L1052 : 함수 __pagevec_lru_add_fn()로 pagevec에서 lruvec으로 page를 flush한다
- L1054 : 마지막 페이지의 lruvec에 대해 lruvec lock을 풀고 IRQ enable 및 flags restore를 수행한다
- L1056 : 함수 release_pages()로 pagevec의 page들에 대한 refcount를 1씩 감소시키고 0이 된 페이지들을 버디로 반환한다 <— 진행 중
- L1057 : pagevec의 page 수를 0으로 초기화한다

 

__pagevec_lru_add_fn()
- 인자로 page와 lruvec을 받는다
- L990 : 함수 TestClearPageUnevictable()로 page가 원래 unevictable이었는지 기록하고 unevictable flag는 clear한다
- L991 : 현재 transparent huge page table을 사용하고 있고, 현재 page가 head이면 512개의 페이지를 처리한다. 아니면 1개 페이지를 처리
- L993 : 현재 page가 PageLRU 플래그가 set되어 있으면 버그
- L1021 : 현 page에 대해 PageLRU 플래그를 set한다
- L1022 : 메모리 배리어를 수행하여 위 PageLRU set과 아래 page_evictable() 함수의 위치가 바뀌지 않도록 한다
- L1022에 메모리 배리어가 없을 경우 다음의 경우 문제가 된다

 

1번 CPU에서 __pagevec_lru_add_fn()을 수행
- 배리어가 있으면 SetPageLRU() 다음에 page_evictable() 수행

 

2번 CPU에서 clear_page_mlock()을 수행
- 항상 TestClearPageMlocked() 다음에 TestClearPageLRU() 수행

 

—> 이 경우엔 2번 CPU에서 TestClearPageLRU() 수행이 실패하면 1번 CPU에서 SetPageLRU() 수행 전이므로, 다음 순서대로 진행됨
- TestClearPageMlocked() —> TestClearPageLRU() —> SetPageLRU() —> page_evictable()
- 따라서 항상 evictable list에 페이지가 들어간다

 

그러나 배리어가 없으면 1번 CPU에서 page_evictable() 이후에 SetPageLRU() 수행 가능
따라서 page_evictable() —> TestClearPageMlocked() —> TestClearPageLRU() —> SetPageLRU() 수행 가능
- 이 경우 page는 unevictable list로 잘 못 들어간다

 

- L1024 : 현 page가 evictable이고 동시에 was_evictable이었으면 UNEVICTABLE_PGRESCUED 이벤트 통계 처리
- L1028 : 현 page가 unevictable이면 ClearPageActive(), SetPageUnevictable()를 수행하고 was_evictable이 아니면 UNEVICTABLE_PGCULLED 이벤트 통계 처리
- L1034 : 함수 add_page_to_lru_list() 수행
- L1035 : trace 관련 처리 후 리턴

 

smp_mb__after_atomic()
- arm64에서는 그냥 dmb(ish)임

 

page_evictable()
- 인자로 page를 받는다
- L82 : rcu_read_lock()
- L83 : page의 Mlocked bit가 0이고 page->mapping이 AS_UNEVICTABLE (ramdisk, SHM_LOCK 등)이 아니면 true. 아니면 false
- L84 : rcu_read_unlock()
- L85 : true/false 리턴

 

add_page_to_lru_list()
- 인자로 page와 lruvec을 받는다
- L85 : 현 page에 맞는 lru type을 가져온다
- L87 : Target lruvec size에 대한 통계값 업데이트
- L88 : lruvec->list[lru]에 page를 추가한다

 

put_page()
- release_pages()의 single page 버전
- 인자로 page를 받는다
- L1233 : page의 compound head를 구한다
- L1241 : page가 zone device page이면 따로 처리한다
- L1246 : page refcount를 1 감소시키고 0이면 사용하는 곳이 없으므로 __put_page()로 페이지를 버디로 반환한다

 

__put_page()
- L116 : page가 zone device page이면 따로 처리한다
- L126 : page가 compound page이면 __put_compound_page() 함수를 이용해서 버디로 반환한다
- L128 : page가 compound page가 아니면 __put_single_page() 함수를 이용해서 버디로 반환한다

 

__put_compound_page()
- L109 : page가 hugetlbfs page가 아니면 함수 __page_cache_release()를 수행한다
- L111 : 함수 destroy_compound_page()로 page의 destructor를 호출하여 페이지를 반환한다
- hugetlbfs page인지는 함수 PageHuge()로 판정한다

 

PageHuge()
- 인자로 들어온 페이지가 hugetlbfs page인지 판정
- page가 compound page이면서 compound destructor가 HUGETLB_PAGE_DTOR로 설정되어 있어야 한다

 

__page_cache_release()
- L82 : page의 PageLRU가 1이면, lruvec lock을 잡고 page를 lruvec에서 제거, PageLRU, PageActive, PageUnevictable bit를 클리어 한 후 lruvec lock을 unlock한다
- L91 : PageWaiters bit를 클리어한다

 

__put_single_page()
- 인자로 page를 받는다
- L96 : __page_cache_release()로 page를 lruvec에서 제거하고 PageLRU, PageActive, PageUnevictable, PageWaiters bit를 클리어한다
- L97 : mem_cgroup_uncharge()로 page에 대한 memcg 처리를 한다
- L98 : free_unref_page()로 0-order page를 대해 버디로 반환한다

 

free_unref_page()
- pcp page를 free하는 함수
- 인자로 page와 order를 받는다
- L3393 : page의 pfn을 구한다
- L3396 : free_unref_page_prepare()로 free 준비 작업 수행하고 실패하면 리턴
- L3406 : page->index에 기록된 pageblock migrate type을 가져온다
- L3407 : migrate type이 unmovable, movable, reclaimable 중의 하나가 아니면 isolate type인 경우 free_one_page() 함수로 반환하고 리턴한다. 아니면 migrate type을 movable로 변경한다
- L3416 : free_unref_page_commit()로 pcp page를 free 수행한다

 

free_unref_page_prepare()
- 인자로 page, pfn, order를 받는다
- L3317 : 함수 free_pcp_prepare()로 free 준비 작업을 수행하고 실패하면 false 리턴
- L3320 : page가 속한 pageblock의 migrate type을 가져온다
- L3321 : page->index에 위에서 가져온 pageblock migrate type을 기록한다
- L3322 : true를 리턴한다

 

free_pcp_prepare()
- 함수 free_pages_prepare()를 호출하여 free 준비 작업을 수행한다
- PageHWPoison, compound page order 검증, bad page 검증 등을 수행

 

free_one_page()
- 인자로 zone, page, pfn, order, migrate type, fpi flags를 받는다
- L1552 : zone->lock을 잡는다
- L1553 : zone의 pageblock 중 isolate migrate type이 있거나 migrate type이 isolate이면 migrate type을 sparsemem의 usemap 정보로부터 정확한 migrate type을 받아온다
- L1557 : 함수 __free_one_page()로 page를 버디로 반환한다
- L1558 : zone->lock을 푼다

 

free_unref_page_commit()
- page를 버디 시스템의 pcp cache인 pageset에 반환한다
- 인자로 page, pfn, migrate type, order를 받는다
- L3369 : page의 zone을 가져온다
- L3375 : zone->per_cpu_pageset을 pcp로 가져온다
- L3376 : migrate type과 order로부터 pcp->list의 index를 구한다
- L3377 : pcp->list[index]에 page를 추가한다
- L3378 : pcp->count를 1 << order 만큼 증가시킨다
- L3379 : 함수 nr_pcp_high()로 현재 pcp에 page 수가 많은지 판단하는 기준점인 high를 구한다
- L3380 : pcp->count가 high 보다 같거나 많으면 free_pcppages_bulk() 함수로 pcp로부터 일정 수의 page를 버디로 반환한다. 반환하는 페이지 수는 함수 nr_pcp_free()로 구한다

 

nr_pcp_high()
- 인자로 per_cpu_pages pcp, zone을 받는다
- L3351 : pcp->high를 가져온다
- L3356 : zone->flags의 ZONE_RECLAIM_ACTIVE이 0이면 high를 리턴한다
- L3363 : 아니면 pcp->batch << 2와 high 중 낮은 값을 리턴한다

 

 nr_pcp_free()
- 인자로 pcp, high, batch를 받는다
- L3330 : high < batch 이면 1을 리턴한다
- L3334 : batch를 min_nr_free, high를 max_nr_free로 설정한다
- L3341 : batch << pcp->free_factor
- L3342 : batch < max_nr_free이면 pcp->free_factor를 1 증가시킨다
- L3344 : batch를 min_nr_free와 max_nr_free 사이로 clamp한다
- L3346 : batch를 리턴한다

 

release_pages()
- 인자로 pages 배열과 page 수 nr을 받는다

- L894 : 페이지 수만큼 루프를 돌면서 페이지를 반환한다
- L902 : lruvec lock 및 IRQ disable을 너무 오래 잡고 있으면 안되므로 32번 루프를 돌 동안 같은 lruvec lock을 잡고 있었으면 일단 푼다
- L907 : page의 compound head를 가져온다
- L908 : huge zero page이면 skip
- L911 : page가 zone device이면 따로 처리
- L931 : page의 refcount를 1 감소시키고 그 결과가 1 이상이면 여전히 사용중인 페이지이므로 skip. 0이면 반환을 위해 아래로 진입
- L934 : page가 compound이면 lock 및 IRQ disable을 풀고 __put_compound_page()로 반환, 이후 continue
- L943 : page의 PageLRU == 1이면 if 문 진입
- L946 : lruvec lock 및 IRQ disable을 건다. 이미 걸려 있으면 통과
- L948 : lruvec이 달라졌으면 lock batch를 0으로 리셋
- L951 : page를 lruvec에서 제거하고 PageLRU, PageActive, PageUnevictable를 클리어한다
- L955 : PageWaiters bit를 클리어한다
- L957 : page를 local list head pages_to_free에 넣는다
- L959 : lruvec lock 및 IRQ disable을 푼다
- L962 : mem_cgroup_uncharge_list()로 pages_to_free에 대한 memcg 처리를 한다
- L963 : free_unref_page_list()로 pages_to_free에 있는 page들을 버디로 반환한다

 

HugeTLB Pages
- https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html
- 사용하기 위해서는 사용자는 mmap()을 사용하고 admin이 hugetlbfs type의 file system을 마운트해주어야 함
- 마운트하면 /mnt/huge 디렉토리가 생기고 이 폴더의 파일은 모두 huge page를 사용하게 됨
- 아니면 mmap()으로 MAP_HUGETLB를 쓰면 됨
- 이를 위해서는 libhugetlbfs를 이용해서 app을 link해야 함
- https://github.com/libhugetlbfs/libhugetlbfs/blob/master/HOWTO
- https://access.redhat.com/documentation/ko-kr/red_hat_enterprise_linux/6/html/performance_tuning_guide/main-memory#s-memory-tlb

 

Transparent Huge Pages
- Huge page 사용을 자동화한 것으로 현재는 heap/stack과 같은 anonymous 메모리에만 적용됨
- https://access.redhat.com/documentation/ko-kr/red_hat_enterprise_linux/6/html/performance_tuning_guide/s-memory-transhuge
- DB와 같이 sparse한 access가 필요한 app에서는 사용하면 성능 저하됨
- https://www.mongodb.com/docs/manual/tutorial/transparent-huge-pages/

 

__free_pages()
- Head page만 해제되고 tail들은 해제되지 않은 경우 처리하는 코드 존재
- https://github.com/torvalds/linux/commit/e320d3012d25b1fb5f3df4edb7bd44a1c362ec10

번호 제목 글쓴이 날짜 조회 수
공지 [공지] 스터디 정리 노트 공간입니다. 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
» [커널 17차] 97~98주차 ㅇㅇㅇ 2022.07.24 52
138 [커널 18차] 61주차 kkr 2022.07.23 113
137 [커널 18차] 60주차 kkr 2022.07.16 129
136 [커널 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