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๋ ํฌ๊ฒ ๋ ๊ฐ์ง ์ ํ์ด ์์ต๋๋ค:
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 ํธ์ถ ํ๋ฆ ๋ค์ด์ด๊ทธ๋จ
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 | ์ฌ์ฉ์๊ฐ ๋ช ์์ mmap | DPDK, 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 ์ |