folio 하나를 active로 옮길 때마다 여러 CPU가 같은 lru_lock을 잡는다면 list 조작보다 lock 경합이 더 비싸질 수 있습니다. folio batch는 해야 할 작업을 per-CPU 배열에 모았다가 한 번에 shared LRU에 반영하는 장치입니다. CPU의 pending operation과 실제 LRU 상태가 잠시 다른 이유이기도 합니다.
배열의 크기와 memory 용량은 다릅니다
struct folio_batch는 사용 중인 항목 수와 folio pointer 배열, drain 관련 상태를 담습니다. 오래된 struct pagevec와 이어지는 이름이 사용되기도 합니다. 설명하는 고전적 구성의 PAGEVEC_SIZE=15는 pointer slot 수이지 base page 15장이라는 뜻이 아닙니다. folio가 large이면 포함한 memory 양이 더 큽니다.
이 크기는 metadata의 alignment와 cache footprint를 고려한 선택입니다. 모든 architecture와 앞으로의 version에서 동일한 byte 크기라고 외우기보다 사용하는 선언을 확인하는 편이 안전합니다. large folio나 LRU cache disabled 상태에서는 batch를 오래 쌓아 둘 이점이 작거나 의미가 없어서 즉시 drain하는 경로가 있습니다.
v6.0의 선언은 unsigned char nr, bool percpu_pvec_drained, 15개의 folio pointer로 구성됩니다. 일반적인 64-bit pointer alignment에서는 128 byte이지만, 이것만으로 모든 machine에서 cache line 두 개라고 단정할 수는 없습니다. v6.18에는 항목을 소비하는 위치를 위한 i도 추가되어 있으므로 옛 선언을 현대의 전체 struct로 제시하면 안 됩니다.
folio_batch_add()는 끝에 pointer를 넣고 남은 slot 수를 반환합니다. 반환값 0은 추가 실패가 아니라 마지막 slot까지 사용하여 이제 full이라는 뜻입니다. caller는 이미 초기화했고 공간이 있는 batch에 추가해야 합니다. batch의 pointer 순서가 의미 있는 사용자도 있고, exceptional entry가 섞이는 용도도 있으므로 모든 항목을 곧바로 dereference 가능한 folio라고 가정해서도 안 됩니다. percpu_pvec_drained는 release 과정에서 per-CPU LRU drain을 이미 수행했는지 추적하는 상태이지, 그 folio가 shared LRU에 들어갔다는 보편적인 flag가 아닙니다.
Per-CPU에도 lock이 필요합니다
CPU-local array라는 이유로 모든 context에서 lock 없이 다룰 수 있는 것은 아닙니다. local lock은 실행 중 CPU가 바뀌지 않도록 하고, 같은 CPU의 다른 context와의 경합을 제어합니다. writeback 완료에서 쓰는 rotation batch는 IRQ context와의 관계 때문에 interrupt 제어까지 포함하는 별도 local lock이 필요합니다.
addition·activation·deactivation·lazy-free 등은 operation별 batch를 사용합니다. mlock의 batch는 pointer tag로 작업 종류를 구분하는 별도 protocol을 사용하므로 일반 move_fn callback과 같다고 볼 수 없습니다.
Pending 동안 누가 folio를 살려 둘까요
producer는 folio를 batch에 넣기 전에 reference를 하나 확보합니다. shared list에 아직 들어가지 않았거나 작업이 늦어져도 object가 사라지지 않아야 하기 때문입니다. drain callback은 이 pending reference가 있는 상태에서 LRU를 수정하고, 처리가 끝나면 folios_put() 계열로 reference를 반환합니다.
callback 안에서 매번 새 reference를 얻는다고 설명하면 ownership의 시작점이 늦어집니다. 핵심은 enqueue 전에 object를 붙잡는 것입니다. batch에 있다는 사실 자체가 사용자 mapping reference를 대신하지도 않습니다.
Drain은 같은 lruvec을 묶어 처리합니다
일반 batch move helper는 항목의 PG_lru 상태를 원자적으로 다루며 callback을 실행합니다. 최초 addition callback은 아직 LRU가 아닌 folio를 받아야 하지만, 이미 있는 folio를 옮기는 callback은 다른 경로가 isolate한 non-LRU folio를 무리하게 조작하지 않습니다.
각 folio의 node·memcg에 맞는 lruvec을 찾고, 이전 항목과 같으면 이미 잡은 lock을 재사용합니다. 달라졌다면 이전 lock을 놓고 새 lruvec lock을 잡습니다. per-CPU batch 하나에 반드시 같은 lruvec 항목만 들어온다는 가정은 할 수 없습니다.
callback과 LRU flag 정리를 끝낸 뒤 shared lock을 풀고 pending reference들을 반환합니다. 마지막으로 batch를 다시 사용할 수 있도록 소비 위치와 항목 수 등 해당 version이 요구하는 상태를 정리합니다. 이 reset은 해당 batch의 local lock이 보호하는 영역에서 이뤄져야 합니다.
고전 v6.0은 folios_put(array, count) 뒤 folio_batch_init()을 명시적으로 부릅니다. v6.18은 folios_put(fbatch)가 batch 자체를 받아 소비하고 nr·i를 재사용할 수 있게 되돌리므로, 호출자의 겉모양에 init이 없다는 이유로 누락이라고 볼 수는 없습니다. 현대의 reinit은 percpu_pvec_drained까지 항상 false로 만드는 full init과 구분합니다. 현대 enqueue helper __folio_batch_add_and_move()는 reference 획득과 local lock도 안으로 모았습니다. producer wrapper와 내부 helper에서 둘 다 reference를 얻는다고 설명하지 않아야 합니다.

최초 addition도 evictable 여부를 다시 봅니다
folio_add_lru()는 새 evictable user-memory folio가 LRU 관리에 합류하는 대표적인 interface입니다. anon fault와 file cache 등의 caller가 이를 사용하지만 모든 subsystem의 allocation을 동일한 경로로 환원해서는 안 됩니다. 고전적인 callback 이름 lru_add_fn()은 v6.18에서 lru_add()로 읽어야 합니다. rotation도 예전 별도 lru_rotate 구조와 달리 v6.12·v6.18에서는 cpu_fbatches의 IRQ-protected batch에 함께 들어갑니다.
enqueue할 때의 상태와 drain 시점의 상태가 다를 수 있으므로 lru_add_fn()은 unevictable 상태를 다시 판단합니다. mapping이나 mlock 상태 때문에 회수할 수 없다면 active를 지우고 unevictable accounting으로 분류합니다. 예전에 unevictable이었지만 현재 evictable이 된 folio는 그 상태를 정리하여 올바른 list로 복귀시킵니다. 이미 LRU에 있는 folio를 최초 add로 중복 연결하는 것은 정상적인 작업이 아닙니다.
새로 queue한 folio는 drain 전 folio_mark_accessed()에 의해 active로 표시될 여지가 있습니다. addition callback에서 list family를 결정하는 이유입니다. unevictable로 분류하는 경우 mlock_count를 0으로 시작하는 것은 mlock을 해제하겠다는 뜻이 아닙니다. 이미 다른 경로가 세었는지 모르는 count를 과대 추정해 folio를 영구히 가두는 대신, 이후 reclaim의 상태 보정에 맡기는 선택입니다. PG_mlocked와 이 count, pending reference는 서로 다른 상태입니다.
Fault·page cache·putback은 서로 다른 entrance입니다
anonymous fault가 새 folio를 마련하는 경로, write-protection fault에서 CoW copy를 만드는 경로, swap fault가 resident folio를 복원하는 경로는 LRU 합류 시점을 공유할 수 있습니다. 그렇다고 fault가 날 때마다 반드시 새 folio를 LRU에 중복 추가하지는 않습니다. 이미 관리 중인 folio를 재사용하는지, 새 copy를 만들었는지에 따라 달라집니다. fork도 기존 folio를 공유하는 보통 경우와 copy를 새로 만들어야 하는 예외를 구분해야 합니다.
file cache는 filemap_add_folio()가 cache 삽입과 LRU addition을 연결하는 대표적인 경로입니다. buffered read의 cache miss, read-cache helper, readahead allocation은 이 interface로 모일 수 있습니다. file fault라고 항상 새 cache folio가 필요한 것은 아니며, 이미 page cache에 있는 folio를 PTE에 연결하는 경우에는 cache 삽입을 다시 수행하지 않습니다. private file CoW는 새 anonymous folio를 만드는 다른 경우입니다.
reclaim의 putback은 새 allocation이 아닙니다. folio_putback_lru()는 isolation reference가 살아 있는 folio를 다시 queue하고, queue가 보유할 reference를 확보한 뒤 기존 isolation reference를 내립니다. shmem 역시 일반 disk file과 backing·LRU family가 다를 수 있습니다. 이 entrance들을 구분하면 'folio_add_lru를 호출한다'는 공통점만으로 allocation, cache insertion, PTE installation, isolation 종료를 같은 사건으로 혼동하지 않을 수 있습니다.
이렇게 보면 batch는 단순 성능 최적화 이상의 protocol입니다. pending reference, local serialization, shared LRU serialization, 상태 재검사, reference 반환이 함께 있어야 안전하게 지연 실행할 수 있습니다.
Anonymous fault와 fork에서 새 LRU 항목이 생기는 조건
고전 경로를 구체적으로 따라가려면 version을 고정해야 합니다. 여기서는 Linux v6.0의 base-page 경로를 살펴보겠습니다. handle_mm_fault()에서 __handle_mm_fault()를 거쳐 handle_pte_fault()에 도달하면, 비어 있는 anonymous PTE, swap entry, present이지만 쓰기 보호된 PTE가 서로 다른 분기로 나뉩니다. hugetlb와 THP의 별도 처리를 모두 이 직선 경로에 끼워 넣을 수는 없습니다.
비어 있는 anonymous PTE는 do_anonymous_page()가 맡습니다. read fault에서 zero page를 사용할 수 있으면 공용 zero page를 연결하므로 새 private page를 LRU에 넣지 않습니다. 실제 private page를 할당하고 charge한 경로에서는 PTL 아래에서 PTE가 여전히 비어 있는지, address space가 안정적인지, userfaultfd 처리가 필요한지를 다시 확인합니다. 그 검사를 통과해 page_add_new_anon_rmap()을 수행한 뒤에야 lru_cache_add_inactive_or_unevictable()을 호출합니다. allocation 성공만으로 LRU addition이 확정되는 것은 아닙니다. v6.0 anonymous fault
present PTE의 write-protection 처리는 do_wp_page()에서 출발합니다. 기존 page를 안전하게 재사용할 수 있으면 새 LRU 항목이 필요하지 않습니다. 새 CoW page가 필요한 경우 wp_page_copy()가 새 page의 rmap과 LRU addition을 준비합니다. swap fault의 do_swap_page()도 cache hit와 새 page를 구분합니다. 동기 direct-read 분기의 새 page는 lru_cache_add()를 사용하고, KSM 때문에 별도 copy를 만드는 분기는 lru_cache_add_inactive_or_unevictable()을 사용합니다. writable PTE로 해결하지 못한 write fault는 이후 do_wp_page()로 이어질 수 있습니다. 따라서 swap fault 하나를 '항상 같은 helper로 새 page를 추가한다'고 요약하면 cache hit와 copy의 차이가 사라집니다. v6.0 CoW, v6.0 swap fault
fork에서도 보통은 기존 page를 공유합니다. page-table 복사가 필요한 VMA의 dup_mmap()은 copy_page_range()를 부르고, 일반 PTE 경로에서는 copy_p4d_range()·copy_pud_range()·copy_pmd_range()·copy_pte_range()를 거쳐 copy_present_pte()로 내려갑니다. VM_WIPEONFORK VMA는 이 복사를 건너뛰고, 빈 entry나 huge entry는 각각 별도 처리를 받습니다. present anonymous page에 대해 page_try_dup_anon_rmap()이 실패하면, parent에서 pin되었을 가능성 때문에 child용 copy를 만드는 copy_present_page()로 갑니다. 새 copy의 rmap과 LRU addition은 이 예외 경로의 작업이지 모든 fork page의 공통 작업이 아닙니다. v6.0 dup_mmap, v6.0 present PTE 복사
이 경로에서 lru_cache_add_inactive_or_unevictable()이라는 이름은 최종 list 연결이 이미 끝났다는 뜻이 아닙니다. v6.0은 (VM_LOCKED | VM_SPECIAL) 검사 결과가 정확히 VM_LOCKED인 경우 mlock_new_page()로 보내고, 나머지는 lru_cache_add()로 보냅니다. 후자는 page_folio()로 변환하여 folio_add_lru()를 호출하는 compatibility wrapper입니다. mlock 분기를 일반 addition 화살표 하나로 대체하지 않아야 합니다. v6.0 VMA 조건, v6.0 compatibility wrapper
Shmem의 새 page와 reclaim의 putback을 구별합니다
v6.0의 shmem_getpage()·shmem_fault()·shmem_read_mapping_page_gfp()는 각자 필요한 인자를 정해 shmem_getpage_gfp()를 사용합니다. 이 helper는 기존 cache entry와 swap entry도 처리합니다. 새 folio를 할당하고 shmem cache에 성공적으로 넣은 분기에서 folio_add_lru()를 호출하므로, shmem lookup마다 새 항목이 생기는 것은 아닙니다. swap-in을 위해 다른 page를 copy하는 경로의 lru_cache_add(newpage)도 단순 lookup과는 별개입니다. shmem은 file API를 쓴다는 이유만으로 일반 disk file의 LRU family로 취급할 수 없습니다. v6.0 shmem cache 삽입, v6.0 shmem swap-in copy
reclaim에서는 shrink_inactive_list()가 살아남은 folio를 move_pages_to_lru()로 돌려보냅니다. shrink_active_list() 역시 scan 뒤 active·inactive 결과를 이 helper로 옮기며, scan 중 unevictable로 확인한 folio는 별도로 putback합니다. 중요한 차이는 move_pages_to_lru()의 모든 folio가 folio_putback_lru()를 거치는 것은 아니라는 점입니다. evictable folio는 잡고 있는 lru_lock 아래에서 직접 list에 복귀하고, 더 이상 evictable이 아닌 경우 lock을 놓고 folio_putback_lru()를 호출한 다음 다시 잡습니다. reclaim_page_list()도 회수되지 않은 folio의 isolation을 끝내는 데 putback을 사용합니다. v6.0 list 복귀, v6.0 active scan, v6.0 별도 reclaim list
putback_lru_page()는 page를 folio로 바꿔 folio_putback_lru()에 넘기는 wrapper입니다. 후자는 folio_add_lru()로 pending reference를 확보한 뒤 folio_put()으로 isolation reference를 내립니다. 호출할 때는 lru_lock을 잡고 있지 않아야 하고 interrupt가 enabled여야 합니다. 이것은 기존 page의 isolation을 끝내는 protocol이지 allocation이나 cache insertion이 아닙니다. v6.0 putback의 context와 전체 동작
fault와 reclaim 외에도 같은 interface를 쓰는 subsystem이 있습니다. 예를 들어 v6.0의 THP anonymous allocation은 VMA-aware addition을 사용하고, FUSE의 page 이동은 PIPE_BUF_FLAG_LRU가 없는 새 page에만 lru_cache_add()를 호출합니다. DAMON과 MADV_PAGEOUT은 isolate 뒤 unevictable page를 putback하며, memcg의 charge 이동도 isolation을 끝낼 때 putback을 사용합니다. 이들은 각기 다른 caller 조건을 가진 사례이지 모든 subsystem의 모든 allocation을 나열한 목록은 아닙니다. THP, FUSE, DAMON, madvise, memcg
migration도 새 destination과 되돌릴 source를 구분합니다. v6.0의 __unmap_and_move()는 migration이 성공했을 때 새 page를 LRU에 추가합니다. 반면 putback_movable_pages()는 hugetlb와 non-LRU movable page를 별도로 처리하고, 일반 LRU page에 대해서 isolation accounting을 내린 뒤 putback_lru_page()로 돌려보냅니다. userfaultfd의 mfill_atomic_install_pte() 역시 cache page가 이미 LRU에 있는 보통 경우와 새로 할당된 cache page를 구분합니다. cache page라면 newly_allocated일 때만 일반 addition을 하고, anonymous page라면 새 anon rmap과 VMA-aware addition을 사용합니다. 'migration 또는 userfaultfd에서 호출된다'는 말만으로 기존 page를 중복 enqueue해도 된다는 뜻은 아닙니다. v6.0 migration의 성공 분기, v6.0 movable-page putback, v6.0 userfaultfd의 cache·anon 분기
File fault의 PTE 설치와 cache insertion은 다른 일입니다
v6.0의 file fault는 handle_pte_fault()에서 do_fault()로 들어간 뒤 read·private write·shared write 조건에 따라 do_read_fault()·do_cow_fault()·do_shared_fault()로 나뉩니다. 정상적으로 page를 얻어 PTE를 설치하는 경로는 finish_fault()와 do_set_pte()로 이어지지만, error·retry·이미 처리된 fault 등은 중간에 반환할 수 있습니다. filesystem의 vm_ops->fault callback으로 들어가는 호출도 있으므로 이 흐름을 모든 file fault에 대한 무조건적인 직접 호출열로 읽어서는 안 됩니다. v6.0 file fault 분기
특히 do_set_pte()에서 lru_cache_add_inactive_or_unevictable()을 호출하는 것은 write fault이면서 VM_SHARED가 아닌 경우입니다. 이때 설치하는 것은 private CoW로 얻은 anonymous page입니다. 일반 file page를 mapping하는 분기는 file RSS와 file rmap을 갱신하지만 여기서 cache folio를 LRU에 다시 추가하지 않습니다. present file PTE의 write-protection fault에서 do_wp_page()와 wp_page_copy()를 거쳐 새 anonymous copy를 만드는 경로도 이 구분과 일치합니다. v6.0 do_set_pte의 실제 조건
일반 file cache의 새 folio는 filemap_add_folio()가 담당합니다. __filemap_add_folio()의 삽입이 실패하면 folio의 locked flag를 되돌리고 error를 반환하며 LRU addition은 하지 않습니다. 성공하면 필요한 workingset refault 처리를 거쳐 folio_add_lru()를 호출합니다. add_to_page_cache_lru()는 page를 folio로 바꿔 이 함수에 넘기는 wrapper입니다. cache insertion, LRU enqueue, PTE installation은 서로 다른 시점입니다. v6.0 cache 삽입과 LRU
Buffered read와 readahead가 cache miss를 채우는 경로
buffered read의 대표 경로는 generic_file_read_iter()의 buffered 구간에서 filemap_read()·filemap_get_pages()로 이어집니다. batch가 비어 있으면 동기 readahead를 시도하고 다시 lookup합니다. 그래도 page가 없으면 허용된 blocking 경로에서 filemap_create_folio()가 새 folio를 넣습니다. IOCB_NOIO는 I/O를 유발하는 경로를 제한하고, IOCB_NOWAIT·IOCB_WAITQ는 해당 miss 경로에서 -EAGAIN으로 빠질 수 있습니다. cache hit의 마지막 folio에 readahead marker가 있으면 filemap_readahead()를 통해 비동기 readahead를 추진합니다. 따라서 read 호출 수와 새 LRU 항목 수는 같지 않습니다. v6.0 buffered cache lookup
별도의 read-cache interface는 read_mapping_folio()·read_cache_folio()·do_read_cache_folio()로 이어집니다. 기존 folio를 찾으면 재사용하고, miss에서 할당한 folio를 filemap_add_folio()로 넣다가 -EEXIST를 만나면 경쟁자가 넣은 entry를 다시 찾습니다. pagecache_get_page()는 __filemap_get_folio()의 compatibility wrapper이며, 그 helper가 miss에서 새 folio를 만드는지는 FGP_CREAT에 달려 있습니다. filemap_get_folio()의 flags 0과 filemap_lock_folio()의 FGP_LOCK만으로는 없는 folio를 만들지 않습니다. v6.0 read-cache miss, v6.0 FGP_CREAT 분기, v6.0 lookup wrapper
filemap_fault()는 cache hit에서는 조건에 따라 do_async_mmap_readahead()를 호출하고, miss에서는 do_sync_mmap_readahead()를 시도한 뒤 FGP_CREAT | FGP_FOR_MMAP으로 target folio를 얻습니다. 동기 mmap readahead는 VMA와 file의 조건에 따라 page_cache_sync_ra() 또는 page_cache_ra_order()를 사용합니다. 비동기 mmap readahead와 buffered filemap_readahead()는 marker 등의 조건을 확인하고 page_cache_async_ra()로 이어집니다. page 기반 interface인 page_cache_sync_readahead()·page_cache_async_readahead()는 readahead control을 구성하는 wrapper입니다. v6.0 mmap readahead, v6.0 filemap_fault, v6.0 readahead wrapper
일반 on-demand 정책은 ondemand_readahead()에서 page_cache_ra_order()로 이어지고, 새 folio를 준비하는 ra_alloc_folio()가 filemap_add_folio()를 호출합니다. sync readahead에는 강제 read 경로도 있으므로 언제나 on-demand 정책을 거친다고 단정할 수 없습니다. filesystem이 window를 늘리는 readahead_expand()는 양 끝에 필요한 page를 할당하여 add_to_page_cache_lru()로 넣습니다. 어느 쪽이든 이미 cache에 있는 entry와 allocation·insertion 실패를 처리하므로 window의 모든 index가 새 LRU 항목이 되는 것은 아닙니다. 이 경로를 이해할 때에는 'I/O를 요청한다'와 '성공적으로 cache에 삽입해 LRU에 queue한다'를 따로 보시는 것이 좋습니다. v6.0 readahead allocation과 정책, v6.0 window 확장
전체 code로 ownership과 종료 경로를 확인합니다
v6.0의 struct와 per-CPU instance 선언을 먼저 보고 enqueue·drain을 따라가시면 reference를 얻고 반환하는 책임이 분명해집니다. 이어지는 v6.18 정의에서는 합쳐진 IRQ batch와 내부 enqueue helper, batch 소비 API의 차이를 확인할 수 있습니다.
v6.0 folio_batch_move_lru
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.0, mm/swap.c:232-255.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/swap.c#L232
* License text: GPL-2.0.txt (in this directory).
*/
static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn)
{
int i;
struct lruvec *lruvec = NULL;
unsigned long flags = 0;
for (i = 0; i < folio_batch_count(fbatch); i++) {
struct folio *folio = fbatch->folios[i];
/* block memcg migration while the folio moves between lru */
if (move_fn != lru_add_fn && !folio_test_clear_lru(folio))
continue;
lruvec = folio_lruvec_relock_irqsave(folio, lruvec, &flags);
move_fn(lruvec, folio);
folio_set_lru(folio);
}
if (lruvec)
unlock_page_lruvec_irqrestore(lruvec, flags);
folios_put(fbatch->folios, folio_batch_count(fbatch));
folio_batch_init(fbatch);
}
v6.0 folio_batch_add_and_move
v6_0_folio_batch_add_and_move.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.0, mm/swap.c:257-264.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/swap.c#L257
* License text: GPL-2.0.txt (in this directory).
*/
static void folio_batch_add_and_move(struct folio_batch *fbatch,
struct folio *folio, move_fn_t move_fn)
{
if (folio_batch_add(fbatch, folio) && !folio_test_large(folio) &&
!lru_cache_disabled())
return;
folio_batch_move_lru(fbatch, move_fn);
}
v6.0 folio_add_lru
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.0, mm/swap.c:479-492.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/swap.c#L479
* License text: GPL-2.0.txt (in this directory).
*/
void folio_add_lru(struct folio *folio)
{
struct folio_batch *fbatch;
VM_BUG_ON_FOLIO(folio_test_active(folio) &&
folio_test_unevictable(folio), folio);
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
folio_get(folio);
local_lock(&cpu_fbatches.lock);
fbatch = this_cpu_ptr(&cpu_fbatches.lru_add);
folio_batch_add_and_move(fbatch, folio, lru_add_fn);
local_unlock(&cpu_fbatches.lock);
}
v6.0 lru_add_fn
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.0, mm/swap.c:192-230.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/swap.c#L192
* License text: GPL-2.0.txt (in this directory).
*/
static void lru_add_fn(struct lruvec *lruvec, struct folio *folio)
{
int was_unevictable = folio_test_clear_unevictable(folio);
long nr_pages = folio_nr_pages(folio);
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
/*
* Is an smp_mb__after_atomic() still required here, before
* folio_evictable() tests the mlocked flag, to rule out the possibility
* of stranding an evictable folio on an unevictable LRU? I think
* not, because __munlock_page() only clears the mlocked flag
* while the LRU lock is held.
*
* (That is not true of __page_cache_release(), and not necessarily
* true of release_pages(): but those only clear the mlocked flag after
* folio_put_testzero() has excluded any other users of the folio.)
*/
if (folio_evictable(folio)) {
if (was_unevictable)
__count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
} else {
folio_clear_active(folio);
folio_set_unevictable(folio);
/*
* folio->mlock_count = !!folio_test_mlocked(folio)?
* But that leaves __mlock_page() in doubt whether another
* actor has already counted the mlock or not. Err on the
* safe side, underestimate, let page reclaim fix it, rather
* than leaving a page on the unevictable LRU indefinitely.
*/
folio->mlock_count = 0;
if (!was_unevictable)
__count_vm_events(UNEVICTABLE_PGCULLED, nr_pages);
}
lruvec_add_folio(lruvec, folio);
trace_mm_lru_insertion(folio);
}
v6.0 folio_batch
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* pages. A pagevec is a multipage container which is used for that.
*/
/* Extracted unchanged from Linux v6.0, include/linux/pagevec.h:83-87.
* Complete struct definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/include/linux/pagevec.h#L83
* License text: GPL-2.0.txt (in this directory).
*/
struct folio_batch {
unsigned char nr;
bool percpu_pvec_drained;
struct folio *folios[PAGEVEC_SIZE];
};
v6.0 folio_batch_init
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* pages. A pagevec is a multipage container which is used for that.
*/
/* Extracted unchanged from Linux v6.0, include/linux/pagevec.h:100-104.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/include/linux/pagevec.h#L100
* License text: GPL-2.0.txt (in this directory).
*/
static inline void folio_batch_init(struct folio_batch *fbatch)
{
fbatch->nr = 0;
fbatch->percpu_pvec_drained = false;
}
v6.0 fbatch_space
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* pages. A pagevec is a multipage container which is used for that.
*/
/* Extracted unchanged from Linux v6.0, include/linux/pagevec.h:111-114.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/include/linux/pagevec.h#L111
* License text: GPL-2.0.txt (in this directory).
*/
static inline unsigned int fbatch_space(struct folio_batch *fbatch)
{
return PAGEVEC_SIZE - fbatch->nr;
}
v6.0 folio_batch_add
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* pages. A pagevec is a multipage container which is used for that.
*/
/* Extracted unchanged from Linux v6.0, include/linux/pagevec.h:126-131.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/include/linux/pagevec.h#L126
* License text: GPL-2.0.txt (in this directory).
*/
static inline unsigned folio_batch_add(struct folio_batch *fbatch,
struct folio *folio)
{
fbatch->folios[fbatch->nr++] = folio;
return fbatch_space(fbatch);
}
v6.0 folio_batch_release
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* pages. A pagevec is a multipage container which is used for that.
*/
/* Extracted unchanged from Linux v6.0, include/linux/pagevec.h:133-136.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/include/linux/pagevec.h#L133
* License text: GPL-2.0.txt (in this directory).
*/
static inline void folio_batch_release(struct folio_batch *fbatch)
{
pagevec_release((struct pagevec *)fbatch);
}
v6.18 folio_batch
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* folios. A folio_batch is a container which is used for that.
*/
/* Extracted unchanged from Linux v6.18, include/linux/pagevec.h:28-33.
* Complete struct definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/include/linux/pagevec.h#L28
* License text: GPL-2.0.txt (in this directory).
*/
struct folio_batch {
unsigned char nr;
unsigned char i;
bool percpu_pvec_drained;
struct folio *folios[PAGEVEC_SIZE];
};
v6.18 folio_batch_init
/* SPDX-License-Identifier: GPL-2.0 */
/*
* include/linux/pagevec.h
*
* In many places it is efficient to batch an operation up against multiple
* folios. A folio_batch is a container which is used for that.
*/
/* Extracted unchanged from Linux v6.18, include/linux/pagevec.h:41-46.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/include/linux/pagevec.h#L41
* License text: GPL-2.0.txt (in this directory).
*/
static inline void folio_batch_init(struct folio_batch *fbatch)
{
fbatch->nr = 0;
fbatch->i = 0;
fbatch->percpu_pvec_drained = false;
}
v6.18 cpu_fbatches
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.18, mm/swap.c:50-66.
* Complete struct definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/swap.c#L50
* License text: GPL-2.0.txt (in this directory).
*/
struct cpu_fbatches {
/*
* The following folio batches are grouped together because they are protected
* by disabling preemption (and interrupts remain enabled).
*/
local_lock_t lock;
struct folio_batch lru_add;
struct folio_batch lru_deactivate_file;
struct folio_batch lru_deactivate;
struct folio_batch lru_lazyfree;
#ifdef CONFIG_SMP
struct folio_batch lru_activate;
#endif
/* Protecting the following batches which require disabling interrupts */
local_lock_t lock_irq;
struct folio_batch lru_move_tail;
};
v6.18 folio_batch_move_lru
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.18, mm/swap.c:158-180.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/swap.c#L158
* License text: GPL-2.0.txt (in this directory).
*/
static void folio_batch_move_lru(struct folio_batch *fbatch, move_fn_t move_fn)
{
int i;
struct lruvec *lruvec = NULL;
unsigned long flags = 0;
for (i = 0; i < folio_batch_count(fbatch); i++) {
struct folio *folio = fbatch->folios[i];
/* block memcg migration while the folio moves between lru */
if (move_fn != lru_add && !folio_test_clear_lru(folio))
continue;
folio_lruvec_relock_irqsave(folio, &lruvec, &flags);
move_fn(lruvec, folio);
folio_set_lru(folio);
}
if (lruvec)
unlock_page_lruvec_irqrestore(lruvec, flags);
folios_put(fbatch);
}
v6.18 __folio_batch_add_and_move
v6_18___folio_batch_add_and_move.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.18, mm/swap.c:182-202.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/swap.c#L182
* License text: GPL-2.0.txt (in this directory).
*/
static void __folio_batch_add_and_move(struct folio_batch __percpu *fbatch,
struct folio *folio, move_fn_t move_fn, bool disable_irq)
{
unsigned long flags;
folio_get(folio);
if (disable_irq)
local_lock_irqsave(&cpu_fbatches.lock_irq, flags);
else
local_lock(&cpu_fbatches.lock);
if (!folio_batch_add(this_cpu_ptr(fbatch), folio) ||
!folio_may_be_lru_cached(folio) || lru_cache_disabled())
folio_batch_move_lru(this_cpu_ptr(fbatch), move_fn);
if (disable_irq)
local_unlock_irqrestore(&cpu_fbatches.lock_irq, flags);
else
local_unlock(&cpu_fbatches.lock);
}
v6.18 folio_add_lru
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.18, mm/swap.c:500-512.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/swap.c#L500
* License text: GPL-2.0.txt (in this directory).
*/
void folio_add_lru(struct folio *folio)
{
VM_BUG_ON_FOLIO(folio_test_active(folio) &&
folio_test_unevictable(folio), folio);
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
/* see the comment in lru_gen_folio_seq() */
if (lru_gen_enabled() && !folio_test_unevictable(folio) &&
lru_gen_in_fault() && !(current->flags & PF_MEMALLOC))
folio_set_active(folio);
folio_batch_add_and_move(folio, lru_add);
}
v6.18 lru_add
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.18, mm/swap.c:118-156.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/swap.c#L118
* License text: GPL-2.0.txt (in this directory).
*/
static void lru_add(struct lruvec *lruvec, struct folio *folio)
{
int was_unevictable = folio_test_clear_unevictable(folio);
long nr_pages = folio_nr_pages(folio);
VM_BUG_ON_FOLIO(folio_test_lru(folio), folio);
/*
* Is an smp_mb__after_atomic() still required here, before
* folio_evictable() tests the mlocked flag, to rule out the possibility
* of stranding an evictable folio on an unevictable LRU? I think
* not, because __munlock_folio() only clears the mlocked flag
* while the LRU lock is held.
*
* (That is not true of __page_cache_release(), and not necessarily
* true of folios_put(): but those only clear the mlocked flag after
* folio_put_testzero() has excluded any other users of the folio.)
*/
if (folio_evictable(folio)) {
if (was_unevictable)
__count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
} else {
folio_clear_active(folio);
folio_set_unevictable(folio);
/*
* folio->mlock_count = !!folio_test_mlocked(folio)?
* But that leaves __mlock_folio() in doubt whether another
* actor has already counted the mlock or not. Err on the
* safe side, underestimate, let page reclaim fix it, rather
* than leaving a page on the unevictable LRU indefinitely.
*/
folio->mlock_count = 0;
if (!was_unevictable)
__count_vm_events(UNEVICTABLE_PGCULLED, nr_pages);
}
lruvec_add_folio(lruvec, folio);
trace_mm_lru_insertion(folio);
}
v6.18 folio_may_be_lru_cached
v6_18_folio_may_be_lru_cached.c
/* SPDX-License-Identifier: GPL-2.0 */
/* Extracted unchanged from Linux v6.18, include/linux/swap.h:350-358.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.18/include/linux/swap.h#L350
* License text: GPL-2.0.txt (in this directory).
*/
static inline bool folio_may_be_lru_cached(struct folio *folio)
{
/*
* Holding PMD-sized folios in per-CPU LRU cache unbalances accounting.
* Holding small numbers of low-order mTHP folios in per-CPU LRU cache
* will be sensible, but nobody has implemented and tested that yet.
*/
return !folio_test_large(folio);
}
v6.0 cpu_batch_declarations
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Exact v6.0 mm/swap.c:49-74 declaration/macro scope.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/swap.c#L49
* License text: GPL-2.0.txt. Kernel context is required.
*/
/* Protecting only lru_rotate.fbatch which requires disabling interrupts */
struct lru_rotate {
local_lock_t lock;
struct folio_batch fbatch;
};
static DEFINE_PER_CPU(struct lru_rotate, lru_rotate) = {
.lock = INIT_LOCAL_LOCK(lock),
};
/*
* The following folio batches are grouped together because they are protected
* by disabling preemption (and interrupts remain enabled).
*/
struct cpu_fbatches {
local_lock_t lock;
struct folio_batch lru_add;
struct folio_batch lru_deactivate_file;
struct folio_batch lru_deactivate;
struct folio_batch lru_lazyfree;
#ifdef CONFIG_SMP
struct folio_batch activate;
#endif
};
static DEFINE_PER_CPU(struct cpu_fbatches, cpu_fbatches) = {
.lock = INIT_LOCAL_LOCK(lock),
};
v6.18 folio_batch_add_and_move_macro
v6_18_folio_batch_add_and_move_macro.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Exact v6.18 mm/swap.c:203-211 declaration/macro scope.
* Source: https://github.com/torvalds/linux/blob/v6.18/mm/swap.c#L203
* License text: GPL-2.0.txt. Kernel context is required.
*/
#define folio_batch_add_and_move(folio, op) \
__folio_batch_add_and_move( \
&cpu_fbatches.op, \
folio, \
op, \
offsetof(struct cpu_fbatches, op) >= \
offsetof(struct cpu_fbatches, lock_irq) \
)
v6.0 compatibility wrapper와 조건부 addition
다음 정의는 page 기반 caller가 folio interface로 합류하는 경계와, VMA-aware addition·cache insertion·putback의 실제 동작을 보여 줍니다. 위에서 설명한 fault·filesystem caller 전체를 여기에 모두 싣는 것은 아닙니다. 각 정의는 공식 v6.0의 전체 function이며 주변 kernel context가 필요한 읽기 자료입니다.
lru_cache_add 공식 source · 전체 code · GPL-2.0
/*
* Compatibility functions which bloat the callers too much to make inline.
* All of the callers of these functions should be converted to use folios
* eventually.
*/
/* Extracted unchanged from Linux v6.0, mm/folio-compat.c:85-88.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/folio-compat.c#L85
* License text: GPL-2.0.txt (in this directory).
*/
void lru_cache_add(struct page *page)
{
folio_add_lru(page_folio(page));
}
putback_lru_page 공식 source · 전체 code · GPL-2.0
/*
* Compatibility functions which bloat the callers too much to make inline.
* All of the callers of these functions should be converted to use folios
* eventually.
*/
/* Extracted unchanged from Linux v6.0, mm/folio-compat.c:139-142.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/folio-compat.c#L139
* License text: GPL-2.0.txt (in this directory).
*/
void putback_lru_page(struct page *page)
{
folio_putback_lru(page_folio(page));
}
add_to_page_cache_lru 공식 source · 전체 code · GPL-2.0
/*
* Compatibility functions which bloat the callers too much to make inline.
* All of the callers of these functions should be converted to use folios
* eventually.
*/
/* Extracted unchanged from Linux v6.0, mm/folio-compat.c:91-95.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/folio-compat.c#L91
* License text: GPL-2.0.txt (in this directory).
*/
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
pgoff_t index, gfp_t gfp)
{
return filemap_add_folio(mapping, page_folio(page), index, gfp);
}
pagecache_get_page 공식 source · 전체 code · GPL-2.0
/*
* Compatibility functions which bloat the callers too much to make inline.
* All of the callers of these functions should be converted to use folios
* eventually.
*/
/* Extracted unchanged from Linux v6.0, mm/folio-compat.c:98-108.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/folio-compat.c#L98
* License text: GPL-2.0.txt (in this directory).
*/
noinline
struct page *pagecache_get_page(struct address_space *mapping, pgoff_t index,
int fgp_flags, gfp_t gfp)
{
struct folio *folio;
folio = __filemap_get_folio(mapping, index, fgp_flags, gfp);
if ((fgp_flags & FGP_HEAD) || !folio || xa_is_value(folio))
return &folio->page;
return folio_file_page(folio, index);
}
lru_cache_add_inactive_or_unevictable 공식 source · 전체 code · GPL-2.0
v6_0_lru_cache_add_inactive_or_unevictable.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/swap.c
*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
/*
* This file contains the default values for the operation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
* Documentation/admin-guide/sysctl/vm.rst.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
*/
/* Extracted unchanged from Linux v6.0, mm/swap.c:503-512.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/swap.c#L503
* License text: GPL-2.0.txt (in this directory).
*/
void lru_cache_add_inactive_or_unevictable(struct page *page,
struct vm_area_struct *vma)
{
VM_BUG_ON_PAGE(PageLRU(page), page);
if (unlikely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) == VM_LOCKED))
mlock_new_page(page);
else
lru_cache_add(page);
}
folio_putback_lru 공식 source · 전체 code · GPL-2.0
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*
* Swap reorganised 29.12.95, Stephen Tweedie.
* kswapd added: 7.1.96 sct
* Removed kswapd_ctl limits, and swap out as many pages as needed
* to bring the system back to freepages.high: 2.4.97, Rik van Riel.
* Zone aware kswapd started 02/00, Kanoj Sarcar (kanoj@sgi.com).
* Multiqueue VM started 5.8.00, Rik van Riel.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* Extracted unchanged from Linux v6.0, mm/vmscan.c:1420-1424.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/vmscan.c#L1420
* License text: GPL-2.0.txt (in this directory).
*/
void folio_putback_lru(struct folio *folio)
{
folio_add_lru(folio);
folio_put(folio); /* drop ref from isolate */
}
filemap_add_folio 공식 source · 전체 code · GPL-2.0
// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/mm/filemap.c
*
* Copyright (C) 1994-1999 Linus Torvalds
*/
/*
* This file handles the generic file mmap semantics used by
* most "normal" filesystems (but you don't /have/ to use this:
* the NFS filesystem used to do this differently, for example)
*/
/* Extracted unchanged from Linux v6.0, mm/filemap.c:926-951.
* Complete function definition; surrounding kernel context is required.
* Source: https://github.com/torvalds/linux/blob/v6.0/mm/filemap.c#L926
* License text: GPL-2.0.txt (in this directory).
*/
int filemap_add_folio(struct address_space *mapping, struct folio *folio,
pgoff_t index, gfp_t gfp)
{
void *shadow = NULL;
int ret;
__folio_set_locked(folio);
ret = __filemap_add_folio(mapping, folio, index, gfp, &shadow);
if (unlikely(ret))
__folio_clear_locked(folio);
else {
/*
* The folio might have been evicted from cache only
* recently, in which case it should be activated like
* any other repeatedly accessed folio.
* The exception is folios getting rewritten; evicting other
* data from the working set, only to cache data that will
* get overwritten with something else, is a waste of memory.
*/
WARN_ON_ONCE(folio_test_active(folio));
if (!(gfp & __GFP_WRITE) && shadow)
workingset_refault(folio, shadow);
folio_add_lru(folio);
}
return ret;
}
참고 자료
- Lorenzo Stoakes, The Linux Memory Manager, Early Access 2025, Chapter 11, Folio Batches / CPU Folio Batches / Adding Folios to a Batch.
- Linux v6.18 mm/swap.c: enqueue·move·drain과 addition callback.
- Linux v6.18 include/linux/pagevec.h: batch 선언.