๐ 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 ์์ญ ์ฌ์ฉ ํํฉ
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
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
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 ๋
ธ๋ ํ๋๋ง ์ฌ์ฉ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
| ํน์ฑ | kmalloc | vmalloc |
|---|---|---|
| **๋ฌผ๋ฆฌ ์ฐ์** | O (๋ฐ๋์ ์ฐ์) | X (๋น์ฐ์ ๊ฐ๋ฅ) |
| **๊ฐ์ ์ฐ์** | O | O |
| **์ต๋ ํฌ๊ธฐ** | ~4MB (order-10) | ๊ฐ์ ์ฃผ์ ๊ณต๊ฐ ํ๋ |
| **DMA ์ฌ์ฉ** | O | X |
| **์๋** | ๋น ๋ฆ (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)` false | PMD ๋งคํ ๋ถ๊ฐ |
| 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 | ์ ๋ ์ฃผ์๊ฐ ๋ถํ ๋ง๋ค ๋ฌ๋ผ์ง ์ ์์ | `dmesg | grep -i kaslr` |
| KASAN | shadow ์์ญ์ด ์ถ๊ฐ๋์ด vmalloc ์ฃผ๋ณ ๋ฐฐ์น์ ๊ฐ๋ ์ ์ฑ ์ ์ํฅ | `zgrep CONFIG_KASAN /proc/config.gz` | |
| PTI | ์ฌ์ฉ์ CR3์ ์ปค๋ CR3 ๊ด์ ์ ๋งคํ์ด ๋ฌ๋ผ์ง | `grep pti /proc/cpuinfo` | |
| ARM64/RISC-V | VA_BITS, ํ์ด์ง ํฌ๊ธฐ, Sv39/Sv48/Sv57์ ๋ฐ๋ผ vmalloc ๋ฒ์๊ฐ ๋ค๋ฆ | `uname -m`, `zgrep -E 'CONFIG_ARM64_VA_BITS | CONFIG_RISCV' /proc/config.gz` |
vfree ๋์ ์กฐ๊ฑด
| ์ปจํ ์คํธ | ๋์ |
|---|---|
| ํ๋ก์ธ์ค ์ปจํ ์คํธ | ์ฆ์ ํด์ (remove_vm_area + __free_page) |
| ์ธํฐ๋ฝํธ ์ปจํ ์คํธ | vfree_atomic() โ workqueue ์ง์ฐ ํด์ |
| NMI ์ปจํ ์คํธ | BUG_ON() ํจ๋ |
| `__GFP_NOFAIL` ํ ๋น | ์คํจ ์ ์ฌ์๋ ๋ก์ง ์์ (๊ฒฝ๊ณ ํ ์คํจ) |
๊ด๋ จ ๋ฌธ์
์์ฝ
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 ํ์ฒ๋ฆฌ๊ฐ ํจ๊ป ์์ง์ด๋ ๊ฒฝ๋ก์ด๋ฏ๋ก ์ฃผ์ ์ฑ๊ฒฉ๊ณผ ํ ๋น ์ปจํ ์คํธ๋ฅผ ๋จผ์ ๊ตฌ๋ถํด์ผ ์์ ํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.