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 8 struct 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; //能使用的总size mpools[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); //初始化gpool if (!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; //先创建gpool gpool = 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); else chunk = 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); else vaddr = 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_CMPXCHG BUG_ON(in_nmi()); #endif if (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