Memblock ํ ๋น์
๊ฐ์ (Overview)
Memblock์ Linux ์ปค๋ ๋ถํ
์ด๊ธฐ์ ์ผ๋ฐ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์๊ฐ ์์ง ์ฌ์ฉ ๋ถ๊ฐ๋ฅํ ์ํ์์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ฉ์ปค๋์ฆ์ด๋ค. ์์คํ
๋ฉ๋ชจ๋ฆฌ๋ฅผ "์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉ๋ชจ๋ฆฌ(memory)"์ "์์ฝ๋ ๋ฉ๋ชจ๋ฆฌ(reserved)" ๋ ๊ฐ์ง ์ปฌ๋ ์
์ผ๋ก ๋๋์ด ๊ด๋ฆฌํ๋ฉฐ, ํ์จ์ด(E820/UEFI/Device Tree)๊ฐ ์ ๋ฌํ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์์ ํ์ฑํ์ฌ ์ด๊ธฐ ํ ๋น์ ์ํํ๋ค. ๋ถํ
์ด ์๋ฃ๋๋ฉด memblock_free_all()์ ํตํด Buddy Allocator์ ๋จ์ ํ์ด์ง๋ฅผ ๋ชจ๋ ๋ฐํํ๊ณ , CONFIG_ARCH_KEEP_MEMBLOCK์ด ์ค์ ๋์ง ์์ผ๋ฉด memblock ๋ฐ์ดํฐ ๊ตฌ์กฐ ์์ฒด๋ฅผ ํด์ ํ๋ค.
memblock์ ํฌ๊ฒ ์ธ ๊ฐ์ง ๋ฉ๋ชจ๋ฆฌ ํ์
์ ๊ด๋ฆฌํ๋ค: memory(์ปค๋์ด ์ฌ์ฉํ ์ ์๋ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ), reserved(์ด๋ฏธ ํ ๋น/์์ฝ๋ ์์ญ), physmem(์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ, ์ผ๋ถ ์ํคํ
์ฒ์์๋ง ์ง์). ๊ฐ ํ์
์ struct memblock_type์ผ๋ก ํํ๋๋ฉฐ, ์ ๋ ฌ๋ memblock_region ๋ฐฐ์ด์ ํฌํจํ๋ค. ํ ๋น์ bottom-up ๋๋ top-down ๋ฐฉ์์ผ๋ก ์ํ๋๋ฉฐ, NUMA ๋
ธ๋ ํ ๋น, ๋ฏธ๋ฌ๋ง, nomap ํ๋๊ทธ ๋ฑ์ ์ง์ํ๋ค.
์ผ์ ๋น์ : memblock์ ์ ์ ์ฐฝ๊ณ ๊ด๋ฆฌ ์์คํ
(Buddy Allocator)์ด ์ผ์ง๊ธฐ ์ , ์ด์ฌ ํ์ฅ์์ ์์๋ก ์ฐ๋ ํฐ ์ข
์ด ๋ฐฐ์น๋์ ๊ฐ๋ค. ํ์จ์ด๊ฐ ๋๊ฒจ์ค ๊ฑด๋ฌผ ๋๋ฉด์์ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ์ memory์ ํ์ํ๊ณ , ์ปค๋ ์ด๋ฏธ์ง, initrd, ACPI/EFI ํ
์ด๋ธ, crashkernel์ฒ๋ผ ์ด๋ฏธ ์ ์ ํ ๋ฐฉ์ reserved์ ํ์ํ ๋ค, ๊ณต์ฌ๊ฐ ๋๋๋ฉด ๋จ์ ๋ฐฉ ๋ชฉ๋ก์ Buddy Allocator์ ๋๊ธด๋ค.
์์ค ํ์ผ ๊ฒฝ๋ก:
mm/memblock.c โ ํต์ฌ ๊ตฌํ (2768์ค)
include/linux/memblock.h โ ๊ณต๊ฐ API ๋ฐ ์๋ฃ๊ตฌ์กฐ ์ ์ (624์ค)
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# memblock ๋๋ฒ๊ทธ ํ์ฑํ (์ปค๋ ํ๋ผ๋ฏธํฐ)
# ๋ถํ
์: memblock=debug
# ํ์ฌ memblock ์ํ ํ์ธ (CONFIG_MEMBLOCK_DEBUG ํ์ฑํ ์)
dmesg | grep -i memblock
# ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ์ ์ฒด ํฌ๊ธฐ ํ์ธ
cat /proc/meminfo | head -5
# Buddy Allocator์ ๋ฐํ๋ ํ ๋ฉ๋ชจ๋ฆฌ ํํฉ
cat /proc/buddyinfo
# memblock์ด ๊ด๋ฆฌํ๋ ์์ญ ํ์ธ (debugfs)
sudo cat /sys/kernel/debug/memblock/memory 2>/dev/null
sudo cat /sys/kernel/debug/memblock/reserved 2>/dev/null
# ์ปค๋ ๋ถํ
์ memblock ๋ฉ์์ง ํ์ธ
dmesg | grep -E 'BIOS-e820|Memory:|MEMBLOCK'
# NUMA ๋
ธ๋๋ณ ๋ฉ๋ชจ๋ฆฌ ๋ฐฐ์น
numactl -H 2>/dev/null
# ํ์จ์ด/์ปค๋์ด ๋ณธ ๋ฌผ๋ฆฌ ์ฃผ์ ๊ณต๊ฐ
cat /proc/iomem | head -80
# memblock์ ์ํฅ์ ์ค ๋ถํ
ํ๋ผ๋ฏธํฐ ํ์ธ
cat /proc/cmdline
# initrd, crashkernel, reserved-memory๊น์ง ํจ๊ป ํ์ธ
dmesg | grep -Ei 'memblock|BIOS-e820|efi: mem|OF: fdt|reserved-memory|initrd|crashkernel'
# CONFIG_HAVE_MEMBLOCK_PHYS_MAP + debugfs ์ ์ง ์ physmem ํ์ธ
sudo cat /sys/kernel/debug/memblock/physmem 2>/dev/null
# ์ง์ ๋งคํ, ํ์ด์ง ํ
์ด๋ธ, vmalloc ์ฌ์ฉ๋ ํ์ธ
grep -E 'Vmalloc|DirectMap|PageTables' /proc/meminfo
# x86 CONFIG_X86_PTDUMP + debugfs ํ์ฑํ ์ ์ปค๋ ํ์ด์ง ํ
์ด๋ธ ํ์ธ
sudo cat /sys/kernel/debug/kernel_page_tables 2>/dev/null | head -40
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
struct memblock_region โ ๋ฉ๋ชจ๋ฆฌ ์์ญ ํ๋
struct memblock_region {
phys_addr_t base;
phys_addr_t size;
enum memblock_flags flags;
#ifdef CONFIG_NUMA
int nid;
#endif
};
์์น: include/linux/memblock.h:73-80. base์ size๋ ๋ฐ์ด๋ฆผ ๊ตฌ๊ฐ [base, base + size)๋ฅผ ๋ง๋ค๊ณ , flags๋ HOTPLUG/MIRROR/NOMAP ๊ฐ์ ์์ฑ์ ๋ด๋๋ค. CONFIG_NUMA์์๋ nid๊ฐ ํจ๊ป ์ ์ฅ๋์ด ์ด๊ธฐ ๋ฌผ๋ฆฌ ์ฃผ์ ๋ฒ์๋ฅผ NUMA ๋
ธ๋๋ก ๋ฌถ์ ์ ์๋ค.
struct memblock_type โ ๋ฉ๋ชจ๋ฆฌ ์์ญ ์ปฌ๋ ์
struct memblock_type {
unsigned long cnt;
unsigned long max;
phys_addr_t total_size;
struct memblock_region *regions;
char *name;
};
์์น: include/linux/memblock.h:90-96. cnt๋ ํ์ฌ ์ ํจํ region ๊ฐ์์ด๊ณ max๋ ๋ฐฐ์ด ์ฉ๋์ด๋ค. total_size๋ ์ด ํ์
์ ๋ค์ด ์๋ ์์ญ ํฌ๊ธฐ์ ํฉ์ด๋ฉฐ, regions๋ base ๊ธฐ์ค์ผ๋ก ์ ๋ ฌ๋ memblock_region ๋ฐฐ์ด์ ๊ฐ๋ฆฌํจ๋ค.
struct memblock โ ์ ์ญ ํ ๋น์ ๋ฉํ๋ฐ์ดํฐ
struct memblock {
bool bottom_up; /* is bottom up direction? */
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
์์น: include/linux/memblock.h:105-110. bottom_up์ ๋ฎ์ ์ฃผ์๋ถํฐ ์ฐพ์์ง ๋์ ์ฃผ์๋ถํฐ ์ฐพ์์ง๋ฅผ ์ ํ๊ณ , current_limit์ MEMBLOCK_ALLOC_ACCESSIBLE ์์ฒญ์ ์ํ์ผ๋ก ์ฐ์ธ๋ค. memory๋ ์ฌ์ฉ ๊ฐ๋ฅํ RAM ๋ชฉ๋ก, reserved๋ ์ด๋ฏธ ์ ์ ๋ ๋ฌผ๋ฆฌ ์ฃผ์ ๋ชฉ๋ก์ด๋ค.
enum memblock_flags โ ์์ญ ์์ฑ ํ๋๊ทธ
enum memblock_flags {
MEMBLOCK_NONE = 0x0, /* ํน๋ณํ ์์ฒญ ์์ */
MEMBLOCK_HOTPLUG = 0x1, /* ํซํ๋ฌ๊ทธ ๊ฐ๋ฅํ ์์ญ */
MEMBLOCK_MIRROR = 0x2, /* ๋ฏธ๋ฌ๋ง๋ ์์ญ */
MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */
MEMBLOCK_DRIVER_MANAGED = 0x8, /* always detected via a driver */
MEMBLOCK_RSRV_NOINIT = 0x10, /* don't initialize struct pages */
MEMBLOCK_RSRV_KERN = 0x20, /* memory reserved for kernel use */
MEMBLOCK_KHO_SCRATCH = 0x40, /* scratch memory for kexec handover */
};
์์น: include/linux/memblock.h:55-64. MEMBLOCK_NOMAP์ ์ง์ ๋งคํ์์ ์ ์ธ๋๋ RAM์ ํ์ํ๊ณ , MEMBLOCK_RSRV_KERN์ memblock ํ ๋น API๊ฐ ์์ฝํ ์ปค๋์ฉ ์์ญ์ ๋ถ๋๋ค. MEMBLOCK_DRIVER_MANAGED์ MEMBLOCK_KHO_SCRATCH๋ ์ผ๋ฐ System RAM๊ณผ ๋ค๋ฅด๊ฒ ์ทจ๊ธํด์ผ ํ๋ ์์ญ์ ๊ตฌ๋ถํ๋ค.
์ ์ญ ์ด๊ธฐํ ๋ฐ์ดํฐ
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_MEMORY_REGIONS] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;
struct memblock memblock __initdata_memblock = {
.memory.regions = memblock_memory_init_regions,
.memory.max = INIT_MEMBLOCK_MEMORY_REGIONS,
.memory.name = "memory",
.reserved.regions = memblock_reserved_init_regions,
.reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS,
.reserved.name = "reserved",
.bottom_up = false,
.current_limit = MEMBLOCK_ALLOC_ANYWHERE,
};
์์น: mm/memblock.c:123-140. ์ด๊ธฐ ๋ฐฐ์ด ์ฉ๋์ INIT_MEMBLOCK_MEMORY_REGIONS์ INIT_MEMBLOCK_RESERVED_REGIONS๋ก ์์ํ๋ค. ์์ญ์ด ๋ ๋ง์ด ํ์ํ ๋๋ memblock_allow_resize() ์ดํ memblock_double_array()๊ฐ ์ ๋ฐฐ์ด์ ์ก์ ํ์ฅํ๋ค.
struct memblock_type physmem = {
.regions = memblock_physmem_init_regions,
.max = INIT_PHYSMEM_REGIONS,
.name = "physmem",
};
์์น: mm/memblock.c:142-148. CONFIG_HAVE_MEMBLOCK_PHYS_MAP์ด ์๋ ์ํคํ
์ฒ์์๋ physmem์ด ์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๋ชฉ๋ก์ ๋ณ๋๋ก ๋ณด์กดํ๋ค. mem= ๊ฐ์ ์ ํ์ผ๋ก ์ปค๋ ์ฌ์ฉ ๊ฐ๋ฅ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ค์ด๋, ํ์จ์ด๊ฐ ๋ณด๊ณ ํ ์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๊ด์ฐฐ์ ์ physmem ์ชฝ์ ๋จ์ ์ ์๋ค.
ํต์ฌ ํจ์
1. memblock_add_range() โ ์์ญ ์ถ๊ฐ์ ํต์ฌ
// mm/memblock.c:609-713
static int __init_memblock memblock_add_range(struct memblock_type *type,
phys_addr_t base, phys_addr_t size,
int nid, enum memblock_flags flags)
์ญํ : ์๋ก์ด ๋ฉ๋ชจ๋ฆฌ ์์ญ์ memblock ํ์ ์ ์ถ๊ฐํ๋ค. ๊ธฐ์กด ์์ญ๊ณผ ์ค๋ณต๋ ์ ์์ผ๋ฉฐ, ์ค๋ณต ์ ๊ฒน์น์ง ์๋ ๋ถ๋ถ๋ง ์ฝ์ ํ๋ค. ์ถ๊ฐ ํ ์ธ์ ํธํ ์์ญ์ ์๋ ๋ณํฉํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
cnt * 2 + 1 <= max์ด๋ฉด ๋ฐ๋ก ์ฝ์
๊ฐ๋ฅํ๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด ํฌ๊ธฐ ํ์ฅ ํ ๋ฐ๋ณตmemblock_double_array()๋ก ๋ฐฐ์ด ํฌ๊ธฐ 2๋ฐฐ ํ์ฅ ๊ฐ๋ฅif (!size)
return 0;
/* special case for empty array */
if (type->regions[0].size == 0) {
WARN_ON(type->cnt != 0 || type->total_size);
type->regions[0].base = base;
type->regions[0].size = size;
type->regions[0].flags = flags;
memblock_set_region_node(&type->regions[0], nid);
type->total_size = size;
type->cnt = 1;
return 0;
}
/*
* The worst case is when new range overlaps all existing regions,
* then we'll need type->cnt + 1 empty regions in @type. So if
* type->cnt * 2 + 1 is less than or equal to type->max, we know
* that there is enough empty regions in @type, and we can insert
* regions directly.
*/
if (type->cnt * 2 + 1 <= type->max)
insert = true;
์์น: mm/memblock.c:619-642. ์ฒซ region์ ๋ฃ๋ ๊ฒฝ์ฐ์ ๋ฐฐ์ด ์ฌ์ ๊ฐ ์ถฉ๋ถํ ๊ฒฝ์ฐ๋ฅผ ๋จผ์ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋ค.
/* insert the remaining portion */
if (base < end) {
nr_new++;
if (insert) {
if (start_rgn == -1)
start_rgn = idx;
end_rgn = idx + 1;
memblock_insert_region(type, idx, base, end - base,
nid, flags);
}
}
if (!nr_new)
return 0;
/*
* If this was the first round, resize array and repeat for actual
* insertions; otherwise, merge and return.
*/
if (!insert) {
while (type->cnt + nr_new > type->max)
if (memblock_double_array(type, obase, size) < 0)
return -ENOMEM;
insert = true;
goto repeat;
} else {
memblock_merge_regions(type, start_rgn, end_rgn);
return 0;
}
์์น: mm/memblock.c:684-712. ์ฒซ pass๋ ํ์ํ region ์๋ฅผ ๊ณ์ฐํ๊ณ , ๋ ๋ฒ์งธ pass๋ ์ฝ์
ํ ์ธ์ ํ ๊ฐ์ ์์ฑ์ region์ ๋ณํฉํ๋ค.
2. memblock_find_in_range_node() โ ์์ญ ๊ฒ์
// mm/memblock.c:308-328
static phys_addr_t __init_memblock memblock_find_in_range_node(
phys_addr_t size, phys_addr_t align,
phys_addr_t start, phys_addr_t end, int nid,
enum memblock_flags flags)
์ญํ : ์ง์ ๋ ๋ฒ์์ NUMA ๋ ธ๋์์ ์์ฒญ ํฌ๊ธฐ์ ๋น ์์ญ์ ์ฐพ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
end == MEMBLOCK_ALLOC_ACCESSIBLE โ memblock.current_limit๋ก ๋์ฒดstart๋ ์ต์ PAGE_SIZE (์ฒซ ํ์ด์ง ํ ๋น ํํผ)memblock_bottom_up() โ bottom-up / top-down ๋ถ๊ธฐstatic phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size,
phys_addr_t align, phys_addr_t start,
phys_addr_t end, int nid,
enum memblock_flags flags)
{
/* pump up @end */
if (end == MEMBLOCK_ALLOC_ACCESSIBLE ||
end == MEMBLOCK_ALLOC_NOLEAKTRACE)
end = memblock.current_limit;
/* avoid allocating the first page */
start = max_t(phys_addr_t, start, PAGE_SIZE);
end = max(start, end);
if (memblock_bottom_up())
return __memblock_find_range_bottom_up(start, end, size, align,
nid, flags);
else
return __memblock_find_range_top_down(start, end, size, align,
nid, flags);
}
์์น: mm/memblock.c:308-328. MEMBLOCK_ALLOC_NOLEAKTRACE๋ ์ ๊ทผ ๊ฐ๋ฅ ์ํ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก current_limit์ ์ฌ์ฉํ์ง๋ง, ๋์ค์ kmemleak ๋ฑ๋ก์ ๊ฑด๋๋ฐ๋ ์๋ฏธ๊ฐ ์ถ๊ฐ๋๋ค.
3. memblock_alloc_range_nid() โ ๋ถํ ๋ฉ๋ชจ๋ฆฌ ํ ๋น
// mm/memblock.c:1474-1546
phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,
phys_addr_t align, phys_addr_t start,
phys_addr_t end, int nid, bool exact_nid)
์ญํ : ๋ถํ ์ค ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ๊ณ ๋ฌผ๋ฆฌ ์ฃผ์๋ฅผ ๋ฐํํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
slab_is_available() โ slab ํ ๋น์๋ก fallbackexact_nid == false โ ๋ชจ๋ ๋
ธ๋๋ก ํ์ฅkmemleak_alloc_phys()๋ก ๋์ ์ถ์ ์ ์ธif (WARN_ON_ONCE(slab_is_available())) {
void *vaddr = kzalloc_node(size, GFP_NOWAIT, nid);
return vaddr ? virt_to_phys(vaddr) : 0;
}
if (!align) {
/* powerpc์์ ๋ถํ
์ด๊ธฐ์๋ WARN์ ์ฌ์ฉํ ์ ์์ */
dump_stack();
align = SMP_CACHE_BYTES;
}
again:
found = memblock_find_in_range_node(size, align, start, end, nid,
flags);
if (found && !__memblock_reserve(found, size, nid, MEMBLOCK_RSRV_KERN))
goto done;
if (numa_valid_node(nid) && !exact_nid) {
found = memblock_find_in_range_node(size, align, start,
end, NUMA_NO_NODE,
flags);
if (found && !memblock_reserve_kern(found, size))
goto done;
}
if (flags & MEMBLOCK_MIRROR) {
flags &= ~MEMBLOCK_MIRROR;
pr_warn_ratelimited("Could not allocate %pap bytes of mirrored memory\n",
&size);
goto again;
}
์์น: mm/memblock.c:1487-1518. slab ์ค๋น ์ดํ์ ์ฐ๋ฐ์ ํธ์ถ์ kzalloc_node()๋ก ์ฐํํ๊ณ , NUMA fallback๊ณผ mirror fallback์ ๊ฐ์ ๊ฒ์ ํจ์๋ฅผ ๋ค๋ฅธ ์กฐ๊ฑด์ผ๋ก ๋ค์ ํธ์ถํ๋ค.
done:
/*
* Skip kmemleak for those places like kasan_init() and
* early_pgtable_alloc() due to high volume.
*/
if (end != MEMBLOCK_ALLOC_NOLEAKTRACE)
/*
* Memblock allocated blocks are never reported as
* leaks. This is because many of these blocks are
* only referred via the physical address which is
* not looked up by kmemleak.
*/
kmemleak_alloc_phys(found, size, 0);
/*
* Some Virtual Machine platforms, such as Intel TDX or AMD SEV-SNP,
* require memory to be accepted before it can be used by the
* guest.
*
* Accept the memory of the allocated buffer.
*/
accept_memory(found, size);
return found;
์์น: mm/memblock.c:1522-1545. ๋ฌผ๋ฆฌ ์ฃผ์๋ง์ผ๋ก ์ฐธ์กฐ๋๋ ์ด๊ธฐ ํ ๋น์ kmemleak ์คํ์ ํผํ๊ณ , TDX/SEV-SNP ๊ฐ์ ๊ฒ์คํธ ํ๊ฒฝ์์๋ ๋ฐํ ์ ์ ๋ฉ๋ชจ๋ฆฌ accept๋ฅผ ์ํํ๋ค.
4. memblock_alloc_try_nid() โ ๊ฐ์ ์ฃผ์ ๋ฐํ ํ ๋น
// mm/memblock.c:1716-1732
void * __init memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align,
phys_addr_t min_addr, phys_addr_t max_addr, int nid)
์ญํ : memblock_alloc_range_nid()์ ๊ฒฐ๊ณผ๋ฅผ phys_to_virt()๋ก ๋ณํํ์ฌ ๊ฐ์ ์ฃผ์๋ฅผ ๋ฐํํ๊ณ , memset(ptr, 0, size)๋ก ์ ๋ก์ํ๋ค.
void * __init memblock_alloc_try_nid(
phys_addr_t size, phys_addr_t align,
phys_addr_t min_addr, phys_addr_t max_addr,
int nid)
{
void *ptr;
memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=%pa max_addr=%pa %pS\n",
__func__, (u64)size, (u64)align, nid, &min_addr,
&max_addr, (void *)_RET_IP_);
ptr = memblock_alloc_internal(size, align,
min_addr, max_addr, nid, false);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
์์น: mm/memblock.c:1716-1732. memblock_alloc_try_nid_raw()์ ๋ฌ๋ฆฌ ๋ฐํ ์ง์ ๋ฒํผ๋ฅผ 0์ผ๋ก ์ฑ์ด๋ค.
5. memblock_free_all() โ Buddy Allocator์ ๋ฐํ
void __init memblock_free_all(void)
{
unsigned long pages;
free_unused_memmap();
reset_all_zones_managed_pages();
memblock_clear_kho_scratch_only();
pages = free_low_memory_core_early();
totalram_pages_add(pages);
}
์ญํ : ๋ถํ
์ด ์๋ฃ๋๋ฉด ๋จ์ ๋ชจ๋ free ๋ฉ๋ชจ๋ฆฌ๋ฅผ Buddy Allocator์ ๋ฐํํ๋ค. free_low_memory_core_early()๊ฐ for_each_free_mem_range()๋ก ์ํํ๋ฉฐ __free_pages_memory()๋ก ๊ฐ ์์ญ์ ํด์ ํ๋ค.
ํธ์ถ ํ๋ฆ
ํ์จ์ด ๋ฉ๋ชจ๋ฆฌ๋งต (E820/UEFI/DT)
โ
โผ
arch_mem_init() / setup_arch()
โ
โโโ memblock_add(base, size) โ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๋ฑ๋ก
โ โโโ memblock_add_range()
โ โโโ memblock_insert_region()
โ โโโ memblock_double_array() โ ๋ฐฐ์ด ํ์ฅ
โ โโโ memblock_merge_regions() โ ์ธ์ ์์ญ ๋ณํฉ
โ
โโโ memblock_reserve(base, size) โ ์์ฝ (์ปค๋ ์ด๋ฏธ์ง, initrd ๋ฑ)
โ โโโ __memblock_reserve()
โ โโโ memblock_add_range(&memblock.reserved, ...)
โ
โโโ memblock_alloc(size, align) โ ๋ถํ
์ค ํ ๋น
โ โโโ memblock_alloc_try_nid()
โ โโโ memblock_alloc_internal()
โ โโโ memblock_alloc_range_nid()
โ โโโ memblock_find_in_range_node()
โ โ โโโ __memblock_find_range_top_down()
โ โ โโโ __memblock_find_range_bottom_up()
โ โโโ __memblock_reserve()
โ
โโโ memblock_free_all() โ ๋ถํ
์๋ฃ ์ Buddy์ ๋ฐํ
โโโ free_unused_memmap()
โโโ reset_all_zones_managed_pages()
โโโ free_low_memory_core_early()
โโโ __free_pages_memory()
ํ์จ์ด ์ ๋ ฅ๊ณผ ์์ฝ ์์ญ
memblock_add()์ ์์ฒ์ ์ํคํ
์ฒ๋ณ ํ์จ์ด ๋ฉ๋ชจ๋ฆฌ๋งต์ด๋ค. x86์ BIOS E820 ๋๋ UEFI Memory Map์, ARM64/์๋ฒ ๋๋ ์์คํ
์ Device Tree์ /memory์ /reserved-memory๋ฅผ, NUMA ์๋ฒ๋ ACPI SRAT/HMAT ๊ฐ์ ๋
ธ๋ ์ ๋ณด๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ค. ์ด ๋จ๊ณ์์ memory์ ๋ค์ด๊ฐ๋ ๊ฒ์ ์ปค๋์ด ์ผ๋ฐ RAM์ผ๋ก ์ฌ์ฉํ ์ ์๋ ๋ฒ์์ด๊ณ , ์ฅ์น MMIO, ํ์จ์ด ๋ฐํ์ ์๋น์ค, ACPI ํ
์ด๋ธ, initrd, crashkernel, CMA ์์ฝ ์์ญ์ ์ผ๋ฐ ํ ๋น ๋์์ผ๋ก ๋จ๊ฒจ ๋๋ฉด ์ ๋๋ค.
reserved๋ ๋จ์ํ "์ฌ์ฉ ๋ถ๊ฐ" ๋ชฉ๋ก์ด ์๋๋ผ ์ด๊ธฐ ๋ถํ
์ค ์ด๋ฏธ ๋ชฉ์ ์ด ์ ํด์ง ๋ฌผ๋ฆฌ ์ฃผ์ ๋ชฉ๋ก์ด๋ค. ์ปค๋ ์ด๋ฏธ์ง์ ์ด๊ธฐ ํ์ด์ง ํ
์ด๋ธ์ ๋ถํ
์์ฒด์ ํ์ํ๊ณ , initrd๋ ๋ฃจํธ ํ์ผ์์คํ
์ ๊ฐ ์ ๊น์ง ๋ณด์กด๋์ด์ผ ํ๋ฉฐ, crashkernel์ ํจ๋ ์ดํ ๋ ๋ฒ์งธ ์ปค๋์ด ์ฌ์ฉํ ์ ์์ด์ผ ํ๋ค. Device Tree์ /reserved-memory๋ ํ์จ์ด, ๋ณด์ ์์ญ, ๋์คํ๋ ์ด ๋ฒํผ, ์๊ฒฉ ํ๋ก์ธ์ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ, CMA์ฒ๋ผ ์ด์ ์ค์๋ ์ผ๋ฐ Buddy ํ ๋น๊ณผ ์์ด๋ฉด ์ ๋๋ ์์ญ์ ํํํ๋ค.
/proc/iomem์ ๋ถํ
์ดํ ๋ฌผ๋ฆฌ ์ฃผ์ ๊ณต๊ฐ์ ๋ณด๋ ๊ฐ์ฅ ์ง์ ์ ์ธ ๊ด์ฐฐ์ ์ด๋ค. System RAM์ผ๋ก ํ์๋ ๋ฒ์๊ฐ ๋์ฒด๋ก memblock memory์ ์ถ๋ฐ์ ์ด๊ณ , ๊ทธ ํ์์ Kernel code, Kernel data, reserved, Crash kernel ๊ฐ์ ํญ๋ชฉ์ memblock reserved ๋๋ ์ดํ resource tree์์ ์์ฝ๋ ์์ญ๊ณผ ์ฐ๊ฒฐ๋๋ค. ์ปค๋ ๊ฐ์ ์ฃผ์ ๋ฐฐ์น๊ฐ ๊ถ๊ธํ ๋๋ /proc/meminfo์ DirectMap, Vmalloc, PageTables์ kernel_page_tables debugfs๋ฅผ ํจ๊ป ๋ด์ผ ํ๋ค.
์กฐ๊ฑด๋ณ ๋น๊ต
| ์กฐ๊ฑด | ํ ๋น ๋ฐฉํฅ | ๋ฉ๋ชจ๋ฆฌ ํ์ | ์ฌ์ฉ API | ๋น๊ณ |
|---|---|---|---|---|
| UMA ์์คํ | top-down (๊ธฐ๋ณธ) | memory + reserved | `memblock_add()` + `memblock_reserve()` | NUMA ๋ ธ๋ ๋ฏธ์ฌ์ฉ |
| NUMA ์์คํ | top-down (๊ธฐ๋ณธ) | memory + reserved | `memblock_add_node()` + `memblock_set_node()` | ๋ ธ๋๋ณ ์์ญ ๊ด๋ฆฌ |
| bottom-up ๋ชจ๋ | bottom-up | memory + reserved | `memblock_set_bottom_up(true)` ์ดํ ํ ๋น | ํน์ ์ด๊ธฐํ ์ ์ฌ์ฉ |
| ๋ฏธ๋ฌ ๋ฉ๋ชจ๋ฆฌ | top-down | memory (MIRROR ํ๋๊ทธ) | `memblock_mark_mirror()` + ์ผ๋ฐ ํ ๋น | ๋ฏธ๋ฌ ํ ๋น ์คํจ ์ ์ผ๋ฐ ์์ญ์ผ๋ก fallback |
| KHO scratch | bottom-up | memory (KHO_SCRATCH ํ๋๊ทธ) | `memblock_set_kho_scratch_only()` | kexec handover ์ ์์ ๋ฉ๋ชจ๋ฆฌ |
| nomap ์์ญ | top-down | memory (NOMAP ํ๋๊ทธ) | `memblock_mark_nomap()` | ์ง์ ๋งคํ์์ ์ ์ธ, struct page๋ PageReserved() |
| memblock=debug | - | - | - | ๋ถํ ์ ์์ธ ๋ก๊ทธ ์ถ๋ ฅ |
| slab ์ฌ์ฉ ๊ฐ๋ฅ ์ | - | - | `kzalloc_node()` fallback | memblock ์ด๊ธฐํ ํ slab์ด ์ค๋น๋๋ฉด ์๋ ์ ํ |
๋ฉ๋ชจ๋ฆฌ๋งต ์ ๋ ฅ๋ณ ์ฒ๋ฆฌ
| ์ ๋ ฅ | memblock ์ฒ๋ฆฌ | ์ฃผ์ํ ์ | ํ์ธ ๋ฐฉ๋ฒ | ||
|---|---|---|---|---|---|
| BIOS E820 | RAM ๋ฒ์๋ `memblock_add()`, reserved/ACPI/NVS/MMIO๋ ์์ฝ ๋๋ ์ ์ธ | E820์ ์ ์ฒด ๋ฌผ๋ฆฌ ์ฃผ์ ๊ณต๊ฐ๊ณผ ์ปค๋ ์ง์ ๋งคํ ๋ฒ์๋ ๊ฐ์ ํ๊ฐ ์๋ | `dmesg \ | grep BIOS-e820`, `cat /proc/iomem` | |
| UEFI Memory Map | Conventional Memory๋ ์ฌ์ฉ ํ๋ณด, Runtime/ACPI/Reserved ํ์ ์ ๋ณด์กด | EFI runtime ์๋น์ค ์์ญ์ ๋์ค์๋ ์ ๊ทผ ๊ฒฝ๋ก๊ฐ ํ์ํ ์ ์์ | `dmesg \ | grep 'efi: mem'` | |
| Device Tree `/memory` | RAM bank๋ฅผ `memory` region์ผ๋ก ๋ฑ๋ก | bank ์ฌ์ด hole๊ณผ SoC MMIO๋ฅผ RAM์ผ๋ก ์ทจ๊ธํ๋ฉด ์ ๋จ | `dmesg \ | grep -Ei 'OF: fdt\ | Memory:'` |
| Device Tree `/reserved-memory` | CMA, framebuffer, secure memory ๋ฑ์ `reserved` ๋๋ NOMAP์ผ๋ก ๋ถ๋ฆฌ | ๋๋ผ์ด๋ฒ ์ ์ฉ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ๋ Buddy์ ์์ด๋ฉด ๋ฐ์ดํฐ ์์ ๊ฐ๋ฅ | `dmesg \ | grep -Ei 'reserved-memory\ | cma'` |
| ACPI SRAT/HMAT | `memblock_add_node()` ๋๋ `memblock_set_node()`๋ก NUMA ๋ ธ๋ ์ฐ๊ฒฐ | ์ฃผ์๊ฐ ์ฐ์์ด์ด๋ latency/locality๊ฐ ๋ค๋ฅผ ์ ์์ | `numactl -H`, `/sys/devices/system/node/` | ||
| `mem=`, `crashkernel=`, `movable_node` | ์ฌ์ฉ ๊ฐ๋ฅ ์ํ, crash kernel ์์ฝ, hotplug ์ ํธ ํ๋๊ทธ์ ์ํฅ | `physmem`๊ณผ `memory`๊ฐ ์๋ก ๋ค๋ฅธ ์๋ฏธ๋ฅผ ๊ฐ์ง ์ ์์ | `cat /proc/cmdline`, `cat /proc/iomem` |
์ด๊ธฐ ํ ๋น์ ๋น๊ต
| ๊ตฌ๋ถ | memblock | Buddy Allocator | SLUB |
|---|---|---|---|
| ์ฌ์ฉ ์์ | ๋ถํ ์ด๊ธฐ, ์ผ๋ฐ ํ ๋น์ ์ค๋น ์ | Zone๊ณผ `struct page` ์ด๊ธฐํ ์ดํ | Buddy ์์์ ์ปค๋ ๊ฐ์ฒด ํ ๋น ์ |
| ๊ด๋ฆฌ ๋จ์ | ๋ฌผ๋ฆฌ ์ฃผ์ ๊ตฌ๊ฐ `[base, size)` | `struct page`์ order๋ณ free list | ์บ์๋ณ object/slab |
| ๋ํ API | `memblock_add()`, `memblock_reserve()`, `memblock_alloc()` | `alloc_pages()`, `__free_pages()` | `kmalloc()`, `kmem_cache_alloc()` |
| ๊ฐ์ | ํ์จ์ด ๋ฉ๋ชจ๋ฆฌ๋งต๊ณผ ์์ฝ ์์ญ์ ๋จ์ํ ๊ตฌ๊ฐ ๋ฐฐ์ด๋ก ์ ๋ฆฌ | ๋ฐํ์ ํ์ด์ง ํ ๋น/ํด์ ์ ๋ณํฉ ์ฒ๋ฆฌ | ์์ ๊ฐ์ฒด ๋ฐ๋ณต ํ ๋น ์ต์ ํ |
| ์ ํ์ | `memblock_free_all()`์์ ๋จ์ free range ๋ฐํ | ์ดํ ์ผ๋ฐ ํ์ด์ง ํ ๋น์ ๋ด๋น | slab ์ค๋น ํ memblock ์ฐ๋ฐ ํธ์ถ fallback ๋์ |
๋ฉ๋ชจ๋ฆฌ ํ ๋น API ๊ณ์ธต
| ํจ์ | ๋ฐํ ํ์ | ํน์ง |
|---|---|---|
| `memblock_phys_alloc()` | `phys_addr_t` | ๋ฌผ๋ฆฌ ์ฃผ์ ๋ฐํ, ์ ๋ก์ ์์ |
| `memblock_alloc()` | `void *` | ๊ฐ์ ์ฃผ์ ๋ฐํ, ์ ๋ก์ ํฌํจ |
| `memblock_alloc_raw()` | `void *` | ๊ฐ์ ์ฃผ์ ๋ฐํ, ์ ๋ก์ ์์ |
| `memblock_alloc_low()` | `void *` | ๋ฎ์ ์ฃผ์ ์์ญ์์ ํ ๋น |
| `memblock_alloc_node()` | `void *` | ํน์ NUMA ๋ ธ๋์์ ํ ๋น |
| `memblock_alloc_or_panic()` | `void *` | ํ ๋น ์คํจ ์ panic ๋ฐ์ |
memblock โ Buddy ์ ํ
๋ถํ
์ด ์๋ฃ๋๋ฉด memblock_free_all()์ด ํธ์ถ๋์ด ๋ชจ๋ ๋จ์ ํ์ด์ง๋ฅผ Buddy Allocator๋ก ๋๊ธด๋ค. ์ด ๊ณผ์ ์์:
1. free_unused_memmap() โ ์ฌ์ฉํ์ง ์๋ struct page ๋ฐฐ์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์
2. reset_all_zones_managed_pages() โ Zone์ managed_pages ์นด์ดํฐ ๋ฆฌ์
3. free_low_memory_core_early() โ for_each_free_mem_range()๋ก ์ํํ๋ฉฐ __free_pages_memory()๋ก ๊ฐ ํ์ด์ง๋ฅผ Buddy์ ๋ฐํ
4. totalram_pages_add() โ ์ ์ฒด RAM ํ์ด์ง ์ ์
๋ฐ์ดํธ
์ดํ CONFIG_ARCH_KEEP_MEMBLOCK์ด ์์ผ๋ฉด memblock_discard()๊ฐ ํธ์ถ๋์ด memblock ๋ด๋ถ ๋ฐฐ์ด์ ํด์ ํ๊ณ , memblock ํฌ์ธํฐ๋ NULL๋ก ์ค์ ๋๋ค.