๏ปฟ# SLUB ํ• ๋‹น์ž (SLUB Allocator)

๊ฐœ์š” (Overview)

Linux ์ปค๋„์˜ SLUB(SUnqueued Lockless Allocator) ํ• ๋‹น์ž๋Š” ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ์˜ ํšจ์œจ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น/ํ•ด์ œ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” slab ํ• ๋‹น์ž์ž…๋‹ˆ๋‹ค. SLUB๋Š” ๊ธฐ์กด SLAB ํ• ๋‹น์ž์˜ ๋ณต์žกํ•œ ๊ตฌ์กฐ๋ฅผ ๋‹จ์ˆœํ™”ํ•˜๋ฉด์„œ lockless fastpath๋ฅผ ํ†ตํ•ด ๋†’์€ ์„ฑ๋Šฅ์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ(task_struct, inode, dentry ๋“ฑ)๋Š” SLUB๋ฅผ ํ†ตํ•ด ํ• ๋‹น๋˜๋ฉฐ, kmalloc/kfree๋„ ๋‚ด๋ถ€์ ์œผ๋กœ SLUB๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

SLUB์˜ ํ•ต์‹ฌ ์„ค๊ณ„ ์ฒ ํ•™์€ per-CPU ์บ์‹œ(sheaves)๋ฅผ ํ†ตํ•œ fastpath ์ฒ˜๋ฆฌ์™€ NUMA ๋…ธ๋“œ๋ณ„ partial list๋ฅผ ํ†ตํ•œ slowpath ๋ถ„๋ฆฌ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋Œ€๋ถ€๋ถ„์˜ ํ• ๋‹น/ํ•ด์ œ๊ฐ€ lock ์—†์ด ์ฒ˜๋ฆฌ๋˜์–ด ๋ฉ€ํ‹ฐ์†Œ์ผ“ ์‹œ์Šคํ…œ์—์„œ๋„ ๋†’์€ ํ™•์žฅ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ

mm/slub.c          โ€” SLUB ํ• ๋‹น์ž ํ•ต์‹ฌ ๊ตฌํ˜„ (์•ฝ 9,800์ค„)
mm/slab.h          โ€” ๋‚ด๋ถ€ ๊ตฌ์กฐ์ฒด ์ •์˜ (struct slab, kmem_cache_node ๋“ฑ)
include/linux/slab.h โ€” ์™ธ๋ถ€ API (kmalloc, kfree, kmem_cache_create ๋“ฑ)

SLUB ํ• ๋‹น์ž ๊ฐœ์š”

SLUB๋Š” Buddy Allocator ์œ„์— ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์บ์‹ฑํ•˜๋Š” ๊ณ„์ธต์ž…๋‹ˆ๋‹ค. SLUB๋Š” ๋Œ€ํ˜• ๋ฌผ๋ฅ˜์ฐฝ๊ณ ์˜ ์†Œํ˜• ๋ถ€ํ’ˆ ์„œ๋ž์žฅ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. Buddy Allocator๊ฐ€ ํŽ˜์ด์ง€๋ผ๋Š” ํฐ ์ƒ์ž๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด, SLUB๋Š” ๊ทธ ์ƒ์ž ์•ˆ์„ task_struct, inode, dentry, kmalloc-256 ๊ฐ™์€ ๊ฐ™์€ ํฌ๊ธฐ์˜ ์นธ์œผ๋กœ ๋‚˜๋ˆ  ์ž์ฃผ ์“ฐ๋Š” ๋ถ€ํ’ˆ์„ CPU ๊ฐ€๊นŒ์ด์— ๋ณด๊ด€ํ•ฉ๋‹ˆ๋‹ค. ๋น ๋ฅธ ๊ฒฝ๋กœ์—์„œ๋Š” CPU๋ณ„ sheaf์—์„œ ๋ฐ”๋กœ ๊บผ๋‚ด๊ณ , ๋น„๋ฉด node barn๊ณผ partial slab์„ ๊ฑฐ์ณ ์ƒˆ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.


์‹œ๊ฐ์  ๊ฐœ์š”

SLUB ํ• ๋‹น์ž ๊ตฌ์กฐ๋„

SLUB ๊ตฌ์กฐ๋„

SLUB ํ• ๋‹น/ํ•ด์ œ ํ๋ฆ„๋„

ํ• ๋‹น/ํ•ด์ œ ํ๋ฆ„

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

# SLUB ์บ์‹œ ํ†ต๊ณ„ ํ™•์ธ
cat /proc/slabinfo | head -20

# ํŠน์ • ์บ์‹œ ์ƒ์„ธ ์ •๋ณด
cat /sys/kernel/slab/task_struct/alloc_fastpath
cat /sys/kernel/slab/task_struct/alloc_slowpath

# SLUB ๋””๋ฒ„๊ทธ ๋ชจ๋“œ ํ™•์ธ (๋ถ€ํŒ… ํŒŒ๋ผ๋ฏธํ„ฐ)
cat /proc/cmdline | grep slub_debug

# ๋ฉ”๋ชจ๋ฆฌ์—์„œ SLAB ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ
cat /proc/meminfo | grep -E 'Slab|SReclaimable|SUnreclaim'

# ํŠน์ • ์บ์‹œ์˜ partial slab ์ˆ˜ ํ™•์ธ
cat /sys/kernel/slab/<cache_name>/partial

# SLUB ํ†ต๊ณ„ ๋ฆฌ์…‹ ๋ฐ ํ™•์ธ
echo 1 > /sys/kernel/slab/<cache_name>/alloc_fastpath
echo 0 > /sys/kernel/slab/<cache_name>/alloc_fastpath

# ์บ์‹œ๋ณ„ ์†Œ๋น„๋Ÿ‰ ๊ธฐ์ค€ ์ƒ์œ„ ํ•ญ๋ชฉ
slabtop -s c

# kmalloc ํฌ๊ธฐ ํด๋ž˜์Šค์™€ ์ฃผ์š” ์บ์‹œ ํ™•์ธ
grep -E '^(kmalloc-|task_struct|dentry|inode_cache)' /proc/slabinfo | head -20

# ์บ์‹œ๋ณ„ ๊ฐ์ฒด ํฌ๊ธฐ, slab๋‹น ๊ฐ์ฒด ์ˆ˜, order ํ™•์ธ
cat /sys/kernel/slab/<cache_name>/object_size
cat /sys/kernel/slab/<cache_name>/objs_per_slab
cat /sys/kernel/slab/<cache_name>/order

# SLUB ๋””๋ฒ„๊ทธ ์˜ต์…˜ ํŒŒ์ผ ํ™•์ธ (CONFIG_SLUB_DEBUG ํ•„์š”)
for f in sanity_checks red_zone poison store_user trace failslab; do cat /sys/kernel/slab/<cache_name>/$f 2>/dev/null; done

# ๋ถ€ํŒ… ์˜ต์…˜ ์˜ˆ: ์ „์ฒด sanity/redzone/poison/user tracking ๋˜๋Š” kmalloc-256๋งŒ ์ ์šฉ
# slab_debug=FZPU
# slab_debug=FZPU,kmalloc-256

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

struct kmem_cache โ€” ์Šฌ๋ž˜๋ธŒ ์บ์‹œ ๊ด€๋ฆฌ์ž

struct kmem_cache๋Š” ํ•˜๋‚˜์˜ slab ์บ์‹œ(์˜ˆ: task_struct์šฉ ์บ์‹œ)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ตœ์ƒ์œ„ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. ๊ฐ ์บ์‹œ๋Š” ๊ณ ์ •๋œ ํฌ๊ธฐ์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ• ๋‹นํ•˜๋ฉฐ, per-CPU sheaves์™€ NUMA ๋…ธ๋“œ๋ณ„ partial list๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

/* mm/slab.h:197-251 */
struct kmem_cache {
struct slub_percpu_sheaves __percpu *cpu_sheaves;  // per-CPU ์บ์‹œ
slab_flags_t flags;                                // ์บ์‹œ ํ”Œ๋ž˜๊ทธ
unsigned long min_partial;                         // ์ตœ์†Œ partial slab ์ˆ˜
unsigned int size;                                 // ์˜ค๋ธŒ์ ํŠธ ํฌ๊ธฐ (๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํฌํ•จ)
unsigned int object_size;                          // ์˜ค๋ธŒ์ ํŠธ ํฌ๊ธฐ (๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ฏธํฌํ•จ)
struct reciprocal_value reciprocal_size;           // ์—ญ์ˆ˜ ๋‚˜๋ˆ—์…ˆ์šฉ
unsigned int offset;                               // free pointer ์˜คํ”„์…‹
unsigned int sheaf_capacity;                       // sheaf ์ตœ๋Œ€ ์šฉ๋Ÿ‰
struct kmem_cache_order_objects oo;                // ์ตœ์  order/์˜ค๋ธŒ์ ํŠธ ์ˆ˜
struct kmem_cache_order_objects min;               // ์ตœ์†Œ order/์˜ค๋ธŒ์ ํŠธ ์ˆ˜
gfp_t allocflags;                                 // ํŽ˜์ด์ง€ ํ• ๋‹น ์‹œ GFP ํ”Œ๋ž˜๊ทธ
int refcount;                                     // ์ฐธ์กฐ ์นด์šดํŠธ
void (*ctor)(void *object);                       // ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ์ž
unsigned int inuse;                               // ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊นŒ์ง€์˜ ์˜คํ”„์…‹
unsigned int align;                               // ์ •๋ ฌ
const char *name;                                 // ์บ์‹œ ์ด๋ฆ„
struct list_head list;                            // ์ „์ฒด ์บ์‹œ ๋ฆฌ์ŠคํŠธ
struct kmem_cache_node *node[MAX_NUMNODES];       // NUMA ๋…ธ๋“œ๋ณ„ ๊ด€๋ฆฌ ๊ตฌ์กฐ์ฒด
};

struct slab โ€” ๋‹จ์ผ ์Šฌ๋ž˜๋ธŒ ํŽ˜์ด์ง€

struct slab๋Š” ํ•˜๋‚˜์˜ ์Šฌ๋ž˜๋ธŒ ํŽ˜์ด์ง€(4KB ์ด์ƒ์˜ compound page)๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. struct page์™€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜์—ฌ overhead๋ฅผ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

/* mm/slab.h:74-92 */
struct slab {
memdesc_flags_t flags;                    // ์Šฌ๋ž˜๋ธŒ ํ”Œ๋ž˜๊ทธ
struct kmem_cache *slab_cache;            // ์†Œ์† ์บ์‹œ
union {
struct {
struct list_head slab_list;       // partial/full ๋ฆฌ์ŠคํŠธ
struct freelist_counters;         // freelist + ์นด์šดํ„ฐ (cmpxchg)
};
struct rcu_head rcu_head;             // RCU ํ•ด์ œ์šฉ
};
unsigned int __page_type;                 // ํŽ˜์ด์ง€ ํƒ€์ž…
atomic_t __page_refcount;                 // ์ฐธ์กฐ ์นด์šดํŠธ
unsigned long obj_exts;                   // ์˜ค๋ธŒ์ ํŠธ ํ™•์žฅ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
};

freelist_counters โ€” Lockless freelist ๊ด€๋ฆฌ

SLUB์˜ ํ•ต์‹ฌ ํ˜์‹ ์€ freelist, inuse, objects, frozen์„ ํ•˜๋‚˜์˜ 64๋น„ํŠธ/128๋น„ํŠธ ๊ฐ’์œผ๋กœ ๋ฌถ์–ด cmpxchg๋กœ ์›์ž์  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

/* mm/slab.h:41-71 */
struct freelist_counters {
union {
struct {
void *freelist;           // ๋‹ค์Œ ๋นˆ ์˜ค๋ธŒ์ ํŠธ ํฌ์ธํ„ฐ
union {
unsigned long counters;  // 64๋น„ํŠธ ์นด์šดํ„ฐ
struct {
unsigned inuse:16;   // ์‚ฌ์šฉ ์ค‘์ธ ์˜ค๋ธŒ์ ํŠธ ์ˆ˜
unsigned objects:15; // ์ „์ฒด ์˜ค๋ธŒ์ ํŠธ ์ˆ˜
unsigned frozen:1;   // frozen ์ƒํƒœ (๋””๋ฒ„๊ทธ์šฉ)
};
};
};
freelist_full_t freelist_counters;  // cmpxchg์šฉ 128๋น„ํŠธ ๊ฐ’
};
};

struct kmem_cache_node โ€” NUMA ๋…ธ๋“œ๋ณ„ ๊ด€๋ฆฌ

๊ฐ NUMA ๋…ธ๋“œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ์กด์žฌํ•˜๋ฉฐ, partial slab ๋ฆฌ์ŠคํŠธ์™€ barn์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

/* mm/slab.h:430-440 */
struct kmem_cache_node {
spinlock_t list_lock;              // ๋ฆฌ์ŠคํŠธ ๋ณดํ˜ธ์šฉ ์Šคํ•€๋ฝ
unsigned long nr_partial;          // partial slab ์ˆ˜
struct list_head partial;          // partial slab ๋ฆฌ์ŠคํŠธ
atomic_long_t nr_slabs;            // ์ „์ฒด slab ์ˆ˜ (๋””๋ฒ„๊ทธ์šฉ)
atomic_long_t total_objects;       // ์ „์ฒด ์˜ค๋ธŒ์ ํŠธ ์ˆ˜ (๋””๋ฒ„๊ทธ์šฉ)
struct list_head full;             // full slab ๋ฆฌ์ŠคํŠธ (๋””๋ฒ„๊ทธ์šฉ)
struct node_barn *barn;            // barn ๊ด€๋ฆฌ ๊ตฌ์กฐ์ฒด
};

struct slub_percpu_sheaves โ€” per-CPU ์บ์‹œ

SLUB์˜ fastpath๋ฅผ ๋‹ด๋‹นํ•˜๋Š” per-CPU ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. ๊ฐ CPU๋Š” ๋‘ ๊ฐœ์˜ sheaf(main, spare)๋ฅผ ๊ฐ€์ง€๋ฉฐ, ํ• ๋‹น/ํ•ด์ œ์˜ ๋Œ€๋ถ€๋ถ„์ด ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

/* mm/slub.c:420-425 */
struct slub_percpu_sheaves {
local_trylock_t lock;              // per-CPU ์ž ๊ธˆ
struct slab_sheaf *main;           // ๋ฉ”์ธ sheaf (ํ• ๋‹น/ํ•ด์ œ ์ฃผ๋ ฅ)
struct slab_sheaf *spare;          // ์ŠคํŽ˜์–ด sheaf (main ๊ต์ฒด์šฉ)
struct slab_sheaf *rcu_free;       // kfree_rcu ๋ฐฐ์น˜์šฉ
};

struct slab_sheaf โ€” ์˜ค๋ธŒ์ ํŠธ ์บ์‹œ ๋‹จ์œ„

์‹ค์ œ ์˜ค๋ธŒ์ ํŠธ ํฌ์ธํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. main sheaf์—์„œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ• ๋‹นํ•˜๊ณ , ํ•ด์ œ ์‹œ์—๋„ main sheaf์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

/* mm/slub.c:404-418 */
struct slab_sheaf {
union {
struct rcu_head rcu_head;
struct list_head barn_list;       // barn ๋ฆฌ์ŠคํŠธ ์—ฐ๊ฒฐ
struct {
unsigned int capacity;        // ์ตœ๋Œ€ ์šฉ๋Ÿ‰
bool pfmemalloc;              // PF_MEMALLOC ํ• ๋‹น ์—ฌ๋ถ€
};
};
struct kmem_cache *cache;             // ์†Œ์† ์บ์‹œ
unsigned int size;                    // ํ˜„์žฌ ์ €์žฅ๋œ ์˜ค๋ธŒ์ ํŠธ ์ˆ˜
int node;                             // NUMA ๋…ธ๋“œ (rcu_sheaf์šฉ)
void *objects[];                      // ์˜ค๋ธŒ์ ํŠธ ํฌ์ธํ„ฐ ๋ฐฐ์—ด
};

struct node_barn โ€” NUMA ๋…ธ๋“œ๋ณ„ barn

barn(barnyard)์€ NUMA ๋…ธ๋“œ์—์„œ empty/full sheaf๋ฅผ ๊ตํ™˜ํ•˜๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค. CPU ๊ฐ„ sheaf ๊ตํ™˜์˜ ์ค‘๊ฐ„ ๋‹ค๋ฆฌ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

/* mm/slub.c:396-402 */
struct node_barn {
spinlock_t lock;                   // barn ๋ณดํ˜ธ์šฉ ์Šคํ•€๋ฝ
struct list_head sheaves_full;     // ๊ฐ€๋“ ์ฐฌ sheaf ๋ฆฌ์ŠคํŠธ
struct list_head sheaves_empty;    // ๋นˆ sheaf ๋ฆฌ์ŠคํŠธ
unsigned int nr_full;              // full sheaf ์ˆ˜
unsigned int nr_empty;             // empty sheaf ์ˆ˜
};

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

kmem_cache_alloc_noprof() โ€” ๋ฉ”์ธ ํ• ๋‹น API

/* mm/slub.c:4871-4880 */
void *kmem_cache_alloc_noprof(struct kmem_cache *s, gfp_t gfpflags)
{
void *ret = slab_alloc_node(s, NULL, gfpflags, NUMA_NO_NODE, _RET_IP_,
s->object_size);
trace_kmem_cache_alloc(_RET_IP_, ret, s, gfpflags, NUMA_NO_NODE);
return ret;
}

์—ญํ• : ์‚ฌ์šฉ์ž๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ์ฃผ์š” ํ• ๋‹น API์ž…๋‹ˆ๋‹ค. slab_alloc_node()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‹ค์ œ ํ• ๋‹น์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

ํ๋ฆ„: kmem_cache_alloc โ†’ slab_alloc_node โ†’ fastpath(per-CPU sheaves) ๋˜๋Š” slowpath(___slab_alloc)

slab_alloc_node() โ€” fastpath/slowpath ๋ถ„๊ธฐ

/* mm/slub.c:4750-4868 (์š”์•ฝ) */
static __always_inline void *slab_alloc_node(struct kmem_cache *s,
struct list_lru *lru, gfp_t gfpflags, int node, unsigned long addr,
size_t orig_size)
{
struct slub_percpu_sheaves *pcs;
void *object;

/* 1๋‹จ๊ณ„: pre-allocation hook (KFENCE, KASAN ๋“ฑ) */
s = slab_pre_alloc_hook(s, gfpflags);
if (!s) return NULL;

/* 2๋‹จ๊ณ„: per-CPU sheaves์—์„œ ํ• ๋‹น ์‹œ๋„ (fastpath) */
pcs = this_cpu_ptr(s->cpu_sheaves);
local_lock(&pcs->lock);

if (pcs->main->size > 0) {
/* main sheaf์—์„œ ์˜ค๋ธŒ์ ํŠธ ๋ฐ˜ํ™˜ */
object = pcs->main->objects[pcs->main->size - 1];
pcs->main->size--;
goto success;
}

/* 3๋‹จ๊ณ„: main์ด ๋น„์–ด์žˆ์œผ๋ฉด spare์™€ ๊ต์ฒด ๋˜๋Š” slowpath */
object = ___slab_alloc(s, gfpflags, node, addr, orig_size);

success:
slab_post_alloc_hook(s, lru, gfpflags, 1, &object, ...);
return object;
}

fastpath ์กฐ๊ฑด: main sheaf์— ๋นˆ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ์„ ๋•Œ (lock 1ํšŒ๋กœ ํ• ๋‹น ์™„๋ฃŒ)

___slab_alloc() โ€” slowpath ์ฒ˜๋ฆฌ

/* mm/slub.c:4374-4451 */
static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
unsigned long addr, unsigned int orig_size)
{
struct partial_context pc;
void *object;
struct slab *slab;

/* 1๋‹จ๊ณ„: partial list์—์„œ ํ• ๋‹น ์‹œ๋„ */
pc.flags = gfpflags;
object = get_from_partial(s, node, &pc);
if (object) goto success;

/* 2๋‹จ๊ณ„: ์ƒˆ slab ํ• ๋‹น */
slab = new_slab(s, pc.flags, node);
if (!slab) {
slab_out_of_memory(s, gfpflags, node);
return NULL;
}

/* 3๋‹จ๊ณ„: ์ƒˆ slab์—์„œ ์˜ค๋ธŒ์ ํŠธ ํ• ๋‹น */
object = alloc_from_new_slab(s, slab, &object, 1, allow_spin);

success:
if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
set_track(s, object, TRACK_ALLOC, addr, gfpflags);
return object;
}

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

1. get_from_partial() ์„ฑ๊ณต โ†’ partial slab์—์„œ ํ• ๋‹น

2. new_slab() โ†’ ํŽ˜์ด์ง€ ํ• ๋‹น์ž์—์„œ ์ƒˆ slab ํŽ˜์ด์ง€ ํ• ๋‹น

3. alloc_from_new_slab() โ†’ ์ƒˆ slab์—์„œ ์˜ค๋ธŒ์ ํŠธ ํ• ๋‹น ํ›„ sheaf์— ์ฑ„์šฐ๊ธฐ

__slab_free() โ€” slowpath ํ•ด์ œ

/* mm/slub.c:5470-5574 */
static void __slab_free(struct kmem_cache *s, struct slab *slab,
void *head, void *tail, int cnt, unsigned long addr)
{
struct freelist_counters old, new;
struct kmem_cache_node *n = NULL;
bool was_full;

do {
old.freelist = slab->freelist;
old.counters = slab->counters;
was_full = (old.freelist == NULL);

/* freelist์— ์˜ค๋ธŒ์ ํŠธ ์ถ”๊ฐ€ */
set_freepointer(s, tail, old.freelist);
new.freelist = head;
new.counters = old.counters;
new.inuse -= cnt;

/* slab์ด ๋น„์–ด์žˆ๊ฑฐ๋‚˜ full์—์„œ ๋ณ€ํ™˜๋  ๋•Œ list_lock ํ•„์š” */
if (!new.inuse || was_full) {
n = get_node(s, slab_nid(slab));
spin_lock_irqsave(&n->list_lock, flags);
}
} while (!slab_update_freelist(s, slab, &old, &new, "__slab_free"));

/* list_lock ํš๋“ ํ›„ ์ฒ˜๋ฆฌ */
if (!was_full && !on_node_partial) return;  // ์ด๋ฏธ partial์— ์žˆ์Œ

if (unlikely(!new.inuse && n->nr_partial >= s->min_partial))
goto slab_empty;  // slab์ด ๋น„์—ˆ๊ณ  partial์ด ์ถฉ๋ถ„ํžˆ ๋งŽ์Œ

if (unlikely(was_full)) {
add_partial(n, slab, ADD_TO_TAIL);  // full โ†’ partial ์ „ํ™˜
}

return;

slab_empty:
remove_partial(n, slab);  // partial์—์„œ ์ œ๊ฑฐ
discard_slab(s, slab);    // ํŽ˜์ด์ง€ ํ• ๋‹น์ž์— ๋ฐ˜ํ™˜
}

allocate_slab() โ€” ์ƒˆ slab ํŽ˜์ด์ง€ ํ• ๋‹น

/* mm/slub.c:3455-3530 */
static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
{
struct kmem_cache_order_objects oo = s->oo;
gfp_t alloc_gfp;
struct slab *slab;

/* ๋†’์€ order ํ• ๋‹น ์‹คํŒจ ์‹œ min order๋กœ ํด๋ฐฑ */
alloc_gfp = (flags | __GFP_NOWARN | __GFP_NORETRY) & ~__GFP_NOFAIL;
slab = alloc_slab_page(alloc_gfp, node, oo, allow_spin);
if (unlikely(!slab)) {
oo = s->min;
slab = alloc_slab_page(alloc_gfp, node, oo, allow_spin);
if (unlikely(!slab)) return NULL;
}

/* slab ์ดˆ๊ธฐํ™” */
slab->objects = oo_objects(oo);
slab->inuse = 0;
slab->frozen = 0;
slab->slab_cache = s;

/* freelist ์ดˆ๊ธฐํ™” */
kasan_poison_slab(slab);
setup_slab_debug(s, slab, start);
alloc_slab_obj_exts_early(s, slab);

/* freelist ์—ฐ๊ฒฐ (๋˜๋Š” shuffle) */
shuffle = shuffle_freelist(s, slab, allow_spin);
if (!shuffle) {
start = fixup_red_left(s, start);
slab->freelist = start;
for (idx = 0; idx < slab->objects - 1; idx++) {
next = p + s->size;
set_freepointer(s, p, next);
p = next;
}
}
return slab;
}

SLAB / SLUB / SLOB ๋น„๊ต

ํ•ญ๋ชฉSLABSLUBSLOB
์„ค๊ณ„ ๋ฐฉํ–ฅmagazine/per-CPU array ์ค‘์‹ฌ์˜ ์ „ํ†ต ๊ตฌํ˜„๋‹จ์ˆœํ•œ per-CPU fastpath์™€ slab metadata ํ†ตํ•ฉ์ž‘์€ ์‹œ์Šคํ…œ์šฉ ๋‹จ์ˆœ first-fit ๊ณ„์—ด
Linux 7.0 ๊ด€์ ๊ณผ๊ฑฐ ๋น„๊ต ๋ฐฐ๊ฒฝ๊ธฐ๋ณธ slab allocator ๊ตฌํ˜„(`mm/slub.c`)legacy ๋น„๊ต ๋ฐฐ๊ฒฝ
์ž‘์€ ๊ฐ์ฒด ์ฒ˜๋ฆฌcache๋ณ„ ๊ฐ์ฒด ์žฌ์‚ฌ์šฉ`kmalloc-*` cache์™€ `kmem_cache_create()` cache ์žฌ์‚ฌ์šฉ์ž‘์€ footprint ์šฐ์„ 
NUMA์ง€์›node partial list, barn, mempolicy ์—ฐ๋™์ œํ•œ์ 
๋””๋ฒ„๊น…์ง€์›`slab_debug`, red zone, poison, user tracking, sysfs ํ†ต๊ณ„์ œํ•œ์ 

kmalloc ํฌ๊ธฐ ๋ฐ˜์˜ฌ๋ฆผ๊ณผ ๋‚ด๋ถ€ ๋‹จํŽธํ™”

์š”์ฒญ ํฌ๊ธฐ๋Œ€ํ‘œ cache index๊ฒฐ๊ณผ์˜๋ฏธ
1-8B`kmalloc-8`์ตœ์†Œ ํฌ๊ธฐ cache ์‚ฌ์šฉ๋„ˆ๋ฌด ์ž‘์€ ๊ฐ์ฒด๋„ ์ตœ์†Œ align ๋น„์šฉ ๋ฐœ์ƒ
65-96B`kmalloc-96`128B๋ณด๋‹ค ์ž‘์€ ํŠน์ˆ˜ bucket๋‚ด๋ถ€ ๋‹จํŽธํ™” ๊ฐ์†Œ ๋ชฉ์ 
129-192B`kmalloc-192`256B๋ณด๋‹ค ์ž‘์€ ํŠน์ˆ˜ bucket์ค‘๊ฐ„ ํฌ๊ธฐ ๋‚ญ๋น„ ์™„ํ™”
193-256B`kmalloc-256`256B bucket๋‚˜๋จธ์ง€ ๊ณต๊ฐ„์€ ๋‚ด๋ถ€ ๋‹จํŽธํ™”
4KB ์ดˆ๊ณผ, 8KB ์ดํ•˜`kmalloc-8k`order-1๊นŒ์ง€ SLUB cache๊ทธ๋ณด๋‹ค ํฐ ์š”์ฒญ์€ page allocator ์„ฑ๊ฒฉ ๊ฐ•ํ™”

kmem_cache_create()๋Š” ๊ฐ™์€ ํฌ๊ธฐ์˜ ๊ฐ์ฒด๋ฅผ ์ž์ฃผ ๋งŒ๋“ค ๋•Œ ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ctor, SLAB_NO_MERGE, debug flag, usercopy region์ด ์žˆ์œผ๋ฉด merge๊ฐ€ ๋ง‰ํžˆ๋ฏ€๋กœ /sys/kernel/slab/์—์„œ alias์™€ debug ํŒŒ์ผ์„ ํ•จ๊ป˜ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


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

ํ• ๋‹น ํ๋ฆ„ (Allocation Flow)

kmem_cache_alloc(s, gfp)
โ”‚
โ”œโ”€โ†’ slab_alloc_node()
โ”‚     โ”‚
โ”‚     โ”œโ”€โ†’ [FASTPATH] per-CPU main sheaf์—์„œ ํ• ๋‹น
โ”‚     โ”‚     โ””โ”€ local_trylock(cpu_sheaves) โ†’ main->objects[--size] โ†’ unlock
โ”‚     โ”‚
โ”‚     โ””โ”€โ†’ [SLOWPATH] ___slab_alloc()
โ”‚           โ”‚
โ”‚           โ”œโ”€โ†’ get_from_partial()
โ”‚           โ”‚     โ”œโ”€โ†’ get_from_partial_node()  โ† partial list ํƒ์ƒ‰
โ”‚           โ”‚     โ””โ”€โ†’ get_node()๋กœ NUMA ๋…ธ๋“œ ๊ด€๋ฆฌ
โ”‚           โ”‚
โ”‚           โ”œโ”€โ†’ new_slab() โ†’ allocate_slab()
โ”‚           โ”‚     โ””โ”€โ†’ alloc_slab_page()  โ† ํŽ˜์ด์ง€ ํ• ๋‹น์ž(Buddy) ํ˜ธ์ถœ
โ”‚           โ”‚
โ”‚           โ””โ”€โ†’ alloc_from_new_slab()  โ† ์ƒˆ slab์—์„œ sheaf ์ฑ„์šฐ๊ธฐ
โ”‚
โ””โ”€โ†’ slab_post_alloc_hook()  โ† KFENCE, memcg ๋“ฑ ํ›„์ฒ˜๋ฆฌ

ํ•ด์ œ ํ๋ฆ„ (Free Flow)

kmem_cache_free(s, obj)
โ”‚
โ”œโ”€โ†’ slab_free()
โ”‚     โ”‚
โ”‚     โ”œโ”€โ†’ [FASTPATH] per-CPU main sheaf์— ์ถ”๊ฐ€
โ”‚     โ”‚     โ””โ”€ free_to_pcs() โ†’ per-CPU main/spare sheaf ๋˜๋Š” barn ๊ตํ™˜
โ”‚     โ”‚
โ”‚     โ””โ”€โ†’ [SLOWPATH] __slab_free()
โ”‚           โ”‚
โ”‚           โ”œโ”€โ†’ cmpxchg๋กœ freelist ์›์ž์  ์—…๋ฐ์ดํŠธ
โ”‚           โ”‚
โ”‚           โ”œโ”€โ†’ [fullโ†’partial] add_partial()  โ† node->list_lock ํš๋“
โ”‚           โ”‚
โ”‚           โ””โ”€โ†’ [slab empty] remove_partial() โ†’ discard_slab()
โ”‚                 โ””โ”€โ†’ free_slab() โ†’ __free_slab()  โ† ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜
โ”‚
โ””โ”€โ†’ memcg ๋“ฑ ํ›„์ฒ˜๋ฆฌ

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

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

์กฐ๊ฑดfastpathslowpath๊ฒฐ๊ณผ
main sheaf์— ์˜ค๋ธŒ์ ํŠธ ์กด์žฌlock 1ํšŒ๋กœ ํ• ๋‹น-**์ตœ๊ณ  ์„ฑ๋Šฅ**
main ๋น„์–ด์žˆ์Œ, barn์— empty sheaf-sheaf ๊ต์ฒด ํ›„ ํ• ๋‹น์ค‘๊ฐ„ ์„ฑ๋Šฅ
barn์—๋„ empty ์—†์Œ-์ƒˆ slab ํ• ๋‹น๋А๋ฆผ (Buddy ํ˜ธ์ถœ)
๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ-slab_out_of_memory()**ํ• ๋‹น ์‹คํŒจ**

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

์กฐ๊ฑดfastpathslowpath๊ฒฐ๊ณผ
main sheaf์— ์—ฌ์œ  ๊ณต๊ฐ„lock 1ํšŒ๋กœ ํ•ด์ œ-**์ตœ๊ณ  ์„ฑ๋Šฅ**
main ๊ฐ€๋“์ฐธ, spare ์‚ฌ์šฉ-spare/main ๊ต์ฒด ํ›„ ํ•ด์ œ์ค‘๊ฐ„ ์„ฑ๋Šฅ
remote node ์˜ค๋ธŒ์ ํŠธ-barn ๋˜๋Š” direct free์›๊ฒฉ ๋…ธ๋“œ ๋น„์šฉ ๋ฐœ์ƒ
slab ์™„์ „ ํ•ด์ œ-remove_partial โ†’ discard_slabํŽ˜์ด์ง€ ๋ฐ˜ํ™˜

NUMA ์ •์ฑ…๋ณ„ ์ฐจ์ด

NUMA ์ •์ฑ…ํ• ๋‹น ๋…ธ๋“œ ๊ฒฐ์ •remote ๋น„์œจ์„ฑ๋Šฅ ์˜ํ–ฅ
defaultlocal node ์šฐ์„ ๋‚ฎ์Œ**์ตœ์ **
strict_numalocal node๋งŒ0%์›๊ฒฉ ์‹คํŒจ ์‹œ ํด๋ฐฑ ์—†์Œ
mempolicy(BIND)์ง€์ • ๋…ธ๋“œ0%์ง€์ • ๋…ธ๋“œ ๋ถ€์กฑ ์‹œ ์‹คํŒจ
mempolicy(INTERLEAVE)์ˆœ์ฐจ ๋ถ„์‚ฐ๋†’์Œ๊ท ๋“ฑ ๋ถ„๋ฐฐ, locality ๊ฐ์†Œ

Linux 7.0 ์›๋ฌธ ์ƒ์„ธ

kmalloc ํฌ๊ธฐ ํด๋ž˜์Šค์™€ ์ตœ๋Œ€ ์บ์‹œ ํฌ๊ธฐ

kmalloc()์€ ์š”์ฒญ ํฌ๊ธฐ๋ฅผ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด slab cache index๋กœ ๋ฐ˜์˜ฌ๋ฆผํ•ฉ๋‹ˆ๋‹ค. Linux 7.0 SLUB๋Š” order-1 ํŽ˜์ด์ง€๊นŒ์ง€๋Š” slab cache๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ณ , ๊ทธ๋ณด๋‹ค ํฐ ์š”์ฒญ์€ page allocator๋กœ ๋„˜๊น๋‹ˆ๋‹ค.

/* include/linux/slab.h:580-593 */
/*
* SLUB directly allocates requests fitting in to an order-1 page
* (PAGE_SIZE*2).  Larger requests are passed to the page allocator.
*/
#define KMALLOC_SHIFT_HIGH	(PAGE_SHIFT + 1)
#define KMALLOC_SHIFT_MAX	(MAX_PAGE_ORDER + PAGE_SHIFT)
#ifndef KMALLOC_SHIFT_LOW
#define KMALLOC_SHIFT_LOW	3
#endif

/* ์ตœ๋Œ€ ํ• ๋‹น ๊ฐ€๋Šฅ ํฌ๊ธฐ */
#define KMALLOC_MAX_SIZE	(1UL << KMALLOC_SHIFT_MAX)
/* ์‹ค์ œ๋กœ slab ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ตœ๋Œ€ ํฌ๊ธฐ */
#define KMALLOC_MAX_CACHE_SIZE	(1UL << KMALLOC_SHIFT_HIGH)
/* include/linux/slab.h:710-733 */
static __always_inline unsigned int __kmalloc_index(size_t size,
						    bool size_is_constant)
{
	if (!size)
		return 0;

	if (size <= KMALLOC_MIN_SIZE)
		return KMALLOC_SHIFT_LOW;

	if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96)
		return 1;
	if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192)
		return 2;
	if (size <=          8) return 3;
	if (size <=         16) return 4;
	if (size <=         32) return 5;
	if (size <=         64) return 6;
	if (size <=        128) return 7;
	if (size <=        256) return 8;
	if (size <=        512) return 9;
	if (size <=       1024) return 10;
	if (size <=   2 * 1024) return 11;
	if (size <=   4 * 1024) return 12;
	if (size <=   8 * 1024) return 13;
/* mm/slab_common.c:861-883 */
const struct kmalloc_info_struct kmalloc_info[] __initconst = {
	INIT_KMALLOC_INFO(0, 0),
	INIT_KMALLOC_INFO(96, 96),
	INIT_KMALLOC_INFO(192, 192),
	INIT_KMALLOC_INFO(8, 8),
	INIT_KMALLOC_INFO(16, 16),
	INIT_KMALLOC_INFO(32, 32),
	INIT_KMALLOC_INFO(64, 64),
	INIT_KMALLOC_INFO(128, 128),
	INIT_KMALLOC_INFO(256, 256),
	INIT_KMALLOC_INFO(512, 512),
	INIT_KMALLOC_INFO(1024, 1k),
	INIT_KMALLOC_INFO(2048, 2k),
	INIT_KMALLOC_INFO(4096, 4k),
	INIT_KMALLOC_INFO(8192, 8k),
	INIT_KMALLOC_INFO(16384, 16k),
	INIT_KMALLOC_INFO(32768, 32k),
	INIT_KMALLOC_INFO(65536, 64k),
	INIT_KMALLOC_INFO(131072, 128k),
	INIT_KMALLOC_INFO(262144, 256k),
	INIT_KMALLOC_INFO(524288, 512k),
	INIT_KMALLOC_INFO(1048576, 1M),
	INIT_KMALLOC_INFO(2097152, 2M)
};

์บ์‹œ ๋ณ‘ํ•ฉ ์กฐ๊ฑด

minzkn์ด ์–ธ๊ธ‰ํ•œ cache merging์€ slab_nomerge, ๋””๋ฒ„๊ทธ/RCU/usercopy/ctor ์กฐ๊ฑด, alignment์™€ size compatibility์— ์˜ํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

/* mm/slab_common.c:155-174 */
int slab_unmergeable(struct kmem_cache *s)
{
	if (slab_nomerge || (s->flags & SLAB_NEVER_MERGE))
		return 1;

	if (s->ctor)
		return 1;

#ifdef CONFIG_HARDENED_USERCOPY
	if (s->usersize)
		return 1;
#endif

	/*
	 * We may have set a slab to be unmergeable during bootstrap.
	 */
	if (s->refcount < 0)
		return 1;

	return 0;
}
/* mm/slab_common.c:194-228 */
static struct kmem_cache *find_mergeable(unsigned int size, slab_flags_t flags,
		const char *name, struct kmem_cache_args *args)
{
	struct kmem_cache *s;
	unsigned int align;

	flags = kmem_cache_flags(flags, name);
	if (slab_args_unmergeable(args, flags))
		return NULL;

	size = ALIGN(size, sizeof(void *));
	align = calculate_alignment(flags, args->align, size);
	size = ALIGN(size, align);

	list_for_each_entry_reverse(s, &slab_caches, list) {
		if (slab_unmergeable(s))
			continue;

		if (size > s->size)
			continue;

		if ((flags & SLAB_MERGE_SAME) != (s->flags & SLAB_MERGE_SAME))
			continue;
		/*
		 * Check if alignment is compatible.
		 * Courtesy of Adrian Drzewiecki
		 */
		if ((s->size & ~(align - 1)) != s->size)
			continue;

		if (s->size - size >= sizeof(void *))
			continue;

		return s;
	}

Linux 7.0 fastpath ํ• ๋‹น ์›๋ฌธ

๋ฌธ์„œ ์•ž์ชฝ์˜ slab_alloc_node() ์š”์•ฝ์€ ์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•œ ํ๋ฆ„์ž…๋‹ˆ๋‹ค. ์‹ค์ œ Linux 7.0 fastpath๋Š” alloc_from_pcs()์—์„œ main->objects[main->size - 1]๋ฅผ ์ง์ ‘ ๊บผ๋‚ด๊ณ , ๋น„์–ด ์žˆ์œผ๋ฉด __pcs_replace_empty_main()์œผ๋กœ spare/barn ๊ต์ฒด๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

/* mm/slub.c:4711-4743 */
	if (!local_trylock(&s->cpu_sheaves->lock))
		return NULL;

	pcs = this_cpu_ptr(s->cpu_sheaves);

	if (unlikely(pcs->main->size == 0)) {
		pcs = __pcs_replace_empty_main(s, pcs, gfp);
		if (unlikely(!pcs))
			return NULL;
	}

	object = pcs->main->objects[pcs->main->size - 1];

	if (unlikely(node_requested)) {
		/*
		 * Verify that the object was from the node we want. This could
		 * be false because of cpu migration during an unlocked part of
		 * the current allocation or previous freeing process.
		 */
		if (page_to_nid(virt_to_page(object)) != node) {
			local_unlock(&s->cpu_sheaves->lock);
			stat(s, ALLOC_NODE_MISMATCH);
			return NULL;
		}
	}

	pcs->main->size--;

	local_unlock(&s->cpu_sheaves->lock);

	stat(s, ALLOC_FASTPATH);

	return object;
/* mm/slub.c:4837-4868 */
static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list_lru *lru,
		gfp_t gfpflags, int node, unsigned long addr, size_t orig_size)
{
	void *object;
	bool init = false;

	s = slab_pre_alloc_hook(s, gfpflags);
	if (unlikely(!s))
		return NULL;

	object = kfence_alloc(s, orig_size, gfpflags);
	if (unlikely(object))
		goto out;

	object = alloc_from_pcs(s, gfpflags, node);

	if (!object)
		object = __slab_alloc_node(s, gfpflags, node, addr, orig_size);

	maybe_wipe_obj_freeptr(s, object);
	init = slab_want_init_on_alloc(gfpflags, s);

out:
	/*
	 * When init equals 'true', like for kzalloc() family, only
	 * @orig_size bytes might be zeroed instead of s->object_size
	 * In case this fails due to memcg_slab_post_alloc_hook(),
	 * object is set to NULL
	 */
	slab_post_alloc_hook(s, lru, gfpflags, 1, &object, init, orig_size);

	return object;
}

Linux 7.0 slowpath ํ• ๋‹น ์›๋ฌธ

___slab_alloc()์€ preferred NUMA node๊ฐ€ ์ง€์ •๋์ง€๋งŒ __GFP_THISNODE๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋จผ์ € target node๋ฅผ opportunisticํ•˜๊ฒŒ ์‹œ๋„ํ•˜๊ณ , ์‹คํŒจํ•˜๋ฉด ์›๋ž˜ gfpflags๋กœ ๋‹ค๋ฅธ node partial list๊นŒ์ง€ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

/* mm/slub.c:4383-4450 */
	stat(s, ALLOC_SLOWPATH);

new_objects:

	pc.flags = gfpflags;
	/*
	 * When a preferred node is indicated but no __GFP_THISNODE
	 *
	 * 1) try to get a partial slab from target node only by having
	 *    __GFP_THISNODE in pc.flags for get_from_partial()
	 * 2) if 1) failed, try to allocate a new slab from target node with
	 *    GPF_NOWAIT | __GFP_THISNODE opportunistically
	 * 3) if 2) failed, retry with original gfpflags which will allow
	 *    get_from_partial() try partial lists of other nodes before
	 *    potentially allocating new page from other nodes
	 */
	if (unlikely(node != NUMA_NO_NODE && !(gfpflags & __GFP_THISNODE)
		     && try_thisnode)) {
		if (unlikely(!allow_spin))
			/* ๋” ์ œํ•œ์ ์ธ ๋ชจ๋“œ์—์„œ NOWAIT๋กœ gfp๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜์ง€ ์•Š์Œ */
			pc.flags = gfpflags | __GFP_THISNODE;
		else
			pc.flags = GFP_NOWAIT | __GFP_THISNODE;
	}

	pc.orig_size = orig_size;
	object = get_from_partial(s, node, &pc);
	if (object)
		goto success;

	slab = new_slab(s, pc.flags, node);

	if (unlikely(!slab)) {
		if (node != NUMA_NO_NODE && !(gfpflags & __GFP_THISNODE)
		    && try_thisnode) {
			try_thisnode = false;
			goto new_objects;
		}
		slab_out_of_memory(s, gfpflags, node);
		return NULL;
	}

	stat(s, ALLOC_SLAB);

	if (IS_ENABLED(CONFIG_SLUB_TINY) || kmem_cache_debug(s)) {
		object = alloc_single_from_new_slab(s, slab, orig_size, gfpflags);

		if (likely(object))
			goto success;
	} else {
		alloc_from_new_slab(s, slab, &object, 1, allow_spin);

		/* we don't need to check SLAB_STORE_USER here */
		if (likely(object))
			return object;
	}

	if (allow_spin)
		goto new_objects;

	/* ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Œ. ๋Œ€์‹  ์‹คํŒจ ๋ฐ˜ํ™˜ */
	return NULL;

success:
	if (kmem_cache_debug_flags(s, SLAB_STORE_USER))
		set_track(s, object, TRACK_ALLOC, addr, gfpflags);

	return object;

Linux 7.0 ํ•ด์ œ fastpath ์›๋ฌธ

kmem_cache_free()๋Š” virt_to_slab()๋กœ slab์„ ์ฐพ์€ ๋’ค slab_free()๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ๊ฐ™์€ NUMA node์ด๊ณ  pfmemalloc slab์ด ์•„๋‹ˆ๋ฉด free_to_pcs()๊ฐ€ per-CPU sheaf์— ๋˜๋Œ๋ฆฌ๋Š” fastpath์ž…๋‹ˆ๋‹ค.

/* mm/slub.c:6158-6175 */
static __fastpath_inline
void slab_free(struct kmem_cache *s, struct slab *slab, void *object,
	       unsigned long addr)
{
	memcg_slab_free_hook(s, slab, &object, 1);
	alloc_tagging_slab_free_hook(s, slab, &object, 1);

	if (unlikely(!slab_free_hook(s, object, slab_want_init_on_free(s), false)))
		return;

	if (likely(!IS_ENABLED(CONFIG_NUMA) || slab_nid(slab) == numa_mem_id())
	    && likely(!slab_test_pfmemalloc(slab))) {
		if (likely(free_to_pcs(s, object, true)))
			return;
	}

	__slab_free(s, slab, object, object, 1, addr);
	stat(s, FREE_SLOWPATH);
}

kmem_cache_node ์‹ค์ œ ์œ„์น˜ ์ •์ •

struct kmem_cache_node๋Š” Linux 7.0์—์„œ mm/slub.c์— ์ •์˜๋ฉ๋‹ˆ๋‹ค. NUMA node๋ณ„ partial/full list์™€ barn ํฌ์ธํ„ฐ๊ฐ€ ์—ฌ๊ธฐ์„œ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

/* mm/slub.c:430-440 */
struct kmem_cache_node {
	spinlock_t list_lock;
	unsigned long nr_partial;
	struct list_head partial;
#ifdef CONFIG_SLUB_DEBUG
	atomic_long_t nr_slabs;
	atomic_long_t total_objects;
	struct list_head full;
#endif
	struct node_barn *barn;
};

slab_debug ์˜ต์…˜ ๋งคํ•‘

์ปค๋„ ๋ฌธ์„œ Documentation/admin-guide/mm/slab.rst ๊ธฐ์ค€์œผ๋กœ slab_debug=FZPU๋Š” sanity check, red zone, poison, alloc/free user tracking์„ ์ผญ๋‹ˆ๋‹ค.

์˜ต์…˜sysfs ํŒŒ์ผ์˜๋ฏธ์ฃผ์˜์ 
`F``sanity_checks`๊ฐ์ฒด/ํ”„๋ฆฌ๋ฆฌ์ŠคํŠธ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ์ตœ์†Œ ๋””๋ฒ„๊น…์œผ๋กœ๋„ ์„ฑ๋Šฅ ์˜ํ–ฅ ๊ฐ€๋Šฅ
`Z``red_zone`๊ฐ์ฒด ์–‘๋ red zone ๊ฒ€์‚ฌ๊ฐ์ฒด ๋ฐฐ์น˜๊ฐ€ ๋ฐ”๋€Œ์–ด order ์ฆ๊ฐ€ ๊ฐ€๋Šฅ
`P``poison`ํ•ด์ œ ๊ฐ์ฒด์™€ padding poisoninguse-after-free ํƒ์ง€์— ์œ ์šฉ
`U``store_user`alloc/free ํ˜ธ์ถœ์ž ์ถ”์ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ฆ๊ฐ€, ๋””๋ฒ„๊ทธ ๋นŒ๋“œ ํ•„์š”
`T``trace`๋‹จ์ผ cache trace์ž˜๋ชป ์ผœ๋ฉด ๋กœ๊ทธ ํญ์ฆ ๊ฐ€๋Šฅ
`A``failslab`failslab fault injection ๋Œ€์ƒ๋Ÿฐํƒ€์ž„์— 0/1 ์“ฐ๊ธฐ ๊ฐ€๋Šฅ

ํ• ๋‹น์ž ๋น„๊ต

๊ด€๋ จ ๋ฌธ์„œ

  • 00-overview.html โ€” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” (Buddy Allocator ์œ„์˜ slab ๊ณ„์ธต ์œ„์น˜)
  • 01-page_alloc.html โ€” Buddy Allocator (allocate_slab์—์„œ alloc_slab_page ํ˜ธ์ถœ)
  • 03-vma_mmap.html โ€” VMA / mmap (kmalloc ์‚ฌ์šฉ)
  • 05-page_reclaim.html โ€” ํŽ˜์ด์ง€ ํšŒ์ˆ˜ (slab shrinker ์—ฐ๋™)
  • 14-memcontrol.html โ€” Memory Cgroup (memcg slab ํ• ๋‹น ๊ณ„์ •)
  • 27-debug.html โ€” ๋””๋ฒ„๊ทธ ๋„๊ตฌ (SLUB_DEBUG, KASAN, KFENCE)

  • ์ฐธ์กฐ

  • Linux 7.0 ์†Œ์Šค: mm/slub.c, mm/slab.h, include/linux/slab.h
  • minzkn.com: ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š” โ€” Slab ์„น์…˜
  • Documentation/mm/slub.rst