staticint kgsl_pool_max_pages;staticstructkgsl_page_pool kgsl_pools[6];voidkgsl_probe_page_pools(void){structdevice_node*node,*child;int index =0;// qcom,gpu-mempools节点
node =of_find_compatible_node(NULL,NULL,"qcom,gpu-mempools");if(!node)return;/* Get Max pages limit for mempool */// 获取最大页面限制of_property_read_u32(node,"qcom,mempool-max-pages",&kgsl_pool_max_pages);// 初始化kgsl pool缓存[见第2节]kgsl_pool_cache_init();// 遍历qcom,gpu-mempools下的每个节点for_each_child_of_node(node, child){// 解析每个qcom,gpu-mempool节点[见第3节]if(!kgsl_of_parse_mempool(&kgsl_pools[index], child))
index++;if(index ==ARRAY_SIZE(kgsl_pools)){of_node_put(child);break;}}// kgsl池的数量
kgsl_num_pools = index;of_node_put(node);/* Initialize shrinker */// 注册shrinker: 在内存紧张时触发回收[见第4节]register_shrinker(&kgsl_pool_shrinker);}
2. kgsl_pool_cache_init
// 支持kgsl pool排序#ifdefCONFIG_QCOM_KGSL_SORT_POOLstructkgsl_pool_page_entry{phys_addr_t physaddr;structpage*page;structrb_node node;};staticstructkmem_cache*addr_page_cache;/**
* struct kgsl_page_pool - Structure to hold information for the pool
* @pool_order: Page order describing the size of the page
* @page_count: Number of pages currently present in the pool
* @reserved_pages: Number of pages reserved at init for the pool
* @list_lock: Spinlock for page list in the pool
* @pool_rbtree: RB tree with all pages held/reserved in this pool
* @mempool: Mempool to pre-allocate tracking structs for pages in this pool
*/structkgsl_page_pool{unsignedint pool_order;unsignedint page_count;unsignedint reserved_pages;spinlock_t list_lock;// 通过基数树对池内持有/预留的页面进行排序structrb_root pool_rbtree;mempool_t*mempool;};staticvoid*_pool_entry_alloc(gfp_t gfp_mask,void*arg){returnkmem_cache_alloc(addr_page_cache, gfp_mask);}staticvoid_pool_entry_free(void*element,void*arg){returnkmem_cache_free(addr_page_cache, element);}staticint__kgsl_pool_add_page(structkgsl_page_pool*pool,structpage*p){structrb_node**node,*parent;structkgsl_pool_page_entry*new_page,*entry;gfp_t gfp_mask = GFP_KERNEL &~__GFP_DIRECT_RECLAIM;
new_page = pool->mempool ?mempool_alloc(pool->mempool, gfp_mask):kmem_cache_alloc(addr_page_cache, gfp_mask);if(new_page ==NULL)return-ENOMEM;spin_lock(&pool->list_lock);
node =&pool->pool_rbtree.rb_node;
new_page->physaddr =page_to_phys(p);
new_page->page = p;while(*node !=NULL){
parent =*node;
entry =rb_entry(parent,structkgsl_pool_page_entry, node);if(new_page->physaddr < entry->physaddr)
node =&parent->rb_left;else
node =&parent->rb_right;}rb_link_node(&new_page->node, parent, node);rb_insert_color(&new_page->node,&pool->pool_rbtree);
pool->page_count++;spin_unlock(&pool->list_lock);return0;}staticstructpage*__kgsl_pool_get_page(structkgsl_page_pool*pool){structrb_node*node;structkgsl_pool_page_entry*entry;structpage*p;
node =rb_first(&pool->pool_rbtree);if(!node)returnNULL;
entry =rb_entry(node,structkgsl_pool_page_entry, node);
p = entry->page;rb_erase(&entry->node,&pool->pool_rbtree);if(pool->mempool)mempool_free(entry, pool->mempool);elsekmem_cache_free(addr_page_cache, entry);
pool->page_count--;return p;}staticvoidkgsl_pool_list_init(structkgsl_page_pool*pool){// 初始化基数树
pool->pool_rbtree = RB_ROOT;}staticvoidkgsl_pool_cache_init(void){
addr_page_cache =KMEM_CACHE(kgsl_pool_page_entry,0);}staticvoidkgsl_pool_cache_destroy(void){kmem_cache_destroy(addr_page_cache);}staticvoidkgsl_destroy_page_pool(structkgsl_page_pool*pool){mempool_destroy(pool->mempool);}#else/**
* struct kgsl_page_pool - Structure to hold information for the pool
* @pool_order: Page order describing the size of the page
* @page_count: Number of pages currently present in the pool
* @reserved_pages: Number of pages reserved at init for the pool
* @list_lock: Spinlock for page list in the pool
* @page_list: List of pages held/reserved in this pool
*/structkgsl_page_pool{// 池子的阶数unsignedint pool_order;// 池子页面数量unsignedint page_count;// 池子预留的页面数量unsignedint reserved_pages;spinlock_t list_lock;// 通过链表对池内持有/预留页面进行管理structlist_head page_list;};staticint__kgsl_pool_add_page(structkgsl_page_pool*pool,structpage*p){spin_lock(&pool->list_lock);// 将页面插入表尾list_add_tail(&p->lru,&pool->page_list);// 池内页面数量加1
pool->page_count++;spin_unlock(&pool->list_lock);return0;}staticstructpage*__kgsl_pool_get_page(structkgsl_page_pool*pool){structpage*p;// 从表头取出页面
p =list_first_entry_or_null(&pool->page_list,structpage, lru);if(p){// 池内页面数量减1
pool->page_count--;// 将页面从池中移除list_del(&p->lru);}return p;}staticvoidkgsl_pool_list_init(structkgsl_page_pool*pool){INIT_LIST_HEAD(&pool->page_list);}staticvoidkgsl_pool_cache_init(void){}staticvoidkgsl_pool_cache_destroy(void){}staticvoidkgsl_destroy_page_pool(structkgsl_page_pool*pool){}#endif
3. kgsl_of_parse_mempool
staticintkgsl_of_parse_mempool(structkgsl_page_pool*pool,structdevice_node*node){
u32 size;int order;// 解析qcom,mempool-page-size:页面大小, 单位为字节if(of_property_read_u32(node,"qcom,mempool-page-size",&size))return-EINVAL;// 根据页面大小获取阶数
order =get_order(size);// 最大支持7阶页面if(order >8){pr_err("kgsl: %pOF: pool order %d is too big\n", node, order);return-EINVAL;}// 初始化池子阶数
pool->pool_order = order;spin_lock_init(&pool->list_lock);// 初始化池子链表kgsl_pool_list_init(pool);// 预留页面[见3.1节]kgsl_pool_reserve_pages(pool, node);return0;}
3.1 kgsl_pool_reserve_pages
staticvoidkgsl_pool_reserve_pages(structkgsl_page_pool*pool,structdevice_node*node){
u32 reserved =0;int i;// 解析qcom,mempool-reserved:预留的页面数量, 以4K的池子为例, 预留2048个页面of_property_read_u32(node,"qcom,mempool-reserved",&reserved);/* Limit the total number of reserved pages to 4096 */// 预留页面不能超过4096
pool->reserved_pages =min_t(u32, reserved,4096);#ifIS_ENABLED(CONFIG_QCOM_KGSL_SORT_POOL)/*
* Pre-allocate tracking structs for reserved_pages so that
* the pool can hold them even in low memory conditions
*/
pool->mempool =mempool_create(pool->reserved_pages,
_pool_entry_alloc, _pool_entry_free,NULL);#endiffor(i =0; i < pool->reserved_pages; i++){// 根据阶数初始化分配标志位[见3.1.1节]gfp_t gfp_mask =kgsl_gfp_mask(pool->pool_order);structpage*page;// 向伙伴系统申请分配对应阶数的物理页面
page =alloc_pages(gfp_mask, pool->pool_order);// 将页面加入池中[见3.1.2节]_kgsl_pool_add_page(pool, page);}}
3.1.1 kgsl_gfp_mask
/*
* The user can set this from debugfs to force failed memory allocations to
* fail without trying OOM first. This is a debug setting useful for
* stress applications that want to test failure cases without pushing the
* system into unrecoverable OOM panics
*/
bool kgsl_sharedmem_noretry_flag;gfp_tkgsl_gfp_mask(int page_order){gfp_t gfp_mask = __GFP_HIGHMEM;// 非0阶if(page_order >0){
gfp_mask |= __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN;// 禁止回收
gfp_mask &=~__GFP_RECLAIM;}else// 0阶
gfp_mask |= GFP_KERNEL;// 分配失败时禁止重试和警告if(kgsl_sharedmem_noretry_flag)
gfp_mask |= __GFP_NORETRY | __GFP_NOWARN;return gfp_mask;}
3.1.2 _kgsl_pool_add_page
/* Add a page to specified pool */staticvoid_kgsl_pool_add_page(structkgsl_page_pool*pool,structpage*p){if(!p)return;/*
* Sanity check to make sure we don't re-pool a page that
* somebody else has a reference to.
*/if(WARN_ON(unlikely(page_count(p)>1))){__free_pages(p, pool->pool_order);return;}// 如第2节所示: 将页面加入池中的基数树或者链表, 添加成功返回0if(__kgsl_pool_add_page(pool, p)){__free_pages(p, pool->pool_order);trace_kgsl_pool_free_page(pool->pool_order);return;}trace_kgsl_pool_add_page(pool->pool_order, pool->page_count);mod_node_page_state(page_pgdat(p), NR_KERNEL_MISC_RECLAIMABLE,(1<< pool->pool_order));}
intkgsl_get_page_size(size_t size,unsignedint align){size_t pool;// 遍历每个大于4K, 小于等于1M的内存池for(pool = SZ_1M; pool > PAGE_SIZE; pool >>=1)// 判断是否有对应的满足条件的kgsl池[见5.1节]if((align >=ilog2(pool))&&(size >= pool)&&kgsl_pool_available(pool))return pool;return PAGE_SIZE;}
5.1 kgsl_pool_available
/*
* Return true if the pool of specified page size is supported
* or no pools are supported otherwise return false.
*/static bool kgsl_pool_available(unsignedint page_size){// 确定阶数int order =get_order(page_size);// kgsl池个数if(!kgsl_num_pools)return true;// 根据阶数确认是否有对应的kgsl池return(kgsl_get_pool_index(order)>=0);}
5.2 kgsl_get_pool_index
/* Return the index of the pool for the specified order */staticintkgsl_get_pool_index(int order){int i;// 按阶数从小到大遍历每个kgsl池for(i =0; i < kgsl_num_pools; i++){// 传入的阶数如果与某个kgsl池的阶数一致, 则返回其索引if(kgsl_pools[i].pool_order == order)return i;}return-EINVAL;}
6. kgsl_pool_alloc_page
intkgsl_pool_alloc_page(int*page_size,structpage**pages,unsignedint pages_len,unsignedint*align,structdevice*dev){int j;int pcount =0;structkgsl_page_pool*pool;structpage*page =NULL;structpage*p =NULL;// 根据页面大小确认阶数int order =get_order(*page_size);int pool_idx;size_t size =0;if((pages ==NULL)|| pages_len <(*page_size >> PAGE_SHIFT))return-EINVAL;/* If the pool is not configured get pages from the system */// 如果未配置kgsl池, 则从系统分配物理页if(!kgsl_num_pools){gfp_t gfp_mask =kgsl_gfp_mask(order);
page =alloc_pages(gfp_mask, order);if(page ==NULL){/* Retry with lower order pages */if(order >0){
size = PAGE_SIZE <<--order;goto eagain;}elsereturn-ENOMEM;}trace_kgsl_pool_alloc_page_system(order);goto done;}// 从kgsl池获取[见6.1节]
pool =_kgsl_get_pool_from_order(order);if(pool ==NULL){/* Retry with lower order pages */// 高阶分配失败再从低阶进行重试[见6.2节]if(order >0){
size = PAGE_SIZE <<kgsl_pool_get_retry_order(order);goto eagain;}else{/*
* Fall back to direct allocation in case
* pool with zero order is not present
*/gfp_t gfp_mask =kgsl_gfp_mask(order);// 0阶页面直接从伙伴系统申请分配
page =alloc_pages(gfp_mask, order);if(page ==NULL)return-ENOMEM;trace_kgsl_pool_alloc_page_system(order);goto done;}}
pool_idx =kgsl_get_pool_index(order);// 从kgsl池获取page[见6.3阶]
page =_kgsl_pool_get_page(pool);/* Allocate a new page if not allocated from pool */// 从kgsl池申请分配失败if(page ==NULL){gfp_t gfp_mask =kgsl_gfp_mask(order);// 重新从伙伴系统申请分配
page =alloc_pages(gfp_mask, order);// 仍然分配失败if(!page){if(pool_idx >0){/* Retry with lower order pages */
size = PAGE_SIZE <<
kgsl_pools[pool_idx-1].pool_order;goto eagain;}elsereturn-ENOMEM;}trace_kgsl_pool_alloc_page_system(order);}
done:kgsl_zero_page(page, order, dev);// 返回结果for(j =0; j <(*page_size >> PAGE_SHIFT); j++){
p =nth_page(page, j);
pages[pcount]= p;
pcount++;}return pcount;
eagain:trace_kgsl_pool_try_page_lower(get_order(*page_size));*page_size =kgsl_get_page_size(size,ilog2(size));*align =ilog2(*page_size);return-EAGAIN;}
6.1 _kgsl_get_pool_from_order
/* Returns KGSL pool corresponding to input page order*/staticstructkgsl_page_pool*_kgsl_get_pool_from_order(int order){// 如5.2节所示, 返回阶数对应的kgsl池索引int index =kgsl_get_pool_index(order);// 返回索引指定的kgsl_page_poolreturn index >=0?&kgsl_pools[index]:NULL;}
/* Returns a page from specified pool */staticstructpage*_kgsl_pool_get_page(structkgsl_page_pool*pool){structpage*p =NULL;spin_lock(&pool->list_lock);// 从kgsl池的基数树或者链表中取出1个page
p =__kgsl_pool_get_page(pool);spin_unlock(&pool->list_lock);if(p !=NULL){trace_kgsl_pool_get_page(pool->pool_order, pool->page_count);mod_node_page_state(page_pgdat(p), NR_KERNEL_MISC_RECLAIMABLE,-(1<< pool->pool_order));}return p;}