๏ปฟ# Page Table Walk ๐Ÿšถ

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

๊ฐœ์š” (Overview)

Linux ์ปค๋„์˜ Page Table Walk๋Š” ๊ฐ€์ƒ ์ฃผ์†Œ ๊ณต๊ฐ„์„ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์—”ํŠธ๋ฆฌ(PGD, P4D, PUD, PMD, PTE)์— ๋Œ€ํ•ด ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. walk_page_range()๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ์ž๋Š” ์ง€์ •๋œ ๊ฐ€์ƒ ์ฃผ์†Œ ๋ฒ”์œ„ ๋‚ด ๋ชจ๋“  ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋ ˆ๋ฒจ์„ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ์—”ํŠธ๋ฆฌ๋งˆ๋‹ค pte_entry, pmd_entry, pud_entry ๊ฐ™์€ ์ฝœ๋ฐฑ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ”„๋ ˆ์ž„์›Œํฌ๋Š” mlock, madvise, mincore, mprotect, KSM, DAMON, memory-failure ๋“ฑ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์„œ๋ธŒ์‹œ์Šคํ…œ ์ „๋ฐ˜์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋Š” ์•„ํ‚คํ…์ฒ˜์— ๋”ฐ๋ผ 4๋‹จ๊ณ„(PGDโ†’PUDโ†’PMDโ†’PTE) ๋˜๋Š” 5๋‹จ๊ณ„(PGDโ†’P4Dโ†’PUDโ†’PMDโ†’PTE)๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, p4d/pud ์ˆ˜์ค€์€ ์•„ํ‚คํ…์ฒ˜์— ๋”ฐ๋ผ fold๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. pagewalk ํ”„๋ ˆ์ž„์›Œํฌ๋Š” real_depth() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด fold๋œ ๋ ˆ๋ฒจ์„ ๋ณด์ •ํ•˜์—ฌ ์ •ํ™•ํ•œ ๊นŠ์ด ์ •๋ณด๋ฅผ ์ฝœ๋ฐฑ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋„์„œ๊ด€์œผ๋กœ ๋น„์œ ํ•˜๋ฉด, ์ด ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์„œ๊ฐ€ ๋ฒˆํ˜ธ๋ฅผ ํ•˜๋‚˜์”ฉ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์ฑ…์ด ์žˆ๋Š” ์นธ๋งŒ ํ™•์ธํ•˜๋Š” ์‚ฌ์„œ์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. Linux 7.0์—์„œ๋Š” folio_walk_start() API๊ฐ€ ์ถ”๊ฐ€๋˜์–ด, ๋‹จ์ผ ๊ฐ€์ƒ ์ฃผ์†Œ์—์„œ ํ•ด๋‹นํ•˜๋Š” folio๋ฅผ ์ง์ ‘ ์ฐพ์•„ PTL(PTE Lock)์„ ํš๋“ํ•˜๋Š” ๊ฐ„๊ฒฐํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ํดํŠธ ์ฒ˜๋ฆฌ๊ธฐ์™€ ๊ฐ™์€ ๊ฒฝ๋กœ๋ฅผ ์ง์ ‘ ๋Œ€์ฒดํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ๋งคํ•‘์„ ๋‹ค๋ฅธ ์„œ๋ธŒ์‹œ์Šคํ…œ์ด ์•ˆ์ „ํ•˜๊ฒŒ ํ›‘๊ฑฐ๋‚˜ ์กฐ์ •ํ•  ๋•Œ ์ฃผ๋กœ ์“ฐ์ž…๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ

mm/pagewalk.c              โ† ๋ฉ”์ธ ๊ตฌํ˜„ (1050์ค„)
include/linux/pagewalk.h   โ† API ์ •์˜ (207์ค„)
mm/internal.h              โ† _unsafe() ํ•จ์ˆ˜ ์„ ์–ธ
include/linux/leafops.h    โ† softleaf ํƒ€์ž…/ํ”Œ๋ž˜๊ทธ

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

# 1. pagewalk ๊ด€๋ จ ์ปค๋„ ์‹ฌ๋ณผ ํ™•์ธ
cat /proc/kallsyms | grep -E "walk_page|folio_walk"

# 2. ์ปค๋„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ (x86_64 4-level)
ls /sys/kernel/mm/page_tables/

# 3. pagewalk์ด ์‚ฌ์šฉ๋œ ๋ชจ๋“ˆ/API ํ˜ธ์ถœ๋ถ€ grep
grep -rn "walk_page_range\|walk_page_vma\|folio_walk_start" mm/

# 4. ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์˜ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋ ˆ๋ฒจ ํ™•์ธ (x86_64)
cat /proc/cpuinfo | grep "page table" || echo "4-level"  # ๊ธฐ๋ณธ 4-level

# 5. pte_offset_map_lock ๋“ฑ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์•ก์„ธ์Šค ํ•จ์ˆ˜ ์‚ฌ์šฉ ํ™•์ธ
grep -rn "pte_offset_map\|pmd_offset\|pud_offset\|pgd_offset" mm/

# 6. CONFIG_PGTABLE_HAS_HUGE_LEAVES ํ™œ์„ฑํ™” ์—ฌ๋ถ€
grep "PGTABLE_HAS_HUGE_LEAVES" /boot/config-$(uname -r) 2>/dev/null

# 7. pagewalk ๊ด€๋ จ ํ˜ธ์ถœ๋ถ€ ์ „์ฒด ํ™•์ธ
grep -rn "walk_page_range\|walk_page_range_vma\|walk_page_mapping\|folio_walk_start" mm/

# 8. ์ปค๋„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋คํ”„ ํ™•์ธ
cat /sys/kernel/debug/kernel_page_tables 2>/dev/null | head -40

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

struct mm_walk_ops

์ฝœ๋ฐฑ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ํ…Œ์ด๋ธ” โ€” ํ˜ธ์ถœ์ž๊ฐ€ ์ˆœํšŒ ๋™์ž‘์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•ฉ๋‹ˆ๋‹ค.

// include/linux/pagewalk.h:70-94
struct mm_walk_ops {
int (*pgd_entry)(pgd_t *pgd, unsigned long addr,
unsigned long next, struct mm_walk *walk);
int (*p4d_entry)(p4d_t *p4d, unsigned long addr,
unsigned long next, struct mm_walk *walk);
int (*pud_entry)(pud_t *pud, unsigned long addr,
unsigned long next, struct mm_walk *walk);
int (*pmd_entry)(pmd_t *pmd, unsigned long addr,
unsigned long next, struct mm_walk *walk);
int (*pte_entry)(pte_t *pte, unsigned long addr,
unsigned long next, struct mm_walk *walk);
int (*pte_hole)(unsigned long addr, unsigned long next,
int depth, struct mm_walk *walk);
int (*hugetlb_entry)(pte_t *pte, unsigned long hmask,
unsigned long addr, unsigned long next,
struct mm_walk *walk);
int (*test_walk)(unsigned long addr, unsigned long next,
struct mm_walk *walk);
int (*pre_vma)(unsigned long start, unsigned long end,
struct mm_walk *walk);
void (*post_vma)(struct mm_walk *walk);
int (*install_pte)(unsigned long addr, unsigned long next,
pte_t *ptep, struct mm_walk *walk);
enum page_walk_lock walk_lock;
};
ํ•„๋“œ์—ญํ• 
`pgd_entry`๊ฐ non-empty PGD ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ
`p4d_entry`๊ฐ non-empty P4D ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ
`pud_entry`๊ฐ non-empty PUD ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ
`pmd_entry`๊ฐ non-empty PMD ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ (huge page ์ฒ˜๋ฆฌ ํ•„์š”)
`pte_entry`๊ฐ PTE ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ (install_pte ์„ค์ • ์‹œ ๊ธฐ์กด PTE๋งŒ)
`pte_hole`hole ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ, depth: -1(๋ชจ๋ฆ„), 0:PGD~3:PMD
`hugetlb_entry`HugeTLB ์—”ํŠธ๋ฆฌ์—์„œ ํ˜ธ์ถœ (VMA lock ์œ ์ง€)
`test_walk`VMA ์ˆœํšŒ ์ „ ํ…Œ์ŠคํŠธ, 0:์ง„ํ–‰, >0:์Šคํ‚ต, <0:์ค‘๋‹จ
`pre_vma` / `post_vma`VMA ์ˆœํšŒ ์ „ํ›„ ์ฝœ๋ฐฑ
`install_pte`๋ˆ„๋ฝ๋œ PTE ์ž๋™ ์„ค์น˜ (๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์ „์šฉ)
`walk_lock`mmap_lock ์ž ๊ธˆ ๋ชจ๋“œ (RDLOCK/WRLOCK ๋“ฑ)

struct mm_walk

์ˆœํšŒ ์ƒํƒœ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ตฌ์กฐ์ฒด โ€” ์ฝœ๋ฐฑ ๊ฐ„ ๊ณต์œ ๋ฉ๋‹ˆ๋‹ค.

// include/linux/pagewalk.h:121-129
struct mm_walk {
const struct mm_walk_ops *ops;     // ์ฝœ๋ฐฑ ํ…Œ์ด๋ธ”
struct mm_struct *mm;              // ๋Œ€์ƒ ํ”„๋กœ์„ธ์Šค mm
pgd_t *pgd;                        // ์ปค๋„ ํ…Œ์ด๋ธ” ์ˆœํšŒ ์‹œ PGD ํฌ์ธํ„ฐ
struct vm_area_struct *vma;        // ํ˜„์žฌ ์ˆœํšŒ ์ค‘์ธ VMA
enum page_walk_action action;      // ๋‹ค์Œ ๋™์ž‘ (SUBTREE/CONTINUE/AGAIN)
bool no_vma;                       // VMA ๋ฌด์‹œ ๋ชจ๋“œ
void *private;                     // ์ฝœ๋ฐฑ์šฉ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ
};

enum page_walk_action

pmd_entry/pud_entry ์ฝœ๋ฐฑ์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ๋™์ž‘ ์ œ์–ด ๊ฐ’:

// include/linux/pagewalk.h:100-107
enum page_walk_action {
ACTION_SUBTREE = 0,  // ํ•˜์œ„ ๋ ˆ๋ฒจ๋กœ ๋‚ด๋ ค๊ฐ (THP ๋ถ„ํ•  ํฌํ•จ)
ACTION_CONTINUE = 1, // ๊ฐ™์€ ๋ ˆ๋ฒจ์˜ ๋‹ค์Œ ์—”ํŠธ๋ฆฌ๋กœ ์ด๋™
ACTION_AGAIN = 2     // ํ˜„์žฌ ์—”ํŠธ๋ฆฌ๋ฅผ ๋‹ค์‹œ ์ฒ˜๋ฆฌ
};

enum page_walk_lock

์ž ๊ธˆ ๋ชจ๋“œ โ€” VMA ์ฒ˜๋ฆฌ ์‹œ mmap_lock ์ž ๊ธˆ ์ˆ˜์ค€์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค:

// include/linux/pagewalk.h:10-19
enum page_walk_lock {
PGWALK_RDLOCK = 0,             // mmap_lock ์ฝ๊ธฐ ์ž ๊ธˆ
PGWALK_WRLOCK = 1,             // VMA ์“ฐ๊ธฐ ์ž ๊ธˆ ํš๋“
PGWALK_WRLOCK_VERIFY = 2,      // VMA ์“ฐ๊ธฐ ์ž ๊ธˆ ์ด๋ฏธ ํš๋“๋จ (๊ฒ€์ฆ๋งŒ)
PGWALK_VMA_RDLOCK_VERIFY = 3,  // VMA ์ฝ๊ธฐ ์ž ๊ธˆ ์ด๋ฏธ ํš๋“๋จ (๊ฒ€์ฆ๋งŒ)
};

struct folio_walk

folio_walk_start() / folio_walk_end() API์˜ ๋ฐ˜ํ™˜/์ƒํƒœ ๊ตฌ์กฐ์ฒด:

// include/linux/pagewalk.h:177-194
struct folio_walk {
/* public */
struct page *page;                       // ์ฐธ์กฐ๋œ ์ •ํ™•ํ•œ ํŽ˜์ด์ง€
enum folio_walk_level level;             // PTE/PMD/PUD ์ค‘ ์—”ํŠธ๋ฆฌ ๋ ˆ๋ฒจ
union {
pte_t *ptep;                         // PTE ํฌ์ธํ„ฐ (FW_LEVEL_PTE)
pud_t *pudp;                         // PUD ํฌ์ธํ„ฐ (FW_LEVEL_PUD)
pmd_t *pmdp;                         // PMD ํฌ์ธํ„ฐ (FW_LEVEL_PMD)
};
union {
pte_t pte;                           // PTE ๊ฐ’
pud_t pud;                           // PUD ๊ฐ’
pmd_t pmd;                           // PMD ๊ฐ’
};
/* private */
struct vm_area_struct *vma;              // ๋Œ€์ƒ VMA
spinlock_t *ptl;                         // ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ž ๊ธˆ
};

folio_walk ํ”Œ๋ž˜๊ทธ

// include/linux/pagewalk.h:155-158
#define FW_MIGRATION  BIT(0)  // migration ์—”ํŠธ๋ฆฌ๋„ ์ˆœํšŒ
#define FW_ZEROPAGE   BIT(1)  // ๊ณต์œ  zero page๋„ ๋ฐ˜ํ™˜

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

1. walk_page_range()

์™ธ๋ถ€ API ์ง„์ž…์  โ€” check_ops_safe()๋กœ install_pte ์‚ฌ์šฉ ์—ฌ๋ถ€ ๊ฒ€์ฆ ํ›„ walk_page_range_mm_unsafe() ํ˜ธ์ถœ.

// mm/pagewalk.c:598-606
int walk_page_range(struct mm_struct *mm, unsigned long start,
unsigned long end, const struct mm_walk_ops *ops,
void *private)
{
if (!check_ops_safe(ops))
return -EINVAL;

return walk_page_range_mm_unsafe(mm, start, end, ops, private);
}
  • ๋ฐ˜ํ™˜๊ฐ’: 0=์„ฑ๊ณต, <0=์—๋Ÿฌ, >0=์ฝœ๋ฐฑ ์ •์˜ ์กฐ๊ธฐ ์ข…๋ฃŒ
  • ์ž ๊ธˆ: ํ˜ธ์ถœ์ž๊ฐ€ mm->mmap_lock ๋ณด์œ  ํ•„์š”
  • ๋ณด์•ˆ: install_pte๊ฐ€ ์„ค์ •๋œ ops๋Š” ์™ธ๋ถ€ API์—์„œ ์ฐจ๋‹จ
  • 2. walk_page_range_mm_unsafe()

    ์‹ค์ œ ์ˆœํšŒ ๋กœ์ง โ€” VMA๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ์˜์—ญ์— ๋Œ€ํ•ด __walk_page_range() ํ˜ธ์ถœ.

    // mm/pagewalk.c:496-530
    vma = find_vma(walk.mm, start);
    do {
    if (!vma) { /* ๋งˆ์ง€๋ง‰ VMA ์ดํ›„ */
    		walk.vma = NULL;
    		next = end;
    		if (ops->pte_hole)
    			err = ops->pte_hole(start, next, -1, &walk);
    } else if (start < vma->vm_start) { /* VMA ๋ฐ”๊นฅ */
    		walk.vma = NULL;
    		next = min(end, vma->vm_start);
    		if (ops->pte_hole)
    			err = ops->pte_hole(start, next, -1, &walk);
    } else { /* VMA ๋‚ด๋ถ€ */
    		process_vma_walk_lock(vma, ops->walk_lock);
    		walk.vma = vma;
    		next = min(end, vma->vm_end);
    		vma = find_vma(mm, vma->vm_end);
    
    		err = walk_page_test(start, next, &walk);
    		if (err > 0) {
    			err = 0;
    			continue;
    		}
    		if (err < 0)
    			break;
    		err = __walk_page_range(start, next, &walk);
    	}
    	if (err)
    		break;
    } while (start = next, start < end);
  • VMA ํƒ์ƒ‰: find_vma()๋กœ ์‹œ์ž‘ ์ฃผ์†Œ์˜ VMA๋ฅผ ์ฐพ๊ณ , VMA ๊ฐ„ gap์€ pte_hole๋กœ ์ฒ˜๋ฆฌ
  • VMA ๋ฐ”๊นฅ ์˜์—ญ: pte_hole(start, next, -1, &walk) ํ˜ธ์ถœ
  • VMA ๋‚ด๋ถ€: walk_page_test()๋กœ VMA ๊ฒ€์ฆ ํ›„ __walk_page_range() ํ˜ธ์ถœ
  • 3. __walk_page_range()

    VMA ๋‚ด๋ถ€ ์ˆœํšŒ โ€” HugeTLB๋ฉด walk_hugetlb_range(), ์•„๋‹ˆ๋ฉด walk_pgd_range() ํ˜ธ์ถœ.

    // mm/pagewalk.c:408-435
    static int __walk_page_range(unsigned long start, unsigned long end,
    			struct mm_walk *walk)
    {
    	int err = 0;
    	struct vm_area_struct *vma = walk->vma;
    	const struct mm_walk_ops *ops = walk->ops;
    	bool is_hugetlb = is_vm_hugetlb_page(vma);
    
    	/* hugetlb PTE ์„ค์น˜๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. */
    	if (ops->install_pte && is_hugetlb)
    		return -EINVAL;
    
    	if (ops->pre_vma) {
    		err = ops->pre_vma(start, end, walk);
    		if (err)
    			return err;
    	}
    
    	if (is_hugetlb) {
    		if (ops->hugetlb_entry)
    			err = walk_hugetlb_range(start, end, walk);
    	} else
    		err = walk_pgd_range(start, end, walk);
    
    	if (ops->post_vma)
    		ops->post_vma(walk);
    
    	return err;
    }

    4. walk_pgd_range() โ†’ walk_p4d_range() โ†’ walk_pud_range() โ†’ walk_pmd_range() โ†’ walk_pte_range()

    ๊ณ„์ธต์  ์ˆœํšŒ ์ฒด์ธ โ€” ๊ฐ ๋ ˆ๋ฒจ์—์„œ ์—”ํŠธ๋ฆฌ ์กด์žฌ/none/leaf ๊ฒ€์‚ฌ ํ›„ ํ•˜์œ„ ๋ ˆ๋ฒจ๋กœ ๋‚ด๋ ค๊ฐ.

    // mm/pagewalk.c:289-325
    static int walk_pgd_range(unsigned long addr, unsigned long end,
    			  struct mm_walk *walk)
    {
    	pgd_t *pgd;
    	unsigned long next;
    	const struct mm_walk_ops *ops = walk->ops;
    	bool has_handler = ops->p4d_entry || ops->pud_entry || ops->pmd_entry ||
    		ops->pte_entry;
    	bool has_install = ops->install_pte;
    	int err = 0;
    
    	if (walk->pgd)
    		pgd = walk->pgd + pgd_index(addr);
    	else
    		pgd = pgd_offset(walk->mm, addr);
    	do {
    		next = pgd_addr_end(addr, end);
    		if (pgd_none_or_clear_bad(pgd)) {
    			if (has_install)
    				err = __p4d_alloc(walk->mm, pgd, addr);
    			else if (ops->pte_hole)
    				err = ops->pte_hole(addr, next, 0, walk);
    			if (err)
    				break;
    			if (!has_install)
    				continue;
    		}
    		if (ops->pgd_entry) {
    			err = ops->pgd_entry(pgd, addr, next, walk);
    			if (err)
    				break;
    		}
    		if (has_handler || has_install)
    			err = walk_p4d_range(pgd, addr, next, walk);
    		if (err)
    			break;
    	} while (pgd++, addr = next, addr != end);
    
    	return err;
    }

    ๊ณตํ†ต ํŒจํ„ด (๋ชจ๋“  walk_*_range ํ•จ์ˆ˜):

    1. ํ˜„์žฌ ๋ ˆ๋ฒจ์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํ„ฐ ํš๋“ (pgd_offset, pmd_offset ๋“ฑ)

    2. none_or_clear_bad ๊ฒ€์‚ฌ โ†’ ์—†์œผ๋ฉด __xxx_alloc() ๋˜๋Š” pte_hole()

    3. ํ•ด๋‹น ๋ ˆ๋ฒจ ์ฝœ๋ฐฑ ํ˜ธ์ถœ (pgd_entry, pmd_entry ๋“ฑ)

    4. walk->action ํ™•์ธ:

    - ACTION_SUBTREE โ†’ ํ•˜์œ„ ๋ ˆ๋ฒจ๋กœ ๋‚ด๋ ค๊ฐ

    - ACTION_CONTINUE โ†’ ๋‹ค์Œ ์—”ํŠธ๋ฆฌ๋กœ (ํ•˜์œ„ ๋ฌด์‹œ)

    - ACTION_AGAIN โ†’ ๋‹ค์‹œ ํ˜„์žฌ ์—”ํŠธ๋ฆฌ ์ฒ˜๋ฆฌ

    5. ํ•˜์œ„ ๋ ˆ๋ฒจ ํ•จ์ˆ˜ ์žฌ๊ท€ ํ˜ธ์ถœ

    5. folio_walk_start()

    ๋‹จ์ผ ๊ฐ€์ƒ ์ฃผ์†Œ์—์„œ folio๋ฅผ ์ง์ ‘ ์ฐพ๋Š” ๊ฐ„๊ฒฐํ•œ API โ€” folio_walk_end()๋กœ ํ•ด์ œ.

    // mm/pagewalk.c:969-1004
    if (IS_ENABLED(CONFIG_PGTABLE_HAS_HUGE_LEAVES) &&
    (!pmd_present(pmd) || pmd_leaf(pmd))) {
    	ptl = pmd_lock(vma->vm_mm, pmdp);
    	pmd = pmdp_get(pmdp);
    
    	entry_size = PMD_SIZE;
    	fw->level = FW_LEVEL_PMD;
    	fw->pmdp = pmdp;
    	fw->pmd = pmd;
    
    	if (pmd_none(pmd)) {
    		spin_unlock(ptl);
    		goto not_found;
    	} else if (pmd_present(pmd) && !pmd_leaf(pmd)) {
    		spin_unlock(ptl);
    		goto pte_table;
    	} else if (pmd_present(pmd)) {
    		page = vm_normal_page_pmd(vma, addr, pmd);
    		if (page)
    			goto found;
    	} else if ((flags & FW_MIGRATION) &&
    		   pmd_is_migration_entry(pmd)) {
    		const softleaf_t entry = softleaf_from_pmd(pmd);
    
    		page = softleaf_to_page(entry);
    		expose_page = false;
    		goto found;
    	}
    	spin_unlock(ptl);
    	goto not_found;
    }
  • ๋ฐ˜ํ™˜: ๋งคํ•‘๋œ folio, ์—†์œผ๋ฉด NULL
  • ํŠน์ง•: VMA, addr, flags๋กœ ์ง์ ‘ folio ํš๋“, PTL ์œ ์ง€
  • folio_walk_end(): PTL ํ•ด์ œ + pte_unmap + vma_pgtable_walk_end

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

    walk_page_range(mm, start, end, ops, private)
    โ”‚
    โ”œโ”€โ”€ check_ops_safe(ops)          โ† install_pte ์ฐจ๋‹จ
    โ”‚
    โ””โ”€โ”€ walk_page_range_mm_unsafe(mm, start, end, ops, private)
    โ”‚
    โ”œโ”€โ”€ process_mm_walk_lock()  โ† mmap_lock ๊ฒ€์ฆ
    โ”‚
    โ””โ”€โ”€ VMA ์ˆœํšŒ ๋ฃจํ”„
    โ”‚
    โ”œโ”€โ”€ VMA ๋ฐ”๊นฅ โ†’ ops->pte_hole()
    โ”‚
    โ””โ”€โ”€ VMA ๋‚ด๋ถ€
    โ”‚
    โ”œโ”€โ”€ process_vma_walk_lock()  โ† VMA ์ž ๊ธˆ
    โ”œโ”€โ”€ walk_page_test()         โ† test_walk ์ฝœ๋ฐฑ
    โ”‚
    โ””โ”€โ”€ __walk_page_range(start, end, walk)
    โ”‚
    โ”œโ”€โ”€ ops->pre_vma()
    โ”‚
    โ”œโ”€โ”€ HugeTLB?
    โ”‚   โ”œโ”€โ”€ YES โ†’ walk_hugetlb_range()
    โ”‚   โ”‚         โ””โ”€โ”€ ops->hugetlb_entry()
    โ”‚   โ””โ”€โ”€ NO  โ†’ walk_pgd_range()
    โ”‚             โ”‚
    โ”‚             โ”œโ”€โ”€ ops->pgd_entry()
    โ”‚             โ””โ”€โ”€ walk_p4d_range()
    โ”‚                   โ”‚
    โ”‚                   โ”œโ”€โ”€ ops->p4d_entry()
    โ”‚                   โ””โ”€โ”€ walk_pud_range()
    โ”‚                         โ”‚
    โ”‚                         โ”œโ”€โ”€ ops->pud_entry()
    โ”‚                         โ””โ”€โ”€ walk_pmd_range()
    โ”‚                               โ”‚
    โ”‚                               โ”œโ”€โ”€ ops->pmd_entry()
    โ”‚                               โ”œโ”€โ”€ split_huge_pmd()
    โ”‚                               โ””โ”€โ”€ walk_pte_range()
    โ”‚                                     โ”‚
    โ”‚                                     โ”œโ”€โ”€ ops->install_pte()  ๋˜๋Š”
    โ”‚                                     โ””โ”€โ”€ ops->pte_entry()
    โ”‚
    โ””โ”€โ”€ ops->post_vma()

    folio_walk_start() ํ˜ธ์ถœ ํ๋ฆ„

    folio_walk_start(fw, vma, addr, flags)
    โ”‚
    โ”œโ”€โ”€ pgd_offset(mm, addr) โ†’ PGD ๊ฒ€์‚ฌ
    โ”œโ”€โ”€ p4d_offset(pgdp, addr) โ†’ P4D ๊ฒ€์‚ฌ
    โ”œโ”€โ”€ pud_offset(p4dp, addr) โ†’ PUD ๊ฒ€์‚ฌ
    โ”‚     โ””โ”€โ”€ CONFIG_PGTABLE_HAS_HUGE_LEAVES โ†’ PUD leaf ํ™•์ธ
    โ”‚           โ””โ”€โ”€ vm_normal_page_pud() โ†’ found
    โ”œโ”€โ”€ pmd_offset(pudp, addr) โ†’ PMD ๊ฒ€์‚ฌ
    โ”‚     โ””โ”€โ”€ PMD leaf ํ™•์ธ
    โ”‚           โ”œโ”€โ”€ vm_normal_page_pmd() โ†’ found
    โ”‚           โ””โ”€โ”€ is_huge_zero_pmd() โ†’ found (FW_ZEROPAGE)
    โ””โ”€โ”€ pte_offset_map_lock() โ†’ PTE ๊ฒ€์‚ฌ
    โ”œโ”€โ”€ pte_present โ†’ vm_normal_page() โ†’ found
    โ””โ”€โ”€ migration entry โ†’ softleaf_to_page() โ†’ found (FW_MIGRATION)

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

    1. API ์œ ํ˜•๋ณ„ ๋น„๊ต

    API๋ฒ”์œ„VMA ํ•„์š”PTL ์ฒ˜๋ฆฌ์ฃผ์š” ์‚ฌ์šฉ์ฒ˜
    `walk_page_range()`๋ฉ€ํ‹ฐ VMA์•„๋‹ˆ์˜ค๋‚ด๋ถ€ ์ฒ˜๋ฆฌmadvise, mprotect, mlock, mincore
    `walk_page_range_vma()`๋‹จ์ผ VMA์˜ˆ๋‚ด๋ถ€ ์ฒ˜๋ฆฌKSM, madvise
    `walk_page_vma()`์ „์ฒด VMA์˜ˆ๋‚ด๋ถ€ ์ฒ˜๋ฆฌํŠน์ˆ˜ ์šฉ๋„
    `walk_page_mapping()`address_space์•„๋‹ˆ์˜ค๋‚ด๋ถ€ ์ฒ˜๋ฆฌwriteback, dirty helpers
    `walk_kernel_page_table_range()`์ปค๋„ ํ…Œ์ด๋ธ”์•„๋‹ˆ์˜คlocklessvmalloc, ioremap
    `folio_walk_start()`๋‹จ์ผ ์ฃผ์†Œ์˜ˆ (ํ˜ธ์ถœ ํ•„์š”)PTL ํš๋“rmap, migrate, huge_memory

    2. walk_lock ๋ชจ๋“œ๋ณ„ ๋น„๊ต

    ๋ชจ๋“œmmap_lockVMA lock์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
    `PGWALK_RDLOCK`์ฝ๊ธฐ์—†์Œ์ผ๋ฐ˜์ ์ธ ์ฝ๊ธฐ ์ˆœํšŒ (mincore, mlock)
    `PGWALK_WRLOCK`์“ฐ๊ธฐ์“ฐ๊ธฐ ํš๋“์ˆ˜์ • ํ•„์š”ํ•œ ์ˆœํšŒ (mprotect, madvise FREE)
    `PGWALK_WRLOCK_VERIFY`์“ฐ๊ธฐ์“ฐ๊ธฐ ๊ฒ€์ฆ์ด๋ฏธ ์ž ๊ธด VMA (madvise ๋‚ด๋ถ€)
    `PGWALK_VMA_RDLOCK_VERIFY`-์ฝ๊ธฐ ๊ฒ€์ฆVMA read lock ์ด๋ฏธ ํš๋“๋จ

    3. walk->action๋ณ„ ๋™์ž‘

    action ๊ฐ’๋™์ž‘์ ์šฉ ๋ ˆ๋ฒจ
    `ACTION_SUBTREE` (๊ธฐ๋ณธ)ํ•˜์œ„ ๋ ˆ๋ฒจ๋กœ ๋‚ด๋ ค๊ฐ, THP ํ•„์š”์‹œ ๋ถ„ํ• pud_entry, pmd_entry
    `ACTION_CONTINUE`๋‹ค์Œ ์—”ํŠธ๋ฆฌ๋กœ ์ด๋™ (ํ•˜์œ„ ๋ฌด์‹œ)pud_entry, pmd_entry
    `ACTION_AGAIN`ํ˜„์žฌ ์—”ํŠธ๋ฆฌ๋ฅผ ๋‹ค์‹œ ์ฒ˜๋ฆฌ (๋™๊ธฐํ™” ์žฌํ™•์ธ)pmd_entry, pud_entry

    4. hole ์ฒ˜๋ฆฌ ๋ฐฉ์‹ ๋น„๊ต

    ์กฐ๊ฑดpte_hole ๋™์ž‘depth ๊ฐ’
    VMA ์—†์Œ (find_vma ์‹คํŒจ)pte_hole(start, end, -1)-1 (๋ชจ๋ฆ„)
    VMA ๋ฐ”๊นฅ gappte_hole(start, next, -1)-1 (๋ชจ๋ฆ„)
    PMD none (install_pte ์—†์Œ)pte_hole(addr, next, 3)3 (PMD)
    PUD none (install_pte ์—†์Œ)pte_hole(addr, next, 2)2 (PUD)
    P4D none (install_pte ์—†์Œ)pte_hole(addr, next, 1)1 (P4D)
    PGD none (install_pte ์—†์Œ)pte_hole(addr, next, 0)0 (PGD)
    install_pte ์„ค์ • ์‹œ hole์—†์Œ (ํ•ญ์ƒ ์—”ํŠธ๋ฆฌ ์„ค์น˜)-

    5. folio_walk_start() vs walk_page_range()

    ํ•ญ๋ชฉfolio_walk_start()walk_page_range()
    ๋ชฉ์ ๋‹จ์ผ ์ฃผ์†Œ โ†’ folio ๋ฐ˜ํ™˜๋ฒ”์œ„ ์ˆœํšŒ + ์ฝœ๋ฐฑ
    ๋ฐ˜ํ™˜๊ฐ’folio* ๋˜๋Š” NULL์—๋Ÿฌ ์ฝ”๋“œ
    PTLcaller๊ฐ€ folio_walk_end()๋กœ ํ•ด์ œ๋‚ด๋ถ€์—์„œ pte_unmap_unlock
    ์ฝœ๋ฐฑ์—†์Œops->pte_entry ๋“ฑ
    ๋ฒ”์œ„ํ•˜๋‚˜์˜ ์ฃผ์†Œ๋งŒ์ž„์˜ ๋ฒ”์œ„
    ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜คrmap, migrate, huge_memorymlock, madvise, mincore ๋“ฑ

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • 03 โ€” VMA / mmap
  • 10 โ€” Huge Pages / THP
  • 12 โ€” Folio / Page Cache
  • 20 โ€” Migration
  • 28 โ€” Reverse Mapping
  • 33 โ€” GUP (Get User Pages)
  • 36 โ€” mprotect
  • 37 โ€” mremap
  • 39 โ€” vmpressure