Huge Pages / THP

๊ด€๋ จ ์†Œ์Šค: mm/huge_memory.c (4978์ค„), mm/khugepaged.c (2873์ค„), include/linux/huge_mm.h (807์ค„)
๊ด€๋ จ ๋ฌธ์„œ: VMA / mmap ยท Buddy Allocator ยท Folio / Page Cache ยท Compaction ยท ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”

๊ฐœ์š” (Overview)

Linux ์ปค๋„์˜ Transparent Huge Pages (THP) ์€ ์ต๋ช… ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์— ๋Œ€ํ•ด 4KB ๋Œ€์‹  2MB (PMD ํฌ๊ธฐ) ๋˜๋Š” ๊ทธ๋ณด๋‹ค ํฐ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€๋ฅผ ์ž๋™์œผ๋กœ ํ• ๋‹น/๋ณ‘ํ•ฉํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. THP๋Š” TLB(Translation Lookaside Buffer) ๋ฏธ์Šค๋ฅผ ์ค„์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋ฉฐ, khugepaged ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ธฐ์กด 4KB ํŽ˜์ด์ง€ ์„ธํŠธ๋ฅผ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€๋กœ "๋ณ‘ํ•ฉ(collapse)"ํ•ฉ๋‹ˆ๋‹ค.

Huge Pages๋Š” ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  • HugeTLB: ์‚ฌ์ „ ์˜ˆ์•ฝ๋œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€์„ ์‚ฌ์šฉํ•˜๋Š” ์ •์  ํ• ๋‹น ๋ฐฉ์‹ (DPDK, KVM, DB์— ์ ํ•ฉ)
  • THP (Transparent Huge Pages): ๊ธฐ์กด ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ์œ„์—์„œ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๋™์  ๋ณ‘ํ•ฉ ๋ฐฉ์‹
  • Linux 7.0์—์„œ๋Š” THP์˜ order ๋ฒ”์œ„๊ฐ€ ํ™•์žฅ๋˜์–ด PMD ํฌ๊ธฐ(2MB)๋ฟ ์•„๋‹ˆ๋ผ ๊ทธ๋ณด๋‹ค ์ž‘์€ mTHP(multi-size THP, large folio) (order-2 ~ order-9)๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. khugepaged๋Š” ์ต๋ช… ํŽ˜์ด์ง€์™€ ํŒŒ์ผ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€ ๋ชจ๋‘์—์„œ ๋ณ‘ํ•ฉ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ compound page๋Š” ์ปค๋„ 5.16+์—์„œ folio ์ถ”์ƒํ™”๋กœ ๊ฐ์‹ธ์ ธ head/tail page ๊ตฌ๋ถ„์ด ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

    ์ผ์ƒ ๋น„์œ : Huge Page๋Š” ์ผ๋ฐ˜ ๋„์„œ๊ด€ ์ฑ…์žฅ(4KB ํŽ˜์ด์ง€) ๋Œ€์‹  ๋Œ€ํ˜• ์‚ฌ์ „(2MB)์„ ๊บผ๋‚ด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ž์ฃผ ์ฐพ๋Š” ํŽ˜์ด์ง€๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ์œผ๋ฉด ์ฑ…์žฅ ๋„˜๊ธฐ๋Š” ํšŸ์ˆ˜(TLB ๋ฏธ์Šค)๊ฐ€ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. HugeTLB๋Š” ๋ฏธ๋ฆฌ ๋Œ€ํ˜• ์‚ฌ์ „์„ ์˜ˆ์•ฝํ•˜๋Š” ๊ฒƒ์ด๊ณ , THP๋Š” ํ•„์š”ํ•  ๋•Œ ์ž๋™์œผ๋กœ ๋Œ€ํ˜• ์‚ฌ์ „์œผ๋กœ ๋ฌถ์–ด์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    /* ์ฃผ์š” ์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ */
    mm/huge_memory.c          /* THP ํดํŠธ ์ฒ˜๋ฆฌ, sysfs ์ธํ„ฐํŽ˜์ด์Šค, ์ œ๋กœ ํŽ˜์ด์ง€ ๊ด€๋ฆฌ */
    mm/khugepaged.c           /* ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ณ‘ํ•ฉ ์Šค๋ ˆ๋“œ (khugepaged) */
    include/linux/huge_mm.h   /* THP ๊ด€๋ จ ์ž๋ฃŒ๊ตฌ์กฐ, ๋งคํฌ๋กœ, API ์„ ์–ธ */

    THP ํ˜ธ์ถœ ํ๋ฆ„ ๋‹ค์ด์–ด๊ทธ๋žจ

    THP ํดํŠธ ํ˜ธ์ถœ ํ๋ฆ„

    khugepaged ๋ณ‘ํ•ฉ ํ๋ฆ„ ๋‹ค์ด์–ด๊ทธ๋žจ

    khugepaged ๋ณ‘ํ•ฉ ํ๋ฆ„

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

    # THP ํ™œ์„ฑํ™” ์ƒํƒœ ํ™•์ธ
    cat /sys/kernel/mm/transparent_hugepage/enabled
    # [always] madvise never  โ† ํ˜„์žฌ THP ๋ชจ๋“œ
    
    # THP ๋””ํ”„๋ž˜๊ทธ(defrag) ์ •์ฑ… ํ™•์ธ
    cat /sys/kernel/mm/transparent_hugepage/defrag
    # [always] defer defer+madvise madvise never
    
    # PMD ํฌ๊ธฐ ํ™•์ธ (๋ณดํ†ต 2MB = 512 * 4KB)
    cat /sys/kernel/mm/transparent_hugepage/hpage_pmd_size
    # 2097152  (2MB in bytes)
    
    # khugepaged ์Šค์บ”/๋ณ‘ํ•ฉ ํ†ต๊ณ„
    cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
    cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed
    cat /sys/kernel/mm/transparent_hugepage/khugepaged/full_scans
    
    # ์‹œ์Šคํ…œ HugePages ์ƒํƒœ
    grep -i huge /proc/meminfo
    
    # ํ”„๋กœ์„ธ์Šค๋ณ„ THP ์‚ฌ์šฉ๋Ÿ‰ (RSS ๋‚ด large folio)
    cat /proc/<pid>/smaps | grep -A 5 -i huge
    
    # THP ๊ด€๋ จ vmstat ์ด๋ฒคํŠธ
    cat /proc/vmstat | grep -E 'thp_|khugepaged'
    
    # MADV_HUGEPAGE ์ ์šฉ๋œ VMA ํ™•์ธ
    cat /proc/<pid>/smaps | grep -B 2 -i huge

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

    1. `transparent_hugepage_flags` (์ „์—ญ ๋น„ํŠธ๋งต)

    THP ๋™์ž‘์„ ์ œ์–ดํ•˜๋Š” ์ „์—ญ ํ”Œ๋ž˜๊ทธ์ž…๋‹ˆ๋‹ค. ๊ฐ ๋น„ํŠธ๋Š” enum transparent_hugepage_flag๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค.

    /* mm/huge_memory.c:59-68 โ€” THP ์ „์—ญ ํ”Œ๋ž˜๊ทธ ์ดˆ๊ธฐํ™” */
    unsigned long transparent_hugepage_flags __read_mostly =
    #ifdef CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS
    	(1<<TRANSPARENT_HUGEPAGE_FLAG)|
    #endif
    #ifdef CONFIG_TRANSPARENT_HUGEPAGE_MADVISE
    	(1<<TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG)|
    #endif
    	(1<<TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG)|
    	(1<<TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG)|
    	(1<<TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG);
    /* include/linux/huge_mm.h:49-59 โ€” THP ํ”Œ๋ž˜๊ทธ ์—ด๊ฑฐํ˜• */
    enum transparent_hugepage_flag {
    	TRANSPARENT_HUGEPAGE_UNSUPPORTED,
    	TRANSPARENT_HUGEPAGE_FLAG,              /* always ๋ชจ๋“œ */
    	TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG,     /* madvise ๋ชจ๋“œ */
    	TRANSPARENT_HUGEPAGE_DEFRAG_DIRECT_FLAG,
    	TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_FLAG,
    	TRANSPARENT_HUGEPAGE_DEFRAG_KSWAPD_OR_MADV_FLAG,
    	TRANSPARENT_HUGEPAGE_DEFRAG_REQ_MADV_FLAG,
    	TRANSPARENT_HUGEPAGE_DEFRAG_KHUGEPAGED_FLAG,
    	TRANSPARENT_HUGEPAGE_USE_ZERO_PAGE_FLAG,
    };

    2. `struct khugepaged_scan` (์Šค์บ” ์ปค์„œ)

    khugepaged๊ฐ€ ์ˆœํšŒํ•  mm ๋ชฉ๋ก์˜ ์ปค์„œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

    /* mm/khugepaged.c:107-123 โ€” ์Šค์บ” ์ปค์„œ ๊ตฌ์กฐ์ฒด */
    struct khugepaged_scan {
    	struct list_head mm_head;     /* mm ๋ชฉ๋ก ๋จธ๋ฆฌ */
    	struct mm_slot *mm_slot;      /* ํ˜„์žฌ ์Šค์บ” ์ค‘์ธ mm_slot */
    	unsigned long address;        /* ๋‹ค์Œ ์Šค์บ”ํ•  ๊ฐ€์ƒ ์ฃผ์†Œ */
    };
    
    static struct khugepaged_scan khugepaged_scan = {
    	.mm_head = LIST_HEAD_INIT(khugepaged_scan.mm_head),
    };

    3. `struct collapse_control` (๋ณ‘ํ•ฉ ์ œ์–ด)

    ๋ณ‘ํ•ฉ ์ž‘์—…์˜ ์„ฑ๊ฒฉ๊ณผ NUMA ๋…ธ๋“œ๋ณ„ ๋กœ๋“œ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

    /* mm/khugepaged.c:97-105 โ€” ๋ณ‘ํ•ฉ ์ œ์–ด ๊ตฌ์กฐ์ฒด */
    struct collapse_control {
    	bool is_khugepaged;               /* khugepaged initiated vs MADV_COLLAPSE */
    	u32 node_load[MAX_NUMNODES];      /* ๋…ธ๋“œ๋ณ„ ์Šค์บ”๋œ ํŽ˜์ด์ง€ ์ˆ˜ */
    	nodemask_t alloc_nmask;           /* ํ• ๋‹น ํด๋ฐฑ nodemask */
    };

    4. `enum scan_result` (์Šค์บ” ๊ฒฐ๊ณผ)

    PTE ์˜์—ญ ์Šค์บ” ์‹œ ๊ฐ ์กฐ๊ฑด๋ณ„ ๊ฒฐ๊ณผ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. khugepaged๋Š” ์ด ๊ฐ’์— ๋”ฐ๋ผ ๋ณ‘ํ•ฉ์„ ์‹œ๋„ํ•˜๊ฑฐ๋‚˜ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค.

    /* mm/khugepaged.c:31-63 โ€” ์Šค์บ” ๊ฒฐ๊ณผ ์—ด๊ฑฐํ˜• (์‹ค์ œ ์†Œ์Šค ๊ธฐ์ค€) */
    enum scan_result {
    	SCAN_FAIL,                  /* ์ผ๋ฐ˜ ์‹คํŒจ */
    	SCAN_SUCCEED,               /* ์Šค์บ” ์„ฑ๊ณต โ†’ ๋ณ‘ํ•ฉ ์ง„ํ–‰ */
    	SCAN_NO_PTE_TABLE,          /* PMD๊ฐ€ ์—†์Œ (PTE ํ…Œ์ด๋ธ” ๋ฏธํ• ๋‹น) */
    	SCAN_PMD_MAPPED,            /* ์ด๋ฏธ THP๋กœ ๋งคํ•‘๋จ */
    	SCAN_EXCEED_NONE_PTE,       /* ๋นˆ PTE ํ—ˆ์šฉ ์ดˆ๊ณผ (max_ptes_none) */
    	SCAN_EXCEED_SWAP_PTE,       /* ์Šค์™‘ PTE ์ดˆ๊ณผ (max_ptes_swap) */
    	SCAN_EXCEED_SHARED_PTE,     /* ๊ณต์œ  ํŽ˜์ด์ง€ ์ดˆ๊ณผ (max_ptes_shared) */
    	SCAN_PTE_NON_PRESENT,       /* ์กด์žฌํ•˜์ง€ ์•Š๋Š” PTE */
    	SCAN_PTE_UFFD_WP,           /* userfaultfd ์“ฐ๊ธฐ ๋ณดํ˜ธ */
    	SCAN_PTE_MAPPED_HUGEPAGE,   /* PTE๋กœ ๋งคํ•‘๋œ hugepage */
    	SCAN_LACK_REFERENCED_PAGE,  /* ์ ‘๊ทผ๋œ ํŽ˜์ด์ง€ ๋ถ€์กฑ */
    	SCAN_SCAN_ABORT,            /* NUMA ๋กœ๋“œ ๊ธฐ๋ฐ˜ ์Šค์บ” ์ค‘๋‹จ */
    	SCAN_PAGE_COUNT,            /* ref count ๋ถˆ์ผ์น˜ */
    	SCAN_PAGE_LRU,              /* LRU์— ์—†์Œ */
    	SCAN_PAGE_LOCK,             /* ํŽ˜์ด์ง€ ์ž ๊น€ */
    	SCAN_PAGE_ANON,             /* ์ต๋ช… ํŽ˜์ด์ง€ ์•„๋‹˜ */
    	SCAN_PAGE_COMPOUND,         /* compound page (์ด๋ฏธ THP) */
    	SCAN_ANY_PROCESS,           /* ํ”„๋กœ์„ธ์Šค ๊ฒ€์‚ฌ ์‹คํŒจ */
    	SCAN_VMA_NULL,              /* VMA๊ฐ€ NULL */
    	SCAN_VMA_CHECK,             /* VMA ๊ฒ€์‚ฌ ์‹คํŒจ */
    	SCAN_ADDRESS_RANGE,         /* ์ฃผ์†Œ ๋ฒ”์œ„ ์˜ค๋ฅ˜ */
    	SCAN_DEL_PAGE_LRU,          /* LRU์—์„œ ์ œ๊ฑฐ ์‹คํŒจ */
    	SCAN_ALLOC_HUGE_PAGE_FAIL,  /* hugepage ํ• ๋‹น ์‹คํŒจ */
    	SCAN_CGROUP_CHARGE_FAIL,    /* memcg ์ถฉ์ „ ์‹คํŒจ */
    	SCAN_TRUNCATED,             /* ์ž˜๋ฆฐ ๋งคํ•‘ */
    	SCAN_PAGE_HAS_PRIVATE,      /* private ๋ฐ์ดํ„ฐ ์กด์žฌ (ํŒŒ์ผ ๊ธฐ๋ฐ˜) */
    	SCAN_STORE_FAILED,          /* PTE ์ €์žฅ ์‹คํŒจ */
    	SCAN_COPY_MC,               /* ๋ณต์‚ฌ ์ค‘ ๋ฉ”๋ชจ๋ฆฌ ์นจํ•ด */
    	SCAN_PAGE_FILLED,           /* ํŽ˜์ด์ง€๊ฐ€ ์ด๋ฏธ ์ฑ„์›Œ์ง */
    	SCAN_PAGE_DIRTY_OR_WRITEBACK,/* ๋”๋Ÿฝ๊ฑฐ๋‚˜ writeback ์ค‘ */
    };

    5. THP ์ฃผ๋ฌธ(Order) ๋งˆ์Šคํฌ

    /* include/linux/huge_mm.h:79-95 โ€” THP ์ง€์› order ์ •์˜ */
    /* ์ต๋ช… THP: order-2 ~ PMD_ORDER (order-1์€ THP ๊ตฌ์กฐ ์ œ์•ฝ์œผ๋กœ ์ œ์™ธ) */
    #define THP_ORDERS_ALL_ANON	((BIT(PMD_ORDER + 1) - 1) & ~(BIT(0) | BIT(1)))
    
    /* ํŠน์ˆ˜(DAX/PFNMAP): PMD + PUD ํฌ๊ธฐ๋งŒ ์ง€์› */
    #define THP_ORDERS_ALL_SPECIAL	(BIT(PMD_ORDER) | BIT(PUD_ORDER))
    
    /* ํŒŒ์ผ THP: MAX_PAGECACHE_ORDER๊นŒ์ง€ ์ง€์› (order-0 ์ œ์™ธ) */
    #define THP_ORDERS_ALL_FILE_DEFAULT \
    	((BIT(MAX_PAGECACHE_ORDER + 1) - 1) & ~BIT(0))

    6. compound page์™€ folio

    ์ปค๋„ ๋‚ด๋ถ€์—์„œ Huge Page๋Š” compound page(์ปค๋„ 5.16+์—์„œ๋Š” folio ์ถ”์ƒํ™”)๋กœ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์—ฐ์†๋œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ ๋‹จ์œ„๋กœ ๋ฌถ์–ด ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€(head page)๊ฐ€ ์ „์ฒด๋ฅผ ๋Œ€ํ‘œํ•ฉ๋‹ˆ๋‹ค. 2MB huge page๋Š” 512๊ฐœ์˜ ์—ฐ์† struct page(order-9)๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

    folio๋Š” compound page์˜ ์ถ”์ƒํ™”๋กœ, head/tail page ๊ตฌ๋ถ„์„ ์ œ๊ฑฐํ•˜์—ฌ ์ฝ”๋“œ ์•ˆ์ „์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค. struct folio * ๋ฐ˜ํ™˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

    7. Multi-size THP (mTHP, ์ปค๋„ 6.8+)

    mTHP๋Š” ๊ธฐ์กด PMD ํฌ๊ธฐ(2MB) THP๋ฟ ์•„๋‹ˆ๋ผ 16KB/32KB/64KB ๋“ฑ ์ค‘๊ฐ„ ํฌ๊ธฐ์˜ large folio๋„ PTE ๋ ˆ๋ฒจ์—์„œ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. 2MB ์—ฐ์† ํ™•๋ณด๊ฐ€ ์–ด๋ ค์šด ํ™˜๊ฒฝ์—์„œ ์œ ์šฉํ•˜๋ฉฐ, khugepaged์˜ large folio ๋ณ‘ํ•ฉ๊ณผ ์—ฐ๋™๋ฉ๋‹ˆ๋‹ค.


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

    1. `do_huge_pmd_anonymous_page()` โ€” ์ต๋ช… THP ํดํŠธ ์ฒ˜๋ฆฌ

    PMD ๋ ˆ๋ฒจ์˜ ์ต๋ช… ํŽ˜์ด์ง€ ํดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค. ์ฝ๊ธฐ ์ „์šฉ ์ ‘๊ทผ์ด๋ฉด zero page๋ฅผ ๋งคํ•‘ํ•˜๊ณ , ์“ฐ๊ธฐ ์ ‘๊ทผ์ด๋ฉด ์ƒˆ THP๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

    /* mm/huge_memory.c:1461-1516 โ€” ์ต๋ช… THP ํดํŠธ ๋ฉ”์ธ ํ•ธ๋“ค๋Ÿฌ */
    vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf)
    {
    	struct vm_area_struct *vma = vmf->vma;
    	unsigned long haddr = vmf->address & HPAGE_PMD_MASK;
    	vm_fault_t ret;
    
    	/* 1. VMA๊ฐ€ THP์— ์ ํ•ฉํ•œ์ง€ ๊ฒ€์‚ฌ */
    	if (!thp_vma_suitable_order(vma, haddr, PMD_ORDER))
    		return VM_FAULT_FALLBACK;  /* 4KB ํŽ˜์ด์ง€๋กœ ํด๋ฐฑ */
    
    	ret = vmf_anon_prepare(vmf);
    	if (ret)
    		return ret;
    	khugepaged_enter_vma(vma, vma->vm_flags);
    
    	/* 2. ์ฝ๊ธฐ ์ „์šฉ + zero page ์‚ฌ์šฉ ๊ฐ€๋Šฅ โ†’ ์ œ๋กœ ํŽ˜์ด์ง€ ๋งคํ•‘ */
    	if (!(vmf->flags & FAULT_FLAG_WRITE) &&
    			!mm_forbids_zeropage(vma->vm_mm) &&
    			transparent_hugepage_use_zero_page()) {
    		/* ... huge zero folio ๋งคํ•‘ ... */
    		return ret;
    	}
    
    	/* 3. ์ƒˆ THP ํ• ๋‹น ๋ฐ ๋งคํ•‘ */
    	return __do_huge_pmd_anonymous_page(vmf);
    }

    2. `do_huge_pmd_wp_page()` โ€” THP COW (Write-Protect) ์ฒ˜๋ฆฌ

    THP์— ๋Œ€ํ•œ ์“ฐ๊ธฐ ํดํŠธ(COW)๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€๊ฐ€ exclusive์ด๋ฉด ์žฌ์‚ฌ์šฉํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด THP๋ฅผ ์ชผ๊ฐœ์„œ 4KB๋กœ ํด๋ฐฑํ•ฉ๋‹ˆ๋‹ค.

    /* mm/huge_memory.c:2060-2152 โ€” THP COW ์ฒ˜๋ฆฌ */
    vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf)
    {
    	/* ... */
    	page = pmd_page(orig_pmd);
    	folio = page_folio(page);
    
    	/* 1. exclusive ํŽ˜์ด์ง€ โ†’ ์žฌ์‚ฌ์šฉ (๋ณต์‚ฌ ๋ถˆํ•„์š”) */
    	if (PageAnonExclusive(page))
    		goto reuse;
    
    	/* 2. ์ฐธ์กฐ ์นด์šดํŠธ๊ฐ€ 1์ด๋ฉด exclusive ํ™•๋ณด ๊ฐ€๋Šฅ */
    	if (folio_ref_count(folio) == 1) {
    		folio_move_anon_rmap(folio, vma);
    		SetPageAnonExclusive(page);
    		goto reuse;
    	}
    
    	/* 3. ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ณต์œ  ์ค‘ โ†’ THP๋ฅผ ์ชผ๊ฐœ๊ณ  4KB COW๋กœ ํด๋ฐฑ */
    fallback:
    	__split_huge_pmd(vma, vmf->pmd, vmf->address, false);
    	return VM_FAULT_FALLBACK;
    }

    3. `collapse_huge_page()` โ€” ์‹ค์ œ THP ๋ณ‘ํ•ฉ ์ˆ˜ํ–‰

    khugepaged ๋˜๋Š” MADV_COLLAPSE์— ์˜ํ•ด ํ˜ธ์ถœ๋˜๋ฉฐ, ๊ธฐ์กด 4KB ํŽ˜์ด์ง€ ์„ธํŠธ๋ฅผ ํ•˜๋‚˜์˜ THP๋กœ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

    /* mm/khugepaged.c:1078-1231 โ€” THP ๋ณ‘ํ•ฉ ํ•ต์‹ฌ */
    static enum scan_result collapse_huge_page(struct mm_struct *mm,
    		unsigned long address, int referenced, int unmapped,
    		struct collapse_control *cc)
    {
    	/* 1. mmap_lock ํ•ด์ œ ํ›„ hugepage ํ• ๋‹น (์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Œ) */
    	mmap_read_unlock(mm);
    	result = alloc_charge_folio(&folio, mm, cc);
    
    	/* 2. VMA/PMD ์žฌ๊ฒ€์ฆ */
    	mmap_read_lock(mm);
    	result = hugepage_vma_revalidate(mm, address, true, &vma, cc);
    
    	/* 3. ์Šค์™‘์ธ ์ฒ˜๋ฆฌ (unmapped PTE๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ) */
    	if (unmapped)
    		result = __collapse_huge_page_swapin(mm, vma, address, pmd, referenced);
    
    	/* 4. mmap_write_lock ํš๋“ โ†’ ์•ˆ์ „ํ•œ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ˆ˜์ • */
    	mmap_write_lock(mm);
    	_pmd = pdp_collapse_flush(vma, address, pmd);  /* ๊ธฐ์กด PMD ์ œ๊ฑฐ */
    
    	/* 5. PTE ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ */
    	result = __collapse_huge_page_isolate(vma, address, pte, cc, &compound_pagelist);
    
    	/* 6. ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ (4KB โ†’ THP) */
    	result = __collapse_huge_page_copy(pte, folio, pmd, _pmd, vma, address, pte_ptl, ...);
    
    	/* 7. ์ƒˆ THP ๋งคํ•‘ ์„ค์น˜ */
    	map_anon_folio_pmd_nopf(folio, pmd, vma, address);
    }

    4. `hpage_collapse_scan_pmd()` โ€” PMD ์˜์—ญ PTE ์Šค์บ”

    PMD ์˜์—ญ์˜ 512๊ฐœ PTE๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๋ณ‘ํ•ฉ ๊ฐ€๋Šฅํ•œ์ง€ ํŒ๋ณ„ํ•ฉ๋‹ˆ๋‹ค.

    /* mm/khugepaged.c:1233-1401 โ€” PMD ์˜์—ญ ์Šค์บ” */
    static enum scan_result hpage_collapse_scan_pmd(struct mm_struct *mm,
    		struct vm_area_struct *vma, unsigned long start_addr, ...)
    {
    	/* 512๊ฐœ PTE ์ˆœํšŒ */
    	for (addr = start_addr, _pte = pte; _pte < pte + HPAGE_PMD_NR; _pte++) {
    		if (pte_none_or_zero(pteval)) {
    			++none_or_zero;
    			/* none ์ดˆ๊ณผ โ†’ ์Šค์บ” ์ค‘๋‹จ */
    		}
    		if (!pte_present(pteval)) {
    			++unmapped;
    			/* swap PTE ์ดˆ๊ณผ โ†’ ์Šค์บ” ์ค‘๋‹จ */
    		}
    		if (folio_maybe_mapped_shared(folio)) {
    			++shared;
    			/* ๊ณต์œ  ํŽ˜์ด์ง€ ์ดˆ๊ณผ โ†’ ์Šค์บ” ์ค‘๋‹จ */
    		}
    		/* NUMA ๋…ธ๋“œ๋ณ„ ๋กœ๋“œ ๊ธฐ๋ก */
    		cc->node_load[folio_nid(folio)]++;
    	}
    	/* referenced ํŽ˜์ด์ง€ ๋ถ€์กฑ โ†’ ๋ณ‘ํ•ฉ ์•ˆ ํ•จ */
    	if (!referenced || (unmapped && referenced < HPAGE_PMD_NR / 2))
    		result = SCAN_LACK_REFERENCED_PAGE;
    	else
    		result = collapse_huge_page(mm, start_addr, referenced, unmapped, cc);
    }

    5. `khugepaged()` โ€” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๋ฃจํ”„

    ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ชจ๋“  ๋“ฑ๋ก๋œ mm์„ ์Šค์บ”ํ•˜์—ฌ ๋ณ‘ํ•ฉ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

    /* mm/khugepaged.c:2612-2631 โ€” khugepaged ๋ฉ”์ธ ๋ฃจํ”„ */
    static int khugepaged(void *none)
    {
    	set_freezable();
    	set_user_nice(current, MAX_NICE);  /* ์ตœ์ € ์šฐ์„ ์ˆœ์œ„ */
    
    	while (!kthread_should_stop()) {
    		khugepaged_do_scan(&khugepaged_collapse_control);
    		khugepaged_wait_work();  /* scan_sleep_millisecs ๋™์•ˆ ๋Œ€๊ธฐ */
    	}
    	return 0;
    }

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

    A. ํŽ˜์ด์ง€ ํดํŠธ โ†’ THP ํ• ๋‹น ํ๋ฆ„

    CPU ํดํŠธ ๋ฐœ์ƒ
    โ†’ handle_mm_fault()
    โ†’ __handle_mm_fault()
    โ†’ handle_pmd_fault()
    โ†’ do_huge_pmd_anonymous_page()     โ† THP ์ง„์ž…์ 
    โ”œโ”€ thp_vma_suitable_order()       VMA ์ •๋ ฌ/ํฌ๊ธฐ ๊ฒ€์‚ฌ
    โ”œโ”€ vmf_anon_prepare()             anon_vma ์ค€๋น„
    โ”œโ”€ khugepaged_enter_vma()         mm ๋“ฑ๋ก (๋ฏธ๋“ฑ๋ก ์‹œ)
    โ”œโ”€ zero page ๊ฒฝ๋กœ:
    โ”‚   โ””โ”€ set_huge_zero_folio()      ์ œ๋กœ ํŽ˜์ด์ง€ ๋งคํ•‘
    โ””โ”€ ์ผ๋ฐ˜ ๊ฒฝ๋กœ:
    โ””โ”€ __do_huge_pmd_anonymous_page()
    โ”œโ”€ vma_alloc_anon_folio_pmd()   ์ƒˆ THP ํ• ๋‹น
    โ”œโ”€ pte_alloc_one()               ํ•˜์œ„ PTE ํ…Œ์ด๋ธ” ํ• ๋‹น
    โ””โ”€ map_anon_folio_pmd_pf()       PMD ๋งคํ•‘ ์„ค์น˜

    B. THP COW (์“ฐ๊ธฐ ํดํŠธ) ํ๋ฆ„

    ์“ฐ๊ธฐ ํดํŠธ (THP ์˜์—ญ)
    โ†’ handle_mm_fault()
    โ†’ __handle_mm_fault()
    โ†’ handle_pmd_fault()
    โ†’ do_huge_pmd_wp_page()            โ† THP COW
    โ”œโ”€ is_huge_zero_pmd()?
    โ”‚   โ””โ”€ do_huge_zero_wp_pmd()     ์ œ๋กœ ํŽ˜์ด์ง€ COW
    โ”œโ”€ PageAnonExclusive? โ†’ reuse     exclusive๋ฉด ์žฌ์‚ฌ์šฉ
    โ”œโ”€ folio_ref_count == 1? โ†’ reuse  ๋‹จ๋… ์‚ฌ์šฉ์ด๋ฉด ์žฌ์‚ฌ์šฉ
    โ””โ”€ fallback:
    โ”œโ”€ __split_huge_pmd()         THP๋ฅผ 4KB๋กœ ๋ถ„ํ• 
    โ””โ”€ VM_FAULT_FALLBACK          ์ผ๋ฐ˜ 4KB COW๋กœ ์žฌ์‹œ๋„

    C. khugepaged ๋ณ‘ํ•ฉ ํ๋ฆ„

    khugepaged ์Šค๋ ˆ๋“œ (๋ฐฑ๊ทธ๋ผ์šด๋“œ)
    โ†’ khugepaged()
    โ†’ khugepaged_do_scan()
    โ†’ khugepaged_scan_mm_slot()           mm ์ˆœํšŒ
    โ†’ hpage_collapse_scan_pmd()         PMD ์˜์—ญ ์Šค์บ”
    โ”œโ”€ find_pmd_or_thp_or_none()      PMD ์ƒํƒœ ํ™•์ธ
    โ”œโ”€ PTE 512๊ฐœ ์ˆœํšŒ:
    โ”‚   โ”œโ”€ none_or_zero ์ฒดํฌ
    โ”‚   โ”œโ”€ swap/unmapped ์ฒดํฌ
    โ”‚   โ”œโ”€ shared ์ฒดํฌ
    โ”‚   โ””โ”€ referenced ์ฒดํฌ
    โ””โ”€ collapse_huge_page()           ๋ณ‘ํ•ฉ ์‹คํ–‰
    โ”œโ”€ alloc_charge_folio()       THP ํ• ๋‹น + memcg ์ถฉ์ „
    โ”œโ”€ hugepage_vma_revalidate()  VMA ์žฌ๊ฒ€์ฆ
    โ”œโ”€ __collapse_huge_page_swapin()  ์Šค์™‘์ธ
    โ”œโ”€ pdp_collapse_flush()       ๊ธฐ์กด PMD ์ œ๊ฑฐ
    โ”œโ”€ __collapse_huge_page_isolate() PTE ๊ฒฉ๋ฆฌ
    โ”œโ”€ __collapse_huge_page_copy()   ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ
    โ””โ”€ map_anon_folio_pmd_nopf()  ์ƒˆ THP ๋งคํ•‘

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

    THP defrag ์ •์ฑ… ๋น„๊ต

    ์ •์ฑ…GFP ํ”Œ๋ž˜๊ทธ๋™์ž‘์ ํ•ฉํ•œ ๊ฒฝ์šฐ
    **always**`GFP_TRANSHUGE`์ฆ‰์‹œ compaction ์‹œ๋„, ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„๋Œ€์šฉ๋Ÿ‰ ๋ฉ”๋ชจ๋ฆฌ ์„œ๋ฒ„, ์ง€์—ฐ ํ—ˆ์šฉ
    **defer**`GFP_TRANSHUGE_LIGHT + __GFP_KSWAPD_RECLAIM`kcompactd์— ์œ„์ž„, ๋น ๋ฅธ ์‹คํŒจ์ผ๋ฐ˜ ๋ฐ์Šคํฌํ†ฑ, ๋ฐ˜์‘์„ฑ ์ค‘์š”
    **defer+madvise**`GFP_TRANSHUGE_LIGHT`MADV_HUGEPAGE๋Š” ์ง์ ‘, ์•„๋‹ˆ๋ฉด kcompactdํ˜ผํ•ฉ ์›Œํฌ๋กœ๋“œ
    **madvise**`GFP_TRANSHUGE_LIGHT`MADV_HUGEPAGE๋งŒ ์ง์ ‘ compaction์•ฑ์ด THP ์˜๋„ ์ง€์ •
    **never**`GFP_TRANSHUGE_LIGHT`compaction ์—†์ด ์ฆ‰์‹œ ์‹คํŒจ์‹ค์‹œ๊ฐ„, ์ž„๋ฒ ๋””๋“œ

    THP enabled ๋ชจ๋“œ ๋น„๊ต

    ๋ชจ๋“œํ”Œ๋ž˜๊ทธ๋™์ž‘
    **always**`TRANSPARENT_HUGEPAGE_FLAG`๋ชจ๋“  VMA์— THP ์ ์šฉ ์‹œ๋„
    **madvise**`TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG``MADV_HUGEPAGE`๊ฐ€ ์„ค์ •๋œ VMA๋งŒ ์ ์šฉ
    **never**(both cleared)THP ๋น„ํ™œ์„ฑํ™” (hugepage_madvise๋งŒ ํ—ˆ์šฉ)

    ์Šค์บ” ๊ฒฐ๊ณผ๋ณ„ khugepaged ๋™์ž‘

    ์Šค์บ” ๊ฒฐ๊ณผkhugepaged ๋™์ž‘MADV_COLLAPSE ๋ฐ˜ํ™˜๊ฐ’
    `SCAN_SUCCEED``collapse_huge_page()` ํ˜ธ์ถœ์„ฑ๊ณต
    `SCAN_ALLOC_HUGE_PAGE_FAIL`alloc_sleep ํ›„ ์žฌ์‹œ๋„ ๋˜๋Š” ์ค‘๋‹จ`-ENOMEM`
    `SCAN_EXCEED_NONE_PTE`๋‹ค์Œ PMD๋กœ ๊ฑด๋„ˆ๋œ€`-EBUSY`
    `SCAN_PAGE_COUNT`, `SCAN_PAGE_LOCK`๋‹ค์Œ PMD๋กœ ๊ฑด๋„ˆ๋œ€`-EAGAIN`
    `SCAN_LACK_REFERENCED_PAGE`๋‹ค์Œ PMD๋กœ ๊ฑด๋„ˆ๋œ€`-EINVAL`
    `SCAN_PTE_UFFD_WP`๋‹ค์Œ PMD๋กœ ๊ฑด๋„ˆ๋œ€`-EINVAL`

    Huge Pages ์œ ํ˜• ๋น„๊ต

    ์œ ํ˜•ํ• ๋‹น ์‹œ์ ํฌ๊ธฐ๊ด€๋ฆฌ ๋ฐฉ์‹๋น„๊ณ 
    **HugeTLB**๋ถ€ํŒ… ์‹œ ์‚ฌ์ „ ์˜ˆ์•ฝ (mmap)2MB / 1GB์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์  mmapDPDK, KVM, DB์— ์ ํ•ฉ
    **THP (PMD)**ํดํŠธ ์‹œ/๋ณ‘ํ•ฉ ์‹œ ๋™์ 2MB (512 * 4KB)`khugepaged` ์ž๋™ ๋ณ‘ํ•ฉcompaction ๋น„์šฉ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
    **mTHP (large folio)**ํดํŠธ ์‹œ ๋™์ order-2 ~ order-9 (16KB~2MB)PTE ๋ ˆ๋ฒจ large folio์ปค๋„ 6.8+ ์‹ ๊ทœ, 2MB ํ™•๋ณด ์–ด๋ ค์šธ ๋•Œ ์œ ์šฉ
    **folio**์ปค๋„ 5.16+ ์ถ”์ƒํ™”๋ชจ๋“  ํฌ๊ธฐcompound page ์ถ”์ƒํ™”head/tail ๊ตฌ๋ถ„ ์ œ๊ฑฐ, API ์•ˆ์ •์„ฑ ๊ฐœ์„ 

    sysfs tunables ์š”์•ฝ

    ํŒŒ์ผ๊ธฐ๋ณธ๊ฐ’์„ค๋ช…
    `enabled``madvise`THP ํ™œ์„ฑํ™” ๋ชจ๋“œ (always/madvise/never)
    `defrag``madvise`๋””ํ”„๋ž˜๊ทธ ์ •์ฑ…
    `use_zero_page``1`์ฝ๊ธฐ ์ „์šฉ THP์— zero page ์‚ฌ์šฉ ์—ฌ๋ถ€
    `hpage_pmd_size``2097152`PMD ํฌ๊ธฐ (bytes)
    `khugepaged/pages_to_scan``HPAGE_PMD_NR * 8`ไธ€ๆฌก ์Šค์บ” ์‹œ ์Šค์บ”ํ•  PTE ์ˆ˜
    `khugepaged/scan_sleep_millisecs``10000`์Šค์บ” ๊ฐ„ ๋Œ€๊ธฐ ์‹œ๊ฐ„ (ms)
    `khugepaged/alloc_sleep_millisecs``60000`ํ• ๋‹น ์‹คํŒจ ํ›„ ๋Œ€๊ธฐ ์‹œ๊ฐ„ (ms)
    `khugepaged/max_ptes_none``HPAGE_PMD_NR - 1`ํ—ˆ์šฉ ์ตœ๋Œ€ ๋นˆ PTE ์ˆ˜
    `khugepaged/max_ptes_swap``HPAGE_PMD_NR / 8`ํ—ˆ์šฉ ์ตœ๋Œ€ ์Šค์™‘ PTE ์ˆ˜
    `khugepaged/max_ptes_shared``HPAGE_PMD_NR / 2`ํ—ˆ์šฉ ์ตœ๋Œ€ ๊ณต์œ  PTE ์ˆ˜

    ๊ด€๋ จ ๋ฌธ์„œ

  • VMA / mmap โ€” THP๊ฐ€ ๋งคํ•‘๋˜๋Š” VMA ๊ธฐ๋ฐ˜
  • Buddy Allocator โ€” THP ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น
  • Compaction โ€” THP ํ• ๋‹น ์‹œ compaction ํ•„์š”์„ฑ
  • Folio / Page Cache โ€” large folio์™€ THP์˜ ๊ด€๊ณ„
  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” โ€” ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ๊ณ„์ธต ์กฐ๊ฐ๋„
  • Swap / zswap โ€” THP ์Šค์™‘์ธ/์Šค์™‘์•„์›ƒ
  • Userfaultfd โ€” THP์™€ uffd-wp ์ƒํ˜ธ์ž‘์šฉ