๐Ÿงฎ zsmalloc

๊ด€๋ จ ์†Œ์Šค: mm/zsmalloc.c, mm/zpdesc.h, include/linux/zsmalloc.h
๊ด€๋ จ ๋ฌธ์„œ: Swap / zswap ยท SLUB ํ• ๋‹น์ž ยท Buddy Allocator

๊ฐœ์š” (Overview)

zsmalloc์€ ๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ ์••์ถ• ๋ฉ”๋ชจ๋ฆฌ ์ „์šฉ ํ• ๋‹น์ž์ž…๋‹ˆ๋‹ค. zswap, zram ๋“ฑ์—์„œ ์••์ถ•๋œ ํŽ˜์ด์ง€ ํ”„๋ ˆ์ž„์„ ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ผ๋ฐ˜์ ์ธ slab ํ• ๋‹น์ž(SLUB)์™€ ๋‹ฌ๋ฆฌ ์—ฌ๋Ÿฌ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€์— ๊ฑธ์ณ ๋ถ„์‚ฐ๋œ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์  ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ ํŠน์ง•์ž…๋‹ˆ๋‹ค.

์ผ๋ฐ˜ slab ํ• ๋‹น์ž๋Š” ํ•˜๋‚˜์˜ ๊ฐ์ฒด๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€ ์•ˆ์— ์™„์ „ํžˆ ๋“ค์–ด๊ฐ€์•ผ ํ•˜์ง€๋งŒ, zsmalloc์€ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์— ๊ฑธ์ณ ๋ถ„์‚ฐ๋œ ๊ฐ์ฒด๋„ ํ•˜๋‚˜์˜ zspage๋กœ ๋ฌถ์–ด ํ• ๋‹น/ํ•ด์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์••์ถ• ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๊ฐ€ ํŽ˜์ด์ง€ ๊ฒฝ๊ณ„๋ฅผ ๋„˜๋‚˜๋“ค ๋•Œ๋„ ํšจ์œจ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์šด์˜ ๊ด€์ ์—์„œ๋Š” zsmalloc์„ "์••์ถ•๋œ ์ง ์ƒ์ž๋ฅผ ์—ฌ๋Ÿฌ ์นธ์งœ๋ฆฌ ์„ ๋ฐ˜์— ๋ผ์›Œ ๋„ฃ๊ณ , ์†์žก์ด(handle)๋งŒ ๋“ค๊ณ  ์œ„์น˜๋ฅผ ์ฐพ์•„๊ฐ€๋Š” ์ฐฝ๊ณ "์ฒ˜๋Ÿผ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค. zswap์ด๋‚˜ zram์€ ์••์ถ• ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ณด๊ด€ํ•˜๊ณ , ์‹ค์ œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ๋ฌถ์Œ๊ณผ ๋นˆ ์Šฌ๋กฏ ๊ด€๋ฆฌ, compaction, migration ๊ฐ™์€ ์ €์žฅ์†Œ ์šด์˜์€ zsmalloc์ด ๋งก์Šต๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ:
mm/zsmalloc.c        โ† ํ•ต์‹ฌ ํ• ๋‹น ๋กœ์ง (2258์ค„)
mm/zpdesc.h          โ† zpdesc ๊ตฌ์กฐ์ฒด (struct page ์˜ค๋ฒ„๋ ˆ์ด)
include/linux/zsmalloc.h  โ† ์™ธ๋ถ€ API ์„ ์–ธ

๋น ๋ฅธ ์ ๊ฒ€ ๋ช…๋ น

# zsmalloc ๋ชจ๋“ˆ ๋กœ๋“œ ์ƒํƒœ ํ™•์ธ
lsmod | grep zsmalloc

# zsmalloc ํ†ต๊ณ„ (CONFIG_ZSMALLOC_STAT ํ™œ์„ฑํ™” ์‹œ)
cat /sys/kernel/debug/zsmalloc/*/classes

# zsmalloc ํ’€์—์„œ ์‚ฌ์šฉ ์ค‘์ธ ํŽ˜์ด์ง€ ์ˆ˜
cat /sys/kernel/debug/zsmalloc/*/classes | awk '{sum += $14} END {print "total pages:", sum}'

# zswap์—์„œ ์‚ฌ์šฉํ•˜๋Š” zsmalloc ํ’€ ํ™•์ธ
cat /sys/kernel/mm/zswap/mm_stat

# zsmalloc ๊ด€๋ จ ์ปค๋„ ์„ค์ • ํ™•์ธ
grep CONFIG_ZSMALLOC /boot/config-$(uname -r)

# ํ˜„์žฌ ์‹œ์Šคํ…œ์˜ Zswap / Zswapped ๋ฉ”๋ชจ๋ฆฌ๋Ÿ‰ ํ™•์ธ
grep -E 'Zswap|Zswapped' /proc/meminfo

# zspage compaction ์ƒํƒœ ํ™•์ธ (kern.msgbuf ๋˜๋Š” dmesg)
dmesg | grep -i "zsmalloc\|zspage"

# ํ˜„์žฌ ์‹œ์Šคํ…œ์˜ zsmalloc pool ์ˆ˜
ls /sys/kernel/debug/zsmalloc/ 2>/dev/null || echo "debugfs not available"

# zram๊ณผ zsmalloc ์—ฐ๊ฒฐ ํ™•์ธ
cat /sys/block/zram0/mm_stat 2>/dev/null

# ํด๋ž˜์Šค๋ณ„ ํ†ต๊ณ„๋ฅผ ํ‘œ ํ˜•ํƒœ๋กœ ๋ณด๊ธฐ ์ข‹๊ฒŒ ์ •๋ ฌ
cat /sys/kernel/debug/zsmalloc/*/classes 2>/dev/null | column -t

ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ

struct zpdesc โ€” ํŽ˜์ด์ง€ ์„ค๋ช…์ž (struct page ์˜ค๋ฒ„๋ ˆ์ด)

zpdesc๋Š” struct page์˜ ํ•„๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์—ฌ zsmalloc์ด ๊ด€๋ฆฌํ•˜๋Š” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

/* mm/zpdesc.h:32-50 */
struct zpdesc {
unsigned long flags;           /* ํŽ˜์ด์ง€ ํ”Œ๋ž˜๊ทธ (PG_private: ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€ ์‹๋ณ„) */
struct list_head lru;          /* ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์šฉ LRU */
unsigned long movable_ops;     /* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ ์˜คํผ๋ ˆ์ด์…˜ */
union {
struct zpdesc *next;       /* ๋‹ค์Œ zpdesc (zspage ๋‚ด ์—ฐ๊ฒฐ) */
unsigned long handle;      /* huge zspage์˜ ํ• ๋‹น ํ•ธ๋“ค */
};
struct zspage *zspage;         /* ์†Œ์† zspage ํฌ์ธํ„ฐ */
unsigned int first_obj_offset; /* ์ฒซ ๋ฒˆ์งธ ๊ฐ์ฒด์˜ ํŽ˜์ด์ง€ ๋‚ด ์˜คํ”„์…‹ (ํ•˜์œ„ 24๋น„ํŠธ) */
atomic_t _refcount;            /* ์ฐธ์กฐ ์นด์šดํŠธ */
};
/* struct page์™€ ์ •ํ™•ํžˆ ๊ฐ™์€ ํฌ๊ธฐ/์˜คํ”„์…‹ ๋งคํ•‘ (static_assert๋กœ ๊ฒ€์ฆ) */

struct zspage โ€” zsmalloc ํ• ๋‹น ๋‹จ์œ„

์—ฌ๋Ÿฌ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€(zpdesc)๋กœ ๊ตฌ์„ฑ๋œ ๋…ผ๋ฆฌ์  ํ• ๋‹น ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.

/* mm/zsmalloc.c:261-274 */
struct zspage {
struct {
unsigned int huge:HUGE_BITS;       /* 1: ํ•˜๋‚˜์˜ zpdesc์— ํ•˜๋‚˜์˜ ๊ฐ์ฒด (huge class) */
unsigned int fullness:FULLNESS_BITS;/* ํ˜„์žฌ fullness ๊ทธ๋ฃน ์ธ๋ฑ์Šค */
unsigned int class:CLASS_BITS + 1;  /* ์†Œ์† size_class ์ธ๋ฑ์Šค */
unsigned int magic:MAGIC_VAL_BITS;  /* ZSPAGE_MAGIC (0x58) ๊ฒ€์ฆ์šฉ */
};
unsigned int inuse;                     /* ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ ๊ฐ์ฒด ์ˆ˜ */
unsigned int freeobj;                   /* freelist์˜ ์ฒซ ๋ฒˆ์งธ ๊ฐ์ฒด ์ธ๋ฑ์Šค */
struct zpdesc *first_zpdesc;            /* ์ฒซ ๋ฒˆ์งธ zpdesc ํฌ์ธํ„ฐ */
struct list_head list;                  /* size_class์˜ fullness_list ์—ฐ๊ฒฐ์šฉ */
struct zs_pool *pool;                   /* ์†Œ์† ํ’€ ํฌ์ธํ„ฐ */
struct zspage_lock zsl;                 /* zspage๋ณ„ ๋ฝ */
};

struct zs_pool โ€” zsmalloc ํ’€

zsmalloc ํ• ๋‹น์ž์˜ ์ตœ์ƒ์œ„ ๊ด€๋ฆฌ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.

/* mm/zsmalloc.c:199-220 */
struct zs_pool {
const char *name;                       /* ํ’€ ์ด๋ฆ„ (์˜ˆ: "zswap") */
struct size_class *size_class[ZS_SIZE_CLASSES]; /* ํฌ๊ธฐ๋ณ„ ํด๋ž˜์Šค ๋ฐฐ์—ด */
atomic_long_t pages_allocated;          /* ํ• ๋‹น๋œ ์ด ํŽ˜์ด์ง€ ์ˆ˜ */
struct zs_pool_stats stats;             /* compaction ํ†ต๊ณ„ */
struct shrinker *shrinker;              /* ๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜์šฉ shrinker */
rwlock_t lock;                          /* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜/compaction ๋ณดํ˜ธ */
atomic_t compaction_in_progress;        /* compaction ์ง„ํ–‰ ์ค‘ ํ”Œ๋ž˜๊ทธ */
#ifdef CONFIG_COMPACTION
struct work_struct free_work;           /* ์ง€์—ฐ ํ•ด์ œ ์›Œํฌํ */
#endif
};

struct size_class โ€” ํฌ๊ธฐ๋ณ„ ํด๋ž˜์Šค

๋™์ผํ•œ ํฌ๊ธฐ์˜ ๊ฐ์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” zspage ๊ทธ๋ฃน์ž…๋‹ˆ๋‹ค.

/* mm/zsmalloc.c:160-174 */
struct size_class {
spinlock_t lock;                        /* ํด๋ž˜์Šค ๋‚ด ๋™์‹œ์„ฑ ์ œ์–ด */
struct list_head fullness_list[NR_FULLNESS_GROUPS]; /* fullness๋ณ„ freelist */
int size;                               /* ๊ฐ์ฒด ํฌ๊ธฐ (ZS_ALIGN ๋ฐฐ์ˆ˜) */
int objs_per_zspage;                    /* zspage๋‹น ์ตœ๋Œ€ ๊ฐ์ฒด ์ˆ˜ */
int pages_per_zspage;                   /* zspage๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned int index;                     /* ํด๋ž˜์Šค ์ธ๋ฑ์Šค */
struct zs_size_stat stats;              /* ํ• ๋‹น/์‚ฌ์šฉ ํ†ต๊ณ„ */
};

struct link_free โ€” free list ๋…ธ๋“œ

zspage ๋‚ด๋ถ€์˜ ๋นˆ ๊ฐ์ฒด์— ์‚ฝ์ž…๋˜์–ด singly linked list๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

/* mm/zsmalloc.c:182-194 */
struct link_free {
union {
unsigned long next;     /* ๋‹ค์Œ ๋นˆ ๊ฐ์ฒด์˜ ์ธ๋ฑ์Šค (OBJ_TAG_BITS ์‹œํ”„ํŠธ) */
unsigned long handle;   /* ํ• ๋‹น๋œ ๊ฐ์ฒด์˜ ํ•ธ๋“ค */
};
};

Handle ์ธ์ฝ”๋”ฉ ๋ฐฉ์‹

๊ฐ์ฒด ์œ„์น˜๋Š” (PFN, obj_idx) ์Œ์œผ๋กœ ํ•˜๋‚˜์˜ unsigned long ํ•ธ๋“ค์— ์ธ์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค.

/* mm/zsmalloc.c:56-92 */
#define _PFN_BITS               (MAX_POSSIBLE_PHYSMEM_BITS - PAGE_SHIFT)

/*
* ํ• ๋‹น๋œ ๊ฐ์ฒด์˜ ํ—ค๋”๋Š” OBJ_ALLOCATED_TAG๋ฅผ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.
* ์ตœํ•˜์œ„ ๋น„ํŠธ์— ์ƒํƒœ ๋น„ํŠธ๋ฅผ ๋„ฃ์–ด๋„ ๋˜๋Š” ์ด์œ ๋Š”
* handle์ด ์ •๋ ฌ๋œ ์ฃผ์†Œ๋ผ์„œ ํ•˜์œ„ ๋น„ํŠธ ์—ฌ์œ ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
*/
#define OBJ_ALLOCATED_TAG       1

#define OBJ_TAG_BITS            1
#define OBJ_TAG_MASK            OBJ_ALLOCATED_TAG

#define OBJ_INDEX_BITS          (BITS_PER_LONG - _PFN_BITS)
#define OBJ_INDEX_MASK          ((_AC(1, UL) << OBJ_INDEX_BITS) - 1)

enum fullness_group โ€” zspage ์ ์œ ์œจ ๊ตฌ๊ฐ„

fullness_list๋Š” size_class ์•ˆ์—์„œ ๊ฐ™์€ ํฌ๊ธฐ ๊ฐ์ฒด๋ฅผ ๋‹ด๋Š” zspage๋ฅผ ์ ์œ ์œจ๋ณ„๋กœ ๋‚˜๋ˆ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. compaction์€ ์ด ๊ตฌ๊ฐ„์„ ๊ธฐ์ค€์œผ๋กœ ๊ฐ€์žฅ ๋น„์–ด ์žˆ๋Š” zspage์™€ ๊ฐ€์žฅ ์ฐจ ์žˆ๋Š” zspage๋ฅผ ๊ณจ๋ผ ๊ฐ์ฒด๋ฅผ ์˜ฎ๊น๋‹ˆ๋‹ค.

/* mm/zsmalloc.c:134-140 */
enum fullness_group {
	ZS_INUSE_RATIO_0,
	ZS_INUSE_RATIO_10,
	/* 8๊ฐœ์˜ fullness ๊ทธ๋ฃน์ด ์ค‘๊ฐ„์— ๋” ์žˆ๋‹ค */
	ZS_INUSE_RATIO_99       = 10,
	ZS_INUSE_RATIO_100,
	NR_FULLNESS_GROUPS,
};

ํ•ต์‹ฌ ํ•จ์ˆ˜

zs_malloc โ€” ๊ฐ์ฒด ํ• ๋‹น

/* mm/zsmalloc.c:1297-1354 */
unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp, const int nid);

์—ญํ• : ํ’€์—์„œ ์ง€์ •๋œ ํฌ๊ธฐ์˜ ๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

๋ถ„๊ธฐ ๋กœ์ง:

1. size == 0 โ†’ ERR_PTR(-EINVAL) ๋ฐ˜ํ™˜

2. size > ZS_MAX_ALLOC_SIZE โ†’ ERR_PTR(-ENOSPC) ๋ฐ˜ํ™˜

3. handle ์บ์‹œ์—์„œ ํ• ๋‹น ์‹คํŒจ โ†’ ERR_PTR(-ENOMEM) ๋ฐ˜ํ™˜

4. find_get_zspage()๋กœ ๊ธฐ์กด zspage ํƒ์ƒ‰

- zspage ์กด์žฌ โ†’ obj_malloc() + fix_fullness_group() ํ›„ ๋ฐ˜ํ™˜

- zspage ์—†์Œ โ†’ alloc_zspage()๋กœ ์ƒˆ๋กœ ์ƒ์„ฑ โ†’ obj_malloc() + insert_zspage()

zs_free โ€” ๊ฐ์ฒด ํ•ด์ œ

/* mm/zsmalloc.c:1384-1417 */
void zs_free(struct zs_pool *pool, unsigned long handle);

์—ญํ• : ํ• ๋‹น๋œ ๊ฐ์ฒด๋ฅผ ํ’€์— ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋ถ„๊ธฐ ๋กœ์ง:

1. handle์ด ERR_PTR ๋˜๋Š” NULL โ†’ ์ฆ‰์‹œ ๋ฐ˜ํ™˜

2. obj_free()๋กœ freelist์— ์‚ฝ์ž…

3. fix_fullness_group()๋กœ fullness ๊ทธ๋ฃน ๊ฐฑ์‹ 

4. fullness == ZS_INUSE_RATIO_0 โ†’ free_zspage()๋กœ zspage ์ „์ฒด ํ•ด์ œ

zs_compact โ€” ํ’€ ์ปดํŒฉ์…˜

/* mm/zsmalloc.c:1938-1964 */
unsigned long zs_compact(struct zs_pool *pool);

์—ญํ• : ๊ฐ ํฌ๊ธฐ ํด๋ž˜์Šค์˜ zspage๋ฅผ ์••์ถ•ํ•˜์—ฌ ๋นˆ ํŽ˜์ด์ง€๋ฅผ ํšŒ์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.

๋ถ„๊ธฐ ๋กœ์ง:

1. compaction_in_progress๊ฐ€ ์ด๋ฏธ 1์ด๋ฉด ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€ (0 ๋ฐ˜ํ™˜)

2. ํฐ ํฌ๊ธฐ ํด๋ž˜์Šค๋ถ€ํ„ฐ ์ˆœํšŒํ•˜๋ฉฐ __zs_compact() ํ˜ธ์ถœ

3. __zs_compact() ๋‚ด๋ถ€:

- isolate_dst_zspage()๋กœ ๊ฐ€์žฅ ๋งŽ์ด ์ฐฌ zspage๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์„ ํƒ

- isolate_src_zspage()๋กœ ๊ฐ€์žฅ ์ ๊ฒŒ ์ฐฌ zspage๋ฅผ ์†Œ์Šค๋กœ ์„ ํƒ

- migrate_zspage()๋กœ ๊ฐ์ฒด๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๋Œ€์ƒ zspage์— ์ด๋™

- ์†Œ์Šค zspage๊ฐ€ ๋น„๋ฉด free_zspage()๋กœ ํ•ด์ œ

zs_shrinker_scan โ€” ๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ• ์‹œ compaction ์ง„์ž…

/* mm/zsmalloc.c:1972-1985 */
static unsigned long zs_shrinker_scan(struct shrinker *shrinker,
		struct shrink_control *sc);

์—ญํ• : shrinker ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ• ์‹œ zsmalloc ํ’€์—์„œ ํšŒ์ˆ˜ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€๋ฅผ ์ฐพ๊ณ  zs_compact()๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๋ถ„๊ธฐ ๋กœ์ง:

1. shrinker->private_data์—์„œ ๋Œ€์ƒ zs_pool ํš๋“

2. zs_compact(pool) ์‹คํ–‰

3. ํšŒ์ˆ˜ํ•œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜

4. ํšŒ์ˆ˜ํ•˜์ง€ ๋ชปํ•˜๋ฉด SHRINK_STOP ๋ฐ˜ํ™˜

zs_obj_read_begin / zs_obj_read_end โ€” ๊ฐ์ฒด ์ฝ๊ธฐ

/* mm/zsmalloc.c:1037-1085 */
void *zs_obj_read_begin(struct zs_pool *pool, unsigned long handle,
size_t mem_len, void *local_copy);
/* mm/zsmalloc.c:1087-1112 */
void zs_obj_read_end(struct zs_pool *pool, unsigned long handle,
size_t mem_len, void *handle_mem);

์—ญํ• : ๊ฐ์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ฝ๊ธฐ ์œ„ํ•ด ๋งคํ•‘/์–ธ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.

๋ถ„๊ธฐ ๋กœ์ง:

1. pool->lock read_lock์œผ๋กœ ํ•ธ๋“คโ†’zpdesc ๋ณ€ํ™˜ ๋ณดํ˜ธ

2. zspage_read_lock()์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ค‘๋‹จ ๋ณด์žฅ

3. ๊ฐ์ฒด๊ฐ€ ํ•œ ํŽ˜์ด์ง€์— ์™„์ „ํžˆ ํฌํ•จ โ†’ kmap_local_zpdesc()๋กœ ์ง์ ‘ ๋งคํ•‘

4. ๊ฐ์ฒด๊ฐ€ ๋‘ ํŽ˜์ด์ง€์— ๊ฑธ์นจ โ†’ local_copy์— ๋ณต์‚ฌํ•˜์—ฌ ๋ฐ˜ํ™˜

zs_page_migrate โ€” ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

/* mm/zsmalloc.c:1686-1777 */
static int zs_page_migrate(struct page *newpage, struct page *page,
enum migrate_mode mode);

์—ญํ• : zsmalloc ํŽ˜์ด์ง€๋ฅผ ๋‹ค๋ฅธ ๋ฌผ๋ฆฌ ์œ„์น˜๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค (compaction, NUMA balancing).

๋ถ„๊ธฐ ๋กœ์ง:

1. zpdesc->zspage == NULL โ†’ ์ด๋ฏธ ํ•ด์ œ๋œ ํŽ˜์ด์ง€, ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ถˆ๊ฐ€

2. pool->lock write_lock + class->lock spin_lock ํš๋“

3. zspage_write_trylock() ์‹คํŒจ โ†’ -EINVAL ๋ฐ˜ํ™˜

4. ์ƒˆ ํŽ˜์ด์ง€์— ์ „์ฒด ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ โ†’ handle ์—…๋ฐ์ดํŠธ โ†’ replace_sub_page() ํ˜ธ์ถœ

ํ˜ธ์ถœ ํ๋ฆ„

ํ• ๋‹น ํ๋ฆ„

zs_malloc(pool, size, gfp, nid)
โ”œโ”€โ”€ cache_alloc_handle()              โ† handle ์บ์‹œ์—์„œ ํ• ๋‹น
โ”œโ”€โ”€ get_size_class_index(size)        โ† ํฌ๊ธฐ โ†’ ํด๋ž˜์Šค ์ธ๋ฑ์Šค ๋ณ€ํ™˜
โ”œโ”€โ”€ [class->lock]
โ”œโ”€โ”€ find_get_zspage(class)            โ† fullness_list์—์„œ ๋นˆ zspage ํƒ์ƒ‰
โ”‚   โ””โ”€โ”€ (ZS_INUSE_RATIO_99 โ†’ 0 ์ˆœ์„œ)
โ”œโ”€โ”€ [zspage ๋ฐœ๊ฒฌ ์‹œ]
โ”‚   โ”œโ”€โ”€ obj_malloc(pool, zspage, handle)
โ”‚   โ”‚   โ”œโ”€โ”€ get_freeobj()             โ† freelist head
โ”‚   โ”‚   โ”œโ”€โ”€ link->handle = handle     โ† handle ๊ธฐ๋ก
โ”‚   โ”‚   โ”œโ”€โ”€ location_to_obj()         โ† (zpdesc, obj_idx) โ†’ obj ์ธ์ฝ”๋”ฉ
โ”‚   โ”‚   โ””โ”€โ”€ record_obj(handle, obj)   โ† handle์— obj ์ €์žฅ
โ”‚   โ””โ”€โ”€ fix_fullness_group()          โ† fullness ๊ทธ๋ฃน ๊ฐฑ์‹ 
โ”œโ”€โ”€ [zspage ๋ฏธ๋ฐœ๊ฒฌ ์‹œ]
โ”‚   โ”œโ”€โ”€ alloc_zspage(pool, class, gfp, nid)
โ”‚   โ”‚   โ”œโ”€โ”€ cache_alloc_zspage()      โ† zspage ๊ตฌ์กฐ์ฒด ํ• ๋‹น
โ”‚   โ”‚   โ”œโ”€โ”€ alloc_zpdesc() ร— N        โ† ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น (N = pages_per_zspage)
โ”‚   โ”‚   โ”œโ”€โ”€ create_page_chain()       โ† zpdesc๋“ค ์—ฐ๊ฒฐ
โ”‚   โ”‚   โ””โ”€โ”€ init_zspage()             โ† freelist ์ดˆ๊ธฐํ™” (link_free ์—ฐ๊ฒฐ)
โ”‚   โ”œโ”€โ”€ obj_malloc()
โ”‚   โ”œโ”€โ”€ insert_zspage(class, zspage, newfg)
โ”‚   โ””โ”€โ”€ SetZsPageMovable()            โ† ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ ํ”Œ๋ž˜๊ทธ ์„ค์ •
โ””โ”€โ”€ [class->lock ํ•ด์ œ] โ†’ handle ๋ฐ˜ํ™˜

ํ•ด์ œ ํ๋ฆ„

zs_free(pool, handle)
โ”œโ”€โ”€ [pool->lock read_lock]
โ”œโ”€โ”€ handle_to_obj(handle)             โ† handle โ†’ obj ๋””์ฝ”๋”ฉ
โ”œโ”€โ”€ obj_to_zpdesc(obj)                โ† obj โ†’ zpdesc ๋ณ€ํ™˜
โ”œโ”€โ”€ [class->lock]
โ”œโ”€โ”€ class_stat_sub(ZS_OBJS_INUSE)     โ† ์‚ฌ์šฉ ์ค‘์ธ ๊ฐ์ฒด ์ˆ˜ ๊ฐ์†Œ
โ”œโ”€โ”€ obj_free(class_size, obj)
โ”‚   โ”œโ”€โ”€ obj_to_location()             โ† obj โ†’ (zpdesc, obj_idx)
โ”‚   โ”œโ”€โ”€ link->next = freeobj << BITS  โ† freelist์— ์‚ฝ์ž…
โ”‚   โ””โ”€โ”€ mod_zspage_inuse(zspage, -1)  โ† inuse ์นด์šดํŠธ ๊ฐ์†Œ
โ”œโ”€โ”€ fix_fullness_group()              โ† fullness ๊ทธ๋ฃน ๊ฐฑ์‹ 
โ”œโ”€โ”€ [fullness == ZS_INUSE_RATIO_0]
โ”‚   โ””โ”€โ”€ free_zspage(pool, class, zspage)
โ”‚       โ”œโ”€โ”€ trylock_zspage()          โ† ๋ชจ๋“  zpdesc ์ž ๊ธˆ ์‹œ๋„
โ”‚       โ”‚   โ””โ”€โ”€ [์ž ๊ธˆ ์‹คํŒจ ์‹œ] kick_deferred_free()
โ”‚       โ”œโ”€โ”€ remove_zspage()           โ† fullness_list์—์„œ ์ œ๊ฑฐ
โ”‚       โ””โ”€โ”€ __free_zspage()
โ”‚           โ”œโ”€โ”€ reset_zpdesc() ร— N    โ† zpdesc ์ดˆ๊ธฐํ™”
โ”‚           โ”œโ”€โ”€ free_zpdesc() ร— N     โ† ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ•ด์ œ
โ”‚           โ””โ”€โ”€ cache_free_zspage()   โ† zspage ๊ตฌ์กฐ์ฒด ํ•ด์ œ
โ””โ”€โ”€ cache_free_handle()               โ† handle ํ•ด์ œ

Compaction ํ๋ฆ„

zs_compact(pool)
โ”œโ”€โ”€ [compaction_in_progress ํ™•์ธ]
โ”œโ”€โ”€ [ํฐ ํฌ๊ธฐ ํด๋ž˜์Šค โ†’ ์ž‘์€ ํฌ๊ธฐ ํด๋ž˜์Šค ์ˆœํšŒ]
โ””โ”€โ”€ __zs_compact(pool, class)
โ”œโ”€โ”€ [pool->lock write_lock + class->lock]
โ”œโ”€โ”€ while (zs_can_compact(class))
โ”‚   โ”œโ”€โ”€ isolate_dst_zspage()      โ† ๊ฐ€์žฅ ์ฐฌ zspage ์„ ํƒ
โ”‚   โ”œโ”€โ”€ isolate_src_zspage()      โ† ๊ฐ€์žฅ ๋นˆ zspage ์„ ํƒ
โ”‚   โ”œโ”€โ”€ migrate_zspage()
โ”‚   โ”‚   โ””โ”€โ”€ while (์†Œ์Šค์— ๊ฐ์ฒด ๋‚จ์Œ)
โ”‚   โ”‚       โ”œโ”€โ”€ find_alloced_obj()  โ† ํ• ๋‹น๋œ ๊ฐ์ฒด ์ฐพ๊ธฐ
โ”‚   โ”‚       โ”œโ”€โ”€ obj_malloc()        โ† ๋Œ€์ƒ์— ์ƒˆ ์œ„์น˜ ํ• ๋‹น
โ”‚   โ”‚       โ”œโ”€โ”€ zs_object_copy()    โ† ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ
โ”‚   โ”‚       โ””โ”€โ”€ obj_free()          โ† ์†Œ์Šค ์œ„์น˜ ํ•ด์ œ
โ”‚   โ”œโ”€โ”€ [์†Œ์Šค zspage ๋น„๋ฉด] free_zspage()
โ”‚   โ””โ”€โ”€ [๋Œ€์ƒ zspage ์ฐจ๋ฉด] putback_zspage() + ์ƒˆ ๋Œ€์ƒ ์„ ํƒ
โ””โ”€โ”€ [๋ฐ˜๋ณต ์ข…๋ฃŒ]

๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ•์—์„œ compaction์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ํ๋ฆ„

๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ•
โ””โ”€โ”€ shrink_slab()/shrinker ํ˜ธ์ถœ
โ””โ”€โ”€ zs_shrinker_scan(shrinker, sc)
โ”œโ”€โ”€ pool = shrinker->private_data
โ”œโ”€โ”€ pages_freed = zs_compact(pool)
โ””โ”€โ”€ [pages_freed == 0] SHRINK_STOP

์กฐ๊ฑด๋ณ„ ๋น„๊ต

size_class ๊ฒฐ์ • ๊ธฐ์ค€

์กฐ๊ฑด๋™์ž‘๋น„๊ณ 
`size <= ZS_MIN_ALLOC_SIZE`class index = 0์ตœ์†Œ 32๋ฐ”์ดํŠธ
`size > ZS_MAX_ALLOC_SIZE`ํ• ๋‹น ์‹คํŒจ (ENOSPC)PAGE_SIZE ์ดˆ๊ณผ ๋ถˆ๊ฐ€
`is_power_of_2(class_size)`pages_per_zspage = 1๋‚ญ๋น„ 0
`class_size`๊ฐ€ 2์˜ ๊ฑฐ๋“ญ์ œ๊ณฑ ์•„๋‹˜`calculate_zspage_chain_size()`์ตœ์†Œ ๋‚ญ๋น„ chain ์„ ํƒ

fullness ๊ทธ๋ฃน ๋ถ„๋ฅ˜

fullness ๊ทธ๋ฃนinuse ๋น„์œจ์„ค๋ช…
ZS_INUSE_RATIO_00%์™„์ „ํžˆ ๋นˆ zspage (ํ•ด์ œ ๊ฐ€๋Šฅ)
ZS_INUSE_RATIO_101~10%๊ฑฐ์˜ ๋นˆ zspage (compaction ๋Œ€์ƒ)
.........
ZS_INUSE_RATIO_9990~99%๊ฑฐ์˜ ์ฐฌ zspage
ZS_INUSE_RATIO_100100%์™„์ „ํžˆ ์ฐฌ zspage

lock ๊ณ„์ธต

๋ฝ๋ฒ”์œ„ํš๋“ ์ˆœ์„œ
`pool->lock`ํ’€ ์ „์ฒด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ณดํ˜ธ1๋ฒˆ์งธ
`class->lock`ํฌ๊ธฐ ํด๋ž˜์Šค ๋‚ด zspage ํ• ๋‹น/ํ•ด์ œ2๋ฒˆ์งธ
`zspage->zsl`๊ฐœ๋ณ„ zspage ์ฝ๊ธฐ/์“ฐ๊ธฐ ์ ‘๊ทผ3๋ฒˆ์งธ
`zpdesc` (folio lock)๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์ž ๊ธˆ4๋ฒˆ์งธ

huge vs non-huge zspage

์†์„ฑnon-hugehuge
pages_per_zspage> 1 ๋˜๋Š” objs > 1= 1์ด๊ณ  objs = 1
handle ์ €์žฅ ์œ„์น˜์ฒซ ๋ฒˆ์งธ ๊ฐ์ฒด์˜ link_freezpdesc->handle
freelist ๊ตฌ์กฐlink_free singly linked list๋‹จ์ผ ๊ฐ์ฒด (freelist ๋ถˆํ•„์š”)
`ZsHugePage()`falsetrue

๊ด€๋ จ ๋ฌธ์„œ

  • Swap / zswap โ€” zswap์ด zsmalloc์„ ์‚ฌ์šฉํ•˜์—ฌ ์••์ถ•๋œ ํŽ˜์ด์ง€ ์ €์žฅ
  • SLUB ํ• ๋‹น์ž โ€” ์ผ๋ฐ˜ slab ํ• ๋‹น์ž์™€์˜ ๋น„๊ต
  • Buddy Allocator โ€” zsmalloc์ด buddy์—์„œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜๋Š” ๋ฐฉ์‹
  • Compaction โ€” zsmalloc compaction๊ณผ ์ปค๋„ compaction์˜ ๊ด€๊ณ„
  • Folio / Page Cache โ€” zpdesc๊ฐ€ struct page๋ฅผ ์˜ค๋ฒ„๋ ˆ์ดํ•˜๋Š” ๋ฐฉ์‹
  • SVG ๋‹ค์ด์–ด๊ทธ๋žจ

    zsmalloc ๊ตฌ์กฐ ๊ณ„์ธต๋„

    zsmalloc ๊ตฌ์กฐ ๊ณ„์ธต๋„

    ํ• ๋‹น/ํ•ด์ œ ํ˜ธ์ถœ ํ๋ฆ„

    ํ• ๋‹น/ํ•ด์ œ ํ˜ธ์ถœ ํ๋ฆ„