๏ปฟ# 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 ๊ด๋ จ ์ปค๋ ์ฌ๋ณผ ํ์ธ
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;
}
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;
}
min_nr๊ฐ ์์๋ฅผ ๋ฏธ๋ฆฌ ํ ๋นkmalloc_array ์คํจ โ -ENOMEM, alloc_fn ์คํจ โ mempool_exit ํ -ENOMEMmin_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;
}
__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);
}
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;
}
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 ์ฌ์ฉ */