๐ Swap / zswap
๊ด๋ จ ์์ค: mm/swapfile.c, mm/page_io.c, mm/zswap.c, mm/zsmalloc.c, mm/memory.c
๊ด๋ จ ํค๋: include/linux/swap.h, include/linux/zswap.h, include/linux/zsmalloc.h, include/linux/swapops.h, include/linux/mm_types.h
๊ด๋ จ ๋ฌธ์: ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ฐ์ ยท Buddy Allocator ยท ํ์ด์ง ํ์ ยท zsmalloc ยท OOM Killer ยท Memory Cgroup
๊ฐ์ (Overview)
Linux์ Swap๋ ์ต๋ช
ํ์ด์ง(Anonymous Page)๊ฐ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ ๋์คํฌ ์ค์ ์์ญ์ผ๋ก ๋ด๋ณด๋ด๋ ๋ฉ์ปค๋์ฆ์
๋๋ค. swapfile.c๋ ์ค์ ๋๋ฐ์ด์ค/ํ์ผ ๊ด๋ฆฌ, ์ค์ ์ฌ๋กฏ ํ ๋น/ํด์ , swap cache ์ฐ์ฐ์ ๋ด๋นํฉ๋๋ค.
zswap์ ์ค์ ์ฐ๊ธฐ ๊ฒฝ๋ก๋ฅผ ๊ฐ๋ก์ฑ์ด, ํ์ด์ง๊ฐ ๋์คํฌ์ ๊ธฐ๋ก๋๊ธฐ ์ ์ RAM ๊ธฐ๋ฐ ํ์ ์์ถ ์ ์ฅํ๋ ์บ์์
๋๋ค. ์ด๋ฅผ ํตํด ๋์คํฌ I/O๋ฅผ ํฌ๊ฒ ์ค์ด๊ณ ์ค์ ์ ๊ทผ ์ง์ฐ์๊ฐ์ ๊ฐ์ ํฉ๋๋ค. ์์ถ๋ ํ์ด์ง๋ zsmalloc ํ ๋น์๊ฐ ๊ด๋ฆฌํ๋ ๋ฉ๋ชจ๋ฆฌ ํ์ ์ ์ฅ๋ฉ๋๋ค.
์ผ์์ ์ผ๋ก๋ ์ฑ ์ ์ ๊ณต๊ฐ(RAM)์ด ๋ถ์กฑํ ๋ ๋ ์ฐ๋ ๋ฌธ์(์ต๋ช ํ์ด์ง)๋ฅผ ์ฐฝ๊ณ (์ค์ ๋๋ฐ์ด์ค)๋ก ์ฎ๊ธฐ๋ ๊ณผ์ ๊ณผ ๋น์ทํฉ๋๋ค. zswap์ ์ฐฝ๊ณ ๋ก ๋ณด๋ด๊ธฐ ์ ์ ๋ฌธ์๋ฅผ ์์ถ ๋ดํฌ์ ๋ฃ์ด ์ฑ ์ ์ ์๋์ ์ ์ ๋ณด๊ดํ๋ ๊ณ์ธต์ด๋ฉฐ, ๋ค์ ํ์ํด์ง๋ฉด ๋์คํฌ๊น์ง ๊ฐ์ง ์๊ณ ์์ถ์ ํ์ด ๋น ๋ฅด๊ฒ ๋ณต์ํ ์ ์์ต๋๋ค.
Swap Cache๋ ์ค์์ผ๋ก ๋๊ฐ ํ์ด์ง๊ฐ ๋ค์ ์ ๊ทผ๋ ๋ ์ค๋ณต I/O๋ฅผ ์ค์ด๋ ๋ฉ๋ชจ๋ฆฌ ๋ด ์บ์์
๋๋ค. ๊ฐ์ ์ต๋ช
ํ์ด์ง๊ฐ fork() ์ดํ ์ฌ๋ฌ ํ๋ก์ธ์ค์์ ๊ณต์ ๋๊ฑฐ๋, swap out ์งํ ๋ค์ ์ ๊ทผ๋๋ ๊ฒฝ์ฐ ํ ๋ฒ ์ฝ์ folio๋ฅผ swap cache์ ๋ณด๊ดํด ๋ค๋ฅธ fault๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํฉ๋๋ค.
minzkn.com์ Swap/zswap์ "๋ฉ๋ชจ๋ฆฌ ํ์" ์น์ ์์ ๊ฐ๋ตํ ๋ค๋ฃจ๋ฉฐ, kswapd, LRU, OOM Killer์ ํจ๊ป ์ค๋ช ํฉ๋๋ค. ์ฐ๋ฆฌ ๋ฌธ์์์๋ ์์ค ์ฝ๋ ์์ค์์ swapfile ๊ด๋ฆฌ, zswap ์ ์ฅ/๋ก๋ ๊ฒฝ๋ก, shrinker ๋ฉ์ปค๋์ฆ์ ์์ธ ๋ถ์ํฉ๋๋ค.
์์ค ํ์ผ ๊ฒฝ๋ก
linux-7.0-source/
โโโ mm/
โ โโโ swapfile.c โ ์ค์ ๋๋ฐ์ด์ค ๊ด๋ฆฌ, ์ฌ๋กฏ ํ ๋น, swap cache
โ โโโ page_io.c โ swap read/write I/O, zswap ์ง์
์
โ โโโ zswap.c โ zswap ์บ์ (์์ถ ์ ์ฅ, ๋ก๋, writeback)
โ โโโ zsmalloc.c โ ์์ถ ๋ฐ์ดํฐ ์ ์ฉ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์
โ โโโ memory.c โ do_swap_page() swap-in fault ์ฒ๋ฆฌ
โโโ include/linux/
โโโ swap.h โ swap_info_struct, swap_extent, ํ๋๊ทธ ์ ์
โโโ zswap.h โ zswap API (store/load/invalidate)
โโโ zsmalloc.h โ zs_pool, zs_malloc, zs_free API
โโโ swapops.h โ swp_entry_t type/offset ์ธ์ฝ๋ฉ
โโโ mm_types.h โ swp_entry_t, folio ๊ธฐ๋ณธ ํ์
Swap / zswap ํธ์ถ ํ๋ฆ
ํต์ฌ ์๋ฃ๊ตฌ์กฐ ๊ด๊ณ๋
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# ์ค์ ๋๋ฐ์ด์ค/ํ์ผ ํ์ฑ ์ํ ํ์ธ
cat /proc/swaps
# ์์คํ
์ ์ฒด ์ค์ ์ฌ์ฉ๋
free -h | grep Swap
# ์ค์ ํํฐ์
์์ธ ์ ๋ณด
swapon -s
# zswap ํต๊ณ ํ์ธ (์ปค๋ 4.7+)
cat /sys/kernel/debug/zswap/pool_total_size # zswap ํ ์ ์ฒด ํฌ๊ธฐ
cat /sys/kernel/debug/zswap/stored_pages # zswap์ ์ ์ฅ๋ ํ์ด์ง ์
cat /sys/kernel/debug/zswap/same_filled_pages # ์ ๋ก ํ์ด์ง ์
cat /sys/kernel/debug/zswap/duplicate_entry # ์ค๋ณต ์ํธ๋ฆฌ ์
# zswap ํ๋ผ๋ฏธํฐ ํ์ธ
cat /sys/module/zswap/parameters/enabled # zswap ํ์ฑํ ์ฌ๋ถ
cat /sys/module/zswap/parameters/max_pool_percent # ์ต๋ ํ ๋น์จ (๊ธฐ๋ณธ 20%)
# zswap compressor ํ์ธ
cat /sys/module/zswap/parameters/compressor # ์์ถ ์๊ณ ๋ฆฌ์ฆ (zstd/lzo/lz4 ๋ฑ)
# cgroup๋ณ swap/zswap ์ฌ์ฉ๋ (cgroup v2)
find /sys/fs/cgroup -maxdepth 3 \( -name memory.swap.current -o -name memory.zswap.current \) -print -exec cat {} \; 2>/dev/null
# vmstat์์ ์ค์ ํ๋ ๊ด์ฐฐ
cat /proc/vmstat | grep -E 'pswpin|pswpout|zswpin|zswpout'
# ์ค์ ์บ์ ํํฉ
cat /proc/meminfo | grep -E 'SwapTotal|SwapFree|SwapCached'
# ์ค์ ์์ญ๋ณ ํฌ๊ธฐ, ์ฌ์ฉ๋, ์ฐ์ ์์
swapon --show --bytes --output=NAME,TYPE,SIZE,USED,PRIO
# ์ต๋ช
ํ์ด์ง์ ํ์ผ ์บ์ ํ์ ์ฑํฅ ํ์ธ
sysctl vm.swappiness
cat /proc/sys/vm/swappiness
# zswap ๋ฐฑ์๋์ zpool ํ์ธ
cat /sys/module/zswap/parameters/zpool 2>/dev/null
cat /sys/module/zswap/parameters/accept_threshold_percent 2>/dev/null
# zswap debugfs ํต๊ณ ์ผ๊ด ํ์ธ
grep -r . /sys/kernel/debug/zswap/ 2>/dev/null || true
# cgroup v2 swap/zswap ๊ณ์ ์์ธ ํ์ธ
find /sys/fs/cgroup -maxdepth 3 \( -name memory.zswap.max -o -name memory.zswap.writeback \) -print -exec cat {} \; 2>/dev/null
# ๋ฉ๋ชจ๋ฆฌ ์๋ฐ๊ณผ ์ค์ ๊ด๋ จ ์ปค๋ ๋ก๊ทธ
cat /proc/pressure/memory
dmesg | grep -Ei 'zswap|swap|swapon|swapoff|Out of memory' | tail -50
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
1. swap_info_struct โ ์ค์ ๋๋ฐ์ด์ค ๊ด๋ฆฌ
์ค์ ํ์ผ/๋๋ฐ์ด์ค ํ๋๋น ํ๋์ swap_info_struct์ด ์กด์ฌํฉ๋๋ค. priority ์์๋ก ์ ๋ ฌ๋ plist(swap_active_head)์ ์ฐ๊ฒฐ๋ฉ๋๋ค.
/* include/linux/swap.h:261-303 */
struct swap_info_struct {
struct percpu_ref users; /* ์ค์ ๋๋ฐ์ด์ค ์ ํจ์ฑ ์ ์ง */
unsigned long flags; /* SWP_USED, SWP_WRITEOK ๋ฑ */
signed short prio; /* ์ค์ ์ฐ์ ์์ (๋์์๋ก ๋จผ์ ์ฌ์ฉ) */
struct plist_node list; /* swap_active_head ์ฐ๊ฒฐ */
signed char type; /* swap_info[] ์ธ๋ฑ์ค */
unsigned int max; /* swap_map ์ต๋ ํฌ๊ธฐ */
unsigned char *swap_map; /* vmalloc ๋ฐฐ์ด: ๊ฐ ์ฌ๋กฏ ์ฐธ์กฐ ์นด์ดํธ */
unsigned long *zeromap; /* ์ ๋ก ํ์ด์ง ์ถ์ ๋นํธ๋งต */
struct swap_cluster_info *cluster_info; /* SSD ํด๋ฌ์คํฐ ๊ด๋ฆฌ */
struct list_head free_clusters; /* ๋น ํด๋ฌ์คํฐ ๋ชฉ๋ก */
struct list_head full_clusters; /* ๊ฐ๋ ์ฐฌ ํด๋ฌ์คํฐ ๋ชฉ๋ก */
struct list_head nonfull_clusters[SWAP_NR_ORDERS]; /* ๋ถ๋ถ ์ฌ์ฉ ํด๋ฌ์คํฐ */
struct list_head frag_clusters[SWAP_NR_ORDERS]; /* ๋จํธํ๋ ํด๋ฌ์คํฐ */
unsigned int pages; /* ์ฌ์ฉ ๊ฐ๋ฅํ ์ด ํ์ด์ง ์ */
atomic_long_t inuse_pages; /* ํ์ฌ ์ฌ์ฉ ์ค์ธ ํ์ด์ง ์ */
struct swap_sequential_cluster *global_cluster; /* ํ์ ๋๋ฐ์ด์ค์ฉ */
spinlock_t global_cluster_lock; /* global_cluster ์ง๋ ฌํ */
struct rb_root swap_extent_root; /* swap extent rbtree */
struct block_device *bdev; /* ๋ธ๋ก ๋๋ฐ์ด์ค */
struct file *swap_file; /* ์ค์ ํ์ผ */
struct completion comp; /* swapoff ์๋ฃ ๋๊ธฐ */
spinlock_t lock; /* swap_map ๋ณดํธ */
spinlock_t cont_lock; /* count continuation ๋ณดํธ */
struct work_struct discard_work; /* discard ์์ปค */
struct work_struct reclaim_work; /* reclaim ์์ปค */
struct list_head discard_clusters; /* discard ๋์ ํด๋ฌ์คํฐ */
struct plist_node avail_list; /* swap_avail_head ์ฐ๊ฒฐ */
};
์ฃผ์ ํ๋๊ทธ (flags):
| ํ๋๊ทธ | ์๋ฏธ |
|---|---|
| `SWP_USED` | ์ฌ๋กฏ ์ฌ์ฉ ์ค |
| `SWP_WRITEOK` | ์ฐ๊ธฐ ๊ฐ๋ฅ |
| `SWP_SOLIDSTATE` | SSD (seek ๋น์ฉ ์์) |
| `SWP_PAGE_DISCARD` | ์ฌ์ฉ ํ ํ์ด์ง ํด๋ฌ์คํฐ discard |
| `SWP_SYNCHRONOUS_IO` | ๋๊ธฐ I/O ํจ์จ์ |
| `SWP_ACTIVATED` | swap_activate ์ฑ๊ณต |
2. swap_extent โ ๋์คํฌ ๋ธ๋ก ๋งคํ
์ค์ ํ์ผ์ ๋ ผ๋ฆฌ์ ํ์ด์ง ์คํ์ ์ ์ค์ ๋์คํฌ ๋ธ๋ก์ผ๋ก ๋ณํํฉ๋๋ค. rbtree๋ก ๊ด๋ฆฌ๋ฉ๋๋ค.
/* include/linux/swap.h:191-196 */
struct swap_extent {
struct rb_node rb_node;
pgoff_t start_page; /* ์ด extent์ ์์ ๋
ผ๋ฆฌ ํ์ด์ง */
pgoff_t nr_pages; /* ์ด extent์ ํ์ด์ง ์ */
sector_t start_block; /* ์ด extent์ ์์ ๋์คํฌ ๋ธ๋ก */
};
3. zswap_entry โ ์์ถ๋ ํ์ด์ง ๋ฉํ๋ฐ์ดํฐ
zswap์ ์ ์ฅ๋ ์์ถ๋ ํ์ด์ง ํ๋์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ์ ํฉ๋๋ค.
/* mm/zswap.c:190-198 */
struct zswap_entry {
swp_entry_t swpentry; /* ๋์ํ๋ swap entry (type + offset) */
unsigned int length; /* ์์ถ๋ ๋ฐ์ดํฐ ๋ฐ์ดํธ ์ */
bool referenced; /* ์ต๊ทผ ์ ๊ทผ ์ฌ๋ถ (second chance) */
struct zswap_pool *pool; /* ์์ zswap ํ */
unsigned long handle; /* zsmalloc ํ ๋น ํธ๋ค */
struct obj_cgroup *objcg; /* memcg ์ถฉ์ ๋์ */
struct list_head lru; /* LRU ๋ฆฌ์คํธ ์ฐ๊ฒฐ */
};
4. zswap_pool โ zswap ํ ๊ด๋ฆฌ
ํ๋์ zswap ํ์ ํ๋์ ์์ถ ์๊ณ ๋ฆฌ์ฆ๊ณผ ํ๋์ zsmalloc ํ์ ๊ฐ์ง๋๋ค. ์ฌ๋ฌ ํ์ด ์กด์ฌํ ์ ์์ผ๋ฉฐ, RCU๋ก ๋ณดํธ๋๋ ๋ฆฌ์คํธ์ ์ฐ๊ฒฐ๋ฉ๋๋ค.
/* mm/zswap.c:153-161 */
struct zswap_pool {
struct zs_pool *zs_pool; /* zsmalloc ๋ฉ๋ชจ๋ฆฌ ํ */
struct crypto_acomp_ctx __percpu *acomp_ctx; /* CPU๋ณ ์์ถ ์ปจํ
์คํธ */
struct percpu_ref ref; /* ์ฐธ์กฐ ์นด์ดํธ */
struct list_head list; /* zswap_pools ๋ฆฌ์คํธ */
struct work_struct release_work; /* ํด์ ์์ปค */
struct hlist_node node; /* CPU hotplug ๋
ธ๋ */
char tfm_name[CRYPTO_MAX_ALG_NAME]; /* ์์ถ ์๊ณ ๋ฆฌ์ฆ ์ด๋ฆ */
};
5. crypto_acomp_ctx โ CPU๋ณ ์์ถ ์ปจํ ์คํธ
CPU๋ง๋ค ํ๋์ฉ ์กด์ฌํ๋ ์์ถ ์ปจํ ์คํธ์ ๋๋ค. ๋ฝ ์์ด ๋ณ๋ ฌ ์์ถ์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
/* mm/zswap.c:139-145 */
struct crypto_acomp_ctx {
struct crypto_acomp *acomp; /* ์์ถ ์๊ณ ๋ฆฌ์ฆ ํธ๋ค */
struct acomp_req *req; /* ์์ถ ์์ฒญ */
struct crypto_wait wait; /* ๋๊ธฐํ ๋๊ธฐ */
u8 *buffer; /* ์์ ์์ถ ๋ฒํผ (PAGE_SIZE) */
struct mutex mutex; /* ์ปจํ
์คํธ ๋ณดํธ */
};
6. zspage โ zsmalloc ํ์ด์ง
zsmalloc์ ํ๋ ์ด์์ ํ์ด์ง๋ฅผ ๋ฌถ์ด "zspage"๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ์์ ๋์ผ ํฌ๊ธฐ ๊ฐ์ฒด๋ฅผ ๋ฐฐ์นํฉ๋๋ค.
/* mm/zsmalloc.c:261-274 */
struct zspage {
struct {
unsigned int huge:HUGE_BITS; /* ๋จ์ผ ํ์ด์ง zspage ์ฌ๋ถ */
unsigned int fullness:FULLNESS_BITS; /* ์ฌ์ฉ๋ฅ ๊ทธ๋ฃน */
unsigned int class:CLASS_BITS + 1; /* ํฌ๊ธฐ ํด๋์ค ์ธ๋ฑ์ค */
unsigned int magic:MAGIC_VAL_BITS; /* ๋งค์ง ๋๋ฒ (0x58) */
};
unsigned int inuse; /* ์ฌ์ฉ ์ค์ธ ๊ฐ์ฒด ์ */
unsigned int freeobj; /* ์ฒซ ๋ฒ์งธ ๋น ๊ฐ์ฒด ์ธ๋ฑ์ค */
struct zpdesc *first_zpdesc; /* ์ฒซ ๋ฒ์งธ ํ์ด์ง ๋์คํฌ๋ฆฝํฐ */
struct list_head list; /* fullness ๋ฆฌ์คํธ */
struct zs_pool *pool; /* ์์ ํ */
struct zspage_lock zsl; /* zspage ๋ฝ */
};
7. zswap_trees โ xarray ๊ธฐ๋ฐ ์ธ๋ฑ์ฑ
์ค์ offset๋ณ๋ก zswap_entry๋ฅผ ๋น ๋ฅด๊ฒ ์ฐพ๊ธฐ ์ํด xarray๋ฅผ ์ฌ์ฉํฉ๋๋ค. 64MB๋น 1๊ฐ์ xarray๋ก ๋ถํ ๋ฉ๋๋ค.
/* mm/zswap.c:200-234 */
static struct xarray *zswap_trees[MAX_SWAPFILES];
static unsigned int nr_zswap_trees[MAX_SWAPFILES];
/* 64M ์ค์ ๊ณต๊ฐ๋ง๋ค ํ๋์ ์ค์ ์ฃผ์ ๊ณต๊ฐ */
#define ZSWAP_ADDRESS_SPACE_SHIFT 14
#define ZSWAP_ADDRESS_SPACE_PAGES (1 << ZSWAP_ADDRESS_SPACE_SHIFT)
static inline struct xarray *swap_zswap_tree(swp_entry_t swp)
{
return &zswap_trees[swp_type(swp)][swp_offset(swp)
ZSWAP_ADDRESS_SPACE_SHIFT];
}
8. swp_entry_t โ PTE์ ๋ค์ด๊ฐ๋ ์ค์ ์์น
์ค์ ์์๋ ์ต๋ช
ํ์ด์ง์ PTE์๋ ๋ฌผ๋ฆฌ PFN ๋์ swp_entry_t๊ฐ ๋ค์ด๊ฐ๋๋ค. type์ ์ด๋ค ์ค์ ์์ญ์ธ์ง ๊ฐ๋ฆฌํค๊ณ , offset์ ๊ทธ ์์ญ ๋ด๋ถ์ ํ์ด์ง ์์น๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
/* include/linux/mm_types.h:281-287 */
typedef struct {
unsigned long val;
} swp_entry_t;
/* include/linux/swapops.h:27-28,84-108 */
#define SWP_TYPE_SHIFT (BITS_PER_XA_VALUE - MAX_SWAPFILES_SHIFT)
#define SWP_OFFSET_MASK ((1UL << SWP_TYPE_SHIFT) - 1)
static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset)
{
swp_entry_t ret;
ret.val = (type << SWP_TYPE_SHIFT) | (offset & SWP_OFFSET_MASK);
return ret;
}
static inline unsigned swp_type(swp_entry_t entry)
{
return (entry.val >> SWP_TYPE_SHIFT);
}
static inline pgoff_t swp_offset(swp_entry_t entry)
{
return entry.val & SWP_OFFSET_MASK;
}
ํต์ฌ ํจ์
1. zswap_store() โ ํ์ด์ง ์์ถ ์ ์ฅ
folio๋ฅผ zswap์ ์์ถ ์ ์ฅํฉ๋๋ค. swap writeback ๊ฒฝ๋ก์์ ํธ์ถ๋ฉ๋๋ค.
/* mm/zswap.c:1488 */
bool zswap_store(struct folio *folio)
ํ๋ฆ:
1. zswap_enabled ํ์ธ โ ๋นํ์ฑํ ์ ์คํจ ์ฒ๋ฆฌ
2. get_obj_cgroup_from_folio() โ memcg ์ถฉ์ ๋์ ํ์ธ
3. zswap_check_limits() โ ํ ํฌ๊ธฐ ์ ํ ์ด๊ณผ ์ ๊ฑฐ๋ถ
4. zswap_pool_current_get() โ ํ์ฌ zswap ํ ํ๋
5. ๊ฐ ํ์ด์ง์ ๋ํด zswap_store_page() ํธ์ถ:
- zswap_entry_cache_alloc() โ ์ํธ๋ฆฌ ํ ๋น
- zswap_compress() โ ์์ถ (crypto API)
- xa_store() โ xarray์ ์ ์ฅ
- zswap_lru_add() โ LRU์ ์ถ๊ฐ
6. ์คํจ ์ ์ด์ ์ ์ ์ฅ๋ stale ์ํธ๋ฆฌ ์ ๋ฆฌ
๋ถ๊ธฐ ์กฐ๊ฑด:
| ์กฐ๊ฑด | ๊ฒฐ๊ณผ |
|---|---|
| `!zswap_enabled` | false ๋ฐํ, ์ด์ stale ์ํธ๋ฆฌ ์ ๋ฆฌ |
| `obj_cgroup_may_zswap() == false` | `shrink_memcg()` ์๋ ํ ์ฌ๊ฒ์ฌ |
| `zswap_check_limits() == true` | false ๋ฐํ (ํ ๊ฐ๋ ์ฐธ) |
| `pool == NULL` | false ๋ฐํ |
| `zswap_store_page() ์คํจ` | false ๋ฐํ, ์ด์ ์ํธ๋ฆฌ ์ ๋ฆฌ |
| ์์ถ ์คํจ ๋๋ `dlen >= PAGE_SIZE` | writeback ํ์ฉ ์ ์๋ณธ PAGE_SIZE๋ก ์ ์ฅ, writeback ๋นํ์ฑํ ์ ๊ฑฐ๋ถ |
2. zswap_load() โ ์์ถ๋ ํ์ด์ง ๋ก๋
xarray์์ zswap_entry๋ฅผ ์ฐพ์ ์์ถ ํด์ ํ folio์ ๋ณต์ํฉ๋๋ค.
/* mm/zswap.c:1594 */
int zswap_load(struct folio *folio)
ํ๋ฆ:
1. zswap_never_enabled() ํ์ธ โ ํ ๋ฒ๋ ํ์ฑํ๋ ์ ์ด ์์ผ๋ฉด -ENOENT
2. folio_test_large() ํ์ธ โ large folio๋ ์ง์ ์ ํจ (-EINVAL)
3. xa_load(tree, offset) โ xarray์์ ์ํธ๋ฆฌ ๊ฒ์
4. zswap_decompress() โ ์์ถ ํด์
5. folio_mark_uptodate() โ folio ์ต์ ์ํ๋ก ํ์
6. swapcache์์ ๋ก๋๋ ๊ฒฝ์ฐ xa_erase() + zswap_entry_free()๋ก ์ํธ๋ฆฌ ์ ๊ฑฐ
๋ฐํ๊ฐ:
| ๊ฐ | ์๋ฏธ |
|---|---|
| `0` | ์ฑ๊ณต (folio unlocked, up-to-date) |
| `-ENOENT` | zswap์ ํด๋น ์ํธ๋ฆฌ ์์ (๋์คํฌ์์ ์ฝ์ด์ผ ํจ) |
| `-EIO` | ์์ถ ํด์ ์คํจ |
| `-EINVAL` | large folio๋ ์ง์ํ์ง ์์ |
3. zswap_compress() โ ํ์ด์ง ์์ถ
์ค์ ์์ถ์ ์ํํฉ๋๋ค. per-CPU ์ปจํ ์คํธ์์ ๋์ํฉ๋๋ค.
/* mm/zswap.c:853 */
static bool zswap_compress(struct page *page, struct zswap_entry *entry,
struct zswap_pool *pool)
ํ๋ฆ:
1. acomp_ctx_get_cpu_lock() โ CPU๋ณ ์์ถ ์ปจํ
์คํธ ํ๋
2. crypto_acomp_compress() โ ๋น๋๊ธฐ ์์ถ ์์ฒญ
3. crypto_wait_req() โ ๋๊ธฐ ๋๊ธฐ
4. dlen >= PAGE_SIZE์ธ ๊ฒฝ์ฐ:
- writeback ๋นํ์ฑํ ์ ๊ฑฐ๋ถ
- writeback ํ์ฑํ ์ ์๋ณธ ๊ทธ๋๋ก ์ ์ฅ
5. zs_malloc() โ zsmalloc์์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น
6. zs_obj_write() โ ์์ถ๋ ๋ฐ์ดํฐ ๊ธฐ๋ก
/* mm/zswap.c:885-929 */
comp_ret = crypto_wait_req(crypto_acomp_compress(acomp_ctx->req), &acomp_ctx->wait);
dlen = acomp_ctx->req->dlen;
if (comp_ret || !dlen || dlen >= PAGE_SIZE) {
if (!mem_cgroup_zswap_writeback_enabled(
folio_memcg(page_folio(page)))) {
comp_ret = comp_ret ? comp_ret : -EINVAL;
goto unlock;
}
comp_ret = 0;
dlen = PAGE_SIZE;
dst = kmap_local_page(page);
mapped = true;
}
gfp = GFP_NOWAIT | __GFP_NORETRY | __GFP_HIGHMEM | __GFP_MOVABLE;
handle = zs_malloc(pool->zs_pool, dlen, gfp, page_to_nid(page));
if (IS_ERR_VALUE(handle)) {
alloc_ret = PTR_ERR((void *)handle);
goto unlock;
}
zs_obj_write(pool->zs_pool, handle, dst, dlen);
entry->handle = handle;
entry->length = dlen;
unlock:
if (mapped)
kunmap_local(dst);
if (comp_ret == -ENOSPC || alloc_ret == -ENOSPC)
zswap_reject_compress_poor++;
else if (comp_ret)
zswap_reject_compress_fail++;
else if (alloc_ret)
zswap_reject_alloc_fail++;
acomp_ctx_put_unlock(acomp_ctx);
return comp_ret == 0 && alloc_ret == 0;
4. shrink_memcg_cb() โ LRU ๊ธฐ๋ฐ ์ฐ๊ธฐ๋ฐฑ
zswap shrinker์ ํต์ฌ ์ฝ๋ฐฑ ํจ์์ ๋๋ค. LRU์์ ์ํธ๋ฆฌ๋ฅผ ์ ํํ์ฌ writebackํฉ๋๋ค.
/* mm/zswap.c:1100 */
static enum lru_status shrink_memcg_cb(struct list_head *item,
struct list_lru_one *l, void *arg)
ํ๋ฆ:
1. entry->referenced == true โ second chance: referenced = false ํ LRU_ROTATE
2. swpentry๋ฅผ ์คํ์ ๋ณต์ฌ (LRU ๋ฝ ํด์ ํ ์ํธ๋ฆฌ ํด์ ๊ฐ๋ฅ)
3. list_move_tail() โ LRU ๋์ผ๋ก ์ด๋
4. zswap_writeback_entry() โ ์ค์ writeback ์๋
5. writeback ์คํจ ์ LRU_RETRY, swapcache์์ ์ด๋ฏธ ๋ฐ๊ฒฌ ์ LRU_STOP
5. swap_alloc_fast() / swap_alloc_slow() โ ์ค์ ์ฌ๋กฏ ํ ๋น
์ค์ ์ฌ๋กฏ ํ ๋น์ ๋ ๊ฐ์ง ๊ฒฝ๋ก์ ๋๋ค.
/* mm/swapfile.c:1318 */
static bool swap_alloc_fast(struct folio *folio)
/* mm/swapfile.c:1348 */
static void swap_alloc_slow(struct folio *folio)
swap_avail_head plist๋ฅผ ์ํํ๋ฉฐ ๊ฐ์ฉํ ๋๋ฐ์ด์ค ํ์/* mm/swapfile.c:1519-1538 */
again:
local_lock(&percpu_swap_cluster.lock);
if (!swap_alloc_fast(folio))
swap_alloc_slow(folio);
local_unlock(&percpu_swap_cluster.lock);
if (!order && unlikely(!folio_test_swapcache(folio))) {
if (swap_sync_discard())
goto again;
}
/* ํ ๋น ์คํจ๋ MEMCG_SWAP_FAIL ๊ณ์ ์ ์ํด ํธ์ถํฉ๋๋ค. */
if (unlikely(mem_cgroup_try_charge_swap(folio, folio->swap)))
swap_cache_del_folio(folio);
if (unlikely(!folio_test_swapcache(folio)))
return -ENOMEM;
return 0;
6. swap_writeout() โ zswap ์๋จ ์บ์ ์ง์
swap_writeout()์ ์ค์ ๋์คํฌ writeback ์ ์ zeromap๊ณผ zswap์ ๋จผ์ ๊ฒ์ฌํฉ๋๋ค. zswap ์ ์ฅ์ด ์ฑ๊ณตํ๋ฉด folio๋ ๋์คํฌ๋ก ์ฐ์ด์ง ์๊ณ unlock๋ฉ๋๋ค.
/* mm/page_io.c:240-285 */
int swap_writeout(struct folio *folio, struct swap_iocb **swap_plug)
{
int ret = 0;
if (folio_free_swap(folio))
goto out_unlock;
ret = arch_prepare_to_swap(folio);
if (ret) {
folio_mark_dirty(folio);
goto out_unlock;
}
if (is_folio_zero_filled(folio)) {
swap_zeromap_folio_set(folio);
goto out_unlock;
}
swap_zeromap_folio_clear(folio);
if (zswap_store(folio)) {
count_mthp_stat(folio_order(folio), MTHP_STAT_ZSWPOUT);
goto out_unlock;
}
if (!mem_cgroup_zswap_writeback_enabled(folio_memcg(folio))) {
folio_mark_dirty(folio);
return AOP_WRITEPAGE_ACTIVATE;
}
__swap_writepage(folio, swap_plug);
return 0;
out_unlock:
folio_unlock(folio);
return ret;
}
ํธ์ถ ํ๋ฆ
์ค์ ์ฐ๊ธฐ ๊ฒฝ๋ก (zswap ์ ์ฅ)
vmscan: shrink_folio_list()
โโ folio_alloc_swap() [swapfile.c:1493] (0์ด๋ฉด ์ฑ๊ณต)
โโ swap_alloc_fast() [swapfile.c:1318] โ fast path
โโ swap_alloc_slow() [swapfile.c:1348] โ slow path
โโ swap_writeout() [page_io.c:240]
โโ is_folio_zero_filled() โ zeromap ์ค์ ํ I/O ์๋ต
โโ zswap_store() [zswap.c:1488]
โโ zswap_check_limits()
โโ zswap_pool_current_get()
โโ zswap_store_page() [zswap.c:1408] (๊ฐ ํ์ด์ง)
โโ zswap_compress() [zswap.c:853]
โ โโ crypto_acomp_compress()
โ โโ zs_malloc() [zsmalloc.c:1297]
โ โโ zs_obj_write() [zsmalloc.c:1176]
โโ xa_store() (xarray์ ์ ์ฅ)
์ค์ ์ฝ๊ธฐ ๊ฒฝ๋ก (zswap ๋ก๋)
do_swap_page() [memory.c:4706]
โโ swap_cache_get_folio() [memory.c:4785 โ swap_state.c:87]
โโ swap_read_folio() [page_io.c:609]
โโ swap_read_folio_zeromap() [page_io.c:508]
โโ zswap_load() [zswap.c:1594]
โ โโ xa_load(tree, offset) โ xarray ๊ฒ์
โ โโ zswap_decompress() [zswap.c:932]
โ โ โโ zs_obj_read_sg_begin() [zsmalloc.c:1114]
โ โ โโ crypto_acomp_decompress()
โ โโ folio_mark_uptodate()
โ โโ (swapcache ๋ก๋ ์) xa_erase() + zswap_entry_free()
โโ zswap_load() == -ENOENT์ด๋ฉด swap_read_folio_bdev_*() [page_io.c:572/594]
Shrinker (์๋ฐ ์ writeback)
kswapd / direct reclaim
โโ zswap_shrinker_scan() [zswap.c:1181]
โโ list_lru_shrink_walk()
โโ shrink_memcg_cb() [zswap.c:1100]
โโ referenced? โ second chance (LRU_ROTATE)
โโ zswap_writeback_entry() [zswap.c:994]
โโ swap_cache_alloc_folio() [swap_state.c:551]
โโ zswap_decompress()
โโ xa_erase()
โโ __swap_writepage() [page_io.c:447] โ ๋์คํฌ์ ๊ธฐ๋ก
์กฐ๊ฑด๋ณ ๋น๊ต
์ค์ ์ฌ๋กฏ ํ ๋น ๊ฒฝ๋ก ๋น๊ต
| ์กฐ๊ฑด | fast path (swap_alloc_fast) | slow path (swap_alloc_slow) |
|---|---|---|
| **์๋** | ๋งค์ฐ ๋น ๋ฆ (per-CPU ํด๋ฌ์คํฐ ์ฌ์ฌ์ฉ) | ๋๋ฆผ (plist ์ํ) |
| **๋์** | per-CPU ํด๋ฌ์คํฐ | swap_avail_head plist |
| **์ ๊ธ** | `percpu_swap_cluster.lock` + cluster lock | `swap_avail_lock` + cluster lock |
| **์ ํฉ์ฑ** | SSD, ๋ค์ค ํ๋ก์ธ์ค | ํ์ ๋๋ฐ์ด์ค, ๋จ์ผ ํ๋ก์ธ์ค |
| **์คํจ ์** | slow path๋ก ํด๋ฐฑ | ๋ชจ๋ ๋๋ฐ์ด์ค ์ํ ํ ์คํจ |
zswap ์ ์ฅ ๊ฒฝ๋ก ๋ถ๊ธฐ
| ์กฐ๊ฑด | ๋์ |
|---|---|
| zswap ๋นํ์ฑํ | false ๋ฐํ, stale ์ํธ๋ฆฌ ์ ๋ฆฌ |
| ํ ํฌ๊ธฐ ์ ํ ์ด๊ณผ | false ๋ฐํ + shrink_worker ํ์ |
| obj_cgroup ๋ฏธํ์ฉ | memcg shrink ํ ์ฌ์๋ |
| ์์ถ ์คํจ (`comp_ret != 0`) | ๊ฑฐ๋ถ (zswap_reject_compress_fail++) |
| ์์ถ ๊ฒฐ๊ณผ๊ฐ PAGE_SIZE ์ด์ | writeback ํ์ฉ ์ ์๋ณธ PAGE_SIZE ์ ์ฅ, ๋นํ์ฑํ ์ ๊ฑฐ๋ถ |
| zsmalloc ํ ๋น ์คํจ | ๊ฑฐ๋ถ (zswap_reject_alloc_fail++) |
| ๋ชจ๋ ์ฑ๊ณต | xarray ์ ์ฅ + LRU ์ถ๊ฐ |
zswap vs ๊ธฐ์กด ์ค์ ๋์คํฌ I/O
| ํญ๋ชฉ | zswap | ๊ธฐ์กด ์ค์ |
|---|---|---|
| **์ ์ฅ ์์น** | RAM (zsmalloc ํ) | ๋์คํฌ/๋ธ๋ก ๋๋ฐ์ด์ค |
| **์ง์ฐ์๊ฐ** | ฮผs (RAM ์ ๊ทผ) | ms (๋์คํฌ I/O) |
| **๋์ญํญ** | ๋ฉ๋ชจ๋ฆฌ ๋์ญํญ | ๋์คํฌ ๋์ญํญ |
| **์ฉ๋ ์ ํ** | ์ต๋ ๋ฉ๋ชจ๋ฆฌ์ 20% (๊ธฐ๋ณธ) | ์ค์ ๋๋ฐ์ด์ค ์ ์ฒด |
| **CPU ์ค๋ฒํค๋** | ์์ถ/ํด์ ๋น์ฉ | I/O ๋๊ธฐ ๋น์ฉ |
| **์ ํฉ ์ํฌ๋ก๋** | ๋์ ์์ถ๋ฅ ํ์ด์ง | ๋ฎ์ ์์ถ๋ฅ ํ์ด์ง |
| **writeback** | shrinker์ ์ํด ๋์คํฌ๋ก ์ ์ก | - |
์ค์ ๊ณต๊ฐ ๊ตฌ์ฑ ๋น๊ต
| ๊ตฌ์ฑ | ์ฅ์ | ๋จ์ | ๋ํ ํ์ธ ๋ช ๋ น |
|---|---|---|---|
| ํํฐ์ ์ค์ | ํ์ผ์์คํ ๊ฒฝ๋ก๋ฅผ ๊ฑฐ์น์ง ์์ ๋จ์ํ๊ณ ์์ธก ๊ฐ๋ฅ | ์ฉ๋ ๋ณ๊ฒฝ์ด ๋ฒ๊ฑฐ๋ก์ | `swapon --show --output=NAME,TYPE,SIZE,USED,PRIO` |
| ํ์ผ ์ค์ | ์ด์ ์ค ์์ฑยทํ์ฅยท์ญ์ ๊ฐ ์ฌ์ | ํ์ผ์์คํ /๋ธ๋ก ๋งคํ ํน์ฑ์ ํจ๊ป ๊ณ ๋ คํด์ผ ํจ | `findmnt -T /swapfile; swapon --show` |
| zram ์ค์ | ๋์คํฌ I/O ์์ด RAM ์์์ ์์ถ ๋ธ๋ก ๋๋ฐ์ด์ค๋ก ๋์ | ์์ถ์ CPU๋ฅผ ์ฐ๊ณ RAM์ ์๋น | `zramctl; swapon --show` |
| zswap | ๊ธฐ์กด ์ค์ ์๋จ์ ์์ถ ์บ์๋ผ ์ค์ ๋๋ฐ์ด์ค์ ํจ๊ป ์ฌ์ฉ | ์์ถ๋ฅ ์ด ๋ฎ์ผ๋ฉด writeback์ผ๋ก ๋ฐ๋ ค๋จ | `cat /sys/module/zswap/parameters/enabled` |
swap cache / zswap / zram ์ญํ ๋น๊ต
| ํญ๋ชฉ | ์์น | ์ฃผ ์ญํ | ํ์ ์ ๋์ |
|---|---|---|---|
| Swap Cache | ์ผ๋ฐ ํ์ด์ง ์บ์/`swapper_space` | swap in/out ์ค ๊ฐ์ ์ค์ ์ํธ๋ฆฌ์ ์ค๋ณต I/O ๋ฐฉ์ง | folio ์ฐธ์กฐ๊ฐ ์ฌ๋ผ์ง๋ฉด ์ผ๋ฐ LRU ํ์ ๋์ |
| zswap | RAM ์์ ์์ถ ํ(`zsmalloc`) | ๋์คํฌ ์ฐ๊ธฐ ์ ์์ถ ์บ์ | shrinker๊ฐ cold entry๋ฅผ ๋์คํฌ๋ก writeback |
| zram | RAM ๊ธฐ๋ฐ ์์ถ ๋ธ๋ก ๋๋ฐ์ด์ค | ์์ฒด๊ฐ ์ค์ ๋๋ฐ์ด์ค | zram ๋๋ฐ์ด์ค ๋ด๋ถ ์์ถ ๊ฐ์ฒด๊ฐ ์ ์ง๋จ |
swappiness ํด์
| ๊ฐ | ์๋ฏธ | ์ฌ์ฉ ์ |
|---|---|---|
| `0`์ ๊ฐ๊น์ | ์ต๋ช ํ์ด์ง ์ค์์ ์ต๋ํ ๋ฆ์ถ๊ณ ํ์ผ ์บ์ ํ์๋ฅผ ์ ํธ | ์ง์ฐ์ ๋ฏผ๊ฐํ DB, ์ถฉ๋ถํ RAM |
| `60` | ์ผ๋ฐ ๋ฐฐํฌํ์ ๊ท ํ๊ฐ | ๋ฒ์ฉ ์๋ฒ/๋ฐ์คํฌํฑ |
| `100` ์ด์ | ์ต๋ช ํ์ด์ง์ ํ์ผ ์บ์ ํ์์์ ์ค์์ ๋ ์ ๊ทน ํ์ฉ | ์์ถ ์ค์/zswap ํจ๊ณผ๊ฐ ํฌ๊ณ I/O ๋ณ๋ชฉ์ด ๋ฎ์ ํ๊ฒฝ |
zsmalloc ํ ๋น ํ๋ฆ ๋น๊ต
| ๋จ๊ณ | zs_malloc() | zs_free() |
|---|---|---|
| **1** | handle ํ ๋น (kmem_cache) | handle โ zspage ์กฐํ |
| **2** | ํฌ๊ธฐ ํด๋์ค ๊ฒฐ์ | class->lock ํ๋ |
| **3** | class->lock ํ๋ | ํต๊ณ ๊ฐ์ (ZS_OBJS_INUSE) |
| **4** | find_get_zspage() โ ๊ธฐ์กด zspage ํ์ | obj_free() โ freelist ์ฐ๊ฒฐ |
| **5** | ์์ผ๋ฉด alloc_zspage() โ ์ zspage | fix_fullness_group() |
| **6** | obj_malloc() โ ๊ฐ์ฒด ํ ๋น | ๋น์ด ์์ผ๋ฉด free_zspage() |
| **7** | fix_fullness_group() โ fullness ๊ฐฑ์ | handle ํด์ (kmem_cache) |
zswap ํ๋ผ๋ฏธํฐ ์์ฝ
| ํ๋ผ๋ฏธํฐ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
| `enabled` | CONFIG_ZSWAP_DEFAULT_ON | zswap ํ์ฑํ/๋นํ์ฑํ |
| `compressor` | CONFIG_ZSWAP_COMPRESSOR_DEFAULT | ์์ถ ์๊ณ ๋ฆฌ์ฆ (zstd, lz4, lzo ๋ฑ) |
| `zpool` | zsmalloc ๊ณ์ด ์ค์ | ์์ถ ๊ฐ์ฒด๋ฅผ ๋ด๋ zpool ๋ฐฑ์๋ |
| `max_pool_percent` | 20 | zswap ํ ์ต๋ ๋น์จ (% of RAM) |
| `accept_threshold_percent` | 90 | ํ ์ ํ ์ด๊ณผ ํ ์ฌ์์ฉ ๊ธฐ์ค (%) |
| `shrinker_enabled` | CONFIG_ZSWAP_SHRINKER_DEFAULT_ON | ๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์ shrinker ๋์ |