Folio / Page Cache
๊ด๋ จ ์์ค: mm/filemap.c, mm/folio-compat.c, mm/page-writeback.c
๊ด๋ จ ๋ฌธ์: ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ฐ์ ยท Buddy Allocator ยท ํ์ด์ง ํ์ ยท VMA / mmap ยท tmpfs / shmem
๊ฐ์ (Overview)
Folio๋ Linux 5.16์์ ๋์
๋ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ํต์ฌ ๋จ์๋ก, ๊ธฐ์กด struct page์ ๋จ์ ์ ๋ณด์ํฉ๋๋ค. ๊ธฐ์กด ํ์ด์ง ์บ์๋ ๊ฐ๋ณ ํ์ด์ง ๋จ์๋ก ๊ด๋ฆฌ๋์ด large folio(THP ๋ฑ) ์ง์์ ๋นํจ์จ์ ์ด์์ผ๋, folio๋ compound page ์ ์ฒด๋ฅผ ํ๋์ ๋จ์๋ก ๊ด๋ฆฌํ์ฌ ์ฌ๋ฌ ํ์ด์ง๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ํ์ด์ง ์บ์(Page Cache)๋ ํ์ผ ์์คํ
์ ๋ธ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์บ์ฑํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ, address_space ๊ตฌ์กฐ์ฒด๋ฅผ ํตํด ๊ด๋ฆฌ๋ฉ๋๋ค.
folio์ ํต์ฌ ์ฅ์ ์ compound page์ head page๋ง ์ถ์ ํ๋ฉด ๋์ด lock ๊ด๋ฆฌ, dirty/writeback ์ถ์ , reference counting์ด ๋จ์ํด์ง๋ค๋ ์ ์
๋๋ค. ๋ํ ๊ธฐ์กด page API์์ ํธํ์ฑ์ ์ํด folio-compat.c์ ๋ ๊ฑฐ์ ๋ํผ ํจ์๊ฐ ์ ๊ณต๋ฉ๋๋ค. Linux 7.0์์๋ _mm_id ๊ธฐ๋ฐ rmap ์ต์ ํ, _deferred_list๋ฅผ ํตํ ๋๊ธฐ ๋ถํ (split under pressure), HugeTLB ์ ์ฉ ํ๋ ๋ฑ์ด ์ถ๊ฐ๋์ด ๊ตฌ์กฐ์ ์ผ๋ก ๋ ์ ๊ตํด์ก์ต๋๋ค.
์์ค ํ์ผ ๊ฒฝ๋ก:
mm/filemap.c โ ํ์ด์ง ์บ์ ํต์ฌ ์ฐ์ฐ (์กฐํ, ์ถ๊ฐ, ์ฝ๊ธฐ, ์ฐ๊ธฐ, ํดํธ)
mm/folio-compat.c โ ๊ธฐ์กด page API โ folio ๋ํผ (unlock_page, mark_page_accessed ๋ฑ)
mm/page-writeback.c โ ๋ํฐ ํ์ด์ง writeback ๋ฐ dirty throttling
include/linux/mm_types.h โ struct folio ์ ์ (Linux 7.0: _mm_id, _deferred_list ํฌํจ)
include/linux/fs.h โ struct address_space, address_space_operations ์ ์
include/linux/pagemap.h โ folio_lock, folio_trylock ๋ฑ ์ ๊ธ API
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# 1. ํ์ด์ง ์บ์์ ์บ์ฑ๋ ํ์ด์ง ์ ํ์ธ
cat /proc/meminfo | grep -i "Cached"
# 2. ํ์ฌ ์์คํ
์ Active/Inactive(file) ํ์ด์ง ์ ํ์ธ
cat /proc/meminfo | grep -E "Active\(file\)|Inactive\(file\)"
# 3. writeback ์ค์ธ ํ์ด์ง ์ ํ์ธ
cat /proc/meminfo | grep -i "Dirty\|Writeback"
# 4. ํ์ด์ง ์บ์ ๊ด๋ จ slab ์บ์ ํ์ธ (radix_tree_node โ xarray)
slabtop -o | grep -i "xarray\|inode_cache\|dentry"
# 5. vm.dirty_ratio / vm.dirty_background_ratio ํ๋ผ๋ฏธํฐ ํ์ธ
sysctl vm.dirty_ratio vm.dirty_background_ratio vm.dirty_writeback_interval vm.dirty_expire_interval
# 6. ํน์ inode์ ํ์ด์ง ์บ์ ํฌ๊ธฐ ํ์ธ (debugfs)
cat /proc/<pid>/smaps | grep -A 5 "file" | grep -i "size"
# 7. ํ์ผ ์บ์ ํํธ์จ ํ์ธ (perf ๋๋ sar)
sar -B 1 5 # pgscank/s (page reclaim), pgscand/s (direct reclaim) ๊ด์ฐฐ
# 8. ํ์ผ ์์คํ
๋ณ ํ์ด์ง ์บ์ ํต๊ณ ํ์ธ
cat /proc/meminfo | grep -E "Cached|Buffers|SwapCached"
# 9. folio lock ๋๊ธฐ ๊ณต์ ์ฑ ํ์ธ
sysctl vm.page_lock_unfairness # ๊ธฐ๋ณธ๊ฐ: 5
# 10. writeback ์ค๋ ๋ ๋์ ํ์ธ
cat /proc/vmstat | grep -E "nr_dirty|nr_writeback|nr_congested"
# 11. mmap๋ ํ์ผ ํ์ด์ง ํดํธ ํต๊ณ
cat /proc/vmstat | grep -E "pgmajfault|pgfault"
# 12. ์ฃผ์ ํ๋ก์ธ์ค์ ํ์ด์ง ์บ์ ์ฌ์ฉ๋ ํ์ธ
cat /proc/<pid>/smaps_rollup | grep -E "RSS|PSS"
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
struct folio
folio๋ ํ๋ ์ด์์ ์ฐ์๋ ๋ฌผ๋ฆฌ ํ์ด์ง๋ฅผ ํ๋์ ๋
ผ๋ฆฌ์ ๋จ์๋ก ๊ด๋ฆฌํฉ๋๋ค. struct page์์ ํธํ์ฑ์ ์ํด union์ผ๋ก ๊ฒน์ณ์ ธ ์์ผ๋ฉฐ, Linux 7.0์์๋ _mm_id ๊ธฐ๋ฐ rmap ์ต์ ํ, _deferred_list ๋ฑ ์๋ก์ด ํ๋๊ฐ ์ถ๊ฐ๋์์ต๋๋ค.
// include/linux/mm_types.h:401-506 (Linux 7.0)
struct folio {
union {
struct {
memdesc_flags_t flags; // Atomic ํ๋๊ทธ (locked, uptodate, dirty ๋ฑ)
union {
struct list_head lru; // LRU ๋ฆฌ์คํธ (active/inactive)
struct { void *__filler; unsigned long mlock_count; }; // Unevictable
struct dev_pagemap *pgmap; // ZONE_DEVICE์ฉ
};
struct address_space *mapping; // ์์ address_space ํฌ์ธํฐ
union {
pgoff_t index; // ํ์ผ ๋ด ์คํ์
(ํ์ด์ง ์ธ๋ฑ์ค)
unsigned long share; // fsdax์ฉ share count
};
union {
void *private; // ํ์ผ ์์คํ
์ ์ฉ ๋ฐ์ดํฐ (buffer_head ๋ฑ)
swp_entry_t swap; // swap ์บ์์์์ swap entry
};
atomic_t _mapcount; // RMAP์์์ ๋งคํ ์นด์ดํธ (-1 = ๋งคํ ์๋จ)
atomic_t _refcount; // ์ฐธ์กฐ ์นด์ดํธ
unsigned long memcg_data; // memcgroup ๋ฐ์ดํฐ
};
struct page page; // ๊ธฐ์กด struct page์ ํธํ์ฑ
};
// 2๋ฒ์งธ struct page ์์ญ (large folio์ฉ)
union {
struct {
atomic_t _large_mapcount; // large folio ์ ์ฒด ๋งคํ ์นด์ดํธ
atomic_t _nr_pages_mapped; // ์ค์ ๋ก ๋งคํ๋ ํ์ด์ง ์
atomic_t _entire_mapcount; // ์ ์ฒด ๋งคํ ์นด์ดํธ (64bit)
atomic_t _pincount; // DMA ํ ์นด์ดํธ
mm_id_mapcount_t _mm_id_mapcount[2]; // rmap์ฉ MM ID๋ณ ๋งคํ ์นด์ดํธ
union {
mm_id_t _mm_id[2]; // rmap์ฉ MM ID
unsigned long _mm_ids; // ๋นํธ ํ๋๊ทธ (์ ๊ธ ํฌํจ)
};
unsigned int _nr_pages; // folio ๋ด ์ด ํ์ด์ง ์
};
struct page __page_1;
};
// 3๋ฒ์งธ struct page ์์ญ (deferred split์ฉ)
union {
struct {
struct list_head _deferred_list; // ๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์ ๋ถํ ๋๊ธฐ folio ๋ฆฌ์คํธ
};
struct page __page_2;
};
// 4๋ฒ์งธ struct page ์์ญ (HugeTLB ์ ์ฉ)
union {
struct {
void *_hugetlb_subpool; // HugeTLB ์๋ธํ
void *_hugetlb_cgroup; // HugeTLB memcg
void *_hugetlb_cgroup_rsvd; // HugeTLB memcg ์์ฝ
void *_hugetlb_hwpoison; // HugeTLB ํ๋์จ์ด ์ค๋ฅ
};
struct page __page_3;
};
};
Linux 7.0 FOLIO_MATCH ์ ์ ๊ฒ์ฆ (mm_types.h:508-547):
// struct folio์ struct page์ ํ๋ ์คํ์
์ด ์ผ์นํ๋์ง ์ปดํ์ผ ํ์ ๊ฒ์ฆ
FOLIO_MATCH(flags, flags); // folio->flags == page->flags
FOLIO_MATCH(lru, lru); // folio->lru == page->lru (compound_head)
FOLIO_MATCH(mapping, mapping); // folio->mapping == page->mapping
FOLIO_MATCH(__folio_index, index); // folio->index == page->__folio_index
FOLIO_MATCH(_mapcount, _mapcount); // folio->_mapcount == page->_mapcount
FOLIO_MATCH(_refcount, _refcount); // folio->_refcount == page->_refcount
ํต์ฌ ํ๋ ์ค๋ช :
| ํ๋ | ์ญํ |
|---|---|
| `flags` | ํ์ด์ง ์ํ ํ๋๊ทธ (locked, uptodate, dirty, writeback, lru ๋ฑ). `include/linux/page-flags.h`์์ `PG_locked`, `PG_dirty`, `PG_uptodate` ๋ฑ ์ ์ |
| `mapping` | ์ด folio๊ฐ ์ํ `address_space` ํฌ์ธํฐ. `NULL`์ด๋ฉด unlinked. LSB๊ฐ 1์ด๋ฉด anonymous page (`FOLIO_MAPPING_ANON`) |
| `index` | ํ์ผ ๋ด ํ์ด์ง ์ธ๋ฑ์ค (page offset). `address_space->i_pages` xarray์์์ ํค |
| `private` | ํ์ผ ์์คํ ์ ์ฉ ๋ฐ์ดํฐ. buffer_head ํฌ์ธํฐ, swap entry ๋ฑ์ผ๋ก ์ฌ์ฉ |
| `_mapcount` | ์ด ํ์ด์ง๊ฐ ๋ช ๊ฐ์ PTE์ ๋งคํ๋์ด ์๋์ง ์ถ์ . `-1`์ด๋ฉด ๋งคํ ์์ |
| `_refcount` | ์ด folio์ ๋ํ ์ฐธ์กฐ ์นด์ดํธ. 0์ด ๋๋ฉด free ๊ฐ๋ฅ |
| `_large_mapcount` | large folio์์ ์ ์ฒด ๋งคํ ์นด์ดํธ (2๋ฒ์งธ page ์์ญ) |
| `_nr_pages_mapped` | large folio์์ ์ค์ ๋ก ๋งคํ๋ ํ์ด์ง ์ |
| `_entire_mapcount` | large folio ์ ์ฒด ๋งคํ ์นด์ดํธ (64bit ์์คํ ) |
| `_mm_id` | Linux 7.0 rmap ์ต์ ํ: MM ID ๊ธฐ๋ฐ ๋งคํ ์ถ์ . ๊ธฐ์กด anon_vma ์ฒด์ธ ์ํ ๋๋น O(1) ์ ๊ทผ ๊ฐ๋ฅ |
| `_deferred_list` | ๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์ large folio๋ฅผ splitํ๊ธฐ ์ํด ๋๊ธฐํ๋ ๋ฆฌ์คํธ |
| `_nr_pages` | folio๊ฐ ํฌํจํ๋ ๋ฌผ๋ฆฌ ํ์ด์ง ์ (large folio) |
| `_hugetlb_*` | HugeTLB ์ ์ฉ ํ๋: ์๋ธํ, memcg, ํ๋์จ์ด ์ค๋ฅ ์ถ์ |
struct address_space
ํ์ผ ์บ์์ ๋ฃจํธ ๊ตฌ์กฐ์ฒด์
๋๋ค. ๋ชจ๋ ์บ์ฑ๋ ํ์ด์ง๋ ์ด ๊ตฌ์กฐ์ฒด์ i_pages xarray์ ์ ์ฅ๋ฉ๋๋ค.
// include/linux/fs.h:470-490
struct address_space {
struct inode *host; // ์์ inode (ํ์ผ ๋๋ block device)
struct xarray i_pages; // ํ์ด์ง ์บ์ (XArray ๊ธฐ๋ฐ)
struct rw_semaphore invalidate_lock; // invalidate ์ coherency ๋ณด์ฅ
gfp_t gfp_mask; // ํ ๋น ์ ์ฌ์ฉํ GFP ํ๋๊ทธ
atomic_t i_mmap_writable; // VM_SHARED writable ๋งคํ ์
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
atomic_t nr_thps; // non-shmem THP ์
#endif
struct rb_root_cached i_mmap; // mmap๋ VMA ํธ๋ฆฌ (private/shared)
unsigned long nrpages; // ์ด ์บ์ฑ๋ ํ์ด์ง ์
pgoff_t writeback_index; // writeback ์์ ์คํ์
const struct address_space_operations *a_ops; // ํ์ผ ์์คํ
์ฐ์ฐ ํ
์ด๋ธ
unsigned long flags; // AS_EIO, AS_ENOSPC, AS_DIRTY ๋ฑ
errseq_t wb_err; // ์ต๊ทผ writeback ์๋ฌ
spinlock_t i_private_lock; // i_private_list ๋ณดํธ
struct list_head i_private_list; // ํ์ผ ์์คํ
์ ์ฉ ๋ฆฌ์คํธ
struct rw_semaphore i_mmap_rwsem; // i_mmap ๋ณดํธ
void *i_private_data; // ํ์ผ ์์คํ
์ ์ฉ ๋ฐ์ดํฐ
} __attribute__((aligned(sizeof(long))));
struct address_space_operations
ํ์ผ ์์คํ ์ด ๊ตฌํํด์ผ ํ ํ์ด์ง ์บ์ ์ฐ์ฐ ํ ์ด๋ธ์ ๋๋ค.
// include/linux/fs.h:403-444
struct address_space_operations {
int (*read_folio)(struct file *, struct folio *); // ๋จ์ผ folio ์ฝ๊ธฐ
int (*writepages)(struct address_space *, struct writeback_control *); // ๋ํฐ ํ์ด์ง writeback
bool (*dirty_folio)(struct address_space *, struct folio *); // folio๋ฅผ dirty๋ก ํ์
void (*readahead)(struct readahead_control *); // readahead ์ฐ์ฐ
int (*write_begin)(const struct kiocb *, struct address_space *,
loff_t pos, unsigned len,
struct folio **foliop, void **fsdata); // ์ฐ๊ธฐ ์์
int (*write_end)(const struct kiocb *, struct address_space *,
loff_t pos, unsigned len, unsigned copied,
struct folio *folio, void *fsdata); // ์ฐ๊ธฐ ์๋ฃ
bool (*release_folio)(struct folio *, gfp_t); // folio ํด์ ๊ฒ์ฌ
void (*free_folio)(struct folio *folio); // folio ์์ ํด์
// ... swap, direct_IO, migrate ๋ฑ
};
ํต์ฌ ํจ์
1. `__filemap_get_folio_mpol` โ ํ์ด์ง ์บ์ ์กฐํ/์์ฑ
// mm/filemap.c:1940-2057
struct folio *__filemap_get_folio_mpol(struct address_space *mapping,
pgoff_t index, fgf_t fgp_flags, gfp_t gfp, struct mempolicy *policy)
์ญํ : ์ง์ ๋ mapping๊ณผ index์ ํด๋นํ๋ folio๋ฅผ ํ์ด์ง ์บ์์์ ๊ฒ์ํฉ๋๋ค. ์์ผ๋ฉด FGP_CREAT ํ๋๊ทธ์ ๋ฐ๋ผ ์ folio๋ฅผ ํ ๋นํ์ฌ ์ถ๊ฐํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. filemap_get_entry(mapping, index)๋ก XArray์์ folio/shadow entry ๊ฒ์
2. ์ฐพ์ folio๊ฐ ์๊ณ FGP_LOCK์ด๋ฉด:
- FGP_NOWAIT: folio_trylock() ์คํจ ์ -EAGAIN ๋ฐํ
- ์ผ๋ฐ: folio_lock() (๋ธ๋กํน)
- truncate ํ์ธ: folio->mapping != mapping์ด๋ฉด unlock ํ ์ฌ์๋
3. FGP_ACCESSED์ด๋ฉด folio_mark_accessed() ํธ์ถ
4. ์ฐพ์ง ๋ชปํ๊ณ FGP_CREAT์ด๋ฉด:
- order ๊ฒฐ์ : mapping_min_folio_order(mapping) ~ FGF_GET_ORDER(fgp_flags)
- filemap_alloc_folio()๋ก ํ ๋น
- filemap_add_folio()๋ก XArray์ ์ถ๊ฐ
- ์คํจ ์ order๋ฅผ ์ค์ฌ์ ์ฌ์๋ (์ต์ min_order๊น์ง)
2. `__filemap_add_folio` โ XArray์ folio ์ถ๊ฐ
// mm/filemap.c:848-946
noinline int __filemap_add_folio(struct address_space *mapping,
struct folio *folio, pgoff_t index, gfp_t gfp, void **shadowp)
์ญํ : ํ ๋น๋ folio๋ฅผ mapping->i_pages XArray์ ์ฝ์
ํฉ๋๋ค. ๊ธฐ์กด shadow entry์ ์ถฉ๋ ์ ๋ถ๊ธฐํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. XA_STATE_ORDER๋ก XArray ์ํ ์ด๊ธฐํ
2. xas_lock_irq() ํ ์ถฉ๋ ํญ๋ชฉ ๋ฐ๋ณต (xas_for_each_conflict)
3. ์ถฉ๋ ํญ๋ชฉ์ด ์ผ๋ฐ folio(!xa_is_value)์ด๋ฉด -EEXIST ๋ฐํ (์ด๋ฏธ ์กด์ฌ)
4. shadow entry์ด๋ฉด ๊ธฐ์กด entry์ order ํ์ธ
5. ๊ธฐ์กด entry๊ฐ ๋ ํฌ๋ฉด(order > forder): xas_try_split()๋ก large entry๋ฅผ smaller pieces๋ก ๋ถํ
6. xas_store(&xas, folio)๋ก ์ต์ข
์ฝ์
7. ํต๊ณ ์
๋ฐ์ดํธ: mapping->nrpages += nr, NR_FILE_PAGES, NR_FILE_THPS
3. `filemap_add_folio` โ memcgroup ํฌํจ folio ์ถ๊ฐ
// mm/filemap.c:949-990
int filemap_add_folio(struct address_space *mapping, struct folio *folio,
pgoff_t index, gfp_t gfp)
์ญํ : memcgroup ์ฒดํฌ ํ __filemap_add_folio()๋ฅผ ํธ์ถํฉ๋๋ค. ์ถ๊ฐ ์ฑ๊ณต ์ LRU์ ๋ฑ๋กํฉ๋๋ค.
ํ๋ฆ:
1. mem_cgroup_charge(folio, ...) โ memcgroup ๊ณผ๊ธ
2. __filemap_set_locked(folio) โ folio ์ ๊ธ
3. __filemap_add_folio() โ ์ค์ XArray ์ฝ์
4. ์คํจ ์ memcgroup uncharge
5. ์ฑ๊ณต ์ workingset_refault()๋ก recently-evicted folio ํ์ฑํ
6. folio_add_lru(folio)๋ก LRU ๋ฆฌ์คํธ์ ์ถ๊ฐ
4. `filemap_read` โ ํ์ผ ์ฝ๊ธฐ
// mm/filemap.c:2768-2880
ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter, ssize_t already_read)
์ญํ : ํ์ด์ง ์บ์์์ ํ์ผ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด ์ฌ์ฉ์ ๊ณต๊ฐ์ผ๋ก ๋ณต์ฌํฉ๋๋ค.
ํ๋ฆ:
1. ์ ํจ์ฑ ๊ฒ์ฌ (์์น, ํฌ๊ธฐ)
2. do-while ๋ฃจํ์์ ๋ฐ๋ณต:
- filemap_get_pages() โ ์ฝ์ folio ๋ฐฐ์น ๊ฐ์ ธ์ค๊ธฐ (readahead ํฌํจ)
- folio_mark_accessed() โ ์ ๊ทผ ํ์ (LRU ํ์ฑํ)
- copy_folio_to_iter() โ ์ฌ์ฉ์ ๊ณต๊ฐ์ผ๋ก ๋ฐ์ดํฐ ๋ณต์ฌ
- folio_put() โ ์ฐธ์กฐ ํด์
3. file_accessed(filp) โ atime ์
๋ฐ์ดํธ
5. `filemap_fault` โ mmap ํ์ด์ง ํดํธ ์ฒ๋ฆฌ
// mm/filemap.c:3512-3670
vm_fault_t filemap_fault(struct vm_fault *vmf)
์ญํ : mmap๋ ํ์ผ ์์ญ์ ๋ํ ํ์ด์ง ํดํธ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ํ์ด์ง ์บ์์์ folio๋ฅผ ์ฐพ๊ณ , ์์ผ๋ฉด I/O๋ก ์ฝ์ด์ต๋๋ค.
ํ๋ฆ:
1. ํ์ด์ง ์บ์์ folio ์กด์ฌ ์ฌ๋ถ ํ์ธ (filemap_get_folio)
2. ์กด์ฌํ๋ฉด async readahead ์๋ (do_async_mmap_readahead)
3. ์กด์ฌํ์ง ์์ผ๋ฉด:
- PGMAJFAULT ์นด์ดํธ ์ฆ๊ฐ (major fault)
- sync readahead ํธ๋ฆฌ๊ฑฐ (do_sync_mmap_readahead)
- __filemap_get_folio(FGP_CREAT|FGP_FOR_MMAP)๋ก ์ folio ์์ฑ
4. lock_folio_maybe_drop_mmap() โ folio ์ ๊ธ
5. folio_test_uptodate() ํ์ธ:
- Uptodate์ด๋ฉด vmf->page ์ค์ ํ ๋ฐํ
- ์๋๋ฉด filemap_read_folio()๋ก ์ง์ ์ฝ๊ธฐ
6. VM_FAULT_MAJOR ๋๋ VM_FAULT_LOCKED ํ๋๊ทธ ๋ฐํ
6. `filemap_write_and_wait_range` โ ๋ํฐ ํ์ด์ง writeback
// mm/filemap.c:675-698
int filemap_write_and_wait_range(struct address_space *mapping,
loff_t lstart, loff_t lend)
์ญํ : ์ง์ ๋ ๋ฒ์์ ๋ํฐ ํ์ด์ง๋ฅผ ๋์คํฌ์ ๊ธฐ๋กํ๊ณ ์๋ฃ๋ฅผ ๋๊ธฐํฉ๋๋ค.
ํ๋ฆ:
1. mapping_needs_writeback(mapping) ํ์ธ
2. ๋ํฐ ํ์ด์ง๊ฐ ์์ผ๋ฉด filemap_fdatawrite_range()๋ก writeback ์์
3. -EIO๊ฐ ์๋๋ฉด __filemap_fdatawait_range()๋ก ์๋ฃ ๋๊ธฐ
4. filemap_check_errors()๋ก ์๋ฌ ํ์ธ
7. `filemap_remove_folio` โ ํ์ด์ง ์บ์์์ folio ์ ๊ฑฐ
// mm/filemap.c:250-264
void filemap_remove_folio(struct folio *folio)
์ญํ : ์ ๊ธ์ด ๊ฑธ๋ฆฐ folio๋ฅผ ํ์ด์ง ์บ์์์ ์ ๊ฑฐํ๊ณ ํด์ ํฉ๋๋ค.
ํ๋ฆ:
1. BUG_ON(!folio_test_locked(folio)) โ ์ ๊ธ ํ์ธ
2. spin_lock(&mapping->host->i_lock) โ inode ์ ๊ธ
3. xa_lock_irq(&mapping->i_pages) โ XArray ์ ๊ธ
4. __filemap_remove_folio(folio, NULL) โ XArray์์ ์ ๊ฑฐ
5. mapping_shrinkable(mapping) โ inode_lru_list_add() โ inode LRU ์ถ๊ฐ
6. filemap_free_folio(mapping, folio) โ a_ops->free_folio + folio_put_refs
filemap ์ ๊ธ ์์ (Lock Ordering)
// mm/filemap.c:81-127 โ ์ปค๋์ด ์งํค๋ ์ ๊ธ ์์
// i_mmap_rwsem > page_table_lock > i_pages lock (์ผ๋ฐ ๊ฒฝ๋ก)
// mmap_lock > invalidate_lock > lock_page (ํดํธ ๊ฒฝ๋ก)
// i_rwsem > mmap_lock (์ฐ๊ธฐ ๊ฒฝ๋ก)
| ๊ฒฝ๋ก | ์ ๊ธ ์์ |
|---|---|
| truncate | `i_mmap_rwsem` โ `private_lock` โ `swap_lock` โ `i_pages lock` |
| mmap fault | `mmap_lock` โ `invalidate_lock` โ `lock_page(folio)` |
| ์ผ๋ฐ ์ฐ๊ธฐ | `i_rwsem` โ `mmap_lock` โ `page_table_lock` โ `i_pages lock` |
| ํ์ด์ง ํดํธ | `mmap_lock` โ `page_table_lock` โ `i_pages lock` |
folio ์ ๊ธ ๊ณต์ ์ฑ (Page Lock Unfairness)
// mm/filemap.c:1076 โ sysctl_page_lock_unfairness ๊ธฐ๋ณธ๊ฐ
static int sysctl_page_lock_unfairness = 5;
folio lock ๋๊ธฐ ์ unfairness ์นด์ดํฐ๊ฐ 0 ์ดํ๊ฐ ๋๋ฉด WQ_FLAG_CUSTOM ํ๋๊ทธ๊ฐ ์ค์ ๋์ด ๊ณต์ ํ ์ ๊ธ ์๋(fair lock handoff)๊ฐ ํ์ฑํ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด thundering herd ๋ฌธ์ ๋ฅผ ์ํํฉ๋๋ค.
// mm/filemap.c:1267-1273
repeat:
wait->flags = 0;
if (behavior == EXCLUSIVE) {
wait->flags = WQ_FLAG_EXCLUSIVE;
if (--unfairness < 0)
wait->flags |= WQ_FLAG_CUSTOM; // ๊ณต์ ํ ์๋ ํ์ฑํ
}
EXCLUSIVE ๋๊ธฐ: ํ๋์ ๋๊ธฐ์๋ง ๊นจ์ (๊ธฐ๋ณธ)WQ_FLAG_CUSTOM: ์ ๊ธ ์์ ์๊ฐ ์ง์ ์๋ (๊ณต์ ์ฑ ๋ณด์ฅ)sysctl vm.page_lock_unfairness๋ก ๋ฐํ์ ์กฐ์ ๊ฐ๋ฅํธ์ถ ํ๋ฆ
ํ์ผ ์ฝ๊ธฐ ํ๋ฆ
generic_file_read_iter()
โโ filemap_read()
โโ filemap_get_pages()
โ โโ filemap_get_read_batch() โ XArray์์ folio ๋ฐฐ์น ์กฐํ
โ โโ page_cache_sync_ra() โ ์์ผ๋ฉด sync readahead
โ โโ filemap_create_folio() โ ๊ทธ๋๋ ์์ผ๋ฉด ์ folio ์์ฑ
โ โโ filemap_readahead() โ readahead ํธ๋ฆฌ๊ฑฐ
โโ folio_mark_accessed() โ LRU ํ์ฑํ
โโ copy_folio_to_iter() โ ์ฌ์ฉ์ ๊ณต๊ฐ์ผ๋ก ๋ณต์ฌ
mmap ํดํธ ์ฒ๋ฆฌ ํ๋ฆ
do_read_fault() / do_fault()
โโ filemap_fault()
โโ filemap_get_folio() โ ํ์ด์ง ์บ์ ์กฐํ
โ โโ [hit] do_async_mmap_readahead()
โ โโ [miss] do_sync_mmap_readahead()
โ โโ __filemap_get_folio(FGP_CREAT) โ ์ folio ์์ฑ
โโ lock_folio_maybe_drop_mmap() โ folio ์ ๊ธ
โโ folio_test_uptodate() ๊ฒ์ฌ
โ โโ [uptodate] โ vmf->page ์ค์
โ โโ [not uptodate] filemap_read_folio() โ ์ง์ I/O
โโ VM_FAULT_MAJOR | VM_FAULT_LOCKED ๋ฐํ
ํ์ด์ง ์บ์ ์ถ๊ฐ ํ๋ฆ
__filemap_get_folio_mpol()
โโ filemap_add_folio()
โโ mem_cgroup_charge() โ memcgroup ๊ณผ๊ธ
โโ __filemap_set_locked() โ folio ์ ๊ธ
โโ __filemap_add_folio() โ XArray ์ฝ์
โ โโ xas_for_each_conflict() โ ์ถฉ๋ ๊ฒ์ฌ
โ โ โโ xas_try_split() โ large entry ๋ถํ
โ โโ xas_store(folio) โ ์ต์ข
์ฝ์
โโ workingset_refault() โ recently-evicted ํ์ฑํ
โโ folio_add_lru() โ LRU์ ์ถ๊ฐ
Writeback ํ๋ฆ
balance_dirty_pages_ratelimited()
โโ balance_dirty_pages()
โโ balance_domain_limits() โ global/memcg ๋๋ฉ์ธ ์ ํ ๊ณ์ฐ
โโ wb_start_background_writeback() โ bg_thresh ์ด๊ณผ ์ writeback ์์
โโ balance_wb_limits() โ pos_ratio ๊ณ์ฐ
โโ throttle ๋ก์ง โ task_ratelimit์ ๋ฐ๋ผ ์ฐ๊ธฐ throttling
์กฐ๊ฑด๋ณ ๋น๊ต
folio vs ๊ธฐ์กด struct page
| ํญ๋ชฉ | struct page | struct folio |
|---|---|---|
| ํฌ๊ธฐ | 64 bytes (๊ธฐ๋ณธ) | 64 bytes ร N (compound) |
| ๊ด๋ฆฌ ๋จ์ | ๊ฐ๋ณ ํ์ด์ง | ํ๋ ์ด์์ ์ฐ์ ํ์ด์ง |
| page cache ํค | page index | folio index |
| lock ๋ฒ์ | ๊ฐ๋ณ ํ์ด์ง | folio ์ ์ฒด (head page lock) |
| dirty ์ถ์ | ๊ฐ๋ณ page flag | folio flag (์ ์ฒด dirty ์ถ์ ) |
| compatibility | ๋ชจ๋ ์ปค๋ | 5.16+ (๊ธฐ์กด API ๋ํผ ์ ๊ณต) |
| large folio | ์ ํ์ (compound_page) | ๊ธฐ๋ณธ ์ง์ |
| rmap ์ถ์ | anon_vma ์ฒด์ธ ์ํ | _mm_id ๊ธฐ๋ฐ O(1) ๋งคํ ์นด์ดํธ (Linux 7.0) |
| memcg ๊ณผ๊ธ | ๊ฐ๋ณ page | folio ๋จ์ (large folio๋ 1ํ ๊ณผ๊ธ) |
FOLIO_MAPPING_ANON ํ๋๊ทธ (Linux 7.0):
// folio->mapping์ LSB๊ฐ 1์ด๋ฉด anonymous page
// folio->mapping์์ ์ค์ address_space๋ ~1๋ก ๋ง์คํนํ์ฌ ์ป์
// anon_vma๋ address_space ๋์ ์ฌ์ฉ
ํ์ผ ์ฝ๊ธฐ ๊ฒฝ๋ก ๋น๊ต
| ์๋๋ฆฌ์ค | ๊ฒฝ๋ก | ํน์ง |
|---|---|---|
| ์ผ๋ฐ read | `filemap_read()` โ `filemap_get_pages()` | ํ์ด์ง ์บ์ ํํธ ์ I/O ์์ |
| mmap fault (hit) | `filemap_fault()` โ `folio_test_uptodate()` | async readahead ๊ฐ๋ฅ |
| mmap fault (miss) | `filemap_fault()` โ `do_sync_mmap_readahead()` | major fault, sync I/O |
| direct I/O | `generic_file_read_iter()` โ `mapping->a_ops->direct_IO()` | ํ์ด์ง ์บ์ ์ฐํ |
writeback ํธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด
| ์กฐ๊ฑด | ๋์ | ํ๋ผ๋ฏธํฐ |
|---|---|---|
| `nr_dirty > bg_thresh` | ๋ฐฑ๊ทธ๋ผ์ด๋ writeback ์์ | `vm.dirty_background_ratio` (๊ธฐ๋ณธ 10%) |
| `nr_dirty > dirty_thresh` | ์ฐ๊ธฐ ํ๋ก์ธ์ค throttling | `vm.dirty_ratio` (๊ธฐ๋ณธ 20%) |
| `dirty_expire_interval` ์ด๊ณผ | ๋ง๋ฃ๋ ๋ํฐ ํ์ด์ง ์ฆ์ writeback | `vm.dirty_expire_interval` (๊ธฐ๋ณธ 30s) |
| `dirty_writeback_interval` | kupdate ์คํ์ผ ์ฃผ๊ธฐ์ writeback | `vm.dirty_writeback_interval` (๊ธฐ๋ณธ 5s) |
| `ratelimit_pages` ์ด๊ณผ | per-CPU throttling ์ฒดํฌ | `vm.dirty_writeback_interval` (๊ธฐ๋ณธ 32 pages) |
// mm/page-writeback.c:68,75,92,103,110
static long ratelimit_pages = 32; // per-CPU throttling ์๊ณ๊ฐ
static int dirty_background_ratio = 10; // ๋ฐฑ๊ทธ๋ผ์ด๋ writeback ์์ ๋น์จ
static int vm_dirty_ratio = 20; // ์ฐ๊ธฐ throttling ์์ ๋น์จ
unsigned int dirty_writeback_interval = 5 * 100; // centiseconds (5์ด)
unsigned int dirty_expire_interval = 30 * 100; // centiseconds (30์ด)
folio lock ์ํ
| ํ๋๊ทธ | ์๋ฏธ | ๊ด๋ จ ํจ์ |
|---|---|---|
| `PG_locked` | folio lock ํ๋๋จ | `folio_lock()`, `folio_trylock()` |
| `PG_uptodate` | ๋ฐ์ดํฐ๊ฐ ๋์คํฌ์ ์ผ์น | `folio_end_read()`, `folio_mark_uptodate()` |
| `PG_dirty` | ์์ ๋ ๋ฐ์ดํฐ ์กด์ฌ | `folio_mark_dirty()`, `folio_clear_dirty_for_io()` |
| `PG_writeback` | writeback ์งํ ์ค | `folio_start_writeback()`, `folio_end_writeback()` |
| `PG_lru` | LRU ๋ฆฌ์คํธ์ ์์ | `folio_add_lru()`, `folio_del_lru()` |
| `PG_active` | ํ์ฑ LRU์ ์์ | `folio_set_active()`, `folio_clear_active()` |
์๋ฃ๊ตฌ์กฐ ๊ด๊ณ๋
ํธ์ถ ํ๋ฆ ๋ค์ด์ด๊ทธ๋จ
๊ด๋ จ ๋ฌธ์
minzkn.com ๋น๊ต
minzkn.com ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ฐ์ ํ์ด์ง์์ struct page, folio ๊ด๋ จ ๋ด์ฉ์ ํ์ธํ ๊ฒฐ๊ณผ:
minzkn ํต์ฌ ๋ด์ฉ:
struct page์ ๊ธฐ๋ณธ ํ๋ ์ค๋ช
(flags, lru, mapping, index, private, _refcount, _mapcount)์ฐ๋ฆฌ ๋ฌธ์์์ ๋ณด๊ฐ๋ ๋ด์ฉ:
struct page ์ค๋ช
์ folio ๊ด์ ์์ ํ์ฅํ์ฌ Linux 7.0์ _mm_id, _deferred_list, _hugetlb_* ํ๋๊น์ง ํฌํจfolio_mark_accessed(), folio_clear_dirty_for_io() ๊ฐ์ folio API์ ์ฐ๊ฒฐ