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