๐ŸŒ vmalloc โ€” ๊ฐ€์ƒ ์—ฐ์† ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น

๊ด€๋ จ ์†Œ์Šค: mm/vmalloc.c (5485์ค„), include/linux/vmalloc.h (337์ค„)
๊ด€๋ จ ๋ฌธ์„œ: Buddy Allocator ยท SLUB ํ• ๋‹น์ž ยท Memblock ํ• ๋‹น์ž ยท ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”

๊ฐœ์š” (Overview)

vmalloc์€ ์ปค๋„ ๊ฐ€์ƒ ์ฃผ์†Œ ๊ณต๊ฐ„์—์„œ ๊ฐ€์ƒ์ ์œผ๋กœ ์—ฐ์†๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ฆฌ์ ์œผ๋กœ๋Š” ๋น„์—ฐ์†์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋งคํ•‘์„ ํ†ตํ•ด ๊ฐ€์ƒ ์ฃผ์†Œ ๊ณต๊ฐ„์—์„œ๋Š” ์—ฐ์†์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ํฐ ์ปค๋„ ๋ฒ„ํผ, ๋ชจ๋“ˆ ๋กœ๋”ฉ, ioremap ๋“ฑ์— ์‚ฌ์šฉ๋˜๋ฉฐ, DMA ๋ฒ„ํผ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

kmalloc์ด ๋ฌผ๋ฆฌ ์—ฐ์† ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๋Š” ๋ฐ˜๋ฉด, vmalloc์€ ๊ฐœ๋ณ„ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•œ ํ›„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์กฐ์ž‘ํ•˜์—ฌ ๊ฐ€์ƒ ์—ฐ์†์œผ๋กœ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ kmalloc๋ณด๋‹ค ๋А๋ฆฌ์ง€๋งŒ, ํฐ ํ• ๋‹น์— ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋„์„œ๊ด€์— ๋น„์œ ํ•˜๋ฉด kmalloc์€ ๋ถ™์–ด ์žˆ๋Š” ์ฑ…์žฅ์„ ํ†ต์งธ๋กœ ํ™•๋ณดํ•˜๋Š” ๋ฐฉ์‹์ด๊ณ , vmalloc์€ ์—ฌ๋Ÿฌ ์ธต์— ํฉ์–ด์ง„ ์ฑ…์„ ํ•˜๋‚˜์˜ ์—ฐ์†๋œ ๋ชฉ๋ก ๋ฒˆํ˜ธ๋กœ ๋ฌถ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์—ฐ์†๋œ ๋ฒˆํ˜ธ๋งŒ ๋ณด์ง€๋งŒ, ๊ด€๋ฆฌ์ž๋Š” ๊ฐ ๋ฒˆํ˜ธ๊ฐ€ ์‹ค์ œ ์–ด๋А ์ฑ…์žฅ์— ์žˆ๋Š”์ง€ ๋ณ„๋„์˜ ํ‘œ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์„ค์ •๊ณผ TLB/์บ์‹œ ๋™๊ธฐํ™” ๋น„์šฉ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

์ปค๋„ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ๋งต์—์„œ vmalloc/ioremap ์˜์—ญ์€ ์ง์ ‘ ๋งคํ•‘(Direct Mapping) ์˜์—ญ๊ณผ ๋ณ„๋„์ž…๋‹ˆ๋‹ค. x86_64 4๋‹จ๊ณ„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์˜ ๋Œ€ํ‘œ ๋ฐฐ์น˜์—์„œ๋Š” ์ง์ ‘ ๋งคํ•‘์ด ffff888000000000 ๊ทผ์ฒ˜๋ถ€ํ„ฐ, vmalloc/ioremap ์˜์—ญ์ด ffffc90000000000 ๊ทผ์ฒ˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์ง€๋งŒ ์‹ค์ œ ๊ฐ’์€ KASLR, 5๋‹จ๊ณ„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”, KASAN, ์•„ํ‚คํ…์ฒ˜ ์„ค์ •์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ vmalloc ์ฃผ์†Œ์—๋Š” __pa()/__va() ์‚ฐ์ˆ  ๋ณ€ํ™˜์„ ์ ์šฉํ•˜์ง€ ๋ง๊ณ , ์ฃผ์†Œ ์„ฑ๊ฒฉ์€ is_vmalloc_addr()์™€ ํ˜„์žฌ ๋ถ€ํŒ… ์ธ์Šคํ„ด์Šค์˜ /proc/vmallocinfo, kernel_page_tables๋กœ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

mm/vmalloc.c              โ† ๋ฉ”์ธ ๊ตฌํ˜„ (5485์ค„)
include/linux/vmalloc.h   โ† API ์„ ์–ธ ๋ฐ vm_struct ์ •์˜ (337์ค„)
include/linux/mm.h        โ† vmalloc_to_page(), is_vmalloc_addr() ๋“ฑ
mm/internal.h             โ† ๋‚ด๋ถ€ ํ—ฌํผ ํ•จ์ˆ˜
vmalloc ํ˜ธ์ถœ ํ๋ฆ„

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

# vmalloc ์˜์—ญ ์‚ฌ์šฉ ํ˜„ํ™ฉ
cat /proc/vmallocinfo | wc -l              # ์ „์ฒด vmalloc ์˜์—ญ ์ˆ˜
cat /proc/vmallocinfo | head -10           # ์ƒ์œ„ vmalloc ํ• ๋‹น ๋ชฉ๋ก
grep -i vmalloc /proc/meminfo              # VmallocTotal, VmallocUsed, VmallocChunk

# vmalloc ์ฃผ์†Œ ๋ฒ”์œ„ ํ™•์ธ
sudo awk 'NR <= 10 {print $1}' /proc/vmallocinfo  # ์‹œ์ž‘-๋ ๊ฐ€์ƒ ์ฃผ์†Œ ๋ฒ”์œ„

# ํŠน์ • ํ”„๋กœ์„ธ์Šค์˜ vmalloc ์‚ฌ์šฉ๋Ÿ‰
cat /proc/$$/status | grep -i vm           # VmData, VmStk ๋“ฑ

# vmalloc ํ• ๋‹น ์ •๋ณด (์ปค๋„ 5.10+)
sudo cat /proc/vmallocinfo | grep -i "vm_struct\|pages=" | head -20

# ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ํ™•์ธ
cat /proc/pressure/memory                  # PSI ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ

# ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๋งต์—์„œ vmalloc ์˜์—ญ ํ™•์ธ
grep -E 'Vmalloc|PageTables' /proc/meminfo

# ์ง์ ‘ ๋งคํ•‘, vmalloc, ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์‚ฌ์šฉ๋Ÿ‰์„ ํ•จ๊ป˜ ํ™•์ธ
grep -E 'Vmalloc|PageTables|DirectMap' /proc/meminfo

# vmalloc/ioremap ์‹ค์ œ ๋งคํ•‘๊ณผ ํ˜ธ์ถœ์ž ํ™•์ธ
sudo cat /proc/vmallocinfo | grep -E 'vmalloc|ioremap|vmap' | head -30

# ๋ฌผ๋ฆฌ ์ฃผ์†Œ ๊ณต๊ฐ„์—์„œ RAM๊ณผ MMIO hole ๊ตฌ๋ถ„
cat /proc/iomem | head -80

# x86 CONFIG_X86_PTDUMP์™€ debugfs๊ฐ€ ์žˆ์œผ๋ฉด ์ปค๋„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ํ™•์ธ
sudo cat /sys/kernel/debug/kernel_page_tables 2>/dev/null | grep -E 'vmalloc|ioremap|page_offset|direct' | head -40

# ์ฃผ์†Œ ํญ๊ณผ ๋””๋ฒ„๊ทธ ์˜ต์…˜์ด vmalloc ์˜์—ญ ๋ฐฐ์น˜์— ์˜ํ–ฅ์„ ์ฃผ๋Š”์ง€ ํ™•์ธ
zgrep -E 'CONFIG_X86_5LEVEL|CONFIG_KASAN|CONFIG_MITIGATION_PAGE_TABLE_ISOLATION|CONFIG_HAVE_ARCH_HUGE_VMALLOC' /proc/config.gz 2>/dev/null

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

vmalloc ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

struct vm_struct โ€” vmalloc ํ• ๋‹น ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ

/* include/linux/vmalloc.h:52-69 */
struct vm_struct {
union {
struct vm_struct *next;    /* vm_area ์กฐ๊ธฐ ๋“ฑ๋ก. */
struct llist_node llnode;  /* ์˜ค๋ฅ˜ ๊ฒฝ๋กœ์—์„œ ๋น„๋™๊ธฐ ํ•ด์ œ. */
};

void            *addr;
unsigned long    size;
unsigned long    flags;
struct page    **pages;
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
unsigned int     page_order;
#endif
unsigned int     nr_pages;
phys_addr_t      phys_addr;
const void      *caller;
unsigned long    requested_size;
};

ํ•„๋“œ ์„ค๋ช…:

  • addr: vmalloc ์˜์—ญ์—์„œ ํ• ๋‹น๋ฐ›์€ ๊ฐ€์ƒ ์ฃผ์†Œ
  • size: guard page ํฌํ•จ ์ „์ฒด ํฌ๊ธฐ (flags์— VM_NO_GUARD ์—†์œผ๋ฉด +PAGE_SIZE)
  • flags: ํ• ๋‹น ์œ ํ˜• (VM_ALLOC=0x02, VM_IOREMAP=0x01, VM_MAP=0x04 ๋“ฑ)
  • pages: alloc_pages_bulk()๋กœ ํ• ๋‹นํ•œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ๋ฐฐ์—ด
  • page_order: huge vmalloc ํ™œ์„ฑํ™” ์‹œ PMD_SHIFT ๋“ฑ์œผ๋กœ ์„ค์ •
  • nr_pages: ์‹ค์ œ ํ• ๋‹น๋œ ํŽ˜์ด์ง€ ์ˆ˜
  • caller: ๋””๋ฒ„๊น…์šฉ ํ• ๋‹น ํ˜ธ์ถœ์ž ์ฃผ์†Œ
  • struct vmap_area โ€” ๊ฐ€์ƒ ์ฃผ์†Œ ์˜์—ญ ๊ด€๋ฆฌ

    /* include/linux/vmalloc.h:71-89 */
    struct vmap_area {
    unsigned long va_start;
    unsigned long va_end;
    
    struct rb_node rb_node;         /* ์ฃผ์†Œ ์ •๋ ฌ rbtree */
    struct list_head list;          /* ์ฃผ์†Œ ์ •๋ ฌ list */
    
    /*
    * ๋‹ค์Œ ๋‘ ๋ณ€์ˆ˜๋Š” ํ•˜๋‚˜๋กœ ๋ฌถ์„ ์ˆ˜ ์žˆ๋‹ค. vmap_area ๊ฐ์ฒด๋Š” ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋‹ค.
    *    1) "free" tree(root๋Š” free_vmap_area_root)์— ์žˆ์Œ
    *    2) ๋˜๋Š” "busy" tree(root๋Š” vmap_area_root)์— ์žˆ์Œ
    */
    union {
    unsigned long subtree_max_size; /* "free" tree์— ์žˆ์Œ */
    struct vm_struct *vm;           /* "busy" tree์— ์žˆ์Œ */
    };
    unsigned long flags; /* vm_map_ram ์˜์—ญ ํƒ€์ž… ํ‘œ์‹œ */
    };

    ํ•„๋“œ ์„ค๋ช…:

  • va_start/va_end: vmalloc ์ฃผ์†Œ ๊ณต๊ฐ„ ๋‚ด ๊ฐ€์ƒ ์ฃผ์†Œ ๋ฒ”์œ„
  • rb_node: red-black tree ๋…ธ๋“œ (์ฃผ์†Œ ์ •๋ ฌ)
  • subtree_max_size: free tree์—์„œ ์‚ฌ์šฉ๋˜๋Š” augmented ํ•„๋“œ
  • vm: busy tree์—์„œ ์—ฐ๊ฒฐ๋œ vm_struct ํฌ์ธํ„ฐ
  • struct vmap_node โ€” per-CPU vmap ๊ด€๋ฆฌ ๋…ธ๋“œ

    /* mm/vmalloc.c:936-952 */
    static struct vmap_node {
    /* ๋‹จ์ˆœํ•œ ํฌ๊ธฐ๋ณ„ ๊ฒฉ๋ฆฌ ์ €์žฅ์†Œ. */
    struct vmap_pool pool[MAX_VA_SIZE_PAGES];
    spinlock_t pool_lock;
    bool skip_populate;
    
    /* ์ด ๋…ธ๋“œ์˜ ๊ด€๋ฆฌ ๋ฐ์ดํ„ฐ. */
    struct rb_list busy;
    struct rb_list lazy;
    
    /*
    * ํ•ด์ œ ์ค€๋น„๊ฐ€ ๋๋‚œ ์˜์—ญ.
    */
    struct list_head purge_list;
    struct work_struct purge_work;
    unsigned long nr_purged;
    } single;

    ๋™์ž‘ ๋ฐฉ์‹:

  • ์‹œ์Šคํ…œ ์ดˆ๊ธฐ์—๋Š” single ๋…ธ๋“œ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ
  • ๋‚˜์ค‘์— vmap ์ดˆ๊ธฐํ™” ํ›„ nr_vmap_nodes๋งŒํผ ํ™•์žฅ
  • ์ฃผ์†Œ ๊ธฐ๋ฐ˜์œผ๋กœ addr_to_node()๋กœ vmap_area๋ฅผ ํŠน์ • ๋…ธ๋“œ์— ๋งคํ•‘
  • ํ•ต์‹ฌ ํ•จ์ˆ˜

    1. vmalloc_noprof โ€” ๋ฉ”์ธ ํ• ๋‹น API

    /* mm/vmalloc.c:4157-4162 */
    void *vmalloc_noprof(unsigned long size)
    {
    return __vmalloc_node_noprof(size, 1, GFP_KERNEL, NUMA_NO_NODE,
    __builtin_return_address(0));
    }
    EXPORT_SYMBOL(vmalloc_noprof);

    ์—ญํ• : ํฌ๊ธฐ๋งŒ ๋ฐ›์•„ ๊ธฐ๋ณธ GFP_KERNEL ํ”Œ๋ž˜๊ทธ๋กœ vmalloc ํ˜ธ์ถœ

    ๋ถ„๊ธฐ: ์—†์Œ (๋‹จ์ˆœ ๋ž˜ํผ)

    2. __vmalloc_node_range_noprof โ€” ํ•ต์‹ฌ ํ• ๋‹น ๋กœ์ง

    /* mm/vmalloc.c:3986-4103 */
    void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align,
    unsigned long start, unsigned long end, gfp_t gfp_mask,
    pgprot_t prot, unsigned long vm_flags, int node,
    const void *caller)
    {
    struct vm_struct *area;
    void *ret;
    kasan_vmalloc_flags_t kasan_flags = KASAN_VMALLOC_NONE;
    unsigned long original_align = align;
    unsigned int shift = PAGE_SHIFT;
    
    if (WARN_ON_ONCE(!size))
    return NULL;
    
    if ((size >> PAGE_SHIFT) > totalram_pages()) {
    warn_alloc(gfp_mask, NULL,
    "vmalloc error: size %lu, exceeds total pages", size);
    return NULL;
    }
    
    if (vmap_allow_huge && (vm_flags & VM_ALLOW_HUGE_VMAP)) {
    /*
    * Huge page๋ฅผ ์‹œ๋„ํ•œ๋‹ค. PAGE_KERNEL ํ• ๋‹น์—๋งŒ ์‹œ๋„ํ•œ๋‹ค.
    * module ๊ฐ™์€ ๋‹ค๋ฅธ ํ• ๋‹น์€ apply_to_page_range๊ฐ€ huge page๋ฅผ
    * ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์•„์ง huge page๋ฅผ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    */
    
    if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
    shift = PMD_SHIFT;
    else
    shift = arch_vmap_pte_supported_shift(size);
    align = max(original_align, 1UL << shift);
    }
    
    again:
    area = __get_vm_area_node(size, align, shift, VM_ALLOC |
    VM_UNINITIALIZED | vm_flags, start, end, node,
    gfp_mask, caller);
    if (!area) {
    bool nofail = gfp_mask & __GFP_NOFAIL;
    warn_alloc(gfp_mask, NULL,
    "vmalloc error: size %lu, vm_struct allocation failed%s",
    size, (nofail) ? ". Retrying." : "");
    if (nofail) {
    schedule_timeout_uninterruptible(1);
    goto again;
    }
    goto fail;
    }
    
    /*
    * __vmalloc_area_node()์™€ kasan_unpoison_vmalloc()์— ๋„˜๊ธธ
    * ์ธ์ž๋ฅผ ์ค€๋น„ํ•œ๋‹ค.
    */
    if (pgprot_val(prot) == pgprot_val(PAGE_KERNEL)) {
    if (kasan_hw_tags_enabled()) {
    /*
    * tagging์„ ํ—ˆ์šฉํ•˜๋„๋ก protection bit๋ฅผ ๋ฐ”๊พผ๋‹ค.
    * ๋งคํ•‘ ์ „์— ํ•ด์•ผ ํ•œ๋‹ค.
    */
    prot = arch_vmap_pgprot_tagged(prot);
    
    /*
    * VM_ALLOC ๋งคํ•‘์„ ๋’ท๋ฐ›์นจํ•˜๋Š” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€์— ๋Œ€ํ•ด page_alloc
    * poisoning๊ณผ zeroing์„ ๊ฑด๋„ˆ๋›ด๋‹ค. ๋Œ€์‹  ๋ฉ”๋ชจ๋ฆฌ๋Š”
    * kasan_unpoison_vmalloc()์—์„œ poison/zero ์ฒ˜๋ฆฌ๋œ๋‹ค.
    */
    gfp_mask |= __GFP_SKIP_KASAN | __GFP_SKIP_ZERO;
    }
    /* ๋งคํ•‘์ด PAGE_KERNEL์ด๋ผ๋Š” ์ ์„ ๊ธฐ๋กํ•œ๋‹ค. */
    kasan_flags |= KASAN_VMALLOC_PROT_NORMAL;
    }
    
    /* ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜๊ณ  vmalloc ๊ณต๊ฐ„์— ๋งคํ•‘ํ•œ๋‹ค. */
    ret = __vmalloc_area_node(area, gfp_mask, prot, shift, node);
    if (!ret)
    goto fail;
    
    /*
    * ํŽ˜์ด์ง€๊ฐ€ ๋งคํ•‘๋˜์—ˆ์œผ๋ฏ€๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ‘œ์‹œํ•œ๋‹ค.
    * KASAN_VMALLOC_INIT ์„ค์ • ์กฐ๊ฑด์€ __GFP_SKIP_ZERO ๊ด€์ ์—์„œ
    * post_alloc_hook()์˜ ์กฐ๊ฑด๊ณผ ๋ณด์™„๋˜์–ด์•ผ ํ•˜๋ฉฐ, ๊ฐ™์€ ์กฐ๊ฑด์—์„œ
    * ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜๋„๋ก ๋งž์ถ˜๋‹ค. tag ๊ธฐ๋ฐ˜ KASAN ๋ชจ๋“œ๋Š”
    * ์ผ๋ฐ˜ non-executable ํ• ๋‹น์—๋งŒ tag๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค.
    */
    kasan_flags |= KASAN_VMALLOC_VM_ALLOC;
    if (!want_init_on_free() && want_init_on_alloc(gfp_mask) &&
    (gfp_mask & __GFP_SKIP_ZERO))
    kasan_flags |= KASAN_VMALLOC_INIT;
    
    /* KASAN_VMALLOC_PROT_NORMAL์€ ํ•„์š”ํ•  ๋•Œ ์ด๋ฏธ ์„ค์ •๋˜์–ด ์žˆ๋‹ค. */
    area->addr = kasan_unpoison_vmalloc(area->addr, size, kasan_flags);
    
    /*
    * ์ด ํ•จ์ˆ˜์—์„œ ์ƒˆ๋กœ ํ• ๋‹นํ•œ vm_struct์—๋Š” VM_UNINITIALIZED ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ๋‹ค.
    * ์ด๋Š” vm_struct๊ฐ€ ์•„์ง ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Œ์„ ๋œปํ•œ๋‹ค.
    * ์ด์ œ ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”๋˜์—ˆ์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.
    */
    clear_vm_uninitialized_flag(area);
    
    if (!(vm_flags & VM_DEFER_KMEMLEAK))
    kmemleak_vmalloc(area, PAGE_ALIGN(size), gfp_mask);
    
    return area->addr;
    
    fail:
    if (shift > PAGE_SHIFT) {
    shift = PAGE_SHIFT;
    align = original_align;
    goto again;
    }
    return NULL;
    }

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

    1. size ๊ฒ€์ฆ: totalram_pages() ์ดˆ๊ณผ ์‹œ ์‹คํŒจ

    2. huge page ์‹œ๋„: vmap_allow_huge && VM_ALLOW_HUGE_VMAP && size >= PMD_SIZE

    3. ํ• ๋‹น ์‹คํŒจ ์‹œ: __GFP_NOFAIL์ด๋ฉด 1 tick ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„

    4. PAGE_SHIFT fallback: huge page ์‹คํŒจ ์‹œ order-0๋กœ ์žฌ์‹œ๋„

    3. __get_vm_area_node โ€” ๊ฐ€์ƒ ์ฃผ์†Œ ์˜์—ญ ์˜ˆ์•ฝ

    /* mm/vmalloc.c:3203-3251 */
    struct vm_struct *__get_vm_area_node(unsigned long size,
    unsigned long align, unsigned long shift, unsigned long flags,
    unsigned long start, unsigned long end, int node,
    gfp_t gfp_mask, const void *caller)
    {
    struct vmap_area *va;
    struct vm_struct *area;
    unsigned long requested_size = size;
    
    BUG_ON(in_interrupt());
    size = ALIGN(size, 1ul << shift);
    if (unlikely(!size))
    return NULL;
    
    if (flags & VM_IOREMAP)
    align = 1ul << clamp_t(int, get_count_order_long(size),
    PAGE_SHIFT, IOREMAP_MAX_ORDER);
    
    area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
    if (unlikely(!area))
    return NULL;
    
    if (!(flags & VM_NO_GUARD))
    size += PAGE_SIZE;
    
    area->flags = flags;
    area->caller = caller;
    area->requested_size = requested_size;
    
    va = alloc_vmap_area(size, align, start, end, node, gfp_mask, 0, area);
    if (IS_ERR(va)) {
    kfree(area);
    return NULL;
    }
    
    /*
    * VM_ALLOC์ด ์•„๋‹Œ ๋งคํ•‘์˜ ํŽ˜์ด์ง€๋Š” ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ‘œ์‹œํ•œ๋‹ค.
    * vmalloc ์ฝ”๋“œ ๋ฐ–์—์„œ ๋งคํ•‘๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์ตœ์„  ๋…ธ๋ ฅ ๋ฐฉ์‹์œผ๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค.
    * VM_ALLOC ๋งคํ•‘์€ __vmalloc_node_range()์—์„œ ๋งคํ•‘๋œ ๋’ค ์ ‘๊ทผ ๊ฐ€๋Šฅ ํ‘œ์‹œ๋œ๋‹ค.
    * hardware tag ๊ธฐ๋ฐ˜ KASAN์—์„œ๋Š” non-VM_ALLOC ๋งคํ•‘ ํ‘œ์‹œ๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค.
    */
    if (!(flags & VM_ALLOC))
    area->addr = kasan_unpoison_vmalloc(area->addr, requested_size,
    KASAN_VMALLOC_PROT_NORMAL);
    
    return area;
    }

    ์—ญํ• : vm_struct๋ฅผ ๋งŒ๋“ค๊ณ  vmalloc ์ฃผ์†Œ ๋ฒ”์œ„์—์„œ ์—ฐ์† ๊ฐ€์ƒ ์˜์—ญ์„ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค. VM_NO_GUARD๊ฐ€ ์—†์œผ๋ฉด guard page๋ฅผ ๋”ํ•˜๊ณ , VM_IOREMAP์€ IOREMAP_MAX_ORDER๋ฅผ ๋„˜์ง€ ์•Š๋Š” ๋ฒ”์œ„์—์„œ ์ •๋ ฌ์„ ํ‚ค์›๋‹ˆ๋‹ค.

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

    1. ์ธํ„ฐ๋ŸฝํŠธ ์ปจํ…์ŠคํŠธ ๊ธˆ์ง€: BUG_ON(in_interrupt())

    2. ํฌ๊ธฐ ์ •๋ ฌ: huge vmalloc์ด๋ฉด shift ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ

    3. guard page ์ถ”๊ฐ€: ๊ธฐ๋ณธ์ ์œผ๋กœ size += PAGE_SIZE

    4. ๊ฐ€์ƒ ์ฃผ์†Œ ์˜ˆ์•ฝ: alloc_vmap_area()๊ฐ€ free tree ๋˜๋Š” per-node pool์—์„œ ์˜์—ญ์„ ์ฐพ์•„ busy tree์— ์—ฐ๊ฒฐ

    4. __vmalloc_area_node โ€” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น ๋ฐ ๋งคํ•‘

    /* mm/vmalloc.c:3827-3933 */
    static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
    pgprot_t prot, unsigned int page_shift,
    int node)
    {
    const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
    bool nofail = gfp_mask & __GFP_NOFAIL;
    unsigned long addr = (unsigned long)area->addr;
    unsigned long size = get_vm_area_size(area);
    unsigned long array_size;
    unsigned int nr_small_pages = size >> PAGE_SHIFT;
    unsigned int page_order;
    unsigned int flags;
    int ret;
    
    array_size = (unsigned long)nr_small_pages * sizeof(struct page *);
    
    /* __GFP_NOFAIL๊ณผ "noblock" ํ”Œ๋ž˜๊ทธ๋Š” ํ•จ๊ป˜ ์“ธ ์ˆ˜ ์—†๋‹ค. */
    if (!gfpflags_allow_blocking(gfp_mask))
    nofail = false;
    
    if (!(gfp_mask & (GFP_DMA | GFP_DMA32)))
    gfp_mask |= __GFP_HIGHMEM;
    
    /* ์žฌ๊ท€๋Š” ์—„๊ฒฉํ•˜๊ฒŒ ์ œํ•œ๋œ๋‹ค. */
    if (array_size > PAGE_SIZE) {
    area->pages = __vmalloc_node_noprof(array_size, 1, nested_gfp, node,
    area->caller);
    } else {
    area->pages = kmalloc_node_noprof(array_size, nested_gfp, node);
    }
    
    if (!area->pages) {
    warn_alloc(gfp_mask, NULL,
    "vmalloc error: size %lu, failed to allocated page array size %lu",
    nr_small_pages * PAGE_SIZE, array_size);
    goto fail;
    }
    
    set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
    page_order = vm_area_page_order(area);
    
    /*
    * ๊ณ ์ฐจ nofail ํ• ๋‹น์€ ๋งค์šฐ ๋น„์‹ธ๊ณ  ์ž ์žฌ์ ์œผ๋กœ ์œ„ํ—˜ํ•˜๋‹ค
    * (์ด๋ฅธ OOM, ๋ฐฉํ•ด์ ์ธ reclaim๊ณผ compaction ๋“ฑ).
    *
    * __vmalloc_node_range_noprof()๋Š” ๊ณ ์ฐจ ์‹œ๋„๊ฐ€ ์‹คํŒจํ•˜๋ฉด
    * order-0 ํŽ˜์ด์ง€๋กœ ๋Œ€์ฒดํ•œ๋‹ค.
    */
    area->nr_pages = vm_area_alloc_pages(
    vmalloc_gfp_adjust(gfp_mask, page_order), node,
    page_order, nr_small_pages, area->pages);
    
    atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
    
    /* vm์˜ ๋ชจ๋“  ํŽ˜์ด์ง€๋Š” ๊ฐ™์€ memcg์— ์ฒญ๊ตฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ฒซ ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. */
    if (gfp_mask & __GFP_ACCOUNT && area->nr_pages)
    mod_memcg_page_state(area->pages[0], MEMCG_VMALLOC,
    area->nr_pages);
    
    /*
    * ํ• ๋‹น ์š”์ฒญ์„ ์™„๋ฃŒํ•  ๋งŒํผ ์ถฉ๋ถ„ํ•œ ํŽ˜์ด์ง€๋ฅผ ์–ป์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด,
    * ์ผ๋ถ€๊ฐ€ ์žˆ๋”๋ผ๋„ vfree()๋ฅผ ํ†ตํ•ด ํ•ด์ œํ•œ๋‹ค.
    */
    if (area->nr_pages != nr_small_pages) {
    /*
    * vm_area_alloc_pages()๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ๋ฟ ์•„๋‹ˆ๋ผ ๋‹ค์Œ ์ด์œ ๋กœ๋„ ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋‹ค.
    *
    * - ๋Œ€๊ธฐ ์ค‘์ธ fatal signal
    * - huge page-order ํŽ˜์ด์ง€ ๋ถ€์กฑ
    *
    * huge page ๊ฒฝ์šฐ์—๋Š” ํ•ญ์ƒ order-0์œผ๋กœ ์žฌ์‹œ๋„ํ•˜๋ฏ€๋กœ ๋‘˜ ๋‹ค ๊ฒฝ๊ณ ๊ฐ€ ๋ถ€์ ์ ˆํ•˜๋‹ค.
    */
    if (!fatal_signal_pending(current) && page_order == 0)
    warn_alloc(gfp_mask, NULL,
    "vmalloc error: size %lu, failed to allocate pages",
    area->nr_pages * PAGE_SIZE);
    goto fail;
    }
    
    /*
    * ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ํ• ๋‹น์€ ์™ธ๋ถ€ gfp mask๋ฅผ ๋ฌด์‹œํ•˜๋ฏ€๋กœ
    * scope API๋กœ ๊ฐ•์ œํ•œ๋‹ค.
    */
    flags = memalloc_apply_gfp_scope(gfp_mask);
    do {
    ret = __vmap_pages_range(addr, addr + size, prot, area->pages,
    page_shift, nested_gfp);
    if (nofail && (ret < 0))
    schedule_timeout_uninterruptible(1);
    } while (nofail && (ret < 0));
    memalloc_restore_scope(flags);
    
    if (ret < 0) {
    warn_alloc(gfp_mask, NULL,
    "vmalloc error: size %lu, failed to map pages",
    area->nr_pages * PAGE_SIZE);
    goto fail;
    }
    
    return area->addr;
    
    fail:
    defer_vm_area_cleanup(area);
    return NULL;
    }

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

    1. ํŽ˜์ด์ง€ ๋ฐฐ์—ด ํ• ๋‹น: array_size > PAGE_SIZE โ†’ vmalloc, ์•„๋‹ˆ๋ฉด kmalloc

    2. huge page ์‹คํŒจ ์‹œ: order-0 fallback (vm_area_alloc_pages ๋‚ด๋ถ€)

    3. ๋งคํ•‘ ์‹คํŒจ ์‹œ: nofail์ด๋ฉด 1 tick ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„

    4. ํ• ๋‹น ๋ถˆ์™„์ „ ์‹œ: fatal_signal_pending ์•„๋‹ˆ๊ณ  order-0์ด๋ฉด ๊ฒฝ๊ณ  ํ›„ ์‹คํŒจ

    vm_area_alloc_pages()๋Š” ๋จผ์ € ๊ฐ€๋Šฅํ•œ ํฐ order ํŽ˜์ด์ง€๋ฅผ ์‹œ๋„ํ•˜๊ณ , order-0์—์„œ๋Š” bulk allocator๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. bulk ์š”์ฒญ์€ ํ•œ ๋ฒˆ์— ์ตœ๋Œ€ 100ํŽ˜์ด์ง€๋กœ ์ œํ•œ๋˜๊ณ , ์ผ๋ถ€๋งŒ ์ฑ„์›Œ์ง€๋ฉด ๋‹จ์ผ ํŽ˜์ด์ง€ ํ• ๋‹น ๊ฒฝ๋กœ๋กœ ๋‚ด๋ ค๊ฐ‘๋‹ˆ๋‹ค.

    /* mm/vmalloc.c:3640-3760 */
    static inline unsigned int
    vm_area_alloc_pages(gfp_t gfp, int nid,
    unsigned int order, unsigned int nr_pages, struct page **pages)
    {
    unsigned int nr_allocated = 0;
    unsigned int nr_remaining = nr_pages;
    unsigned int max_attempt_order = MAX_PAGE_ORDER;
    struct page *page;
    int i;
    unsigned int large_order = ilog2(nr_remaining);
    gfp_t large_gfp = vmalloc_gfp_adjust(gfp, large_order) & ~__GFP_DIRECT_RECLAIM;
    
    large_order = min(max_attempt_order, large_order);
    
    /*
    * ์ฒ˜์Œ์—๋Š” page allocator๊ฐ€ large-order ํŽ˜์ด์ง€๋ฅผ ์ฃผ๋„๋ก ์‹œ๋„ํ•œ๋‹ค.
    * __vmap_pages_range()๊ฐ€ ์ •ํ™•ํžˆ order ๊ธธ์ด์˜ ๋ฌผ๋ฆฌ ์—ฐ์† chunk๋ฅผ ๊ธฐ๋Œ€ํ•˜๋ฏ€๋กœ
    * order๋ณด๋‹ค ์ž‘์€ chunk๋Š” ์‹œ๋„ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    */
    while (large_order > order && nr_remaining) {
    if (nid == NUMA_NO_NODE)
    page = alloc_pages_noprof(large_gfp, large_order);
    else
    page = alloc_pages_node_noprof(nid, large_gfp, large_order);
    
    if (unlikely(!page)) {
    max_attempt_order = --large_order;
    continue;
    }
    
    split_page(page, large_order);
    for (i = 0; i < (1U << large_order); i++)
    pages[nr_allocated + i] = page + i;
    
    nr_allocated += 1U << large_order;
    nr_remaining = nr_pages - nr_allocated;
    
    large_order = ilog2(nr_remaining);
    large_order = min(max_attempt_order, large_order);
    }
    
    /*
    * order-0 ํŽ˜์ด์ง€๋Š” bulk allocator๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์‹คํŒจ๋กœ ์ธํ•ด page array๊ฐ€
    * ์ผ๋ถ€๋งŒ ์ฑ„์›Œ์กŒ๊ฑฐ๋‚˜ ์ „ํ˜€ ์ฑ„์›Œ์ง€์ง€ ์•Š์œผ๋ฉด ๋” ๊ด€๋Œ€ํ•œ ๋‹จ์ผ ํŽ˜์ด์ง€
    * allocator๋กœ ๋Œ€์ฒดํ•œ๋‹ค.
    */
    if (!order) {
    while (nr_allocated < nr_pages) {
    unsigned int nr, nr_pages_request;
    
    /*
    * ํ—ˆ์šฉ๋˜๋Š” ์ตœ๋Œ€ ์š”์ฒญ์€ ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ์œผ๋ฉฐ ํ˜ธ์ถœ๋‹น 100ํŽ˜์ด์ง€๋‹ค.
    * bulk allocator์—์„œ ๊ธด preemption-off ์ƒํ™ฉ์„ ๋ง‰๊ธฐ ์œ„ํ•œ ๊ฒƒ์œผ๋กœ,
    * ๋ฒ”์œ„๋Š” [1:100]์ด๋‹ค.
    */
    nr_pages_request = min(100U, nr_pages - nr_allocated);
    
    /* ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์€ mempolicy๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค. nid == NUMA_NO_NODE์ผ ๋•Œ
    * nearest node๋ฅผ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด mempolicy๊ฐ€
    * interleaving์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๋ ค๋Š”๋ฐ ํ•œ ๋…ธ๋“œ์—๋งŒ ํ• ๋‹น๋  ์ˆ˜ ์žˆ๋‹ค.
    */
    if (IS_ENABLED(CONFIG_NUMA) && nid == NUMA_NO_NODE)
    nr = alloc_pages_bulk_mempolicy_noprof(gfp,
    nr_pages_request,
    pages + nr_allocated);
    else
    nr = alloc_pages_bulk_node_noprof(gfp, nid,
    nr_pages_request,
    pages + nr_allocated);
    
    nr_allocated += nr;
    
    /*
    * 0ํŽ˜์ด์ง€ ๋˜๋Š” ์ผ๋ถ€ ํŽ˜์ด์ง€๋งŒ ์–ป์—ˆ๋‹ค๋ฉด ๋‹จ์ผ ํŽ˜์ด์ง€ allocator๋กœ ๋Œ€์ฒดํ•œ๋‹ค.
    */
    if (nr != nr_pages_request)
    break;
    }
    }
    
    /* ๊ณ ์ฐจ ํŽ˜์ด์ง€ ๋˜๋Š” "bulk" ์‹คํŒจ ์‹œ ๋Œ€์ฒด ๊ฒฝ๋กœ. */
    while (nr_allocated < nr_pages) {
    if (!(gfp & __GFP_NOFAIL) && fatal_signal_pending(current))
    break;
    
    if (nid == NUMA_NO_NODE)
    page = alloc_pages_noprof(gfp, order);
    else
    page = alloc_pages_node_noprof(nid, gfp, order);
    
    if (unlikely(!page))
    break;
    
    /*
    * ๊ณ ์ฐจ ํ• ๋‹น์€ ํ˜ธ์ถœ์ž๊ฐ€ ๋…๋ฆฝ์ ์ธ ์ž‘์€ ํŽ˜์ด์ง€์ฒ˜๋Ÿผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
    * ์ผ๋ถ€ ๋“œ๋ผ์ด๋ฒ„๋Š” vmalloc_to_page() ํŽ˜์ด์ง€์— ์ž์ฒด refcounting์„ ํ•˜๊ณ ,
    * ์ผ๋ถ€๋Š” page->mapping, page->lru ๋“ฑ์„ ์‚ฌ์šฉํ•œ๋‹ค.
    */
    if (order)
    split_page(page, order);
    
    /*
    * page-order ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜๊ณ  ๋งคํ•‘ํ•˜์ง€๋งŒ tracking์€ PAGE_SIZE ํŽ˜์ด์ง€๋งˆ๋‹ค
    * ์ˆ˜ํ–‰ํ•˜์—ฌ vm_struct API๊ฐ€ ๋ฌผ๋ฆฌ/๋งคํ•‘ ํฌ๊ธฐ์— ์˜์กดํ•˜์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.
    */
    for (i = 0; i < (1U << order); i++)
    pages[nr_allocated + i] = page + i;
    
    nr_allocated += 1U << order;
    }
    
    return nr_allocated;
    }

    5. vfree โ€” ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ

    /* mm/vmalloc.c:3442-3487 */
    void vfree(const void *addr)
    {
    struct vm_struct *vm;
    int i;
    
    if (unlikely(in_interrupt())) {
    vfree_atomic(addr);
    return;
    }
    
    BUG_ON(in_nmi());
    kmemleak_free(addr);
    might_sleep();
    
    if (!addr)
    return;
    
    vm = remove_vm_area(addr);
    if (unlikely(!vm)) {
    WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",
    addr);
    return;
    }
    
    if (unlikely(vm->flags & VM_FLUSH_RESET_PERMS))
    vm_reset_perms(vm);
    /* vm์˜ ๋ชจ๋“  ํŽ˜์ด์ง€๋Š” ๊ฐ™์€ memcg์— ์ฒญ๊ตฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ฒซ ํŽ˜์ด์ง€๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. */
    if (vm->nr_pages && !(vm->flags & VM_MAP_PUT_PAGES))
    mod_memcg_page_state(vm->pages[0], MEMCG_VMALLOC, -vm->nr_pages);
    for (i = 0; i < vm->nr_pages; i++) {
    struct page *page = vm->pages[i];
    
    BUG_ON(!page);
    /*
    * huge vmalloc์˜ high-order ํ• ๋‹น์€ split๋˜๋ฏ€๋กœ
    * order-0 ํ• ๋‹น ๋ฐฐ์—ด์ฒ˜๋Ÿผ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๋‹ค.
    */
    __free_page(page);
    cond_resched();
    }
    if (!(vm->flags & VM_MAP_PUT_PAGES))
    atomic_long_sub(vm->nr_pages, &nr_vmalloc_pages);
    kvfree(vm->pages);
    kfree(vm);
    }
    EXPORT_SYMBOL(vfree);

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

    1. ์ธํ„ฐ๋ŸฝํŠธ ์ปจํ…์ŠคํŠธ: vfree_atomic()์œผ๋กœ ์ง€์—ฐ ํ•ด์ œ

    2. NMI ์ปจํ…์ŠคํŠธ: BUG_ON()์œผ๋กœ ํŒจ๋‹‰

    3. VM_FLUSH_RESET_PERMS: ๊ถŒํ•œ ์ดˆ๊ธฐํ™” ํ›„ ํ•ด์ œ

    4. VM_MAP_PUT_PAGES: ํŽ˜์ด์ง€ ๋ฐฐ์—ด ์†Œ์œ ๊ถŒ ์ด์ „๋จ

    6. vfree_atomic โ€” ์ง€์—ฐ ํ•ด์ œ

    /* mm/vmalloc.c:3408-3423 */
    void vfree_atomic(const void *addr)
    {
    struct vfree_deferred *p = raw_cpu_ptr(&vfree_deferred);
    
    BUG_ON(in_nmi());
    
    kmemleak_free(addr);
    
    /*
    * ์„ ์  ๊ฐ€๋Šฅํ•œ ์ปจํ…์ŠคํŠธ์—์„œ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ raw_cpu_ptr()๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    * ์—ฌ๊ธฐ์„œ๋Š” ์„ ์ ์ด ๋ฌธ์ œ๋˜์ง€ ์•Š๋Š”๋‹ค. llist_add() ๊ตฌํ˜„์€ lockless๋ผ์„œ
    * ๋‹ค๋ฅธ CPU์˜ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€ํ•˜๋”๋ผ๋„ ๋™์ž‘ํ•˜๋ฉฐ, schedule_work()๋„ ๊ดœ์ฐฎ๋‹ค.
    */
    if (addr && llist_add((struct llist_node *)addr, &p->list))
    schedule_work(&p->wq);
    }

    ์—ญํ• : ์›์ž์  ์ปจํ…์ŠคํŠธ์—์„œ ์ฃผ์†Œ๋ฅผ per-CPU llist์— ์ถ”๊ฐ€ํ•˜๊ณ  workqueue์—์„œ ๋‚˜์ค‘์— vfree() ๊ฒฝ๋กœ๋กœ ๋„˜๊น๋‹ˆ๋‹ค. NMI์—์„œ๋Š” ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ, addr == NULL์ด๋ฉด ๋ฆฌ์ŠคํŠธ์— ๋„ฃ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

    vmalloc(size)
    โ””โ”€> __vmalloc_node_noprof(size, 1, GFP_KERNEL, NUMA_NO_NODE, caller)
    โ””โ”€> __vmalloc_node_range_noprof(size, align, VMALLOC_START, VMALLOC_END, ...)
    โ”œโ”€> [1] __get_vm_area_node()         โ† ๊ฐ€์ƒ ์ฃผ์†Œ ์˜์—ญ ํ• ๋‹น
    โ”‚     โ””โ”€> alloc_vmap_area()          โ† vmap_area red-black tree ๊ฒ€์ƒ‰
    โ”œโ”€> [2] __vmalloc_area_node()        โ† ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น + ๋งคํ•‘
    โ”‚     โ”œโ”€> kmalloc_node() / __vmalloc_node()  โ† ํŽ˜์ด์ง€ ๋ฐฐ์—ด ํ• ๋‹น
    โ”‚     โ”œโ”€> vm_area_alloc_pages()      โ† Buddy/bulk allocator์—์„œ ํŽ˜์ด์ง€ ํ• ๋‹น
    โ”‚     โ””โ”€> __vmap_pages_range()       โ† ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋งคํ•‘
    โ”œโ”€> [3] kasan_unpoison_vmalloc()     โ† KASAN ํ•ด์ œ
    โ””โ”€> [4] clear_vm_uninitialized_flag() โ† VM_UNINITIALIZED ์ œ๊ฑฐ
    
    vfree(addr)
    โ”œโ”€> [interrupt context] vfree_atomic()
    โ”‚     โ””โ”€> llist_add() + schedule_work()
    โ””โ”€> [process context] remove_vm_area()
    โ””โ”€> [1] find_vmap_area()           โ† vmap_area ๊ฒ€์ƒ‰
    โ””โ”€> [2] unlink_va()               โ† busy tree์—์„œ ์ œ๊ฑฐ
    โ””โ”€> [3] __free_page() per page    โ† ํŽ˜์ด์ง€๋ณ„ Buddy ๋ฐ˜ํ™˜
    โ””โ”€> [4] kvfree(vm->pages)         โ† ํŽ˜์ด์ง€ ๋ฐฐ์—ด ํ•ด์ œ
    โ””โ”€> [5] kfree(vm)                 โ† vm_struct ํ•ด์ œ

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

    vmalloc vs kmalloc

    ํŠน์„ฑkmallocvmalloc
    **๋ฌผ๋ฆฌ ์—ฐ์†**O (๋ฐ˜๋“œ์‹œ ์—ฐ์†)X (๋น„์—ฐ์† ๊ฐ€๋Šฅ)
    **๊ฐ€์ƒ ์—ฐ์†**OO
    **์ตœ๋Œ€ ํฌ๊ธฐ**~4MB (order-10)๊ฐ€์ƒ ์ฃผ์†Œ ๊ณต๊ฐ„ ํ•œ๋„
    **DMA ์‚ฌ์šฉ**OX
    **์†๋„**๋น ๋ฆ„ (SLUB ์ง์ ‘)๋А๋ฆผ (ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋งคํ•‘)
    **slab ์บ์‹œ**kmalloc-32, kmalloc-64 ๋“ฑ์‚ฌ์šฉ ์•ˆ ํ•จ
    **๋Œ€ํ‘œ ์‚ฌ์šฉ์ฒ˜**์ปค๋„ ์˜ค๋ธŒ์ ํŠธ, small bufferํฐ ๋ฒ„ํผ, ๋ชจ๋“ˆ ๋กœ๋”ฉ

    ์ง์ ‘ ๋งคํ•‘ vs vmalloc/ioremap

    ํŠน์„ฑ์ง์ ‘ ๋งคํ•‘ (Direct Mapping)vmalloc/ioremap ์˜์—ญ
    **์ฃผ์š” ๋Œ€์ƒ**System RAM์˜ ์„ ํ˜• ๋งคํ•‘vmalloc ํŽ˜์ด์ง€, vmap ๋งคํ•‘, MMIO ioremap
    **๋ฌผ๋ฆฌ ์—ฐ์†์„ฑ**๋ฌผ๋ฆฌ ์ฃผ์†Œ์™€ ์„ ํ˜• ์˜คํ”„์…‹ ๊ด€๊ณ„๋ฌผ๋ฆฌ์ ์œผ๋กœ ํฉ์–ด์ง„ ํŽ˜์ด์ง€ ๋˜๋Š” ์žฅ์น˜ ๋ฌผ๋ฆฌ ์ฃผ์†Œ ๊ฐ€๋Šฅ
    **์ฃผ์†Œ ๋ณ€ํ™˜**`__pa()` / `__va()` ์‚ฐ์ˆ  ๋ณ€ํ™˜ ๊ฐ€๋ŠฅํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋งคํ•‘์„ ์กฐํšŒํ•ด์•ผ ํ•˜๋ฉฐ ์‚ฐ์ˆ  ๋ณ€ํ™˜ ๊ธˆ์ง€
    **ํŒ๋ณ„ API**`virt_addr_valid()`๊ฐ€ ์ง์ ‘ ๋งคํ•‘ RAM ํŒ๋ณ„์— ๊ฐ€๊นŒ์›€`is_vmalloc_addr()`, `/proc/vmallocinfo`๋กœ ํ™•์ธ
    **DMA ์ ํ•ฉ์„ฑ**์ผ๋ฐ˜์ ์œผ๋กœ DMA API์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ`vmalloc()` ์ฃผ์†Œ๋Š” ์ง์ ‘ DMA ๋ฒ„ํผ๋กœ ๋ถ€์ ํ•ฉ
    **๋Œ€ํ‘œ ์‚ฌ์šฉ์ฒ˜**Buddy/SLUB๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ์ผ๋ฐ˜ ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌํฐ ์ปค๋„ ๋ฒ„ํผ, ๋ชจ๋“ˆ, BPF/JIT ๋ณด์กฐ ์˜์—ญ, MMIO mapping

    ioremap()์€ RAM์„ ์ƒˆ๋กœ ํ• ๋‹นํ•˜๋Š” API๊ฐ€ ์•„๋‹ˆ๋ผ ์žฅ์น˜ ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ™์€ MMIO ๋ฌผ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ปค๋„ ๊ฐ€์ƒ ์ฃผ์†Œ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” API์ž…๋‹ˆ๋‹ค. ์ž„๋ฒ ๋””๋“œ SoC๋‚˜ PCI ๋“œ๋ผ์ด๋ฒ„์—์„œ๋Š” Device Tree/ACPI/PCI BAR๊ฐ€ ์•Œ๋ ค์ค€ ๋ฆฌ์†Œ์Šค๋ฅผ devm_ioremap_resource()๋กœ ๋งคํ•‘ํ•˜๊ณ , DMA ๋ฒ„ํผ๋Š” ๋ณ„๋„๋กœ dma_alloc_coherent() ๋˜๋Š” dma_map_*() ๊ณ„์—ด์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    kvmalloc ์„ ํƒ ๊ธฐ์ค€

    ์ƒํ™ฉ๊ถŒ์žฅ API์ด์œ 
    ํฌ๊ธฐ๊ฐ€ ์ž‘๊ณ  ๋ฌผ๋ฆฌ ์—ฐ์†์„ฑ์ด ํ•„์š”`kmalloc()`SLUB fast path์™€ DMA ์นœํ™”์„ฑ
    ํฌ๊ธฐ๊ฐ€ ํฌ์ง€๋งŒ ๋ฌผ๋ฆฌ ์—ฐ์†์„ฑ์ด ํ•„์š” ์—†์Œ`vmalloc()`์™ธ๋ถ€ ๋‹จํŽธํ™” ์˜ํ–ฅ์„ ๋œ ๋ฐ›์Œ
    ํฌ๊ธฐ๊ฐ€ ๋Ÿฐํƒ€์ž„์— ๋‹ฌ๋ผ ์ž‘์„ ์ˆ˜๋„ ํผ์งํ•  ์ˆ˜๋„ ์žˆ์Œ`kvmalloc()``kmalloc()` ์šฐ์„  ์‹œ๋„ ํ›„ ํฐ ์š”์ฒญ์—์„œ vmalloc fallback
    0์œผ๋กœ ์ดˆ๊ธฐํ™”๋œ ํฐ ๋ฒ„ํผ`kvzalloc()` / `vzalloc()`์ดˆ๊ธฐํ™” ๋น„์šฉ์„ API ์˜๋ฏธ์— ํฌํ•จ
    NUMA ๋…ธ๋“œ ์„ ํ˜ธ๊ฐ€ ์žˆ์Œ`kvmalloc_node()` / `vmalloc_node()`page allocator ํ˜ธ์ถœ ์‹œ ๋…ธ๋“œ ํžŒํŠธ ์ „๋‹ฌ

    kvmalloc() ๊ณ„์—ด์€ ์ž‘์€ ํฌ๊ธฐ์—์„œ๋Š” kmalloc์˜ ๋น ๋ฅธ ๊ฒฝ๋กœ๋ฅผ ์‚ด๋ฆฌ๊ณ , ํฐ ํฌ๊ธฐ๋‚˜ ๋‹จํŽธํ™” ์ƒํ™ฉ์—์„œ๋Š” vmalloc fallback์œผ๋กœ ์„ฑ๊ณต๋ฅ ์„ ๋†’์ž…๋‹ˆ๋‹ค. ๋‹จ, fallback ์ดํ›„ ๋ฐ˜ํ™˜ ์ฃผ์†Œ๋Š” vmalloc ์ฃผ์†Œ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ฌผ๋ฆฌ ์—ฐ์†์„ฑ, DMA ์ง์ ‘ ์‚ฌ์šฉ, virt_to_phys() ๊ฐ™์€ ๊ฐ€์ •์€ ๋‘๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

    vmalloc API ๋น„๊ต

    APIํŠน์ง•์šฉ๋„
    `vmalloc(size)`GFP_KERNEL, nozeroํฐ ์ปค๋„ ๋ฒ„ํผ
    `vzalloc(size)`GFP_KERNEL + __GFP_ZERO์ œ๋กœ ์ดˆ๊ธฐํ™” ํ•„์š” ์‹œ
    `vmalloc_node(size, node)`ํŠน์ • NUMA ๋…ธ๋“œNUMA ์นœํ™” ํ• ๋‹น
    `vmalloc_user(size)`VM_USERMAP ํ”Œ๋ž˜๊ทธuserspace ๋งคํ•‘ ๊ฐ€๋Šฅ
    `vmalloc_32(size)`32๋น„ํŠธ ํ˜ธํ™˜32๋น„ํŠธ ๋“œ๋ผ์ด๋ฒ„
    `__vmalloc(size, gfp)`GFP ํ”Œ๋ž˜๊ทธ ์ง€์ •์„ธ๋ฐ€ํ•œ ์ œ์–ด

    huge vmalloc ์กฐ๊ฑด

    ์กฐ๊ฑด๊ฒฐ๊ณผ
    `CONFIG_HAVE_ARCH_HUGE_VMALLOC` ๋ฏธํ™œ์„ฑํ™”huge vmalloc ๋ถˆ๊ฐ€
    `vmap_allow_huge = false` (nohugevmalloc ํŒŒ๋ผ๋ฏธํ„ฐ)huge vmalloc ๋ถˆ๊ฐ€
    `size < PMD_SIZE` (์ผ๋ฐ˜์ ์œผ๋กœ 2MB)order-0 ํ• ๋‹น
    `arch_vmap_pmd_supported(prot)` falsePMD ๋งคํ•‘ ๋ถˆ๊ฐ€
    huge page ์‹คํŒจorder-0 fallback ์ž๋™

    vmalloc ์˜์—ญ ๋ฐฐ์น˜์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์กฐ๊ฑด

    ์กฐ๊ฑด์˜ํ–ฅํ™•์ธ ๋ฐฉ๋ฒ•
    x86_64 4๋‹จ๊ณ„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”vmalloc/ioremap ์˜์—ญ์ด ๋Œ€ํ‘œ์ ์œผ๋กœ `ffffc90000000000` ๊ทผ์ฒ˜์— ๋ฐฐ์น˜`sudo cat /sys/kernel/debug/kernel_page_tables`
    x86_64 5๋‹จ๊ณ„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”(LA57)์œ ์ €/์ปค๋„ ๊ฐ€์ƒ ์ฃผ์†Œ ํญ์ด ์ปค์ง€๊ณ  vmalloc ์˜์—ญ ๊ทœ๋ชจ๋„ PB ๋‹จ์œ„๋กœ ํ™•์žฅ`grep la57 /proc/cpuinfo`, `zgrep CONFIG_X86_5LEVEL /proc/config.gz`
    KASLR์ ˆ๋Œ€ ์ฃผ์†Œ๊ฐ€ ๋ถ€ํŒ…๋งˆ๋‹ค ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ`dmesggrep -i kaslr`
    KASANshadow ์˜์—ญ์ด ์ถ”๊ฐ€๋˜์–ด vmalloc ์ฃผ๋ณ€ ๋ฐฐ์น˜์™€ ๊ฐ€๋“œ ์ •์ฑ…์— ์˜ํ–ฅ`zgrep CONFIG_KASAN /proc/config.gz`
    PTI์‚ฌ์šฉ์ž CR3์™€ ์ปค๋„ CR3 ๊ด€์ ์˜ ๋งคํ•‘์ด ๋‹ฌ๋ผ์ง`grep pti /proc/cpuinfo`
    ARM64/RISC-VVA_BITS, ํŽ˜์ด์ง€ ํฌ๊ธฐ, Sv39/Sv48/Sv57์— ๋”ฐ๋ผ vmalloc ๋ฒ”์œ„๊ฐ€ ๋‹ค๋ฆ„`uname -m`, `zgrep -E 'CONFIG_ARM64_VA_BITSCONFIG_RISCV' /proc/config.gz`

    vfree ๋™์ž‘ ์กฐ๊ฑด

    ์ปจํ…์ŠคํŠธ๋™์ž‘
    ํ”„๋กœ์„ธ์Šค ์ปจํ…์ŠคํŠธ์ฆ‰์‹œ ํ•ด์ œ (remove_vm_area + __free_page)
    ์ธํ„ฐ๋ŸฝํŠธ ์ปจํ…์ŠคํŠธvfree_atomic() โ†’ workqueue ์ง€์—ฐ ํ•ด์ œ
    NMI ์ปจํ…์ŠคํŠธBUG_ON() ํŒจ๋‹‰
    `__GFP_NOFAIL` ํ• ๋‹น์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ ๋กœ์ง ์—†์Œ (๊ฒฝ๊ณ  ํ›„ ์‹คํŒจ)

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” โ€” ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์กฐ๊ฐ๋„
  • Buddy Allocator โ€” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น์ž
  • SLUB ํ• ๋‹น์ž โ€” ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ ์บ์‹œ
  • VMA / mmap โ€” ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ ๊ด€๋ฆฌ
  • Memblock ํ• ๋‹น์ž โ€” ๋ถ€ํŒ… ์ดˆ๊ธฐ ํ• ๋‹น
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜ โ€” kswapd, direct reclaim

  • ์š”์•ฝ

    vmalloc์˜ ํ•ต์‹ฌ ๋‚ด์šฉ:

    1. vmalloc 3๋‹จ๊ณ„ ๋‚ด๋ถ€ ๋™์ž‘: alloc_vmap_area() โ†’ alloc_pages_bulk() โ†’ vmap_pages_range()

    2. kvmalloc: kmalloc ์šฐ์„  ์‹œ๋„ ํ›„ vmalloc fallback

    3. vmalloc vs kmalloc ๋น„๊ต: ๋ฌผ๋ฆฌ ์—ฐ์†, ์ตœ๋Œ€ ํฌ๊ธฐ, ์šฉ๋„

    4. vmalloc ์˜์—ญ ๋ช…๋ น์–ด: /proc/vmallocinfo, /proc/meminfo์˜ VmallocTotal/Used/Chunk

    5. ioremap๊ณผ์˜ ๊ด€๊ณ„: vmalloc/ioremap ์˜์—ญ ๊ณต์œ  (์ปค๋„ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ๋งต)

    vmalloc์€ ๊ฐ€์ƒ ์ฃผ์†Œ ์˜ˆ์•ฝ, ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ™•๋ณด, ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋งคํ•‘, KASAN/memcg ํ›„์ฒ˜๋ฆฌ๊ฐ€ ํ•จ๊ป˜ ์›€์ง์ด๋Š” ๊ฒฝ๋กœ์ด๋ฏ€๋กœ ์ฃผ์†Œ ์„ฑ๊ฒฉ๊ณผ ํ• ๋‹น ์ปจํ…์ŠคํŠธ๋ฅผ ๋จผ์ € ๊ตฌ๋ถ„ํ•ด์•ผ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.