๏ปฟ# Per-CPU ๋ฉ”๋ชจ๋ฆฌ

๊ด€๋ จ ์†Œ์Šค: mm/percpu.c, mm/percpu-internal.h, mm/percpu-vm.c, include/linux/percpu.h

๊ฐœ์š” (Overview)

Per-CPU ํ• ๋‹น๊ธฐ๋Š” ์ปค๋„ ๋‚ด์—์„œ CPU๋ณ„๋กœ ๋…๋ฆฝ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ๊ด€๋ฆฌํ•˜๋Š” ํ• ๋‹น๊ธฐ์ž…๋‹ˆ๋‹ค. ๊ฐ CPU๊ฐ€ ๊ณ ์œ ํ•œ ๋ฐ์ดํ„ฐ ์˜์—ญ์„ ๊ฐ€์ง€๋ฏ€๋กœ ๋ฝ ์—†์ด ๋น ๋ฅด๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์บ์‹œ ๋ผ์ธ ์ถฉ๋Œ(False Sharing)์„ ๊ทผ๋ณธ์ ์œผ๋กœ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. Linux 7.0์—์„œ Per-CPU ํ• ๋‹น๊ธฐ๋Š” static ๋ณ€์ˆ˜ ์˜์—ญ๊ณผ ๋™์  ํ• ๋‹น ์˜์—ญ์„ ๋ชจ๋‘ ๋‹ด๋‹นํ•˜๋ฉฐ, slab ํ• ๋‹น๊ธฐ์˜ ์ดˆ๊ธฐํ™” ์ด์ „์—๋„ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์–ด ์ปค๋„ ๋ถ€ํŒ… ๊ณผ์ •์—์„œ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

ํ• ๋‹น๊ธฐ๋Š” chunk ๋‹จ์œ„๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ chunk๋Š” ์—ฌ๋Ÿฌ unit์œผ๋กœ ๋‚˜๋‰˜๋ฉฐ, ๊ฐ unit์€ ํŠน์ • CPU์— ๋Œ€์‘ํ•ฉ๋‹ˆ๋‹ค. NUMA ํ™˜๊ฒฝ์—์„œ๋Š” unit ๊ฐ„ ๋ฌผ๋ฆฌ์  ๊ฑฐ๋ฆฌ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ group ๋‹จ์œ„๋กœ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค. bitmap ๊ธฐ๋ฐ˜์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋นˆ ์˜์—ญ์„ ์ถ”์ ํ•˜๋ฉฐ, metadata block ํžŒํŠธ๋ฅผ ํ†ตํ•ด ๋Œ€๊ทœ๋ชจ ๋น„ํŠธ๋งต ์Šค์บ” ์—†์ด ๋น ๋ฅด๊ฒŒ ํ• ๋‹น ์œ„์น˜๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

memcg-aware ํ• ๋‹น์€ __GFP_ACCOUNT๊ฐ€ ๋ถ™์€ ๊ฒฝ์šฐ์—๋งŒ pcpu_memcg_pre_alloc_hook()์„ ํ†ตํ•ด ๊ณ„์ •๋˜๋ฉฐ, ๊ณ„์ •๋œ ํ• ๋‹น๊ณผ ๋น„๊ณ„์ • ํ• ๋‹น์€ ์„œ๋กœ ๋‹ค๋ฅธ chunk ์ง‘ํ•ฉ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ : Per-CPU ํ• ๋‹น๊ธฐ๋Š” ๋„์„œ๊ด€์˜ "๊ฐœ์ธ ์‚ฌ๋ฌผํ•จ"๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฐ CPU(์‚ฌ๋žŒ)๋งˆ๋‹ค ์ „์šฉ ์‚ฌ๋ฌผํ•จ(Per-CPU ์˜์—ญ)์ด ์žˆ์–ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์‚ฌ๋ฌผํ•จ์„ ์“ธ ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฝ์ด ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ๋ฌผํ•จ์€ ๋” ํฐ ๊ตฌ์—ญ(chunk)์œผ๋กœ ๋‚˜๋‰˜๊ณ , ๊ฐ ๊ตฌ์—ญ์€ ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ๊ณต์œ ํ•˜์ง€๋งŒ ๊ฐ์ž ๋‹ค๋ฅธ ์นธ(unit)์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ:
mm/percpu.c              โ† ๋ฉ”์ธ ํ• ๋‹น ๋กœ์ง (3388์ค„)
mm/percpu-internal.h     โ† chunk/block ๋‚ด๋ถ€ ๊ตฌ์กฐ์ฒด ์ •์˜ (288์ค„)
mm/percpu-vm.c           โ† vmalloc ๊ธฐ๋ฐ˜ chunk ๋ฐฑ์—… ๊ตฌํ˜„ (410์ค„)
include/linux/percpu.h   โ† ์‚ฌ์šฉ์ž API, ์ƒ์ˆ˜ ์ •์˜ (164์ค„)

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

# Per-CPU ์˜์—ญ ๊ธฐ๋ณธ ์ •๋ณด ์ถœ๋ ฅ
cat /proc/pagetypeinfo | grep -A5 "Per-CPU"

# ํ˜„์žฌ ์‹œ์Šคํ…œ์˜ Per-CPU ๋™์  ํ• ๋‹น ํ†ต๊ณ„
cat /proc/slabinfo | grep percpu

# Per-CPU ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ (์ „์ฒด)
cat /proc/meminfo | grep Percpu

# ํŠน์ • ํ”„๋กœ์„ธ์Šค์˜ Per-CPU ๊ด€๋ จ ๋ฉ”๋ชจ๋ฆฌ ๋งต ํ™•์ธ
cat /proc/1/maps | grep -i percpu 2>/dev/null || echo "ํ•ด๋‹น ์—†์Œ"

# ์ปค๋„ ๋ชจ๋“ˆ์˜ Per-CPU ์˜์—ญ ํ™•์ธ
cat /proc/modules | awk '{print $1}' | head -5 | xargs -I{} sh -c 'echo "--- {} ---"; cat /sys/module/{}/sections/.data..percpu 2>/dev/null || echo "์—†์Œ"'

# Per-CPU ํ• ๋‹น๊ธฐ ๋‚ด๋ถ€ ์ƒํƒœ ์ถ”์  (CONFIG_PERCPU_STATS ํ™œ์„ฑํ™” ์‹œ)
cat /sys/kernel/debug/percpu_stats 2>/dev/null || echo "๋””๋ฒ„๊ทธ ์ •๋ณด ์—†์Œ (CONFIG_PERCPU_STATS ๋น„ํ™œ์„ฑํ™”)"

# ์ปค๋„ ์‹œ์ž‘ ์‹œ Per-CPU ์ดˆ๊ธฐํ™” ๋กœ๊ทธ ํ™•์ธ
dmesg | grep -i "pcpu" | head -20

# ์‹œ์Šคํ…œ์˜ ์ด Per-CPU ์˜์—ญ ํฌ๊ธฐ ํ™•์ธ
dmesg | grep "pcpu-alloc" | head -5

# NUMA ๋…ธ๋“œ๋ณ„ Per-CPU ์˜์—ญ ๋ถ„ํฌ ํ™•์ธ
cat /proc/buddyinfo | head -5

# ์ปค๋„ ์„ค์ •์—์„œ Per-CPU ๊ด€๋ จ ์˜ต์…˜ ํ™•์ธ
grep -i "PERCPU\|PER_CPU" /boot/config-$(uname -r) 2>/dev/null | head -10

# Per-CPU ํ• ๋‹น ํฌ๊ธฐ๋ณ„ ๋ถ„ํฌ ํ™•์ธ (CONFIG_PERCPU_STATS ์‹œ)
cat /sys/kernel/debug/percpu_stats 2>/dev/null | grep -A20 "alloc_size" || echo "ํ†ต๊ณ„ ์ •๋ณด ์—†์Œ"

# ํ˜„์žฌ Per-CPU ์˜์—ญ์˜ ์‹ค์ œ ์‚ฌ์šฉ๋ฅ  ํ™•์ธ
cat /proc/vmstat | grep -i "percpu\|pcpu" || echo "๊ด€๋ จ ํ†ต๊ณ„ ์—†์Œ"

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

struct pcpu_chunk

ํ• ๋‹น๊ธฐ์˜ ํ•ต์‹ฌ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ๊ฐ chunk๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ unit(๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์„ธํŠธ)์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, bitmap ๊ธฐ๋ฐ˜์œผ๋กœ ํ• ๋‹น ์ƒํƒœ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.

/* mm/percpu-internal.h:48-88 */
struct pcpu_chunk {
#ifdef CONFIG_PERCPU_STATS
int             nr_alloc;       /* # of allocations */
size_t          max_alloc_size; /* largest allocation size */
#endif

struct list_head    list;       /* pcpu_chunk_lists ์Šฌ๋กฏ์— ์—ฐ๊ฒฐ */
int             free_bytes;     /* chunk ๋‚ด ๋นˆ ๋ฐ”์ดํŠธ ์ˆ˜ */
struct pcpu_block_md chunk_md;  /* chunk ์ˆ˜์ค€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ธ”๋ก */
unsigned long   *bound_map;     /* ๊ฒฝ๊ณ„ ๋น„ํŠธ๋งต (ํ• ๋‹น ๊ฒฝ๊ณ„ ํ‘œ์‹œ) */

/* base_addr: ์บ์‹œ ๋ผ์ธ ๋ถ„๋ฆฌ๋ฅผ ์œ„ํ•ด ๋ณ„๋„ ์บ์‹œ ๋ผ์ธ์— ๋ฐฐ์น˜ */
void            *base_addr ____cacheline_aligned_in_smp;

unsigned long   *alloc_map;     /* ํ• ๋‹น ๋น„ํŠธ๋งต (1=ํ• ๋‹น๋จ, 0=๋นˆ ์˜์—ญ) */
struct pcpu_block_md *md_blocks;/* ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ธ”๋ก ๋ฐฐ์—ด */

void            *data;          /* chunk ๋ฐ์ดํ„ฐ */
bool            immutable;      /* [de]population ๊ธˆ์ง€ (์ฒซ ๋ฒˆ์งธ chunk) */
bool            isolated;       /* ํ™œ์„ฑ ์Šฌ๋กฏ์—์„œ ๊ฒฉ๋ฆฌ๋จ */
int             start_offset;   /* ์ด์ „ ์˜์—ญ๊ณผ์˜ ๊ฒน์นจ (ํŽ˜์ด์ง€ ์ •๋ ฌ ๋ณด์ •) */
int             end_offset;     /* ๋ ์ •๋ ฌ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ์˜์—ญ */
#ifdef NEED_PCPUOBJ_EXT
struct pcpuobj_ext *obj_exts;   /* obj_cgroup ๋ฒกํ„ฐ (memcg์šฉ) */
#endif

int             nr_pages;       /* ์ด chunk๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์ˆ˜ */
int             nr_populated;   /* ์‹ค์ œ ํ• ๋‹น๋œ ํŽ˜์ด์ง€ ์ˆ˜ */
int             nr_empty_pop_pages; /* ๋นˆ ํŽ˜์ด์ง€ ์ˆ˜ */
unsigned long   populated[];    /* ํŽ˜์ด์ง€๋ณ„ ํ• ๋‹น ์ƒํƒœ ๋น„ํŠธ๋งต (์œ ์—ฐ ๋ฐฐ์—ด) */
};

struct pcpu_block_md

bitmap ๋‚ด๋ถ€์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ธ”๋ก์ž…๋‹ˆ๋‹ค. chunk์˜ bitmap์€ PCPU_BITMAP_BLOCK_BITS(=PAGE_SIZE/4)๊ฐœ์˜ ๋น„ํŠธ๋กœ ๋‚˜๋‰˜๋ฉฐ, ๊ฐ ๋ธ”๋ก๋งˆ๋‹ค ๋นˆ ์˜์—ญ ํžŒํŠธ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

/* mm/percpu-internal.h:20-33 */
struct pcpu_block_md {
int     scan_hint;          /* ๋ธ”๋ก ๋‚ด ์Šค์บ” ํžŒํŠธ (์ตœ๋Œ€ ๋นˆ ์˜์—ญ) */
int     scan_hint_start;    /* ์Šค์บ” ํžŒํŠธ์˜ ์‹œ์ž‘ ์œ„์น˜ (๋ธ”๋ก ์ƒ๋Œ€) */
int     contig_hint;        /* ๋ธ”๋ก ๋‚ด ์ตœ๋Œ€ ์—ฐ์† ๋นˆ ์˜์—ญ ํฌ๊ธฐ */
int     contig_hint_start;  /* contig_hint ์‹œ์ž‘ ์œ„์น˜ */
int     left_free;          /* ๋ธ”๋ก ์™ผ์ชฝ ๋์—์„œ์˜ ๋นˆ ์˜์—ญ ํฌ๊ธฐ */
int     right_free;         /* ๋ธ”๋ก ์˜ค๋ฅธ์ชฝ ๋์—์„œ์˜ ๋นˆ ์˜์—ญ ํฌ๊ธฐ */
int     first_free;         /* ๋ธ”๋ก ๋‚ด ์ฒซ ๋ฒˆ์งธ ๋นˆ ๋น„ํŠธ ์œ„์น˜ */
int     nr_bits;            /* ์ด ๋ธ”๋ก์ด ๋‹ด๋‹นํ•˜๋Š” ์ด ๋น„ํŠธ ์ˆ˜ */
};

struct pcpu_alloc_info

์ดˆ๊ธฐํ™” ์‹œ Per-CPU ์˜์—ญ์˜ ์ „์ฒด ๋ ˆ์ด์•„์›ƒ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฃน, ์œ ๋‹›, ์ •์ /์˜ˆ์•ฝ/๋™์  ์˜์—ญ ํฌ๊ธฐ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

/* include/linux/percpu.h:85-95 */
struct pcpu_alloc_info {
size_t      static_size;    /* ์ •์  percpu ๋ณ€์ˆ˜ ์˜์—ญ ํฌ๊ธฐ */
size_t      reserved_size;  /* ์˜ˆ์•ฝ ์˜์—ญ ํฌ๊ธฐ (๋ชจ๋“ˆ์šฉ) */
size_t      dyn_size;       /* ๋™์  ํ• ๋‹น ๊ฐ€๋Šฅ ์˜์—ญ ํฌ๊ธฐ */
size_t      unit_size;      /* ๋‹จ์ผ ์œ ๋‹›์˜ ์ด ํฌ๊ธฐ */
size_t      atom_size;      /* ํ• ๋‹น ์ •๋ ฌ ๋‹จ์œ„ (vmalloc ๋งคํ•‘ ๊ธฐ์ค€) */
size_t      alloc_size;     /* ์ด ํ• ๋‹น ํฌ๊ธฐ (atom_size ๋ฐฐ์ˆ˜) */
size_t      __ai_size;      /* ๋‚ด๋ถ€ ์‚ฌ์šฉ ํฌ๊ธฐ */
int         nr_groups;      /* NUMA ๊ทธ๋ฃน ์ˆ˜ (0์ด๋ฉด ๋‹จ์ผ ๊ทธ๋ฃน) */
struct pcpu_group_info groups[];
};

ํ•ต์‹ฌ ์ƒ์ˆ˜

/* include/linux/percpu.h:24-37 */
#define PCPU_MIN_UNIT_SIZE      PFN_ALIGN(32 << 10)  /* ์ตœ์†Œ ์œ ๋‹› ํฌ๊ธฐ: 32KB */
#define PCPU_MIN_ALLOC_SHIFT    2                     /* ์ตœ์†Œ ํ• ๋‹น ๋‹จ์œ„: 4 bytes */
#define PCPU_MIN_ALLOC_SIZE     (1 << PCPU_MIN_ALLOC_SHIFT)  /* = 4 bytes */
#define PCPU_BITMAP_BLOCK_SIZE  PAGE_SIZE             /* ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ธ”๋ก = ํŽ˜์ด์ง€ ํฌ๊ธฐ */
#define PCPU_BITMAP_BLOCK_BITS  (PCPU_BITMAP_BLOCK_SIZE >> PCPU_MIN_ALLOC_SHIFT)
/* = PAGE_SIZE/4 ๋น„ํŠธ */

์ „์—ญ ๋ณ€์ˆ˜

/* mm/percpu.c:132-198 */
static int pcpu_unit_pages;      /* ์œ ๋‹›๋‹น ํŽ˜์ด์ง€ ์ˆ˜ */
static int pcpu_unit_size;       /* ์œ ๋‹› ํฌ๊ธฐ (bytes) */
static int pcpu_nr_units;        /* ์ „์ฒด ์œ ๋‹› ์ˆ˜ */
static int pcpu_atom_size;       /* ํ• ๋‹น ์ •๋ ฌ ๋‹จ์œ„ */
int pcpu_nr_slots;               /* chunk ์Šฌ๋กฏ ์ˆ˜ */
static int pcpu_free_slot;       /* ์™„์ „ํžˆ ๋นˆ chunk ์Šฌ๋กฏ ์ธ๋ฑ์Šค */
int pcpu_sidelined_slot;         /* ๊ฒฉ๋ฆฌ๋œ chunk ์Šฌ๋กฏ ์ธ๋ฑ์Šค */
int pcpu_to_depopulate_slot;     /* depopulate ๋Œ€๊ธฐ chunk ์Šฌ๋กฏ ์ธ๋ฑ์Šค */

void *pcpu_base_addr;            /* ์ฒซ ๋ฒˆ์งธ chunk์˜ ๊ธฐ๋ณธ ์ฃผ์†Œ */
struct pcpu_chunk *pcpu_first_chunk;  /* ์ฒซ ๋ฒˆ์งธ chunk (์ •์  ์˜์—ญ ํฌํ•จ) */
struct pcpu_chunk *pcpu_reserved_chunk; /* ์˜ˆ์•ฝ ์˜์—ญ chunk */

DEFINE_SPINLOCK(pcpu_lock);      /* ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋ณดํ˜ธ */
static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk ์ƒ์„ฑ/ํŒŒ๊ดด, [de]pop ๋ณดํ˜ธ */

static int pcpu_nr_empty_pop_pages;    /* ๋นˆ ํŽ˜์ด์ง€ ์ˆ˜ (pcpu_lock ๋ณดํ˜ธ) */
static unsigned long pcpu_nr_populated; /* ํ• ๋‹น๋œ ํŽ˜์ด์ง€ ์ˆ˜ */

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

pcpu_alloc_noprof (๋™์  Per-CPU ํ• ๋‹น)

Per-CPU ์˜์—ญ์˜ ๋ฉ”์ธ ํ• ๋‹น ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. slab ์ดˆ๊ธฐํ™” ์ด์ „์—๋„ ๋™์ž‘ํ•˜๋ฉฐ, memcg ์ธ์‹ ํ• ๋‹น์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

/* mm/percpu.c:1736-1938 */
void __percpu *pcpu_alloc_noprof(size_t size, size_t align, bool reserved,
gfp_t gfp)
/* mm/percpu.c:1615-1633 */
#ifdef CONFIG_MEMCG
static bool pcpu_memcg_pre_alloc_hook(size_t size, gfp_t gfp,
				      struct obj_cgroup **objcgp)
{
	struct obj_cgroup *objcg;

	if (!memcg_kmem_online() || !(gfp & __GFP_ACCOUNT))
		return true;

	objcg = current_obj_cgroup();
	if (!objcg)
		return true;

	if (obj_cgroup_charge(objcg, gfp, pcpu_obj_full_size(size)))
		return false;

	*objcgp = objcg;
	return true;
}
#endif

ํ๋ฆ„:

1. GFP ํ”Œ๋ž˜๊ทธ์—์„œ ํ—ˆ์šฉ๋œ ํ”Œ๋ž˜๊ทธ๋งŒ ์ถ”์ถœ (pcpu_gfp)

2. ํฌ๊ธฐ๋ฅผ PCPU_MIN_ALLOC_SIZE(4B) ๋‹จ์œ„๋กœ ์ •๋ ฌ โ†’ ๋น„ํŠธ ์ˆ˜๋กœ ๋ณ€ํ™˜

3. memcg ์‚ฌ์ „ ํ• ๋‹น ๊ฒ€์‚ฌ (pcpu_memcg_pre_alloc_hook)

4. ๋น„์›์ž์  ํ• ๋‹น ์‹œ pcpu_alloc_mutex ํš๋“ (๋ฐ๋“œ๋ฝ ๋ฐฉ์ง€ ์œ„ํ•ด killable ์‚ฌ์šฉ)

5. pcpu_lock ํš๋“ ํ›„:

- ์˜ˆ์•ฝ chunk๊ฐ€ ์žˆ์œผ๋ฉด ๋จผ์ € ์‹œ๋„

- pcpu_chunk_lists[slot] ์ˆœํšŒํ•˜๋ฉฐ pcpu_find_block_fit + pcpu_alloc_area ํ˜ธ์ถœ

- ๊ณต๊ฐ„ ์—†์œผ๋ฉด ์ƒˆ chunk ์ƒ์„ฑ ํ›„ ์žฌ์‹œ๋„

6. ํŽ˜์ด์ง€ ๋ฏธํ• ๋‹น ์˜์—ญ์€ pcpu_populate_chunk๋กœ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ์—ฐ๊ฒฐ

7. ๋ชจ๋“  ์œ ๋‹›์— ๋Œ€ํ•ด memset์œผ๋กœ ์˜์—ญ ์ œ๋กœ ์ดˆ๊ธฐํ™”

8. __addr_to_pcpu_ptr๋กœ Per-CPU ํฌ์ธํ„ฐ ๋ณ€ํ™˜ ํ›„ ๋ฐ˜ํ™˜

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

  • reserved == true โ†’ pcpu_reserved_chunk์—์„œ๋งŒ ํ• ๋‹น
  • is_atomic == true โ†’ ํŽ˜์ด์ง€ pop ์—†์ด bitmap๋งŒ ์‚ฌ์šฉ, ์‹คํŒจ ์‹œ ์ฆ‰์‹œ ๋ฐ˜ํ™˜
  • pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW โ†’ ๋ฐธ๋Ÿฐ์Šค ์›Œํฌ ์Šค์ผ€์ค„
  • free_percpu (Per-CPU ํ•ด์ œ)

    ํ• ๋‹น๋œ Per-CPU ์˜์—ญ์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค. chunk ์ƒํƒœ์— ๋”ฐ๋ผ ๋ฐธ๋Ÿฐ์Šค ์›Œํฌ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    /* mm/percpu.c:2232-2288 */
    void free_percpu(void __percpu *ptr)

    ํ๋ฆ„:

    1. __pcpu_ptr_to_addr๋กœ ์ผ๋ฐ˜ ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜

    2. pcpu_chunk_addr_search๋กœ ํ•ด๋‹น chunk ํƒ์ƒ‰

    3. pcpu_free_area๋กœ bitmap ํ•ด์ œ โ†’ freed ๋ฐ”์ดํŠธ ์ˆ˜ ๋ฐ˜ํ™˜

    4. pcpu_memcg_free_hook / pcpu_alloc_tag_free_hook ํ˜ธ์ถœ

    5. ์™„์ „ํžˆ ๋นˆ chunk๊ฐ€ 2๊ฐœ ์ด์ƒ์ด๋ฉด ๋ฐธ๋Ÿฐ์Šค ์›Œํฌ ์Šค์ผ€์ค„

    6. pcpu_should_reclaim_chunk ์กฐ๊ฑด ์ถฉ์กฑ ์‹œ chunk ๊ฒฉ๋ฆฌ ํ›„ ๋ฐธ๋Ÿฐ์Šค ์›Œํฌ

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

  • chunk->free_bytes == pcpu_unit_size โ†’ ์™„์ „ํžˆ ๋นˆ chunk โ†’ ์ถ”๊ฐ€ ๋นˆ chunk ์กด์žฌ ์‹œ ๋ฐธ๋Ÿฐ์Šค
  • pcpu_should_reclaim_chunk(chunk) โ†’ ๊ฒฉ๋ฆฌ๋œ chunk โ†’ ๋ฐธ๋Ÿฐ์Šค ์›Œํฌ ํŠธ๋ฆฌ๊ฑฐ
  • pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW โ†’ ๋นˆ ํŽ˜์ด์ง€ ๋ถ€์กฑ โ†’ ๋ฐธ๋Ÿฐ์Šค ์›Œํฌ
  • pcpu_balance_workfn (๋น„๋™๊ธฐ ๋ฐธ๋Ÿฐ์Šค)

    ๋นˆ chunk ์ •๋ฆฌ, ํŽ˜์ด์ง€ repopulate, depopulate๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋น„๋™๊ธฐ ์›Œํฌ์ž…๋‹ˆ๋‹ค.

    /* mm/percpu.c:2196-2221 */
    static void pcpu_balance_workfn(struct work_struct *work)

    ํ๋ฆ„:

    1. memalloc_noio_save()๋กœ GFP_NOIO ์ปจํ…์ŠคํŠธ ์„ค์ •

    2. pcpu_alloc_mutex + pcpu_lock ํš๋“

    3. pcpu_balance_free(false) โ†’ ์™„์ „ํžˆ ๋นˆ chunk ๋ชจ๋‘ ์ •๋ฆฌ (ํ•˜๋‚˜ ์ œ์™ธ)

    4. pcpu_reclaim_populated() โ†’ depopulate ๋Œ€๊ธฐ chunk์—์„œ ๋นˆ ํŽ˜์ด์ง€ ํ•ด์ œ

    5. pcpu_balance_populated() โ†’ atomic ํ• ๋‹น์„ ์œ„ํ•ด ๋ฏธ๋ฆฌ populating

    6. pcpu_balance_free(true) โ†’ populating ์—†๋Š” ๋นˆ chunk๋งŒ ์ถ”๊ฐ€ ์ •๋ฆฌ

    pcpu_alloc_area (bitmap ๊ธฐ๋ฐ˜ ์˜์—ญ ํ• ๋‹น)

    ์‹ค์ œ bitmap์—์„œ ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ์˜์—ญ์„ ์ฐพ์•„ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

    /* mm/percpu.c:1216-1263 */
    static int pcpu_alloc_area(struct pcpu_chunk *chunk, int alloc_bits,
    size_t align, int start)

    ํ๋ฆ„:

    1. pcpu_find_zero_area๋กœ ์ •๋ ฌ๋œ ๋นˆ ์˜์—ญ ํƒ์ƒ‰

    2. alloc_map์— ๋น„ํŠธ ์„ค์ • (bitmap_set)

    3. bound_map์— ๊ฒฝ๊ณ„ ๋น„ํŠธ ์„ค์ • (ํ• ๋‹น ์‹œ์ž‘/๋ ํ‘œ์‹œ)

    4. chunk_md->first_free ๊ฐฑ์‹ 

    5. pcpu_block_update_hint_alloc๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํžŒํŠธ ๊ฐฑ์‹ 

    6. pcpu_chunk_relocate๋กœ ์ ์ ˆํ•œ ์Šฌ๋กฏ์œผ๋กœ ์ด๋™

    pcpu_find_block_fit (๋ธ”๋ก ์ˆ˜์ค€ ํ• ๋‹น ๊ฐ€๋Šฅ ์œ„์น˜ ํƒ์ƒ‰)

    ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ธ”๋ก์˜ ํžŒํŠธ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ์‹œ์ž‘ ์œ„์น˜๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

    /* mm/percpu.c:1110-1139 */
    static int pcpu_find_block_fit(struct pcpu_chunk *chunk, int alloc_bits,
    size_t align, bool pop_only)

    ํ๋ฆ„:

    1. chunk_md->contig_hint๋กœ ์ „์ฒด chunk ์ˆ˜์ค€ ๊ฒ€์‚ฌ โ€” ํ• ๋‹น ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด ์ฆ‰์‹œ ์‹คํŒจ

    2. pcpu_next_hint๋กœ ์Šค์บ” ์‹œ์ž‘ ์œ„์น˜ ๊ฒฐ์ •

    3. pcpu_for_each_fit_region ๋งคํฌ๋กœ๋กœ fit ์˜์—ญ ์ˆœํšŒ

    4. pop_only ์‹œ pcpu_is_populated๋กœ ํŽ˜์ด์ง€ ์กด์žฌ ํ™•์ธ

    5. ์œ ํšจํ•œ offset ๋ฐ˜ํ™˜ ๋˜๋Š” -1


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

    alloc_percpu(type)
    โ””โ”€ pcpu_alloc_noprof(size, align, false, GFP_KERNEL)
    โ”œโ”€ [๋ฉ”๋ชจ๋ฆฌ ํฌ๊ธฐ ๊ฒ€์ฆ]
    โ”œโ”€ pcpu_memcg_pre_alloc_hook()
    โ”œโ”€ mutex_lock(&pcpu_alloc_mutex)    โ† ๋น„์›์ž์  ํ• ๋‹น๋งŒ
    โ”œโ”€ spin_lock_irqsave(&pcpu_lock)
    โ”‚    โ”œโ”€ [์˜ˆ์•ฝ chunk ์‹œ๋„] pcpu_find_block_fit โ†’ pcpu_alloc_area
    โ”‚    โ”œโ”€ [์ผ๋ฐ˜ chunk ์ˆœํšŒ] for each slot โ†’ pcpu_find_block_fit โ†’ pcpu_alloc_area
    โ”‚    โ””โ”€ [๊ณต๊ฐ„ ์—†์Œ] pcpu_create_chunk โ†’ ์žฌ์‹œ๋„
    โ”œโ”€ pcpu_populate_chunk()            โ† ๋ฏธํ• ๋‹น ํŽ˜์ด์ง€ ์—ฐ๊ฒฐ
    โ”œโ”€ memset() (๋ชจ๋“  CPU)
    โ”œโ”€ __addr_to_pcpu_ptr()            โ† Per-CPU ํฌ์ธํ„ฐ ๋ณ€ํ™˜
    โ””โ”€ pcpu_memcg_post_alloc_hook()
    
    free_percpu(ptr)
    โ”œโ”€ __pcpu_ptr_to_addr()
    โ”œโ”€ pcpu_chunk_addr_search()
    โ”œโ”€ spin_lock_irqsave(&pcpu_lock)
    โ”‚    โ””โ”€ pcpu_free_area()
    โ”‚         โ””โ”€ bitmap_clear + pcpu_block_update_hint_free
    โ”œโ”€ pcpu_memcg_free_hook()
    โ””โ”€ pcpu_schedule_balance_work()         โ† ํ•„์š” ์‹œ
    
    pcpu_balance_workfn()                     โ† ๋น„๋™๊ธฐ ์›Œํฌ ํ
    โ”œโ”€ pcpu_balance_free(false)             โ† ๋นˆ chunk ์ •๋ฆฌ
    โ”œโ”€ pcpu_reclaim_populated()             โ† depopulate ๋Œ€๊ธฐ ํ•ด์ œ
    โ”œโ”€ pcpu_balance_populated()             โ† atomic์šฉ ์‚ฌ์ „ populating
    โ””โ”€ pcpu_balance_free(true)              โ† ์ถ”๊ฐ€ ์ •๋ฆฌ
    
    pcpu_create_chunk(gfp)                    โ† ์ƒˆ chunk ์ƒ์„ฑ
    โ”œโ”€ pcpu_mem_zalloc()                    โ† chunk ๊ตฌ์กฐ์ฒด ํ• ๋‹น
    โ”œโ”€ pcpu_mem_zalloc()                    โ† alloc_map ํ• ๋‹น
    โ”œโ”€ pcpu_mem_zalloc()                    โ† bound_map ํ• ๋‹น
    โ”œโ”€ pcpu_mem_zalloc()                    โ† md_blocks ํ• ๋‹น
    โ””โ”€ pcpu_init_md_blocks()               โ† ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ธ”๋ก ์ดˆ๊ธฐํ™”
    
    pcpu_populate_chunk(chunk, page_start, page_end, gfp)
    โ””โ”€ pcpu-vm.c/pcpu-km.c ๊ตฌํ˜„            โ† ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์—ฐ๊ฒฐ

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

    ํ• ๋‹น ๊ฒฝ๋กœ ๋น„๊ต

    ์กฐ๊ฑด๋™์ž‘์„ค๋ช…
    `reserved == true``pcpu_reserved_chunk`์—์„œ๋งŒ ํ• ๋‹น๋ชจ๋“ˆ์šฉ ์ •์  percpu ์˜์—ญ
    `reserved == false`์ผ๋ฐ˜ chunk ์Šฌ๋กฏ ์ˆœํšŒ๋™์  percpu ํ• ๋‹น
    `is_atomic == true`populating ์—†์ด bitmap๋งŒ ์‚ฌ์šฉIRQ ์ปจํ…์ŠคํŠธ ๋“ฑ ๋ธ”๋กœํ‚น ๋ถˆ๊ฐ€
    `is_atomic == false``pcpu_populate_chunk` ํ˜ธ์ถœ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์‹ค์ œ ์—ฐ๊ฒฐ
    `GFP_NOFS/NOIO``memalloc_noio_save()` ์ ์šฉํŒŒ์ผ ์‹œ์Šคํ…œ ๋ฐ๋“œ๋ฝ ๋ฐฉ์ง€
    `__GFP_ACCOUNT`memcg-aware chunk ์„ธํŠธ ์‚ฌ์šฉ`obj_cgroup` ์ถฉ์ „, root cgroup/๋น„๊ณ„์ •๊ณผ ๋ถ„๋ฆฌ

    chunk ์Šฌ๋กฏ ์œ ํ˜•

    ์Šฌ๋กฏ๋ณ€์ˆ˜์šฉ๋„
    0 ~ N-1`pcpu_chunk_lists[0..N-1]`ํฌ๊ธฐ๋ณ„ ํ• ๋‹น ๊ฐ€๋Šฅ chunk ์ •๋ ฌ
    `pcpu_sidelined_slot``pcpu_sidelined_slot`๊ฒฉ๋ฆฌ๋œ chunk (depopulate ๋ถˆ๊ฐ€)
    `pcpu_free_slot``pcpu_free_slot`์™„์ „ํžˆ ๋นˆ chunk
    `pcpu_to_depopulate_slot``pcpu_to_depopulate_slot`depopulate ๋Œ€๊ธฐ chunk

    ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํžŒํŠธ ์ข…๋ฅ˜

    ํžŒํŠธํ•„๋“œ์—ญํ• 
    `contig_hint`๋ธ”๋ก/ chunk ์ˆ˜์ค€์ตœ๋Œ€ ์—ฐ์† ๋นˆ ์˜์—ญ ํฌ๊ธฐ
    `scan_hint`๋ธ”๋ก ์ˆ˜์ค€contig_hint ์ด์ „์˜ ๋‘ ๋ฒˆ์งธ๋กœ ํฐ ๋นˆ ์˜์—ญ
    `first_free`๋ธ”๋ก ์ˆ˜์ค€์ฒซ ๋ฒˆ์งธ ๋นˆ ๋น„ํŠธ ์œ„์น˜
    `left_free` / `right_free`๋ธ”๋ก ์ˆ˜์ค€๋ธ”๋ก ์–‘ ๋์˜ ๋นˆ ์˜์—ญ ํฌ๊ธฐ

    Per-CPU ํฌ์ธํ„ฐ ๋ณ€ํ™˜

    /* ์ผ๋ฐ˜ ์ฃผ์†Œ โ†’ Per-CPU ํฌ์ธํ„ฐ */
    addr - pcpu_base_addr + __per_cpu_start
    
    /* Per-CPU ํฌ์ธํ„ฐ โ†’ ์ผ๋ฐ˜ ์ฃผ์†Œ */
    ptr + pcpu_base_addr - __per_cpu_start
  • SMP: ์œ„ ๋ณ€ํ™˜ ์‚ฌ์šฉ
  • UP: ํ•ญ๋“ฑ ๋งคํ•‘ (๋ณ€ํ™˜ ๋ถˆํ•„์š”)

  • ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ

    ์ฒซ ๋ฒˆ์งธ chunk์˜ ๊ตฌ์กฐ:

    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚                    ์ฒซ ๋ฒˆ์งธ chunk (pcpu_first_chunk)          โ”‚
    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ Static ์˜์—ญ  โ”‚ Reserved ์˜์—ญ    โ”‚     Dynamic ์˜์—ญ           โ”‚
    โ”‚ (__per_cpu_  โ”‚ (๋ชจ๋“ˆ percpu)    โ”‚ (๋™์  ํ• ๋‹น ๊ฐ€๋Šฅ)           โ”‚
    โ”‚  start)      โ”‚                  โ”‚                           โ”‚
    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
    โ”‚ ๊ฐ unit์€ CPU์— 1:1 ๋Œ€์‘, NUMA ๊ทธ๋ฃน์œผ๋กœ ๋ถ„๋ฆฌ                โ”‚
    โ”‚                                                             โ”‚
    โ”‚  c0: [u0][u1][u2][u3]  c1: [u0][u1][u2][u3]  ...          โ”‚
    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

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

    Per-CPU ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ๋„
    Per-CPU ํ• ๋‹น๊ธฐ ํ˜ธ์ถœ ํ๋ฆ„

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • SLUB ํ• ๋‹น์ž
  • vmalloc
  • Memblock ํ• ๋‹น์ž