๏ปฟ# Mempool (๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ ํ’€)

๊ด€๋ จ ์†Œ์Šค: mm/mempool.c, include/linux/mempool.h

๊ฐœ์š” (Overview)

Mempool์€ Linux ์ปค๋„์—์„œ ๊ทน์‹ฌํ•œ ๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ• ์ƒํ™ฉ์—์„œ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•˜๋Š” ๋น„์ƒ ์˜ˆ์•ฝ ํ’€(emergency reserve pool)์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์Šฌ๋Ÿผ ํ• ๋‹น์ž(SLUB)๋‚˜ ํŽ˜์ด์ง€ ํ• ๋‹น์ž๊ฐ€ ์‹คํŒจํ•˜๋”๋ผ๋„, mempool์€ ๋ฏธ๋ฆฌ ํ• ๋‹นํ•ด๋‘” ์š”์†Œ๋ฅผ ์ œ๊ณตํ•˜์—ฌ deadlock์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ : ๋น„์ƒ ํœด๋Œ€์ „ํ™” ์ถฉ์ „๊ธฐ๋ฅผ ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ํ‰์†Œ์—๋Š” ์ผ๋ฐ˜ ์ถฉ์ „๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์ •์ „์ด๋‚˜ ์žฌ๋‚œ ์ƒํ™ฉ์—์„œ ์ผ๋ฐ˜ ์ „๊ธฐ๊ฐ€ ๋Š๊ธฐ๋ฉด ๋ฏธ๋ฆฌ ์ถฉ์ „ํ•ด๋‘” ๋น„์ƒ ์ถฉ์ „๊ธฐ๋กœ ๊ธด๊ธ‰ํ•˜๊ฒŒ ์ „ํ™”๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Mempool๋„ ์ด์™€ ๊ฐ™์€ ์›๋ฆฌ๋กœ, ์ผ๋ฐ˜ ํ• ๋‹น์ž๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์œผ๋กœ ์‹คํŒจํ•ด๋„ ๋ฏธ๋ฆฌ ํ™•๋ณด๋œ ๋น„์ƒ ๋ฉ”๋ชจ๋ฆฌ๋กœ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์›๋ฆฌ: mempool_alloc()์€ ๋จผ์ € ์‚ฌ์šฉ์ž ์ •์˜ alloc_fn์„ ํ˜ธ์ถœํ•˜๊ณ , ์ด๊ฒƒ์ด ์‹คํŒจํ•˜๋ฉด ๋ฏธ๋ฆฌ ํ™•๋ณด๋œ ํ’€์—์„œ ์š”์†Œ๋ฅผ ๊บผ๋ƒ…๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ mempool_free()๋Š” ํ’€์ด min_nr ๋ฏธ๋งŒ์ด๋ฉด ์š”์†Œ๋ฅผ ํ’€์— ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ทธ ์™ธ์—๋Š” ์ง์ ‘ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜์—ฌ ํ’€์˜ ์š”์†Œ ์ˆ˜๊ฐ€ ์ผ์ • ์ˆ˜์ค€ ์ด์ƒ ์œ ์ง€๋˜์–ด, OOM ์ƒํ™ฉ์—์„œ๋„ ํ•ต์‹ฌ ํ• ๋‹น์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ:
mm/mempool.c                  โ† mempool ์ „์ฒด ๊ตฌํ˜„
include/linux/mempool.h       โ† struct mempool ์ •์˜, API ๋งคํฌ๋กœ
Mempool ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„
Mempool ํ• ๋‹น/ํ•ด์ œ ํ˜ธ์ถœ ํ๋ฆ„

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

# mempool ๊ด€๋ จ ์ปค๋„ ์‹ฌ๋ณผ ํ™•์ธ
cat /proc/kallsyms | grep mempool

# slab์—์„œ mempool ํ• ๋‹น ํ˜„ํ™ฉ ํ™•์ธ
cat /proc/slabinfo | grep mempool

# ์ปค๋„ ์„ค์ •์—์„œ mempool ๊ด€๋ จ ์˜ต์…˜ ํ™•์ธ
grep -i mempool /boot/config-$(uname -r)

# mempool ๊ด€๋ จ ๋ชจ๋“ˆ/๋“œ๋ผ์ด๋ฒ„ ์‚ฌ์šฉ ํ˜„ํ™ฉ
lsmod | grep -i mempool

# mempool fault injection debugfs ํ™•์ธ (CONFIG_FAULT_INJECTION ํ™œ์„ฑํ™” ์‹œ)
ls /sys/kernel/debug/fail_mempool_alloc/ 2>/dev/null

# ์ปค๋„ ๋กœ๊ทธ์—์„œ mempool ๊ด€๋ จ ๋ฉ”์‹œ์ง€ ํ™•์ธ
dmesg | grep -i mempool

# SLUB debug๊ฐ€ ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ mempool poison ์˜ค๋ฅ˜ ํ™•์ธ
dmesg | grep "mempool element poison"

# KASAN์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ mempool ๊ด€๋ จ ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฅ˜ ํ™•์ธ
dmesg | grep -i "mempool.*kasan"

# mempool ์‚ฌ์šฉ ์ปค๋„ ๋ชจ๋“ˆ ๋ชฉ๋ก ํ™•์ธ
cat /proc/modules | grep -i mempool

# sysfs์—์„œ mempool ๊ด€๋ จ ์ •๋ณด ํ™•์ธ (ํ•ด๋‹น ๋ชจ๋“ˆ ๋กœ๋“œ ์‹œ)
find /sys -name "*mempool*" 2>/dev/null

# ์ปค๋„ ๋นŒ๋“œ ์„ค์ •์—์„œ mempool ๋””๋ฒ„๊ทธ ์˜ต์…˜ ํ™•์ธ
grep -E "CONFIG_MEMPOOL|CONFIG_SLUB_DEBUG" /boot/config-$(uname -r)

# ๋ฉ”๋ชจ๋ฆฌ ์••๋ฐ• ์ƒํ™ฉ์—์„œ mempool ๋™์ž‘ ๋ชจ๋‹ˆํ„ฐ๋ง
vmstat 1 | grep -E "allocstall|pgfault"

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

struct mempool

/* include/linux/mempool.h:18-28 */
typedef struct mempool {
spinlock_t lock;            /* ์š”์†Œ ๋ฐฐ์—ด ๋ณดํ˜ธ์šฉ ์Šคํ•€๋ฝ */
int min_nr;                 /* ํ’€์— ๋ณด์žฅํ•  ์ตœ์†Œ ์š”์†Œ ์ˆ˜ */
int curr_nr;                /* ํ˜„์žฌ ํ’€์— ๋ณด์œ ํ•œ ์š”์†Œ ์ˆ˜ */
void **elements;            /* ์š”์†Œ ํฌ์ธํ„ฐ ๋ฐฐ์—ด (์Šคํƒ ์—ญํ• ) */
void *pool_data;            /* alloc/free ์ฝœ๋ฐฑ์— ์ „๋‹ฌํ•  ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ */
mempool_alloc_t *alloc;     /* ์š”์†Œ ํ• ๋‹น ํ•จ์ˆ˜ ํฌ์ธํ„ฐ */
mempool_free_t *free;       /* ์š”์†Œ ํ•ด์ œ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ */
wait_queue_head_t wait;     /* ์š”์†Œ ๋ถ€์กฑ ์‹œ ๋Œ€๊ธฐํ•  wait queue */
} mempool_t;

ํ•„๋“œ ์„ค๋ช…:

ํ•„๋“œํƒ€์ž…์—ญํ• 
`lock``spinlock_t``elements` ๋ฐฐ์—ด๊ณผ `curr_nr` ๋ณ€๊ฒฝ์„ ๋ณดํ˜ธ. IRQ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก `spin_lock_irqsave` ์‚ฌ์šฉ
`min_nr``int`ํ’€์ด ์œ ์ง€ํ•ด์•ผ ํ•  ์ตœ์†Œ ์š”์†Œ ์ˆ˜. ์ด ๊ฐ’ ์ดํ•˜๋กœ ๋‚ด๋ ค๊ฐ€๋ฉด ํ• ๋‹น ํ•จ์ˆ˜์—์„œ ๋ฐ˜ํ™˜๋œ ์š”์†Œ๋ฅผ ํ’€์— ๋ณด๊ด€
`curr_nr``int`ํ˜„์žฌ ํ’€์— ์ €์žฅ๋œ ์š”์†Œ ์ˆ˜. `curr_nr == 0`์ด๋ฉด ํ’€์ด ๋น„์–ด์žˆ์Œ
`elements``void **`LIFO ์Šคํƒ ์—ญํ• ์˜ ํฌ์ธํ„ฐ ๋ฐฐ์—ด. ์ธ๋ฑ์Šค `0`~`curr_nr-1`์— ์š”์†Œ ์ €์žฅ
`pool_data``void *`์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ์ „๋‹ฌ๋˜๋Š” ์ปจํ…์ŠคํŠธ. kmalloc์ด๋ฉด size, slab์ด๋ฉด `kmem_cache *`, ํŽ˜์ด์ง€๋ฉด order
`alloc``mempool_alloc_t *`์š”์†Œ ํ• ๋‹น ์ฝœ๋ฐฑ. ์‹คํŒจ ์‹œ NULL ๋ฐ˜ํ™˜ ๊ฐ€๋Šฅ
`free``mempool_free_t *`์š”์†Œ ํ•ด์ œ ์ฝœ๋ฐฑ
`wait``wait_queue_head_t``curr_nr == 0`์ด๊ณ  `__GFP_DIRECT_RECLAIM`์ผ ๋•Œ ์š”์†Œ๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ

ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ํƒ€์ž…

/* include/linux/mempool.h:15-16 */
typedef void * (mempool_alloc_t)(gfp_t gfp_mask, void *pool_data);
typedef void (mempool_free_t)(void *element, void *pool_data);

ํ—ฌํผ ํ•จ์ˆ˜

/* include/linux/mempool.h:30-38 โ€” mempool ์ƒํƒœ ํ™•์ธ */
static inline bool mempool_initialized(struct mempool *pool)
{
return pool->elements != NULL;  /* elements ๋ฐฐ์—ด ํ• ๋‹น ์—ฌ๋ถ€๋กœ ์ดˆ๊ธฐํ™” ํ™•์ธ */
}

static inline bool mempool_is_saturated(struct mempool *pool)
{
return READ_ONCE(pool->curr_nr) >= pool->min_nr;  /* ํ’€์ด ์ถฉ๋ถ„ํžˆ ์ฐจ ์žˆ๋Š”์ง€ ํ™•์ธ */
}
  • mempool_initialized(): mempool์ด ์ •์ƒ์ ์œผ๋กœ ์ดˆ๊ธฐํ™”๋˜์—ˆ๋Š”์ง€ ํ™•์ธ (elements ๋ฐฐ์—ด ์กด์žฌ ์—ฌ๋ถ€)
  • mempool_is_saturated(): ํ’€์˜ ์š”์†Œ ์ˆ˜๊ฐ€ min_nr ์ด์ƒ์ธ์ง€ ํ™•์ธ (ํ’€์ด ํฌํ™” ์ƒํƒœ์ธ์ง€)
  • ๋‚ด๋ถ€ ํ—ฌํผ ํ•จ์ˆ˜๋“ค

    /* mm/mempool.c:175-191 โ€” ์š”์†Œ ์ถ”๊ฐ€/์ œ๊ฑฐ */
    static __always_inline void add_element(struct mempool *pool, void *element)
    {
    BUG_ON(pool->min_nr != 0 && pool->curr_nr >= pool->min_nr);
    poison_element(pool, element);              /* SLUB debug: ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ ์ค‘๋… */
    if (kasan_poison_element(pool, element))    /* KASAN: ์ ‘๊ทผ ๋ถˆ๊ฐ€ ์˜์—ญ ํ‘œ์‹œ */
    pool->elements[pool->curr_nr++] = element;
    }
    
    static void *remove_element(struct mempool *pool)
    {
    void *element = pool->elements[--pool->curr_nr];
    BUG_ON(pool->curr_nr < 0);
    kasan_unpoison_element(pool, element);      /* KASAN: ์ ‘๊ทผ ํ—ˆ์šฉ ์˜์—ญ ํ‘œ์‹œ */
    check_element(pool, element);               /* SLUB debug: ์ค‘๋… ํŒจํ„ด ๊ฒ€์ฆ */
    return element;
    }

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

    1. mempool_create_node_noprof

    /* mm/mempool.c:313-331 โ€” ๋ฉ”๋ชจ๋ฆฌ ํ’€ ์ƒ์„ฑ (NUMA ์ธ์‹) */
    struct mempool *mempool_create_node_noprof(int min_nr,
    mempool_alloc_t *alloc_fn, mempool_free_t *free_fn,
    void *pool_data, gfp_t gfp_mask, int node_id)
    {
    struct mempool *pool;
    pool = kmalloc_node_noprof(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id);
    if (!pool)
    return NULL;
    if (mempool_init_node(pool, min_nr, alloc_fn, free_fn, pool_data,
    gfp_mask, node_id)) {
    kfree(pool);
    return NULL;
    }
    return pool;
    }
  • ์—ญํ• : ์ƒˆ mempool์„ ๋™์  ํ• ๋‹นํ•˜์—ฌ ์ดˆ๊ธฐํ™”
  • ๋ถ„๊ธฐ: kmalloc ์‹คํŒจ โ†’ NULL ๋ฐ˜ํ™˜, mempool_init_node ์‹คํŒจ โ†’ kfree ํ›„ NULL ๋ฐ˜ํ™˜
  • 2. mempool_init_node

    /* mm/mempool.c:233-269 โ€” mempool ์ดˆ๊ธฐํ™” (๊ธฐ๋ณธ ์š”์†Œ ์‚ฌ์ „ ํ• ๋‹น ํฌํ•จ) */
    int mempool_init_node(struct mempool *pool, int min_nr,
    mempool_alloc_t *alloc_fn, mempool_free_t *free_fn,
    void *pool_data, gfp_t gfp_mask, int node_id)
    {
    spin_lock_init(&pool->lock);
    pool->min_nr    = min_nr;
    pool->pool_data = pool_data;
    pool->alloc     = alloc_fn;
    pool->free      = free_fn;
    init_waitqueue_head(&pool->wait);
    pool->elements = kmalloc_array_node(max(1, min_nr), sizeof(void *),
    gfp_mask, node_id);
    if (!pool->elements)
    return -ENOMEM;
    while (pool->curr_nr < max(1, pool->min_nr)) {
    void *element;
    element = pool->alloc(gfp_mask, pool->pool_data);
    if (unlikely(!element)) {
    mempool_exit(pool);
    return -ENOMEM;
    }
    add_element(pool, element);
    }
    return 0;
    }
  • ์—ญํ• : ๊ธฐ์กด mempool ๊ตฌ์กฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  min_nr๊ฐœ ์š”์†Œ๋ฅผ ๋ฏธ๋ฆฌ ํ• ๋‹น
  • ๋ถ„๊ธฐ: kmalloc_array ์‹คํŒจ โ†’ -ENOMEM, alloc_fn ์‹คํŒจ โ†’ mempool_exit ํ›„ -ENOMEM
  • ํŠน์ง•: min_nr == 0์ด์–ด๋„ ์ตœ์†Œ 1๊ฐœ ์š”์†Œ๋ฅผ ํ• ๋‹น (์•ˆ์ „์žฅ์น˜)
  • 3. mempool_alloc_noprof

    /* mm/mempool.c:552-590 โ€” ์š”์†Œ 1๊ฐœ ํ• ๋‹น (ํ•ต์‹ฌ ํ• ๋‹น ๊ฒฝ๋กœ) */
    void *mempool_alloc_noprof(struct mempool *pool, gfp_t gfp_mask)
    {
    gfp_t gfp_temp = mempool_adjust_gfp(&gfp_mask);
    void *element;
    
    VM_WARN_ON_ONCE(gfp_mask & __GFP_ZERO);
    might_alloc(gfp_mask);
    
    repeat_alloc:
    if (should_fail_ex(&fail_mempool_alloc, 1, FAULT_NOWARN)) {
    pr_info("forcing mempool usage for %pS\n", (void *)_RET_IP_);
    element = NULL;
    } else {
    element = pool->alloc(gfp_temp, pool->pool_data);
    }
    
    if (unlikely(!element)) {
    if (!mempool_alloc_from_pool(pool, &element, 1, 0, gfp_temp)) {
    if (gfp_temp != gfp_mask) {
    gfp_temp = gfp_mask;
    goto repeat_alloc;
    }
    if (gfp_mask & __GFP_DIRECT_RECLAIM) {
    goto repeat_alloc;
    }
    }
    }
    return element;
    }
  • ์—ญํ• : ํ’€์—์„œ ์š”์†Œ ํ• ๋‹น. ์ผ๋ฐ˜ ํ• ๋‹น ์šฐ์„ , ์‹คํŒจ ์‹œ ์˜ˆ์•ฝ ํ’€์—์„œ ๊บผ๋ƒ„
  • ํ๋ฆ„:
  • 1. mempool_adjust_gfp๋กœ GFP ํ”Œ๋ž˜๊ทธ ์กฐ์ • (emergency reserve ์‚ฌ์šฉ ๋ฐฉ์ง€)

    2. alloc_fn ํ˜ธ์ถœ ์‹œ๋„

    3. ์‹คํŒจ ์‹œ โ†’ mempool_alloc_from_pool๋กœ ํ’€์—์„œ ํ• ๋‹น

    4. ํ’€๋„ ๋น„์–ด์žˆ์œผ๋ฉด โ†’ ์žฌ์‹œ๋„ ์—ฌ๋ถ€ ๊ฒฐ์ • (__GFP_DIRECT_RECLAIM ์œ ๋ฌด)

    4. mempool_free

    /* mm/mempool.c:708-713 โ€” ์š”์†Œ ๋ฐ˜ํ™˜ */
    void mempool_free(void *element, struct mempool *pool)
    {
    if (likely(element) && !mempool_free_bulk(pool, &element, 1))
    pool->free(element, pool->pool_data);
    }
  • ์—ญํ• : ์š”์†Œ๋ฅผ ํ’€์— ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ํ•ด์ œ
  • ํ๋ฆ„: mempool_free_bulk๊ฐ€ ํ’€์— ๋„ฃ์—ˆ์œผ๋ฉด ์ข…๋ฃŒ, ์•„๋‹ˆ๋ฉด free_fn ํ˜ธ์ถœ
  • 5. mempool_resize

    /* mm/mempool.c:351-409 โ€” ํ’€ ํฌ๊ธฐ ๋™์  ์กฐ์ ˆ */
    int mempool_resize(struct mempool *pool, int new_min_nr)
  • ์—ญํ• :่ฟ่กŒๆ—ถ ํ’€์˜ min_nr๋ฅผ ๋ณ€๊ฒฝ
  • ๋ถ„๊ธฐ:
  • - ์ถ•์†Œ (new_min_nr < min_nr): ์ดˆ๊ณผ ์š”์†Œ๋ฅผ free_fn์œผ๋กœ ํ•ด์ œ

    - ํ™•์žฅ (new_min_nr > min_nr): ์ƒˆ ๋ฐฐ์—ด ํ• ๋‹น, ์š”์†Œ ์ถ”๊ฐ€ ํ™•๋ณด

    6. mempool_alloc_from_pool (์ •์  ํ• ๋‹น ๊ฒฝ๋กœ)

    /* mm/mempool.c:412-462 โ€” ํ’€ ๋‚ด๋ถ€ ์š”์†Œ์—์„œ ํ• ๋‹น */
    static unsigned int mempool_alloc_from_pool(struct mempool *pool, void **elems,
    unsigned int count, unsigned int allocated, gfp_t gfp_mask)
    {
    /* ... */
    spin_lock_irqsave(&pool->lock, flags);
    if (unlikely(pool->curr_nr < count - allocated))
    goto fail;
    for (i = 0; i < count; i++) {
    if (!elems[i]) {
    elems[i] = remove_element(pool);
    allocated++;
    }
    }
    spin_unlock_irqrestore(&pool->lock, flags);
    /* ... */
    fail:
    if (gfp_mask & __GFP_DIRECT_RECLAIM) {
    /* ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„ */
    DEFINE_WAIT(wait);
    prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);
    spin_unlock_irqrestore(&pool->lock, flags);
    io_schedule_timeout(5 * HZ);
    finish_wait(&pool->wait, &wait);
    } else {
    spin_unlock_irqrestore(&pool->lock, flags);
    }
    return allocated;
    }
  • ์—ญํ• : lock์„ ์žก๊ณ  ํ’€ ์Šคํƒ์—์„œ ์š”์†Œ ์ œ๊ฑฐ
  • ๋ถ„๊ธฐ: ์š”์†Œ ๋ถ€์กฑ + __GFP_DIRECT_RECLAIM โ†’ 5์ดˆ ํƒ€์ž„์•„์›ƒ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„
  • 7. mempool_adjust_gfp

    /* mm/mempool.c:471-475 โ€” GFP ํ”Œ๋ž˜๊ทธ ์กฐ์ • */
    static inline gfp_t mempool_adjust_gfp(gfp_t *gfp_mask)
    {
    *gfp_mask |= __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN;
    return *gfp_mask & ~(__GFP_DIRECT_RECLAIM | __GFP_IO);
    }
  • ์—ญํ• : ์ฒซ ํ• ๋‹น ์‹œ๋„์—์„œ emergency reserve์™€ direct reclaim๋ฅผ ์ฐจ๋‹จ
  • ์ด์œ : mempool์€ ์ตœํ›„์˜ ๋ณด๋ฃจ์ด๋ฏ€๋กœ, ์ผ๋ฐ˜ ํ• ๋‹น์ž๊ฐ€ ์ถฉ๋ถ„ํžˆ ์‹œ๋„ํ•˜๋„๋ก ์œ ๋„
  • 8. mempool_alloc_bulk_noprof (๋ฐฐ์น˜ ํ• ๋‹น)

    /* mm/mempool.c:492-534 โ€” ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ํ•œ๋ฒˆ์— ํ• ๋‹น */
    int mempool_alloc_bulk_noprof(struct mempool *pool, void **elems,
    unsigned int count, unsigned int allocated)
    {
    gfp_t gfp_mask = GFP_KERNEL;
    gfp_t gfp_temp = mempool_adjust_gfp(&gfp_mask);
    unsigned int i = 0;
    
    VM_WARN_ON_ONCE(count > pool->min_nr);  /* min_nr ์ด๋‚ด๋กœ๋งŒ ๋ฐฐ์น˜ ํ• ๋‹น ํ—ˆ์šฉ */
    might_alloc(gfp_mask);
    
    /* fault injection ํ…Œ์ŠคํŠธ์šฉ ๋ถ„๊ธฐ */
    if (should_fail_ex(&fail_mempool_alloc_bulk, 1, FAULT_NOWARN)) {
    pr_info("forcing mempool usage for %pS\n", (void *)_RET_IP_);
    goto use_pool;
    }
    
    repeat_alloc:
    /* 1์ฐจ: alloc_fn์œผ๋กœ ๊ฐ ์š”์†Œ ํ• ๋‹น ์‹œ๋„ */
    for (i = 0; i < count; i++) {
    if (elems[i])
    continue;  /* ์ด๋ฏธ ํ• ๋‹น๋œ ์Šฌ๋กฏ์€ ๊ฑด๋„ˆ๋œ€ */
    elems[i] = pool->alloc(gfp_temp, pool->pool_data);
    if (unlikely(!elems[i]))
    goto use_pool;
    allocated++;
    }
    return 0;
    
    use_pool:
    /* 2์ฐจ: ํ’€์—์„œ ํ• ๋‹น ํ›„ ์žฌ์‹œ๋„ */
    allocated = mempool_alloc_from_pool(pool, elems, count, allocated, gfp_temp);
    gfp_temp = gfp_mask;  /* ๋‹ค์Œ ๋ฐ˜๋ณต๋ถ€ํ„ฐ๋Š” ๋ชจ๋“  GFP ํ”Œ๋ž˜๊ทธ ํ—ˆ์šฉ */
    goto repeat_alloc;
    }
  • ์—ญํ• : ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ํ•œ๋ฒˆ์— ๋ฐฐ์น˜ ํ• ๋‹น (์„ฑ๋Šฅ ์ตœ์ ํ™”)
  • ๋ถ„๊ธฐ: count > pool->min_nr ๊ฒฝ๊ณ , fault injection ํ…Œ์ŠคํŠธ ์ง€์›
  • 9. mempool_alloc_preallocated (์‚ฌ์ „ ํ• ๋‹น๋œ ์š”์†Œ๋งŒ ์‚ฌ์šฉ)

    /* mm/mempool.c:604-611 โ€” ์‚ฌ์ „ ํ• ๋‹น๋œ ์š”์†Œ์—์„œ๋งŒ ํ• ๋‹น */
    void *mempool_alloc_preallocated(struct mempool *pool)
    {
    void *element = NULL;
    
    /* GFP_NOWAIT๋กœ ์ฆ‰์‹œ ํ• ๋‹น ์‹œ๋„, ์‹คํŒจ ์‹œ NULL ๋ฐ˜ํ™˜ */
    mempool_alloc_from_pool(pool, &element, 1, 0, GFP_NOWAIT);
    return element;
    }
  • ์—ญํ• : IRQ ์ปจํ…์ŠคํŠธ ๋“ฑ์—์„œ ์Šฌ๋ฆฝ ์—†์ด ์‚ฌ์ „ ํ• ๋‹น๋œ ์š”์†Œ๋งŒ ํ• ๋‹น
  • ํŠน์ง•: GFP_NOWAIT ์‚ฌ์šฉ์œผ๋กœ ๋Œ€๊ธฐ ์—†์ด ์ฆ‰์‹œ ๋ฐ˜ํ™˜
  • 10. mempool_free_bulk (๋ฐฐ์น˜ ๋ฐ˜ํ™˜)

    /* mm/mempool.c:627-696 โ€” ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ํ•œ๋ฒˆ์— ๋ฐ˜ํ™˜ */
    unsigned int mempool_free_bulk(struct mempool *pool, void **elems,
    unsigned int count)
    {
    unsigned long flags;
    unsigned int freed = 0;
    bool added = false;
    
    /* mempool_alloc๊ณผ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฐฐ๋ฆฌ์–ด ํŽ˜์–ด๋ง */
    smp_rmb();
    
    /* ํ’€์ด ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ ์š”์†Œ๋ฅผ ํ’€์— ๋ฐ˜ํ™˜ */
    if (unlikely(READ_ONCE(pool->curr_nr) < pool->min_nr)) {
    spin_lock_irqsave(&pool->lock, flags);
    while (pool->curr_nr < pool->min_nr && freed < count) {
    add_element(pool, elems[freed++]);
    added = true;
    }
    spin_unlock_irqrestore(&pool->lock, flags);
    } else if (unlikely(pool->min_nr == 0 &&
    READ_ONCE(pool->curr_nr) == 0)) {
    /* min_nr == 0 ์—ฃ์ง€ ์ผ€์ด์Šค: ์ตœ์†Œ 1๊ฐœ ์š”์†Œ ๋ณด์žฅ */
    spin_lock_irqsave(&pool->lock, flags);
    if (likely(pool->curr_nr == 0)) {
    add_element(pool, elems[freed++]);
    added = true;
    }
    spin_unlock_irqrestore(&pool->lock, flags);
    }
    
    /* ๋Œ€๊ธฐ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ์œผ๋ฉด ๊นจ์šฐ๊ธฐ */
    if (unlikely(added) && wq_has_sleeper(&pool->wait))
    wake_up(&pool->wait);
    
    return freed;  /* ํ’€์— ๋ฐ˜ํ™˜๋œ ์š”์†Œ ์ˆ˜ */
    }
  • ์—ญํ• : ์—ฌ๋Ÿฌ ์š”์†Œ๋ฅผ ํ•œ๋ฒˆ์— ํ’€์— ๋ฐ˜ํ™˜
  • ํŠน์ง•: smp_rmb() ๋ฉ”๋ชจ๋ฆฌ ๋ฐฐ๋ฆฌ์–ด๋กœ ํ• ๋‹น/๋ฐ˜ํ™˜ ์ˆœ์„œ ๋ณด์žฅ, ๋Œ€๊ธฐ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค ๊นจ์šฐ๊ธฐ

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

    mempool_create(min_nr, alloc_fn, free_fn, pool_data)
    โ””โ”€ mempool_create_node_noprof()
    โ””โ”€ mempool_init_node()
    โ”œโ”€ kmalloc_array_node()         โ† elements ๋ฐฐ์—ด ํ• ๋‹น
    โ””โ”€ while (curr_nr < min_nr)
    โ””โ”€ pool->alloc()           โ† ๋ฏธ๋ฆฌ ์š”์†Œ ์ฑ„์šฐ๊ธฐ
    โ””โ”€ add_element()
    
    mempool_alloc(pool, gfp_mask)
    โ””โ”€ mempool_alloc_noprof()
    โ”œโ”€ mempool_adjust_gfp()             โ† GFP ํ”Œ๋ž˜๊ทธ ์กฐ์ •
    โ”œโ”€ pool->alloc(gfp_temp)            โ† 1์ฐจ: alloc_fn ํ˜ธ์ถœ
    โ””โ”€ (์‹คํŒจ ์‹œ) mempool_alloc_from_pool()
    โ”œโ”€ spin_lock_irqsave()
    โ”œโ”€ remove_element()            โ† ํ’€ ์Šคํƒ์—์„œ ๊บผ๋‚ด๊ธฐ
    โ”œโ”€ spin_unlock_irqrestore()
    โ””โ”€ (๋ถ€์กฑ ์‹œ) io_schedule_timeout(5*HZ) ๋Œ€๊ธฐ
    
    mempool_alloc_bulk(pool, elems, count, allocated)
    โ””โ”€ mempool_alloc_bulk_noprof()
    โ”œโ”€ mempool_adjust_gfp()             โ† GFP ํ”Œ๋ž˜๊ทธ ์กฐ์ •
    โ”œโ”€ for each elem: pool->alloc()     โ† 1์ฐจ: ๊ฐ ์š”์†Œ ํ• ๋‹น ์‹œ๋„
    โ””โ”€ (์‹คํŒจ ์‹œ) mempool_alloc_from_pool() + ์žฌ์‹œ๋„
    
    mempool_alloc_preallocated(pool)
    โ””โ”€ mempool_alloc_from_pool(GFP_NOWAIT)  โ† ์ฆ‰์‹œ ํ• ๋‹น, ์—†์œผ๋ฉด NULL ๋ฐ˜ํ™˜
    
    mempool_free(element, pool)
    โ””โ”€ mempool_free_bulk(pool, &element, 1)
    โ”œโ”€ (curr_nr < min_nr) โ†’ add_element()  โ† ํ’€์— ๋ฐ˜ํ™˜
    โ””โ”€ (ํ’€ ๊ฝ‰ ์ฐธ) โ†’ pool->free()         โ† ์ง์ ‘ ํ•ด์ œ
    
    mempool_free_bulk(pool, elems, count)
    โ”œโ”€ smp_rmb()                            โ† ๋ฉ”๋ชจ๋ฆฌ ๋ฐฐ๋ฆฌ์–ด
    โ”œโ”€ (curr_nr < min_nr) โ†’ add_element()   โ† ํ’€์— ๋ฐ˜ํ™˜
    โ”œโ”€ (min_nr == 0 && curr_nr == 0) โ†’ add_element()  โ† ์—ฃ์ง€ ์ผ€์ด์Šค
    โ””โ”€ wake_up(&pool->wait)                 โ† ๋Œ€๊ธฐ ํ”„๋กœ์„ธ์Šค ๊นจ์šฐ๊ธฐ
    
    mempool_destroy(pool)
    โ””โ”€ mempool_exit()
    โ”œโ”€ while (curr_nr) โ†’ remove_element() + pool->free()
    โ””โ”€ kfree(elements)
    โ””โ”€ kfree(pool)

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

    GFP ํ”Œ๋ž˜๊ทธ์— ๋”ฐ๋ฅธ ํ• ๋‹น ๋™์ž‘

    ์กฐ๊ฑดmempool_adjust_gfp ์ ์šฉํ• ๋‹น ์‹œ๋„ํ’€ ์‚ฌ์šฉ ํ›„ ๋™์ž‘
    `GFP_KERNEL` (๊ธฐ๋ณธ)`__GFP_DIRECT_RECLAIM` ์ œ๊ฑฐ, `__GFP_NOMEMALLOC` ์ถ”๊ฐ€alloc_fn 1ํšŒ ์‹œ๋„์‹คํŒจ ์‹œ ํ’€์—์„œ ๊บผ๋‚ด๊ณ , still ์‹คํŒจ ์‹œ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„
    `__GFP_DIRECT_RECLAIM` ํฌํ•จ์ œ๊ฑฐ ํ›„ ๋ณต์›alloc_fn + reclaim ๊ฐ€๋Šฅํ’€ ๋ถ€์กฑ ์‹œ `io_schedule_timeout(5*HZ)` ๋Œ€๊ธฐ
    `__GFP_NOWAIT`๊ทธ๋Œ€๋กœalloc_fn 1ํšŒ๋งŒ ์‹œ๋„ํ’€ ์‹คํŒจ ์‹œ ์ฆ‰์‹œ NULL ๋ฐ˜ํ™˜

    ์‚ฌ์ „ ์ •์˜ alloc/free ์ฝœ๋ฐฑ

    ์ฝœ๋ฐฑ ์Œpool_data์‚ฌ์šฉ ์šฉ๋„๋‚ด๋ถ€ ํ• ๋‹น์ž
    `mempool_alloc_slab` / `mempool_free_slab``struct kmem_cache *`slab ์บ์‹œ ๊ธฐ๋ฐ˜ ํ• ๋‹น`kmem_cache_alloc` / `kmem_cache_free`
    `mempool_kmalloc` / `mempool_kfree``size_t` (๋ฐ”์ดํŠธ ์ˆ˜)kmalloc ํฌ๊ธฐ ์ง€์ • ํ• ๋‹น`kmalloc` / `kfree`
    `mempool_alloc_pages` / `mempool_free_pages``int order`ํŽ˜์ด์ง€ ํ• ๋‹น์ž ๊ธฐ๋ฐ˜ ํ• ๋‹น`alloc_pages` / `__free_pages`

    ํ’€ ์ƒํƒœ๋ณ„ mempool_free ๋™์ž‘

    ํ’€ ์ƒํƒœ (`curr_nr` vs `min_nr`)๋™์ž‘
    `curr_nr < min_nr`์š”์†Œ๋ฅผ ํ’€ ์Šคํƒ์— ๋ฐ˜ํ™˜ (์žฌ์‚ฌ์šฉ ์ค€๋น„)
    `curr_nr >= min_nr``free_fn`์œผ๋กœ ์ง์ ‘ ํ•ด์ œ
    `min_nr == 0` && `curr_nr == 0`์ตœ์†Œ 1๊ฐœ ์š”์†Œ๋ฅผ ํ’€์— ๋ฐ˜ํ™˜ (์—ฃ์ง€ ์ผ€์ด์Šค ์ฒ˜๋ฆฌ)

    mempool_alloc ์žฌ์‹œ๋„ ๋ถ„๊ธฐ

    ์‹œ๋‚˜๋ฆฌ์˜คalloc_fn ๊ฒฐ๊ณผํ’€ ์ƒํƒœ๋‹ค์Œ ๋™์ž‘
    ์ •์ƒ ํ• ๋‹น์„ฑ๊ณต๋ฌด๊ด€์š”์†Œ ๋ฐ˜ํ™˜
    ํ• ๋‹น ์‹คํŒจ 1์ฐจ์‹คํŒจ์š”์†Œ ์žˆ์Œํ’€์—์„œ ๊บผ๋‚ด๊ธฐ + ์žฌ์‹œ๋„
    ํ• ๋‹น ์‹คํŒจ 2์ฐจ (ํ’€ ๋น„์–ด์žˆ์Œ)์‹คํŒจ`curr_nr == 0``__GFP_DIRECT_RECLAIM` โ†’ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„, ์•„๋‹ˆ๋ฉด NULL

    Fault Injection ๋ฐ ๋””๋ฒ„๊น…

    Fault Injection ๋ฉ”์ปค๋‹ˆ์ฆ˜

    Mempool์€ CONFIG_FAULT_INJECTION์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ fault injection์„ ์ง€์›ํ•˜์—ฌ, ํ• ๋‹น ์‹คํŒจ ์ƒํ™ฉ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    /* mm/mempool.c:21-22 โ€” fault injection ์†์„ฑ ์ •์˜ */
    static DECLARE_FAULT_ATTR(fail_mempool_alloc);
    static DECLARE_FAULT_ATTR(fail_mempool_alloc_bulk);
    
    /* mm/mempool.c:24-38 โ€” debugfs ์ธํ„ฐํŽ˜์ด์Šค ์ดˆ๊ธฐํ™” */
    static int __init mempool_faul_inject_init(void)
    {
    int error;
    error = PTR_ERR_OR_ZERO(fault_create_debugfs_attr("fail_mempool_alloc",
    NULL, &fail_mempool_alloc));
    if (error)
    return error;
    return PTR_ERR_OR_ZERO(
    fault_create_debugfs_attr("fail_mempool_alloc_bulk", NULL,
    &fail_mempool_alloc_bulk));
    }
    late_initcall(mempool_faul_inject_init);

    debugfs ์ธํ„ฐํŽ˜์ด์Šค:

  • /sys/kernel/debug/fail_mempool_alloc/ โ€” ๋‹จ์ผ ํ• ๋‹น ์‹คํŒจ ํ…Œ์ŠคํŠธ
  • /sys/kernel/debug/fail_mempool_alloc_bulk/ โ€” ๋ฐฐ์น˜ ํ• ๋‹น ์‹คํŒจ ํ…Œ์ŠคํŠธ
  • SLUB/KASAN ๋””๋ฒ„๊น…

    /* mm/mempool.c:40-150 โ€” CONFIG_SLUB_DEBUG_ON ํ™œ์„ฑํ™” ์‹œ ๋™์ž‘ */
    #ifdef CONFIG_SLUB_DEBUG_ON
    static void poison_error(struct mempool *pool, void *element, size_t size,
    size_t byte);  /* ์ค‘๋… ํŒจํ„ด ๋ถˆ์ผ์น˜ ์˜ค๋ฅ˜ ์ถœ๋ ฅ */
    static void __check_element(struct mempool *pool, void *element, size_t size);  /* ์š”์†Œ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ */
    static void check_element(struct mempool *pool, void *element);  /* KASAN ์ƒํƒœ ํ™•์ธ ํ›„ ๊ฒ€์ฆ */
    static void __poison_element(void *element, size_t size);  /* ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ์— ์ค‘๋… ํŒจํ„ด ๊ธฐ๋ก */
    static void poison_element(struct mempool *pool, void *element);  /* KASAN ์ƒํƒœ ํ™•์ธ ํ›„ ์ค‘๋… */
    #endif
    
    /* KASAN ๋ฉ”๋ชจ๋ฆฌ ์ค‘๋…/ํ•ด์ œ ํ•จ์ˆ˜ */
    static __always_inline bool kasan_poison_element(struct mempool *pool, void *element);
    static void kasan_unpoison_element(struct mempool *pool, void *element);

    ๋™์ž‘ ์›๋ฆฌ:

    1. poison_element(): ์š”์†Œ๋ฅผ ํ’€์— ๋„ฃ์„ ๋•Œ POISON_FREE ํŒจํ„ด์œผ๋กœ ๋ฎ์–ด์“ฐ๊ธฐ

    2. check_element(): ์š”์†Œ๋ฅผ ํ’€์—์„œ ๊บผ๋‚ผ ๋•Œ ์ค‘๋… ํŒจํ„ด ๊ฒ€์ฆ

    3. kasan_poison_element(): KASAN์—๊ฒŒ ํ•ด๋‹น ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์ด ์ ‘๊ทผ ๋ถˆ๊ฐ€์ž„์„ ์•Œ๋ฆผ

    4. kasan_unpoison_element(): KASAN์—๊ฒŒ ํ•ด๋‹น ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•จ์„ ์•Œ๋ฆผ


    mempool์ด ์‚ฌ์šฉ๋˜๋Š” ์ฃผ์š” ์ปค๋„ ์„œ๋ธŒ์‹œ์Šคํ…œ

    ์„œ๋ธŒ์‹œ์Šคํ…œ์‚ฌ์šฉ ๋ชฉ์ ์˜ˆ์‹œ๊ด€๋ จ ์†Œ์Šค
    I/O ์Šค์ผ€์ค„๋Ÿฌ์š”์ฒญ ํ’€์ด ์†Œ์ง„๋˜์ง€ ์•Š๋„๋ก ๋ณด์žฅ`elevator_mempool_init`block/elevator.c
    ๋ธ”๋ก ์žฅ์น˜bio ํ• ๋‹น ๋น„์ƒ ์˜ˆ์•ฝ`blk_rq_mempool_init`block/blk-mq.c
    ๋„คํŠธ์›Œํฌsk_buff ํ• ๋‹น ๋ณด์žฅ`skb_mempool_init`net/core/skbuff.c
    ํŒŒ์ผ ์‹œ์Šคํ…œjournal/head ํ• ๋‹นext4, XFS ๋“ฑfs/ext4/, fs/xfs/
    DM (Device Mapper)์ž‘์—… ๊ตฌ์กฐ์ฒด ํ• ๋‹น`dm_mempool_create`drivers/md/dm.c
    SCSI์š”์ฒญ ํ’€ ํ• ๋‹น`scsi_mempool_create`drivers/scsi/scsi.c
    USB์š”์ฒญ ํ’€ ํ• ๋‹น`usb_mempool_create`drivers/usb/core/
    InfiniBand์ž‘์—… ์š”์ฒญ ํ’€`ib_mempool_create`drivers/infiniband/

    ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

    /* ๋„คํŠธ์›Œํฌ ์Šคํƒ: sk_buff ํ• ๋‹น์„ ์œ„ํ•œ mempool ์‚ฌ์šฉ ์˜ˆ์‹œ */
    /* net/core/skbuff.c์—์„œ sk_buff_mempool ์ƒ์„ฑ */
    
    /* ๋ธ”๋ก ์žฅ์น˜: blk_rq_mempool ์ƒ์„ฑ ์˜ˆ์‹œ */
    /* block/blk-mq.c์—์„œ request ํ• ๋‹น์„ ์œ„ํ•œ mempool ์‚ฌ์šฉ */

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • SLUB ํ• ๋‹น์ž
  • Buddy Allocator
  • vmalloc
  • Mempool (DMA Pool)
  • Shrinker