22. ๋ฉ”๋ชจ๋ฆฌ Hotplug

๊ด€๋ จ ์†Œ์Šค: mm/memory_hotplug.c, include/linux/memory_hotplug.h, include/linux/memory.h
๊ด€๋ จ ๋ฌธ์„œ: 00-๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” ยท 04-Memblock ยท 13-NUMA ยท 11-Compaction ยท 20-Migration ยท 18-CMA ยท 24-ํ‹ฐ์–ด๋ง

๊ฐœ์š” (Overview)

๋ฉ”๋ชจ๋ฆฌ Hotplug๋Š” ๋Ÿฐํƒ€์ž„์— ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ปค๋„์— ์ถ”๊ฐ€(Hot-Add)ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐ(Hot-Remove)ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๊ฐ€์ƒํ™” ํ™˜๊ฒฝ(KVM, Hyper-V, Xen), CXL ์žฅ์น˜, DIMM ์Šฌ๋กฏ ๊ต์ฒด ์‹œ์Šคํ…œ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์šฉ๋Ÿ‰์„ ๋™์ ์œผ๋กœ ์กฐ์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. Linux 7.0์—์„œ๋Š” mm/memory_hotplug.c๊ฐ€ ์ด ์ „์ฒด ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, ACPI ์ด๋ฒคํŠธ, sysfs, virtio-mem ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ Hotplug๋Š” ํฌ๊ฒŒ ๋‘ ๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. (1) Hot-Add: ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ธ์ง€๊ฐ€ ์ปค๋„์— ๋“ฑ๋ก๋˜๊ณ  struct page ๋ฐฐ์—ด(vmemmap)์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—์„œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ์•„์ง ์‚ฌ์šฉ ๋ถˆ๊ฐ€ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. (2) Online: ๋“ฑ๋ก๋œ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์„ Buddy Allocator์˜ free list์— ์ถ”๊ฐ€ํ•˜์—ฌ ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์ œ๊ฑฐ ์‹œ์—๋Š” Offline(๋ชจ๋“  ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜) โ†’ Hot-Remove(์ปค๋„ ๋“ฑ๋ก ํ•ด์ œ) ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. ์˜คํ”„๋ผ์ธ ์‹คํŒจ์˜ ์ฃผ๋œ ์›์ธ์€ MIGRATE_UNMOVABLE ํƒ€์ž…์˜ ํŽ˜์ด์ง€(์ปค๋„ slab ๊ฐ์ฒด, ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋“ฑ)๊ฐ€ ํ•ด๋‹น ๋ธ”๋ก์— ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ์ด๋ฉฐ, ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ํ•ซํ”Œ๋Ÿฌ๊ทธ ๋Œ€์ƒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ZONE_MOVABLE์— ๋ฐฐ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ผ์ƒ์ ์œผ๋กœ ๋ณด๋ฉด ๋ฉ”๋ชจ๋ฆฌ Hotplug๋Š” ์šด์˜ ์ค‘์ธ ๋„์„œ๊ด€์— ์ƒˆ ์„œ๊ฐ€๋ฅผ ๊ฝ‚๊ฑฐ๋‚˜ ๋‹ค์‹œ ๋นผ๋Š” ์ž‘์—…๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ์„œ๊ฐ€๋ฅผ ๋“ค์—ฌ๋†“๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ฑ…์„ ๋ฐ”๋กœ ๋นŒ๋ฆด ์ˆ˜ ์—†๊ณ (Hot-Add), ์„œ๊ฐ€ ์•ˆ์˜ ์ฑ…์„ ๋‹ค๋ฅธ ๊ตฌ์—ญ์œผ๋กœ ๋ชจ๋‘ ์˜ฎ๊ฒจ์•ผ ์•ˆ์ „ํ•˜๊ฒŒ ๋น„์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Offline โ†’ Hot-Remove). kernelcore=์™€ movablecore=๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์„œ๊ฐ€์˜ ๋น„์œจ์„ ๋‚˜๋ˆ„๋Š” ๋ถ€ํŒ… ํŒŒ๋ผ๋ฏธํ„ฐ์ด๊ณ , online_policy์™€ /sys/devices/system/memory/auto_online_blocks๋Š” ์ƒˆ๋กœ ๋“ค์–ด์˜จ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์–ด๋А ๊ตฌ์—ญ์— ๋‘˜์ง€ ์ •ํ•˜๋Š” ์šด์˜ ์Šค์œ„์น˜์ž…๋‹ˆ๋‹ค.

/* ์ฃผ์š” ์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ */
mm/memory_hotplug.c          /* ํ•ต์‹ฌ Hotplug ๋กœ์ง (2435์ค„) */
include/linux/memory_hotplug.h  /* API ์„ ์–ธ, mhp_params, MMOP_* ์—ด๊ฑฐ */
include/linux/memory.h         /* struct memory_block, memory_group ์ •์˜ */
mm/sparse.c                   /* SPARSEMEM ์„น์…˜ ๊ด€๋ฆฌ (sparse_add_section) */
drivers/base/memory.c         /* sysfs ์ธํ„ฐํŽ˜์ด์Šค, memory_block ๋””๋ฐ”์ด์Šค */

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

# ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ์ƒํƒœ ์ „์ฒด ํ™•์ธ
cat /sys/devices/system/memory/memory*/state | sort | uniq -c | sort -rn

# ํŠน์ • ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ์˜จ๋ผ์ธ/์˜คํ”„๋ผ์ธ
echo online > /sys/devices/system/memory/memory32/state
echo offline > /sys/devices/system/memory/memory32/state

# ZONE_MOVABLE ์˜จ๋ผ์ธ (๋‚˜์ค‘์— ์ œ๊ฑฐ ๊ฐ€๋Šฅ)
echo online_movable > /sys/devices/system/memory/memory32/state

# ์ž๋™ ์˜จ๋ผ์ธ ์ •์ฑ… ํ™•์ธ
cat /sys/devices/system/memory/auto_online_blocks
# online_movable / online_kernel / offline

# ํ˜„์žฌ ๋ฉ”๋ชจ๋ฆฌ Hotplug ๊ด€๋ จ ์ปค๋„ ํŒŒ๋ผ๋ฏธํ„ฐ
cat /proc/cmdline | tr ' ' '\n' | grep -E 'memhp|movable|hotplug'

# kernelcore/movablecore/movable_node ์„ค์ • ํ™•์ธ
cat /proc/cmdline | tr ' ' '\n' | grep -E 'kernelcore|movablecore|memhp_default_state|movable_node'

# NUMA ๋…ธ๋“œ๋ณ„ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ์ •๋ณด
cat /sys/devices/system/node/node*/meminfo | head -20

# ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ์ „์ฒด ๋งต (/proc/iomem์—์„œ System RAM ํ™•์ธ)
sudo cat /proc/iomem | grep -i "System RAM"

# ๋ฉ”๋ชจ๋ฆฌ Hotplug ๊ด€๋ จ ์ปค๋„ ๋กœ๊ทธ
dmesg | grep -Ei 'hotplug|memory block|online|offline'

# ์ปค๋„ ์„ค์ •์—์„œ Hotplug ๊ด€๋ จ ์˜ต์…˜ ํ™•์ธ
zgrep -E 'CONFIG_MEMORY_HOTPLUG|CONFIG_MEMORY_HOTREMOVE|CONFIG_MHP_MEMMAP_ON_MEMORY' /proc/config.gz 2>/dev/null

# ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ํฌ๊ธฐ ํ™•์ธ (๋ณดํ†ต 128MB)
cat /sys/devices/system/memory/block_size_bytes | xargs printf "%d\n"

# ์ž๋™ ์˜จ๋ผ์ธ ๋น„์œจ ์„ค์ •
cat /sys/module/memory_hotplug/parameters/auto_movable_ratio
cat /sys/module/memory_hotplug/parameters/online_policy

# memmap_on_memory ๋ชจ๋“œ ํ™•์ธ
cat /sys/module/memory_hotplug/parameters/memmap_on_memory

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

struct memory_block โ€” ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ๋””๋ฐ”์ด์Šค

๋ฉ”๋ชจ๋ฆฌ Hotplug์˜ ๊ธฐ๋ณธ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. sysfs์˜ /sys/devices/system/memory/memoryN์œผ๋กœ ๋…ธ์ถœ๋˜๋ฉฐ, ํ•˜๋‚˜์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์€ ํ•˜๋‚˜ ์ด์ƒ์˜ SPARSEMEM ์„น์…˜์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

/* include/linux/memory.h:77-96 */
struct memory_block {
unsigned long start_section_nr;              /* ์‹œ์ž‘ ์„น์…˜ ๋ฒˆํ˜ธ */
enum memory_block_state state;               /* MEM_ONLINE, MEM_OFFLINE ๋“ฑ */
int online_type;                             /* MMOP_ONLINE_KERNEL ๋“ฑ */
int nid;                                     /* ์ด ๋ธ”๋ก์˜ NUMA ๋…ธ๋“œ ID */
struct zone *zone;                           /* ๋‹จ์ผ zone (์—†์œผ๋ฉด NULL) */
struct device dev;                           /* sysfs ๋””๋ฐ”์ด์Šค ๊ตฌ์กฐ์ฒด */
struct vmem_altmap *altmap;                  /* memmap_on_memory์šฉ ๋Œ€์ฒด ๋งต */
struct memory_group *group;                  /* ์†Œ์† ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ฃน */
struct list_head group_next;                 /* ๊ทธ๋ฃน ๋‚ด ๋‹ค์Œ ๋ธ”๋ก */
#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG)
atomic_long_t nr_hwpoison;                   /* ํ•˜๋“œ์›จ์–ด ์˜ค๋ฅ˜ ํŽ˜์ด์ง€ ์ˆ˜ */
#endif
};

struct memory_group โ€” ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ฃน

ํ•˜๋‚˜์˜ ๋ฉ”๋ชจ๋ฆฌ ๋””๋ฐ”์ด์Šค(DIMM, CXL ๋ฆฌ์ „ ๋“ฑ)๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์„ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ฌถ์Šต๋‹ˆ๋‹ค. ์ •์ (static)๊ณผ ๋™์ (dynamic) ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

/* include/linux/memory.h:51-65 */
struct memory_group {
int nid;                                     /* ๊ทธ๋ฃน ์ „์ฒด ๋…ธ๋“œ ID */
struct list_head memory_blocks;              /* ์†Œ์† ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ๋ชฉ๋ก */
unsigned long present_kernel_pages;          /* ZONE_MOVABLE ์™ธ ์˜จ๋ผ์ธ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned long present_movable_pages;         /* ZONE_MOVABLE ๋‚ด ์˜จ๋ผ์ธ ํŽ˜์ด์ง€ ์ˆ˜ */
bool is_dynamic;                             /* ์ •์  vs ๋™์  ๊ทธ๋ฃน */
union {
struct {
unsigned long max_pages;             /* ์ •์ : ์ตœ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜ */
} s;
struct {
unsigned long unit_pages;            /* ๋™์ : ๋‹จ์œ„ ํŽ˜์ด์ง€ ์ˆ˜ (์ถ”๊ฐ€/์ œ๊ฑฐ ๋‹จ์œ„) */
} d;
};
};

struct mhp_params โ€” Hot-Add ํ™•์žฅ ํŒŒ๋ผ๋ฏธํ„ฐ

add_memory_resource()์— ์ „๋‹ฌ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋‹ด์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ๋ณดํ˜ธ ํ”Œ๋ž˜๊ทธ, ๋Œ€์ฒด memmap ํ• ๋‹น์ž, ZONE_DEVICE ๋งต์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/memory_hotplug.h:68-72 */
struct mhp_params {
struct vmem_altmap *altmap;     /* memmap ๋ฐฐ์—ด์˜ ๋Œ€์ฒด ํ• ๋‹น์ž (์„ ํƒ) */
pgprot_t pgprot;                /* ์ƒˆ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์˜ ๋ณดํ˜ธ ํ”Œ๋ž˜๊ทธ (ํ•„์ˆ˜) */
struct dev_pagemap *pgmap;      /* ZONE_DEVICE์šฉ ํŽ˜์ด์ง€ ๋งต (์„ ํƒ) */
};

enum memory_block_state โ€” ๋ธ”๋ก ์ƒํƒœ ๋จธ์‹ 

sysfs๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์— ํ…์ŠคํŠธ๋กœ ๋…ธ์ถœ๋˜๋Š” ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

/* include/linux/memory.h:67-75 */
enum memory_block_state {
MEM_ONLINE,          /* ์˜จ๋ผ์ธ โ€” Buddy free list์— ์ถ”๊ฐ€๋จ */
MEM_GOING_OFFLINE,   /* ์˜คํ”„๋ผ์ธ ์ง„ํ–‰ ์ค‘ */
MEM_OFFLINE,         /* ์˜คํ”„๋ผ์ธ โ€” ์‚ฌ์šฉ ๋ถˆ๊ฐ€ */
MEM_GOING_ONLINE,    /* ์˜จ๋ผ์ธ ์ง„ํ–‰ ์ค‘ */
MEM_CANCEL_ONLINE,   /* ์˜จ๋ผ์ธ ์ทจ์†Œ๋จ */
MEM_CANCEL_OFFLINE,  /* ์˜คํ”„๋ผ์ธ ์ทจ์†Œ๋จ */
};

๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ์ƒํƒœ ์ „์ด

์ „์ดํŠธ๋ฆฌ๊ฑฐํ•ต์‹ฌ ๋™์ž‘
Hot-Add โ†’ Registered`add_memory_resource()`memblock ์ถ”๊ฐ€, memory_block ๋””๋ฐ”์ด์Šค ์ƒ์„ฑ, ์•„์ง ํ• ๋‹น ๋ถˆ๊ฐ€
Registered โ†’ Online`online_pages()`zone ์—ฐ๊ฒฐ, Buddy free list ์ถ”๊ฐ€, ์›Œํ„ฐ๋งˆํฌ/kswapd/kcompactd ๊ฐฑ์‹ 
Online โ†’ Offline`offline_pages()`ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜, ๊ฒฉ๋ฆฌ, Buddy ์ œ๊ฑฐ
Offline โ†’ Hot-Remove`try_remove_memory()`memory_block ๋””๋ฐ”์ด์Šค ์ œ๊ฑฐ, arch ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ, memblock ์ •๋ฆฌ

MMOP_* ์˜จ๋ผ์ธ ํƒ€์ž… ์—ด๊ฑฐ

๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์„ ์–ด๋–ค Zone์— ์˜จ๋ผ์ธ์‹œํ‚ฌ์ง€๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/memory_hotplug.h:23-32 */
enum {
MMOP_OFFLINE = 0,           /* ์˜คํ”„๋ผ์ธ */
MMOP_ONLINE,                /* ๊ธฐ๋ณธ ์˜จ๋ผ์ธ โ€” zone ์ž๋™ ๊ฒฐ์ • */
MMOP_ONLINE_KERNEL,         /* ZONE_NORMAL(์ปค๋„)์— ์˜จ๋ผ์ธ */
MMOP_ONLINE_MOVABLE,        /* ZONE_MOVABLE์— ์˜จ๋ผ์ธ (์ œ๊ฑฐ ๊ฐ€๋Šฅ) */
};

auto_movable_stats โ€” ์ž๋™ MOVABLE ํ†ต๊ณ„

auto-movable ์˜จ๋ผ์ธ ์ •์ฑ…์—์„œ MOVABLE:KERNEL ๋น„์œจ์„ ํŒ๋‹จํ•˜๊ธฐ ์œ„ํ•œ ํ†ต๊ณ„ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

/* mm/memory_hotplug.c:789-792 */
struct auto_movable_stats {
unsigned long kernel_early_pages;  /* ๋ถ€ํŒ… ์ดˆ๊ธฐ ์ปค๋„ ์˜์—ญ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned long movable_pages;       /* ZONE_MOVABLE ํŽ˜์ด์ง€ ์ˆ˜ */
};

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

1. online_pages() โ€” ๋ฉ”๋ชจ๋ฆฌ ์˜จ๋ผ์ธ

move_pfn_range_to_zone()๋กœ pfn ๋ฒ”์œ„๋ฅผ zone์— ์—ฐ๊ฒฐํ•œ ๋’ค, online_pages_range()๋ฅผ ํ†ตํ•ด Buddy์— ํŽ˜์ด์ง€๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์˜จ๋ผ์ธ ์™„๋ฃŒ ํ›„ zonelist ์žฌ๊ตฌ์„ฑ, watermarks ์ดˆ๊ธฐํ™”, kswapd/kcompactd ์‹œ์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

/* mm/memory_hotplug.c:1144-1257 */
int online_pages(unsigned long pfn, unsigned long nr_pages,
		       struct zone *zone, struct memory_group *group)
{
struct memory_notify mem_arg = {
		.start_pfn = pfn,
		.nr_pages = nr_pages,
	};
	struct node_notify node_arg = {
		.nid = NUMA_NO_NODE,
	};
	const int nid = zone_to_nid(zone);
	int need_zonelists_rebuild = 0;
	unsigned long flags;
	int ret;

	if (WARN_ON_ONCE(!nr_pages || !pageblock_aligned(pfn) ||
			 !IS_ALIGNED(pfn + nr_pages, PAGES_PER_SECTION)))
		return -EINVAL;

	move_pfn_range_to_zone(zone, pfn, nr_pages, NULL, MIGRATE_MOVABLE,
			       true);

	if (!node_state(nid, N_MEMORY)) {
		node_arg.nid = nid;
		ret = node_notify(NODE_ADDING_FIRST_MEMORY, &node_arg);
		ret = notifier_to_errno(ret);
		if (ret)
			goto failed_addition;
	}

	ret = memory_notify(MEM_GOING_ONLINE, &mem_arg);
	ret = notifier_to_errno(ret);
	if (ret)
		goto failed_addition;

	spin_lock_irqsave(&zone->lock, flags);
	zone->nr_isolate_pageblock += nr_pages / pageblock_nr_pages;
	spin_unlock_irqrestore(&zone->lock, flags);

	if (!populated_zone(zone)) {
		need_zonelists_rebuild = 1;
		setup_zone_pageset(zone);
	}

	online_pages_range(pfn, nr_pages);
	adjust_present_page_count(pfn_to_page(pfn), group, nr_pages);

	if (node_arg.nid >= 0)
		node_set_state(nid, N_MEMORY);
	if (!node_state(nid, N_NORMAL_MEMORY) && zone_idx(zone) <= ZONE_NORMAL)
		node_set_state(nid, N_NORMAL_MEMORY);

	if (need_zonelists_rebuild)
		build_all_zonelists(NULL);

	undo_isolate_page_range(pfn, pfn + nr_pages);
	shuffle_zone(zone);
	init_per_zone_wmark_min();
	kswapd_run(nid);
	kcompactd_run(nid);

	if (node_arg.nid >= 0)
		node_notify(NODE_ADDED_FIRST_MEMORY, &node_arg);

	writeback_set_ratelimit();

	memory_notify(MEM_ONLINE, &mem_arg);
	return 0;

failed_addition:
	remove_pfn_range_from_zone(zone, pfn, nr_pages);
	return ret;
}

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

  • !node_state(nid, N_MEMORY) โ†’ ์ตœ์ดˆ ๋ฉ”๋ชจ๋ฆฌ ์ถ”๊ฐ€ ๋…ธ๋“œ ์•Œ๋ฆผ
  • !populated_zone(zone) โ†’ zonelist ์žฌ๊ตฌ์„ฑ + pageset ์„ค์ •
  • ์‹คํŒจ ์‹œ remove_pfn_range_from_zone()์œผ๋กœ ๋กค๋ฐฑ
  • 2. offline_pages() โ€” ๋ฉ”๋ชจ๋ฆฌ ์˜คํ”„๋ผ์ธ

    ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๋‹ค๋ฅธ ๋ธ”๋ก์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ ๋’ค, Buddy์—์„œ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. PCP ๋น„ํ™œ์„ฑํ™”, LRU ์บ์‹œ ๋น„ํ™œ์„ฑํ™”, ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

    /* mm/memory_hotplug.c:1901-2117 */
    int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
    		 struct zone *zone, struct memory_group *group)
    {
    unsigned long pfn, managed_pages, system_ram_pages = 0;
    	const unsigned long end_pfn = start_pfn + nr_pages;
    	struct pglist_data *pgdat = zone->zone_pgdat;
    	const int node = zone_to_nid(zone);
    	struct memory_notify mem_arg = {
    		.start_pfn = start_pfn,
    		.nr_pages = nr_pages,
    	};
    	struct node_notify node_arg = {
    		.nid = NUMA_NO_NODE,
    	};
    	unsigned long flags;
    	char *reason;
    	int ret;
    	unsigned long normal_pages = 0;
    	enum zone_type zt;
    
    	if (WARN_ON_ONCE(!nr_pages || !pageblock_aligned(start_pfn) ||
    			 !IS_ALIGNED(start_pfn + nr_pages, PAGES_PER_SECTION)))
    		return -EINVAL;
    
    	walk_system_ram_range(start_pfn, nr_pages, &system_ram_pages,
    			     count_system_ram_pages_cb);
    	if (system_ram_pages != nr_pages) {
    		ret = -EINVAL;
    		reason = "memory holes";
    		goto failed_removal;
    	}
    
    	if (WARN_ON_ONCE(page_zone(pfn_to_page(start_pfn)) != zone ||
    			 page_zone(pfn_to_page(end_pfn - 1)) != zone)) {
    		ret = -EINVAL;
    		reason = "multizone range";
    		goto failed_removal;
    	}
    
    	zone_pcp_disable(zone);
    	lru_cache_disable();
    
    	ret = start_isolate_page_range(start_pfn, end_pfn,
    				       PB_ISOLATE_MODE_MEM_OFFLINE);
    	if (ret) {
    		reason = "failure to isolate range";
    		goto failed_removal_pcplists_disabled;
    	}
    
    	if (nr_pages >= pgdat->node_present_pages) {
    		node_arg.nid = node;
    		ret = node_notify(NODE_REMOVING_LAST_MEMORY, &node_arg);
    		ret = notifier_to_errno(ret);
    		if (ret) {
    			reason = "node notifier failure";
    			goto failed_removal_isolated;
    		}
    	}
    
    	ret = memory_notify(MEM_GOING_OFFLINE, &mem_arg);
    	ret = notifier_to_errno(ret);
    	if (ret) {
    		reason = "notifier failure";
    		goto failed_removal_isolated;
    	}
    
    	do {
    		pfn = start_pfn;
    		do {
    			if (signal_pending(current)) {
    				ret = -EINTR;
    				reason = "signal backoff";
    				goto failed_removal_isolated;
    			}
    
    			cond_resched();
    
    			ret = scan_movable_pages(pfn, end_pfn, &pfn);
    			if (!ret)
    				do_migrate_range(pfn, end_pfn);
    		} while (!ret);
    
    		if (ret != -ENOENT) {
    			reason = "unmovable page";
    			goto failed_removal_isolated;
    		}
    
    		ret = dissolve_free_hugetlb_folios(start_pfn, end_pfn);
    		if (ret) {
    			reason = "failure to dissolve huge pages";
    			goto failed_removal_isolated;
    		}
    
    		ret = test_pages_isolated(start_pfn, end_pfn,
    					  PB_ISOLATE_MODE_MEM_OFFLINE);
    	} while (ret);
    
    	managed_pages = __offline_isolated_pages(start_pfn, end_pfn);
    	spin_lock_irqsave(&zone->lock, flags);
    	zone->nr_isolate_pageblock -= nr_pages / pageblock_nr_pages;
    	spin_unlock_irqrestore(&zone->lock, flags);
    
    	lru_cache_enable();
    	zone_pcp_enable(zone);
    
    	adjust_managed_page_count(pfn_to_page(start_pfn), -managed_pages);
    	adjust_present_page_count(pfn_to_page(start_pfn), group, -nr_pages);
    	init_per_zone_wmark_min();
    
    	if (zone_idx(zone) <= ZONE_NORMAL) {
    		for (zt = 0; zt <= ZONE_NORMAL; zt++)
    			normal_pages += pgdat->node_zones[zt].present_pages;
    		if (!normal_pages)
    			node_clear_state(node, N_NORMAL_MEMORY);
    	}
    
    	if (node_arg.nid >= 0)
    		node_clear_state(node, N_MEMORY);
    	if (!populated_zone(zone)) {
    		zone_pcp_reset(zone);
    		build_all_zonelists(NULL);
    	}
    
    	if (node_arg.nid >= 0) {
    		kcompactd_stop(node);
    		kswapd_stop(node);
    		node_notify(NODE_REMOVED_LAST_MEMORY, &node_arg);
    	}
    
    	writeback_set_ratelimit();
    
    	memory_notify(MEM_OFFLINE, &mem_arg);
    	remove_pfn_range_from_zone(zone, start_pfn, nr_pages);
    	return 0;
    
    failed_removal_isolated:
    	undo_isolate_page_range(start_pfn, end_pfn);
    	memory_notify(MEM_CANCEL_OFFLINE, &mem_arg);
    	if (node_arg.nid != NUMA_NO_NODE)
    		node_notify(NODE_CANCEL_REMOVING_LAST_MEMORY, &node_arg);
    failed_removal_pcplists_disabled:
    	lru_cache_enable();
    	zone_pcp_enable(zone);
    failed_removal:
    	return ret;
    }

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

  • system_ram_pages != nr_pages โ†’ ๋ฉ”๋ชจ๋ฆฌ ํ™€ ์กด์žฌ โ†’ ์˜คํ”„๋ผ์ธ ๋ถˆ๊ฐ€
  • signal_pending(current) โ†’ ์‹œ๊ทธ๋„๋กœ ์ธํ•œ ์ค‘๋‹จ โ†’ -EINTR
  • scan_movable_pages() ๋ฐ˜ํ™˜๊ฐ’ -EBUSY โ†’ ์ด๋™ ๋ถˆ๊ฐ€ ํŽ˜์ด์ง€ ์กด์žฌ โ†’ ์‹คํŒจ
  • 3. add_memory_resource() โ€” ๋ฉ”๋ชจ๋ฆฌ ์ถ”๊ฐ€ (๋ฆฌ์†Œ์Šค ๊ธฐ๋ฐ˜)

    ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ธ์ง€๊ฐ€ ์ปค๋„์— ๋“ฑ๋ก๋˜๋Š” ์ „์ฒด ๊ณผ์ •์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. memblock ์ถ”๊ฐ€, ๋…ธ๋“œ ์ดˆ๊ธฐํ™”, arch ๋ฉ”๋ชจ๋ฆฌ ์ถ”๊ฐ€, ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ๋””๋ฐ”์ด์Šค ์ƒ์„ฑ, ์˜จ๋ผ์ธ๊นŒ์ง€์˜ ์ „์ฒด ํ๋ฆ„์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.

    /* mm/memory_hotplug.c:1498-1603 */
    int add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
    {
    	struct mhp_params params = { .pgprot = pgprot_mhp(PAGE_KERNEL) };
    	enum memblock_flags memblock_flags = MEMBLOCK_NONE;
    	struct memory_group *group = NULL;
    	u64 start, size;
    	bool new_node = false;
    	int ret;
    
    	start = res->start;
    	size = resource_size(res);
    
    	ret = check_hotplug_memory_range(start, size);
    	if (ret)
    		return ret;
    
    	if (mhp_flags & MHP_NID_IS_MGID) {
    		group = memory_group_find_by_id(nid);
    		if (!group)
    			return -EINVAL;
    		nid = group->nid;
    	}
    
    	if (!node_possible(nid)) {
    		WARN(1, "node %d was absent from the node_possible_map\n", nid);
    		return -EINVAL;
    	}
    
    	mem_hotplug_begin();
    
    	if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) {
    		if (res->flags & IORESOURCE_SYSRAM_DRIVER_MANAGED)
    			memblock_flags = MEMBLOCK_DRIVER_MANAGED;
    		ret = memblock_add_node(start, size, nid, memblock_flags);
    		if (ret)
    			goto error_mem_hotplug_end;
    	}
    
    	ret = __try_online_node(nid, false);
    	if (ret < 0)
    		goto error_memblock_remove;
    	if (ret) {
    		node_set_online(nid);
    		ret = register_node(nid);
    		if (WARN_ON(ret)) {
    			node_set_offline(nid);
    			goto error_memblock_remove;
    		}
    		new_node = true;
    	}
    
    	if ((mhp_flags & MHP_MEMMAP_ON_MEMORY) &&
    	    mhp_supports_memmap_on_memory()) {
    		ret = create_altmaps_and_memory_blocks(nid, group, start, size);
    		if (ret)
    			goto error;
    	} else {
    		ret = arch_add_memory(nid, start, size, &params);
    		if (ret < 0)
    			goto error;
    
    		ret = create_memory_block_devices(start, size, nid, NULL, group);
    		if (ret) {
    			arch_remove_memory(start, size, params.altmap);
    			goto error;
    		}
    	}
    
    	register_memory_blocks_under_node_hotplug(nid, PFN_DOWN(start),
    					  PFN_UP(start + size - 1));
    
    	if (!strcmp(res->name, "System RAM"))
    		firmware_map_add_hotplug(start, start + size, "System RAM");
    
    	mem_hotplug_done();
    
    	if (mhp_flags & MHP_MERGE_RESOURCE)
    		merge_system_ram_resource(res);
    
    	if (mhp_get_default_online_type() != MMOP_OFFLINE)
    		walk_memory_blocks(start, size, NULL, online_memory_block);
    
    	return ret;
    error:
    	if (new_node) {
    		node_set_offline(nid);
    		unregister_node(nid);
    	}
    error_memblock_remove:
    	if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))
    		memblock_remove(start, size);
    error_mem_hotplug_end:
    	mem_hotplug_done();
    	return ret;
    }

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

  • MHP_NID_IS_MGID โ†’ nid ๋Œ€์‹  memory_group ID ์‚ฌ์šฉ
  • MHP_MEMMAP_ON_MEMORY + ์ง€์› ๊ฐ€๋Šฅ โ†’ altmap ๊ธฐ๋ฐ˜ memmap ํ• ๋‹น
  • MHP_MERGE_RESOURCE โ†’ ์ธ์ ‘ ๋ฆฌ์†Œ์Šค์™€ ๋ณ‘ํ•ฉ
  • 4. try_remove_memory() โ€” ๋ฉ”๋ชจ๋ฆฌ ์ œ๊ฑฐ

    ๋ชจ๋“  ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์ด ์˜คํ”„๋ผ์ธ ์ƒํƒœ์ธ์ง€ ํ™•์ธํ•œ ๋’ค, ๋ฆฌ์†Œ์Šค ํ•ด์ œ, ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ๋””๋ฐ”์ด์Šค ์ œ๊ฑฐ, arch ๋ฉ”๋ชจ๋ฆฌ ์ œ๊ฑฐ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    /* mm/memory_hotplug.c:2237-2288 */
    static int try_remove_memory(u64 start, u64 size)
    {
    int rc, nid = NUMA_NO_NODE;
    
    BUG_ON(check_hotplug_memory_range(start, size));
    
    rc = walk_memory_blocks(start, size, &nid, check_memblock_offlined_cb);
    if (rc)
    return rc;
    
    firmware_map_remove(start, start + size, "System RAM");
    
    mem_hotplug_begin();
    
    rc = memory_blocks_have_altmaps(start, size);
    if (rc < 0) {
    mem_hotplug_done();
    return rc;
    } else if (!rc) {
    remove_memory_block_devices(start, size);
    arch_remove_memory(start, size, NULL);
    } else {
    remove_memory_blocks_and_altmaps(start, size);
    }
    
    if (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))
    memblock_remove(start, size);
    
    release_mem_region_adjustable(start, size);
    
    if (nid != NUMA_NO_NODE)
    try_offline_node(nid);
    
    mem_hotplug_done();
    return 0;
    }

    5. zone_for_pfn_range() โ€” ์˜จ๋ผ์ธ Zone ๊ฒฐ์ •

    ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์„ ์–ด๋–ค Zone์— ์˜จ๋ผ์ธ์‹œํ‚ฌ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์ •์ฑ… ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. online_policy์™€ online_type์— ๋”ฐ๋ผ ๋ถ„๊ธฐ๋ฉ๋‹ˆ๋‹ค.

    /* mm/memory_hotplug.c:1049-1062 */
    struct zone *zone_for_pfn_range(int online_type, int nid,
    		struct memory_group *group, unsigned long start_pfn,
    		unsigned long nr_pages)
    {
    	if (online_type == MMOP_ONLINE_KERNEL)
    		return default_kernel_zone_for_pfn(nid, start_pfn, nr_pages);
    
    	if (online_type == MMOP_ONLINE_MOVABLE)
    		return &NODE_DATA(nid)->node_zones[ZONE_MOVABLE];
    
    	if (online_policy == ONLINE_POLICY_AUTO_MOVABLE)
    		return auto_movable_zone_for_pfn(nid, group, start_pfn, nr_pages);
    
    	return default_zone_for_pfn(nid, start_pfn, nr_pages);
    }

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

    Hot-Add ํ๋ฆ„ (๋ฉ”๋ชจ๋ฆฌ ์ถ”๊ฐ€)

    ๋“œ๋ผ์ด๋ฒ„/ACPI/sysfs ํŠธ๋ฆฌ๊ฑฐ
    โ””โ†’ add_memory()                         /* lock_device_hotplug() ํฌํ•จ */
    โ””โ†’ __add_memory()
    โ””โ†’ register_memory_resource()     /* /proc/iomem์— ๋“ฑ๋ก */
    โ””โ†’ add_memory_resource()          /* ํ•ต์‹ฌ ์ถ”๊ฐ€ ๋กœ์ง */
    โ”œโ†’ memblock_add_node()        /* memblock์— ๋“ฑ๋ก */
    โ”œโ†’ __try_online_node()        /* ๋…ธ๋“œ ์ดˆ๊ธฐํ™” */
    โ”œโ†’ arch_add_memory()          /* ์•„ํ‚คํ…์ฒ˜๋ณ„ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ƒ์„ฑ */
    โ”œโ†’ create_memory_block_devices()  /* sysfs ๋ธ”๋ก ์ƒ์„ฑ */
    โ””โ†’ online_memory_block()      /* ์ž๋™ ์˜จ๋ผ์ธ ์‹œ */
    โ””โ†’ device_online()
    โ””โ†’ online_pages()     /* Buddy์— ์ถ”๊ฐ€ */
    โ”œโ†’ move_pfn_range_to_zone()
    โ”œโ†’ online_pages_range()
    โ”‚   โ””โ†’ online_page_callback() (= generic_online_page)
    โ”‚       โ””โ†’ __free_pages_core()
    โ”œโ†’ build_all_zonelists()
    โ”œโ†’ init_per_zone_wmark_min()
    โ””โ†’ kswapd_run() / kcompactd_run()

    Hot-Remove ํ๋ฆ„ (๋ฉ”๋ชจ๋ฆฌ ์ œ๊ฑฐ)

    ๋“œ๋ผ์ด๋ฒ„/ACPI/sysfs ํŠธ๋ฆฌ๊ฑฐ
    โ””โ†’ remove_memory() / offline_and_remove_memory()
    โ””โ†’ try_remove_memory()
    โ”œโ†’ check_memblock_offlined_cb()  /* ๋ชจ๋“  ๋ธ”๋ก ์˜คํ”„๋ผ์ธ ํ™•์ธ */
    โ”œโ†’ remove_memory_block_devices() / remove_memory_blocks_and_altmaps()
    โ”œโ†’ arch_remove_memory()          /* ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์ œ๊ฑฐ */
    โ”œโ†’ memblock_remove()
    โ””โ†’ try_offline_node()            /* ๋…ธ๋“œ ์˜คํ”„๋ผ์ธ ์‹œ๋„ */
    
    offline_pages()                            /* ๊ฐœ๋ณ„ ๋ธ”๋ก ์˜คํ”„๋ผ์ธ */
    โ”œโ†’ zone_pcp_disable() / lru_cache_disable()
    โ”œโ†’ start_isolate_page_range()           /* ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ */
    โ”œโ†’ scan_movable_pages() + do_migrate_range()  /* ๋ฐ˜๋ณต ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ */
    โ”‚   โ””โ†’ migrate_pages()
    โ”œโ†’ dissolve_free_hugetlb_folios()
    โ”œโ†’ test_pages_isolated()
    โ”œโ†’ __offline_isolated_pages()           /* Buddy์—์„œ ์ œ๊ฑฐ */
    โ””โ†’ remove_pfn_range_from_zone()

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

    ์˜จ๋ผ์ธ ์ •์ฑ… ๋น„๊ต

    ์ •์ฑ…์„ค์ • ์œ„์น˜๋™์ž‘ZONE_MOVABLE์ œ๊ฑฐ ์šฉ์ด์„ฑ
    `contig-zones``online_policy` ๋ชจ๋“ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๊ธฐ์กด zone ๊ฒฝ๊ณ„๋ฅผ ๋”ฐ๋ผ ์—ฐ์† ์˜จ๋ผ์ธ๊ธฐ๋ณธ ๋ถˆ๊ฐ€์–ด๋ ค์›€
    `auto-movable``online_policy` ๋ชจ๋“ˆ ํŒŒ๋ผ๋ฏธํ„ฐMOVABLE:KERNEL ๋น„์œจ ๊ธฐ๋ฐ˜ ์ž๋™ ๊ฒฐ์ •๊ฐ€๋Šฅ์‰ฌ์›€
    `online_movable`sysfs state๋ช…์‹œ์ ์œผ๋กœ ZONE_MOVABLE์— ์˜จ๋ผ์ธํ•ญ์ƒ๊ฐ€์žฅ ์‰ฌ์›€
    `online_kernel`sysfs state๋ช…์‹œ์ ์œผ๋กœ ZONE_NORMAL์— ์˜จ๋ผ์ธ๋ถˆ๊ฐ€๋งค์šฐ ์–ด๋ ค์›€

    memmap_on_memory ๋ชจ๋“œ ๋น„๊ต

    ๋ชจ๋“œ๋™์ž‘๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„vmemmap ์œ„์น˜์‚ฌ์šฉ ์‹œ์ 
    DISABLE (0)memmap์„ ๊ธฐ์กด ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹น์—†์Œ์ผ๋ฐ˜ ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๊ธฐ๋ณธ๊ฐ’
    ENABLE (1)Hotplug ๋ฉ”๋ชจ๋ฆฌ ์ž์ฒด์— vmemmap ๋ฐฐ์น˜์—†์ŒHotplug ๋ฉ”๋ชจ๋ฆฌ ๋‚ด๋ถ€CXL/๋Œ€์šฉ๋Ÿ‰ DIMM
    FORCE (2)pageblock ์ •๋ ฌ ๋งž์ถฐ ๊ฐ•์ œ ๋ฐฐ์น˜ํŽ˜์ด์ง€ ๋‚ญ๋น„ ๊ฐ€๋ŠฅHotplug ๋ฉ”๋ชจ๋ฆฌ ๋‚ด๋ถ€์ •๋ ฌ ๋ฌธ์ œ ํ•ด๊ฒฐ ํ•„์š” ์‹œ

    ๋ฉ”๋ชจ๋ฆฌ ์ œ๊ฑฐ ์‹คํŒจ ์›์ธ ๋น„๊ต

    ์›์ธ์ฆ์ƒํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
    `MIGRATE_UNMOVABLE` ํŽ˜์ด์ง€ ์กด์žฌ`scan_movable_pages()` โ†’ `-EBUSY``online_movable`๋กœ ์˜จ๋ผ์ธ ํ›„ ์žฌ์‹œ๋„
    ๋ฉ”๋ชจ๋ฆฌ ํ™€ ์กด์žฌ`system_ram_pages != nr_pages`ํ™€ ์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ๋งŒ ์˜คํ”„๋ผ์ธ ๊ฐ€๋Šฅ
    HugeTLB folio ๋ฏธํ•ด์ฒด`dissolve_free_hugetlb_folios()` ์‹คํŒจ์‚ฌ์ „์— HugeTLB ํ•ด์ œ
    ์˜คํ”„๋ผ์ธ ์ค‘ ์‹œ๊ทธ๋„`signal_pending()` โ†’ `-EINTR`์žฌ์‹œ๋„ ๋˜๋Š” ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
    CPU๊ฐ€ ๋…ธ๋“œ์— ์ƒ์ฃผ`check_cpu_on_node()` โ†’ `-EBUSY`CPU ๋จผ์ € ์˜คํ”„๋ผ์ธ/์ด๋™

    ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ฃน ์œ ํ˜• ๋น„๊ต

    ์œ ํ˜•`is_dynamic`์ถ”๊ฐ€ ๋‹จ์œ„์ œ๊ฑฐ ๋‹จ์œ„๋Œ€ํ‘œ ์‚ฌ์šฉ์ฒ˜
    ์ •์  (Static)`false`๊ทธ๋ฃน ์ „์ฒด๊ทธ๋ฃน ์ „์ฒด์ „ํ†ต DIMM
    ๋™์  (Dynamic)`true``unit_pages` ๋‹จ์œ„`unit_pages` ๋‹จ์œ„virtio-mem, CXL

    memmap_on_memory ์ƒ์„ธ

    Linux 7.0์—์„œ ๋ฉ”๋ชจ๋ฆฌ Hotplug ์‹œ struct page ๋ฐฐ์—ด(vmemmap)์„ ์–ด๋–ป๊ฒŒ ํ• ๋‹นํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค.

    memmap_on_memory = 0 (DISABLE)
    โ†’ vmemmap์€ ๊ธฐ์กด ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ(Direct Mapping ์˜์—ญ)์— ํ• ๋‹น
    โ†’ ์žฅ์ : ์ผ๋ฐ˜ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ
    โ†’ ๋‹จ์ : ๋Œ€์šฉ๋Ÿ‰ Hotplug ์‹œ ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ ์†Œ๋ชจ
    
    memmap_on_memory = 1 (ENABLE)
    โ†’ Hotplug ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์˜ ์•ž๋ถ€๋ถ„์— vmemmap ๋ฐฐ์น˜
    โ†’ ์žฅ์ : ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ
    โ†’ ๋‹จ์ : ์‹ค์ œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ์†Œ
    
    memmap_on_memory = 2 (FORCE)
    โ†’ ENABLE์™€ ๋™์ผํ•˜๋‚˜ pageblock ์ •๋ ฌ์„ ๊ฐ•์ œ
    โ†’ ์žฅ์ : ์ •๋ ฌ ๋ฌธ์ œ ํ•ด๊ฒฐ
    โ†’ ๋‹จ์ : ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„ ๊ฐ€๋Šฅ

    mhp_supports_memmap_on_memory() ํ•จ์ˆ˜๊ฐ€ ๋‹ค์Œ ์กฐ๊ฑด์„ ๋ชจ๋‘ ๋งŒ์กฑํ•  ๋•Œ๋งŒ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค:

    1. memmap_on_memory ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ™œ์„ฑํ™”๋จ

    2. vmemmap ํฌ๊ธฐ๊ฐ€ PAGE_SIZE๋กœ ์ •๋ ฌ๋จ

    3. vmemmap ํŽ˜์ด์ง€๊ฐ€ pageblock_nr_pages๋กœ ์ •๋ ฌ๋จ

    4. vmemmap ํฌ๊ธฐ๊ฐ€ ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก๊ณผ ๊ฐ™์ง€ ์•Š์Œ (์œ ํšจํ•˜์ง€ ์•Š์€ ํ•ซํ”Œ๋Ÿฌ๊ทธ)

    5. arch_supports_memmap_on_memory()๊ฐ€ ์•„ํ‚คํ…์ฒ˜ ์ˆ˜์ค€์—์„œ ์ง€์›


    auto-movable ์˜จ๋ผ์ธ ์ •์ฑ… ์ƒ์„ธ

    Linux 7.0์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์˜จ๋ผ์ธ ์‹œ ZONE_MOVABLE์— ์ž๋™ ๋ฐฐ์น˜ํ•˜๋Š” ์ •์ฑ…์ž…๋‹ˆ๋‹ค.

    kernelcore=์™€ movablecore=๋Š” ๋ถ€ํŒ… ๋‹จ๊ณ„์—์„œ ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ์™€ ์ด๋™ ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ธฐ์ค€์„ ์„ ๋จผ์ € ์žก์Šต๋‹ˆ๋‹ค. ๊ทธ ์œ„์—์„œ movable_node์™€ online_policy="auto-movable"๊ฐ€ ์‹ค์ œ hotplug ๋ฉ”๋ชจ๋ฆฌ์˜ ๋ฐฐ์น˜ ๋ฐฉํ–ฅ์„ ๊ฒฐ์ •ํ•˜๊ณ , auto_movable_ratio์™€ auto_movable_numa_aware๊ฐ€ ์„ธ๋ถ€ ๋น„์œจ์„ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

    online_policy = "auto-movable"
    โ†’ MOVABLE : KERNEL ๋น„์œจ์„ ๊ณ„์‚ฐํ•˜์—ฌ ZONE_MOVABLE ํ• ๋‹น ์—ฌ๋ถ€ ๊ฒฐ์ •
    
    ๋น„์œจ ๊ณ„์‚ฐ:
    auto_movable_ratio (๊ธฐ๋ณธ 301%) ๊ธฐ์ค€
    MOVABLE ํŽ˜์ด์ง€ ์ˆ˜ โ‰ค (auto_movable_ratio ร— KERNEL_EARLY ํŽ˜์ด์ง€ ์ˆ˜) / 100
    
    KERNEL_EARLY = ๋ถ€ํŒ… ์‹œ ์ปค๋„ ์˜์—ญ์— ์žˆ๋Š” ํŽ˜์ด์ง€
    โ†’ Hotplug๋กœ ์ถ”๊ฐ€๋œ ์ปค๋„ ์˜์—ญ์€ ์ œ์™ธ (ํ–ฅํ›„ ์ œ๊ฑฐ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ)
    โ†’ ๋™์  ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ฃน์€ ์˜ˆ์™ธ ( DRIVER_MANAGED ๋ฉ”๋ชจ๋ฆฌ )
    
    NUMA ์ธ์‹:
    auto_movable_numa_aware = true (๊ธฐ๋ณธ๊ฐ’)
    โ†’ ๋…ธ๋“œ๋ณ„ ๋น„์œจ๋„ ๊ฒ€์‚ฌํ•˜์—ฌ ํŠน์ • ๋…ธ๋“œ์— ์ ๋ฆผ ๋ฐฉ์ง€

    ๊ด€๋ จ ๋ฌธ์„œ

  • 00-๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” โ€” ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์กฐ๊ฐ๋„
  • 04-Memblock โ€” ๋ถ€ํŒ… ์ดˆ๊ธฐ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ž
  • 11-Compaction โ€” Memory Compaction (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „์ œ ์กฐ๊ฑด)
  • 13-NUMA โ€” NUMA ์•„ํ‚คํ…์ฒ˜์™€ ๋…ธ๋“œ ๊ด€๋ฆฌ
  • 18-CMA โ€” Contiguous Memory Allocator
  • 20-Migration โ€” ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ธ
  • 24-ํ‹ฐ์–ด๋ง โ€” ๋ฉ”๋ชจ๋ฆฌ ํ‹ฐ์–ด๋ง

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

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

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

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

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