当前位置: 首页 > 工具软件 > ION > 使用案例 >

android内存管理之ion

廖琪
2023-12-01

参考:android内存管理之ion - 知乎

驱动代码分析:

static struct of_device_id tcc_ion_of_match[] = {
	{ .compatible = "telechips,tcc-ion" },
	{},
};

static struct platform_driver tcc_ion_driver = {
	.probe = tcc_ion_probe,
	.driver = {
		.of_match_table = of_match_ptr(tcc_ion_of_match),
	},
};
int tcc_ion_probe(struct platform_device *pdev)
{
	struct ion_platform_data *pdata;
	int i;

	if (pdev->dev.of_node) {
		pdata = tcc_ion_parse_dt(pdev);
	} 

	num_heaps = pdata->nr;

	heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL);

	/* create the heaps as specified in the board file */
	for (i = 0; i < num_heaps; i++) {
		struct ion_platform_heap *heap_data = &pdata->heaps[i];
		heaps[i] = ion_heap_create(heap_data);
		ion_device_add_heap(heaps[i]);
	}

	return 0;
}

结构体定义:

struct ion_platform_data {
	int nr;
	struct ion_platform_heap *heaps;
};

/**
 * struct ion_platform_heap - defines a heap in the given platform
 * @type:	type of the heap from ion_heap_type enum
 * @id:		unique identifier for heap. 
 * @name:	used for debug purposes
 * @base:	base address of heap in physical memory if applicable
 * @size:	size of the heap in bytes if applicable
 * @priv:	private info passed from the board file
 *
 * Provided by the board file.
 */
struct ion_platform_heap {
	enum ion_heap_type type;
	unsigned int id;
	const char *name;
	phys_addr_t base;
	size_t size;
	phys_addr_t align;
	void *priv;
};

 设备树节点设置:

        telechips,ion {
                compatible = "telechips,tcc-ion";

                ion-heap@2 {
                        reg = <2>;
                        telechips,ion-heap-name = "ion_carveout_heap";
                };

                ion-heap@3 {
                         reg = <3>;
                         telechips,ion-heap-name = "ion_carveout_cam_heap";
                };
        };


         pmap_ump_reserved: ump_reserved {
                 compatible = "telechips,pmap";
                 telechips,pmap-name = "ump_reserved";
                 size = <PMAP_SIZE_UMP_RESERVED>; 
         };

tcc_ion_parse_dt函数,解析设备树: 

static struct ion_platform_data *tcc_ion_parse_dt(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	struct device_node *child;
	struct ion_platform_data *pdata;
	int index = 0;
	u32 val;

	num_heaps = 0;
	for_each_child_of_node(node, child)
		++num_heaps;

	pdata = devm_kzalloc(&pdev->dev, sizeof(struct ion_platform_data),
			     GFP_KERNEL);

	pdata->heaps = devm_kzalloc(&pdev->dev,num_heaps * sizeof(struct ion_platform_heap),
			GFP_KERNEL);
 
	for_each_child_of_node(node, child) {
		struct ion_platform_heap *heap = &pdata->heaps[index];

		of_property_read_u32(child, "reg", &val);

		heap->id = index;
		heap->type = val;

		of_property_read_string(child,"telechips,ion-heap-name", &heap->name);

		if(heap->type == ION_HEAP_TYPE_CARVEOUT)
		{
			if(0 > pmap_get_info("ump_reserved", &pmap_ump_reserved)){
				return ERR_PTR(-ENOMEM);
			}

			heap->base = pmap_ump_reserved.base;
			heap->size = pmap_ump_reserved.size;
        }
        .....	
		++index;
	}

	pdata->nr = num_heaps;

	return pdata;
}

ion_heap_create函数:

struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
{
	struct ion_heap *heap = NULL;

	switch (heap_data->type) {
#ifdef CONFIG_ION_CARVEOUT_HEAP
	case ION_HEAP_TYPE_CARVEOUT:
		heap = ion_carveout_heap_create(heap_data);
		break;
#endif		
    ......
	default:
		return ERR_PTR(-EINVAL);
	}

	heap->name = heap_data->name;
	heap->id = heap_data->id;
	return heap;
}

ion_carveout_heap_create函数:

struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
	struct ion_carveout_heap *carveout_heap;
	struct page *page;
	size_t size;
	int ret;

	page = pfn_to_page(PFN_DOWN(heap_data->base));
	size = heap_data->size;

	ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));

	carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL);

	carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1);

	carveout_heap->base = heap_data->base;
	gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,-1);
	carveout_heap->heap.ops = &carveout_heap_ops;
	carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
    ......
	return &carveout_heap->heap;
}

结构体定义:

struct ion_carveout_heap {
	struct ion_heap heap;
	struct gen_pool *pool;
	phys_addr_t base;
    ......
};

//函数指针
typedef unsigned long (*genpool_algo_t)(unsigned long *map,unsigned long size,
	        unsigned long start, unsigned int nr,void *data, struct gen_pool *pool);

/*General purpose special memory pool descriptor.*/
struct gen_pool {
	struct list_head chunks;	/* list of chunks in this pool */
	int min_alloc_order;		/* minimum allocation order */
	genpool_algo_t algo;		/* allocation function */
	void *data;
	const char *name;
    ......
};

struct ion_heap {
	struct plist_node node;
	struct ion_device *dev;
	enum ion_heap_type type;
	struct ion_heap_ops *ops;
	unsigned long flags;
	unsigned int id;
	const char *name;
	struct shrinker shrinker;
	struct list_head free_list;
	size_t free_list_size;
	wait_queue_head_t waitqueue;
	struct task_struct *task;

	int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
};


/*General purpose special memory pool chunk descriptor.*/
struct gen_pool_chunk {
	struct list_head next_chunk;	/* next chunk in pool */
	atomic_long_t avail;
	phys_addr_t phys_addr;		/* physical starting address of memory chunk */
	unsigned long start_addr;	/* start address of memory chunk */
	unsigned long end_addr;		/* end address of memory chunk (inclusive) */
	unsigned long bits[0];		/* bitmap for allocating memory chunk */
};

gen_pool_create函数:分配一个内存池

struct gen_pool *gen_pool_create(int min_alloc_order, int nid)
{
	struct gen_pool *pool;

	pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);
	if (pool != NULL) {
		spin_lock_init(&pool->lock);
		INIT_LIST_HEAD(&pool->chunks);
		pool->min_alloc_order = min_alloc_order;
		pool->algo = gen_pool_first_fit;
		pool->data = NULL;
		pool->name = NULL;
	}
	return pool;
}

gen_pool_first_fit函数:查找第一块满足要求的可用内存区域。

/*
 * @map: The address to base the search on
 * @size: The bitmap size in bits
 * @start: The bitnumber to start searching at
 * @nr: The number of zeroed bits we're looking for
 * @data: additional data - unused
 * @pool: pool to find the fit region memory from
 */
unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
		unsigned long start, unsigned int nr, void *data,struct gen_pool *pool)
{
	return bitmap_find_next_zero_area(map, size, start, nr, 0);
}
static inline unsigned long bitmap_find_next_zero_area(unsigned long *map,
			   unsigned long size,
			   unsigned long start,
			   unsigned int nr,
			   unsigned long align_mask)
{
	return bitmap_find_next_zero_area_off(map, size, start, nr, align_mask, 0);
}
/**
 * bitmap_find_next_zero_area_off - find a contiguous aligned zero area
 * @map: The address to base the search on
 * @size: The bitmap size in bits
 * @start: The bitnumber to start searching at
 * @nr: The number of zeroed bits we're looking for
 * @align_mask: Alignment mask for zero area
 * @align_offset: Alignment offset for zero area.
 */
unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
					     unsigned long size,
					     unsigned long start,
					     unsigned int nr,
					     unsigned long align_mask,
					     unsigned long align_offset)
{
	unsigned long index, end, i;

again:
    //以位图的map为基地址、start为起点、大小为size,开始查找第一个是0的比特值的索引下标
	index = find_next_zero_bit(map, size, start);
    //从该下标开始连续nr个bit都是0
	end = index + nr;
     //以位图的map为基地址、index为起点、大小为end,开始查找第一个是1的比特值的索引下标
	i = find_next_bit(map, end, index);
	if (i < end) { //如果i小于end,表示以index为起点、大小为nr个bit的范围内有比特值为1的值
		start = i + 1;
		goto again;
	}
	return index; //表示以index为起点、大小为nr个bit的范围内都是0
}

first fit:

First Fit分配器是最基本的内存分配器,它使用bitmap而不是空闲块列表来表示内存。在bitmap中,如果page对应位为1,则表示此page已经被分配,为0则表示此page没有被分配。为了分配小于一个page的内存块,First Fit分配器记录了最后被分配的PFN (Page Frame Number)和分配的结束地址在页内的偏移量。随后小的内存分配被Merge到一起并存储到同一页中。

First Fit分配器不会造成严重的内存碎片,但其效率较低,由于内存经常通过线性地址进行search,而First Fit中的小块内存经常在物理内存的开始处,为了分配大块内存而不得不扫描前面大量的内存。

参考:安全验证 - 知乎

gen_pool_add函数:

/**
 * gen_pool_add - add a new chunk of special memory to the pool
 * @pool: pool to add new memory chunk to
 * @addr: starting address of memory chunk to add to pool
 * @size: size in bytes of the memory chunk to add to pool
 * @nid: node id of the node the chunk structure and bitmap should be
 *       allocated on, or -1
 */
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);
}
/**
 * gen_pool_add_virt - add a new chunk of special memory to the pool
 * @pool: pool to add new memory chunk to
 * @virt: virtual starting address of memory chunk to add to pool
 * @phys: physical starting address of memory chunk to add to pool
 * @size: size in bytes of the memory chunk to add to pool
 * @nid: node id of the node the chunk structure and bitmap should be
 *       allocated on, or -1
 */
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;
    // pool->min_alloc_order等于PAGE_SHIFT,用位图表示:一个page对应1个bit,
    //大小为size的内存有多少page,即有多少bit位
	int nbits = size >> pool->min_alloc_order;
    //BITS_TO_LONGS(nbits) * sizeof(long):
    //nbits个bit数转换成long的数量*每个long占的字节数,计算出bitmap占的字节数
	int nbytes = sizeof(struct gen_pool_chunk) +
				BITS_TO_LONGS(nbits) * sizeof(long);

	chunk = kzalloc_node(nbytes, GFP_KERNEL, nid);

	chunk->phys_addr = phys;
	chunk->start_addr = virt;
	chunk->end_addr = virt + size - 1;
	atomic_long_set(&chunk->avail, size);
    //将内存块添加到内存池的链表中
	list_add_rcu(&chunk->next_chunk, &pool->chunks);

	return 0;
}
struct gen_pool_chunk {
	struct list_head next_chunk;	/* next chunk in pool */
	atomic_long_t avail;
	phys_addr_t phys_addr;		/* physical starting address of memory chunk */
	unsigned long start_addr;	/* start address of memory chunk */
	unsigned long end_addr;		/* end address of memory chunk (inclusive) */
	unsigned long bits[0];		/* bitmap for allocating memory chunk */
};

carveout_heap_ops的实现: 

static struct ion_heap_ops carveout_heap_ops = {
	.allocate = ion_carveout_heap_allocate,
	.free = ion_carveout_heap_free,
	.map_user = ion_heap_map_user,
	.map_kernel = ion_heap_map_kernel,
	.unmap_kernel = ion_heap_unmap_kernel,
};

重点关注ion_carveout_heap_allocate函数,该函数的实现在下面会连续起来一起讲。

ion_device_add_heap函数:将堆添加到ion设备的链表上

void ion_device_add_heap(struct ion_heap *heap)
{
	struct ion_device *dev = internal_dev;

	heap->dev = dev;

	heap->id = heap_id++;

    //将heap添加到ion设备的heaps链表上
	plist_node_init(&heap->node, -heap->id);
	plist_add(&heap->node, &dev->heaps);

	dev->heap_cnt++;
    
    ......
}

ion的入口驱动:

static int ion_device_create(void)
{
	struct ion_device *idev;

	idev = kzalloc(sizeof(*idev), GFP_KERNEL);

	idev->dev.minor = MISC_DYNAMIC_MINOR;
	idev->dev.name = "ion";
	idev->dev.fops = &ion_fops;
	misc_register(&idev->dev);
    ......
debugfs_done:
	plist_head_init(&idev->heaps);
    //internal_dev作为全局变量,在这里初始化,后面将会被到处使用
	internal_dev = idev;
	return 0;
}
subsys_initcall(ion_device_create);
static const struct file_operations ion_fops = {
	.owner          = THIS_MODULE,
	.unlocked_ioctl = ion_ioctl,
};

ioctl函数:

long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	unsigned int dir;
	union ion_ioctl_arg data;

	dir = ion_ioctl_dir(cmd);

	if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
		return -EFAULT;

	switch (cmd) {
	    case ION_IOC_ALLOC:
	    {
		    int fd;

		    fd = ion_alloc(data.allocation.len,data.allocation.heap_id_mask,
			       data.allocation.flags);

		    data.allocation.fd = fd;
		    break;
	    }
    }
}

ion_alloc函数:

int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
{
	struct ion_device *dev = internal_dev;
	struct ion_buffer *buffer = NULL;
	struct ion_heap *heap;

	len = PAGE_ALIGN(len);

	plist_for_each_entry(heap, &dev->heaps, node) {
		/* if the caller didn't specify this heap id */
		if (!((1 << heap->id) & heap_id_mask))
			continue;
		buffer = ion_buffer_create(heap, dev, len, flags);
		if (!IS_ERR(buffer))
			break;
	}
    ......
}

结构体定义:

struct ion_buffer {
	union {
		struct rb_node node;
		struct list_head list;
	};
	struct ion_device *dev;
	struct ion_heap *heap;
	unsigned long flags;
	unsigned long private_flags;
	size_t size;
	void *priv_virt;
	struct mutex lock;
	int kmap_cnt;
	void *vaddr;
	struct sg_table *sg_table;
    ......
};

ion_buffer_create函数:

static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
					    struct ion_device *dev,
					    unsigned long len,
					    unsigned long flags)
{
	struct ion_buffer *buffer;
	struct sg_table *table;

	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);

	buffer->heap = heap;
	buffer->flags = flags;

    //对应ion_carveout_heap_allocate函数
	heap->ops->allocate(heap, buffer, len, flags);

	table = buffer->sg_table;
	buffer->dev = dev;
	buffer->size = len;

	ion_buffer_add(dev, buffer);

	return buffer;
}

ion_carveout_heap_allocate函数实现:

static int ion_carveout_heap_allocate(struct ion_heap *heap,
				      struct ion_buffer *buffer,
				      unsigned long size,
				      unsigned long flags)
{
	struct sg_table *table;
	unsigned long paddr;

	table = kmalloc(sizeof(*table), GFP_KERNEL);
	sg_alloc_table(table, 1, GFP_KERNEL);

	paddr = ion_carveout_allocate(heap, size);

	sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);

	buffer->sg_table = table;
	return 0;

}

ion_carveout_allocate函数:

static unsigned long ion_carveout_allocate(struct ion_heap *heap,
					 unsigned long size)
{
	struct ion_carveout_heap *carveout_heap = container_of(heap, struct ion_carveout_heap, 
                                                             heap);
	unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);

	return offset;
}

gen_pool_alloc函数:

/**
 * gen_pool_alloc - allocate special memory from the pool
 * @pool: pool to allocate from
 * @size: number of bytes to allocate from the pool
 */
unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
{
	return gen_pool_alloc_algo(pool, size, pool->algo, pool->data);
}
unsigned long gen_pool_alloc_algo(struct gen_pool *pool, size_t size,
		genpool_algo_t algo, void *data)
{
	struct gen_pool_chunk *chunk;
	unsigned long addr = 0;
    //1 page =4KB,即2^12B,order即是指数12,用log2X即是对数
	int order = pool->min_alloc_order;
	int nbits, start_bit, end_bit, remain;

    //(1UL << order) - 1低12位都是1
    //只要size低12位不为0,就会向高位进1,即如果size剩余内存有不满1page时当作1page
	nbits = (size + (1UL << order) - 1) >> order;

    //遍历内存池的块链表
	list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
		if (size > atomic_long_read(&chunk->avail))
			continue;

		start_bit = 0;
		end_bit = chunk_size(chunk) >> order;
retry:
        //查找chunk位图中连续nbits都是0的开始下标,algo指向gen_pool_first_fit函数
		start_bit = algo(chunk->bits, end_bit, start_bit,
				 nbits, data, pool);
		if (start_bit >= end_bit)
			continue;
        //将块中位图以start_bit为开始、大小为nbits的值都设置为1,表示已用过
		remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
        //计算开始地址、大小
		addr = chunk->start_addr + ((unsigned long)start_bit << order);
		size = nbits << order;
		atomic_long_sub(size, &chunk->avail);
		break;
	}

	return addr;
}

 类似资料: