๏ปฟ# HugeTLB (HugeTLBfs)

๊ด€๋ จ ์†Œ์Šค: mm/hugetlb.c, include/linux/hugetlb.h

๊ฐœ์š” (Overview)

HugeTLB๋Š” ๋ฆฌ๋ˆ…์Šค ์ปค๋„์—์„œ ์‚ฌ์ „ ํ• ๋‹น๋œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€(Huge Page)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋ธŒ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. THP(Transparent Huge Pages)์™€ ๋‹ฌ๋ฆฌ HugeTLB๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€(Huge Page Pool)์„ ๊ตฌ์„ฑํ•˜๊ณ , hugetlbfs ํŒŒ์ผ ์‹œ์Šคํ…œ์ด๋‚˜ MAP_HUGETLB ํ”Œ๋ž˜๊ทธ๋ฅผ ํ†ตํ•ด ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€๋ฅผ ์ง์ ‘ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. 2MB(๊ธฐ๋ณธ) ๋˜๋Š” 1GB ํฌ๊ธฐ์˜ ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ TLB ๋ฏธ์Šค๋ฅผ ์ค„์ด๊ณ  ๋Œ€์šฉ๋Ÿ‰ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์„ค๊ณ„ ์›์น™์€ ์‚ฌ์ „ ํ• ๋‹น(Pre-allocation)๊ณผ ์˜ˆ์•ฝ(Reservation)์ž…๋‹ˆ๋‹ค. ๋ถ€ํŒ… ์‹œ ๋˜๋Š” ๋Ÿฐํƒ€์ž„์— hugepages= ์ปค๋„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฏธ๋ฆฌ ํ™•๋ณดํ•œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€์—์„œ ํ• ๋‹น์ด ์ด๋ฃจ์–ด์ง€๋ฉฐ, hugetlb_reserve_pages()๊ฐ€ VMA ์ƒ์„ฑ ์‹œ ์˜ˆ์•ฝ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  alloc_hugetlb_folio()๊ฐ€ ์‹ค์ œ ํ• ๋‹น์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. subpool์€ hugetlbfs ํŒŒ์ผ ์‹œ์Šคํ…œ๋ณ„, memcg๋Š” cgroup๋ณ„๋กœ ํ’€์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

// ์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ
mm/hugetlb.c                    /* ํ•ต์‹ฌ ํ• ๋‹น/๊ด€๋ฆฌ ๋กœ์ง */
mm/hugetlb_vmemmap.c            /* vmemmap ์ตœ์ ํ™” */
mm/hugetlb_cma.c                /* CMA๋ฅผ ํ†ตํ•œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ• ๋‹น */
include/linux/hugetlb.h         /* ํ•ต์‹ฌ ๊ตฌ์กฐ์ฒด/ํ•จ์ˆ˜ ์„ ์–ธ */
include/linux/hugetlb_inline.h  /* ์ธ๋ผ์ธ ํ—ฌํผ */
fs/hugetlbfs/inode.c            /* hugetlbfs ํŒŒ์ผ ์‹œ์Šคํ…œ */
HugeTLB ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„
HugeTLB ํ˜ธ์ถœ ํ๋ฆ„

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

# HugeTLB ํ’€ ์ƒํƒœ ํ™•์ธ
cat /proc/meminfo | grep -i huge
# HugePages_Total: ํ• ๋‹น๋œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜
# HugePages_Free:  ํ’€์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ˆ˜
# HugePages_Rsvd:  ์˜ˆ์•ฝ๋˜์—ˆ์œผ๋‚˜ ์•„์ง ๋งคํ•‘ ์•ˆ ๋œ ์ˆ˜
# HugePages_Surplus: ์˜ค๋ฒ„์ปค๋ฐ‹์œผ๋กœ ์ดˆ๊ณผ ํ• ๋‹น๋œ ์ˆ˜
# Hugepagesize:    ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํฌ๊ธฐ (๋ณดํ†ต 2048 kB)

# ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€ ํฌ๊ธฐ ํ™•์ธ (์•„ํ‚คํ…์ฒ˜๋ณ„)
grep -i huge /proc/meminfo

# hugetlbfs ๋งˆ์šดํŠธ ํ™•์ธ
mount | grep hugetlb

# NUMA ๋…ธ๋“œ๋ณ„ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ๋ถ„ํฌ
cat /sys/devices/system/node/node*/hugepages/hugepages-2048kB/*

# ํ”„๋กœ์„ธ์Šค ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์‚ฌ์šฉ๋Ÿ‰
cat /proc/$$/status | grep -i hugetlb

# ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ• ๋‹น/ํ•ด์ œ ์ถ”์ 
cat /proc/vmstat | grep -i huge

# ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€ ๋ฆฌ์‚ฌ์ด์ฆˆ (๋Ÿฐํƒ€์ž„)
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

# hugetlbfs ๋””๋ ‰ํ† ๋ฆฌ ๋งˆ์šดํŠธ
mkdir -p /mnt/huge
mount -t hugetlbfs -o pagesize=2M none /mnt/huge

# memcg ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ œํ•œ ํ™•์ธ
cat /sys/fs/cgroup/memory hugetlb.max 2>/dev/null

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

struct hstate โ€” ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ƒํƒœ ๊ด€๋ฆฌ

ํ•˜๋‚˜์˜ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํฌ๊ธฐ(์˜ˆ: 2MB, 1GB)๋ฅผ ๋Œ€ํ‘œํ•˜๋Š” ์ „์—ญ ์ƒํƒœ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. hstates[] ๋ฐฐ์—ด์— HUGE_MAX_HSTATE๊ฐœ๊นŒ์ง€ ์กด์žฌํ•˜๋ฉฐ, for_each_hstate() ๋งคํฌ๋กœ๋กœ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/hugetlb.h:666-687 */
struct hstate {
struct mutex resize_lock;             /* ๋ฆฌ์‚ฌ์ด์ฆˆ ์ง๋ ฌํ™” */
struct lock_class_key resize_key;
int next_nid_to_alloc;                /* ๋‹ค์Œ ํ• ๋‹น ๋Œ€์ƒ ๋…ธ๋“œ */
int next_nid_to_free;                 /* ๋‹ค์Œ ํ•ด์ œ ๋Œ€์ƒ ๋…ธ๋“œ */
unsigned int order;                   /* ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ order (1=2MB, 3=1GB) */
unsigned int demote_order;            /* ๋””๋ชจํŠธ ์‹œ target order */
unsigned long mask;                   /* ํŽ˜์ด์ง€ ์ •๋ ฌ ๋งˆ์Šคํฌ ~(size-1) */
unsigned long max_huge_pages;         /* ๋ชฉํ‘œ ํ’€ ํฌ๊ธฐ (sysctl ์„ค์ •) */
unsigned long nr_huge_pages;          /* ํ˜„์žฌ ํ’€์— ์žˆ๋Š” ์ „์ฒด ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned long free_huge_pages;        /* ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned long resv_huge_pages;        /* ์˜ˆ์•ฝ๋œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned long surplus_huge_pages;     /* ์˜ค๋ฒ„์ปค๋ฐ‹ ์ดˆ๊ณผ ์ˆ˜ */
unsigned long nr_overcommit_huge_pages; /* ์˜ค๋ฒ„์ปค๋ฐ‹ ํ•œ๋„ */
struct list_head hugepage_activelist; /* ํ™œ์„ฑ ๋ฆฌ์ŠคํŠธ (ํ• ๋‹น๋œ ํŽ˜์ด์ง€) */
struct list_head hugepage_freelists[MAX_NUMNODES]; /* ๋…ธ๋“œ๋ณ„ ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ */
unsigned int max_huge_pages_node[MAX_NUMNODES];    /* ๋…ธ๋“œ๋ณ„ ๋ชฉํ‘œ ์ˆ˜ */
unsigned int nr_huge_pages_node[MAX_NUMNODES];     /* ๋…ธ๋“œ๋ณ„ ํ˜„์žฌ ์ˆ˜ */
unsigned int free_huge_pages_node[MAX_NUMNODES];   /* ๋…ธ๋“œ๋ณ„ ์—ฌ์œ  ์ˆ˜ */
unsigned int surplus_huge_pages_node[MAX_NUMNODES];/* ๋…ธ๋“œ๋ณ„ ์ดˆ๊ณผ ์ˆ˜ */
char name[HSTATE_NAME_LEN];           /* "hugepages-2048kB" ๋“ฑ */
};
/*ๅ‡บๅค„: mm/hugetlb.c:57 โ€” ์ „์—ญ ๋ฐฐ์—ด */
struct hstate hstates[HUGE_MAX_HSTATE];

struct hugepage_subpool โ€” ์„œ๋ธŒํ’€ ๊ด€๋ฆฌ

hugetlbfs ํŒŒ์ผ ์‹œ์Šคํ…œ์ด๋‚˜ memcg์— ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. max_hpages๋กœ ์ƒํ•œ, min_hpages๋กœ ํ•˜ํ•œ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/hugetlb.h:37-47 */
struct hugepage_subpool {
spinlock_t lock;          /* ์ž ๊ธˆ */
long count;               /* ์ฐธ์กฐ ์นด์šดํŠธ */
long max_hpages;          /* ์ตœ๋Œ€ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜ ๋˜๋Š” -1 (๋ฌด์ œํ•œ) */
long used_hpages;         /* ์‚ฌ์šฉ ์ค‘์ธ ์ˆ˜ (ํ• ๋‹น+์˜ˆ์•ฝ ํฌํ•จ) */
struct hstate *hstate;    /* ์—ฐ๊ฒฐ๋œ hstate */
long min_hpages;          /* ์ตœ์†Œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜ ๋˜๋Š” -1 */
long rsv_hpages;          /* ๊ธ€๋กœ๋ฒŒ ํ’€์—์„œ ์˜ˆ์•ฝํ•œ ์ˆ˜ */
};

struct resv_map / file_region โ€” ์˜ˆ์•ฝ ์˜์—ญ ์ถ”์ 

๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์˜ˆ์•ฝ(reservation)์„ ๊ตฌ๊ฐ„(region) ๋‹จ์œ„๋กœ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. file_region์€ [from, to) ๊ตฌ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋Š” interval ๋…ธ๋“œ์ด๊ณ , resv_map์€ ์ด๋“ค์˜ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์™€ ์ž ๊ธˆ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/hugetlb.h:49-67 */
struct resv_map {
struct kref refs;                 /* ์ฐธ์กฐ ์นด์šดํŠธ */
spinlock_t lock;                  /* ์ž ๊ธˆ */
struct list_head regions;         /* file_region ๋ฆฌ์ŠคํŠธ */
long adds_in_progress;           /* ์ง„ํ–‰ ์ค‘์ธ ์ถ”๊ฐ€ ์ˆ˜ */
struct list_head region_cache;   /* ์žฌ์‚ฌ์šฉ ์บ์‹œ */
long region_cache_count;
struct rw_semaphore rw_sema;     /* ์ฝ๊ธฐ/์“ฐ๊ธฐ ์„ธ๋งˆํฌ์–ด */
/* CONFIG_CGROUP_HUGETLB */
struct page_counter *reservation_counter; /* memcg ์˜ˆ์•ฝ ์นด์šดํ„ฐ */
unsigned long pages_per_hpage;
struct cgroup_subsys_state *css;
};

/* include/linux/hugetlb.h:88-101 */
struct file_region {
struct list_head link;   /* ๋ฆฌ์ŠคํŠธ ์—ฐ๊ฒฐ */
long from;               /* ์‹œ์ž‘ ์ธ๋ฑ์Šค (ํฌํ•จ) */
long to;                 /* ์ข…๋ฃŒ ์ธ๋ฑ์Šค (๋ถˆํฌํ•จ) โ€” [from, to) */
/* CONFIG_CGROUP_HUGETLB */
struct page_counter *reservation_counter;
struct cgroup_subsys_state *css;
};

struct hugetlb_vma_lock โ€” VMA๋ณ„ ์ž ๊ธˆ

๊ณต์œ  ๋งคํ•‘(VM_MAYSHARE)์—์„œ PMD ์…ฐ์–ด๋ง๊ณผ ํดํŠธ/ํŠธ๋Ÿฐ์ผ€์ด์…˜ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด VMA๋ณ„ rwsem์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/hugetlb.h:103-107 */
struct hugetlb_vma_lock {
struct kref refs;               /* ์ฐธ์กฐ ์นด์šดํŠธ */
struct rw_semaphore rw_sema;    /* ์ฝ๊ธฐ/์“ฐ๊ธฐ ์„ธ๋งˆํฌ์–ด */
struct vm_area_struct *vma;     /* ์—ฐ๊ฒฐ๋œ VMA */
};

HUGETLB ํŽ˜์ด์ง€ ํ”Œ๋ž˜๊ทธ (HPG_*)

๊ฑฐ๋Œ€ ํŽ˜์ด์ง€์˜ ํŠน์ˆ˜ ์ƒํƒœ๋ฅผ folio->private ๋น„ํŠธ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/hugetlb.h:593-602 */
enum hugetlb_page_flags {
HPG_restore_reserve = 0,  /* ํ• ๋‹น ์‹œ ์˜ˆ์•ฝ ์†Œ๋น„ โ†’ ์˜ค๋ฅ˜ ์‹œ ๋ณต์› ํ•„์š” */
HPG_migratable,           /* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ›„๋ณด */
HPG_temporary,            /* Buddy์—์„œ ์ž„์‹œ ํ• ๋‹น (์ด์ „์šฉ ํƒ€๊ฒŸ) */
HPG_freed,                /* ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ์— ์žˆ์Œ */
HPG_vmemmap_optimized,    /* vmemmap ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ๋จ */
HPG_raw_hwp_unreliable,   /* hwpoison ์„œ๋ธŒํŽ˜์ด์ง€ ๋ฏธ์ถ”์  */
HPG_cma,                  /* CMA ์˜์—ญ์—์„œ ํ• ๋‹น๋จ */
__NR_HPAGEFLAGS,
};

struct hugetlbfs_sb_info โ€” ํŒŒ์ผ ์‹œ์Šคํ…œ ์ •๋ณด

hugetlbfs์˜ superblock์— ์—ฐ๊ฒฐ๋œ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํŒŒ์ผ ์‹œ์Šคํ…œ ์ •๋ณด์ž…๋‹ˆ๋‹ค.

/* include/linux/hugetlb.h:503-512 */
struct hugetlbfs_sb_info {
long max_inodes;              /* ํ—ˆ์šฉ inode ์ˆ˜ */
long free_inodes;             /* ์—ฌ์œ  inode ์ˆ˜ */
spinlock_t stat_lock;
struct hstate *hstate;        /* ์—ฐ๊ฒฐ๋œ hstate */
struct hugepage_subpool *spool; /* ์„œ๋ธŒํ’€ */
kuid_t uid;                   /* ์†Œ์œ ์ž UID */
kgid_t gid;                   /* ์†Œ์œ ์ž GID */
umode_t mode;                 /* ๊ถŒํ•œ */
};

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

hugetlb_fault() โ€” ํดํŠธ ์ง„์ž…์ 

HugeTLB ํŽ˜์ด์ง€ ํดํŠธ์˜ ๋ฉ”์ธ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์— ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ PTE๊ฐ€ ์—†์œผ๋ฉด hugetlb_no_page()๋กœ, COW๊ฐ€ ํ•„์š”ํ•˜๋ฉด hugetlb_wp()๋กœ ๋ถ„๊ธฐํ•ฉ๋‹ˆ๋‹ค.

/* mm/hugetlb.c:5972-6161 */
vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
// 1. fault mutex๋กœ ๋™์ผ ํŽ˜์ด์ง€ ํ• ๋‹น ์ง๋ ฌํ™”
hash = hugetlb_fault_mutex_hash(mapping, vmf.pgoff);
mutex_lock(&hugetlb_fault_mutex_table[hash]);

// 2. VMA lock ํš๋“ ํ›„ PTE ํ• ๋‹น
hugetlb_vma_lock_read(vma);
vmf.pte = huge_pte_alloc(mm, vma, vmf.address, huge_page_size(h));

// 3. PTE ์ƒํƒœ์— ๋”ฐ๋ฅธ ๋ถ„๊ธฐ
vmf.orig_pte = huge_ptep_get(mm, vmf.address, vmf.pte);

if (huge_pte_none(vmf.orig_pte))
return hugetlb_no_page(mapping, &vmf);  /* ์ƒˆ ํŽ˜์ด์ง€ ํ• ๋‹น */

if (softleaf_is_migration(softleaf))
migration_entry_wait_huge(...);          /* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋Œ€๊ธฐ */

if (softleaf_is_hwpoison(softleaf))
return VM_FAULT_HWPOISON_LARGE;         /* ํ•˜๋“œ์›จ์–ด ์˜ค๋ฅ˜ */

// 4. ๊ธฐ์กด ํŽ˜์ด์ง€์— ๋Œ€ํ•œ COW ์ฒ˜๋ฆฌ
if ((flags & (FAULT_FLAG_WRITE|FAULT_FLAG_UNSHARE)) && ...)
ret = hugetlb_wp(&vmf);                 /* Copy-on-Write */

// 5. accessed bit ๊ฐฑ์‹ 
vmf.orig_pte = pte_mkyoung(vmf.orig_pte);
huge_ptep_set_access_flags(...);
}

hugetlb_no_page() โ€” ์ƒˆ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ• ๋‹น ๋ฐ ๋งคํ•‘

PTE๊ฐ€ ๋น„์–ด์žˆ์„ ๋•Œ ์ƒˆ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜๊ณ  ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์— ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ๊ธฐ๋ฐ˜ ๋งคํ•‘์€ page cache์—, ์ต๋ช… ๋งคํ•‘์€ anon_rmap์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

/* mm/hugetlb.c:5722-5946 */
static vm_fault_t hugetlb_no_page(struct address_space *mapping,
struct vm_fault *vmf)
{
// 1. ํŽ˜์ด์ง€ ์บ์‹œ์—์„œ ๊ธฐ์กด folio ๊ฒ€์ƒ‰
folio = filemap_lock_hugetlb_folio(h, mapping, vmf->pgoff);

if (IS_ERR(folio)) {
// 2. ์—†์œผ๋ฉด ์ƒˆ folio ํ• ๋‹น
folio = alloc_hugetlb_folio(vma, vmf->address, false);
folio_zero_user(folio, vmf->real_address);
__folio_mark_uptodate(folio);

// 3. ๊ณต์œ  ๋งคํ•‘: page cache์— ์ถ”๊ฐ€
if (vma->vm_flags & VM_MAYSHARE)
hugetlb_add_to_page_cache(folio, mapping, vmf->pgoff);
else
new_anon_folio = true;  /* ์ต๋ช…: rmap์— ์ถ”๊ฐ€ */
}

// 4. PTE์— ์ƒˆ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ๋งคํ•‘
new_pte = make_huge_pte(vma, folio, vma->vm_flags & VM_SHARED);
set_huge_pte_at(mm, vmf->address, vmf->pte, new_pte, huge_page_size(h));

// 5. COW๊ฐ€ ํ•„์š”ํ•œ ์‚ฌ์œ  ๋งคํ•‘์ด๋ฉด ์ฆ‰์‹œ ์ฒ˜๋ฆฌ
if ((vmf->flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED))
ret = hugetlb_wp(vmf);
}

alloc_hugetlb_folio() โ€” ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ• ๋‹น ํ•ต์‹ฌ

VMA์˜ ์„œ๋ธŒํ’€, memcg, ๊ธ€๋กœ๋ฒŒ ํ’€์—์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ํ• ๋‹น ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ  folio๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

/* mm/hugetlb.c:2908-3078 */
struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,
unsigned long addr, bool cow_from_owner)
{
// 1. VMA ์˜ˆ์•ฝ ํ™•์ธ (map_chg: REUSE/NEEDED/ENFORCED)
retval = vma_needs_reservation(h, vma, addr);

// 2. ์„œ๋ธŒํ’€ ๊ธ€๋กœ๋ฒŒ ํ• ๋‹น ํ™•์ธ
if (map_chg)
gbl_chg = hugepage_subpool_get_pages(spool, 1);

// 3. memcg ์˜ˆ์•ฝ ์ถฉ์ „
if (map_chg)
hugetlb_cgroup_charge_cgroup_rsvd(idx, ...);

// 4. memcg ์ผ๋ฐ˜ ์ถฉ์ „
hugetlb_cgroup_charge_cgroup(idx, ...);

// 5. ๊ธ€๋กœ๋ฒŒ ํ’€์—์„œ dequeue ๋˜๋Š” buddy ํ• ๋‹น
spin_lock_irq(&hugetlb_lock);
folio = dequeue_hugetlb_folio_vma(h, vma, addr, gbl_chg);
if (!folio)
folio = alloc_buddy_hugetlb_folio_with_mpol(h, vma, addr);

// 6. ์˜ˆ์•ฝ ์†Œ๋น„ ๋งˆํฌ
if (!gbl_chg)
folio_set_hugetlb_restore_reserve(folio);

// 7. memcg ์ปค๋ฐ‹
hugetlb_cgroup_commit_charge(idx, ...);

// 8. VMA ์˜ˆ์•ฝ ์ปค๋ฐ‹
if (map_chg != MAP_CHG_ENFORCED)
vma_commit_reservation(h, vma, addr);
}

hugetlb_reserve_pages() โ€” ์˜ˆ์•ฝ ์ฒ˜๋ฆฌ

mmap() ์‹œ ํ˜ธ์ถœ๋˜์–ด ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ์˜ˆ์•ฝ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ณต์œ  ๋งคํ•‘์€ resv_map์˜ region์œผ๋กœ, ์‚ฌ์œ  ๋งคํ•‘์€ ๋ณ„๋„ resv_map์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

/* mm/hugetlb.c:6570-6749 */
long hugetlb_reserve_pages(struct inode *inode,
long from, long to,
struct vm_area_desc *desc, vma_flags_t vma_flags)
{
// 1. VM_NORESERVE์ด๋ฉด ์˜ˆ์•ฝ ์—†์Œ
if (vma_flags_test(&vma_flags, VMA_NORESERVE_BIT))
return 0;

// 2. ๊ณต์œ  ๋งคํ•‘: inode์˜ resv_map์— region_chg
if (!desc || vma_desc_test_flags(desc, VMA_MAYSHARE_BIT)) {
resv_map = inode_resv_map(inode);
chg = region_chg(resv_map, from, to, &regions_needed);
} else {
// 3. ์‚ฌ์œ  ๋งคํ•‘: ์ƒˆ resv_map ํ• ๋‹น
resv_map = resv_map_alloc();
chg = to - from;
set_vma_desc_resv_map(desc, resv_map);
}

// 4. memcg ์˜ˆ์•ฝ ์ถฉ์ „
hugetlb_cgroup_charge_cgroup_rsvd(...);

// 5. ์„œ๋ธŒํ’€ ํ• ๋‹น ํ™•์ธ
gbl_reserve = hugepage_subpool_get_pages(spool, chg);

// 6. ๊ธ€๋กœ๋ฒŒ ํ’€ ์—ฌ์œ  ํ™•์ธ
hugetlb_acct_memory(h, gbl_reserve);

// 7. ๊ณต์œ : region_add๋กœ ์˜ˆ์•ฝ ์˜์—ญ ํ™•์ •
if (๊ณต์œ  ๋งคํ•‘)
add = region_add(resv_map, from, to, regions_needed, h, h_cg);
}

dequeue_hugetlb_folio_vma() โ€” VMA ๊ธฐ๋ฐ˜ ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ dequeue

VMA์˜ NUMA ์ •์ฑ…์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๋…ธ๋“œ์˜ ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ์—์„œ folio๋ฅผ ๊บผ๋‚ด์˜ต๋‹ˆ๋‹ค.

/* mm/hugetlb.c:1373-1412 */
static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,
struct vm_area_struct *vma, unsigned long addr, bool gbl_chg)
{
// 1. NUMA ์ •์ฑ…์— ๋”ฐ๋ฅธ ๋…ธ๋“œ ๊ฒฐ์ •
int nid = huge_node(vma, addr, htlb_alloc_mask(h), &mpol, &nodemask);

// 2. ์ง€์ • ๋…ธ๋“œ์—์„œ dequeue ์‹œ๋„
folio = dequeue_hugetlb_folio_node_exact(h, nid, gfp_mask, gbl_chg);

// 3. ์‹คํŒจ ์‹œ nodemask ๋‚ด ๋‹ค๋ฅธ ๋…ธ๋“œ๋กœ fallback
if (!folio)
folio = dequeue_hugetlb_folio_nodemask(h, gfp_mask, nodemask, gbl_chg);

return folio;
}

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

mmap(MAP_HUGETLB) โ”€โ”€โ†’ hugetlb_file_setup() โ”€โ”€โ†’ hugetlb_reserve_pages()
โ”‚                                              โ”‚
โ”‚                                    region_chg() / resv_map_alloc()
โ”‚                                    hugepage_subpool_get_pages()
โ”‚                                    hugetlb_acct_memory()
โ”‚
โ–ผ
์ฒซ ์ ‘๊ทผ ์‹œ Page Fault
โ”‚
โ–ผ
hugetlb_fault()
โ”‚
โ”œโ”€โ”€ huge_pte_none() โ”€โ”€โ†’ hugetlb_no_page()
โ”‚       โ”‚                    โ”‚
โ”‚       โ”‚                    โ”œโ”€โ”€ filemap_lock_hugetlb_folio() [์บ์‹œ ๊ฒ€์ƒ‰]
โ”‚       โ”‚                    โ”œโ”€โ”€ alloc_hugetlb_folio() [ํ• ๋‹น]
โ”‚       โ”‚                    โ”‚       โ”‚
โ”‚       โ”‚                    โ”‚       โ”œโ”€โ”€ vma_needs_reservation()
โ”‚       โ”‚                    โ”‚       โ”œโ”€โ”€ hugepage_subpool_get_pages()
โ”‚       โ”‚                    โ”‚       โ”œโ”€โ”€ hugetlb_cgroup_charge_cgroup()
โ”‚       โ”‚                    โ”‚       โ”œโ”€โ”€ dequeue_hugetlb_folio_vma()
โ”‚       โ”‚                    โ”‚       โ”‚       โ””โ”€โ”€ dequeue_hugetlb_folio_node_exact()
โ”‚       โ”‚                    โ”‚       โ””โ”€โ”€ alloc_buddy_hugetlb_folio_with_mpol()
โ”‚       โ”‚                    โ”‚
โ”‚       โ”‚                    โ”œโ”€โ”€ hugetlb_add_to_page_cache() [๊ณต์œ ]
โ”‚       โ”‚                    โ””โ”€โ”€ hugetlb_add_new_anon_rmap() [์ต๋ช…]
โ”‚       โ”‚
โ”‚       โ””โ”€โ”€ set_huge_pte_at() โ”€โ”€โ†’ COW ํ•„์š” ์‹œ hugetlb_wp()
โ”‚
โ”œโ”€โ”€ migration_entry_wait_huge() [๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋Œ€๊ธฐ]
โ”‚
โ””โ”€โ”€ hugetlb_wp() [COW ์ฒ˜๋ฆฌ]
โ”‚
โ”œโ”€โ”€ folio_ref_count==1 โ†’ wp_page_reuse() [๋‹จ๋… ์‚ฌ์šฉ โ†’ ์žฌ์‚ฌ์šฉ]
โ””โ”€โ”€ folio_ref_count>1  โ†’ wp_page_copy() [๊ณต์œ  โ†’ ๋ณต์‚ฌ]

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

๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ• ๋‹น ๊ฒฝ๋กœ ๋น„๊ต

์กฐ๊ฑดํ• ๋‹น ๊ฒฝ๋กœ์„ค๋ช…
VMA ์˜ˆ์•ฝ ์žˆ์Œ (REUSE)`dequeue_hugetlb_folio_vma()`ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ์—์„œ ์ฆ‰์‹œ ํ• ๋‹น
VMA ์˜ˆ์•ฝ ์—†์Œ (NEEDED)`subpool โ†’ buddy`์„œ๋ธŒํ’€ ํ™•์ธ ํ›„ buddy ํ• ๋‹น
COW from_owner`MAP_CHG_ENFORCED`์˜ˆ์•ฝ ์ฒดํฌ ๊ฑด๋„ˆ๋›ฐ๊ณ  buddy ํ• ๋‹น
Buddy ํ• ๋‹น ์‹คํŒจ`alloc_surplus_hugetlb_folio()`์˜ค๋ฒ„์ปค๋ฐ‹์œผ๋กœ ์ดˆ๊ณผ ํ• ๋‹น
NUMA ์ •์ฑ… ์ ์šฉ`huge_node()` โ†’ ๋…ธ๋“œ ์ง€์ •mpol์— ๋”ฐ๋ผ ํŠน์ • ๋…ธ๋“œ ์šฐ์„ 

๋งคํ•‘ ์œ ํ˜•๋ณ„ ์ฒ˜๋ฆฌ ๋น„๊ต

๋งคํ•‘ ์œ ํ˜•์˜ˆ์•ฝ ๊ด€๋ฆฌํŽ˜์ด์ง€ ์บ์‹œrmapCOW
๊ณต์œ  (VM_MAYSHARE)inode resv_map์˜ region`__filemap_add_folio()``hugetlb_add_file_rmap()`์—†์Œ (๊ณต์œ )
์‚ฌ์œ  (private)VMA๋ณ„ ๋ณ„๋„ resv_map์—†์Œ`hugetlb_add_new_anon_rmap()``hugetlb_wp()`
MAP_NORESERVE์˜ˆ์•ฝ ์—†์Œ--ํ• ๋‹น ์‹œ ์˜ˆ์•ฝ ๋ถˆํ•„์š”
memcg ์ ์šฉreservation_counter--memcg ์ถฉ์ „

๊ฑฐ๋Œ€ ํŽ˜์ด์ง€ ํ’€ ์†Œ๋น„์ž ๋น„๊ต

์†Œ๋น„์ž์—ญํ• ์ž ๊ธˆ
`hstate.max_huge_pages`๋ชฉํ‘œ ํ’€ ํฌ๊ธฐ`hugetlb_lock`
`hstate.nr_huge_pages`ํ˜„์žฌ ํ• ๋‹น ์ˆ˜`hugetlb_lock`
`hstate.free_huge_pages`์‚ฌ์šฉ ๊ฐ€๋Šฅ ์ˆ˜`hugetlb_lock`
`hstate.resv_huge_pages`์˜ˆ์•ฝ ๋Œ€๊ธฐ ์ˆ˜`hugetlb_lock`
`hstate.surplus_huge_pages`์˜ค๋ฒ„์ปค๋ฐ‹ ์ดˆ๊ณผ`hugetlb_lock`
`hugepage_subpool.used_hpages`์„œ๋ธŒํ’€ ์‚ฌ์šฉ ์ˆ˜`spool->lock`
memcg `reservation_counter`memcg ์˜ˆ์•ฝ ์ˆ˜page_counter lock

HPG ํ”Œ๋ž˜๊ทธ๋ณ„ ๋™์ž‘ ๋น„๊ต

ํ”Œ๋ž˜๊ทธ์„ค์ • ์‹œ์ ํ•ด์ œ ์‹œ์ ์˜๋ฏธ
`HPG_restore_reserve`์˜ˆ์•ฝ ์†Œ๋น„ ํ• ๋‹น ์‹œํŽ˜์ด์ง€ ์™„์ „ ์ธ์Šคํ„ด์Šคํ™” ์‹œ์˜ค๋ฅ˜ ์‹œ ์˜ˆ์•ฝ ๋ณต์› ํ•„์š”
`HPG_migratable`page cache/tab์— ์ถ”๊ฐ€ ํ›„๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒฉ๋ฆฌ ์‹œ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ›„๋ณด
`HPG_temporary`buddy์—์„œ ์ž„์‹œ ํ• ๋‹น ์‹œํ”„๋ฆฌ ์‹œ ์ฆ‰์‹œ buddy ๋ฐ˜ํ™˜ํ’€ ํŽ˜์ด์ง€ ์•„๋‹˜
`HPG_freed`ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ enqueue ์‹œdequeue ์‹œํ”„๋ฆฌ ์ƒํƒœ ํ‘œ์‹œ
`HPG_vmemmap_optimized`vmemmap ํ•ด์ œ ์‹œ-vmemmap ์ตœ์ ํ™” ์ ์šฉ

minzkn.com ๋น„๊ต ์ฐธ๊ณ 

minzkn.com์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” ํŽ˜์ด์ง€๋Š” HugeTLB/THP ์„น์…˜์—์„œ ๋‹ค์Œ ๊ฐœ๋…์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค:

  • ์ผ์ƒ ๋น„์œ : ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€๋Š” ๋„์„œ๊ด€์˜ "๋Œ€ํ˜• ์‚ฌ์ „ ์ „์šฉ ์ฝ”๋„ˆ"์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ž์ฃผ ์ฐพ๋Š” ๋Œ€ํ˜• ์‚ฌ์ „์„ ์ผ๋ฐ˜ ์ฑ…์žฅ์ด ์•„๋‹Œ ์ „์šฉ ์ฝ”๋„ˆ์— ๋‘๋ฉด ์ฐพ๊ธฐ๊ฐ€ ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค.
  • THP vs HugeTLB ์ฐจ์ด: THP๋Š” ์ปค๋„์ด ์ž๋™์œผ๋กœ ๊ฑฐ๋Œ€ ํŽ˜์ด์ง€๋ฅผ ์„ ํƒํ•˜๊ณ  ๋ณ‘ํ•ฉํ•˜๋Š” ๋ฐ˜๋ฉด, HugeTLB๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ํ’€์„ ๊ตฌ์„ฑํ•˜๊ณ  ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • ์ ๊ฒ€ ๋ช…๋ น: cat /proc/meminfo | grep Huge, hugepages= ์ปค๋„ ํŒŒ๋ผ๋ฏธํ„ฐ, hugetlbfs ๋งˆ์šดํŠธ ๋ฐฉ๋ฒ• ๋“ฑ์€ minzkn๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ด€๋ จ ํ‘œ: minzkn์˜ Huge Pages ์„ค๋ช…์€ hugepagesize, HugePages_Total/Free/Rsvd/Surplus ํ•„๋“œ ์„ค๋ช…์„ ํฌํ•จํ•˜๋ฉฐ, ์šฐ๋ฆฌ ๋ฌธ์„œ์˜ ๋น ๋ฅธ ์ ๊ฒ€ ๋ช…๋ น ์„น์…˜์— ์ด๋ฏธ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • VMA / mmap
  • Huge Pages / THP
  • CMA
  • Migration
  • Memory Cgroup
  • Folio / Page Cache