memory pool 原理及使用

chipset: msm8x25

codebase: android4.1


  1. 一、  初始化:  
  2.   
  3. int __init memory_pool_init(void)  
  4. {  
  5.     int i;  
  6.   
  7.     alloc_root = RB_ROOT;  
  8.     mutex_init(&alloc_mutex);  
  9.     for (i = 0; i < ARRAY_SIZE(mpools); i++) {  
  10.         mutex_init(&mpools[i].pool_mutex);  
  11.         mpools[i].gpool = NULL;  
  12.     }  
  13.   
  14.     return 0;  
  15. }  
  16.   
  17.   
  18. Mpools结构体如下,最多能存放8个,存放类型由平台自己决定:  
  19. #define MAX_MEMPOOLS 8  
  20. struct mem_pool mpools[MAX_MEMPOOLS];  
  21. struct mem_pool {  
  22.     struct mutex pool_mutex;  
  23.     struct gen_pool *gpool;  
  24.     unsigned long paddr;        //存放的是物理或者虚拟地址都可以。  
  25.     unsigned long size;         //pool 的size大小。  
  26.     unsigned long free;         //还有多少空闲部分可用。  
  27.     unsigned int id;  
  28. };  
  29.   
  30. 本平台定义的type如下:  
  31. enum {  
  32.     MEMTYPE_NONE = -1,  
  33.     MEMTYPE_SMI_KERNEL = 0,  
  34.     MEMTYPE_SMI,  
  35.     MEMTYPE_EBI0,  
  36.     MEMTYPE_EBI1,  
  37.     MEMTYPE_MAX,  
  38. };  
  39.   
  40. 下面函数是和平台相关,其中调用了kernel中的initialize_memory_pool函数,  
  41. 当然自己使用的时候也可用按照这种写法:  
  42. static void __init initialize_mempools(void)  
  43. {  
  44.     struct mem_pool *mpool;  
  45.     int memtype;  
  46.     struct memtype_reserve *mt;  
  47.   
  48.     //保留内存相关信息,其实type为MEMTYPE_EBI0部分才有size,  
  49. 因为平台用的就是EBI1接口的DDR。  
  50.     mt = &reserve_info->memtype_reserve_table[0];  
  51.     for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) {  
  52.         if (!mt->size)  
  53.             continue;  
  54.         //依次将平台所用到的保留内存信息保存到mpool中。  
  55.         mpool = initialize_memory_pool(mt->start, mt->size, memtype);  
  56.         if (!mpool)  
  57.             pr_warning("failed to create %s mempool\n",  
  58.                 memtype_name[memtype]);  
  59.     }  
  60. }  
  61.   
  62. 好了,看公共的函数initialize_memory_pool:  
  63. struct mem_pool *initialize_memory_pool(unsigned long start,  
  64.     unsigned long size, int mem_type)  
  65. {  
  66.     int id = mem_type;  
  67.   
  68.     //类型不符合或者size小于4k就返回  
  69.     if (id >= MAX_MEMPOOLS || size <= PAGE_SIZE || size % PAGE_SIZE)  
  70.         return NULL;  
  71.   
  72.     mutex_lock(&mpools[id].pool_mutex);  
  73.   
  74.     mpools[id].paddr = start;   //保留内存的虚拟地址,注意是虚拟地址。  
  75.     mpools[id].size = size;     //能使用的总size  
  76.     mpools[id].free = size;     //空闲size,一开始肯定和总size一样。  
  77.     mpools[id].id = id;  
  78.     mutex_unlock(&mpools[id].pool_mutex);  
  79.   
  80.     pr_info("memory pool %d (start %lx size %lx) initialized\n",  
  81.         id, start, size);  
  82.     return &mpools[id];  
  83. }  
  84.   
  85. 二、  使用:  
  86. 平台提供了两种接口供我们分配mempool:allocate_contiguous_ebi 和 allocate_contiguous_ebi_nomap, 区别只在于是否map。  
  87. void *allocate_contiguous_ebi(unsigned long size,  
  88.     unsigned long align, int cached)  
  89. {  
  90.     return allocate_contiguous_memory(size, get_ebi_memtype(),  
  91.         align, cached);  
  92. }  
  93. EXPORT_SYMBOL(allocate_contiguous_ebi);  
  94.   
  95. unsigned long allocate_contiguous_ebi_nomap(unsigned long size,  
  96.     unsigned long align)  
  97. {  
  98.     return _allocate_contiguous_memory_nomap(size, get_ebi_memtype(),  
  99.         align, __builtin_return_address(0));  
  100. }  
  101. EXPORT_SYMBOL(allocate_contiguous_ebi_nomap);  
  102.   
  103. static int get_ebi_memtype(void)  
  104. {  
  105.     /* on 7x30 and 8x55 "EBI1 kernel PMEM" is really on EBI0 */  
  106.     if (cpu_is_msm7x30() || cpu_is_msm8x55())  
  107.         return MEMTYPE_EBI0;  
  108.     //平台返回的是这个。  
  109.     return MEMTYPE_EBI1;  
  110. }  
  111. 其实对应地就是调用了kernel的分配连续内存接口,就看allocate_contiguous_memory如何实现。  
  112.   
  113. void *allocate_contiguous_memory(unsigned long size,  
  114.     int mem_type, unsigned long align, int cached)  
  115. {  
  116.     //叶框对齐  
  117.     unsigned long aligned_size = PFN_ALIGN(size);  
  118.     struct mem_pool *mpool;  
  119.   
  120.     mpool = mem_type_to_memory_pool(mem_type);  
  121.     if (!mpool)  
  122.         return NULL;  
  123.     return __alloc(mpool, aligned_size, align, cached,  
  124.         __builtin_return_address(0));  
  125.   
  126. }  
  127.   
  128. 先看mem_type_to_memory_pool:  
  129. static struct mem_pool *mem_type_to_memory_pool(int mem_type)  
  130. {  
  131.     //取得mem_type对应的mpool.  
  132.     struct mem_pool *mpool = &mpools[mem_type];  
  133.   
  134.     //这里只有MEMTYPE_EBI1对应的size有赋值,  
  135. 所以其他的mpool都直接返回。  
  136.     if (!mpool->size)  
  137.         return NULL;  
  138.   
  139.     mutex_lock(&mpool->pool_mutex);  
  140.     //初始化gpool  
  141.     if (!mpool->gpool)  
  142.         mpool->gpool = initialize_gpool(mpool->paddr, mpool->size);  
  143.     mutex_unlock(&mpool->pool_mutex);  
  144.     if (!mpool->gpool)  
  145.         return NULL;  
  146.   
  147.     return mpool;  
  148. }  
  149.   
  150. static struct gen_pool *initialize_gpool(unsigned long start,  
  151.     unsigned long size)  
  152. {  
  153.     struct gen_pool *gpool;  
  154.   
  155.     //先创建gpool  
  156.     gpool = gen_pool_create(PAGE_SHIFT, -1);  
  157.   
  158.     if (!gpool)  
  159.         return NULL;  
  160.     //添加gen pool   
  161.     if (gen_pool_add(gpool, start, size, -1)) {  
  162.         gen_pool_destroy(gpool);  
  163.         return NULL;  
  164.     }  
  165.   
  166.     return gpool;  
  167. }  
  168.   
  169. struct gen_pool *gen_pool_create(int min_alloc_order, int nid)  
  170. {  
  171.     struct gen_pool *pool;  
  172.     //比较简单,分配gen_pool空间。  
  173.     pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);  
  174.     if (pool != NULL) {  
  175.         spin_lock_init(&pool->lock);  
  176.         INIT_LIST_HEAD(&pool->chunks);  
  177.         // min_alloc_order为PAGE_SHIFT =12.  
  178.         pool->min_alloc_order = min_alloc_order;  
  179.     }  
  180.     return pool;  
  181. }  
  182.   
  183. static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr,  
  184.                    size_t size, int nid)  
  185. {  
  186.     return gen_pool_add_virt(pool, addr, -1, size, nid);  
  187. }  
  188.   
  189. int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phys,  
  190.          size_t size, int nid)  
  191. {  
  192.     struct gen_pool_chunk *chunk;  
  193.     //看意思是一个PAGE_SIZE作为一个bit来计算。  
  194.     int nbits = size >> pool->min_alloc_order;  
  195.     //nbits都存放在gen_pool_chunk的bits[0]数组中,用bitmap来管理。  
  196.     int nbytes = sizeof(struct gen_pool_chunk) +  
  197.                 (nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE;  
  198.   
  199.     //分配struct gen_pool_chunk空间。  
  200.     if (nbytes <= PAGE_SIZE)  
  201.         chunk = kmalloc_node(nbytes, __GFP_ZERO, nid);  
  202.     else  
  203.         chunk = vmalloc(nbytes);  
  204.     if (unlikely(chunk == NULL))  
  205.         return -ENOMEM;  
  206.     if (nbytes > PAGE_SIZE)  
  207.         memset(chunk, 0, nbytes);  
  208.   
  209.     chunk->phys_addr = phys; //保存物理地址,传进来的是-1,说明还没计算出来。  
  210.     chunk->start_addr = virt;    //其实这个值是虚拟或者物理地址都可以。如果是//物理地址,就调用allocate_contiguous_memory,会ioremap一次。否则使用//_allocate_contiguous_memory_nomap就可以了。  
  211.     chunk->end_addr = virt + size;    //chuank结束地址。  
  212.     atomic_set(&chunk->avail, size);  //保存当前chunk有效size到avail中。  
  213.   
  214.     spin_lock(&pool->lock);  
  215. //以rcu的形式添加到pool的chunks列表中。  
  216.   
  217.     list_add_rcu(&chunk->next_chunk, &pool->chunks);                   spin_unlock(&pool->lock);  
  218.   
  219.     return 0;  
  220. }  
  221.   
  222. 再看__alloc,要动真格了:  
  223. static void *__alloc(struct mem_pool *mpool, unsigned long size,  
  224.     unsigned long align, int cached, void *caller)  
  225. {  
  226.     unsigned long paddr;  
  227.     void __iomem *vaddr;  
  228.   
  229.     unsigned long aligned_size;  
  230.     int log_align = ilog2(align);  
  231.   
  232.     struct alloc *node;  
  233.   
  234.     aligned_size = PFN_ALIGN(size);  
  235.     //从gen pool去分配内存。  
  236.     paddr = gen_pool_alloc_aligned(mpool->gpool, aligned_size, log_align);  
  237.     if (!paddr)  
  238.         return NULL;  
  239.   
  240.     node = kmalloc(sizeof(struct alloc), GFP_KERNEL);  
  241.     if (!node)  
  242.         goto out;  
  243.   
  244.     //这里返回的肯定是物理内存,所以需要ioremap,调用、//_allocate_contiguous_memory_nomap那就不需要了。  
  245.     if (cached)  
  246.         vaddr = ioremap_cached(paddr, aligned_size);  
  247.     else  
  248.         vaddr = ioremap(paddr, aligned_size);  
  249.   
  250.     if (!vaddr)  
  251.         goto out_kfree;  
  252.   
  253.     node->vaddr = vaddr;  
  254.     //保留相对应参数到node节点中。  
  255.     node->paddr = paddr;  
  256.     node->len = aligned_size;  
  257.     node->mpool = mpool;  
  258.     node->caller = caller;  
  259.     //插入到红黑树中去管理。  
  260.     if (add_alloc(node))  
  261.         goto out_kfree;  
  262.   
  263.     mpool->free -aligned_size;  
  264.   
  265.     return vaddr;  
  266. out_kfree:  
  267.     if (vaddr)  
  268.         iounmap(vaddr);  
  269.     kfree(node);  
  270. out:  
  271.     gen_pool_free(mpool->gpool, paddr, aligned_size);  
  272.     return NULL;  
  273. }  
  274.   
  275. 分析关键函数gen_pool_alloc_aligned:  
  276. unsigned long gen_pool_alloc_aligned(struct gen_pool *pool, size_t size,  
  277.                      unsigned alignment_order)  
  278. {  
  279.     struct gen_pool_chunk *chunk;  
  280.     unsigned long addr = 0align_mask = 0;  
  281.     int order = pool->min_alloc_order;  
  282.     int nbits, start_bit = 0, remain;  
  283.   
  284. #ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG  
  285.     BUG_ON(in_nmi());  
  286. #endif  
  287.   
  288.     if (size == 0)  
  289.         return 0;  
  290.   
  291.     if (alignment_order > order)  
  292.         align_mask = (1 << (alignment_order - order)) - 1;  
  293.       
  294.     //获取当前申请size所对应的nbits数量。  
  295.     nbits = (size + (1UL << order) - 1) >> order;  
  296.   
  297.     rcu_read_lock();  
  298.     //在当前pool的chunks列表上依次查询  
  299.     list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {  
  300.         unsigned long chunk_size;  
  301.         if (size > atomic_read(&chunk->avail))  
  302.             continue;  
  303.         //本chunk所以拥有的总chunk size.  
  304.         chunk_size = (chunk->end_addr - chunk->start_addr) >> order;  
  305.   
  306. retry:  
  307.         //寻找未被使用区域的start bit位置  
  308.         start_bit = bitmap_find_next_zero_area_off(chunk->bits, chunk_size,  
  309.                                0, nbits, align_mask,  
  310.                                chunk->start_addr);  
  311.         //如果超出chunk size,那么再看下一个chunk。  
  312.         if (start_bit >= chunk_size)  
  313.             continue;  
  314.         //没超出那就设置nbits的大小表示这部分内存已经被使用了  
  315.         remain = bitmap_set_ll(chunk->bits, start_bit, nbits);  
  316.         if (remain) {  
  317.             remain = bitmap_clear_ll(chunk->bits, start_bit,  
  318.                          nbits - remain);  
  319.             BUG_ON(remain);  
  320.             goto retry;  
  321.         }  
  322.   
  323.         //获取当前申请size对应的address,这里为物理地址。  
  324.         addr = chunk->start_addr + ((unsigned long)start_bit << order);  
  325.         size = nbits << pool->min_alloc_order;  
  326.         //计算还有多少size可以供其他进程申请。  
  327.         atomic_sub(size, &chunk->avail);  
  328.         break;  
  329.     }  
  330.     rcu_read_unlock();  
  331.     return addr;  
  332. }  
  333.   
  334. 对于bitmap如何使用,这里就不具体追踪了,看函数名知道大概就可以了。  
  335.   
  336.   
  337.   
  338. 最后,我们看下_allocate_contiguous_memory_nomap,其实和上面的区别在于是否remap.  
  339. unsigned long _allocate_contiguous_memory_nomap(unsigned long size,  
  340.     int mem_type, unsigned long align, void *caller)  
  341. {  
  342.     unsigned long paddr;  
  343.     unsigned long aligned_size;  
  344.   
  345.     struct alloc *node;  
  346.     struct mem_pool *mpool;  
  347.     int log_align = ilog2(align);  
  348.   
  349.     mpool = mem_type_to_memory_pool(mem_type);  
  350.     if (!mpool || !mpool->gpool)  
  351.         return 0;  
  352.   
  353.     aligned_size = PFN_ALIGN(size);  
  354.     paddr = gen_pool_alloc_aligned(mpool->gpool, aligned_size, log_align);  
  355.     if (!paddr)  
  356.         return 0;  
  357.   
  358.     node = kmalloc(sizeof(struct alloc), GFP_KERNEL);  
  359.     if (!node)  
  360.         goto out;  
  361.   
  362.     node->paddr = paddr;  
  363.   
  364.     /* We search the tree using node->vaddr, so set  
  365.      * it to something unique even though we don't  
  366.      * use it for physical allocation nodes.  
  367.      * The virtual and physical address ranges  
  368.      * are disjoint, so there won't be any chance of  
  369.      * a duplicate node->vaddr value.  
  370.      */  
  371.     //区别就在于这一步,因为这个函数传进来的就是虚拟地址,所以我们没必要再ioremap了,直接使用。  
  372.     node->vaddr = (void *)paddr;  
  373.     node->len = aligned_size;  
  374.     node->mpool = mpool;  
  375.     node->caller = caller;  
  376.     if (add_alloc(node))  
  377.         goto out_kfree;  
  378.   
  379.     mpool->free -aligned_size;  
  380.     return paddr;  
  381. out_kfree:  
  382.     kfree(node);  
  383. out:  
  384.     gen_pool_free(mpool->gpool, paddr, aligned_size);  
  385.     return 0;  
  386. }  
  387.   
  388.   
  389. Mempool还是比较简单的,后续的ION的使用我们就会看到它使用了mempool了。  
  390.   
  391.   
  392. 2013/01/23 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值