관련 소스:mm/huge_memory.c (4978줄),mm/khugepaged.c (2873줄),include/linux/huge_mm.h (807줄)
관련 문서: Huge Pages / THP · VMA / mmap · Folio / Page Cache · Compaction · 메모리 관리 개요
Linux 6.8+에서 도입된 mTHP (multi-size THP) 는 기존 THP가 지원하던 PMD 크기(2MB)뿐 아니라, order-2(16KB)부터 order-9(PMD 크기)까지 다양한 크기의 large folio를 익명 메모리에 할당할 수 있게 하는 메커니즘입니다. mTHP는 huge_anon_orders_always, huge_anon_orders_madvise, huge_anon_orders_inherit 비트맵을 통해 order별로 개별적인 활성화 정책을 제공하며, sysfs 인터페이스(/sys/kernel/mm/transparent_hugepage/hugepages-XkB/)를 통해 order별 제어가 가능합니다.
mTHP는 기존 THP의 PMD_ORDER 전용 경로와 달리, thp_vma_allowable_orders() 함수가 비트마스크 기반으로 허용되는 order를 결정하고, 페이지 폴트 처리 경로에서 order별 large folio를 할당합니다. khugepaged 백그라운드 스레드는 현재까지 PMD_ORDER(2MB) 전용으로 동작하며, sub-PMD order의 병합은 아직 구현되지 않았습니다. mTHP는 per-CPU 통계(mthp_stat_item)를 통해 order별 할당/폴백/스왑/스플릿 이벤트를 추적합니다.
일상 비유: 기존 THP는 모든 큰 글씨를 2MB 크기의 대형 사전에만 넣을 수 있었다면, mTHP는 16KB, 32KB, 64KB 등 다양한 크기의 중간 사전도 사용할 수 있게 된 것과 같습니다. 작은 문서는 중간 사전에, 큰 문서는 대형 사전에 넣어 책장 넘기는 횟수(TLB 미스)를 최적화합니다.
/* 주요 소스 파일 경로 */
mm/huge_memory.c /* mTHP sysfs, order별 정책, folio 분할, 통계 */
mm/khugepaged.c /* khugepaged 스레드 (PMD_ORDER 전용) */
include/linux/huge_mm.h /* THP_ORDERS_ALL_* 매크로, mthp_stat_item, thpsize */
include/linux/page-flags.h /* PG_partially_mapped, PG_large_rmappable */
include/linux/mmzone.h /* struct deferred_split, NR_PCP_THP */
# mTHP 지원 order 확인 (x86_64: order 2~9 = 16KB~2MB)
ls /sys/kernel/mm/transparent_hugepage/
# hugepages-16kB hugepages-32kB hugepages-64kB hugepages-128kB
# hugepages-256kB hugepages-512kB hugepages-1024kB hugepages-2048kB
# 특정 order의 활성화 정책 확인 (예: 64KB = order-4)
cat /sys/kernel/mm/transparent_hugepage/hugepages-64kB/enabled
# [always] inherit madvise never
# order별 mTHP 통계 확인
cat /sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/anon_fault_alloc
cat /sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/anon_fault_fallback
cat /sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/split
# 전체 mTHP 통계 (모든 order)
cat /proc/vmstat | grep -E 'thp_|khugepaged'
# THP 관련 사용량과 큰 페이지 지표 확인
grep -E 'AnonHugePages|ShmemHugePages|FileHugePages|HugePages_' /proc/meminfo
# khugepaged 관련 (PMD_ORDER 전용)
cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed
# THP 전역 활성화 상태
cat /sys/kernel/mm/transparent_hugepage/enabled
# 커널 부트 파라미터로 mTHP 설정 확인
cat /proc/cmdline | grep -o 'thp_anon=[^ ]*'
익명 메모리에 허용되는 모든 mTHP order의 비트마스크입니다. order-0과 order-1은 제외됩니다.
/* include/linux/huge_mm.h:79 — 익명 mTHP 허용 order 비트맵 */
#define THP_ORDERS_ALL_ANON ((BIT(PMD_ORDER + 1) - 1) & ~(BIT(0) | BIT(1)))
/* include/linux/huge_mm.h:86-89 — 특수/file mTHP 허용 order 비트맵 */
#define THP_ORDERS_ALL_SPECIAL \
(BIT(PMD_ORDER) | BIT(PUD_ORDER))
#define THP_ORDERS_ALL_FILE_DEFAULT \
((BIT(MAX_PAGECACHE_ORDER + 1) - 1) & ~BIT(0))
/* include/linux/huge_mm.h:94-95 — 전체 THP 허용 order 비트맵 */
#define THP_ORDERS_ALL \
(THP_ORDERS_ALL_ANON | THP_ORDERS_ALL_SPECIAL | THP_ORDERS_ALL_FILE_DEFAULT)
order별 THP 활성화 정책을 비트맵으로 관리합니다. 각 비트는 하나의 order를 나타냅니다.
/* include/linux/huge_mm.h:181-183 — order별 정책 비트맵 선언 */
extern unsigned long huge_anon_orders_always; /* 항상 사용 */
extern unsigned long huge_anon_orders_madvise; /* madvise 시 사용 */
extern unsigned long huge_anon_orders_inherit; /* 상속 (기본값: PMD_ORDER만) */
/* mm/huge_memory.c:790-791 — 기본값: PMD_ORDER만 inherit */
huge_anon_orders_inherit = BIT(PMD_ORDER);
order별로 추적되는 mTHP 이벤트 통계 항목입니다.
/* include/linux/huge_mm.h:127-146 — mTHP 통계 항목 열거형 */
enum mthp_stat_item {
MTHP_STAT_ANON_FAULT_ALLOC, /* 익명 폴트 시 order별 할당 성공 */
MTHP_STAT_ANON_FAULT_FALLBACK, /* 익명 폴트 시 order별 폴백 */
MTHP_STAT_ANON_FAULT_FALLBACK_CHARGE, /* 폴백 후 memcg 청구 실패 */
MTHP_STAT_ZSWPOUT, /* order별 zswap out */
MTHP_STAT_SWPIN, /* order별 swap in */
MTHP_STAT_SWPIN_FALLBACK, /* swap in 폴백 */
MTHP_STAT_SWPIN_FALLBACK_CHARGE, /* swap in 폴백 후 청구 실패 */
MTHP_STAT_SWPOUT, /* order별 swap out */
MTHP_STAT_SWPOUT_FALLBACK, /* swap out 폴백 */
MTHP_STAT_SHMEM_ALLOC, /* shmem order별 할당 */
MTHP_STAT_SHMEM_FALLBACK, /* shmem 폴백 */
MTHP_STAT_SHMEM_FALLBACK_CHARGE, /* shmem 폴백 후 청구 실패 */
MTHP_STAT_SPLIT, /* order별 folio 분할 성공 */
MTHP_STAT_SPLIT_FAILED, /* folio 분할 실패 */
MTHP_STAT_SPLIT_DEFERRED, /* 지연 분할 */
MTHP_STAT_NR_ANON, /* 현재 order별 익명 large folio 수 */
MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, /* 부분 매핑된 익명 folio 수 */
__MTHP_STAT_COUNT
};
per-CPU로 관리되는 mTHP 통계 구조체입니다. 각 order별로 모든 통계 항목을 가집니다.
/* include/linux/huge_mm.h:149-151 — per-CPU mTHP 통계 구조체 */
struct mthp_stat {
unsigned long stats[ilog2(MAX_PTRS_PER_PTE) + 1][__MTHP_STAT_COUNT];
};
DECLARE_PER_CPU(struct mthp_stat, mthp_stats);
sysfs에서 각 order의 THP 크기를 나타내는 kobject 구조체입니다.
/* include/linux/huge_mm.h:315-321 — THP 크기별 sysfs kobject */
struct thpsize {
struct kobject kobj;
struct list_head node;
int order;
};
#define to_thpsize(kobj) container_of(kobj, struct thpsize, kobj)
large folio의 지연 분할을 관리하는 큐 구조체입니다.
/* include/linux/mmzone.h:1341-1345 — 지연 분할 큐 */
struct deferred_split {
spinlock_t split_queue_lock;
struct list_head split_queue;
unsigned long split_queue_len;
};
지정된 VMA에서 허용되는 hugepage order 비트마스크를 결정하는 핵심 함수입니다.
/* mm/huge_memory.c:103-211 — VMA별 허용 order 비트마스크 결정 */
unsigned long __thp_vma_allowable_orders(struct vm_area_struct *vma,
vm_flags_t vm_flags,
enum tva_type type,
unsigned long orders)
분기 로직:
1. VMA 유형에 따라 지원 order 세트 선택 (익명: THP_ORDERS_ALL_ANON, 특수: THP_ORDERS_ALL_SPECIAL, 파일: THP_ORDERS_ALL_FILE_DEFAULT)
2. 하드웨어 THP 비활성화 확인
3. VMA 비활성화 플래그 검사 (VM_NOHUGEPAGE, VM_DONTEXPAND)
4. DAX/PFNMAP 처리
5. khugepaged 전용 VMA 제외
6. 정렬/크기 적합성 검사 (thp_vma_suitable_order())
7. shmem 정책 적용
8. 파일 THP 요구사항 확인 (글로벌 활성화, VM_HUGEPAGE 플래그, huge_fault() 핸들러)
커널 부트 파라미터 thp_anon=를 파싱하여 order별 정책을 설정합니다.
/* mm/huge_memory.c:991-1074 — thp_anon= 부트 파라미터 파서 */
static int __init setup_thp_anon(char *str)
분기 로직:
1. 크기 문자열을 order로 변환 (get_order_from_str())
2. 정책 문자열 파싱 (always, madvise, inherit, never)
3. huge_anon_orders_always/madvise/inherit 비트맵 업데이트
4. anon_orders_configured = true 설정
PMD 수준의 익명 페이지 폴트를 처리하는 함수입니다. zero page 최적화와 일반 할당 경로를 가집니다.
/* mm/huge_memory.c:1461-1516 — PMD 익명 페이지 폴트 진입점 */
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;
if (!thp_vma_suitable_order(vma, haddr, PMD_ORDER))
return VM_FAULT_FALLBACK;
ret = vmf_anon_prepare(vmf);
if (ret)
return ret;
khugepaged_enter_vma(vma, vma->vm_flags);
if (!(vmf->flags & FAULT_FLAG_WRITE) &&
!mm_forbids_zeropage(vma->vm_mm) &&
transparent_hugepage_use_zero_page()) {
pgtable_t pgtable;
struct folio *zero_folio;
vm_fault_t ret;
pgtable = pte_alloc_one(vma->vm_mm);
if (unlikely(!pgtable))
return VM_FAULT_OOM;
zero_folio = mm_get_huge_zero_folio(vma->vm_mm);
if (unlikely(!zero_folio)) {
pte_free(vma->vm_mm, pgtable);
count_vm_event(THP_FAULT_FALLBACK);
return VM_FAULT_FALLBACK;
}
vmf->ptl = pmd_lock(vma->vm_mm, vmf->pmd);
ret = 0;
if (pmd_none(*vmf->pmd)) {
ret = check_stable_address_space(vma->vm_mm);
if (ret) {
spin_unlock(vmf->ptl);
pte_free(vma->vm_mm, pgtable);
} else if (userfaultfd_missing(vma)) {
spin_unlock(vmf->ptl);
pte_free(vma->vm_mm, pgtable);
ret = handle_userfault(vmf, VM_UFFD_MISSING);
VM_BUG_ON(ret & VM_FAULT_FALLBACK);
} else {
set_huge_zero_folio(pgtable, vma->vm_mm, vma,
haddr, vmf->pmd, zero_folio);
update_mmu_cache_pmd(vma, vmf->address, vmf->pmd);
spin_unlock(vmf->ptl);
}
} else {
spin_unlock(vmf->ptl);
pte_free(vma->vm_mm, pgtable);
}
return ret;
}
return __do_huge_pmd_anonymous_page(vmf);
}
분기 로직:
1. thp_vma_suitable_order()로 VMA 정렬/크기 검사
2. vmf_anon_prepare()로 anon_vma 초기화
3. khugepaged_enter_vma()로 khugepaged 스캔 대상 등록
4. 읽기 전용 + zero page 가능 시 set_huge_zero_folio() 호출
5. 일반 경로: __do_huge_pmd_anonymous_page() 호출
PMD 수준 large folio를 할당하고 페이지 테이블에 매핑하는 함수입니다.
/* mm/huge_memory.c:1323-1373 — PMD large folio 할당 및 매핑 */
static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf)
{
unsigned long haddr = vmf->address & HPAGE_PMD_MASK;
struct vm_area_struct *vma = vmf->vma;
struct folio *folio;
pgtable_t pgtable;
vm_fault_t ret = 0;
folio = vma_alloc_anon_folio_pmd(vma, vmf->address);
if (unlikely(!folio))
return VM_FAULT_FALLBACK;
pgtable = pte_alloc_one(vma->vm_mm);
if (unlikely(!pgtable)) {
ret = VM_FAULT_OOM;
goto release;
}
vmf->ptl = pmd_lock(vma->vm_mm, vmf->pmd);
if (unlikely(!pmd_none(*vmf->pmd))) {
goto unlock_release;
} else {
ret = check_stable_address_space(vma->vm_mm);
if (ret)
goto unlock_release;
/* 페이지 폴트를 사용자 공간에 전달 */
if (userfaultfd_missing(vma)) {
spin_unlock(vmf->ptl);
folio_put(folio);
pte_free(vma->vm_mm, pgtable);
ret = handle_userfault(vmf, VM_UFFD_MISSING);
VM_BUG_ON(ret & VM_FAULT_FALLBACK);
return ret;
}
pgtable_trans_huge_deposit(vma->vm_mm, vmf->pmd, pgtable);
map_anon_folio_pmd_pf(folio, vmf->pmd, vma, haddr);
mm_inc_nr_ptes(vma->vm_mm);
spin_unlock(vmf->ptl);
}
return 0;
unlock_release:
spin_unlock(vmf->ptl);
release:
if (pgtable)
pte_free(vma->vm_mm, pgtable);
folio_put(folio);
return ret;
}
분기 로직:
1. vma_alloc_anon_folio_pmd()로 order별 large folio 할당
2. pte_alloc_one()으로 하위 PTE 테이블 할당
3. PMD 락 획득 후 pmd_none() 검사
4. check_stable_address_space()으로 안정적 주소 공간 확인
5. userfaultfd_missing() 시 userfaultfd 핸들러 호출
6. pgtable_trans_huge_deposit() + map_anon_folio_pmd_pf()로 매핑
PMD 수준 large folio의 COW를 처리하는 함수입니다.
/* mm/huge_memory.c:2060-2152 — PMD COW 처리 */
vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf)
{
const bool unshare = vmf->flags & FAULT_FLAG_UNSHARE;
struct vm_area_struct *vma = vmf->vma;
struct page *page;
struct folio *folio;
unsigned long haddr = vmf->address & HPAGE_PMD_MASK;
pmd_t orig_pmd = vmf->orig_pmd;
vmf->ptl = pmd_lockptr(vma->vm_mm, vmf->pmd);
VM_BUG_ON_VMA(!vma->anon_vma, vma);
if (is_huge_zero_pmd(orig_pmd)) {
vm_fault_t ret = do_huge_zero_wp_pmd(vmf);
if (!(ret & VM_FAULT_FALLBACK))
return ret;
/* THP를 할당할 수 없으면 PMD 분할로 폴백 */
goto fallback;
}
spin_lock(vmf->ptl);
if (unlikely(!pmd_same(*vmf->pmd, orig_pmd))) {
spin_unlock(vmf->ptl);
return 0;
}
page = pmd_page(orig_pmd);
folio = page_folio(page);
VM_BUG_ON_PAGE(!PageHead(page), page);
/* PT 락만 잡았을 때의 조기 검사 */
if (PageAnonExclusive(page))
goto reuse;
if (!folio_trylock(folio)) {
folio_get(folio);
spin_unlock(vmf->ptl);
folio_lock(folio);
spin_lock(vmf->ptl);
if (unlikely(!pmd_same(*vmf->pmd, orig_pmd))) {
spin_unlock(vmf->ptl);
folio_unlock(folio);
folio_put(folio);
return 0;
}
folio_put(folio);
}
/* 잠깐 PT 락을 내려놓은 뒤 다시 검사 */
if (PageAnonExclusive(page)) {
folio_unlock(folio);
goto reuse;
}
/* do_wp_page()처럼 추가 참조가 없을 때만 독점 재사용 가능 */
if (folio_ref_count(folio) >
1 + folio_test_swapcache(folio) * folio_nr_pages(folio))
goto unlock_fallback;
if (folio_test_swapcache(folio))
folio_free_swap(folio);
if (folio_ref_count(folio) == 1) {
pmd_t entry;
folio_move_anon_rmap(folio, vma);
SetPageAnonExclusive(page);
folio_unlock(folio);
reuse:
if (unlikely(unshare)) {
spin_unlock(vmf->ptl);
return 0;
}
entry = pmd_mkyoung(orig_pmd);
entry = maybe_pmd_mkwrite(pmd_mkdirty(entry), vma);
if (pmdp_set_access_flags(vma, haddr, vmf->pmd, entry, 1))
update_mmu_cache_pmd(vma, vmf->address, vmf->pmd);
spin_unlock(vmf->ptl);
return 0;
}
unlock_fallback:
folio_unlock(folio);
spin_unlock(vmf->ptl);
fallback:
/* PMD를 4KB PTE로 분할 후 폴백 */
__split_huge_pmd(vma, vmf->pmd, vmf->address, false);
return VM_FAULT_FALLBACK;
}
분기 로직:
1. is_huge_zero_pmd()로 zero PMD 검사
2. PageAnonExclusive()로 exclusive 재사용 가능 확인
3. folio_ref_count()로 참조 카운트 검사 (1이면 exclusive)
4. exclusive 가능 시 pmd_mkyoung() + maybe_pmd_mkwrite()로 접근/쓰기 플래그 업데이트
5. 불가능 시 __split_huge_pmd()로 PMD 분할 후 VM_FAULT_FALLBACK 반환
sysfs 인터페이스를 통해 order별 THP 활성화 정책을 읽고 씁니다.
/* mm/huge_memory.c:500-563 — sysfs order별 enabled 제어 */
static ssize_t anon_enabled_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf);
static ssize_t anon_enabled_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count);
동작:
show: [always], [inherit], [madvise], [never] 중 현재 정책 반환store: 정책 문자열 파싱 후 huge_anon_orders_always/madvise/inherit 비트맵 업데이트, start_stop_khugepaged() 호출large folio를 지연 분할 큐에 등록하여 이후 shrinker에 의해 분할되도록 합니다.
/* mm/huge_memory.c:4315-4365 — 지연 분할 큐 등록 */
void deferred_split_folio(struct folio *folio, bool partially_mapped)
분기 로직:
1. order ≤ 1 folio는 큐에 넣지 않음 (공간 부족)
2. folio_test_large() 검사
3. partially_mapped 플래그 설정 시 MTHP_STAT_NR_ANON_PARTIALLY_MAPPED 증가
4. MTHP_STAT_SPLIT_DEFERRED 통계 업데이트
folio가 지정된 order로 분할 가능한지 검사합니다.
/* mm/huge_memory.c:3708-3769 — folio 분할 가능성 검사 */
int folio_check_splittable(struct folio *folio, unsigned int new_order,
enum split_type split_type)
분기 로직:
1. order-1 익명 folio 분할 거부 (-EINVAL)
2. 파일 기반 folio: mapping_large_folio_support() 검사
3. swapcache folio: non-zero order 분할 거부
4. non-uniform 분할 시 추가 검사
사용자 페이지 폴트 (PMD 수준)
→ handle_mm_fault()
→ __handle_mm_fault()
→ handle_pte_fault()
→ do_anonymous_page()
→ vma_alloc_anon_folio() ← order 선택
→ thp_vma_allowable_orders() ← 허용 order 비트마스크 결정
→ __thp_vma_allowable_orders() ← VMA별 필터링
→ alloc_folio(gfp, order) ← order별 large folio 할당
→ finish_fault()
→ do_set_pte() ← PTE 설정
사용자 페이지 폴트 (PMD 수준 large folio)
→ handle_mm_fault()
→ __handle_mm_fault()
→ handle_pte_fault()
→ do_huge_pmd_anonymous_page() ← PMD 진입점 (huge_memory.c:1461)
→ thp_vma_suitable_order() ← VMA 정렬/크기 검사
→ vmf_anon_prepare() ← anon_vma 초기화
→ khugepaged_enter_vma() ← khugepaged 스캔 대상 등록
→ __do_huge_pmd_anonymous_page() ← 실제 할당 (huge_memory.c:1323)
→ vma_alloc_anon_folio_pmd() ← order별 large folio 할당
→ pte_alloc_one() ← 하위 PTE 테이블 할당
→ pmd_lock() ← PMD 락
→ check_stable_address_space() ← 안정적 주소 공간 확인
→ pgtable_trans_huge_deposit() ← PTE 테이블 배포
→ map_anon_folio_pmd_pf() ← PMD에 large folio 매핑
사용자 COW 폴트 (PMD 수준)
→ handle_mm_fault()
→ __handle_mm_fault()
→ handle_pte_fault()
→ do_wp_page()
→ do_huge_pmd_wp_page() ← PMD COW 처리 (huge_memory.c:2060)
→ is_huge_zero_pmd() ← zero PMD 검사
→ PageAnonExclusive() ← exclusive 재사용 가능 확인
→ folio_ref_count() ← 참조 카운트 검사
→ pmd_mkyoung() + maybe_pmd_mkwrite() ← 접근/쓰기 플래그
→ __split_huge_pmd() ← PMD 분할 (불가능 시)
사용자: echo always > /sys/kernel/mm/transparent_hugepage/hugepages-64kB/enabled
→ anon_enabled_store()
→ parsing: "always"
→ set_bit(order, &huge_anon_orders_always)
→ clear_bit(order, &huge_anon_orders_madvise)
→ clear_bit(order, &huge_anon_orders_inherit)
→ start_stop_khugepaged() ← khugepaged 시작/중지
large folio 분할 필요
→ deferred_split_folio()
→ list_add(&folio->deferred_list, &nm->deferred_split_queue)
→ count_mthp_stat(order, MTHP_STAT_SPLIT_DEFERRED)
shrinker 스캔 시
→ deferred_split_scan()
→ folio_split() / split_folio_to_order()
→ __folio_split()
→ __folio_freeze_and_split_unmapped()
→ __split_unmapped_folio()
→ __split_folio_to_order() ← 실제 분할
| 항목 | 기존 THP | mTHP |
|---|---|---|
| 지원 order | PMD_ORDER (9)만 | order 2~9 (16KB~2MB) |
| 기본 활성화 | global always/madvise | order별 개별 정책 |
| sysfs 인터페이스 | `/sys/kernel/mm/transparent_hugepage/enabled` | `/sys/kernel/mm/transparent_hugepage/hugepages-XkB/enabled` |
| khugepaged 병합 | PMD_ORDER 전용 | PMD_ORDER만 지원 (sub-PMD 미구현) |
| per-CPU 통계 | 없음 | order별 17종 통계 |
| 지연 분할 | PMD folio만 | order 2+ 모든 large folio |
| TLB 효율 | 2MB 하나로 매핑 | 크기별 최적 매핑 |
| order | 크기 | x86_64 PTE 수 | sysfs 이름 | 주요 사용 시나리오 |
|---|---|---|---|---|
| 2 | 16KB | 4 | hugepages-16kB | 소규모 large folio, 컨테이너 |
| 3 | 32KB | 8 | hugepages-32kB | 중간 크기 I/O 최적화 |
| 4 | 64KB | 16 | hugepages-64kB | 일반적인 mTHP 사용 |
| 5 | 128KB | 32 | hugepages-128kB | 대용량 데이터 처리 |
| 6 | 256KB | 64 | hugepages-256kB | 대규모 메모리 영역 |
| 7 | 512KB | 128 | hugepages-512kB | 매우 큰 메모리 영역 |
| 8 | 1MB | 256 | hugepages-1024kB | THP 근사 |
| 9 | 2MB | 512 | hugepages-2048kB | classic THP (PMD 크기) |
| 정책 | 동작 | 비트맵 | 사용 시나리오 |
|---|---|---|---|
| `always` | 모든 폴트에서 해당 order 사용 | `huge_anon_orders_always` | 성능 우선, 메모리 여유 시 |
| `madvise` | `MADV_HUGEPAGE` 적용 시에만 사용 | `huge_anon_orders_madvise` | 특정 프로세스만 최적화 |
| `inherit` | VMA 플래그 상속 | `huge_anon_orders_inherit` | 기본값 (PMD만 상속) |
| `never` | 해당 order 사용 안 함 | 비트맵에서 제외 | 메모리 절약, 충돌 회피 |
| 항목 | mTHP | HugeTLB | shmem THP |
|---|---|---|---|
| 할당 시점 | 폴트 시 동적 | 부팅 시 사전 예약 | 폴트 시 동적 |
| 메모리 소비 | 필요 시만 | 사전 예약 | 필요 시만 |
| 스왑 가능 | 가능 | 불가 | 가능 |
| 파일 매핑 | 불가 | 가능 | tmpfs 전용 |
| 컨테이너 지원 | 좋음 | 제한적 | 좋음 |
| TLB 효율 | 중간~높음 | 높음 | 중간~높음 |
# 모든 order를 always로 활성화
thp_anon=always:all
# 특정 order만 madvise로 활성화 (64KB, 128KB)
thp_anon=madvise:64k,128k
# PMD만 always, 나머지 never
thp_anon=always:2m
# 64KB mTHP를 always로 활성화
echo always > /sys/kernel/mm/transparent_hugepage/hugepages-64kB/enabled
# 128KB mTHP를 madvise로 설정
echo madvise > /sys/kernel/mm/transparent_hugepage/hugepages-128kB/enabled
# 모든 sub-PMD order를 never로 비활성화
for sz in 16kB 32kB 64kB 128kB 256kB 512kB 1024kB; do
echo never > /sys/kernel/mm/transparent_hugepage/hugepages-${sz}/enabled
done
/* 특정 VMA에 mTHP 적용 */
madvise(addr, length, MADV_HUGEPAGE); /* allow된 order 모두 사용 */
/* mTHP 비활성화 */
madvise(addr, length, MADV_NOHUGEPAGE);
| 통계 항목 | 설명 | 높은 값의 의미 |
|---|---|---|
| `anon_fault_alloc` | order별 할당 성공 수 | 해당 order가 활발하게 사용됨 |
| `anon_fault_fallback` | smaller order로 폴백 수 | 큰 order 할당 실패 빈번 |
| `anon_fault_fallback_charge` | 폴백 후 memcg 청구 실패 | 메모리 압박 |
| `split` | 분할 성공 수 | large folio가 쪼개지는 빈도 |
| `split_deferred` | 지연 분할 큐 등록 수 | partially-mapped folio 증가 |
| `nr_anon` | 현재 order별 large folio 수 | 메모리 사용 패턴 |
| `nr_anon_partially_mapped` | 부분 매핑 folio 수 | 분할 필요성 지표 |