๏ปฟ# mlock / munlock ๐Ÿ“Œ

๊ด€๋ จ ์†Œ์Šค: mm/mlock.c, mm/swap.c, include/linux/mm.h
mlock ํ˜ธ์ถœ ํ๋ฆ„
mlock ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

๊ฐœ์š” (Overview)

mlock์€ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์˜ ๋ฉ”๋ชจ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ๊ณ ์ •ํ•˜์—ฌ ์ปค๋„์˜ ํŽ˜์ด์ง€ ํšŒ์ˆ˜(kswapd, direct reclaim)๋‚˜ ์Šค์™‘ ์•„์›ƒ์œผ๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•˜๋Š” ์‹œ์Šคํ…œ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ์‹ค์‹œ๊ฐ„ ์‹œ์Šคํ…œ, ์ €์ง€์—ฐ(latency) ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ์•”ํ˜ธํ™” ํ‚ค ๋“ฑ์—์„œ ์ค‘์š”ํ•œ ๋ณดํ˜ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ, mlock, mlock2, mlockall๊ณผ ํ•ด์ œ ํ•จ์ˆ˜ munlock, munlockall์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ปค๋„ ๋‚ด๋ถ€์ ์œผ๋กœ mlock๋œ ํŽ˜์ด์ง€๋Š” folio์— PG_mlocked ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•˜๊ณ , LRU(active/inactive)์—์„œ unevictable ๋ชฉ๋ก์œผ๋กœ ์ด๋™์‹œํ‚ต๋‹ˆ๋‹ค. folio์˜ mlock_count ํ•„๋“œ๋Š” ๊ฐ™์€ folio๊ฐ€ ์—ฌ๋Ÿฌ VMA์—์„œ mlock๋  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. VM_LOCKED ํ”Œ๋ž˜๊ทธ๋Š” VMA์—, VM_LOCKONFAULT(mlock2)์€ ํŽ˜์ด์ง€ ํดํŠธ ์‹œ์ ์— ๊ณ ์ •ํ•˜๋Š” ์ง€์—ฐ ๋ชจ๋“œ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์€ ๋„์„œ๊ด€์—์„œ ์ž์ฃผ ์“ฐ๋Š” ์ฑ…์— ๋ฐ˜๋‚ฉ ๊ธˆ์ง€ ํ‘œ์‹์„ ๋ถ™์—ฌ ๋‘๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ฑ…์„ ์•„์˜ˆ ์น˜์šฐ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํšŒ์ˆ˜ ๋‹ด๋‹น์ž๊ฐ€ ๋จผ์ € ๊ฐ€์ ธ๊ฐ€ ๋ฒ„๋ฆฌ์ง€ ๋ชปํ•˜๊ฒŒ ํ‘œ์‹œ๋งŒ ๋‚จ๊ธฐ๊ณ  ํ•„์š”ํ•  ๋•Œ๋Š” ๊ทธ๋Œ€๋กœ ๊บผ๋‚ด ์ฝ๊ฒŒ ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

// mm/mlock.c:1-29 โ€” ํ—ค๋” ๋ฐ ๊ธฐ๋ณธ ๊ตฌ์กฐ
#include <linux/capability.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/sched/user.h>
#include <linux/swap.h>
#include <linux/pagewalk.h>
#include <linux/mempolicy.h>
#include <linux/rmap.h>

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

# ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์˜ locked ๋ฉ”๋ชจ๋ฆฌ์™€ ๊ธฐ๋ณธ ์ƒํƒœ ํ™•์ธ
grep -E '^VmLck|^VmRSS|^VmSize' /proc/self/status

# ์‹œ์Šคํ…œ ์ „์ฒด unevictable / mlocked ํ†ต๊ณ„
grep -E '^Unevictable:|^Mlocked:' /proc/meminfo

# VMA ๋‹จ์œ„ lock ํ”Œ๋ž˜๊ทธ ํ™•์ธ
grep -E 'VmFlags:.*\blo\b' /proc/<PID>/smaps

# RLIMIT_MEMLOCK ํ•œ๋„ ํ™•์ธ
ulimit -l

# mlock / munlock / rescued / stranded ํ†ต๊ณ„
grep -E 'nr_mlock|unevictable_pgs_(mlocked|munlocked|rescued|stranded)' /proc/vmstat

# ํ”„๋กœ์„ธ์Šค ์š”์•ฝ ํ†ต๊ณ„์—์„œ Locked ํ™•์ธ
grep -E '^Locked:|^Rss:' /proc/<PID>/smaps_rollup

# mlock ๋Œ€์ƒ๊ณผ ํ•จ๊ป˜ ์ž์ฃผ ๋ณด๋Š” per-process ํ•ญ๋ชฉ
grep -E '^VmLck|^VmPin' /proc/<PID>/status

# lock ํ•œ๋„์™€ ์‹ค์ œ NUMA ๋ฐฐ์น˜ ๊ฐ™์ด ๋ณด๊ธฐ
cat /proc/<PID>/numa_maps | head -20

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

struct mlock_fbatch

Per-CPU ๋ฐฐ์น˜ ๊ตฌ์กฐ์ฒด๋กœ, mlock/munlock ์—ฐ์‚ฐ์„ ๋ชจ์•˜๋‹ค๊ฐ€ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜์—ฌ lock ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค.

// mm/mlock.c:31-38 โ€” mlock_fbatch ์ •์˜
struct mlock_fbatch {
	local_lock_t lock;
	struct folio_batch fbatch;
};

static DEFINE_PER_CPU(struct mlock_fbatch, mlock_fbatch) = {
	.lock = INIT_LOCAL_LOCK(lock),
};

folio ํฌ์ธํ„ฐ ํ”Œ๋ž˜๊ทธ ์ธ์ฝ”๋”ฉ

mlock_fbatch ๋‚ด์—์„œ folio ํฌ์ธํ„ฐ์˜ ํ•˜์œ„ ๋น„ํŠธ์— ์ž‘์—… ์œ ํ˜•์„ ์ธ์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค. LRU_FOLIO(0x1)๋Š” ๊ธฐ์กด LRU์˜ folio, NEW_FOLIO(0x2)๋Š” ์ƒˆ๋กœ ํ• ๋‹น๋œ folio๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์•„๋‹ˆ๋ฉด munlock์ž…๋‹ˆ๋‹ค.

// mm/mlock.c:167-177 โ€” folio ํฌ์ธํ„ฐ ํ•˜์œ„ ๋น„ํŠธ ์ธ์ฝ”๋”ฉ
#define LRU_FOLIO 0x1
#define NEW_FOLIO 0x2
static inline struct folio *mlock_lru(struct folio *folio)
{
	return (struct folio *)((unsigned long)folio + LRU_FOLIO);
}

static inline struct folio *mlock_new(struct folio *folio)
{
	return (struct folio *)((unsigned long)folio + NEW_FOLIO);
}

vm_area_struct ์ฃผ์š” ํ”Œ๋ž˜๊ทธ

// include/linux/mm.h:420-575 โ€” VM_LOCKED ๊ด€๋ จ ํ”Œ๋ž˜๊ทธ
#define VM_LOCKED	INIT_VM_FLAG(LOCKED)
#define VM_LOCKONFAULT	INIT_VM_FLAG(LOCKONFAULT)
#define VM_LOCKED_MASK	(VM_LOCKED | VM_LOCKONFAULT)
  • VM_LOCKED: VMA์˜ ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ๊ณ ์ •
  • VM_LOCKONFAULT: ํŽ˜์ด์ง€ ํดํŠธ ์‹œ์ ์—๋งŒ ๊ณ ์ • (์ง€์—ฐ ๋ชจ๋“œ)
  • VM_IO: mlock_vma_pages_range ์ค‘ ๋™์‹œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉ์ง€์šฉ ์ผ์‹œ์  ํ”Œ๋ž˜๊ทธ
  • ํ•ต์‹ฌ ํ•จ์ˆ˜

    mlock_folio โ€” ๊ธฐ์กด LRU folio ๊ณ ์ •

    // mm/mlock.c:242-261 โ€” mlock_folio()
    void mlock_folio(struct folio *folio)
    {
    	struct folio_batch *fbatch;
    
    	local_lock(&mlock_fbatch.lock);
    	fbatch = this_cpu_ptr(&mlock_fbatch.fbatch);
    
    	if (!folio_test_set_mlocked(folio)) {
    		int nr_pages = folio_nr_pages(folio);
    		zone_stat_mod_folio(folio, NR_MLOCK, nr_pages);
    		__count_vm_events(UNEVICTABLE_PGMLOCKED, nr_pages);
    	}
    
    	folio_get(folio);
    	if (!folio_batch_add(fbatch, mlock_lru(folio)) ||
    	    !folio_may_be_lru_cached(folio) || lru_cache_disabled())
    		mlock_folio_batch(fbatch);
    	local_unlock(&mlock_fbatch.lock);
    }
  • folio_test_set_mlocked: atomic set & test๋กœ ์ฒ˜์Œ mlock์ธ์ง€ ํ™•์ธ (NR_MLOCK ํ†ต๊ณ„ ๊ฐฑ์‹ )
  • ๋ฐฐ์น˜๊ฐ€ ๊ฐ€๋“ ์ฐจ๊ฑฐ๋‚˜ LRU ์บ์‹œ ๋น„ํ™œ์„ฑํ™” ์‹œ ์ฆ‰์‹œ drain
  • mlock_new_folio โ€” ์ƒˆ๋กœ ํ• ๋‹น๋œ folio ๊ณ ์ •

    VM_LOCKED VMA์—์„œ ์ƒˆ folio๊ฐ€ LRU์— ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— ์“ฐ๋Š” ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค. ์•„์ง LRU์— ์˜ฌ๋ผ์˜ค๊ธฐ ์ „์ด๋ผ __mlock_new_folio()๊ฐ€ PG_mlocked์™€ mlock_count๋ฅผ ๋จผ์ € ์„ธํŒ…ํ•˜๊ณ  unevictable ์ƒํƒœ๋กœ ๋„ฃ์Šต๋‹ˆ๋‹ค.

    // mm/mlock.c:263-284 โ€” mlock_new_folio()
    void mlock_new_folio(struct folio *folio)
    {
    	struct folio_batch *fbatch;
    	int nr_pages = folio_nr_pages(folio);
    
    	local_lock(&mlock_fbatch.lock);
    	fbatch = this_cpu_ptr(&mlock_fbatch.fbatch);
    	folio_set_mlocked(folio);
    
    	zone_stat_mod_folio(folio, NR_MLOCK, nr_pages);
    	__count_vm_events(UNEVICTABLE_PGMLOCKED, nr_pages);
    
    	folio_get(folio);
    	if (!folio_batch_add(fbatch, mlock_new(folio)) ||
    	    !folio_may_be_lru_cached(folio) || lru_cache_disabled())
    		mlock_folio_batch(fbatch);
    	local_unlock(&mlock_fbatch.lock);
    }
  • ์ƒˆ๋กœ ํ• ๋‹น๋œ folio๋Š” mlock_new()๋กœ ํ‘œ์‹œํ•œ ๋’ค ๋ฐฐ์น˜์— ๋„ฃ์Šต๋‹ˆ๋‹ค.
  • swap.c์˜ folio_add_lru_vma()๋Š” VM_LOCKED VMA๋ฉด ์ด ๊ฒฝ๋กœ๋กœ ๋ฐ”๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • __mlock_folio โ€” LRU์—์„œ unevictable๋กœ ์ด๋™

    // mm/mlock.c:61-101 โ€” __mlock_folio()
    static struct lruvec *__mlock_folio(struct folio *folio, struct lruvec *lruvec)
    {
    	if (!folio_test_clear_lru(folio))
    		return lruvec;
    
    	lruvec = folio_lruvec_relock_irq(folio, lruvec);
    
    	if (unlikely(folio_evictable(folio))) {
    		if (folio_test_unevictable(folio)) {
    			lruvec_del_folio(lruvec, folio);
    			folio_clear_unevictable(folio);
    			lruvec_add_folio(lruvec, folio);
    			__count_vm_events(UNEVICTABLE_PGRESCUED,
    					  folio_nr_pages(folio));
    		}
    		goto out;
    	}
    
    	if (folio_test_unevictable(folio)) {
    		if (folio_test_mlocked(folio))
    			folio->mlock_count++;
    		goto out;
    	}
    
    	lruvec_del_folio(lruvec, folio);
    	folio_clear_active(folio);
    	folio_set_unevictable(folio);
    	folio->mlock_count = !!folio_test_mlocked(folio);
    	lruvec_add_folio(lruvec, folio);
    	__count_vm_events(UNEVICTABLE_PGCULLED, folio_nr_pages(folio));
    out:
    	folio_set_lru(folio);
    	return lruvec;
    }
  • active/inactive ๋ชฉ๋ก์—์„œ unevictable ๋ชฉ๋ก์œผ๋กœ ์ด๋™
  • mlock_count ์ค‘๋ณต mlock ์ถ”์  (์ด๋ฏธ unevictable์ด๋ฉด ์นด์šดํŠธ๋งŒ ์ฆ๊ฐ€)
  • UNEVICTABLE_PGRESCUED: evictable์ธ๋ฐ mlocked๊ฐ€ ํ•ด์ œ๋œ ์ผ€์ด์Šค
  • __munlock_folio โ€” munlock ์ฒ˜๋ฆฌ

    // mm/mlock.c:122-162 โ€” __munlock_folio()
    static struct lruvec *__munlock_folio(struct folio *folio, struct lruvec *lruvec)
    {
    	int nr_pages = folio_nr_pages(folio);
    	bool isolated = false;
    
    	if (!folio_test_clear_lru(folio))
    		goto munlock;
    
    	isolated = true;
    	lruvec = folio_lruvec_relock_irq(folio, lruvec);
    
    	if (folio_test_unevictable(folio)) {
    		if (folio->mlock_count)
    			folio->mlock_count--;
    		if (folio->mlock_count)
    			goto out;
    	}
    
    munlock:
    	if (folio_test_clear_mlocked(folio)) {
    		__zone_stat_mod_folio(folio, NR_MLOCK, -nr_pages);
    		if (isolated || !folio_test_unevictable(folio))
    			__count_vm_events(UNEVICTABLE_PGMUNLOCKED, nr_pages);
    		else
    			__count_vm_events(UNEVICTABLE_PGSTRANDED, nr_pages);
    	}
    
    	if (isolated && folio_test_unevictable(folio) && folio_evictable(folio)) {
    		lruvec_del_folio(lruvec, folio);
    		folio_clear_unevictable(folio);
    		lruvec_add_folio(lruvec, folio);
    		__count_vm_events(UNEVICTABLE_PGRESCUED, nr_pages);
    	}
    out:
    	if (isolated)
    		folio_set_lru(folio);
    	return lruvec;
    }
  • mlock_count ๊ฐ์†Œ ํ›„ 0์ด ๋˜๋ฉด ์‹ค์ œ mlock ํ•ด์ œ
  • UNEVICTABLE_PGSTRANDED: LRU์—์„œ ๊ฒฉ๋ฆฌ๋˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ mlocked ํ•ด์ œ
  • mlock_fixup โ€” VMA ๋‹จ์œ„ mlock/munlock ์ฒ˜๋ฆฌ

    // mm/mlock.c:466-512 โ€” mlock_fixup()
    static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
    	       struct vm_area_struct **prev, unsigned long start,
    	       unsigned long end, vm_flags_t newflags)
    {
    	struct mm_struct *mm = vma->vm_mm;
    	int nr_pages;
    	int ret = 0;
    	vm_flags_t oldflags = vma->vm_flags;
    
    	if (newflags == oldflags || (oldflags & VM_SPECIAL) ||
    	    is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
    	    vma_is_dax(vma) || vma_is_secretmem(vma) || (oldflags & VM_DROPPABLE))
    		goto out;
    
    	vma = vma_modify_flags(vmi, *prev, vma, start, end, &newflags);
    	if (IS_ERR(vma)) {
    		ret = PTR_ERR(vma);
    		goto out;
    	}
    
    	nr_pages = (end - start) >> PAGE_SHIFT;
    	if (!(newflags & VM_LOCKED))
    		nr_pages = -nr_pages;
    	else if (oldflags & VM_LOCKED)
    		nr_pages = 0;
    	mm->locked_vm += nr_pages;
    
    	if ((newflags & VM_LOCKED) && (oldflags & VM_LOCKED)) {
    		vma_start_write(vma);
    		vm_flags_reset(vma, newflags);
    	} else {
    		mlock_vma_pages_range(vma, start, end, newflags);
    	}
    out:
    	*prev = vma;
    	return ret;
    }
  • VM_SPECIAL, HugeTLB, DAX, secretmem, VM_DROPPABLE VMA๋Š” mlock ๋ฌด์‹œ
  • locked_vm ์นด์šดํŠธ ๊ด€๋ฆฌ (RLIMIT_MEMLOCK ์ฒดํฌ์šฉ)
  • ๊ธฐ์กด์— ์ด๋ฏธ VM_LOCKED์˜€์œผ๋ฉด ํŽ˜์ด์ง€ ์›Œํฌ ์—†์ด ํ”Œ๋ž˜๊ทธ๋งŒ ๊ฐฑ์‹ 
  • mlock_vma_pages_range โ€” ํŽ˜์ด์ง€ ์›Œํฌ์™€ ์ž„์‹œ VM_IO ์ฒ˜๋ฆฌ

    mlock_fixup()๊ฐ€ VMA ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ”๊พผ ๋’ค ์‹ค์ œ ํŽ˜์ด์ง€๋ฅผ ์ˆœํšŒํ•˜๋Š” ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค. VM_LOCKED๋ฅผ ์ผ  ์ƒํƒœ์—์„œ ๋‹ค๋ฅธ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ™์€ folio๋ฅผ ๋‹ค์‹œ ์„ธ์ง€ ์•Š๋„๋ก, ์ˆœํšŒ ๋™์•ˆ๋งŒ VM_IO๋ฅผ ๋ง๋ถ™์˜€๋‹ค๊ฐ€ ๋๋‚˜๋ฉด ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

    // mm/mlock.c:423-455 โ€” mlock_vma_pages_range()
    static void mlock_vma_pages_range(struct vm_area_struct *vma,
    	unsigned long start, unsigned long end, vm_flags_t newflags)
    {
    	static const struct mm_walk_ops mlock_walk_ops = {
    		.pmd_entry = mlock_pte_range,
    		.walk_lock = PGWALK_WRLOCK_VERIFY,
    	};
    
    	if (newflags & VM_LOCKED)
    		newflags |= VM_IO;
    	vma_start_write(vma);
    	vm_flags_reset_once(vma, newflags);
    
    	lru_add_drain();
    	walk_page_range(vma->vm_mm, start, end, &mlock_walk_ops, NULL);
    	lru_add_drain();
    
    	if (newflags & VM_IO) {
    		newflags &= ~VM_IO;
    		vm_flags_reset_once(vma, newflags);
    	}
    }
  • VM_IO๋Š” ์—ฌ๊ธฐ์„œ๋งŒ ์ž ๊น ๋ถ™๋Š” ๋ณด์กฐ ํ”Œ๋ž˜๊ทธ์ž…๋‹ˆ๋‹ค.
  • walk_page_range()๊ฐ€ mlock_pte_range()๋ฅผ ๋ถ€๋ฅด๋ฉฐ THP์™€ PTE ๊ฒฝ๋กœ๋ฅผ ๋‚˜๋ˆ•๋‹ˆ๋‹ค.
  • ๋๋‚˜๋ฉด VM_IO๋ฅผ ์ง€์›Œ ์›๋ž˜ VMA ์ƒํƒœ๋กœ ๋Œ๋ฆฝ๋‹ˆ๋‹ค.
  • do_mlock โ€” ์‹œ์Šคํ…œ ํ˜ธ์ถœ ์ง„์ž…์ 

    // mm/mlock.c:612-657 โ€” do_mlock()
    static __must_check int do_mlock(unsigned long start, size_t len, vm_flags_t flags)
    {
    	unsigned long locked;
    	unsigned long lock_limit;
    	int error = -ENOMEM;
    
    	start = untagged_addr(start);
    	if (!can_do_mlock())
    		return -EPERM;
    
    	len = PAGE_ALIGN(len + (offset_in_page(start)));
    	start &= PAGE_MASK;
    
    	lock_limit = rlimit(RLIMIT_MEMLOCK);
    	lock_limit >>= PAGE_SHIFT;
    	locked = len >> PAGE_SHIFT;
    
    	if (mmap_write_lock_killable(current->mm))
    		return -EINTR;
    
    	locked += current->mm->locked_vm;
    	if ((locked > lock_limit) && (!capable(CAP_IPC_LOCK))) {
    		locked -= count_mm_mlocked_page_nr(current->mm, start, len);
    	}
    
    	if ((locked <= lock_limit) || capable(CAP_IPC_LOCK))
    		error = apply_vma_lock_flags(start, len, flags);
    
    	mmap_write_unlock(current->mm);
    	if (error)
    		return error;
    
    	error = __mm_populate(start, len, 0);
    	if (error)
    		return __mlock_posix_error_return(error);
    	return 0;
    }
  • can_do_mlock: RLIMIT_MEMLOCK != 0 ๋˜๋Š” CAP_IPC_LOCK capability ํ™•์ธ
  • ๊ธฐ์กด mlocked ์˜์—ญ ์ค‘๋ณต ์ฒดํฌ ํ›„ lock_limit ์žฌ์กฐ์ •
  • __mm_populate: ์‹ค์ œ ํŽ˜์ด์ง€๋ฅผ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ฆผ (demand paging ํ•ด์†Œ)
  • ํ˜ธ์ถœ ํ๋ฆ„

    mlock(start, len)
    โ†’ do_mlock(start, len, VM_LOCKED)
    โ†’ can_do_mlock()                 [๊ถŒํ•œ ์ฒดํฌ]
    โ†’ mmap_write_lock_killable()     [mmap_lock ํš๋“]
    โ†’ apply_vma_lock_flags(start, len, flags)
    โ†’ for_each_vma_range()
    โ†’ mlock_fixup()
    โ†’ vma_modify_flags()       [VMA ๋ถ„ํ• /๋ณ‘ํ•ฉ]
    โ†’ mm->locked_vm ๊ฐฑ์‹ 
    โ†’ mlock_vma_pages_range()
    โ†’ walk_page_range(mlock_pte_range)
    โ†’ mlock_pte_range()
    โ†’ [THP] pmd_folio() โ†’ mlock_folio()
    โ†’ [PTE] vm_normal_folio() โ†’ mlock_folio() / munlock_folio()
    โ†’ mmap_write_unlock()
    โ†’ __mm_populate()                [ํŽ˜์ด์ง€ ์‹ค์ œ ํ• ๋‹น]
    
    ์ƒˆ๋กœ ์ƒ๊ธด folio๋Š” `mm/swap.c:523-530`์˜ `folio_add_lru_vma()`์—์„œ `VM_LOCKED` VMA์ธ์ง€ ๋ณด๊ณ  `mlock_new_folio()`๋กœ ๋“ค์–ด๊ฐ€๋ฉฐ, ๊ทธ ๋ฐ–์˜ folio๋Š” ์ผ๋ฐ˜ LRU ๊ฒฝ๋กœ๋ฅผ ํƒ‘๋‹ˆ๋‹ค.
    
    munlock(start, len)
    โ†’ apply_vma_lock_flags(start, len, 0)
    โ†’ mlock_fixup() โ†’ mlock_vma_pages_range()
    โ†’ munlock_folio()
    
    mlockall(flags)
    โ†’ do_mlockall โ†’ apply_mlockall_flags()
    โ†’ mm->def_flags |= VM_LOCKED     [MCL_FUTURE ์‹œ]
    โ†’ for_each_vma() โ†’ mlock_fixup()
    โ†’ mm_populate()                   [MCL_CURRENT ์‹œ]

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

    ๋น„๊ต ํ•ญ๋ชฉmlockmlock2mlockall
    ๋ฒ”์œ„ํŠน์ • ์ฃผ์†Œ ๋ฒ”์œ„ํŠน์ • ์ฃผ์†Œ ๋ฒ”์œ„ํ”„๋กœ์„ธ์Šค ์ „์ฒด VMA
    ์ง€์—ฐ ๋ชจ๋“œ์—†์Œ`MLOCK_ONFAULT` ๊ฐ€๋Šฅ`MCL_ONFAULT` ๊ฐ€๋Šฅ
    ๋ฏธ๋ž˜ ํ• ๋‹น์—†์Œ์—†์Œ`MCL_FUTURE` ์ง€์›
    ํ˜„์žฌ ํ• ๋‹นํ•ญ์ƒํ•ญ์ƒ`MCL_CURRENT` (๊ธฐ๋ณธ๊ฐ’)
    lock_limit ์ฒดํฌ`mm->locked_vm + len``mm->locked_vm + len``total_vm <= lock_limit`
    ๋น„๊ต ํ•ญ๋ชฉVM_LOCKEDVM_LOCKONFAULTVM_LOCKED ํ•ด์ œ
    ํŽ˜์ด์ง€ ๊ณ ์ • ์‹œ์ VMA ํƒœ๊ทธ ์‹œํŽ˜์ด์ง€ ํดํŠธ ์‹œํ•ด์ œ ์‹œ unevictable ํ•ด์ œ
    __mm_populateํ•„์š”๋ถˆํ•„์š” (ํดํŠธ ์‹œ)๋ถˆํ•„์š”
    memcg ํšŒ์ˆ˜์ฐจ๋‹จ์ฐจ๋‹จํ—ˆ์šฉ
    unevictable LRU์ฆ‰์‹œ ์ด๋™ํดํŠธ ์‹œ ์ด๋™active/inactive๋กœ ๋ณต๊ท€
    ๋น„๊ต ํ•ญ๋ชฉํŠน์ˆ˜ VMA ์ฒ˜๋ฆฌ๊ฒฐ๊ณผ
    VM_SPECIAL ( vdso, vsyscall ๋“ฑ)mlock ๋ฌด์‹œ`goto out`
    HugeTLB VMAmlock ๋ฌด์‹œ`goto out`
    DAX VMAmlock ๋ฌด์‹œ`goto out`
    secretmem VMAmlock ๋ฌด์‹œ`goto out`
    VM_DROPPABLE VMAmlock ๋ฌด์‹œ`goto out`
    VM_LOCKED + VM_LOCKED (์ด๋ฏธ ๊ณ ์ •)ํŽ˜์ด์ง€ ์›Œํฌ ์—†์ด ํ”Œ๋ž˜๊ทธ๋งŒ ๊ฐฑ์‹ `vm_flags_reset`
    ๋น„๊ต ํ•ญ๋ชฉ__mlock_folio__munlock_folio__mlock_new_folio
    LRU ์ƒํƒœactive/inactive โ†’ unevictableunevictable โ†’ active/inactiveLRU์— ์—†์Œ โ†’ unevictable
    mlock_count์ฆ๊ฐ€ (์ด๋ฏธ unevictable ์‹œ)๊ฐ์†Œ (0์ด ๋˜๋ฉด ํ•ด์ œ)`!!PG_mlocked`
    ํ†ต๊ณ„ ์ด๋ฒคํŠธPGCULLED, PGRESCUEDPGMUNLOCKED, PGSTRANDEDPGCULLED

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • VMA / mmap
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜
  • Folio / Page Cache
  • Memory Cgroup
  • mprotect
  • madvise