内核 mempool 的使用
内核给我们提供了内存管理的一套方式,你只要把申请好的内存池交给mempool管理,
并且提供申请和释放内存的接口, mempool就可以帮你管理这个内存池,而且这个机制支持
设置内存预留,防止在内存紧张的时候,一直分不到内存。
1、mempool_create 创建一个mempool管理的内存池。
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data);
mempool_create - create a memory pool
@min_nr: the minimum number of elements guaranteed to be
allocated for this pool.//内存池最小的元素个数
@alloc_fn: user-defined element-allocation function.
@free_fn: user-defined element-freeing function.
@pool_data: optional private data available to the user-defined functions.
this function creates and allocates a guaranteed size, preallocated
memory pool. The pool can be used from the mempool_alloc() and mempool_free()
functions. This function might sleep. Both the alloc_fn() and the free_fn()
functions might sleep - as long as the mempool_alloc() function is not called
from IRQ contexts.
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data)
{
return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,
GFP_KERNEL, NUMA_NO_NODE);
}
EXPORT_SYMBOL(mempool_create);
mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data,
gfp_t gfp_mask, int node_id)
{
mempool_t *pool;
pool = kzalloc_node(sizeof(*pool), gfp_mask, node_id);
if (!pool)
return NULL;
pool->elements = kmalloc_node(min_nr * sizeof(void *),
gfp_mask, node_id);
if (!pool->elements) {
kfree(pool);
return NULL;
}
spin_lock_init(&pool->lock);
pool->min_nr = min_nr;
pool->pool_data = pool_data;
init_waitqueue_head(&pool->wait);
pool->alloc = alloc_fn;
pool->free = free_fn;
/*
* First pre-allocate the guaranteed number of buffers.
*/
while (pool->curr_nr < pool->min_nr) {//预分配最低的内存个数
void *element;
element = pool->alloc(gfp_mask, pool->pool_data);
if (unlikely(!element)) {
mempool_destroy(pool);
return NULL;
}
add_element(pool, element);
}
return pool;
}
2、内存分配
void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
{
void *element;
unsigned long flags;
wait_queue_t wait;
gfp_t gfp_temp;
VM_WARN_ON_ONCE(gfp_mask & __GFP_ZERO);
might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);
gfp_mask |= __GFP_NOMEMALLOC; /* don't allocate emergency reserves */
gfp_mask |= __GFP_NORETRY; /* don't loop in __alloc_pages */
gfp_mask |= __GFP_NOWARN; /* failures are OK */
gfp_temp = gfp_mask & ~(__GFP_DIRECT_RECLAIM|__GFP_IO);
repeat_alloc:
element = pool->alloc(gfp_temp, pool->pool_data);//分配一个元素
if (likely(element != NULL))
return element;
//如果没有内存可以分配了,就去我们预留的那几个里面去取
spin_lock_irqsave(&pool->lock, flags);
if (likely(pool->curr_nr)) {
element = remove_element(pool, gfp_temp);
spin_unlock_irqrestore(&pool->lock, flags);
/* paired with rmb in mempool_free(), read comment there */
smp_wmb();
/*
* Update the allocation stack trace as this is more useful
* for debugging.
*/
kmemleak_update_trace(element);
return element;
}
/*
* We use gfp mask w/o direct reclaim or IO for the first round. If
* alloc failed with that and @pool was empty, retry immediately.
*/
if (gfp_temp != gfp_mask) {
spin_unlock_irqrestore(&pool->lock, flags);
gfp_temp = gfp_mask;
goto repeat_alloc;
}
/* We must not sleep if !__GFP_DIRECT_RECLAIM */
if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {
spin_unlock_irqrestore(&pool->lock, flags);
return NULL;
}
//如果保留内存也没了,就等一会,等其他的地方释放了内存,再去分配。
/* Let's wait for someone else to return an element to @pool */
init_wait(&wait);
prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);
spin_unlock_irqrestore(&pool->lock, flags);
/*
* FIXME: this should be io_schedule(). The timeout is there as a
* workaround for some DM problems in 2.6.18.
*/
io_schedule_timeout(5*HZ);//开始睡眠
finish_wait(&pool->wait, &wait);
goto repeat_alloc; //重新去分配内存
}
EXPORT_SYMBOL(mempool_alloc);
3、释放内存
void mempool_free(void *element, mempool_t *pool)
{
unsigned long flags;
if (unlikely(element == NULL))
return;
/*
* Paired with the wmb in mempool_alloc(). The preceding read is
* for @element and the following @pool->curr_nr. This ensures
* that the visible value of @pool->curr_nr is from after the
* allocation of @element. This is necessary for fringe cases
* where @element was passed to this task without going through
* barriers.
*
* For example, assume @p is %NULL at the beginning and one task
* performs "p = mempool_alloc(...);" while another task is doing
* "while (!p) cpu_relax(); mempool_free(p, ...);". This function
* may end up using curr_nr value which is from before allocation
* of @p without the following rmb.
*/
smp_rmb();
/*
* For correctness, we need a test which is guaranteed to trigger
* if curr_nr + #allocated == min_nr. Testing curr_nr < min_nr
* without locking achieves that and refilling as soon as possible
* is desirable.
*
* Because curr_nr visible here is always a value after the
* allocation of @element, any task which decremented curr_nr below
* min_nr is guaranteed to see curr_nr < min_nr unless curr_nr gets
* incremented to min_nr afterwards. If curr_nr gets incremented
* to min_nr after the allocation of @element, the elements
* allocated after that are subject to the same guarantee.
*
* Waiters happen iff curr_nr is 0 and the above guarantee also
* ensures that there will be frees which return elements to the
* pool waking up the waiters.
*/
if (unlikely(pool->curr_nr < pool->min_nr)) { //如果预留的内存不够,则需要往预留内存中释放
spin_lock_irqsave(&pool->lock, flags);
if (likely(pool->curr_nr < pool->min_nr)) {
add_element(pool, element);
spin_unlock_irqrestore(&pool->lock, flags);
wake_up(&pool->wait); //唤醒任务[分配那边睡眠了]
return;
}
spin_unlock_irqrestore(&pool->lock, flags);
}
pool->free(element, pool->pool_data);
}
EXPORT_SYMBOL(mempool_free);
下面是使用方法举例:
#include "sal.h"
#include "linux/slab.h"
#include "linux/mempool.h"
#include "linux/types.h"
#include "linux/gfp.h"
struct sal_mem_pool
{
mempool_t* mempool;
struct kmem_cache* cache;
};
void *cc_sal_realloc(void *ptr, size_t size)
{
void *new_ptr = NULL;
if (ptr)
{
if (size != 0)
{
if (!(new_ptr = kmalloc(size, GFP_KERNEL)))
{
return NULL;
}
memmove(new_ptr, ptr, size);
}
kfree(ptr);
}
else
{
if (size != 0)
{
if (!(new_ptr = kmalloc(size, GFP_KERNEL)))
{
return NULL;
}
}
}
return new_ptr;
}
void *cc_sal_malloc(size_t size)
{
return kmalloc(size, GFP_KERNEL);
}
void *cc_sal_calloc(size_t size)
{
void *ptr = kmalloc(size, GFP_KERNEL);
if (ptr)
{
memset(ptr, 0, sizeof(size));
}
return ptr;
}
void*
cc_sal_malloc_atomic(size_t size)
{
return kmalloc(size, GFP_ATOMIC);
}
void cc_sal_free(void *p)
{
kfree(p);
}
void
cc_sal_malloc_failed(const char* file, int line, size_t size)
{
cc_sal_log(SAL_LL_ERROR, file, line, "malloc(%d) failed!", size);
}
int
cc_sal_mem_pool_create(sal_mem_pool_t** mem_pool, const char* name,
size_t size, size_t align, uint32 min_nr)
{
mempool_t* pool;
SAL_MALLOC(*mem_pool, sal_mem_pool_t*, sizeof(sal_mem_pool_t));
if (*mem_pool == NULL)
{
return ENOMEM;
}
(*mem_pool)->cache = kmem_cache_create(name, size, align, 0, NULL);
if (!((*mem_pool)->cache))
{
SAL_FREE(*mem_pool);
return ENOMEM;
}
pool = mempool_create(min_nr, mempool_alloc_slab, mempool_free_slab,
(*mem_pool)->cache);
if (!pool)
{
kmem_cache_destroy((*mem_pool)->cache);
SAL_FREE(*mem_pool);
return ENOMEM;
}
(*mem_pool)->mempool = pool;
return 0;
}
void
cc_sal_mem_pool_destroy(sal_mem_pool_t* mem_pool)
{
mempool_destroy(mem_pool->mempool);
kmem_cache_destroy(mem_pool->cache);
SAL_FREE(mem_pool);
}
#ifdef _SAL_DEBUG
void*
cc_sal_mem_pool_alloc(sal_mem_pool_t* mem_pool, bool atomic,
const char* file, int line)
{
gfp_t gfp_mask;
void* ret;
if (atomic)
{
gfp_mask = GFP_ATOMIC;
}
else
{
gfp_mask = GFP_KERNEL;
}
ret = mempool_alloc(mem_pool->mempool, gfp_mask);
if (!ret)
{
cc_sal_log(SAL_LL_ERROR, file, line, "malloc failed!");
}
return(ret);
}
#else
void*
cc_sal_mem_pool_alloc(sal_mem_pool_t* mem_pool, bool atomic)
{
gfp_t gfp_mask;
if (atomic)
{
gfp_mask = GFP_ATOMIC;
}
else
{
gfp_mask = GFP_KERNEL;
}
return(mempool_alloc(mem_pool->mempool, gfp_mask));
}
#endif
void
cc_sal_mem_pool_free(sal_mem_pool_t* mem_pool, void* p)
{
mempool_free(p, mem_pool->mempool);
}
EXPORT_SYMBOL(sal_malloc);
EXPORT_SYMBOL(sal_free);
EXPORT_SYMBOL(cc_sal_malloc_failed);
下面的函数均可作为mempool_create的分配和释放的参数,但是最后一个参数不一样
* A commonly used alloc and free fn.
*/
void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data)
{
struct kmem_cache *mem = pool_data;
VM_BUG_ON(mem->ctor);
return kmem_cache_alloc(mem, gfp_mask);
}
EXPORT_SYMBOL(mempool_alloc_slab);
void mempool_free_slab(void *element, void *pool_data)
{
struct kmem_cache *mem = pool_data;
kmem_cache_free(mem, element);
}
EXPORT_SYMBOL(mempool_free_slab);
/*
* A commonly used alloc and free fn that kmalloc/kfrees the amount of memory
* specified by pool_data
*/
void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data)
{
size_t size = (size_t)pool_data;
return kmalloc(size, gfp_mask);
}
EXPORT_SYMBOL(mempool_kmalloc);
void mempool_kfree(void *element, void *pool_data)
{
kfree(element);
}
EXPORT_SYMBOL(mempool_kfree);
/*
* A simple mempool-backed page allocator that allocates pages
* of the order specified by pool_data.
*/
void *mempool_alloc_pages(gfp_t gfp_mask, void *pool_data)
{
int order = (int)(long)pool_data;
return alloc_pages(gfp_mask, order);
}
EXPORT_SYMBOL(mempool_alloc_pages);
void mempool_free_pages(void *element, void *pool_data)
{
int order = (int)(long)pool_data;
__free_pages(element, order);
}
EXPORT_SYMBOL(mempool_free_pages);