memory pool 原理及使用

来源:互联网 发布:centos cp 复制文件夹 编辑:程序博客网 时间:2024/05/19 16:29

chipset: msm8x25

codebase: android4.1


一、初始化:int __init memory_pool_init(void){int i;alloc_root = RB_ROOT;mutex_init(&alloc_mutex);for (i = 0; i < ARRAY_SIZE(mpools); i++) {mutex_init(&mpools[i].pool_mutex);mpools[i].gpool = NULL;}return 0;}Mpools结构体如下,最多能存放8个,存放类型由平台自己决定:#define MAX_MEMPOOLS 8struct mem_pool mpools[MAX_MEMPOOLS];struct mem_pool {struct mutex pool_mutex;struct gen_pool *gpool;unsigned long paddr;//存放的是物理或者虚拟地址都可以。unsigned long size;//pool 的size大小。unsigned long free;//还有多少空闲部分可用。unsigned int id;};本平台定义的type如下:enum {MEMTYPE_NONE = -1,MEMTYPE_SMI_KERNEL = 0,MEMTYPE_SMI,MEMTYPE_EBI0,MEMTYPE_EBI1,MEMTYPE_MAX,};下面函数是和平台相关,其中调用了kernel中的initialize_memory_pool函数,当然自己使用的时候也可用按照这种写法:static void __init initialize_mempools(void){struct mem_pool *mpool;int memtype;struct memtype_reserve *mt;//保留内存相关信息,其实type为MEMTYPE_EBI0部分才有size,因为平台用的就是EBI1接口的DDR。mt = &reserve_info->memtype_reserve_table[0];for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) {if (!mt->size)continue;//依次将平台所用到的保留内存信息保存到mpool中。mpool = initialize_memory_pool(mt->start, mt->size, memtype);if (!mpool)pr_warning("failed to create %s mempool\n",memtype_name[memtype]);}}好了,看公共的函数initialize_memory_pool:struct mem_pool *initialize_memory_pool(unsigned long start,unsigned long size, int mem_type){int id = mem_type;//类型不符合或者size小于4k就返回if (id >= MAX_MEMPOOLS || size <= PAGE_SIZE || size % PAGE_SIZE)return NULL;mutex_lock(&mpools[id].pool_mutex);mpools[id].paddr = start;//保留内存的虚拟地址,注意是虚拟地址。mpools[id].size = size;//能使用的总sizempools[id].free = size;//空闲size,一开始肯定和总size一样。mpools[id].id = id;mutex_unlock(&mpools[id].pool_mutex);pr_info("memory pool %d (start %lx size %lx) initialized\n",id, start, size);return &mpools[id];}二、使用:平台提供了两种接口供我们分配mempool:allocate_contiguous_ebi 和 allocate_contiguous_ebi_nomap, 区别只在于是否map。void *allocate_contiguous_ebi(unsigned long size,unsigned long align, int cached){return allocate_contiguous_memory(size, get_ebi_memtype(),align, cached);}EXPORT_SYMBOL(allocate_contiguous_ebi);unsigned long allocate_contiguous_ebi_nomap(unsigned long size,unsigned long align){return _allocate_contiguous_memory_nomap(size, get_ebi_memtype(),align, __builtin_return_address(0));}EXPORT_SYMBOL(allocate_contiguous_ebi_nomap);static int get_ebi_memtype(void){/* on 7x30 and 8x55 "EBI1 kernel PMEM" is really on EBI0 */if (cpu_is_msm7x30() || cpu_is_msm8x55())return MEMTYPE_EBI0;//平台返回的是这个。return MEMTYPE_EBI1;}其实对应地就是调用了kernel的分配连续内存接口,就看allocate_contiguous_memory如何实现。void *allocate_contiguous_memory(unsigned long size,int mem_type, unsigned long align, int cached){//叶框对齐unsigned long aligned_size = PFN_ALIGN(size);struct mem_pool *mpool;mpool = mem_type_to_memory_pool(mem_type);if (!mpool)return NULL;return __alloc(mpool, aligned_size, align, cached,__builtin_return_address(0));}先看mem_type_to_memory_pool:static struct mem_pool *mem_type_to_memory_pool(int mem_type){//取得mem_type对应的mpool.struct mem_pool *mpool = &mpools[mem_type];//这里只有MEMTYPE_EBI1对应的size有赋值,所以其他的mpool都直接返回。if (!mpool->size)return NULL;mutex_lock(&mpool->pool_mutex);//初始化gpoolif (!mpool->gpool)mpool->gpool = initialize_gpool(mpool->paddr, mpool->size);mutex_unlock(&mpool->pool_mutex);if (!mpool->gpool)return NULL;return mpool;}static struct gen_pool *initialize_gpool(unsigned long start,unsigned long size){struct gen_pool *gpool;//先创建gpoolgpool = gen_pool_create(PAGE_SHIFT, -1);if (!gpool)return NULL;//添加gen pool if (gen_pool_add(gpool, start, size, -1)) {gen_pool_destroy(gpool);return NULL;}return gpool;}struct gen_pool *gen_pool_create(int min_alloc_order, int nid){struct gen_pool *pool;//比较简单,分配gen_pool空间。pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);if (pool != NULL) {spin_lock_init(&pool->lock);INIT_LIST_HEAD(&pool->chunks);// min_alloc_order为PAGE_SHIFT =12.pool->min_alloc_order = min_alloc_order;}return pool;}static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr,       size_t size, int nid){return gen_pool_add_virt(pool, addr, -1, size, nid);}int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phys, size_t size, int nid){struct gen_pool_chunk *chunk;//看意思是一个PAGE_SIZE作为一个bit来计算。int nbits = size >> pool->min_alloc_order;//nbits都存放在gen_pool_chunk的bits[0]数组中,用bitmap来管理。int nbytes = sizeof(struct gen_pool_chunk) +(nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE;//分配struct gen_pool_chunk空间。if (nbytes <= PAGE_SIZE)chunk = kmalloc_node(nbytes, __GFP_ZERO, nid);elsechunk = vmalloc(nbytes);if (unlikely(chunk == NULL))return -ENOMEM;if (nbytes > PAGE_SIZE)memset(chunk, 0, nbytes);chunk->phys_addr = phys;//保存物理地址,传进来的是-1,说明还没计算出来。chunk->start_addr = virt;//其实这个值是虚拟或者物理地址都可以。如果是//物理地址,就调用allocate_contiguous_memory,会ioremap一次。否则使用//_allocate_contiguous_memory_nomap就可以了。chunk->end_addr = virt + size;    //chuank结束地址。atomic_set(&chunk->avail, size);  //保存当前chunk有效size到avail中。spin_lock(&pool->lock);//以rcu的形式添加到pool的chunks列表中。list_add_rcu(&chunk->next_chunk, &pool->chunks); spin_unlock(&pool->lock);return 0;}再看__alloc,要动真格了:static void *__alloc(struct mem_pool *mpool, unsigned long size,unsigned long align, int cached, void *caller){unsigned long paddr;void __iomem *vaddr;unsigned long aligned_size;int log_align = ilog2(align);struct alloc *node;aligned_size = PFN_ALIGN(size);//从gen pool去分配内存。paddr = gen_pool_alloc_aligned(mpool->gpool, aligned_size, log_align);if (!paddr)return NULL;node = kmalloc(sizeof(struct alloc), GFP_KERNEL);if (!node)goto out;//这里返回的肯定是物理内存,所以需要ioremap,调用、//_allocate_contiguous_memory_nomap那就不需要了。if (cached)vaddr = ioremap_cached(paddr, aligned_size);elsevaddr = ioremap(paddr, aligned_size);if (!vaddr)goto out_kfree;node->vaddr = vaddr;//保留相对应参数到node节点中。node->paddr = paddr;node->len = aligned_size;node->mpool = mpool;node->caller = caller;//插入到红黑树中去管理。if (add_alloc(node))goto out_kfree;mpool->free -= aligned_size;return vaddr;out_kfree:if (vaddr)iounmap(vaddr);kfree(node);out:gen_pool_free(mpool->gpool, paddr, aligned_size);return NULL;}分析关键函数gen_pool_alloc_aligned:unsigned long gen_pool_alloc_aligned(struct gen_pool *pool, size_t size,     unsigned alignment_order){struct gen_pool_chunk *chunk;unsigned long addr = 0, align_mask = 0;int order = pool->min_alloc_order;int nbits, start_bit = 0, remain;#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHGBUG_ON(in_nmi());#endifif (size == 0)return 0;if (alignment_order > order)align_mask = (1 << (alignment_order - order)) - 1;//获取当前申请size所对应的nbits数量。nbits = (size + (1UL << order) - 1) >> order;rcu_read_lock();//在当前pool的chunks列表上依次查询list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {unsigned long chunk_size;if (size > atomic_read(&chunk->avail))continue;//本chunk所以拥有的总chunk size.chunk_size = (chunk->end_addr - chunk->start_addr) >> order;retry://寻找未被使用区域的start bit位置start_bit = bitmap_find_next_zero_area_off(chunk->bits, chunk_size,       0, nbits, align_mask,       chunk->start_addr);//如果超出chunk size,那么再看下一个chunk。if (start_bit >= chunk_size)continue;//没超出那就设置nbits的大小表示这部分内存已经被使用了remain = bitmap_set_ll(chunk->bits, start_bit, nbits);if (remain) {remain = bitmap_clear_ll(chunk->bits, start_bit, nbits - remain);BUG_ON(remain);goto retry;}//获取当前申请size对应的address,这里为物理地址。addr = chunk->start_addr + ((unsigned long)start_bit << order);size = nbits << pool->min_alloc_order;//计算还有多少size可以供其他进程申请。atomic_sub(size, &chunk->avail);break;}rcu_read_unlock();return addr;}对于bitmap如何使用,这里就不具体追踪了,看函数名知道大概就可以了。最后,我们看下_allocate_contiguous_memory_nomap,其实和上面的区别在于是否remap.unsigned long _allocate_contiguous_memory_nomap(unsigned long size,int mem_type, unsigned long align, void *caller){unsigned long paddr;unsigned long aligned_size;struct alloc *node;struct mem_pool *mpool;int log_align = ilog2(align);mpool = mem_type_to_memory_pool(mem_type);if (!mpool || !mpool->gpool)return 0;aligned_size = PFN_ALIGN(size);paddr = gen_pool_alloc_aligned(mpool->gpool, aligned_size, log_align);if (!paddr)return 0;node = kmalloc(sizeof(struct alloc), GFP_KERNEL);if (!node)goto out;node->paddr = paddr;/* We search the tree using node->vaddr, so set * it to something unique even though we don't * use it for physical allocation nodes. * The virtual and physical address ranges * are disjoint, so there won't be any chance of * a duplicate node->vaddr value. *///区别就在于这一步,因为这个函数传进来的就是虚拟地址,所以我们没必要再ioremap了,直接使用。node->vaddr = (void *)paddr;node->len = aligned_size;node->mpool = mpool;node->caller = caller;if (add_alloc(node))goto out_kfree;mpool->free -= aligned_size;return paddr;out_kfree:kfree(node);out:gen_pool_free(mpool->gpool, paddr, aligned_size);return 0;}Mempool还是比较简单的,后续的ION的使用我们就会看到它使用了mempool了。2013/01/23





原创粉丝点击