Ryotta's Linux 7.0 MM

메모리 관리 서브시스템 완전 분석

mTHP (multi-size THP)

관련 소스: mm/huge_memory.c (4978줄), mm/khugepaged.c (2873줄), include/linux/huge_mm.h (807줄)
관련 문서: Huge Pages / THP · VMA / mmap · Folio / Page Cache · Compaction · 메모리 관리 개요

개요 (Overview)

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 관리 구조도

mTHP Order 관리 구조도

mTHP 페이지 폴트/할당 흐름

mTHP 페이지 폴트/할당 흐름

빠른 점검 명령

# 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=[^ ]*'

핵심 자료구조

1. `THP_ORDERS_ALL_ANON` (order 비트마스크)

익명 메모리에 허용되는 모든 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)

2. `huge_anon_orders_*` (order별 정책 비트맵)

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);

3. `enum mthp_stat_item` (mTHP 통계 항목)

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
};

4. `struct mthp_stat` (per-CPU 통계 구조체)

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);

5. `struct thpsize` (sysfs order별 kobject)

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)

6. `struct deferred_split` (지연 분할 큐)

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;
};

핵심 함수

1. `__thp_vma_allowable_orders()` — VMA별 허용 order 결정

지정된 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() 핸들러)

2. `setup_thp_anon()` — 부트 파라미터 order 정책 설정

커널 부트 파라미터 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 설정

3. `do_huge_pmd_anonymous_page()` — PMD 수준 익명 페이지 폴트 처리

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() 호출

4. `__do_huge_pmd_anonymous_page()` — 실제 large folio 할당 및 매핑

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()로 매핑

5. `do_huge_pmd_wp_page()` — PMD 수준 COW (Copy-on-Write) 처리

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 반환

6. `anon_enabled_show()` / `anon_enabled_store()` — sysfs order별 정책 제어

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() 호출
  • 4. `deferred_split_folio()` — large folio 지연 분할 큐 등록

    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 통계 업데이트

    5. `folio_check_splittable()` — folio 분할 가능 검사

    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 분할 시 추가 검사


    호출 흐름

    mTHP 페이지 폴트 경로

    사용자 페이지 폴트 (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 분할 (불가능 시)

    mTHP sysfs 제어 경로

    사용자: 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 시작/중지

    mTHP folio 분할 경로

    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()  ← 실제 분할

    조건별 비교

    mTHP vs 기존 THP 비교

    항목기존 THPmTHP
    지원 orderPMD_ORDER (9)만order 2~9 (16KB~2MB)
    기본 활성화global always/madviseorder별 개별 정책
    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별 크기 및 특성 비교

    order크기x86_64 PTE 수sysfs 이름주요 사용 시나리오
    216KB4hugepages-16kB소규모 large folio, 컨테이너
    332KB8hugepages-32kB중간 크기 I/O 최적화
    464KB16hugepages-64kB일반적인 mTHP 사용
    5128KB32hugepages-128kB대용량 데이터 처리
    6256KB64hugepages-256kB대규모 메모리 영역
    7512KB128hugepages-512kB매우 큰 메모리 영역
    81MB256hugepages-1024kBTHP 근사
    92MB512hugepages-2048kBclassic THP (PMD 크기)

    mTHP 정책 모드 비교

    정책동작비트맵사용 시나리오
    `always`모든 폴트에서 해당 order 사용`huge_anon_orders_always`성능 우선, 메모리 여유 시
    `madvise``MADV_HUGEPAGE` 적용 시에만 사용`huge_anon_orders_madvise`특정 프로세스만 최적화
    `inherit`VMA 플래그 상속`huge_anon_orders_inherit`기본값 (PMD만 상속)
    `never`해당 order 사용 안 함비트맵에서 제외메모리 절약, 충돌 회피

    mTHP vs HugeTLB vs shmem THP 비교

    항목mTHPHugeTLBshmem THP
    할당 시점폴트 시 동적부팅 시 사전 예약폴트 시 동적
    메모리 소비필요 시만사전 예약필요 시만
    스왑 가능가능불가가능
    파일 매핑불가가능tmpfs 전용
    컨테이너 지원좋음제한적좋음
    TLB 효율중간~높음높음중간~높음

    mTHP 활성화 방법

    1. 부트 파라미터

    # 모든 order를 always로 활성화
    thp_anon=always:all
    
    # 특정 order만 madvise로 활성화 (64KB, 128KB)
    thp_anon=madvise:64k,128k
    
    # PMD만 always, 나머지 never
    thp_anon=always:2m

    2. sysfs 동적 설정

    # 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

    3. 프로세스별 madvise

    /* 특정 VMA에 mTHP 적용 */
    madvise(addr, length, MADV_HUGEPAGE);  /* allow된 order 모두 사용 */
    
    /* mTHP 비활성화 */
    madvise(addr, length, MADV_NOHUGEPAGE);

    mTHP 통계 해석

    통계 항목설명높은 값의 의미
    `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 수분할 필요성 지표

    관련 문서

  • Huge Pages / THP — classic THP 및 khugepaged 상세
  • VMA / mmap — 가상 메모리 영역 관리
  • Folio / Page Cache — folio 추상화 및 페이지 캐시
  • Compaction — 메모리 단편화 해소
  • 메모리 관리 개요 — 전체 메모리 관리 조감도
  • Page Reclaim — 페이지 회수 및 kswapd
  • Memory Cgroup — 메모리 제어 그룹