kgsl pool

Posted bubbleben

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kgsl pool相关的知识,希望对你有一定的参考价值。

&soc 
    ....
    msm_gpu: qcom,kgsl-3d0@2C00000 
        label = "kgsl-3d0";
        compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
        status = "ok";
        reg = <0x2c00000 0x40000>, <0x2c61000 0x800>,
                <0x6900000 0x44000>, <0x780000 0x6fff>;
        reg-names = "kgsl_3d0_reg_memory", "cx_dbgc",
                    "qdss_gfx", "qfprom_memory";
        interrupts = <0 300 0>;
        interrupts-names = "kgsl_3d0_irq";
        qcom,id = <0>;

        qcom,chipid = <>;

        qcom,initial-pwrlevel = <5>;
    
        ...

        /* GPU Mempools */
        // kgsl pool配置
        qcom,gpu-mempools 
            #address-cells = <1>;
            #size-cells = <0>;
            compatible = "qcom,gpu-mempools";

            /* 4K Page Pool configuration */
            qcom,gpu-mempool@0 
                reg = <0>;
                qcom,mempool-page-size = <4096>;
                qcom,mempool-reserved = <2048>;
                qcom,mempool-allocate;
            ;

            /* 8K Page Pool configuration */
            qcom,gpu-mempool@1 
                reg = <1>;
                qcom,mempool-page-size = <8192>;
                qcom,mempool-reserved = <1024>;
                qcom,mempool-allocate;
            ;

            /* 64K Page Pool configuration */
            qcom,gpu-mempool@2 
                reg = <2>;
                qcom,mempool-page-size = <65536>;
                qcom,mempool-reserved = <256>;
                qcom,mempool-allocate;
            ;

            /* 1M Page Pool configuration */
            qcom,gpu-mempool@3 
                reg = <3>;
                qcom,mempool-page-size = <1048576>;
                qcom,mempool-reserved = <32>;
            ;

            ...
         
    

1. kgsl_probe_page_pools

static int kgsl_pool_max_pages;
static struct kgsl_page_pool kgsl_pools[6];

void kgsl_probe_page_pools(void)

	struct device_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排序
#ifdef CONFIG_QCOM_KGSL_SORT_POOL

struct kgsl_pool_page_entry 
	phys_addr_t physaddr;
	struct page *page;
	struct rb_node node;
;

static struct kmem_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
 */
struct kgsl_page_pool 
	unsigned int pool_order;
	unsigned int page_count;
	unsigned int reserved_pages;
	spinlock_t list_lock;
    // 通过基数树对池内持有/预留的页面进行排序
	struct rb_root pool_rbtree;
	mempool_t *mempool;
;

static void *_pool_entry_alloc(gfp_t gfp_mask, void *arg)

	return kmem_cache_alloc(addr_page_cache, gfp_mask);


static void _pool_entry_free(void *element, void *arg)

	return kmem_cache_free(addr_page_cache, element);


static int
__kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p)

	struct rb_node **node, *parent;
	struct kgsl_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, struct kgsl_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);

	return 0;


static struct page *
__kgsl_pool_get_page(struct kgsl_page_pool *pool)

	struct rb_node *node;
	struct kgsl_pool_page_entry *entry;
	struct page *p;

	node = rb_first(&pool->pool_rbtree);
	if (!node)
		return NULL;

	entry = rb_entry(node, struct kgsl_pool_page_entry, node);
	p = entry->page;
	rb_erase(&entry->node, &pool->pool_rbtree);
	if (pool->mempool)
		mempool_free(entry, pool->mempool);
	else
		kmem_cache_free(addr_page_cache, entry);
	pool->page_count--;
	return p;


static void kgsl_pool_list_init(struct kgsl_page_pool *pool)

    // 初始化基数树
	pool->pool_rbtree = RB_ROOT;


static void kgsl_pool_cache_init(void)

	addr_page_cache =  KMEM_CACHE(kgsl_pool_page_entry, 0);


static void kgsl_pool_cache_destroy(void)

	kmem_cache_destroy(addr_page_cache);


static void kgsl_destroy_page_pool(struct kgsl_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
 */
struct kgsl_page_pool 
    // 池子的阶数
	unsigned int pool_order;
    // 池子页面数量
	unsigned int page_count;
    // 池子预留的页面数量
	unsigned int reserved_pages;
	spinlock_t list_lock;
    // 通过链表对池内持有/预留页面进行管理
	struct list_head page_list;
;

static int
__kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p)

	spin_lock(&pool->list_lock);
    // 将页面插入表尾
	list_add_tail(&p->lru, &pool->page_list);
    // 池内页面数量加1
	pool->page_count++;
	spin_unlock(&pool->list_lock);

	return 0;


static struct page *
__kgsl_pool_get_page(struct kgsl_page_pool *pool)

	struct page *p;

    // 从表头取出页面
	p = list_first_entry_or_null(&pool->page_list, struct page, lru);
	if (p) 
        // 池内页面数量减1
		pool->page_count--;
        // 将页面从池中移除
		list_del(&p->lru);
	

	return p;


static void kgsl_pool_list_init(struct kgsl_page_pool *pool)

	INIT_LIST_HEAD(&pool->page_list);


static void kgsl_pool_cache_init(void)



static void kgsl_pool_cache_destroy(void)



static void kgsl_destroy_page_pool(struct kgsl_page_pool *pool)


#endif

3. kgsl_of_parse_mempool

static int kgsl_of_parse_mempool(struct kgsl_page_pool *pool,
		struct device_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);

	return 0;

3.1 kgsl_pool_reserve_pages

static void kgsl_pool_reserve_pages(struct kgsl_page_pool *pool,
		struct device_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);

#if IS_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);
#endif

	for (i = 0; i < pool->reserved_pages; i++) 
        // 根据阶数初始化分配标志位[见3.1.1节]
		gfp_t gfp_mask = kgsl_gfp_mask(pool->pool_order);
		struct page *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_t kgsl_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 */
static void
_kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *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节所示: 将页面加入池中的基数树或者链表, 添加成功返回0
	if (__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));

4. kgsl_pool_shrinker

/* Shrinker callback data*/
static struct shrinker kgsl_pool_shrinker = 
    // 计算扫描的页面
	.count_objects = kgsl_pool_shrink_count_objects,
    // 回收页面
	.scan_objects = kgsl_pool_shrink_scan_objects,
	.seeks = DEFAULT_SEEKS,
	.batch = 0,
;

5. kgsl_get_page_size

int kgsl_get_page_size(size_t size, unsigned int 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(unsigned int 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 */
static int kgsl_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

int kgsl_pool_alloc_page(int *page_size, struct page **pages,
			unsigned int pages_len, unsigned int *align,
			struct device *dev)

	int j;
	int pcount = 0;
	struct kgsl_page_pool *pool;
	struct page *page = NULL;
	struct page *p = NULL;
    // 根据页面大小确认阶数
	int order = get_order(以上是关于kgsl pool的主要内容,如果未能解决你的问题,请参考以下文章

php-fpm的pool池子php慢日志记录open_basedirphp-fpm进程管理

php-fpm的pool

对象池模式(Object Pool Pattern)

golang sync.Pool的用法及实现

12.21 php-fpm的pool 12.22 php-fpm慢执行日志 12.23 open_basedir 12.24 php-fpm进程管理

DHCP拓扑图