๏ปฟ# madvise / process_madvise

๊ด€๋ จ ์†Œ์Šค: mm/madvise.c, include/uapi/asm-generic/mman-common.h, include/linux/mm.h

๊ฐœ์š” (Overview)

madvise()๋Š” ์‚ฌ์šฉ์ž ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ปค๋„์—๊ฒŒ ํŠน์ • ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์— ๋Œ€ํ•œ ํžŒํŠธ(hint)๋ฅผ ์ œ๊ณตํ•˜๋Š” ์‹œ์Šคํ…œ ํ˜ธ์ถœ์ด๋‹ค. readahead ์ •์ฑ… ๋ณ€๊ฒฝ, ํŽ˜์ด์ง€ ํ•ด์ œ, ์Šค์™‘์•„์›ƒ, THP ๋ณ‘ํ•ฉ, KSM ๋ณ‘ํ•ฉ ๋“ฑ ๋‹ค์–‘ํ•œ ๋™์ž‘์„ ๋‹จ์ผ ์ง„์ž…์ ์„ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•œ๋‹ค. Linux 7.0์—์„œ๋Š” madvise_behavior ๊ตฌ์กฐ์ฒด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ locking, VMA walk, ๊ฐ behavior๋ณ„ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ์ฒด๊ณ„ํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, process_madvise()๋ฅผ ํ†ตํ•ด ์›๊ฒฉ ํ”„๋กœ์„ธ์Šค์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ต์‹ฌ ์†Œ์Šค ํŒŒ์ผ:

  • mm/madvise.c โ€” ์‹œ์Šคํ…œ ํ˜ธ์ถœ ๊ตฌํ˜„, behavior ๋ถ„๊ธฐ, VMA walk, ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์กฐ์ž‘
  • include/uapi/asm-generic/mman-common.h โ€” MADV_* ์ƒ์ˆ˜ ์ •์˜ (0~25, 100~103)
  • include/linux/mm.h โ€” VM_* ํ”Œ๋ž˜๊ทธ (VM_DONTCOPY, VM_WIPEONFORK, VM_SEQ_READ, VM_RAND_READ, VM_DONTDUMP)
  • include/linux/mman.h โ€” VMA ์ˆ˜์ • ๊ด€๋ จ ํ—ฌํผ

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

    # ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค์˜ madvise ์‚ฌ์šฉ ํ†ต๊ณ„ ํ™•์ธ (perf)
    perf stat -e 'syscalls:sys_enter_madvise' -a sleep 1
    
    # VMA flag ๋ณ€๊ฒฝ ์ถ”์  (ftrace)
    echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_madvise/enable
    cat /sys/kernel/debug/tracing/trace_pipe
    
    # madvise ๊ด€๋ จ ์ปค๋„ ์‹ฌ๋ณผ ํ™•์ธ
    cat /proc/kallsyms | grep -E 'do_madvise|process_madvise|madvise_walk'
    
    # process_madvise syscall ์ถ”์ 
    perf stat -e 'syscalls:sys_enter_process_madvise' -a sleep 1
    
    # ํŠน์ • ํ”„๋กœ์„ธ์Šค์˜ VMA flags ํ™•์ธ (madvise ๊ฒฐ๊ณผ ๊ด€์ฐฐ)
    cat /proc/<PID>/maps | head -20
    
    # THP collapse ์ƒํƒœ ํ™•์ธ (MADV_COLLAPSE ๊ด€๋ จ)
    cat /sys/kernel/mm/transparent_hugepage/enabled
    
    # KSM ์ƒํƒœ ํ™•์ธ (MADV_MERGEABLE ๊ด€๋ จ)
    cat /sys/kernel/mm/ksm/run
    cat /sys/kernel/mm/ksm/pages_shared
    
    # ๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜ ์ƒํƒœ ํ™•์ธ (MADV_COLD/PAGEOUT ๊ด€๋ จ)
    cat /proc/vmstat | grep -E 'pgscan|pgsteal|pgdeactivate'

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

    madvise_behavior โ€” behavior ์ฒ˜๋ฆฌ ์ƒํƒœ ์ปจํ…Œ์ด๋„ˆ

    // mm/madvise.c:66-82
    struct madvise_behavior {
    struct mm_struct *mm;                              // ๋Œ€์ƒ ํ”„๋กœ์„ธ์Šค์˜ mm_struct
    int behavior;                                      // MADV_* ์ƒ์ˆ˜ ๊ฐ’
    struct mmu_gather *tlb;                             // TLB batch ์ˆ˜์ง‘๊ธฐ
    enum madvise_lock_mode lock_mode;                   // ์ž ๊ธˆ ๋ชจ๋“œ ๊ฒฐ์ •
    struct anon_vma_name *anon_name;                    // ์ต๋ช… VMA ์ด๋ฆ„ (__MADV_SET_ANON_VMA_NAME์šฉ)
    
    struct madvise_behavior_range range;                // ํ˜„์žฌ ์ฒ˜๋ฆฌ ์ค‘์ธ ์ฃผ์†Œ ๋ฒ”์œ„
    struct vm_area_struct *prev;                        // ์ด์ „ VMA (VMA split/merge์šฉ)
    struct vm_area_struct *vma;                         // ํ˜„์žฌ ๋Œ€์ƒ VMA
    bool lock_dropped;                                  // mmap_lock ํ•ด์ œ ์—ฌ๋ถ€ ์ถ”์ 
    };
  • lock_dropped: MADV_WILLNEED์—์„œ vfs_fadvise() ํ˜ธ์ถœ ์‹œ mmap_lock์„ ํ•ด์ œํ•˜๋ฏ€๋กœ, ์ดํ›„ VMA ์žฌํƒ์ƒ‰์ด ํ•„์š”ํ•จ
  • lock_mode: behavior๋ณ„๋กœ MADVISE_NO_LOCK, MADVISE_MMAP_READ_LOCK, MADVISE_MMAP_WRITE_LOCK, MADVISE_VMA_READ_LOCK ์ค‘ ์„ ํƒ
  • madvise_lock_mode โ€” ์ž ๊ธˆ ๋ชจ๋“œ ์—ด๊ฑฐํ˜•

    // mm/madvise.c:54-59
    enum madvise_lock_mode {
    MADVISE_NO_LOCK,           // ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฅ˜ ์ฃผ์ž… ์‹œ ์ž ๊ธˆ ๋ถˆํ•„์š”
    MADVISE_MMAP_READ_LOCK,    // mmap_lock ์ฝ๊ธฐ ์ž ๊ธˆ (WILLNEED, COLD, PAGEOUT ๋“ฑ)
    MADVISE_MMAP_WRITE_LOCK,   // mmap_lock ์“ฐ๊ธฐ ์ž ๊ธˆ (vm_flags ๋ณ€๊ฒฝ ์‹œ)
    MADVISE_VMA_READ_LOCK,     // VMA ์ฝ๊ธฐ ์ž ๊ธˆ (DONTNEED, FREE, GUARD ๋“ฑ)
    };

    madvise_behavior_range โ€” ์ฒ˜๋ฆฌ ๋ฒ”์œ„

    // mm/madvise.c:61-64
    struct madvise_behavior_range {
    unsigned long start;   // ์‹œ์ž‘ ๊ฐ€์ƒ ์ฃผ์†Œ (ํŽ˜์ด์ง€ ์ •๋ ฌ)
    unsigned long end;     // ์ข…๋ฃŒ ๊ฐ€์ƒ ์ฃผ์†Œ (exclusive)
    };

    madvise_walk_private โ€” ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” walk ๋น„๊ณต๊ฐœ ๋ฐ์ดํ„ฐ

    // mm/madvise.c:49-52
    struct madvise_walk_private {
    struct mmu_gather *tlb;   // TLB ์ˆ˜์ง‘ ์ปจํ…์ŠคํŠธ
    bool pageout;             // true = MADV_PAGEOUT, false = MADV_COLD
    };

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

    do_madvise โ€” ์‹œ์Šคํ…œ ํ˜ธ์ถœ ๋ฉ”์ธ ์ง„์ž…์ 

    // mm/madvise.c:2012-2033
    int do_madvise(struct mm_struct *mm, unsigned long start,
    size_t len_in, int behavior)
    {
    int error;
    struct mmu_gather tlb;
    struct madvise_behavior madv_behavior = {
    .mm = mm,
    .behavior = behavior,
    .tlb = &tlb,
    };
    
    if (madvise_should_skip(start, len_in, behavior, &error))
    return error;
    error = madvise_lock(&madv_behavior);       // behavior๋ณ„ ์ž ๊ธˆ ํš๋“
    if (error)
    return error;
    madvise_init_tlb(&madv_behavior);           // TLB batch ์ดˆ๊ธฐํ™”
    error = madvise_do_behavior(start, len_in, &madv_behavior);
    madvise_finish_tlb(&madv_behavior);         // TLB batch ํ•ด์ œ
    madvise_unlock(&madv_behavior);             // ์ž ๊ธˆ ํ•ด์ œ
    
    return error;
    }

    ํ๋ฆ„: ๊ฒ€์ฆ โ†’ ์ž ๊ธˆ โ†’ TLB ์ดˆ๊ธฐํ™” โ†’ behavior ์ฒ˜๋ฆฌ โ†’ TLB ๋งˆ๋ฌด๋ฆฌ โ†’ ์ž ๊ธˆ ํ•ด์ œ

    process_madvise โ€” pidfd ๊ธฐ๋ฐ˜ ์›๊ฒฉ ์ ์šฉ ์ง„์ž…์ 

    // mm/madvise.c:2107-2168
    SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec,
    		size_t, vlen, int, behavior, unsigned int, flags)
    {
    	ssize_t ret;
    	struct iovec iovstack[UIO_FASTIOV];
    	struct iovec *iov = iovstack;
    	struct iov_iter iter;
    	struct task_struct *task;
    	struct mm_struct *mm;
    	unsigned int f_flags;
    
    	if (flags != 0) {
    		ret = -EINVAL;
    		goto out;
    	}
    
    	ret = import_iovec(ITER_DEST, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter);
    	if (ret < 0)
    		goto out;
    
    	task = pidfd_get_task(pidfd, &f_flags);
    	if (IS_ERR(task)) {
    		ret = PTR_ERR(task);
    		goto free_iov;
    	}
    
    	/* ASLR ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์œ ์ถœ์„ ๋ง‰๊ธฐ ์œ„ํ•ด PTRACE_MODE_READ๋ฅผ ์š”๊ตฌํ•œ๋‹ค. */
    	mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
    	if (IS_ERR(mm)) {
    		ret = PTR_ERR(mm);
    		goto release_task;
    	}
    
    	/* ์›๊ฒฉ ์ฃผ์†Œ ๊ณต๊ฐ„์„ ๊ฑด๋“œ๋ฆด ๋•Œ๋งŒ behavior ์ œํ•œ์„ ์ ์šฉํ•œ๋‹ค. */
    	if (mm != current->mm && !process_madvise_remote_valid(behavior)) {
    		ret = -EINVAL;
    		goto release_mm;
    	}
    
    	/* ์›๊ฒฉ ๋Œ€์ƒ์—๋Š” CAP_SYS_NICE๊ฐ€ ํ•„์š”ํ•˜๋‹ค. */
    	if (mm != current->mm && !capable(CAP_SYS_NICE)) {
    		ret = -EPERM;
    		goto release_mm;
    	}
    
    	ret = vector_madvise(mm, &iter, behavior);
    
    release_mm:
    	mmput(mm);
    release_task:
    	put_task_struct(task);
    free_iov:
    	kfree(iov);
    out:
    	return ret;
    }
  • process_madvise_remote_valid()๋Š” MADV_COLD, MADV_PAGEOUT, MADV_WILLNEED, MADV_COLLAPSE๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
  • vector_madvise()๋Š” iovec๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ๊ตฌ๊ฐ„์„ ๊ฐ™์€ madvise ์ฒ˜๋ฆฌ ๊ฒฝ๋กœ๋กœ ๋„˜๊ธด๋‹ค.
  • madvise_vma_behavior โ€” behavior๋ณ„ ๋ถ„๊ธฐ ๋””์ŠคํŒจ์ฒ˜

    // mm/madvise.c:1345-1448
    static int madvise_vma_behavior(struct madvise_behavior *madv_behavior)
    {
    int behavior = madv_behavior->behavior;
    struct vm_area_struct *vma = madv_behavior->vma;
    vm_flags_t new_flags = vma->vm_flags;
    struct madvise_behavior_range *range = &madv_behavior->range;
    int error;
    
    if (unlikely(!can_madvise_modify(madv_behavior)))
    return -EPERM;
    
    switch (behavior) {
    case MADV_REMOVE:        return madvise_remove(madv_behavior);
    case MADV_WILLNEED:      return madvise_willneed(madv_behavior);
    case MADV_COLD:          return madvise_cold(madv_behavior);
    case MADV_PAGEOUT:       return madvise_pageout(madv_behavior);
    case MADV_FREE:
    case MADV_DONTNEED:
    case MADV_DONTNEED_LOCKED: return madvise_dontneed_free(madv_behavior);
    case MADV_COLLAPSE:
    return madvise_collapse(vma, range->start, range->end,
    &madv_behavior->lock_dropped);
    case MADV_GUARD_INSTALL: return madvise_guard_install(madv_behavior);
    case MADV_GUARD_REMOVE:  return madvise_guard_remove(madv_behavior);
    // VMA ํ”Œ๋ž˜๊ทธ ๋ณ€๊ฒฝ (madvise_update_vma ํ˜ธ์ถœ)
    case MADV_NORMAL:
    new_flags = new_flags & ~VM_RAND_READ & ~VM_SEQ_READ;
    break;
    case MADV_SEQUENTIAL:
    new_flags = (new_flags & ~VM_RAND_READ) | VM_SEQ_READ;
    break;
    case MADV_RANDOM:
    new_flags = (new_flags & ~VM_SEQ_READ) | VM_RAND_READ;
    break;
    case MADV_DONTFORK:
    new_flags |= VM_DONTCOPY;
    break;
    case MADV_DOFORK:
    if (new_flags & VM_SPECIAL)
    return -EINVAL;
    new_flags &= ~VM_DONTCOPY;
    break;
    case MADV_WIPEONFORK:
    if (vma->vm_file || new_flags & VM_SHARED)
    return -EINVAL;
    new_flags |= VM_WIPEONFORK;
    break;
    case MADV_KEEPONFORK:
    if (new_flags & VM_DROPPABLE)
    return -EINVAL;
    new_flags &= ~VM_WIPEONFORK;
    break;
    case MADV_DONTDUMP:
    new_flags |= VM_DONTDUMP;
    break;
    case MADV_DODUMP:
    if ((!is_vm_hugetlb_page(vma) && (new_flags & VM_SPECIAL)) ||
    (new_flags & VM_DROPPABLE))
    return -EINVAL;
    new_flags &= ~VM_DONTDUMP;
    break;
    case MADV_MERGEABLE:
    case MADV_UNMERGEABLE:
    error = ksm_madvise(vma, range->start, range->end,
    behavior, &new_flags);
    if (error)
    goto out;
    break;
    case MADV_HUGEPAGE:
    case MADV_NOHUGEPAGE:
    error = hugepage_madvise(vma, &new_flags, behavior);
    if (error)
    goto out;
    break;
    }
    
    /* ์ด ๋ถ„๊ธฐ๋Š” VMA ์ˆ˜์ • ๊ฒฝ๋กœ๋‹ค. */
    VM_WARN_ON_ONCE(madv_behavior->lock_mode != MADVISE_MMAP_WRITE_LOCK);
    error = madvise_update_vma(new_flags, madv_behavior);
    out:
    /* ๋ฉ”๋ชจ๋ฆฌ ์ž์›์ด ์ผ์‹œ์ ์œผ๋กœ ๋ถ€์กฑํ•˜๋ฉด EAGAIN์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. */
    if (error == -ENOMEM)
    error = -EAGAIN;
    return error;
    }

    madvise_walk_vmas โ€” VMA ์ˆœํšŒ + behavior ์ ์šฉ

    // mm/madvise.c:1668-1742
    static int madvise_walk_vmas(struct madvise_behavior *madv_behavior)
    {
    struct mm_struct *mm = madv_behavior->mm;
    struct madvise_behavior_range *range = &madv_behavior->range;
    unsigned long last_end = range->end;
    int unmapped_error = 0;
    int error;
    struct vm_area_struct *prev, *vma;
    
    // VMA read lock ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ๋‹จ์ผ VMA ์‹œ๋„
    if (madv_behavior->lock_mode == MADVISE_VMA_READ_LOCK &&
    try_vma_read_lock(madv_behavior)) {
    error = madvise_vma_behavior(madv_behavior);
    vma_end_read(madv_behavior->vma);
    return error;
    }
    vma = find_vma_prev(mm, range->start, &prev);
    if (vma && range->start > vma->vm_start)
    prev = vma;
    
    // VMA ์ˆœํšŒ ๋ฃจํ”„
    for (;;) {
    if (!vma)
    return -ENOMEM;
    
    if (range->start < vma->vm_start) {
    unmapped_error = -ENOMEM;
    range->start = vma->vm_start;
    if (range->start >= last_end)
    break;
    }
    
    range->end = min(vma->vm_end, last_end);
    error = madvise_vma_behavior(madv_behavior);
    if (error)
    return error;
    if (madv_behavior->lock_dropped) {
    // mmap_lock์„ ๋‚ด๋ ค์„œ VMA ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ
    prev = NULL;
    vma = NULL;
    madv_behavior->lock_dropped = false;
    } else {
    vma = madv_behavior->vma;
    prev = vma;
    }
    
    if (vma && range->end < vma->vm_end)
    range->end = vma->vm_end;
    if (range->end >= last_end)
    break;
    
    vma = find_vma(mm, vma ? vma->vm_end : range->end);
    range->start = range->end;
    }
    
    return unmapped_error;
    }

    madvise_cold_or_pageout_pte_range โ€” PTE ์ˆ˜์ค€ ํŽ˜์ด์ง€ ๋น„ํ™œ์„ฑํ™”/์ถœ๋ ฅ

    // mm/madvise.c:352-565 (ํ•ต์‹ฌ ๋ฃจํ”„)
    static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
    unsigned long addr, unsigned long end, struct mm_walk *walk)
    {
    // THP ์ฒ˜๋ฆฌ: pmd_trans_huge ํ™•์ธ โ†’ split ๋˜๋Š” ์ง์ ‘ ์ฒ˜๋ฆฌ
    // ์ผ๋ฐ˜ PTE ๋ฃจํ”„:
    for (; addr < end; pte += nr, addr += nr * PAGE_SIZE) {
    ptent = ptep_get(pte);
    folio = vm_normal_folio(vma, addr, ptent);
    // Large folio โ†’ madvise_folio_pte_batch()๋กœ ์ผ๊ด„ ์ฒ˜๋ฆฌ
    // LRU ํ…Œ์ŠคํŠธ ์‹คํŒจ ๋˜๋Š” mapcount ๋ถˆ์ผ์น˜ ์‹œ ๊ฑด๋„ˆ๋œ€
    if (!pageout && pte_young(ptent))
    clear_young_dirty_ptes(vma, addr, pte, nr, CYDP_CLEAR_YOUNG);  // COLD
    folio_clear_referenced(folio);
    if (pageout)
    list_add(&folio->lru, &folio_list);  // PAGEOUT: LRU์—์„œ ๊ฒฉ๋ฆฌ
    else
    folio_deactivate(folio);              // COLD: activeโ†’inactive ์ด๋™
    }
    if (pageout)
    reclaim_pages(&folio_list);               // PAGEOUT: ์ฆ‰์‹œ ํšŒ์ˆ˜
    }

    madvise_free_pte_range โ€” lazyfree ๋งˆํ‚น

    // mm/madvise.c:651-782 (ํ•ต์‹ฌ ๋กœ์ง)
    static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
    unsigned long end, struct mm_walk *walk)
    {
    for (; addr != end; pte += nr, addr += PAGE_SIZE * nr) {
    ptent = ptep_get(pte);
    // swap entry โ†’ PTE ํด๋ฆฌ์–ด (์Šค์™‘์ธ๋ณด๋‹ค ํŽ˜์ด์ง€ ํ• ๋‹น+์ œ๋กœ์ž‰์ด ์ €๋ ด)
    if (!pte_present(ptent)) {
    if (softleaf_is_swap(entry))
    swap_put_entries_direct(entry, nr);
    continue;
    }
    folio = vm_normal_folio(vma, addr, ptent);
    // swap cache ๋˜๋Š” dirty โ†’ folio_free_swap ์‹œ๋„, dirty ํ•ด์ œ
    if (folio_test_swapcache(folio) || folio_test_dirty(folio)) {
    if (!folio_free_swap(folio)) continue;
    folio_clear_dirty(folio);
    }
    // young/dirty PTE ํด๋ฆฌ์–ด
    if (pte_young(ptent) || pte_dirty(ptent))
    clear_young_dirty_ptes(vma, addr, pte, nr, cydp_flags);
    folio_mark_lazyfree(folio);  // lazyfree๋กœ ํ‘œ์‹œ โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ ์ž๋™ ํ•ด์ œ
    }
    }

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

    SYSCALL_DEFINE3(madvise, ...)
    โ””โ”€โ”€ do_madvise(mm, start, len_in, behavior)
    โ”œโ”€โ”€ madvise_should_skip()           โ† ์œ ํšจ์„ฑ ๊ฒ€์ฆ
    โ”œโ”€โ”€ madvise_lock()                  โ† behavior๋ณ„ ์ž ๊ธˆ ์„ ํƒ
    โ”‚     โ””โ”€โ”€ get_lock_mode()
    โ”œโ”€โ”€ madvise_init_tlb()              โ† TLB batch ์ดˆ๊ธฐํ™”
    โ”œโ”€โ”€ madvise_do_behavior()
    โ”‚     โ”œโ”€โ”€ [MADV_HWPOISON/SOFT_OFFLINE] โ†’ madvise_inject_error()
    โ”‚     โ”œโ”€โ”€ [MADV_POPULATE_*] โ†’ madvise_populate()
    โ”‚     โ”‚     โ””โ”€โ”€ faultin_page_range()
    โ”‚     โ””โ”€โ”€ [๊ธฐํƒ€] โ†’ madvise_walk_vmas()
    โ”‚           โ”œโ”€โ”€ try_vma_read_lock()  โ† VMA lock ์šฐ์„  ์‹œ๋„
    โ”‚           โ””โ”€โ”€ madvise_vma_behavior() โ† behavior๋ณ„ ๋ถ„๊ธฐ
    โ”‚                 โ”œโ”€โ”€ MADV_WILLNEED    โ†’ madvise_willneed()
    โ”‚                 โ”‚     โ”œโ”€โ”€ [anon] โ†’ swapin_walk_pmd_entry()
    โ”‚                 โ”‚     โ”œโ”€โ”€ [shmem] โ†’ shmem_swapin_range()
    โ”‚                 โ”‚     โ””โ”€โ”€ [file] โ†’ vfs_fadvise(POSIX_FADV_WILLNEED)
    โ”‚                 โ”œโ”€โ”€ MADV_COLD       โ†’ madvise_cold()
    โ”‚                 โ”‚     โ””โ”€โ”€ madvise_cold_page_range()
    โ”‚                 โ”‚           โ””โ”€โ”€ madvise_cold_or_pageout_pte_range(pageout=false)
    โ”‚                 โ”‚                 โ””โ”€โ”€ folio_deactivate()
    โ”‚                 โ”œโ”€โ”€ MADV_PAGEOUT    โ†’ madvise_pageout()
    โ”‚                 โ”‚     โ””โ”€โ”€ madvise_pageout_page_range()
    โ”‚                 โ”‚           โ””โ”€โ”€ madvise_cold_or_pageout_pte_range(pageout=true)
    โ”‚                 โ”‚                 โ””โ”€โ”€ reclaim_pages()
    โ”‚                 โ”œโ”€โ”€ MADV_DONTNEED/FREE โ†’ madvise_dontneed_free()
    โ”‚                 โ”‚     โ”œโ”€โ”€ [DONTNEED] โ†’ zap_page_range_single_batched()
    โ”‚                 โ”‚     โ””โ”€โ”€ [FREE] โ†’ madvise_free_single_vma()
    โ”‚                 โ”‚           โ””โ”€โ”€ madvise_free_pte_range() โ†’ folio_mark_lazyfree()
    โ”‚                 โ”œโ”€โ”€ MADV_REMOVE     โ†’ madvise_remove()
    โ”‚                 โ”‚     โ””โ”€โ”€ vfs_fallocate(PUNCH_HOLE)
    โ”‚                 โ”œโ”€โ”€ MADV_COLLAPSE   โ†’ madvise_collapse()
    โ”‚                 โ”œโ”€โ”€ MADV_GUARD_INSTALL โ†’ madvise_guard_install()
    โ”‚                 โ”‚     โ””โ”€โ”€ walk_page_range โ†’ guard marker PTE ์„ค์น˜
    โ”‚                 โ”œโ”€โ”€ MADV_GUARD_REMOVE โ†’ madvise_guard_remove()
    โ”‚                 โ””โ”€โ”€ [VMA ํ”Œ๋ž˜๊ทธ] โ†’ madvise_update_vma()
    โ”‚                       โ””โ”€โ”€ vma_modify_flags() / vma_modify_name()
    โ”œโ”€โ”€ madvise_finish_tlb()
    โ””โ”€โ”€ madvise_unlock()

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

    behavior๋ณ„ ์ž ๊ธˆ ๋ชจ๋“œ

    behavior์ž ๊ธˆ ๋ชจ๋“œ์„ค๋ช…
    `MADV_DONTNEED`, `MADV_DONTNEED_LOCKED`, `MADV_FREE`VMA_READ_LOCKํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ˆ˜์ •, ๋‹จ์ผ VMA๋งŒ ๊ฐ€๋Šฅ
    `MADV_GUARD_INSTALL`, `MADV_GUARD_REMOVE`VMA_READ_LOCKguard marker ์„ค์น˜/ํ•ด์ œ
    `MADV_WILLNEED`, `MADV_COLD`, `MADV_PAGEOUT`MMAP_READ_LOCKwalk_page_range ์‚ฌ์šฉ
    `MADV_REMOVE`MMAP_READ_LOCKmmap_lock ํ•ด์ œ ํ›„ fallocate ํ˜ธ์ถœ
    `MADV_COLLAPSE`MMAP_READ_LOCKTHP ๋™๊ธฐ ๋ณ‘ํ•ฉ
    `MADV_POPULATE_READ`, `MADV_POPULATE_WRITE`MMAP_READ_LOCKfaultin_page_range ์‚ฌ์šฉ
    `MADV_NORMAL/SEQUENTIAL/RANDOM/DONTFORK/DOFORK`MMAP_WRITE_LOCKvm_flags ๋ณ€๊ฒฝ ํ•„์š”
    `MADV_WIPEONFORK/KEEPONFORK/DONTDUMP/DODUMP`MMAP_WRITE_LOCKvm_flags ๋ณ€๊ฒฝ ํ•„์š”
    `MADV_MERGEABLE/UNMERGEABLE`MMAP_WRITE_LOCKKSM ์„ค์ • ๋ณ€๊ฒฝ
    `MADV_HUGEPAGE/NOHUGEPAGE`MMAP_WRITE_LOCKTHP ์„ค์ • ๋ณ€๊ฒฝ
    `MADV_HWPOISON`, `MADV_SOFT_OFFLINE`NO_LOCK๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฅ˜ ์ฃผ์ž…, ์ž์ฒด ์ฒ˜๋ฆฌ

    MADV_DONTNEED vs MADV_FREE ๋™์ž‘ ๋น„๊ต

    ํ•ญ๋ชฉMADV_DONTNEEDMADV_FREE
    ํŽ˜์ด์ง€ ํ•ด์ œ ์‹œ์ ์ฆ‰์‹œ (zap_page_range)์ง€์—ฐ (๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ)
    PTE ์ฒ˜๋ฆฌํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์™„์ „ ํด๋ฆฌ์–ดlazyfree ๋งˆํ‚น๋งŒ
    dirty ํŽ˜์ด์ง€๋“œ๋กญ (๋ณต๊ตฌ ๋ถˆ๊ฐ€)๋“œ๋กญ (๋ณต๊ตฌ ๋ถˆ๊ฐ€)
    clean ํŽ˜์ด์ง€์ฆ‰์‹œ ๋ฐ˜ํ™˜free list์— ์œ ์ง€
    ์ ์šฉ ๋Œ€์ƒ๋ชจ๋“  VMA (HugeTLB ํฌํ•จ)์ต๋ช… VMA๋งŒ
    mmap_lockVMA_READ_LOCK ํ•„์š”VMA_READ_LOCK ํ•„์š”
    userfaultfd์ œ๊ฑฐ ํ›„ ์žฌ์„ค์ •์ œ๊ฑฐ ํ›„ ์žฌ์„ค์ •

    MADV_COLD vs MADV_PAGEOUT ๋™์ž‘ ๋น„๊ต

    ํ•ญ๋ชฉMADV_COLDMADV_PAGEOUT
    LRU ๋™์ž‘active โ†’ inactive ์ด๋™LRU์—์„œ ๊ฒฉ๋ฆฌ โ†’ ์ฆ‰์‹œ ํšŒ์ˆ˜
    ํŽ˜์ด์ง€ ํ•ด์ œ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ ํšŒ์ˆ˜์ฆ‰์‹œ ํšŒ์ˆ˜ (reclaim_pages)
    THP ์ฒ˜๋ฆฌsplit ์‹œ๋„ ํ›„ deactivationsplit ์‹œ๋„ ํ›„ LRU ๊ฒฉ๋ฆฌ
    ํŒŒ์ผ ๋งคํ•‘๊ฑด๋„ˆ๋œ€ (can_do_file_pageout)์“ฐ๊ธฐ ๊ถŒํ•œ ํ™•์ธ ํ›„ ์ถœ๋ ฅ
    ์ ์šฉ ๋ถˆ๊ฐ€VM_LOCKED, VM_PFNMAP, VM_HUGEPAGEVM_LOCKED, VM_PFNMAP, VM_HUGEPAGE

    process_madvise ์›๊ฒฉ ์ ์šฉ ๊ฐ€๋Šฅ behavior

    behavior์›๊ฒฉ ์ ์šฉ์„ค๋ช…
    `MADV_COLD`OํŽ˜์ด์ง€ ๋น„ํ™œ์„ฑํ™”
    `MADV_PAGEOUT`OํŽ˜์ด์ง€ ์ฆ‰์‹œ ํšŒ์ˆ˜
    `MADV_WILLNEED`Oreadahead ํŠธ๋ฆฌ๊ฑฐ
    `MADV_COLLAPSE`OTHP ๋™๊ธฐ ๋ณ‘ํ•ฉ
    ๊ทธ ์™ธXCAP_SYS_NICE ํ•„์š” + ๋น„ํŒŒ๊ดด์  behavior๋งŒ ํ—ˆ์šฉ

    MADV_* ์ „์ฒด ๋ชฉ๋ก

    ์ƒ์ˆ˜๊ฐ’์„ค๋ช…VMA ๋ณ€๊ฒฝ
    `MADV_NORMAL`0๊ธฐ๋ณธ readaheadVM_SEQ_READ, VM_RAND_READ ํ•ด์ œ
    `MADV_RANDOM`1์ตœ์†Œ readaheadVM_RAND_READ ์„ค์ •
    `MADV_SEQUENTIAL`2๊ณต๊ฒฉ์  readaheadVM_SEQ_READ ์„ค์ •
    `MADV_WILLNEED`3readahead ํŠธ๋ฆฌ๊ฑฐ์—†์Œ
    `MADV_DONTNEED`4์ฆ‰์‹œ ํŽ˜์ด์ง€ ํ•ด์ œ์—†์Œ
    `MADV_FREE`8lazyfree ๋งˆํ‚น์—†์Œ
    `MADV_REMOVE`9ํŒŒ์ผ hole punching์—†์Œ
    `MADV_DONTFORK`10fork ์‹œ ๋ณต์ œ ์ œ์™ธVM_DONTCOPY ์„ค์ •
    `MADV_DOFORK`11fork ์‹œ ๋ณต์ œ ํฌํ•จVM_DONTCOPY ํ•ด์ œ
    `MADV_MERGEABLE`12KSM ๋ณ‘ํ•ฉ ๋Œ€์ƒVM_MERGEABLE ์„ค์ •
    `MADV_UNMERGEABLE`13KSM ๋ณ‘ํ•ฉ ์ œ์™ธVM_MERGEABLE ํ•ด์ œ
    `MADV_HUGEPAGE`14THP ๋Œ€์ƒVM_HUGEPAGE ์„ค์ •
    `MADV_NOHUGEPAGE`15THP ์ œ์™ธVM_NOHUGEPAGE ์„ค์ •
    `MADV_DONTDUMP`16core dump ์ œ์™ธVM_DONTDUMP ์„ค์ •
    `MADV_DODUMP`17core dump ํฌํ•จVM_DONTDUMP ํ•ด์ œ
    `MADV_WIPEONFORK`18fork ์‹œ ์ž์‹ zero-fillVM_WIPEONFORK ์„ค์ •
    `MADV_KEEPONFORK`19fork ์‹œ ๋ฐ์ดํ„ฐ ์œ ์ง€VM_WIPEONFORK ํ•ด์ œ
    `MADV_COLD`20ํŽ˜์ด์ง€ ๋น„ํ™œ์„ฑํ™”์—†์Œ
    `MADV_PAGEOUT`21์ฆ‰์‹œ ์Šค์™‘์•„์›ƒ์—†์Œ
    `MADV_POPULATE_READ`22์ฝ๊ธฐ fault ์‚ฌ์ „ ์ƒ์„ฑ์—†์Œ
    `MADV_POPULATE_WRITE`23์“ฐ๊ธฐ fault ์‚ฌ์ „ ์ƒ์„ฑ์—†์Œ
    `MADV_DONTNEED_LOCKED`24locked ํŽ˜์ด์ง€ ํฌํ•จ ํ•ด์ œ์—†์Œ
    `MADV_COLLAPSE`25THP ๋™๊ธฐ ๋ณ‘ํ•ฉ์—†์Œ
    `MADV_SOFT_OFFLINE`101์†Œํ”„ํŠธ ์˜คํ”„๋ผ์ธ์—†์Œ
    `MADV_HWPOISON`100ํ•˜๋“œ์›จ์–ด ์˜ค๋ฅ˜ ์ฃผ์ž…์—†์Œ
    `MADV_GUARD_INSTALL`102์ ‘๊ทผ ์‹œ fatal signalguard PTE ๋งˆ์ปค
    `MADV_GUARD_REMOVE`103guard ํ•ด์ œguard PTE ํด๋ฆฌ์–ด

    ๋‹ค์ด์–ด๊ทธ๋žจ

    madvise ํ˜ธ์ถœ ํ๋ฆ„
    madvise ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • VMA / mmap
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜
  • process_vm_readv/writev
  • Swap / zswap
  • Huge Pages / THP
  • KSM
  • Sealing (mseal)
  • mlock / munlock
  • mprotect
  • mremap