๐Ÿ”„ 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 ํ˜ธ์ถœ ํ๋ฆ„

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)
  • fast path: ํ˜„์žฌ CPU์˜ per-CPU ํด๋Ÿฌ์Šคํ„ฐ์—์„œ ์ฆ‰์‹œ ํ• ๋‹น ์‹œ๋„
  • slow path: 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 ํšŒ์ˆ˜ ๋Œ€์ƒ
    zswapRAM ์•ˆ์˜ ์••์ถ• ํ’€(`zsmalloc`)๋””์Šคํฌ ์“ฐ๊ธฐ ์ „ ์••์ถ• ์บ์‹œshrinker๊ฐ€ cold entry๋ฅผ ๋””์Šคํฌ๋กœ writeback
    zramRAM ๊ธฐ๋ฐ˜ ์••์ถ• ๋ธ”๋ก ๋””๋ฐ”์ด์Šค์ž์ฒด๊ฐ€ ์Šค์™‘ ๋””๋ฐ”์ด์Šค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() โ†’ ์ƒˆ zspagefix_fullness_group()
    **6**obj_malloc() โ†’ ๊ฐ์ฒด ํ• ๋‹น๋น„์–ด ์žˆ์œผ๋ฉด free_zspage()
    **7**fix_fullness_group() โ†’ fullness ๊ฐฑ์‹ handle ํ•ด์ œ (kmem_cache)

    zswap ํŒŒ๋ผ๋ฏธํ„ฐ ์š”์•ฝ

    ํŒŒ๋ผ๋ฏธํ„ฐ๊ธฐ๋ณธ๊ฐ’์„ค๋ช…
    `enabled`CONFIG_ZSWAP_DEFAULT_ONzswap ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™”
    `compressor`CONFIG_ZSWAP_COMPRESSOR_DEFAULT์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜ (zstd, lz4, lzo ๋“ฑ)
    `zpool`zsmalloc ๊ณ„์—ด ์„ค์ •์••์ถ• ๊ฐ์ฒด๋ฅผ ๋‹ด๋Š” zpool ๋ฐฑ์—”๋“œ
    `max_pool_percent`20zswap ํ’€ ์ตœ๋Œ€ ๋น„์œจ (% of RAM)
    `accept_threshold_percent`90ํ’€ ์ œํ•œ ์ดˆ๊ณผ ํ›„ ์žฌ์ˆ˜์šฉ ๊ธฐ์ค€ (%)
    `shrinker_enabled`CONFIG_ZSWAP_SHRINKER_DEFAULT_ON๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ• ์‹œ shrinker ๋™์ž‘

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” โ€” ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์กฐ๊ฐ๋„
  • Buddy Allocator โ€” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜ โ€” kswapd, direct reclaim, LRU
  • zsmalloc โ€” ์••์ถ• ๋ฐ์ดํ„ฐ ์ „์šฉ ํ• ๋‹น์ž ์ƒ์„ธ
  • OOM Killer โ€” ์Šค์™‘ ๋ถ€์กฑ ์‹œ ์ตœํ›„ ์ˆ˜๋‹จ
  • Memory Cgroup โ€” ์ปจํ…Œ์ด๋„ˆ๋ณ„ ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ
  • SLUB ํ• ๋‹น์ž โ€” ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ ์บ์‹ฑ
  • Compaction โ€” ๋ฉ”๋ชจ๋ฆฌ ๋‹จํŽธํ™” ํ•ด์†Œ
  • Folio / Page Cache โ€” ํŒŒ์ผ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€ ๊ด€๋ฆฌ