ION详细分析

参考:

http://blog.csdn.net/armwind/article/details/53454251?locationNum=2&fps=1

代码路径
驱动代码: kernel-3.18/drivers/staging/android/ion
Native lib代码: system\core\libion & vendor/mediatek/proprietary/external/libion_mtk

一,概述
ION是google在Android4.0 为了解决内存碎片管理而引入的通用内存管理器,在面向程序员编程方面,它和ashmem很相似。但是终究还是ION更胜一筹。
ION 定义了多种不同的heap,实现不同的内存分配策略。
二,kernel代码分析
  • 代码路径:kernel-3.18/drivers/staging/android/ion
  • 针对multimedia(camera)的heap初始化
ION驱动初始化时,同时多个heap被初始化
创建了dev/ion节点,供上层使用
type = ION_HEAP_TYPE_MULTIMEDIA表示用于multimedia
flags = ION_HEAP_FLAG_DEFER_FREE表示需要延时或异步清空buffer, 这样做的好处是当内存充足时,前期申请的连续内存使用完后,并没有立即返还给buddy,而是heap自己缓存了起来。下次申请大块内存时,可以及时从buffer池中获取,避免了内存碎片

1. @Ion_drv.c (drivers\staging\android\ion\mtk)
static int ion_drv_probe(struct platform_device *pdev)
{
	int i;
	struct ion_platform_data *pdata = pdev->dev.platform_data;
	unsigned int num_heaps = pdata->nr;

	IONMSG("ion_drv_probe() heap_nr=%d\n", pdata->nr);
	g_ion_device = ion_device_create(ion_custom_ioctl);

	/* 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]; //数据取自ion_drv_platform_heaps[]数组
		struct ion_heap *heap;
		if (heap_data->type == ION_HEAP_TYPE_CARVEOUT && heap_data->base == 0) {
			/* reserve for carveout heap failed */
			heap_data->size = 0;
			continue;
		}
		heap = ion_mtk_heap_create(heap_data);
		if (IS_ERR_OR_NULL(heap))
			continue;
		ion_device_add_heap(g_ion_device, heap);
	}

	platform_set_drvdata(pdev, g_ion_device);

	/* debugfs_create_file("ion_profile", 0644, g_ion_device->debug_root, NULL, */
	/* &debug_profile_fops); */
	debugfs_create_file("clients_summary", 0644, g_ion_device->clients_debug_root, NULL,
			    &debug_client_fops);
	debugfs_create_symlink("ion_mm_heap", g_ion_device->debug_root, "./heaps/ion_mm_heap");
	ion_history_init();
	ion_profile_init();

	return 0;
}

static struct ion_platform_heap ion_drv_platform_heaps[] = {
		{
				.type = ION_HEAP_TYPE_MULTIMEDIA,
				.id = ION_HEAP_TYPE_MULTIMEDIA,
				.name = "ion_mm_heap",
				.base = 0,
				.size = 0,
				.align = 0,
				.priv = NULL,
		},
。。。
}
struct ion_heap *ion_mtk_heap_create(struct ion_platform_heap *heap_data)
{
	struct ion_heap *heap = NULL;

	switch ((int)heap_data->type) {
	case ION_HEAP_TYPE_MULTIMEDIA:
		heap = ion_mm_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_FB:
		heap = ion_fb_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_MULTIMEDIA_SEC:
		heap = ion_sec_heap_create(heap_data);
		break;
	default:
		heap = ion_heap_create(heap_data);
	}
。。。
	heap->name = heap_data->name;
	heap->id = heap_data->id;
	return heap;
}

2. @Ion_mm_heap.c (drivers\staging\android\ion\mtk)
struct ion_heap *ion_mm_heap_create(struct ion_platform_heap *unused)
{
	struct ion_system_heap *heap;
	int i;

	heap = kzalloc(sizeof(struct ion_system_heap), GFP_KERNEL);
	if (!heap) {
		IONMSG("%s kzalloc failed heap is null.\n", __func__);
		return ERR_PTR(-ENOMEM);
	}
	heap->heap.ops = &system_heap_ops; //包含allocte, free, map, unmap, shrink等操作函数
	heap->heap.type = ION_HEAP_TYPE_MULTIMEDIA;
	heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE; //表示开启一个回收线程 “ion_mm_heap”
	heap->pools = kcalloc(num_orders, sizeof(struct ion_page_pool *), GFP_KERNEL);  为啥创建两个pools???  具体用哪个pool是由上层ION使用者觉定,哪依据规则呢?
	heap->cached_pools = kcalloc(num_orders, sizeof(struct ion_page_pool *), GFP_KERNEL);

	for (i = 0; i < num_orders; i++) {
		struct ion_page_pool *pool;
		gfp_t gfp_flags = order_gfp_flags[i];

		if (unused->id == ION_HEAP_TYPE_MULTIMEDIA_FOR_CAMERA)
			gfp_flags |= (__GFP_HIGHMEM | __GFP_CMA);


		pool = ion_page_pool_create(gfp_flags, orders[i]);
		if (!pool)
			goto err_create_pool;
		heap->pools[i] = pool;

		pool = ion_page_pool_create(gfp_flags, orders[i]);
		if (!pool)
			goto err_create_pool;
		heap->cached_pools[i] = pool;
	}

	heap->heap.debug_show = ion_mm_heap_debug_show;
	return &heap->heap;
...
}

3. 使用者client的创建

进程、ion_client、buffer他们三个的关系还是比较"乱"的。在ion设备结构体中有ion_client,ion_buffer,ion_heap红黑树。它会把系统中所有的ion_client,ion_buffer,ion_heap串联起来。如下所示的结构。

4. multimedia 内存的申请

目前分配最大的16个连续page,由order数组决定。 static const unsigned int orders[] = { 4, 1, 0 };@ion_mm_heap.c
采用的DMA映射机制:scatter/gatther机制, scatterlist 分大块DMA map

5. multimedia 内存释放

三,Native代码分析
1. @Ion.c (drivers\staging\android\ion)
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
{
	struct dentry *debug_file;
	char tmp_name[64];

	if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
	    !heap->ops->unmap_dma)
		pr_err("%s: can not add heap with invalid ops struct.\n",
		       __func__);

	if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) //创建的ion_mm_heap_create就有ION_HEAP_FLAG_DEFER_FREE
		ion_heap_init_deferred_free(heap);  

	if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink)
		ion_heap_init_shrinker(heap);
...
}

2. 异步清理heap中的buffer@Ion_heap.c (drivers\staging\android\ion)
该线程主要是用于heap buffer的异步清理,但该buffer还是会放在cache中,并没有free给buddy;异步处理能减小系统瞬时负担
int ion_heap_init_deferred_free(struct ion_heap *heap)
{
	struct sched_param param = { .sched_priority = 0 }; NICE最低可设置为19,最高可设置为-19

	INIT_LIST_HEAD(&heap->free_list);
	heap->free_list_size = 0;
	spin_lock_init(&heap->free_lock);
	init_waitqueue_head(&heap->waitqueue);
	heap->task = kthread_run(ion_heap_deferred_free, heap, "%s", heap->name); //比如创建了名为“ion_mm_heap” 的线程, PRI优先级=19, PRI最高可为39, 最低为0
	if (IS_ERR(heap->task)) {
		pr_err("%s: creating thread for deferred free failed\n",
		       __func__);
		return PTR_ERR_OR_ZERO(heap->task);
	}
	sched_setscheduler(heap->task, SCHED_IDLE, ¶m); //设置较低的调度策略SCHED_IDLE, 最高为SCHED_FIFO
	return 0;
}
查询到的ION回收线程:
#ps -Al | grep -E "ion_mm_heap|NI"
F S   UID   PID  PPID C PRI  NI BIT    SZ WCHAN  TTY          TIME CMD
1 S     0     58     2      0  19   0   -     0 ion_heap_deferred_free ? 00:00:00 ion_mm_heap

static int ion_heap_deferred_free(void *data)
{
	struct ion_heap *heap = data;

	while (true) {
		struct ion_buffer *buffer;
		wait_event_freezable(heap->waitqueue,   ion_heap_freelist_size(heap) > 0);
		spin_lock(&heap->free_lock);
		if (list_empty(&heap->free_list)) {
			spin_unlock(&heap->free_lock);
			continue;
		}
		buffer = list_first_entry(&heap->free_list, struct ion_buffer,  list);
		list_del(&buffer->list);
		heap->free_list_size -= buffer->size;
		spin_unlock(&heap->free_lock);
		ion_buffer_destroy(buffer);
	}

	return 0;
}

_ion_buffer_destroy@Ion.c (drivers\staging\android\ion)销毁ion buffer的时候如果该heap有ION_HEAP_FLAG_DEFER_FREE标志,则表示异步清除该buffer
->void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer)
{
	spin_lock(&heap->free_lock);
	list_add(&buffer->list, &heap->free_list);
	heap->free_list_size += buffer->size;

	if (heap->free_list_size > 200*1024*1024)
		IONMSG("[ion_dbg] warning: free_list_size=0x%zu\n", heap->free_list_size);

	spin_unlock(&heap->free_lock);
	wake_up(&heap->waitqueue);
}

3. 真正free heap的buffer, @Ion_heap.c (drivers\staging\android\ion)
void ion_heap_init_shrinker(struct ion_heap *heap)
{
	heap->shrinker.count_objects = ion_heap_shrink_count;  获取pool中和freelist中的buffer里包含的page个数
	heap->shrinker.scan_objects = ion_heap_shrink_scan;
	heap->shrinker.seeks = DEFAULT_SEEKS;
	heap->shrinker.batch = 0;
	register_shrinker(&heap->shrinker);
}

当 drop_slab@Drop_caches.c的时候,会调用heap->shrinker
先调用ion_heap_shrink_count
再调用ion_heap_shrink_scan

static unsigned long ion_heap_shrink_scan(struct shrinker *shrinker,
						struct shrink_control *sc)
{
	struct ion_heap *heap = container_of(shrinker, struct ion_heap,
					     shrinker);
	int freed = 0;
	int to_scan = sc->nr_to_scan;

	if (to_scan == 0)
		return 0;

	/*
	 * shrink the free list first, no point in zeroing the memory if we're
	 * just going to reclaim it. Also, skip any possible page pooling.
	 */
	if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
		freed = ion_heap_freelist_shrink(heap, to_scan * PAGE_SIZE) /
				PAGE_SIZE;

	to_scan -= freed;
	if (to_scan <= 0)
		return freed;

	if (heap->ops->shrink)
		freed += heap->ops->shrink(heap, sc->gfp_mask, to_scan); //调用ion_mm_heap_shrink
	return freed;
}

4. @Ion_mm_heap.c (drivers\staging\android\ion\mtk)
static int ion_mm_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan)
{
	struct ion_system_heap *sys_heap;
	int nr_total = 0;
	int i;

	sys_heap = container_of(heap, struct ion_system_heap, heap);

	for (i = 0; i < num_orders; i++) {
		struct ion_page_pool *pool = sys_heap->pools[i];

		nr_total += ion_page_pool_shrink(pool, gfp_mask, nr_to_scan);
		/* shrink cached pool */
		nr_total += ion_page_pool_shrink(sys_heap->cached_pools[i], gfp_mask, nr_to_scan);
	}

	return nr_total;
}

5. @Ion_page_pool.c (drivers\staging\android\ion)	
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, int nr_to_scan)
{
	int freed;
	bool high;

	if (current_is_kswapd())
		high = true;
	else
		high = !!(gfp_mask & __GFP_HIGHMEM);

	if (nr_to_scan == 0)
		return ion_page_pool_total(pool, high);

	for (freed = 0; freed < nr_to_scan; freed++) {
		struct page *page;

		mutex_lock(&pool->mutex);
		if (pool->low_count) {
			page = ion_page_pool_remove(pool, false);
		} else if (high && pool->high_count) {
			page = ion_page_pool_remove(pool, true);
		} else {
			mutex_unlock(&pool->mutex);
			break;
		}
		mutex_unlock(&pool->mutex);
		ion_page_pool_free_pages(pool, page);
	}

	return freed;
}

static void ion_page_pool_free_pages(struct ion_page_pool *pool,
				     struct page *page)
{
	ion_page_pool_free_set_cache_policy(pool, page);
	__free_pages(page, pool->order);  //直接释放回buddy系统了!!!!
}
小结:通过drop_slab->shrink_slab, 最后ION两个pool中的空buffer,都会释放回buddy系统!!!


五,debug
1. push vendor & system /lib/
adb root
adb disable-verity
adb remount
2. 查看各ION信息
dev/ion
/sys/kernel/debug/ion/
3.清理heap中缓存的buffer

echo 1 > /sys/kernel/debug/ion/heaps/ion_mm_heap_shrink
但是只清理的是非高端内存的buffer


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值