Compaction (๋ฉ”๋ชจ๋ฆฌ ์กฐ๊ฐ ๋ชจ์Œ)

Linux 7.0 ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๋ถ„์„ ์‹œ๋ฆฌ์ฆˆ

๊ฐœ์š” (Overview)

Compaction๋Š” ๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ ์™ธ๋ถ€ ๋‹จํŽธํ™”(external fragmentation)๋ฅผ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. Buddy Allocator๋Š” 2์˜ ๊ฑฐ๋“ญ์ œ๊ณฑ(order) ํฌ๊ธฐ ๋ธ”๋ก์„ ํ• ๋‹นํ•˜๋Š”๋ฐ, ํ”„๋ฆฌ ํŽ˜์ด์ง€๊ฐ€ ์ถฉ๋ถ„ํžˆ ์žˆ์ง€๋งŒ ์—ฐ์†๋œ high-order ๋ธ”๋ก์ด ์—†๋Š” ๊ฒฝ์šฐ ํ• ๋‹น์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. Compaction์€ ์ด๋™ ๊ฐ€๋Šฅํ•œ(movable) ํŽ˜์ด์ง€๋ฅผ zone์˜ ํ•œ์ชฝ ๋์œผ๋กœ ์˜ฎ๊ธฐ๊ณ , ๋ฐ˜๋Œ€์ชฝ ๋์˜ ํ”„๋ฆฌ ํŽ˜์ด์ง€๋“ค์„ ๋ณ‘ํ•ฉํ•˜์—ฌ high-order ๋ธ”๋ก์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

Compaction์˜ ํ•ต์‹ฌ์€ page migration์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ์Šค์บ๋„ˆ๊ฐ€ zone์˜ ์–‘์ชฝ ๋์—์„œ ์„œ๋กœ๋ฅผ ํ–ฅํ•ด ์ด๋™ํ•ฉ๋‹ˆ๋‹ค: migrate scanner๋Š” ์ด๋™ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ , free scanner๋Š” ํ”„๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ํƒ์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ์Šค์บ๋„ˆ๊ฐ€ ๋งŒ๋‚˜๋ฉด compaction์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ์ปค๋„์€ Direct compaction (ํ• ๋‹น ์‹คํŒจ ์‹œ ์ฆ‰์‹œ ์‹คํ–‰), Background compaction (kcompactd ๋ฐ๋ชฌ), Proactive compaction (๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž๋™ ๋‹จํŽธํ™” ํ•ด์†Œ, ์ปค๋„ 5.9+) ์„ธ ๊ฐ€์ง€ ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ปค๋„ 6.15๋ถ€ํ„ฐ๋Š” defrag_mode sysctl์ด ์ถ”๊ฐ€๋˜์–ด ํŽ˜์ด์ง€ ํ• ๋‹น์ž๊ฐ€ ๋‹จํŽธํ™” ํšŒํ”ผ๋ฅผ ์ ๊ทน ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ : Compaction๋Š” ์ฃผ์ฐจ์žฅ ์ •๋ฆฌ์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์ฐจ์žฅ์— ๋นˆ ์ž๋ฆฌ๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ ์ฐจ๋“ค์ด ํฉ์–ด์ ธ ์žˆ์–ดๅคงๅž‹ ๋ฒ„์Šค(์—ฐ์† 4MB ๋ธ”๋ก)๋ฅผ ์ฃผ์ฐจํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค. ์ •๋น„์‚ฌ(migrate scanner)๊ฐ€ ํ•œ์ชฝ ๋์˜ ์ฐจ(movable ํŽ˜์ด์ง€)๋ฅผ ๋ชจ๋‘ ์˜ฎ๊ธฐ๊ณ , ๋ฐ˜๋Œ€์ชฝ ๋์˜ ๋นˆ ์ž๋ฆฌ(free scanner)๋ฅผ ๋ชจ์œผ๋ฉด ํฐ ๋ฒ„์Šค๋ฅผ ์ฃผ์ฐจํ•  ์ˆ˜ ์žˆ๋Š” ์—ฐ์† ๊ณต๊ฐ„์ด ์ƒ๊น๋‹ˆ๋‹ค.
์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ:
mm/compaction.c               โ† Compaction ํ•ต์‹ฌ ๋กœ์ง (3334์ค„)
include/linux/compaction.h    โ† enum compact_priority, enum compact_result ์ •์˜
mm/internal.h                 โ† struct compact_control, struct capture_control ์ •์˜
include/linux/mmzone.h        โ† zone ๊ตฌ์กฐ์ฒด ๋‚ด compaction ์บ์‹œ ํ•„๋“œ

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

# Compaction ํŠธ๋ฆฌ๊ฑฐ (์ „์ฒด ์‹œ์Šคํ…œ)
echo 1 > /proc/sys/vm/compact_memory

# NUMA ๋…ธ๋“œ๋ณ„ compaction ํŠธ๋ฆฌ๊ฑฐ
echo 1 > /sys/devices/system/node/node0/compact

# ํ˜„์žฌ compaction ํ†ต๊ณ„
cat /proc/vmstat | grep -E "compact_|kcompactd"

# Compaction deferred ์ƒํƒœ ํ™•์ธ
cat /proc/vmstat | grep "compact_defer_shift"

# ์™ธ๋ถ€ ๋‹จํŽธํ™” ์ง€์ˆ˜ ํ™•์ธ
cat /proc/extfraginfo

# Proactive compaction ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™”
cat /proc/sys/vm/compaction_proactiveness   # 0~100 (๊ธฐ๋ณธ 20)

# ๋‹จํŽธํ™” ์ ์ˆ˜ ํ™•์ธ (๋…ธ๋“œ๋ณ„)
cat /sys/kernel/mm/compaction/proactiveness

# defrag_mode โ€” ๋‹จํŽธํ™” ํšŒํ”ผ ๋ชจ๋“œ (์ปค๋„ 6.15+)
cat /proc/sys/vm/defrag_mode   # 0: ๋น„ํ™œ์„ฑ (๊ธฐ๋ณธ), 1: ์ ๊ทน์  ๋‹จํŽธํ™” ํšŒํ”ผ

# kcompactd ์Šค๋ ˆ๋“œ ์ƒํƒœ ํ™•์ธ
ps -eo pid,comm | grep kcompactd

# zone๋ณ„ compaction ์บ์‹œ PFN ํ™•์ธ
cat /proc/zoneinfo | grep -E "compact_cached"

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

1. `struct compact_control` โ€” Compaction ์ œ์–ด ๊ตฌ์กฐ์ฒด

Compaction์˜ ๋ชจ๋“  ์ƒํƒœ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ํ•ต์‹ฌ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. direct compaction, kcompactd, /proc ํŠธ๋ฆฌ๊ฑฐ ๋“ฑ ๋ชจ๋“  ์ง„์ž…์ ์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

// mm/internal.h:950-988
struct compact_control {
struct list_head freepages[NR_PAGE_ORDERS]; // ํ”„๋ฆฌ ํŽ˜์ด์ง€ ๋ชฉ๋ก (order๋ณ„)
struct list_head migratepages;  // ์ด๋™ ๋Œ€์ƒ ํŽ˜์ด์ง€ ๋ชฉ๋ก
unsigned int nr_freepages;      // ๊ฒฉ๋ฆฌ๋œ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ์ˆ˜
unsigned int nr_migratepages;   // ์ด๋™ ๋Œ€์ƒ ํŽ˜์ด์ง€ ์ˆ˜
unsigned long free_pfn;         // free scanner ์‹œ์ž‘ ์œ„์น˜
unsigned long migrate_pfn;      // migrate scanner ์œ„์น˜ (in/out)
unsigned long fast_start_pfn;   // ์„ ํ˜• ์Šค์บ” ์‹œ์ž‘ ์œ„์น˜
struct zone *zone;              // ๋Œ€์ƒ zone
unsigned long total_migrate_scanned; // ์ด ์ด๋™ ์Šค์บ” ์ˆ˜
unsigned long total_free_scanned;    // ์ด ํ”„๋ฆฌ ์Šค์บ” ์ˆ˜
unsigned short fast_search_fail;     // fast search ์‹คํŒจ ์ˆ˜
short search_order;             // fast search ์‹œ์ž‘ order
const gfp_t gfp_mask;          // GFP ๋งˆ์Šคํฌ
int order;                      // ํ• ๋‹น ๋Œ€์ƒ order
int migratetype;                // ํ• ๋‹น ๋Œ€์ƒ migratetype
const unsigned int alloc_flags; // ํ• ๋‹น ํ”Œ๋ž˜๊ทธ
const int highest_zoneidx;      // ์ตœ๋Œ€ zone ์ธ๋ฑ์Šค
enum migrate_mode mode;         // Async/Sync/Sync_light
bool ignore_skip_hint;          // skip ํžŒํŠธ ๋ฌด์‹œ ์—ฌ๋ถ€
bool no_set_skip_hint;          // skip ํžŒํŠธ ์„ค์ • ์•ˆ ํ•จ
bool ignore_block_suitable;     // ์ ํ•ฉํ•˜์ง€ ์•Š์€ ๋ธ”๋ก ๋ฌด์‹œ
bool direct_compaction;         // true = direct, false = kcompactd
bool proactive_compaction;      // true = proactive
bool whole_zone;                // ์ „์ฒด zone ์Šค์บ” ์—ฌ๋ถ€
bool contended;                 // lock ๊ฒฝ์Ÿ ๋ฐœ์ƒ ์—ฌ๋ถ€
bool finish_pageblock;          // ํ˜„์žฌ pageblock ์Šค์บ” ์™„๋ฃŒ ์—ฌ๋ถ€
bool alloc_contig;              // alloc_contig_range ํ• ๋‹น ์—ฌ๋ถ€
};

์ฃผ์š” ํ•„๋“œ ์„ค๋ช…:

  • freepages[NR_PAGE_ORDERS]: free scanner๊ฐ€ ๊ฒฉ๋ฆฌํ•œ ํ”„๋ฆฌ ํŽ˜์ด์ง€๋ฅผ order๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ณด๊ด€
  • migratepages: migrate scanner๊ฐ€ ๊ฒฉ๋ฆฌํ•œ ์ด๋™ ๋Œ€์ƒ ํŽ˜์ด์ง€ ๋ชฉ๋ก
  • migrate_pfn / free_pfn: ๋‘ ์Šค์บ๋„ˆ์˜ ํ˜„์žฌ ์œ„์น˜. compaction์ด ์ง„ํ–‰๋˜๋ฉด์„œ ์„œ๋กœ๋ฅผ ํ–ฅํ•ด ์ด๋™
  • mode: MIGRATE_ASYNC (๋…ผ๋ธ”๋กœํ‚น), MIGRATE_SYNC_LIGHT (๊ธฐ๋ณธ), MIGRATE_SYNC (์™„์ „ ๋™๊ธฐ)
  • direct_compaction: direct compaction์ธ์ง€ kcompactd/background์ธ์ง€ ๊ตฌ๋ถ„
  • 2. `struct capture_control` โ€” Direct compaction ์บก์ฒ˜ ๊ตฌ์กฐ์ฒด

    Direct compaction ์ค‘ IRQ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ”„๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ฆ‰์‹œ ์บก์ฒ˜ํ•˜์—ฌ ํ• ๋‹น์— ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

    // mm/internal.h:994-997
    struct capture_control {
    struct compact_control *cc;  // compaction ์ œ์–ด ๊ตฌ์กฐ์ฒด ํฌ์ธํ„ฐ
    struct page *page;           // ์บก์ฒ˜๋œ ํ”„๋ฆฌ ํŽ˜์ด์ง€ (ํ• ๋‹น ์„ฑ๊ณต ์‹œ)
    };

    compact_zone_order()์—์„œ current->capture_control์— ์„ค์ •ํ•˜๊ณ , IRQ์—์„œ ํŽ˜์ด์ง€ ํ•ด์ œ ์‹œ __free_pages_core()๊ฐ€ ์ด ๊ตฌ์กฐ์ฒด๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€๋ฅผ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

    3. `enum compact_priority` โ€” Compaction ์šฐ์„ ์ˆœ์œ„

    // include/linux/compaction.h:9-17
    enum compact_priority {
    COMPACT_PRIO_SYNC_FULL,        // 0: ์™„์ „ ๋™๊ธฐ (์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„)
    MIN_COMPACT_PRIORITY = COMPACT_PRIO_SYNC_FULL,
    COMPACT_PRIO_SYNC_LIGHT,       // 1: ๊ฐ€๋ฒผ์šด ๋™๊ธฐ (๊ธฐ๋ณธ)
    MIN_COMPACT_COSTLY_PRIORITY = COMPACT_PRIO_SYNC_LIGHT,
    DEF_COMPACT_PRIORITY = COMPACT_PRIO_SYNC_LIGHT,
    COMPACT_PRIO_ASYNC,            // 2: ๋น„๋™๊ธฐ (์ตœ์ € ์šฐ์„ ์ˆœ์œ„)
    INIT_COMPACT_PRIORITY = COMPACT_PRIO_ASYNC
    };

    4. `enum compact_result` โ€” Compaction ๊ฒฐ๊ณผ ์ฝ”๋“œ

    // include/linux/compaction.h:21-56
    enum compact_result {
    COMPACT_NOT_SUITABLE_ZONE,    // ๋‚ด๋ถ€: zone ์ ํ•ฉํ•˜์ง€ ์•Š์Œ
    COMPACT_SKIPPED,              // compaction ์‹œ์ž‘ ์•ˆ ํ•จ
    COMPACT_DEFERRED,             // ์ด์ „ ์‹คํŒจ๋กœ ์ธํ•ด ์—ฐ๊ธฐ๋จ
    COMPACT_NO_SUITABLE_PAGE,     // ๋‚ด๋ถ€: ์ ํ•ฉ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ์—†์Œ
    COMPACT_CONTINUE,             // ๊ณ„์† ์ง„ํ–‰
    COMPACT_COMPLETE,             // ์ „์ฒด zone ์Šค์บ” ์™„๋ฃŒ
    COMPACT_PARTIAL_SKIPPED,      // ์ผ๋ถ€ ์˜์—ญ๋งŒ ์Šค์บ”
    COMPACT_CONTENDED,            // lock ๊ฒฝ์Ÿ์œผ๋กœ ์ค‘๋‹จ
    COMPACT_SUCCESS,              // ํ• ๋‹น ์„ฑ๊ณต
    };

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

    1. `try_to_compact_pages()` โ€” Direct compaction ์ง„์ž…์ 

    ํ• ๋‹น ์‹คํŒจ ์‹œ page allocator์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์ธ ์ง„์ž… ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. zonelist์˜ ๊ฐ zone์— ๋Œ€ํ•ด compact_zone_order()๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

    // mm/compaction.c:2814-2880
    enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order,
    unsigned int alloc_flags, const struct alloc_context *ac,
    enum compact_priority prio, struct page **capture)
    {
    // 1. gfp_compaction_allowed() ๊ฒ€์‚ฌ
    // 2. zonelist ์ˆœํšŒ
    for_each_zone_zonelist_nodemask(zone, z, ac->zonelist, ...) {
    // cpuset ๊ฒ€์‚ฌ
    // compaction_deferred() ๊ฒ€์‚ฌ โ†’ ์—ฐ๊ธฐ๋œ zone ๊ฑด๋„ˆ๋œ€
    status = compact_zone_order(zone, order, ...);
    if (status == COMPACT_SUCCESS) break;
    // ASYNC์—์„œ need_resched() ๋˜๋Š” fatal signal โ†’ ์ค‘๋‹จ
    }
    }

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

  • gfp_compaction_allowed() ์‹คํŒจ โ†’ ์ฆ‰์‹œ COMPACT_SKIPPED
  • compaction_deferred() ์ฐธ โ†’ COMPACT_DEFERRED (์ด์ „ ์‹คํŒจ๋กœ ์—ฐ๊ธฐ)
  • COMPACT_SUCCESS โ†’ ๋ฐ˜๋ณต ์ฆ‰์‹œ ์ค‘๋‹จ (ํ• ๋‹น ์„ฑ๊ณต ์˜ˆ์ƒ)
  • COMPACT_COMPLETE / COMPACT_PARTIAL_SKIPPED โ†’ defer_compaction() ํ˜ธ์ถœ
  • 2. `compact_zone()` โ€” Compaction ์ฝ”์–ด ๋กœ์ง

    ํ•˜๋‚˜์˜ zone์— ๋Œ€ํ•œ compaction์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•ต์‹ฌ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. migrate/free ์Šค์บ๋„ˆ๋ฅผ ๊ต๋Œ€๋กœ ์‹คํ–‰ํ•˜๊ณ , ์ด๋™ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€๋ฅผ ๊ฒฉ๋ฆฌํ•˜์—ฌ migrationํ•ฉ๋‹ˆ๋‹ค.

    // mm/compaction.c:2510-2747
    static enum compact_result compact_zone(struct compact_control *cc,
    struct capture_control *capc)
    {
    // 1. ์Šค์บ๋„ˆ ์ดˆ๊ธฐ ์œ„์น˜ ์„ค์ • (์บ์‹œ๋œ PFN ๋˜๋Š” zone ๋)
    // 2. while (compact_finished() == COMPACT_CONTINUE) ๋ฐ˜๋ณต:
    //    a. isolate_migratepages(cc) โ†’ ์ด๋™ ๋Œ€์ƒ ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ
    //    b. migrate_pages() โ†’ compaction_alloc/free ์ฝœ๋ฐฑ์œผ๋กœ ์‹ค์ œ ์ด๋™
    //    c. check_drain: order > 0์ด๋ฉด lru_add_drain_cpu_zone()
    // 3. ํ”„๋ฆฌ ํŽ˜์ด์ง€ ํ•ด์ œ ๋ฐ ์บ์‹œ ์—…๋ฐ์ดํŠธ
    }

    ํ•ต์‹ฌ ๋ถ„๊ธฐ:

  • compaction_suit_allocation_order() ์‹คํŒจ โ†’ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ (watermark ๋ฏธ์ถฉ์กฑ)
  • compaction_restarting() โ†’ __reset_isolation_suitable() (์Šคํ‚ต ํžŒํŠธ ์ดˆ๊ธฐํ™”)
  • isolate_migratepages() ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ISOLATE_ABORT / ISOLATE_NONE / ISOLATE_SUCCESS ๋ถ„๊ธฐ
  • migrate_pages() ์‹คํŒจ โ†’ -ENOMEM ์‹œ COMPACT_CONTENDED, ASYNC ์‹คํŒจ ์‹œ finish_pageblock ์„ค์ •
  • 3. `compact_finished()` โ€” Compaction ์™„๋ฃŒ ๊ฒ€์‚ฌ

    compaction์ด ๋๋‚ฌ๋Š”์ง€ๅˆคๆ–ญํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋‘ ์Šค์บ๋„ˆ๊ฐ€ ๋งŒ๋‚ฌ๋Š”์ง€, ํ”„๋ฆฌ ํŽ˜์ด์ง€๊ฐ€ ์ถฉ๋ถ„ํ•œ์ง€, watermark๋ฅผ ๋งŒ์กฑํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

    // mm/compaction.c:2234-2355
    static enum compact_result __compact_finished(struct compact_control *cc)
    {
    // 1. compact_scanners_met(): ๋‘ ์Šค์บ๋„ˆ๊ฐ€ ๋งŒ๋‚˜๋ฉด COMPACT_COMPLETE/PARTIAL_SKIPPED
    // 2. proactive_compaction: fragmentation ์ ์ˆ˜๋กœ ํŒ๋‹จ
    //    - ์ ์ˆ˜ > wmark_low โ†’ COMPACT_CONTINUE
    //    - ์ ์ˆ˜ โ‰ค wmark_low โ†’ COMPACT_SUCCESS
    // 3. defrag_mode && kcompactd: NR_FREE_PAGES_BLOCKS ๊ธฐ์ค€ watermark ๊ฒ€์‚ฌ
    // 4. direct compactor: free_area์—์„œ ๋Œ€์ƒ migratetype์˜ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ํ™•์ธ
    //    - ํ•ด๋‹น migratetype ํ”„๋ฆฌ โ†’ COMPACT_SUCCESS
    //    - MIGRATE_MOVABLE โ†’ CMA fallback ํ—ˆ์šฉ
    //    - steal ๊ฐ€๋Šฅํ•˜๋ฉด COMPACT_SUCCESS
    // 5. contended || fatal_signal โ†’ COMPACT_CONTENDED
    }

    ํ•ต์‹ฌ ๋ถ„๊ธฐ:

  • compact_scanners_met() โ†’ reset_cached_positions() ํ›„ COMPACT_COMPLETE
  • proactive_compaction โ†’ fragmentation_score_zone() vs fragmentation_score_wmark() ๋น„๊ต
  • defrag_mode ํ™œ์„ฑํ™” ์‹œ NR_FREE_PAGES_BLOCKS ๊ธฐ๋ฐ˜ watermark ๊ฒ€์‚ฌ (high watermark ์š”๊ตฌ)
  • free_area_empty() ์‹คํŒจ โ†’ find_suitable_fallback()์œผ๋กœ steal ๊ฐ€๋Šฅ์„ฑ ๊ฒ€์‚ฌ
  • 4. `isolate_migratepages_block()` โ€” ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ

    ํ•˜๋‚˜์˜ pageblock ๋‚ด์—์„œ ์ด๋™ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€๋ฅผ ๊ฒฉ๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. LRU ๋ชฉ๋ก์—์„œ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  cc->migratepages ๋ชฉ๋ก์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    // mm/compaction.c:836-1308
    static int isolate_migratepages_block(struct compact_control *cc,
    unsigned long low_pfn, unsigned long end_pfn, isolate_mode_t mode)
    {
    for (; low_pfn < end_pfn; low_pfn++) {
    // PageHuge โ†’ isolate_or_dissolve_huge_folio()
    // PageBuddy โ†’ ๊ฑด๋„ˆ๋œ€
    // PageCompound && !alloc_contig โ†’ skip_isolation_on_order() ๊ฒ€์‚ฌ
    // PageLRU โ†’ folio ๊ฒฉ๋ฆฌ
    //   - too_many_isolated() ๊ฒ€์‚ฌ (LRU ๊ฒฉ๋ฆฌ ์ œํ•œ)
    //   - ISOLATE_ASYNC_MIGRATE โ†’ writeback/dirty ํŽ˜์ด์ง€ ์ œ์™ธ
    //   - folio_test_clear_lru() โ†’ LRU์—์„œ ์ œ๊ฑฐ
    //   - COMPACT_CLUSTER_MAX ๋„๋‹ฌ ์‹œ ์ค‘๋‹จ
    }
    }

    5. `compaction_alloc()` / `compaction_free()` โ€” Migration ์ฝœ๋ฐฑ

    migrate_pages()๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. compaction_alloc()์€ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ๋ชฉ๋ก์—์„œ ๋Œ€์ƒ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜๊ณ , compaction_free()๋Š” ์‹คํŒจ ์‹œ ํ”„๋ฆฌ ํŽ˜์ด์ง€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    // mm/compaction.c:1797-1848
    static struct folio *compaction_alloc_noprof(struct folio *src, unsigned long data)
    {
    struct compact_control *cc = (struct compact_control *)data;
    // 1. freepages[]์—์„œ ์•Œ๋งž์€ order์˜ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ํƒ์ƒ‰
    // 2. ์—†์œผ๋ฉด isolate_freepages() ํ˜ธ์ถœํ•˜์—ฌ ์ถ”๊ฐ€ ๊ฒฉ๋ฆฌ
    // 3. ํฐ order ๋ธ”๋ก์„ splitํ•˜์—ฌ ์š”์ฒญ order๋กœ ์ œ๊ณต
    // 4. nr_freepages, nr_migratepages ๊ฐฑ์‹ 
    }
    
    static void compaction_free(struct folio *dst, unsigned long data)
    {
    // ์‹คํŒจํ•œ ํŽ˜์ด์ง€๋ฅผ freepages[]๋กœ ๋ฐ˜ํ™˜
    }

    6. `kcompactd()` โ€” ๋ฐฑ๊ทธ๋ผ์šด๋“œ Compaction ๋ฐ๋ชฌ

    ๊ฐ NUMA ๋…ธ๋“œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ์‹คํ–‰๋˜๋Š” ์ปค๋„ ์Šค๋ ˆ๋“œ์ž…๋‹ˆ๋‹ค. proactive compaction๊ณผ ์š”์ฒญ ๊ธฐ๋ฐ˜ compaction์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    // mm/compaction.c:3165-3231
    static int kcompactd(void *p)
    {
    pg_data_t *pgdat = (pg_data_t *)p;
    long default_timeout = msecs_to_jiffies(HPAGE_FRAG_CHECK_INTERVAL_MSEC);
    long timeout = default_timeout;
    
    current->flags |= PF_KCOMPACTD;
    set_freezable();
    
    while (!kthread_should_stop()) {
    // 1. proactiveness ๋น„ํ™œ์„ฑํ™” ์‹œ timeout = MAX_SCHEDULE_TIMEOUT
    // 2. wait_event_freezable_timeout()์œผ๋กœ ๋Œ€๊ธฐ
    // 3. kcompactd_work_requested() โ†’ kcompactd_do_work() ์‹คํ–‰
    // 4. should_proactive_compact_node() โ†’ compact_node() ์‹คํ–‰
    //    - fragmentation_score_node() > wmark_high โ†’ proactive ์‹œ์ž‘
    //    - ์ ์ˆ˜ ๊ฐœ์„  ์—†์œผ๋ฉด timeout ์ฆ๊ฐ€ (์ง€์—ฐ)
    timeout = default_timeout;
    }
    }

    proactive compaction ๋ถ„๊ธฐ:

  • sysctl_compaction_proactiveness == 0 โ†’ timeout = MAX_SCHEDULE_TIMEOUT (๋น„ํ™œ์„ฑ)
  • fragmentation_score_node() > wmark_high โ†’ compact_node() ์‹คํ–‰
  • ์ ์ˆ˜ ๊ฐœ์„  ์‹คํŒจ โ†’ timeout << COMPACT_MAX_DEFER_SHIFT (์ตœ๋Œ€ 64๋ฐฐ ์ง€์—ฐ)
  • 7. `compact_zone_order()` โ€” compact_control ์ดˆ๊ธฐํ™”

    Direct compaction์—์„œ compact_control ๊ตฌ์กฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์šฐ์„ ์ˆœ์œ„(prio)์— ๋”ฐ๋ผ mode, whole_zone, ignore_skip_hint ๋“ฑ์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

    // mm/compaction.c:2749-2768
    static enum compact_result compact_zone_order(struct zone *zone, int order,
    gfp_t gfp_mask, enum compact_priority prio,
    unsigned int alloc_flags, int highest_zoneidx,
    struct page **capture)
    {
    struct compact_control cc = {
    .order = order,
    .search_order = order,
    .gfp_mask = gfp_mask,
    .zone = zone,
    .mode = (prio == COMPACT_PRIO_ASYNC) ?
    MIGRATE_ASYNC : MIGRATE_SYNC_LIGHT,
    .alloc_flags = alloc_flags,
    .highest_zoneidx = highest_zoneidx,
    .direct_compaction = true,
    .whole_zone = (prio == MIN_COMPACT_PRIORITY),        // ์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„์—์„œ ์ „์ฒด zone ์Šค์บ”
    .ignore_skip_hint = (prio == MIN_COMPACT_PRIORITY),  // skip ํžŒํŠธ ๋ฌด์‹œ
    .ignore_block_suitable = (prio == MIN_COMPACT_PRIORITY) // ์ ํ•ฉ ๋ธ”๋ก ๋ฌด์‹œ
    };
    struct capture_control capc = {
    .cc = &cc,
    .page = NULL,
    };
    // capture_control์„ current์— ์„ค์ • โ€” IRQ์—์„œ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ์บก์ฒ˜ ๊ฐ€๋Šฅ
    WRITE_ONCE(current->capture_control, &capc);
    ret = compact_zone(&cc, &capc);
    ...
    }

    compact_control ์ดˆ๊ธฐํ™” ๋ถ„๊ธฐ:

  • COMPACT_PRIO_ASYNC โ†’ MIGRATE_ASYNC (๋…ผ๋ธ”๋กœํ‚น, ๋ถ€๋ถ„ ์Šค์บ”)
  • COMPACT_PRIO_SYNC_LIGHT (๊ธฐ๋ณธ) โ†’ MIGRATE_SYNC_LIGHT (์กฐ๊ฑด๋ถ€ ๋™๊ธฐ)
  • MIN_COMPACT_PRIORITY โ†’ whole_zone=true, ignore_skip_hint=true (์ „์ฒด zone, skip ๋ฌด์‹œ)

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

    Direct Compaction ํ๋ฆ„

    __alloc_pages() ์‹คํŒจ
    โ””โ†’ __alloc_pages_slowpath()
    โ””โ†’ __alloc_pages_direct_reclaim() ์‹คํŒจ
    โ””โ†’ __alloc_pages_direct_compact()
    โ””โ†’ try_to_compact_pages()           โ† Direct compaction ์ง„์ž…
    โ””โ†’ compact_zone_order()        โ† compact_control ์ดˆ๊ธฐํ™”
    โ””โ†’ compact_zone()          โ† ์ฝ”์–ด ๋ฃจํ”„
    โ”œโ†’ compaction_suit_allocation_order()  โ† watermark ๊ฒ€์‚ฌ
    โ”œโ†’ isolate_migratepages()             โ† ์ด๋™ ๋Œ€์ƒ ๊ฒฉ๋ฆฌ
    โ”‚    โ””โ†’ isolate_migratepages_block()  โ† pageblock ๋‹จ์œ„
    โ”œโ†’ migrate_pages()                    โ† ์‹ค์ œ ์ด๋™
    โ”‚    โ”œโ†’ compaction_alloc()            โ† ๋Œ€์ƒ ํŽ˜์ด์ง€ ํ• ๋‹น
    โ”‚    โ””โ†’ compaction_free()             โ† ์‹คํŒจ ์‹œ ๋ฐ˜ํ™˜
    โ””โ†’ compact_finished()                 โ† ์™„๋ฃŒ ๊ฒ€์‚ฌ

    kcompactd ํ๋ฆ„

    wakeup_kcompactd()                    โ† page allocator์—์„œ ํ˜ธ์ถœ
    โ””โ†’ wake_up_interruptible()
    
    kcompactd() ์Šค๋ ˆ๋“œ ๊ธฐ์ƒ
    โ”œโ†’ kcompactd_do_work()              โ† ์ผ๋ฐ˜ compaction
    โ”‚    โ””โ†’ compact_zone()
    โ””โ†’ should_proactive_compact_node()  โ† proactive compaction
    โ””โ†’ compact_node()              โ† ์ „์ฒด zone ์ˆœํšŒ
    โ””โ†’ compact_zone()

    Free Scanner โ†” Meet Scanner ๊ตฌ์กฐ

    Zone ์‹œ์ž‘ (low_pfn)
    โ”‚
    โ”‚  โ† migrate scanner โ†’
    โ”‚  (migrate_pfn: ์•ž์—์„œ ๋’ค๋กœ)
    โ”‚
    โ”‚  ์ด๋™ ๊ฐ€๋Šฅํ•œ ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ โ†’ migratepages
    โ”‚
    โ”‚         โ† free scanner
    โ”‚  (free_pfn: ๋’ค์—์„œ ์•ž์œผ๋กœ)
    โ”‚
    โ”‚  ํ”„๋ฆฌ ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ โ†’ freepages[]
    โ”‚
    โ””โ”€ ๋‘ ์Šค์บ๋„ˆ๊ฐ€ ๋งŒ๋‚˜๋ฉด compaction ์ข…๋ฃŒ

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

    Compaction ๋ชจ๋“œ ๋น„๊ต

    ํ•ญ๋ชฉMIGRATE_ASYNCMIGRATE_SYNC_LIGHTMIGRATE_SYNC
    **๋ธ”๋กœํ‚น**๋น„๋™๊ธฐ (๋…ผ๋ธ”๋กœํ‚น)์กฐ๊ฑด๋ถ€ ๋™๊ธฐ์™„์ „ ๋™๊ธฐ
    **์šฐ์„ ์ˆœ์œ„**COMPACT_PRIO_ASYNCDEF_COMPACT_PRIORITYMIN_COMPACT_PRIORITY
    **์Šค์บ๋„ˆ ๋ฒ”์œ„**๋ถ€๋ถ„ ์Šค์บ”๋ถ€๋ถ„ ์Šค์บ”์ „์ฒด zone (whole_zone)
    **skip_hint**์‚ฌ์šฉ์‚ฌ์šฉ๋ฌด์‹œ (ignore_skip_hint)
    **์ ํ•ฉ ๋ธ”๋ก ๋ฌด์‹œ**์•„๋‹˜์•„๋‹˜๋ฌด์‹œ (ignore_block_suitable)
    **isolation stride**COMPACT_CLUSTER_MAX11
    **lock ๊ฒฝ์Ÿ ์‹œ**trylock โ†’ ์ค‘๋‹จlock ๋Œ€๊ธฐlock ๋Œ€๊ธฐ

    Compaction ํŠธ๋ฆฌ๊ฑฐ ๊ฒฝ๋กœ ๋น„๊ต

    ๊ฒฝ๋กœ์ง„์ž… ํ•จ์ˆ˜compact_control ์„ค์ •์ฃผ์š” ํŠน์ง•
    **Direct compaction**`try_to_compact_pages()`direct_compaction=true, whole_zone=false (prio๋ณ„)ํ• ๋‹น ์‹คํŒจ ์‹œ ์ฆ‰์‹œ ์‹คํ–‰, capture_control๋กœ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ์บก์ฒ˜
    **kcompactd**`kcompactd_do_work()`direct_compaction=false, whole_zone=false๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰, watermark_high ๊ธฐ์ค€
    **Proactive**`compact_node()`proactive_compaction=true, whole_zone=truefragmentation ์ ์ˆ˜ ๊ธฐ๋ฐ˜, kcompactd ๋‚ด๋ถ€
    **compact_memory**`compact_nodes()`order=-1, whole_zone=true, ignore_skip_hint=true/proc/sys/vm/compact_memory ํŠธ๋ฆฌ๊ฑฐ
    **NUMA node compact**`compact_node()`order=-1, whole_zone=true/sys/devices/system/node/nodeX/compact ํŠธ๋ฆฌ๊ฑฐ

    Compaction ๊ฒฐ๊ณผ ๋น„๊ต

    ๊ฒฐ๊ณผ์˜๋ฏธํ›„์† ๋™์ž‘
    `COMPACT_SUCCESS`ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํ”„๋ฆฌ ๋ธ”๋ก ํ™•๋ณดํ• ๋‹น ์‹œ๋„
    `COMPACT_CONTINUE`๊ณ„์† ์ง„ํ–‰ ๊ฐ€๋Šฅ๋ฐ˜๋ณต ๋ฃจํ”„ ๊ณ„์†
    `COMPACT_COMPLETE`์ „์ฒด zone ์Šค์บ” ์™„๋ฃŒ`defer_compaction()` (non-MIN)
    `COMPACT_PARTIAL_SKIPPED`๋ถ€๋ถ„ ์Šค์บ”๋งŒ ์™„๋ฃŒ`defer_compaction()`
    `COMPACT_DEFERRED`์ด์ „ ์‹คํŒจ๋กœ ์—ฐ๊ธฐ๊ฑด๋„ˆ๋œ€
    `COMPACT_SKIPPED`compaction ๋ถˆ๊ฐ€ (watermark ๋ฏธ์ถฉ์กฑ ๋“ฑ)๋‹ค์Œ zone์œผ๋กœ
    `COMPACT_CONTENDED`lock ๊ฒฝ์Ÿ์œผ๋กœ ์ค‘๋‹จ์ฆ‰์‹œ ๋ฐ˜ํ™˜

    ์Šค์บ๋„ˆ skip ํžŒํŠธ ๋™์ž‘ ๋น„๊ต

    ์กฐ๊ฑดskip ์„ค์ •skip ๊ฒ€์‚ฌ์„ค๋ช…
    ๊ฒฉ๋ฆฌ๋œ ํŽ˜์ด์ง€ ์—†์Œ`set_pageblock_skip()``isolation_suitable()`๋นˆ ๋ธ”๋ก์€ ํ–ฅํ›„ ์Šค์บ”์—์„œ ๊ฑด๋„ˆ๋œ€
    isolated == 0 && finish_pageblock`set_pageblock_skip()``isolation_suitable()`์™„๋ฃŒ ์‹œ์—๋„ ์„ค์ •
    ignore_skip_hint=true์„ค์ • ์•ˆ ํ•จํ•ญ์ƒ ํ†ต๊ณผMIN_COMPACT_PRIORITY์—์„œ ์‚ฌ์šฉ
    no_set_skip_hint=true์„ค์ • ์•ˆ ํ•จ์„ค์ •isolated ํฌ์ง€์…˜ ์—…๋ฐ์ดํŠธ ์‹œ
    pageblock_skip_persistent์„ค์ • ์•ˆ ํ•จํ•ญ์ƒ ๊ฑด๋„ˆ๋œ€compound >= pageblock_order

    defrag_mode ๋™์ž‘ ๋น„๊ต (์ปค๋„ 6.15+)

    defrag_modekcompactd ๋™์ž‘compact_finished ๊ธฐ์ค€์ผ๋ฐ˜ ํ• ๋‹น์ž ์˜ํ–ฅ
    `0` (๊ธฐ๋ณธ)์ผ๋ฐ˜ compact_zonefree_area์—์„œ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ํ™•์ธ๊ธฐ์กด๋Œ€๋กœ ๋™์ž‘
    `1` (์ ๊ทน์ )NR_FREE_PAGES_BLOCKS ๊ธฐ์ค€high watermark ์š”๊ตฌํ• ๋‹น ์‹œ compaction ํšŒํ”ผ ๊ฐ•ํ™”

    ์ฆ์ƒ๋ณ„ ์ง„๋‹จ

    minzkn.com์˜ "๋ฉ”๋ชจ๋ฆฌ ์šด์˜ ํ”Œ๋ ˆ์ด๋ถ"๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ, compaction ๊ด€๋ จ ์ฆ์ƒ๋ณ„ ์ง„๋‹จ ํฌ์ธํŠธ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    ์ฆ์ƒ์šฐ์„  ์ ๊ฒ€๊ถŒ์žฅ ์กฐ์น˜
    **๊ณ ์ฐจ ํŽ˜์ด์ง€ ํ• ๋‹น ์‹คํŒจ** (THP 2MB ๋“ฑ)`/proc/buddyinfo`, `cat /proc/vmstat \grep compact`compaction ํŠœ๋‹, hugepage ์ •์ฑ… ์ ๊ฒ€, `defrag_mode=1` ์‹œ๋„
    **compact_stall ๊ธ‰์ฆ**`cat /proc/vmstat \grep compact_stall`THP `defrag` ๋ชจ๋“œ๋ฅผ `madvise`๋กœ ๋ณ€๊ฒฝ, proactive_compactiveness ์กฐ์ •
    **compact_fail > compact_success**`cat /proc/vmstat \grep compact_fail`๋‹จํŽธํ™” ์‹ฌํ™” ์ƒํƒœ โ€” `echo 1 > /proc/sys/vm/compact_memory` ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ
    **kcompactd ๊ณผ๋„ํ•œ CPU ์‚ฌ์šฉ**`top -H -p $(pgrep kcompactd)``compaction_proactiveness` ๊ฐ์†Œ (0์œผ๋กœ ๋น„ํ™œ์„ฑํ™” ๊ฐ€๋Šฅ)
    **khugepaged ๊ณผ๋„ํ•œ CPU**`top -H -p $(pgrep khugepaged)`THP defrag๋ฅผ `madvise`๋กœ ๋ณ€๊ฒฝ, khugepaged ์Šค์บ” ์ฃผ๊ธฐ ์กฐ์ •
    **defer_compaction ๋ฐ˜๋ณต**`cat /proc/vmstat \grep defer`๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์ƒํƒœ โ€” ํšŒ์ˆ˜ ๊ฒฝ๋กœ ์ ๊ฒ€, ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ํ•ด์†Œ
    **zone ๋‚ด fragmentation index ๋†’์Œ**`cat /proc/extfraginfo`memcg ์ œํ•œ ์žฌ์กฐ์ •, CMA ์˜์—ญ ํ™•๋ณด, zonelist ์ˆœ์„œ ๋ณ€๊ฒฝ
    # compaction ๊ด€๋ จ ํ†ต๊ณ„ ์ „์ฒด ํ™•์ธ ์Šคํฌ๋ฆฝํŠธ
    echo "=== Compaction ํ†ต๊ณ„ ==="
    cat /proc/vmstat | grep -E "compact_|kcompactd|defer"
    echo ""
    echo "=== buddyinfo (๋‹จํŽธํ™” ์ƒํƒœ) ==="
    cat /proc/buddyinfo
    echo ""
    echo "=== fragmentation index ==="
    cat /proc/extfraginfo
    echo ""
    echo "=== sysctl ํŒŒ๋ผ๋ฏธํ„ฐ ==="
    sysctl vm.compaction_proactiveness vm.compact_memory vm.extfrag_threshold 2>/dev/null
    echo ""
    echo "=== defrag_mode (์ปค๋„ 6.15+) ==="
    cat /proc/sys/vm/defrag_mode 2>/dev/null || echo "ํ•ด๋‹น ์—†์Œ"

    ๊ด€๋ จ ๋ฌธ์„œ

  • Buddy Allocator โ€” ํ”„๋ฆฌ ํŽ˜์ด์ง€ ๊ด€๋ฆฌ ๋ฐ buddy ๋ณ‘ํ•ฉ
  • Huge Pages / THP โ€” THP๋ฅผ ์œ„ํ•œ compaction ํ™œ์šฉ
  • CMA โ€” CMA๋ฅผ ์œ„ํ•œ compaction ํ™œ์šฉ
  • Migration โ€” compaction์˜ ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ธ page migration
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜ โ€” vmscan๊ณผ์˜ ์—ฐ๊ณ„
  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” โ€” ์ „์ฒด ์•„ํ‚คํ…์ฒ˜ ์ดํ•ด

  • SVG ๋‹ค์ด์–ด๊ทธ๋žจ

    Compaction ์ „์ฒด ํ˜ธ์ถœ ํ๋ฆ„

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

    Compaction ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

    Compaction ์ž๋ฃŒ๊ตฌ์กฐ