๏ปฟ# 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
๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์
์ฒซ ๋ฒ์งธ 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] ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ