MMU Gather (TLB ํ๋ฌ์ ์ต์ ํ)
๊ฐ์ (Overview)
MMU Gather๋ Linux ์ปค๋์ด ํ์ด์ง ํ ์ด๋ธ์์ ์ํธ๋ฆฌ๋ฅผ ์ ๊ฑฐํ ๋ TLB(Translation Lookaside Buffer) ํ๋ฌ์๋ฅผ ํจ์จ์ ์ผ๋ก ์ํํ๊ธฐ ์ํ ๋ฐฐ์น(batching) ๋ฉ์ปค๋์ฆ์ ๋๋ค. ํ์ด์ง ํ ์ด๋ธ์์ PTE, PMD ๊ฐ์ ์ํธ๋ฆฌ๋ฅผ ํ๋์ฉ ์ ๊ฑฐํ ๋๋ง๋ค ์ฆ์ TLB ํ๋ฌ์๋ฅผ ํธ์ถํ๋ฉด SMP ํ๊ฒฝ์์ IPI(Inter-Processor Interrupt) ์ค๋ฒํค๋๊ฐ ๋๋ฌด ์ปค์ง๋ฏ๋ก, ์ฌ๋ฌ ์ ๊ฑฐ ์์ ์ ๋ชจ์์ ํ ๋ฒ์ ํ๋ฌ์ํ๋ ๋ฐฉ์์ผ๋ก ์ฑ๋ฅ์ ์ต์ ํํฉ๋๋ค.
mm/mmu_gather.c๋ struct mmu_gather ๊ตฌ์กฐ์ฒด๋ฅผ ์ด๊ธฐํํ๊ณ , ์ ๊ฑฐ๋ ํ์ด์ง/ํ
์ด๋ธ์ ๋ฐฐ์น๋ก ์์งํ ๋ค, ์ต์ข
์ ์ผ๋ก TLB ํ๋ฌ์์ ๋ฉ๋ชจ๋ฆฌ ํด์ ๋ฅผ ์ํํ๋ ํต์ฌ ๋ฃจํด์ ์ ๊ณตํฉ๋๋ค. ์ํคํ
์ฒ๋ณ๋ก flush_tlb_mm(), flush_tlb_range() ๊ฐ์ ์ ์์ค ํจ์๋ฅผ ์ ๊ณตํ๋ฉฐ, asm-generic/tlb.h์์ ๊ณตํต ์ธ๋ผ์ธ ํจ์๋ค์ ์ ์ํฉ๋๋ค.
TLB shootdown์ ํ์ด์ง ํ
์ด๋ธ์ ์ ๋ฆฌํ๋ ์๊ฐ๋ฟ ์๋๋ผ mprotect(), munmap(), MADV_DONTNEED, COW, NUMA balancing์ฒ๋ผ PTE ์๋ฏธ๊ฐ ๋ฐ๋๋ ์๊ฐ์๋ ํ์ํฉ๋๋ค. ์ด ๋ฌธ์๋ tlb_gather_mmu()๋ก ๋ณ๊ฒฝ์ ๋ชจ์ ๋ค tlb_flush_mmu_tlbonly() ๋๋ tlb_flush_mmu()์์ invalidate์ free๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ ํ๋ฆ์ ๊ฐ์ด ๋ด
๋๋ค.
์์ค ํ์ผ ๊ฒฝ๋ก:
โโโ mm/mmu_gather.c โ MMU gather ํต์ฌ ๋ก์ง (536์ค)
โโโ include/asm-generic/tlb.h โ struct mmu_gather ์ ์ (L321-392), ์ธ๋ผ์ธ ํจ์
โโโ include/linux/mm_types.h โ encode_page/decode_page (L238-279)
โโโ include/linux/mm_types.h:1578-1582 โ tlb_gather_mmu/finish ์ ์ธ
โโโ arch/Kconfig:508-530 โ MMU_GATHER_* ์ต์
์ ์
โโโ arch/x86/Kconfig:285-286 โ x86: RCU_TABLE_FREE, MERGE_VMAS
โโโ arch/arm64/Kconfig:251 โ ARM64: RCU_TABLE_FREE
โโโ arch/s390/Kconfig:265-267 โ s390: MERGE_VMAS, NO_GATHER, RCU_TABLE_FREE
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# MMU_GATHER ๊ด๋ จ ์ปค๋ ์ค์ ํ์ธ
zcat /proc/config.gz 2>/dev/null | grep -E "CONFIG_MMU_GATHER" || grep -E "CONFIG_MMU_GATHER" /boot/config-$(uname -r) 2>/dev/null
# RCU ํ
์ด๋ธ ํด์ ์ง์ ์ฌ๋ถ ํ์ธ
zcat /proc/config.gz 2>/dev/null | grep CONFIG_MMU_GATHER_RCU_TABLE_FREE || grep CONFIG_MMU_GATHER_RCU_TABLE_FREE /boot/config-$(uname -r) 2>/dev/null
# TLB ํ๋ฌ์ ๊ด๋ จ ์ปค๋ ํต๊ณ ํ์ธ
cat /proc/vmstat | grep -E "tlb_flush|pgfault|pgmajfault"
# ํ์ฌ ์์คํ
์ ํ์ด์ง ํฌ๊ธฐ ํ์ธ
getconf PAGESIZE
# ์ปค๋ ๋น๋ ์ MMU_GATHER ๋ชจ๋ ํฌํจ ์ฌ๋ถ ํ์ธ
cat /lib/modules/$(uname -r)/build/.config 2>/dev/null | grep CONFIG_MMU_GATHER
# TLB ๊ด๋ จ ์ปค๋ ๋ก๊ทธ ํ์ธ
dmesg | grep -iE "tlb|flush|gather" | tail -10
# NUMA ์์คํ
์์์ TLB ํ๋ฌ์ ํต๊ณ
cat /proc/interrupts | grep -i "TLB" 2>/dev/null || echo "TLB ์ธํฐ๋ฝํธ ์ ๋ณด ์์"
# ์ํคํ
์ฒ๋ณ MMU_GATHER ์ ํ ์ต์
ํ์ธ
grep -r "select MMU_GATHER" arch/*/Kconfig 2>/dev/null
# ํ์ด์ง ํ
์ด๋ธ ๊ด๋ จ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ํ์ธ
cat /proc/meminfo | grep -E "PageTables|KernelStack"
# ์ปค๋ ์์ค์์์ MMU gather ๊ด๋ จ ํจ์ ๋ชฉ๋ก ํ์ธ
grep -rn "tlb_gather_mmu\|tlb_finish_mmu\|tlb_remove_page\|tlb_remove_table" mm/mmu_gather.c
# TLB miss ๊ด์ฐฐ
perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses -a -- sleep 5
# ํ์ฌ ํ๋ก์ธ์ค์ minor/major fault ํ์ธ
ps -o pid,minflt,majflt -p $$
# ํ์ด์ง ํดํธ ๋ฐ reclaim ์๋ ฅ ํ์ธ
cat /proc/vmstat | grep -E "pgfault|pgmajfault|allocstall|tlb"
cat /proc/pressure/memory
# ํ์ด์ง ํ
์ด๋ธ ๋คํ(ํ์ฑํ๋ ๊ฒฝ์ฐ)
cat /sys/kernel/debug/kernel_page_tables 2>/dev/null | head -40
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
1. `struct mmu_gather` (include/asm-generic/tlb.h:321)
TLB ํ๋ฌ์ ๋ฐฐ์น์ ํต์ฌ ๊ตฌ์กฐ์ฒด์ ๋๋ค. ์คํ์ ํ ๋น๋๋ฉฐ, ํ์ด์ง ํ ์ด๋ธ ์ ๊ฑฐ ์์ ์ ์์งํ๊ณ ๋ฒ์๋ฅผ ์ถ์ ํฉ๋๋ค.
// include/asm-generic/tlb.h:321-392
struct mmu_gather {
struct mm_struct *mm; // ๋์ ์ฃผ์ ๊ณต๊ฐ
struct mmu_table_batch *batch; // ํ
์ด๋ธ ๋ฐฐ์น (CONFIG_MMU_GATHER_TABLE_FREE)
unsigned long start; // ํ๋ฌ์ ๋์ ๋ฒ์ ์์
unsigned long end; // ํ๋ฌ์ ๋์ ๋ฒ์ ๋
unsigned int fullmm : 1; // ์ ์ฒด mm ์ ๊ฑฐ ์ฌ๋ถ (exit/execve)
unsigned int need_flush_all : 1; // ์ ์ฒด flush ํ์ ์ฌ๋ถ
unsigned int freed_tables : 1; // ํ์ด์ง ๋๋ ํ ๋ฆฌ ์ ๊ฑฐ๋จ
unsigned int delayed_rmap : 1; // ์ง์ฐ๋ rmap ์ ๊ฑฐ ๋ณด์ ์ฌ๋ถ
unsigned int cleared_ptes : 1; // PTE ๋ ๋ฒจ ์ ๊ฑฐ ๊ธฐ๋ก
unsigned int cleared_pmds : 1; // PMD ๋ ๋ฒจ ์ ๊ฑฐ ๊ธฐ๋ก
unsigned int cleared_puds : 1; // PUD ๋ ๋ฒจ ์ ๊ฑฐ ๊ธฐ๋ก
unsigned int cleared_p4ds : 1; // P4D ๋ ๋ฒจ ์ ๊ฑฐ ๊ธฐ๋ก
unsigned int vma_exec : 1; // VM_EXEC ํ๋๊ทธ ์ถ์
unsigned int vma_huge : 1; // VM_HUGETLB ํ๋๊ทธ ์ถ์
unsigned int vma_pfn : 1; // VM_PFNMAP/VM_MIXEDMAP ์ถ์
unsigned int unshared_tables : 1; // ๊ณต์ ํด์ ๋ ํ
์ด๋ธ ์กด์ฌ
unsigned int fully_unshared_tables : 1; // ์์ ํ ํด์ ๋ ํ
์ด๋ธ
unsigned int batch_count; // ํ์ฌ ๋ฐฐ์น ์
struct mmu_gather_batch *active; // ํ์ฌ ํ์ฑ ๋ฐฐ์น
struct mmu_gather_batch local; // ์คํ ํ ๋น ๋ก์ปฌ ๋ฐฐ์น
struct page *__pages[MMU_GATHER_BUNDLE]; // ๋ก์ปฌ ํ์ด์ง ๋ฐฐ์ด
unsigned int page_size; // ํ์ฌ ํ์ด์ง ํฌ๊ธฐ (CONFIG_MMU_GATHER_PAGE_SIZE)
};
ํ๋ ์ค๋ช :
start, end: TLB ํ๋ฌ์๊ฐ ํ์ํ ์ฃผ์ ๋ฒ์. __tlb_adjust_range()๋ก ๊ฐฑ์ ๋จ.fullmm: exit(), execve()์ฒ๋ผ ์ ์ฒด ์ฃผ์ ๊ณต๊ฐ์ ์ ๊ฑฐํ ๋ true. ๋ฒ์ ์ถ์ ์ ๊ฑด๋๋ฐ๊ณ ์ ์ฒด flush ์ํ.freed_tables, cleared_ptes/pmds/puds/p4ds: ์ด๋ค ๋ ๋ฒจ์ ์ํธ๋ฆฌ๊ฐ ์ ๊ฑฐ๋์๋์ง ๊ธฐ๋ก. tlb_flush_mmu_tlbonly()์์ flush ํ์ ์ฌ๋ถ ๊ฒฐ์ .delayed_rmap: SMP์์ rmap ์ ๊ฑฐ๋ฅผ TLB flush ํ๋ก ๋ฏธ๋ฃธ. tlb_flush_rmaps()์์ ์ฒ๋ฆฌ.active: ํ์ฌ ์ฑ์ฐ๊ณ ์๋ ๋ฐฐ์น. local์์ ์์ํ์ฌ ํ์ ์ ์ ๋ฐฐ์น ํ ๋น.batch_count: ํ ๋น๋ ๋ฐฐ์น ์. MAX_GATHER_BATCH_COUNT(โ10K ํ์ด์ง/๋ฐฐ์น ํฌ๊ธฐ)๋ก ์ ํํ์ฌ soft lockup ๋ฐฉ์ง.2. `struct mmu_gather_batch` (include/asm-generic/tlb.h:272)
์ธ์ฝ๋ฉ๋ ํ์ด์ง ํฌ์ธํฐ๋ฅผ ์ ์ฅํ๋ ๋จ์ผ linked list ๋ ธ๋์ ๋๋ค.
// include/asm-generic/tlb.h:272-277
struct mmu_gather_batch {
struct mmu_gather_batch *next; // ๋ค์ ๋ฐฐ์น (linked list)
unsigned int nr; // ํ์ฌ ์ ์ฅ๋ ์ํธ๋ฆฌ ์
unsigned int max; // ์ต๋ ์ํธ๋ฆฌ ์
struct encoded_page *encoded_pages[]; // ์ธ์ฝ๋ฉ๋ ํ์ด์ง ๋ฐฐ์ด (flexible array)
};
์ค๋ช :
encoded_pages[]: ํ์ด์ง ํฌ์ธํฐ์ ํ๋๊ทธ๋ฅผ ๋นํธ ๋ง์คํนํ์ฌ ์ ์ฅ. ํ์ 2๋นํธ๋ ENCODED_PAGE_BIT_DELAY_RMAP(bit 1)๊ณผ ENCODED_PAGE_BIT_NR_PAGES_NEXT(bit 2)๋ก ์ฌ์ฉ.MAX_GATHER_BATCH: (PAGE_SIZE - sizeof(struct mmu_gather_batch)) / sizeof(void *). 4KB ํ์ด์ง ๊ธฐ์ค ์ฝ 512 ์ํธ๋ฆฌ.local: ์คํ์ ์ง์ ํ ๋น๋๋ ์ฒซ ๋ฒ์งธ ๋ฐฐ์น. ์ถ๊ฐ ๋ฐฐ์น๋ __get_free_page(GFP_NOWAIT)๋ก ๋์ ํ ๋น.3. `struct mmu_table_batch` (include/asm-generic/tlb.h:205)
ํ์ด์ง ํ
์ด๋ธ ๋๋ ํ ๋ฆฌ ํด์ ๋ฅผ ์ํ ๋ฐฐ์น ๊ตฌ์กฐ์ฒด์
๋๋ค. CONFIG_MMU_GATHER_RCU_TABLE_FREE ํ์ฑํ ์ RCU grace period ํ ํด์ ๋ฉ๋๋ค.
// include/asm-generic/tlb.h:205-211
struct mmu_table_batch {
struct rcu_head rcu; // RCU ํด์ ํค๋ (CONFIG_MMU_GATHER_RCU_TABLE_FREE)
unsigned int nr; // ํ์ฌ ์ ์ฅ๋ ํ
์ด๋ธ ์
void *tables[]; // ํ
์ด๋ธ ํฌ์ธํฐ ๋ฐฐ์ด (flexible array)
};
์ค๋ช :
MAX_TABLE_BATCH: (PAGE_SIZE - sizeof(struct mmu_table_batch)) / sizeof(void *).4. `struct encoded_page` (include/linux/mm_types.h:238)
ํ์ด์ง ํฌ์ธํฐ์ ํ๋๊ทธ๋ฅผ ๋นํธ ๋ง์คํนํ์ฌ ์ ์ฅํ๋ ์ธ์ฝ๋ฉ ํฌ์ธํฐ์ ๋๋ค.
// include/linux/mm_types.h:238-279
struct encoded_page;
#define ENCODED_PAGE_BITS 3ul // ์ฌ์ฉ ๊ฐ๋ฅํ ๋นํธ ์
#define ENCODED_PAGE_BIT_DELAY_RMAP 1ul // TLB flush ํ rmap ์ ๊ฑฐ ์ง์ฐ
#define ENCODED_PAGE_BIT_NR_PAGES_NEXT 2ul // ๋ค์ ์ํธ๋ฆฌ๊ฐ nr_pages ๊ฐ
// ์ธ์ฝ๋ฉ: ํ์ด์ง ํฌ์ธํฐ ํ์ ๋นํธ์ ํ๋๊ทธ ์ถ๊ฐ
static __always_inline struct encoded_page *encode_page(struct page *page, unsigned long flags)
{
BUILD_BUG_ON(flags > ENCODED_PAGE_BITS);
return (struct encoded_page *)(flags | (unsigned long)page);
}
// ๋์ฝ๋ฉ: ํ์ ๋นํธ ์ ๊ฑฐํ์ฌ ์ค์ ํ์ด์ง ํฌ์ธํฐ ๋ณต์
static inline struct page *encoded_page_ptr(struct encoded_page *page)
{
return (struct page *)(~ENCODED_PAGE_BITS & (unsigned long)page);
}
// include/linux/mm_types.h:260-268
static inline unsigned long encoded_page_flags(struct encoded_page *page)
{
return ENCODED_PAGE_BITS & (unsigned long)page;
}
์ค๋ช :
ENCODED_PAGE_BIT_DELAY_RMAP (bit 1): SMP ํ๊ฒฝ์์ rmap ์ ๊ฑฐ๋ฅผ TLB flush ์ดํ๋ก ๋ฏธ๋ฃธ. folio_remove_rmap_ptes() ํธ์ถ์ด ์ง์ฐ๋จ.ENCODED_PAGE_BIT_NR_PAGES_NEXT (bit 2): ์ฐ์๋ ์ฌ๋ฌ ํ์ด์ง(folio)๋ฅผ ํ๋์ ๋ฐฐ์น ์ํธ๋ฆฌ๋ก ํํ. ๋ค์ ์ํธ๋ฆฌ์ ์ค์ ํ์ด์ง ์๊ฐ ์ธ์ฝ๋ฉ๋จ.5. `encode_page` / `encode_nr_pages` (include/linux/mm_types.h:254-279)
ํ์ด์ง ์ธ์ฝ๋ฉ/๋์ฝ๋ฉ ์ ํธ๋ฆฌํฐ ํจ์์ ๋๋ค.
// include/linux/mm_types.h:270-278
// ํ์ด์ง ์ ์ธ์ฝ๋ฉ: ์์ ๋นํธ์ ํ์ด์ง ์ ์ ์ฅ
static __always_inline struct encoded_page *encode_nr_pages(unsigned long nr)
{
VM_WARN_ON_ONCE((nr << 2) >> 2 != nr); // 30๋นํธ ์ด๊ณผ ๊ฒ์ฆ
return (struct encoded_page *)(nr << 2);
}
// ์ธ์ฝ๋ฉ๋ ํ์ด์ง ์ ๋์ฝ๋ฉ
static __always_inline unsigned long encoded_nr_pages(struct encoded_page *page)
{
return ((unsigned long)page) >> 2;
}
ํต์ฌ ํจ์
1. `tlb_gather_mmu()` โ ์ด๊ธฐํ
// mm/mmu_gather.c:443-446
void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm)
{
__tlb_gather_mmu(tlb, mm, false); // fullmm = false
}
mmu_gather ๊ตฌ์กฐ์ฒด๋ฅผ ์ด๊ธฐํ. ํ์ด์ง ํ
์ด๋ธ ์ ๊ฑฐ ์์
์์ ์ ์ ํธ์ถ.__tlb_gather_mmu()์์ mm ์ฐ๊ฒฐ, ๋ฐฐ์น ์ด๊ธฐํ(local ์ฌ์ฉ), ๋ฒ์ ์ด๊ธฐํ(__tlb_reset_range()), inc_tlb_flush_pending() ํธ์ถ. - tlb_gather_mmu_fullmm(): fullmm=true. ์ ์ฒด ์ฃผ์ ๊ณต๊ฐ ์ ๊ฑฐ(exit/execve) ์ ์ฌ์ฉ.
- tlb_gather_mmu_vma(): ๋จ์ผ VMA ๋์. tlb_update_vma_flags()์ HugeTLB ํ์ด์ง ํฌ๊ธฐ ์ค์ ํฌํจ.
2. `__tlb_remove_folio_pages()` โ ํ์ด์ง ์ ๊ฑฐ ๊ธฐ๋ก
// mm/mmu_gather.c:207-212
bool __tlb_remove_folio_pages(struct mmu_gather *tlb, struct page *page,
unsigned int nr_pages, bool delay_rmap)
{
return __tlb_remove_folio_pages_size(tlb, page, nr_pages, delay_rmap,
PAGE_SIZE);
}
tlb_flush_mmu()๋ฅผ ์ ๋ฐ. - nr_pages == 1: ๋จ์ผ ํ์ด์ง โ encode_page(page, flags)๋ก ์ ์ฅ.
- nr_pages > 1: folio โ ENCODED_PAGE_BIT_NR_PAGES_NEXT ์ค์ ํ ๋ค์ ์ํธ๋ฆฌ์ encode_nr_pages(nr_pages) ์ ์ฅ.
- batch->nr >= batch->max - 1: ๋ฐฐ์น ๊ฐ๋ ์ฐธ โ tlb_next_batch()๋ก ์ ๋ฐฐ์น ํ ๋น ์คํจ ์ true ๋ฐํ โ ํธ์ถ๋ถ์์ flush ์ ๋ฐ.
true = ์ฆ์ flush ํ์, false = ๊ณ์ ๋ฐฐ์น ๊ฐ๋ฅ.3. `tlb_flush_mmu()` โ TLB ํ๋ฌ์ ์คํ
// mm/mmu_gather.c:402-406
void tlb_flush_mmu(struct mmu_gather *tlb)
{
tlb_flush_mmu_tlbonly(tlb); // ์ํคํ
์ฒ๋ณ TLB flush
tlb_flush_mmu_free(tlb); // ํ
์ด๋ธ ๋ฐ ํ์ด์ง ํด์
}
tlb_remove_page() ๋ฑ์์ ๋ฐฐ์น๊ฐ ๊ฐ๋ ์ฐผ์ ๋ ํธ์ถ. ์ค์ TLB flush์ ๋ฆฌ์์ค ํด์ ๋ฅผ ์ํ. 1. tlb_flush_mmu_tlbonly(): freed_tables ๋๋ cleared_* ํ๋๊ทธ๊ฐ ์ค์ ๋ ๊ฒฝ์ฐ์๋ง tlb_flush() ํธ์ถ. flush ํ __tlb_reset_range()๋ก ๋ฒ์ ๋ฆฌ์
.
2. tlb_flush_mmu_free(): tlb_table_flush()๋ก ํ
์ด๋ธ ๋ฐฐ์น ํด์ + tlb_batch_pages_flush()๋ก ํ์ด์ง ๋ฐฐ์น ํด์ .
4. `tlb_finish_mmu()` โ ์ต์ข ์ ๋ฆฌ
// mm/mmu_gather.c:496-536
void tlb_finish_mmu(struct mmu_gather *tlb)
1. mm_tlb_flush_nested() ํ์ธ: ๋ณ๋ ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ๋ฒ์์ PTE ๋ณ๊ฒฝ ์ ๊ฐ์ ์ ์ฒด flush (fullmm=1, __tlb_reset_range(), freed_tables=1).
2. tlb_flush_mmu(): ์ต์ข
flush ์คํ.
3. tlb_batch_list_free(): ๋์ ํ ๋น๋ ๋ฐฐ์น ํ์ด์ง ํด์ .
4. dec_tlb_flush_pending(): ๋๊ธฐ ์นด์ดํธ ๊ฐ์.
๋ณด์กฐ ๊ฒฝ๋ก
// include/asm-generic/tlb.h:495-507
static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb)
{
if (!(tlb->freed_tables || tlb->cleared_ptes || tlb->cleared_pmds ||
tlb->cleared_puds || tlb->cleared_p4ds || tlb->unshared_tables))
return;
tlb_flush(tlb);
__tlb_reset_range(tlb);
}
tlb_flush_mmu_tlbonly()๋ freed_tables, cleared_*, unshared_tables๊ฐ ์์ ๋๋ง invalidateํ๊ณ ๋ฒ์๋ฅผ ๋ฆฌ์
ํฉ๋๋ค.tlb_remove_tlb_entry()๋ mm/memory.c:1695-1705์์ ptep_get_and_clear_full() ๋ค์ ํธ์ถ๋์ด PTE ์ ๊ฑฐ ๋ฒ์๋ฅผ ๊ธฐ๋กํฉ๋๋ค.tlb_free_vmas()๋ VM_PFNMAP / VM_MIXEDMAP์ด ์์ธ ๋ฒ์์์ VMA๋ฅผ unlinkํ๊ธฐ ์ ์ TLB flush๋ฅผ ์๋น๊น๋๋ค.5. `tlb_remove_table()` โ ํ ์ด๋ธ ๋ฐฐ์น
// mm/mmu_gather.c:363-380
void tlb_remove_table(struct mmu_gather *tlb, void *table)
CONFIG_MMU_GATHER_TABLE_FREE ํ์ฑํ ์ ์ฌ์ฉ. 1. ๋ฐฐ์น ํ ๋น: __get_free_page(GFP_NOWAIT)๋ก ์ ๋ฐฐ์น ํ ๋น ์คํจ ์ ์ฆ์ tlb_table_invalidate() + ๋จ์ผ ํด์ .
2. ๋ฐฐ์น ์ถ๊ฐ: batch->tables[batch->nr++] = table.
3. ๋ฐฐ์น ๊ฐ๋ ์ฐธ: tlb_table_flush()๋ก ์ฆ์ flush.
6. `tlb_flush_rmaps()` โ ์ง์ฐ rmap ์ ๊ฑฐ
// mm/mmu_gather.c:84-93
void tlb_flush_rmaps(struct mmu_gather *tlb, struct vm_area_struct *vma)
delayed_rmap์ด ์ค์ ๋ ๊ฒฝ์ฐ, TLB flush ์๋ฃ ํ rmap ์ ๊ฑฐ๋ฅผ ์ํ.tlb->local๊ณผ tlb->active ๋ฐฐ์น์ ๋ชจ๋ ์ํธ๋ฆฌ๋ฅผ ์ํํ๋ฉฐ ENCODED_PAGE_BIT_DELAY_RMAP์ด ์ค์ ๋ ํ์ด์ง์ ๋ํด folio_remove_rmap_ptes() ํธ์ถ.7. `tlb_next_batch()` โ ์ ๋ฐฐ์น ํ ๋น
// mm/mmu_gather.c:20-50
static bool tlb_next_batch(struct mmu_gather *tlb)
- tlb->delayed_rmap && tlb->active != &tlb->local: ์ง์ฐ rmap ๋ณด์ ์ ์ ๋ฐฐ์น ์์ ๋ถ๊ฐ (๊ธฐ์กด ๋ฐฐ์น์์ ์ฒ๋ฆฌํด์ผ ํจ).
- batch->next: ์ด๋ฏธ ํ ๋น๋ ๋ค์ ๋ฐฐ์น๊ฐ ์์ผ๋ฉด ์ด๋.
- batch_count == MAX_GATHER_BATCH_COUNT: ๋ฐฐ์น ์ ์ ํ ๋๋ฌ โ ํ ๋น ๊ฑฐ๋ถ (soft lockup ๋ฐฉ์ง).
- __get_free_page(GFP_NOWAIT) ์คํจ: ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ ํ ๋น ๊ฑฐ๋ถ.
ํธ์ถ ํ๋ฆ
์ฌ์ฉ์: munmap() / exit() / madvise(MADV_DONTNEED)
โ
โผ
tlb_gather_mmu(tlb, mm) โ ์ด๊ธฐํ (์คํ ํ ๋น, ๋ฐฐ์น ์ด๊ธฐํ)
โ
โผ
tlb_start_vma(tlb, vma) โ ์บ์ ํ๋ฌ์, VMA ํ๋๊ทธ ๊ธฐ๋ก
โ
โผ (VMA ์ํํ๋ฉฐ)
zap_pte_range(tlb, vma, pmd, addr, end)
โ
โโโ tlb_remove_tlb_entry(tlb, ptep, addr)
โ โโโ tlb_flush_pte_range(tlb, addr, PAGE_SIZE) โ ๋ฒ์ ๊ฐฑ์
โ
โโโ __tlb_remove_folio_pages(tlb, page, nr_pages, delay_rmap)
โโโ __tlb_remove_folio_pages_size()
โโโ ๋ฐฐ์น์ ์ธ์ฝ๋ฉ๋ ํ์ด์ง ์ ์ฅ
โโโ ๋ฐฐ์น ๊ฐ๋ ์ฐธ โ tlb_flush_mmu() ์ ๋ฐ
โ
โผ
tlb_end_vma(tlb, vma) โ VMA ๊ฒฝ๊ณ์์ ํ๋ฌ์ (MERGE_VMAS ๋ฏธํ์ฑํ ์)
โ
โผ
tlb_finish_mmu(tlb) โ ์ต์ข
์ ๋ฆฌ
โโโ mm_tlb_flush_nested() ํ์ธ โ ๋ณ๋ ฌ flush ํ์ ์ fullmm ๊ฐ์
โโโ tlb_flush_mmu()
โ โโโ tlb_flush_mmu_tlbonly() โ flush_tlb_mm() ๋๋ flush_tlb_range()
โ โโโ tlb_flush_mmu_free()
โ โโโ tlb_table_flush() โ RCU/์ง์ ํ
์ด๋ธ ํด์
โ โโโ tlb_batch_pages_flush() โ free_pages_and_swap_cache()
โโโ tlb_batch_list_free() โ ๋์ ๋ฐฐ์น ํด์
์กฐ๊ฑด๋ณ ๋น๊ต
MMU_GATHER_* ์ปค๋ ์ต์ ๋น๊ต
| ์ต์ | ์ค๋ช | ํ์ฑํ ์ํคํ ์ฒ |
|---|---|---|
| `MMU_GATHER_TABLE_FREE` | ํ์ด์ง ํ ์ด๋ธ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ฐฐ์น๋ก ํด์ | x86, ARM64, RISC-V, PowerPC, s390 ๋ฑ ๋๋ถ๋ถ |
| `MMU_GATHER_RCU_TABLE_FREE` | RCU grace period ํ ํ ์ด๋ธ ํด์ . ์ํํธ์จ์ด ์์ปค(gup_fast) ๋๊ธฐํ | x86(SMP), ARM64(SMP), RISC-V(SMP), s390, PowerPC |
| `MMU_GATHER_NO_RANGE` | ๋ฒ์ ๊ธฐ๋ฐ ํ๋ฌ์ ์์ด ์ ์ฒด mm ํ๋ฌ์๋ง ์ฌ์ฉ | nios2, m68k, microblaze, OpenRISC |
| `MMU_GATHER_MERGE_VMAS` | VMA ๊ฒฝ๊ณ์์ ํ๋ฌ์๋ฅผ ๊ฑด๋๋ฐ๊ณ ๋ฒ์ ๋ณํฉ | x86, PowerPC, s390, SPARC, LoongArch |
| `MMU_GATHER_NO_FLUSH_CACHE` | `flush_cache_range()` ํธ์ถ ์ ํจ | s390, SPARC |
| `MMU_GATHER_NO_GATHER` | ํ์ด์ง ๋ฐฐ์น ์์ด ์ฆ์ ํด์ . ์ํคํ ์ฒ๊ฐ ์์ฒด flush ์ง์ ์ | s390 |
| `MMU_GATHER_PAGE_SIZE` | ํ์ด์ง ํฌ๊ธฐ ๋ณ๊ฒฝ ๊ฐ์ง ( HugeTLB) | PowerPC |
์ํคํ ์ฒ๋ณ TLB flush ๋ฐฉ์ ๋น๊ต
| ์ํคํ ์ฒ | flush ๋ฐฉ์ | ํน์ง |
|---|---|---|
| x86_64 | `flush_tlb_mm()`, `flush_tlb_range()` | INVLPG ๋ช ๋ น์ด ์ฌ์ฉ. ์ ์ฒด flush ์ CR3 ์ฌ๋ก๋. MERGE_VMAS๋ก ๋ฒ์ ์ต์ ํ. |
| ARM64 | `tlbi vmalle1is`, `tlbi vae1is` | HW ์ง์ cross-CPU ๋๊ธฐํ. fullmm ์ฌ์ฉ ์ IPI ์คํธ ๋ฐฉ์ง. |
| s390 | ๋๊ธฐ์ ์๊ฒฉ flush | NO_GATHER๋ก ์ฆ์ ํด์ . ์ํํธ์จ์ด ์์ปค ๋ถํ์. |
| RISC-V | `sfence.vma` | SMP ์ RCU_TABLE_FREE๋ก IPI ๋นํ์ฑํ ๊ตฌ๊ฐ ๋๊ธฐํ. |
๋ฐฐ์น ๋์ ๋น๊ต
| ์ํฉ | ๋์ | ์ค๋ช |
|---|---|---|
| ๋จ์ผ ํ์ด์ง ์ ๊ฑฐ | `encode_page(page, 0)` โ `batch->encoded_pages[nr++]` | ํ๋๊ทธ ์์ด ๋จ์ ์ ์ฅ |
| folio ์ฌ๋ฌ ํ์ด์ง | `encode_page(page, NR_PAGES_NEXT)` + `encode_nr_pages(nr)` | ์ฐ์ 2์ํธ๋ฆฌ ์ฌ์ฉ |
| SMP rmap ์ง์ฐ | `tlb_delay_rmap(tlb)` โ `ENCODED_PAGE_BIT_DELAY_RMAP` ์ค์ | flush ํ rmap ์ ๊ฑฐ |
| ๋ฐฐ์น ๊ฐ๋ ์ฐธ | `tlb_next_batch()` โ ์ ๋ฐฐ์น ํ ๋น ๋๋ flush ์ ๋ฐ | `MAX_GATHER_BATCH_COUNT` ์ ํ |
| ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ | `__get_free_page(GFP_NOWAIT)` ์คํจ โ ๊ธฐ์กด ๋ฐฐ์น์์ flush | ์งํ ๋ณด์ฅ |
๋ํ VMA ๊ฒฝ๋ก ๋น๊ต
| ๊ฒฝ๋ก | ์ง์ ํจ์ | mmu_gather ์ฌ์ฉ | ํต์ฌ ํ๋ฆ |
|---|---|---|---|
| `munmap()` | `unmap_region()` | `tlb_gather_mmu()` โ `tlb_finish_mmu()` | `unmap_vmas()` โ `free_pgtables()` โ page table tear-down |
| `mprotect()` | `do_mprotect_pkey()` | `tlb_gather_mmu()` โ `mprotect_fixup()` โ `tlb_finish_mmu()` | `change_protection()` โ `walk_page_range()` โ PTE ๊ถํ ๊ฐฑ์ |
| `MADV_COLD` / `MADV_PAGEOUT` | `madvise_cold()` / `madvise_pageout()` | `tlb_gather_mmu()` โ `tlb_start_vma()` / `tlb_end_vma()` โ `tlb_finish_mmu()` | LRU ์ ๊ทผ ๋นํธ ์ ๋ฆฌ ํ TLB invalidate |
| `MADV_FREE` | `madvise_free_single_vma()` | `tlb_start_vma()` / `tlb_end_vma()` | `clear_young_dirty_ptes()` + `tlb_remove_tlb_entries()` |
| NUMA balancing | `change_prot_numa()` | `tlb_gather_mmu()` โ `tlb_finish_mmu()` | PROT_NONE ๋งํน ํ `change_protection()` |
| exec stack relocation | `relocate_vma_down()` | `tlb_gather_mmu()` โ `free_pgd_range()` โ `tlb_finish_mmu()` | ์ ์์น๋ก ์ด๋ ํ ์์ ํ์ด์ง ํ ์ด๋ธ ์ ๋ฆฌ |
์ํคํ ์ฒ๋ณ invalidate ๋ฐฉ์
| ์ํคํ ์ฒ | ๋ํ primitive | ๋๊ธฐ ๋ฒ์ | ๋น๊ณ |
|---|---|---|---|
| x86_64 | `invlpg`, `flush_tlb_mm()`, `flush_tlb_range()` | local + IPI shootdown | PCID๊ฐ ์์ผ๋ฉด ํ๊ทธ ์ ํ์ผ๋ก ๋น์ฉ์ ์ค์ |
| ARM64 | `TLBI ...`, `dsb ish`, `isb` | inner-shareable broadcast | ASID ๊ธฐ๋ฐ์ผ๋ก range/fullmm ๊ตฌ๋ถ |
| RISC-V | `sfence.vma` | per-hart ๋๋ IPI ๋๊ธฐํ | ASID ์ธ์๊ฐ ์์ผ๋ฉด ๋ฒ์ ์ถ์ ๊ฐ๋ฅ |
| s390 | synchronous remote flush | ๋๊ธฐ์ CPU sync | `MMU_GATHER_NO_GATHER`์ ํจ๊ป ์ฐ์ด๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ |