当前位置: 首页 > 工具软件 > mempool > 使用案例 >

mempool地使用

裴泰平
2023-12-01

内核 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);
 类似资料: