๏ปฟ# Reverse Mapping (rmap)

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

๊ฐœ์š” (Overview)

Reverse Mapping(rmap)์€ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€(folio)๋กœ๋ถ€ํ„ฐ ๊ทธ ํŽ˜์ด์ง€๊ฐ€ ๋งคํ•‘๋œ ๋ชจ๋“  ๊ฐ€์ƒ ์ฃผ์†Œ๋ฅผ ์ฐพ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. Linux ์ปค๋„์—์„œ ํŽ˜์ด์ง€ ํšŒ์ˆ˜(reclaim), KSM(Kernel Samepage Merging), ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜, ํŽ˜์ด์ง€ ์ œ๊ฑฐ(zap) ๋“ฑ ํ•ต์‹ฌ ๋™์ž‘์€ "๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ โ†’ ์–ด๋–ค VMA์— ๋งคํ•‘๋˜์–ด ์žˆ๋Š”๊ฐ€?"๋ฅผ ์ถ”์ ํ•ด์•ผ ํ•˜๋ฉฐ, rmap์ด ์ด๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

rmap์€ ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์˜ ๋งคํ•‘์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค: ์ต๋ช…(anon) ํŽ˜์ด์ง€๋Š” anon_vma ์ฒด์ธ์„ ํ†ตํ•ด ์ถ”์ ํ•˜๊ณ , ํŒŒ์ผ(file) ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€๋Š” address_space->i_mmap interval tree๋ฅผ ํ†ตํ•ด ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ folio์˜ mapping ํ•„๋“œ๊ฐ€ anon_vma ๋˜๋Š” address_space๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ, rmap walk ์‹œ ํ•ด๋‹น ๊ตฌ์กฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜์—ฌ ๋ชจ๋“  ๋งคํ•‘ VMA๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ ๋กœ ๋ณด๋ฉด rmap์€ ๋„์„œ๊ด€์˜ ๋Œ€์ถœ ์นด๋“œ ๋ชฉ๋ก๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์ฑ… ํ•œ ๊ถŒ(folio)์€ ์‹ค์ œ๋กœ ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์˜ ์†์— ๊ฑด๋„ค์งˆ ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋‚ฉยท์ด๋™ยทํ๊ธฐ ์‹œ์ ์—๋Š” "์ด ์ฑ…์„ ๋ˆ„๊ฐ€ ์–ด๋””์—์„œ ๋นŒ๋ ค ๊ฐ”๋Š”๊ฐ€"๋ฅผ ๋ฐ”๋กœ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. rmap์€ ๋ฐ”๋กœ ๊ทธ ์—ญ๋ฐฉํ–ฅ ์ƒ‰์ธ์ž…๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ์—์„œ ๋ณด๋Š” rmap์€ ํŽ˜์ด์ง€ ํดํŠธ์™€ COW๊นŒ์ง€ ์ด์–ด์ง‘๋‹ˆ๋‹ค. ์ฒ˜์Œ ์ ‘๊ทผํ•œ ์ฃผ์†Œ๋Š” ํŽ˜์ด์ง€ ํดํŠธ๋กœ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ณ , fork() ๋’ค ์“ฐ๊ธฐ ์‹œ์ ์—๋Š” COW๋กœ ์ƒˆ ํŽ˜์ด์ง€๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํšŒ์ˆ˜๋‚˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์˜ค๋ฉด rmap์ด ๊ฐ VMA๋ฅผ ๋‹ค์‹œ ์ฐพ์•„ mapcount์™€ PTE๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ:
mm/rmap.c                          โ† rmap ํ•ต์‹ฌ ๊ตฌํ˜„ (3147์ค„)
include/linux/rmap.h               โ† rmap ๊ณต๊ฐœ API, ๊ตฌ์กฐ์ฒด ์ •์˜ (995์ค„)
mm/internal.h                      โ† ๋‚ด๋ถ€ ์œ ํ‹ธ๋ฆฌํ‹ฐ
rmap ๊ตฌ์กฐ ๊ด€๊ณ„๋„
rmap ํ˜ธ์ถœ ํ๋ฆ„

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

rmap๋งŒ ๋‹จ๋…์œผ๋กœ ๋ณด์ง€ ๋ง๊ณ , ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ๊ณผ ํŽ˜์ด์ง€ ํดํŠธ ์ถ”์„ธ๋ฅผ ๊ฐ™์ด ๋ณด๋ฉด ์›์ธ์„ ์žก๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

# anon_vma ์บ์‹œ ํ†ต๊ณ„
grep -i "anon_vma" /proc/slabinfo

# ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ ๋งต์—์„œ rmap ๊ด€๋ จ ์ •๋ณด ํ™•์ธ
cat /proc/<pid>/smaps | head -50

# ํŽ˜์ด์ง€ ๋งคํ•‘ ์นด์šดํŠธ ํ™•์ธ (mapcount)
cat /proc/kpagecount | head -20

# rmap ๊ด€๋ จ ํŠธ๋ ˆ์ด์Šค ์ด๋ฒคํŠธ ํ™•์ธ
ls /sys/kernel/debug/tracing/events/rmap/ 2>/dev/null

# folio mapcount ๋””๋ฒ„๊ทธ (CONFIG_DEBUG_VM ๋นŒ๋“œ)
cat /sys/kernel/debug/rmap_stats 2>/dev/null

# KSM์ด rmap์„ ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธ
cat /sys/kernel/mm/ksm/pages_shared
cat /sys/kernel/mm/ksm/pages_sharing

# ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” walk๋ฅผ ํ†ตํ•œ ๋งคํ•‘ ํ™•์ธ
cat /proc/<pid>/pagemap | head -20

# ๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜ ์‹œ rmap walk ๋™์ž‘ ํ™•์ธ
echo 1 > /proc/sys/vm/drop_caches
dmesg | tail -20

# ํŽ˜์ด์ง€ ํดํŠธ์™€ ์ต๋ช…/ํŒŒ์ผ ํŽ˜์ด์ง€ ๊ทœ๋ชจ ํ™•์ธ
cat /proc/vmstat | grep -E 'pgfault|pgmajfault|nr_anon_pages|nr_file_pages|nr_page_table_pages'
cat /proc/meminfo | grep -E 'AnonPages|Mapped|Cached|SwapCached|PageTables'

# ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ๊ณผ ๋ถ„ํฌ ํ™•์ธ
cat /proc/pressure/memory
cat /proc/buddyinfo
cat /proc/slabinfo | grep -E 'anon_vma|anon_vma_chain|vm_area_struct'

๊ฐ™์ด ๋ณด๋Š” ์ง€ํ‘œ

์ง€ํ‘œ์˜๋ฏธํ•จ๊ป˜ ๋ณด๋Š” ์ด์œ 
`AnonPages`์ต๋ช… ํŽ˜์ด์ง€ ๊ทœ๋ชจ`rmap_walk_anon()`๊ณผ COW ํ™œ๋™์ด ๋งŽ์„์ˆ˜๋ก ๊ฐ™์ด ์ปค์ง€๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
`Cached`ํŒŒ์ผ ํŽ˜์ด์ง€ ๊ทœ๋ชจ`rmap_walk_file()` ๊ฒฝ๋กœ์™€ page cache ํšŒ์ˆ˜๊ฐ€ ์ž์ฃผ ์ผ์–ด๋‚˜๋Š”์ง€ ๋ณด๊ธฐ์— ์ข‹์Šต๋‹ˆ๋‹ค.
`pgfault` / `pgmajfault`ํŽ˜์ด์ง€ ํดํŠธ ์ถ”์„ธ์ƒˆ ๋งคํ•‘๊ณผ ์‹ค์ œ I/O๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
`PageTables`ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋น„์šฉmapcount ๋ณ€ํ™”์™€ PTE ์ •๋ฆฌ ๋ถ€๋‹ด์„ ๊ฐ™์ด ๊ฐ€๋Š ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
`anon_vma` / `anon_vma_chain`rmap ๋ฉ”ํƒ€๋ฐ์ดํ„ฐfork, COW, ์ต๋ช… ํŽ˜์ด์ง€ ์ˆ˜๊ฐ€ ๋Š˜ ๋•Œ slab ์‚ฌ์šฉ๋Ÿ‰์ด ํ•จ๊ป˜ ์ปค์ง‘๋‹ˆ๋‹ค.

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

struct anon_vma

์ต๋ช… ํŽ˜์ด์ง€์˜ ์—ญ๋ฐฉํ–ฅ ๋งคํ•‘์„ ๊ด€๋ฆฌํ•˜๋Š” ํ•ต์‹ฌ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. fork๋œ ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ํŠธ๋ฆฌ ํ˜•ํƒœ๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

// include/linux/rmap.h:32-68
struct anon_vma {
struct anon_vma *root;          // ์ด anon_vma ํŠธ๋ฆฌ์˜ ๋ฃจํŠธ
struct rw_semaphore rwsem;      // ์ˆ˜์ •: ์“ฐ๊ธฐ์ž ๊ธˆ, ์ˆœํšŒ: ์ฝ๊ธฐ์ž ๊ธˆ
atomic_t refcount;              // ์ฐธ์กฐ ์นด์šดํŠธ
unsigned long num_children;     // ์ž์‹ anon_vma ์ˆ˜ (์ž๊ธฐ ํฌํ•จ)
unsigned long num_active_vmas;  // ์ด anon_vma๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” VMA ์ˆ˜
struct anon_vma *parent;        // ๋ถ€๋ชจ anon_vma
struct rb_root_cached rb_root;  // ๊ด€๋ จ VMA์˜ interval tree
};

struct anon_vma_chain (AVC)

VMA์™€ anon_vma๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์ฒด์ธ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ VMA๋Š” ์—ฌ๋Ÿฌ anon_vma์— ์—ฐ๊ฒฐ๋  ์ˆ˜ ์žˆ๊ณ , ํ•˜๋‚˜์˜ anon_vma๋Š” ์—ฌ๋Ÿฌ VMA๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// include/linux/rmap.h:83-92
struct anon_vma_chain {
struct vm_area_struct *vma;         // ์—ฐ๊ฒฐ๋œ VMA
struct anon_vma *anon_vma;          // ์—ฐ๊ฒฐ๋œ anon_vma
struct list_head same_vma;          // ๊ฐ™์€ VMA์˜ ๋‹ค๋ฅธ AVC ๋ชฉ๋ก
struct rb_node rb;                  // anon_vma interval tree ๋…ธ๋“œ
unsigned long rb_subtree_last;      // interval tree ์„œ๋ธŒํŠธ๋ฆฌ ๋งˆ์ง€๋ง‰ ๊ฐ’
};

struct rmap_walk_control

rmap walk๋ฅผ ์ œ์–ดํ•˜๋Š” ์ปจํŠธ๋กค ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. ๊ฐ walk ์œ ํ˜•์— ๋งž๋Š” ์ฝœ๋ฐฑ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

// include/linux/rmap.h:951-965
struct rmap_walk_control {
void *arg;                          // ์ฝœ๋ฐฑ์— ์ „๋‹ฌ๋˜๋Š” ์ธ์ž
bool try_lock;                      // ๋ฝ ํš๋“ ์‹คํŒจ ์‹œ ์ฆ‰์‹œ ๋ฐ˜ํ™˜
bool contended;                     // ๋ฝ ๊ฒฝ์Ÿ์œผ๋กœ ์ค‘๋‹จ๋จ ํ”Œ๋ž˜๊ทธ
bool (*rmap_one)(struct folio *folio, struct vm_area_struct *vma,
unsigned long addr, void *arg);  // ๋งค VMA์—์„œ ํ˜ธ์ถœ๋  ์ฝœ๋ฐฑ
int (*done)(struct folio *folio);   // ์ˆœํšŒ ์ข…๋ฃŒ ์กฐ๊ฑด ๊ฒ€์‚ฌ
struct anon_vma *(*anon_lock)(const struct folio *folio,
struct rmap_walk_control *rwc);  // anon ๋ฝ ํš๋“
bool (*invalid_vma)(struct vm_area_struct *vma, void *arg);    // ๊ฑด๋„ˆ๋›ธ VMA ํŒ๋ณ„
};

struct page_vma_mapped_walk (PVMW)

ํŠน์ • ํŽ˜์ด์ง€๊ฐ€ ์–ด๋–ค VMA์— ๋งคํ•‘๋˜์–ด ์žˆ๋Š”์ง€ ์ˆœํšŒํ•˜๋Š” ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.

// include/linux/rmap.h:864-874
struct page_vma_mapped_walk {
unsigned long pfn;          // ์ฐพ๋Š” ํŽ˜์ด์ง€์˜ PFN
unsigned long nr_pages;     // folio์˜ ํŽ˜์ด์ง€ ์ˆ˜
pgoff_t pgoff;              // folio์˜ ํŽ˜์ด์ง€ ์˜คํ”„์…‹
struct vm_area_struct *vma; // ๋Œ€์ƒ VMA
unsigned long address;      // ํ˜„์žฌ ๊ฐ€์ƒ ์ฃผ์†Œ
pmd_t *pmd;                 // ๋งคํ•‘๋œ PMD ์—”ํŠธ๋ฆฌ
pte_t *pte;                 // ๋งคํ•‘๋œ PTE ์—”ํŠธ๋ฆฌ
spinlock_t *ptl;            // ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ž ๊ธˆ
unsigned int flags;         // PVMW_SYNC, PVMW_MIGRATION ๋“ฑ
};

enum ttu_flags (Try To Unmap ํ”Œ๋ž˜๊ทธ)

// include/linux/rmap.h:94-105
enum ttu_flags {
TTU_USE_SHARED_ZEROPAGE = 0x2,   // large folio ๋ฏธ์‚ฌ์šฉ ํŽ˜์ด์ง€์šฉ
TTU_SPLIT_HUGE_PMD    = 0x4,     // THP PMD๋ฅผ PTE๋กœ ๋ถ„ํ• 
TTU_IGNORE_MLOCK       = 0x8,    // mlock ๋ฌด์‹œ
TTU_SYNC               = 0x10,   // racy ๊ฒ€์‚ฌ ํšŒํ”ผ (๋™๊ธฐํ™”)
TTU_HWPOISON          = 0x20,    // hwpoison ์—”ํŠธ๋ฆฌ๋กœ ๋ณ€ํ™˜
TTU_BATCH_FLUSH        = 0x40,   // TLB flush ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ
TTU_RMAP_LOCKED        = 0x80,   // rmap ๋ฝ ๋ฏธํš๋“ (ํ˜ธ์ถœ์ž๊ฐ€ ๋ณด์œ )
};

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

1. rmap_walk โ€” rmap ์ˆœํšŒ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ

folio์˜ ๋ชจ๋“  ๋งคํ•‘ VMA๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค. folio ์œ ํ˜•์— ๋”ฐ๋ผ anon, file, KSM ์ˆœํšŒ๋กœ ๋ถ„๊ธฐํ•ฉ๋‹ˆ๋‹ค.

// mm/rmap.c:3093-3101
void rmap_walk(struct folio *folio, struct rmap_walk_control *rwc)
{
if (unlikely(folio_test_ksm(folio)))
rmap_walk_ksm(folio, rwc);        // KSM folio ์ฒ˜๋ฆฌ
else if (folio_test_anon(folio))
rmap_walk_anon(folio, rwc, false); // ์ต๋ช… ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ
else
rmap_walk_file(folio, rwc, false); // ํŒŒ์ผ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ
}

๋ถ„๊ธฐ ๋กœ์ง:

  • KSM folio โ†’ rmap_walk_ksm(): KSM stable/unstable tree ์ˆœํšŒ
  • anon folio โ†’ rmap_walk_anon(): anon_vma->rb_root interval tree ์ˆœํšŒ
  • file folio โ†’ rmap_walk_file(): mapping->i_mmap interval tree ์ˆœํšŒ
  • 2. rmap_walk_anon โ€” ์ต๋ช… ํŽ˜์ด์ง€ rmap ์ˆœํšŒ

    anon_vma ํŠธ๋ฆฌ์—์„œ ๋ชจ๋“  ๊ด€๋ จ VMA๋ฅผ ์ฐพ์•„ rmap_one ์ฝœ๋ฐฑ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    // mm/rmap.c:2956-3000
    static void rmap_walk_anon(struct folio *folio,
    struct rmap_walk_control *rwc, bool locked)
    {
    struct anon_vma *anon_vma;
    pgoff_t pgoff_start, pgoff_end;
    struct anon_vma_chain *avc;
    
    /* folio lock์ด ์žˆ์–ด์•ผ mapping์ด ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. */
    VM_WARN_ON_FOLIO(!folio_test_locked(folio), folio);
    
    if (locked) {
    anon_vma = folio_anon_vma(folio);  // ์ด๋ฏธ ๋ฝ๋œ ๊ฒฝ์šฐ
    /* anon_vma๊ฐ€ ์‚ฌ๋ผ์กŒ๋‹ค๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. */
    VM_BUG_ON_FOLIO(!anon_vma, folio);
    } else {
    anon_vma = rmap_walk_anon_lock(folio, rwc); // ๋ฝ ํš๋“
    }
    
    if (!anon_vma) return;
    
    pgoff_start = folio_pgoff(folio);
    pgoff_end = pgoff_start + folio_nr_pages(folio) - 1;
    
    /* interval tree์—์„œ ๊ฒน์น˜๋Š” ๋ชจ๋“  VMA๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค. */
    anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root,
    pgoff_start, pgoff_end) {
    struct vm_area_struct *vma = avc->vma;
    unsigned long address = vma_address(vma, pgoff_start,
    folio_nr_pages(folio));
    
    VM_BUG_ON_VMA(address == -EFAULT, vma);
    cond_resched();
    
    if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
    continue;  // ๊ฑด๋„ˆ๋›ธ VMA
    
    if (!rwc->rmap_one(folio, vma, address, rwc->arg))
    break;     // ์ฝœ๋ฐฑ์ด false ๋ฐ˜ํ™˜ ์‹œ ์ˆœํšŒ ์ค‘๋‹จ
    if (rwc->done && rwc->done(folio))
    break;     // ์ข…๋ฃŒ ์กฐ๊ฑด ์ถฉ์กฑ
    }
    
    if (!locked)
    anon_vma_unlock_read(anon_vma);
    }

    3. try_to_unmap โ€” ํŽ˜์ด์ง€ ๋งคํ•‘ ์ œ๊ฑฐ

    folio์˜ ๋ชจ๋“  ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋งคํ•‘์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ํšŒ์ˆ˜(swap out), OOM kill ์‹œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

    // mm/rmap.c:2386-2399
    void try_to_unmap(struct folio *folio, enum ttu_flags flags)
    {
    struct rmap_walk_control rwc = {
    .rmap_one = try_to_unmap_one,     // ๋งค VMA์—์„œ PTE ์ œ๊ฑฐ
    .arg = (void *)flags,
    .done = folio_not_mapped,          // ๋ชจ๋“  ๋งคํ•‘ ์ œ๊ฑฐ ์‹œ ์ข…๋ฃŒ
    .anon_lock = folio_lock_anon_vma_read,
    };
    
    if (flags & TTU_RMAP_LOCKED)
    rmap_walk_locked(folio, &rwc);    // ์ด๋ฏธ rmap ๋ฝ ๋ณด์œ 
    else
    rmap_walk(folio, &rwc);           // ๋ฝ ์—†์ด ์ˆœํšŒ
    }

    try_to_unmap_one ํ•ต์‹ฌ ๋ถ„๊ธฐ:

  • VM_LOCKED VMA โ†’ mlock ๋ณต์› ํ›„ ์ค‘๋‹จ
  • PMD ๋งคํ•‘ THP โ†’ TTU_SPLIT_HUGE_PMD ํ”Œ๋ž˜๊ทธ ์‹œ PTE๋กœ ๋ถ„ํ• 
  • ์ต๋ช… ํŽ˜์ด์ง€ โ†’ swap entry๋กœ ๊ต์ฒด (page_swap_entry())
  • ํŒŒ์ผ ๊ธฐ๋ฐ˜ ํŽ˜์ด์ง€ โ†’ counter ๊ฐ์†Œ๋งŒ ์ˆ˜ํ–‰
  • hwpoison ํŽ˜์ด์ง€ โ†’ hwpoison swap entry๋กœ ๊ต์ฒด
  • 4. try_to_migrate โ€” ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์œ„ํ•œ ๋งคํ•‘ ๊ต์ฒด

    ๋ชจ๋“  PTE๋ฅผ migration entry๋กœ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ ์ „์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

    // mm/rmap.c:2731-2767
    void try_to_migrate(struct folio *folio, enum ttu_flags flags)
    {
    struct rmap_walk_control rwc = {
    .rmap_one = try_to_migrate_one,   // ๋งค VMA์—์„œ migration entry ์„ค์น˜
    .arg = (void *)flags,
    .done = folio_not_mapped,
    .anon_lock = folio_lock_anon_vma_read,
    };
    
    // ์œ ํšจํ•˜์ง€ ์•Š์€ ํ”Œ๋ž˜๊ทธ ๊ฒ€์ฆ
    if (WARN_ON_ONCE(flags & ~(TTU_RMAP_LOCKED | TTU_SPLIT_HUGE_PMD |
    TTU_SYNC | TTU_BATCH_FLUSH)))
    return;
    
    // ๋””๋ฐ”์ด์Šค ์ „์šฉ folio ์ฒ˜๋ฆฌ ์ œํ•œ
    if (folio_is_zone_device(folio) &&
    (!folio_is_device_private(folio) && !folio_is_device_coherent(folio)))
    return;
    
    // exec() ์ค‘ ์ž„์‹œ VMA ๊ฑด๋„ˆ๋›ฐ๊ธฐ
    if (!folio_test_ksm(folio) && folio_test_anon(folio))
    rwc.invalid_vma = invalid_migration_vma;
    
    if (flags & TTU_RMAP_LOCKED)
    rmap_walk_locked(folio, &rwc);
    else
    rmap_walk(folio, &rwc);
    }

    5. folio_referenced โ€” ํŽ˜์ด์ง€ ์ฐธ์กฐ ๊ฒ€์‚ฌ

    folio๊ฐ€ ์ฐธ์กฐ(referenced)๋˜์—ˆ๋Š”์ง€ ๋ชจ๋“  ๋งคํ•‘์—์„œ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ํšŒ์ˆ˜ ์‹œ ์œ ํœด(idle) ํŒ๋‹จ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

    // mm/rmap.c:1059-1094
    int folio_referenced(struct folio *folio, int is_locked,
    struct mem_cgroup *memcg, vm_flags_t *vm_flags)
    {
    bool we_locked = false;
    struct folio_referenced_arg pra = {
    .mapcount = folio_mapcount(folio),
    .memcg = memcg,
    };
    struct rmap_walk_control rwc = {
    .rmap_one = folio_referenced_one,       // ๋งค VMA์—์„œ referenced ๊ฒ€์‚ฌ
    .arg = (void *)&pra,
    .anon_lock = folio_lock_anon_vma_read,
    .try_lock = true,                        // ๋ฝ ๊ฒฝ์Ÿ ์‹œ ํšŒํ”ผ
    .invalid_vma = invalid_folio_referenced_vma,
    };
    
    *vm_flags = 0;
    if (!pra.mapcount)
    return 0;
    
    if (!folio_raw_mapping(folio))
    return 0;
    
    /* folio_trylock์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ž ๊ธˆ์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค. */
    if (!is_locked) {
    we_locked = folio_trylock(folio);
    if (!we_locked) return 1;
    }
    
    rmap_walk(folio, &rwc);
    *vm_flags = pra.vm_flags;
    
    if (we_locked)
    folio_unlock(folio);
    
    return rwc.contended ? -1 : pra.referenced;
    }

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

    try_to_unmap(folio, flags)
    โ””โ”€ rmap_walk(folio, &rwc)
    โ”œโ”€ [anon] rmap_walk_anon(folio, rwc, false)
    โ”‚    โ”œโ”€ rmap_walk_anon_lock(folio, rwc)  โ† anon_vma ๋ฝ ํš๋“
    โ”‚    โ”œโ”€ anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root)
    โ”‚    โ”‚    โ”œโ”€ rwc->invalid_vma(vma, arg)  โ† ๊ฑด๋„ˆ๋›ธ VMA ํ™•์ธ
    โ”‚    โ”‚    โ”œโ”€ rwc->rmap_one(folio, vma, address, arg)
    โ”‚    โ”‚    โ”‚    โ””โ”€ try_to_unmap_one(folio, vma, address, arg)
    โ”‚    โ”‚    โ”‚         โ”œโ”€ page_vma_mapped_walk(&pvmw)  โ† PTE ์ˆœํšŒ
    โ”‚    โ”‚    โ”‚         โ”‚    โ”œโ”€ [THP] split_huge_pmd_locked() โ†’ ์žฌ์ˆœํšŒ
    โ”‚    โ”‚    โ”‚         โ”‚    โ”œโ”€ [anon] folio_try_share_anon_rmap_pte()
    โ”‚    โ”‚    โ”‚         โ”‚    โ”œโ”€ get_and_clear_ptes()  โ† PTE ์ œ๊ฑฐ
    โ”‚    โ”‚    โ”‚         โ”‚    โ”œโ”€ set_pte_at(swp_pte)   โ† swap entry ์„ค์น˜
    โ”‚    โ”‚    โ”‚         โ”‚    โ””โ”€ folio_remove_rmap_ptes()  โ† mapcount ๊ฐ์†Œ
    โ”‚    โ”‚    โ”‚         โ””โ”€ mmu_notifier_invalidate_range_end()
    โ”‚    โ”‚    โ””โ”€ rwc->done(folio)  โ† folio_not_mapped() ํ™•์ธ
    โ”‚    โ””โ”€ anon_vma_unlock_read(anon_vma)
    โ”œโ”€ [file] rmap_walk_file(folio, rwc, false)
    โ”‚    โ”œโ”€ i_mmap_lock_read(mapping)
    โ”‚    โ”œโ”€ vma_interval_tree_foreach(vma, &mapping->i_mmap)
    โ”‚    โ”‚    โ””โ”€ rwc->rmap_one(folio, vma, address, arg)
    โ”‚    โ””โ”€ i_mmap_unlock_read(mapping)
    โ””โ”€ [KSM] rmap_walk_ksm(folio, rwc)

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

    rmap walk ์œ ํ˜• ๋น„๊ต

    ํ•ญ๋ชฉrmap_walk_anonrmap_walk_filermap_walk_ksm
    **๋Œ€์ƒ**์ต๋ช… ํŽ˜์ด์ง€ (malloc ๋“ฑ)ํŒŒ์ผ ๋งคํ•‘ (mmap ๋“ฑ)KSM ๋ณ‘ํ•ฉ ํŽ˜์ด์ง€
    **๋ฐ์ดํ„ฐ ๊ตฌ์กฐ**anon_vma->rb_root (interval tree)address_space->i_mmap (interval tree)stable_node tree
    **๋ฝ**anon_vma->root->rwsem (์ฝ๊ธฐ)mapping->i_mmap_rwsem (์ฝ๊ธฐ)KSM lock
    **์ˆœํšŒ ๋Œ€์ƒ**anon_vma_chain โ†’ VMAVMA interval tree โ†’ VMAstable_node โ†’ rmap_item โ†’ VMA
    **ํ˜ธ์ถœ ๊ฒฝ๋กœ**ํšŒ์ˆ˜, OOM, KSM ์Šค์บ”ํšŒ์ˆ˜, truncate, fsyncKSM ๋ณ‘ํ•ฉ

    try_to_unmap vs try_to_migrate ๋น„๊ต

    ํ•ญ๋ชฉtry_to_unmaptry_to_migrate
    **๋ชฉ์ **ํŽ˜์ด์ง€ ๋งคํ•‘ ์™„์ „ ์ œ๊ฑฐPTE๋ฅผ migration entry๋กœ ๊ต์ฒด
    **PTE ํ›„์† ์ฒ˜๋ฆฌ**hwpoison/swap entry ๋˜๋Š” ์ œ๊ฑฐwritable/readable/exclusive migration entry
    **VM_LOCKED ์ฒ˜๋ฆฌ**mlock ๋ณต์› ํ›„ ์ค‘๋‹จ๋ฌด์‹œ (mlock ๋ถˆํ•„์š”)
    **TTU_HWPOISON**์ง€์›๋ฏธ์ง€์›
    **TTU_IGNORE_MLOCK**์ง€์›ํ•ด๋‹น ์—†์Œ
    **ํ˜ธ์ถœ ์‹œ์ **ํŽ˜์ด์ง€ ํšŒ์ˆ˜(swap out), OOMํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹œ์ž‘ ์ „

    anon ํŽ˜์ด์ง€ ๋งคํ•‘ ์ถ”๊ฐ€ API ๋น„๊ต

    ํ•จ์ˆ˜์‹œ๋‚˜๋ฆฌ์˜ค๋ ˆ๋ฒจmapcount ์ฒ˜๋ฆฌ
    `folio_add_anon_rmap_ptes()`๊ธฐ์กด folio์— PTE ๋งคํ•‘ ์ถ”๊ฐ€PTEatomic_inc_and_test
    `folio_add_anon_rmap_pmd()`๊ธฐ์กด folio์— PMD ๋งคํ•‘ ์ถ”๊ฐ€PMDatomic_inc (entire_mapcount)
    `folio_add_new_anon_rmap()`์ƒˆ folio์— ์ฒซ ๋งคํ•‘PTE/PMDatomic_set(0) (์ƒˆ๋กœ ์‹œ์ž‘)
    `folio_add_file_rmap_ptes()`ํŒŒ์ผ ๋งคํ•‘ PTE ์ถ”๊ฐ€PTEatomic_inc_and_test
    `folio_add_file_rmap_pmd()`ํŒŒ์ผ ๋งคํ•‘ PMD ์ถ”๊ฐ€PMDatomic_inc (entire_mapcount)

    TTU ํ”Œ๋ž˜๊ทธ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ๋น„๊ต

    ํ”Œ๋ž˜๊ทธtry_to_unmaptry_to_migrate๋น„๊ณ 
    TTU_SPLIT_HUGE_PMDTHP PMD๋ฅผ PTE๋กœ ๋ถ„ํ•  ํ›„ ์žฌ์ˆœํšŒmigration entry ์„ค์น˜ ์ „ ๋ถ„ํ• 
    TTU_SYNCPTE ์ œ๊ฑฐ ํ›„ ๋™๊ธฐํ™” ๋Œ€๊ธฐ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋Œ€๊ธฐ
    TTU_HWPOISONhwpoison swap entry ์„ค์น˜๋ฏธ์ง€์›
    TTU_BATCH_FLUSHTLB flush ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฏธ์ง€์›
    TTU_RMAP_LOCKEDrmap ๋ฝ ๋ฏธํš๋“rmap ๋ฝ ๋ฏธํš๋“
    TTU_IGNORE_MLOCKVM_LOCKED ๋ฌด์‹œํ•ด๋‹น ์—†์Œ

    lock ordering

    rmap ๊ด€๋ จ ์ž ๊ธˆ ์ˆœ์„œ (mm/rmap.c ์ฃผ์„์—์„œ ๋ฐœ์ทŒ):

    inode->i_rwsem (์“ฐ๊ธฐ/ํŠธ๋ ์ผ€์ดํŠธ ์‹œ)
    mm->mmap_lock
    mapping->invalidate_lock
    folio_lock
    hugetlbfs_i_mmap_rwsem_key
    vma_start_write
    mapping->i_mmap_rwsem
    anon_vma->rwsem
    mm->page_table_lock or pte_lock
    swap_lock
    mmlist_lock

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • VMA / mmap
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜
  • Folio / Page Cache
  • Migration
  • KSM
  • OOM Killer
  • Compaction
  • tmpfs / shmem