[Android禅修之路] 解读 GraphicBuffer 之 Ion 驱动层

2,533 阅读15分钟

[Android禅修之路] 解读 GraphicBuffer 之 Ion 驱动层

Android禅修之路

一 前言

GraphicBuffer 从 Framework 层的创建,调用到 HAL 层的内存分配,最后会到 Ion 驱动层进行具体的内存分配工作。

在介绍 Ion 驱动层之前我们先思考这样一个问题,系统分配给 GraphicBuffer 的内存,要如何做,才能在应用进程,系统进程,CPU,GPU这么多个访问者之间共享,并且还要达到传输的数据量大且效率高。也就是,如何提供一个内存管理机制,以供一些特殊的硬件设备(例如GPU,相机,显示设备等)能够直接访问内存。

Google 给出的方案就是 ION 内存管理。在 GraphicBuffer 的内存分配时,会看到一个IONAlloc,这个就是 Android 中的 ION 内存管理机制,SurfaceFlinger 中的 GraphicBuffer,就是通过 ION 内存管理机制进行跨进程传输的。

接下来我们再来说说 ION 是如何使用的(分配内存)

我们先接着上篇的 IonAlloc::AllocBuffer 开始说起,这里调用了 ioctl,传递的参数为 ION_IOC_ALLOC,ION_IOC_ALLOC 这个参数定义在 ion.h 这个头文件中。

#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0,   struct ion_allocation_data)

ion_ioctl 的函数参数是 ION_IOC_ALLOC,它最后会调用到 ion_alloc 。 [详情见3.1]

[android/ion/ion-ioctl.c]

long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
	case ION_IOC_ALLOC:
	{
		struct ion_handle *handle;

                // 调用 ion_alloc  [详情见3.1]
		handle = ion_alloc(client, data.allocation.len,
						data.allocation.align,
						data.allocation.heap_id_mask,
						data.allocation.flags);
		if (IS_ERR(handle))
			return PTR_ERR(handle);

		data.allocation.handle = handle->id;

		cleanup_handle = handle;
		break;
	}
}

所以,ion_ioctl 的内存分配,是从 ion_alloc 函数开始的。不过在介绍 ion_alloc 函数之前,我们需要先了解一下 ion 内存的一些基础知识。

一 ION中的主要数据结构

ION内存管理机制是 google 在 Android 4.0的时候引入的,它最大的特点就是跨进程共享和零拷贝,在音视频和 Camera 领域 ION用得较多。本文基于linux/v4.9.191,linux 源码网站.

在解读 ION 分配内存之前,我们先简单了解一下 ION 的内存管理机制。 首先看一下它里面关键的结构体。

1.1 ion_device

ion_device 是 ION 的核心,一个系统只有一个 ion_device 实例对象。它定义在 kernel 内核中。

[drivers/staging/android/ion/ion_priv.h]
struct ion_device {
	struct miscdevice dev;	// MISC 设备
	
	// 用户申请的 ion_buffer 的红黑树
	struct rb_root buffers;	
	struct mutex buffer_lock;
	struct rw_semaphore lock;
	
	// ion 设备创建的内存堆 heaps,用户可以自定义
	struct plist_head heaps;	
	long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
			     unsigned long arg);
			     
	// 用户申请的 ion_clients 对象的红黑树
	struct rb_root clients;
	
	struct dentry *debug_root;
	struct dentry *heaps_debug_root;
	struct dentry *clients_debug_root;
	int heap_cnt;
};

1.2 ion_clients

接下来再来看看 ion_client 这个结构体

[/drivers/staging/android/ion/ion.c]
struct ion_client {
	struct rb_node node;	// ion_client 所在的红黑树节点
	struct ion_device *dev;	// ion_device 的实例,一个系统只存在一个
	struct rb_root handles;	// 当前 ion_client 所申请的 ion_buffer 的红黑树
	struct idr idr;
	struct mutex lock;
	const char *name;	// 这个 ion_client 的名称,一般是进程名+ion_client的序列号
	char *display_name;
	int display_serial;
	struct task_struct *task;	// 当前进程的任务
	pid_t pid;
	struct dentry *debug_root;
};

ion_client 是由 ion_client_create 创建的客户端实例,

1.3 ion_heap

ion_heap 表示 ION 中的 heap。系统会通过链表或者红黑树来管理所有的 heap(取决 KERNEL 版本),这些 heap 可用 ion_device::heaps 字段来寻找。

[drivers/staging/android/ion/ion_priv.h]
struct ion_heap {
	// 用于将本 heap 实例加入 ion_device 所管理的链表中
	struct plist_node node;
	// ion_device 结构体指针,表示本 heap 挂在哪一个 ion_device 实例下
	struct ion_device *dev;
	// 表示本 heap 属于哪种类型
	enum ion_heap_type type;
	// 本字段提供的回调函数是用于从本 heap 中分配内存时所时用的,它会调用 struct ion_heap_ops::allocate() 等回调函数
	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;
	spinlock_t free_lock;
	wait_queue_head_t waitqueue;
	struct task_struct *task;

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

1.4 ion_heap_type

ion_heap_type 是一个枚举类型,它里面定义了所有 heap 的类型列表

[drivers/staging/android/uapi/ion.h]
enum ion_heap_type {
	ION_HEAP_TYPE_SYSTEM,
	ION_HEAP_TYPE_SYSTEM_CONTIG,
	ION_HEAP_TYPE_CARVEOUT,
	ION_HEAP_TYPE_CHUNK,
	ION_HEAP_TYPE_DMA,
	ION_HEAP_TYPE_CUSTOM,
};

1.5 ion_handle

ion_handle 表示的是用户使用的 buffer

[drivers/staging/android/ion/ion_priv.h]
struct ion_handle {
	struct kref ref; // 引用计数
	// 当前缓冲区所在的客户端的指针
	struct ion_client *client;	
	// 当前 handle 的真正的缓冲区
	struct ion_buffer *buffer;	
	// 客户端句柄 rbtree 中的节点
	struct rb_node node;	
	unsigned int kmap_cnt;
	int id;
};

1.6 ion_buffer

ion_buffer 就是真正的缓冲区结构体,它和 ion_handle 的区别就是一个是用户空间使用的,一个是内核空间使用的。

[drivers/staging/android/ion/ion_priv.h]
struct ion_buffer {
	struct kref ref; // 引用计数
	union {
		struct rb_node node; // ion_device 缓冲区树中的节点
		struct list_head list;
	};
	struct ion_device *dev;	// ion_device 的指针
	struct ion_heap *heap;	// 当前 buffer 所属 heap 的指针
	unsigned long flags;
	unsigned long private_flags;
	size_t size; // 缓冲区大小
	void *priv_virt; // 缓冲区私有数据,可以表示为 void*
	struct mutex lock;
	int kmap_cnt; // 如果 kmap_cnt 不为零,则内核映射
	void *vaddr;
	int dmap_cnt; // 为 dma 映射缓冲区的次数
	struct sg_table *sg_table; // 如果 dmap_cnt 不为零,则为缓冲区的 sg 表
	struct page **pages;
	struct list_head vmas; // 映射此缓冲区的 vma 列表
	/* used to track orphaned buffers */
	int handle_count; // 引用此缓冲区的句柄计数
	char task_comm[TASK_COMM_LEN];
	pid_t pid;
};

二 ION 中的重要函数

在了解 ION 内存分配之前,我们先了解一下 ION 驱动预先会调用的函数。

2.1 ion_device_create

ion_device_create 是创建 ion_device 的函数,它会将 ION 注册到设备中,这样用户空间就可以通过 open()、ioctl() 等函数调用到 ION 驱动

struct ion_device *ion_device_create(long (*custom_ioctl)
				     (struct ion_client *client,
				      unsigned int cmd,
				      unsigned long arg))
{
	struct ion_device *idev;
	int ret;

	// 分配 struct ion_device 实例
	idev = kzalloc(sizeof(*idev), GFP_KERNEL);
	if (!idev)
		return ERR_PTR(-ENOMEM);

	idev->dev.minor = MISC_DYNAMIC_MINOR;
	// 设备名称为 ion,用户空间可以通过 open("/dev/ion") 调用到
	idev->dev.name = "ion";
	idev->dev.fops = &ion_fops;
	idev->dev.parent = NULL;
	// 注册到 MISC 设备中
	ret = misc_register(&idev->dev);
	if (ret) {
		pr_err("ion: failed to register misc device.\n");
		kfree(idev);
		return ERR_PTR(ret);
	}

	// debug 相关
	...

debugfs_done:

	idev->custom_ioctl = custom_ioctl;
	idev->buffers = RB_ROOT;
	mutex_init(&idev->buffer_lock);
	init_rwsem(&idev->lock);
	plist_head_init(&idev->heaps);
	idev->clients = RB_ROOT;
	ion_root_client = &idev->clients;
	mutex_init(&debugfs_mutex);
	return idev;
}

3.2 ion_open

ion_open 是用户通过 open 调用到的函数,主要目的就是创建 ion_client 实例,这样之后就可以通过 ION_IOC_ALLOC、ION_IOC_FREE 分配和释放内存

static int ion_open(struct inode *inode, struct file *file)
{
	struct miscdevice *miscdev = file->private_data;
	struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
	struct ion_client *client;
	char debug_name[64];

	snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
	// 创建 ion_client 实例
	client = ion_client_create(dev, debug_name);
	if (IS_ERR(client))
		return PTR_ERR(client);
	file->private_data = client;

	return 0;
}

3.3 ion_client_create

ion_client_create 创建了一个 ion_client,主要就是一些参数的赋值
struct ion_client *ion_client_create(struct ion_device *dev,
				     const char *name)
{
	struct ion_client *client;
	struct task_struct *task;
	struct rb_node **p;
	struct rb_node *parent = NULL;
	struct ion_client *entry;
	pid_t pid;

	if (!name) {
		return ERR_PTR(-EINVAL);
	}

	get_task_struct(current->group_leader);
	task_lock(current->group_leader);
	pid = task_pid_nr(current->group_leader);
	/*
	 * don't bother to store task struct for kernel threads,
	 * they can't be killed anyway
	 */
	if (current->group_leader->flags & PF_KTHREAD) {
		put_task_struct(current->group_leader);
		task = NULL;
	} else {
		task = current->group_leader;
	}
	task_unlock(current->group_leader);

	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client)
		goto err_put_task_struct;

	client->dev = dev;
	client->handles = RB_ROOT;
	idr_init(&client->idr);
	mutex_init(&client->lock);
	client->task = task;
	client->pid = pid;
	client->name = kstrdup(name, GFP_KERNEL);
	if (!client->name)
		goto err_free_client;

	down_write(&dev->lock);
	client->display_serial = ion_get_client_serial(&dev->clients, name);
	client->display_name = kasprintf(
		GFP_KERNEL, "%s-%d", name, client->display_serial);
	if (!client->display_name) {
		up_write(&dev->lock);
		goto err_free_client_name;
	}
	p = &dev->clients.rb_node;
	while (*p) {
		parent = *p;
		entry = rb_entry(parent, struct ion_client, node);

		if (client < entry)
			p = &(*p)->rb_left;
		else if (client > entry)
			p = &(*p)->rb_right;
	}
	rb_link_node(&client->node, parent, p);
	rb_insert_color(&client->node, &dev->clients);

	client->debug_root = debugfs_create_file(client->display_name, 0664,
						 dev->clients_debug_root,
						 client, &debug_client_fops);
	if (!client->debug_root) {
		char buf[256], *path;

		path = dentry_path(dev->clients_debug_root, buf, 256);
	}

	up_write(&dev->lock);

	return client;

err_free_client_name:
	kfree(client->name);
err_free_client:
	kfree(client);
err_put_task_struct:
	if (task)
		put_task_struct(current->group_leader);
	return ERR_PTR(-ENOMEM);
}

三 内存分配流程

接下来,我们来看内存分配的具体流程,首先是从之前 HAL 层调用过来的 ion_alloc 函数。

3.1 ion_alloc

struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
			     size_t align, unsigned int heap_id_mask,
			     unsigned int flags)
{
	struct ion_handle *handle;
	struct ion_device *dev = client->dev;
	struct ion_buffer *buffer = NULL;
	struct ion_heap *heap;
	int ret;

	pr_debug("%s: len %zu align %zu heap_id_mask %u flags %x\n", __func__,
		 len, align, heap_id_mask, flags);
	// len是用户传递过来的长度,这里需要页对齐。
	// 少于一个page的大小就按一个page给,
	// 比如传递过来是5字节 * 经过page_align后,返回的值就是4096.
	len = PAGE_ALIGN(len);

	if (!len)
		return ERR_PTR(-EINVAL);

	down_read(&dev->lock);
	// 查找用户需要的heap,比如system heap
	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;

		// 找到了需要的heap,创建buffer [见3.4]
		buffer = ion_buffer_create(heap, dev, len, align, flags);
		if (!IS_ERR(buffer))
			break;
	}
	up_read(&dev->lock);

	if (buffer == NULL)
		return ERR_PTR(-ENODEV);

	if (IS_ERR(buffer))
		return ERR_CAST(buffer);

	// 创建 handle [见3.5]
	handle = ion_handle_create(client, buffer);

	// 设置 Buffer 销毁时的函数 [见3.2]
	ion_buffer_put(buffer);

	if (IS_ERR(handle))
		return handle;

	mutex_lock(&client->lock);

	// 将 Buffer 添加进 ion_client 的红黑树 [见3.3]
	ret = ion_handle_add(client, handle);
	mutex_unlock(&client->lock);
	if (ret) {
		ion_handle_put(handle);
		handle = ERR_PTR(ret);
	}

	return handle;
}

其中 ion_buffer_put 是设置 Buffer 销毁时的函数,ion_handle_add 是将 Buffer 添加到 ion_client 的红黑树。这两个函数比较简单。然后再根据调用顺序,分别看

  • ion_buffer_create [3.4]
  • ion_handle_create [3.5]

3.2 ion_buffer_put

设置 Buffer 销毁时的函数 _ion_buffer_destroy

static int ion_buffer_put(struct ion_buffer *buffer)
{
	return kref_put(&buffer->ref, _ion_buffer_destroy);
}

3.3 ion_handle_add

将 Buffer 添加进 ion_client 的红黑树

static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
{
	int id;
	struct rb_node **p = &client->handles.rb_node;
	struct rb_node *parent = NULL;
	struct ion_handle *entry;

	id = idr_alloc(&client->idr, handle, 1, 0, GFP_KERNEL);
	if (id < 0)
		return id;

	handle->id = id;

	while (*p) {
		parent = *p;
		entry = rb_entry(parent, struct ion_handle, node);

		if (handle->buffer < entry->buffer)
			p = &(*p)->rb_left;
		else if (handle->buffer > entry->buffer)
			p = &(*p)->rb_right;
		else
			WARN(1, "%s: buffer already found.", __func__);
	}

	rb_link_node(&handle->node, parent, p);
	rb_insert_color(&handle->node, &client->handles);

	return 0;
}

3.4 ion_buffer_create

接下来,再来看看 Buffer 的创建函数。

static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
					    struct ion_device *dev,
					    unsigned long len,
					    unsigned long align,
					    unsigned long flags)
{
	struct ion_buffer *buffer;
	struct sg_table *table;
	struct scatterlist *sg;
	int i, ret;

	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
	if (!buffer)
		return ERR_PTR(-ENOMEM);

	// 这个 buffer 所属的 heap
	buffer->heap = heap;
	// 这个 buffer 的类型
	buffer->flags = flags;
	// 初始化引用计数
	kref_init(&buffer->ref);
	// 根据 buffer 的 flag 类型,分配具体的 buffer
	// ops 是 ion_heap_ops 结构体,所以调用的是 ion_heap_ops 的 allocate
	// 它的作用就是,在给定的堆上,执行对应的操作
	// 这里的操作就是 allocate,所以这其实是真正分配内存的地方
	// [见3.6]
	ret = heap->ops->allocate(heap, buffer, len, align, flags);

	if (ret) {
		if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE))
			goto err2;

		ion_heap_freelist_drain(heap, 0);
		// 分配 heap 内存
		ret = heap->ops->allocate(heap, buffer, len, align,
					  flags);
		if (ret)
			goto err2;
	}

	if (buffer->sg_table == NULL) {
		ret = -EINVAL;
		goto err1;
	}

	// 给 buffer 的变量赋值
	table = buffer->sg_table;
	buffer->dev = dev;
	buffer->size = len;

	if (ion_buffer_fault_user_mappings(buffer)) {
		// 页对齐
		int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
		struct scatterlist *sg;
		int i, j, k = 0;

		buffer->pages = vmalloc(sizeof(struct page *) * num_pages);
		if (!buffer->pages) {
			ret = -ENOMEM;
			goto err1;
		}

		for_each_sg(table->sgl, sg, table->nents, i) {
			struct page *page = sg_page(sg);

			for (j = 0; j < sg->length / PAGE_SIZE; j++)
				buffer->pages[k++] = page++;
		}
	}

	buffer->dev = dev;
	buffer->size = len;
	INIT_LIST_HEAD(&buffer->vmas);
	mutex_init(&buffer->lock);

	for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) {
		sg_dma_address(sg) = sg_phys(sg);
		sg_dma_len(sg) = sg->length;
	}
	mutex_lock(&dev->buffer_lock);
	// 把 buffer 添加进 ion_device 中的红黑树
	ion_buffer_add(dev, buffer);
	mutex_unlock(&dev->buffer_lock);
	return buffer;

err1:
	heap->ops->free(buffer);
err2:
	kfree(buffer);
	return ERR_PTR(ret);
}

ion_heap_ops 的实现在 ion_system_heap.c 文件中,

static struct ion_heap_ops system_heap_ops = {
	.allocate = ion_system_heap_allocate,
	.free = ion_system_heap_free,
	.map_kernel = ion_heap_map_kernel,
	.unmap_kernel = ion_heap_unmap_kernel,
	.map_user = ion_heap_map_user,
	.shrink = ion_system_heap_shrink,
};

所以 allocate 真正调用的,其实是 ion_system_heap_allocate [3.7]

3.5 ion_handle_create

static struct ion_handle *ion_handle_create(struct ion_client *client,
					    struct ion_buffer *buffer)
{
	struct ion_handle *handle;

	// 创建 ion_handle
	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
	if (!handle)
		return ERR_PTR(-ENOMEM);
	// 引用计数
	kref_init(&handle->ref);
	RB_CLEAR_NODE(&handle->node);
	handle->client = client;
	ion_buffer_get(buffer);
	ion_buffer_add_to_handle(buffer);
	// 设置 buffer
	handle->buffer = buffer;

	return handle;
}

3.6 ion_heap_ops

例如 system_heap_ops 的 allocate,实际上调用的是 ion_system_heap_allocate

[drivers/staging/android/ion/ion_system_heap.c]
static struct ion_heap_ops system_heap_ops = {
	.allocate = ion_system_heap_allocate, // F分配内存的函数,[见3.7]
	.free = ion_system_heap_free,
	.map_kernel = ion_heap_map_kernel,
	.unmap_kernel = ion_heap_unmap_kernel,
	.map_user = ion_heap_map_user, // 把物理内存映射到用户空间
	.shrink = ion_system_heap_shrink, // 内存回收的回调函数
};

3.7 ion_system_heap_allocate

内存类型为 system heap 的分配方式,通过调用 alloc_largest_available 函数进行分配。[3.8]

static int ion_system_heap_allocate(struct ion_heap *heap,
				    struct ion_buffer *buffer,
				    unsigned long size, unsigned long align,
				    unsigned long flags)
{
	struct ion_system_heap *sys_heap = container_of(heap,
							struct ion_system_heap,
							heap);
	struct sg_table *table;
	struct scatterlist *sg;
	struct list_head pages;
	struct page *page, *tmp_page;
	int i = 0;
	unsigned long size_remaining = PAGE_ALIGN(size);
	unsigned int max_order = orders[0];

	if (align > PAGE_SIZE)
		return -EINVAL;

	if (size / PAGE_SIZE > totalram_pages / 2)
		return -ENOMEM;

	INIT_LIST_HEAD(&pages);
	// 页面对齐,假如传递过来的size为4096(在ion_alloc经过对齐了),
	// 再次经过页面对齐,此时的size_remaining也为4096
	while (size_remaining > 0) {
		// 分配内存,system heap中管理着两类pool,根据pool中连续page页的order数,
		// 分别是order为 8、 4、 0,定义在int orders[] = {8, 4, 0};中
		// 4096 << 8 = 1048576   Byte = 1024KB = 1M
		// 4096 << 4 = 65536     Byte = 64KB
		// 4096 << 0 = 4096      Byte = 4KB
		// [见3,8]
		page = alloc_largest_available(sys_heap, buffer, size_remaining,
					       max_order);
		if (!page)
			goto free_pages;
		// 分配成功,将page页加入到pages list中
		list_add_tail(&page->lru, &pages);
		size_remaining -= PAGE_SIZE << compound_order(page);
		max_order = compound_order(page);
		i++;
	}
	// 将分配成功的page放入sg_table中
	table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
	if (!table)
		goto free_pages;

	// 总共分配了多少次(i次)连续的内存块,那么就分配i个scatterlist用来关联物理buffer
	if (sg_alloc_table(table, i, GFP_KERNEL))
		goto free_table;

	sg = table->sgl;

	// 将内存块与scatterlist关联起来,比如上面分配了一个16页,2个一页,
	// 总共3个物理连续的内存buffer,将他们分别放入scatterlist的page_link中,
	// 实际是将page结构体的地址存放在其中。 
	// 接着将page->lru从前面的pages链表中删除。此时的page就只有sg中能找到了,
	// 在buddy中已经找不到了。
	list_for_each_entry_safe(page, tmp_page, &pages, lru) {
		sg_set_page(sg, page, PAGE_SIZE << compound_order(page), 0);
		sg = sg_next(sg);
		list_del(&page->lru);
	}

	// 最后将sg_table赋值,其中就包含刚分配的内存页
	buffer->sg_table = table;
	return 0;

free_table:
	kfree(table);
free_pages:
	list_for_each_entry_safe(page, tmp_page, &pages, lru)
		free_buffer_page(sys_heap, buffer, page);
	return -ENOMEM;
}

3.8 alloc_largest_available

static struct page *alloc_largest_available(struct ion_system_heap *heap,
					    struct ion_buffer *buffer,
					    unsigned long size,
					    unsigned int max_order)
{
	struct page *page;
	int i;

	// system heap中有三种order大小的内存块,分别是8、4、0,对应2^8页...
        // 此处找到小于分配size的最大order,比如size是18*4K(18个page),
        // 那么这里就会选择到order 4 ,先分配一个16个page返回,
        // 再次进入该函数,size变成2*4k(2个page),会选择order为0,分配一个page返回。
        // 第三次进入该函数,size变成1*4k(1个page),此时会选择order为0,分配一个page返回。
	for (i = 0; i < NUM_ORDERS; i++) {
		if (size < order_to_size(orders[i]))
			continue;
		if (max_order < orders[i])
			continue;
		//实际分配:先从pool中分配,如果没有就从buddy中分配。[见3.9]
		page = alloc_buffer_page(heap, buffer, orders[i]);
		if (!page)
			continue;

		return page;
	}

	return NULL;
}

3.9 alloc_buffer_page

static struct page *alloc_buffer_page(struct ion_system_heap *heap,
				      struct ion_buffer *buffer,
				      unsigned long order)
{
	bool cached = ion_buffer_cached(buffer);
	struct ion_page_pool *pool;
	struct page *page;

	// system heap自己定义了两个pool,一个cached一个uncached 
	// 每个pool对应不同order,当前是8/4/0三种
	if (!cached)
		pool = heap->uncached_pools[order_to_index(order)];
	else
		pool = heap->cached_pools[order_to_index(order)];

	// 从pool中分配内存,分配时先从high链表中分配,没有就从low中分配,还没有就从buddy中分配。
	// [见3.10]
	page = ion_page_pool_alloc(pool);

	if (cached)
		ion_pages_sync_for_device(NULL, page, PAGE_SIZE << order,
					  DMA_BIDIRECTIONAL);
	return page;
}

3.10 ion_page_pool_alloc

struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
{
	struct page *page = NULL;

	BUG_ON(!pool);

	mutex_lock(&pool->mutex);
	// 分配时,先从pool中分配
        // pool中又分为highmem和lowmem
        // 先从high中去分配
	if (pool->high_count)
		// [见3.11]
		page = ion_page_pool_remove(pool, true);
	else if (pool->low_count)
		page = ion_page_pool_remove(pool, false);
	mutex_unlock(&pool->mutex);

	// 如果没有分配成功,那么就从伙伴系统中分配。
        // 但是这里分配后并没有加入到pool中。
	if (!page)
		// [见3.12]
		page = ion_page_pool_alloc_pages(pool);

	return page;
}

3.11 ion_page_pool_remove

static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
{
	struct page *page;

	// 如果是从high中申请,那么就从 high_items 链表中拿出page(可能是order为8/4/0),
	// 并且high_count减1.这里可以看到,page被加入到high_items或者low_items中,是通过page->lru
	if (high) {
		BUG_ON(!pool->high_count);
		page = list_first_entry(&pool->high_items, struct page, lru);
		pool->high_count--;
	} else {
		BUG_ON(!pool->low_count);
		page = list_first_entry(&pool->low_items, struct page, lru);
		pool->low_count--;
	}

	list_del(&page->lru);
	return page;
}

3.12 ion_page_pool_alloc_pages

如果pool中没有的话,就调用alloc_pages()从buddy中分配内存

static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
{
	struct page *page = alloc_pages(pool->gfp_mask, pool->order);

	if (!page)
		return NULL;
	if (!pool->cached)
		ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order,
					  DMA_BIDIRECTIONAL);
	return page;
}

到这里,ION 内存分配的流程,我们就大致的梳理完成了。接下来再简单看看 ION 内存释放的函数。

四 内存释放流程

在 3.2 中,我们说它设置了一个 Buffer 销毁时的函数 _ion_buffer_destroy 。接下来,我们就从这个销毁的函数开始看起。

4.1 _ion_buffer_destroy

void ion_buffer_destroy(struct ion_buffer *buffer)
{
	if (buffer->kmap_cnt > 0) {
		pr_warn_once("%s: buffer still mapped in the kernel\n",
			     __func__);
		buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
	}
	// ion_system_heap_free,结构[见3.6]
	// ion_system_heap_free [见4.2]
	buffer->heap->ops->free(buffer);
	vfree(buffer->pages);
	kfree(buffer);
}

static void _ion_buffer_destroy(struct kref *kref)
{
	struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
	struct ion_heap *heap = buffer->heap;
	struct ion_device *dev = buffer->dev;

	mutex_lock(&dev->buffer_lock);
	rb_erase(&buffer->node, &dev->buffers);
	mutex_unlock(&dev->buffer_lock);

	if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
		ion_heap_freelist_add(heap, buffer);
	else
		// 调用不带下划线的同名函数
		ion_buffer_destroy(buffer);
}

这个函数,其实也是调用的不带下划线的同名函数 ion_buffer_destroy。

4.2 ion_system_heap_free

static void ion_system_heap_free(struct ion_buffer *buffer)
{
	struct ion_system_heap *sys_heap = container_of(buffer->heap,
							struct ion_system_heap,
							heap);
	struct sg_table *table = buffer->sg_table;
	struct scatterlist *sg;
	int i;

	/* zero the buffer before goto page pool */
	if (!(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))
		ion_heap_buffer_zero(buffer);

	for_each_sg(table->sgl, sg, table->nents, i)
		// 真正释放内存的函数,[见4.3]
		free_buffer_page(sys_heap, buffer, sg_page(sg));
	sg_free_table(table);
	kfree(table);
}

4.3 free_buffer_page

static void free_buffer_page(struct ion_system_heap *heap,
			     struct ion_buffer *buffer, struct page *page)
{
	struct ion_page_pool *pool;
	unsigned int order = compound_order(page);
	bool cached = ion_buffer_cached(buffer);

	/* go to system */
	// 如果指定了ION_PRIV_FLAG_SHRINKER_FREE标志,则直接返还给buddy
	if (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) {
		__free_pages(page, order);
		return;
	}

	// 一般来说都是返还给pool中,直到系统内存不足时, 
	// kswapd回收内存调用shrink回调,将pool中的内存回收到buddy中
	if (!cached)
		pool = heap->uncached_pools[order_to_index(order)];
	else
		pool = heap->cached_pools[order_to_index(order)];

	// [见4.4]
	ion_page_pool_free(pool, page);
}

4.4 ion_page_pool_free

void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
{
	int ret;

	BUG_ON(pool->order != compound_order(page));

	// 返还给pool,[见4.5]
	ret = ion_page_pool_add(pool, page);
	if (ret)
		// [见4.6]
		ion_page_pool_free_pages(pool, page);
}

4.5 ion_page_pool_add

static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
{
	mutex_lock(&pool->mutex);
	// 先确定具体返还到的order、high或low  
	// 返还给pool后,最终要回到buddy中,需要通过系统回收接口shrink进行主动回收
	if (PageHighMem(page)) {
		list_add_tail(&page->lru, &pool->high_items);
		pool->high_count++;
	} else {
		list_add_tail(&page->lru, &pool->low_items);
		pool->low_count++;
	}
	mutex_unlock(&pool->mutex);
	return 0;
}

4.6 ion_page_pool_free_pages

static void ion_page_pool_free_pages(struct ion_page_pool *pool,
				     struct page *page)
{
	__free_pages(page, pool->order);
}

五 ION的其他函数

了解了 ION 内存的分配和释放,我们再来了解一下 ION的其他基础知识。

5.1 ION 驱动的注册过程

首先,ION 是由硬件驱动实现的,所以在会注册到 linux 的驱动中,在/drivers/staging/android/ion 路径下,可以发现一份示例代码tegra/tegra_ion.c

static int tegra_ion_probe(struct platform_device *pdev)
{
	struct ion_platform_data *pdata = pdev->dev.platform_data;
	int err;
	int i;

	num_heaps = pdata->nr;

	heaps = devm_kcalloc(&pdev->dev, pdata->nr,
			     sizeof(struct ion_heap *), GFP_KERNEL);

	// 创建 ion_device
	idev = ion_device_create(NULL);
	if (IS_ERR(idev))
		return PTR_ERR(idev);

	for (i = 0; i < num_heaps; i++) {
		// 创建堆内存
		struct ion_platform_heap *heap_data = &pdata->heaps[i];

		// [见5.1.1]
		heaps[i] = ion_heap_create(heap_data);
		if (IS_ERR_OR_NULL(heaps[i])) {
			err = PTR_ERR(heaps[i]);
			goto err;
		}
		// 把堆内存添加到ion驱动进行管理,[见5.2]
		ion_device_add_heap(idev, heaps[i]);
	}
	// 保存 idev 到 pdev
	platform_set_drvdata(pdev, idev);
	return 0;
err:
	for (i = 0; i < num_heaps; ++i)
		ion_heap_destroy(heaps[i]);
	return err;
}

在注册驱动的时候,我们关心的函数只有三个

  1. ion_device_create : ion_device 的创建
  2. ion_heap_create : ion_heap 数组指针的创建
  3. 将 ion_heap 数组添加到 ion_device 中进行管理

5.1.1 ion_heap 的创建

ion_device_create 就是创建 ion_device 的函数,它定义在/drivers/staging/android/ion/ion.c文件中,主要做的就是一些变量的赋值,这里就不进行列举了

ion_heap_create 是创建 ion_device 的函数。它定义在/drivers/staging/android/ion/ion_heap.c文件中。

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

	switch (heap_data->type) {
	case ION_HEAP_TYPE_SYSTEM_CONTIG:
		heap = ion_system_contig_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_SYSTEM:
		heap = ion_system_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_CARVEOUT:
		heap = ion_carveout_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_CHUNK:
		heap = ion_chunk_heap_create(heap_data);
		break;
	case ION_HEAP_TYPE_DMA:
		heap = ion_cma_heap_create(heap_data);
		break;
	default:
		pr_err("%s: Invalid heap type %d\n", __func__,
		       heap_data->type);
		return ERR_PTR(-EINVAL);
	}

	if (IS_ERR_OR_NULL(heap)) {
		pr_err("%s: error creating heap %s type %d base %lu size %zu\n",
		       __func__, heap_data->name, heap_data->type,
		       heap_data->base, heap_data->size);
		return ERR_PTR(-EINVAL);
	}

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

它其实就是根据不同的 type 进行堆内存的创建。这里分别有5种type,其实[1.4]中定义的Type是6种,分别对应的是

  • ION_HEAP_TYPE_SYSTEM: 通过vmalloc分配,代码中是直接通过 alloc_pages 分配的,对应文件ion_system_heap.c
  • ION_HEAP_TYPE_SYSTEM_CONTIG: 通过kmalloc进行分配,对应文件ion_system_heap.c
  • ION_HEAP_TYPE_CARVEOUT: 对应文件ion_carveout_heap.c
  • ION_HEAP_TYPE_CHUNK: ion_chunk_heap.c
  • ION_HEAP_TYPE_DMA: 对接cma分配器,对应文件ion_cma_heap.c
  • ION_HEAP_TYPE_CUSTOM: 自定义

我们在[三 内存分配流程]中,使用的是 alloc_pages,所以这里我们看 ION_HEAP_TYPE_SYSTEM 类型的内存分配。对应的函数是 ion_system_heap_create,位于文件ion_system_heap.c

struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
{
	struct ion_system_heap *heap;

	heap = kzalloc(sizeof(*heap), GFP_KERNEL);
	if (!heap)
		return ERR_PTR(-ENOMEM);
	heap->heap.ops = &system_heap_ops;
	heap->heap.type = ION_HEAP_TYPE_SYSTEM;
	heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;

	// [见5.1.2]
	if (ion_system_heap_create_pools(heap->uncached_pools, false))
		goto free_heap;

	if (ion_system_heap_create_pools(heap->cached_pools, true))
		goto destroy_uncached_pools;

	heap->heap.debug_show = ion_system_heap_debug_show;
	return &heap->heap;

destroy_uncached_pools:
	ion_system_heap_destroy_pools(heap->uncached_pools);

free_heap:
	kfree(heap);
	return ERR_PTR(-ENOMEM);
}

5.1.2 ion_system_heap_create_pools

ion_system_heap_create_pools 中通过调用 ion_page_pool_create 创建内存

static int ion_system_heap_create_pools(struct ion_page_pool **pools,
					bool cached)
{
	int i;

	for (i = 0; i < NUM_ORDERS; i++) {
		struct ion_page_pool *pool;
		gfp_t gfp_flags = low_order_gfp_flags;

		if (orders[i] > 4)
			gfp_flags = high_order_gfp_flags;

		// [见5.1.3]
		pool = ion_page_pool_create(gfp_flags, orders[i], cached);
		if (!pool)
			goto err_create_pool;
		pools[i] = pool;
	}
	return 0;

err_create_pool:
	ion_system_heap_destroy_pools(pools);
	return -ENOMEM;
}

5.1.3 ion_page_pool_create

ion_page_pool_create 定义在 ion_page_pool.c

struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order,
					   bool cached)
{
	struct ion_page_pool *pool = kmalloc(sizeof(*pool), GFP_KERNEL);

	if (!pool)
		return NULL;
	pool->high_count = 0;
	pool->low_count = 0;
	INIT_LIST_HEAD(&pool->low_items); // 低地址链表
	INIT_LIST_HEAD(&pool->high_items); // 高地址链表
	pool->gfp_mask = gfp_mask | __GFP_COMP;
	pool->order = order; // order 分别是对应的大小等级,即8,4,0
	mutex_init(&pool->mutex);
	plist_node_init(&pool->list, order);
	if (cached)
		pool->cached = true;

	return pool;
}

5.2 ion_heap 的管理

[5.1] 中说了,在创建完 ion_heap 之后,会调用 ion_device_add_heap 将 ion_heap 添加进 ion_device 中进行管理。ion_device_add_heap 定义在ion.c文件中

5.2.1 ion_device_add_heap

void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
{
	struct dentry *debug_file;

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

	spin_lock_init(&heap->free_lock);
	heap->free_list_size = 0;

	// 重要函数1,[见5.2.2]
	if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
		ion_heap_init_deferred_free(heap);

	// 重要函数2,[见5.2.3]
	if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink)
		ion_heap_init_shrinker(heap);

	...

	dev->heap_cnt++;
	up_write(&dev->lock);
}

ion_device_add_heap 中主要调用了2个重要的函数,ion_heap_init_deferred_free 和 ion_heap_init_shrinker。

5.2.2 ion_heap_init_deferred_free

函数定义在 ion_heap.c

int ion_heap_init_deferred_free(struct ion_heap *heap)
{
	struct sched_param param = { .sched_priority = 0 };

	INIT_LIST_HEAD(&heap->free_list);
	init_waitqueue_head(&heap->waitqueue);

	// SCHED_IDLE 线程对应的 task
	// task [见5.2.3]
	heap->task = kthread_run(ion_heap_deferred_free, heap,
				 "%s", heap->name);
	if (IS_ERR(heap->task)) {
		pr_err("%s: creating thread for deferred free failed\n",
		       __func__);
		return PTR_ERR_OR_ZERO(heap->task);
	}
	// 启动一个 SCHED_IDLE 线程,执行 task
	sched_setscheduler(heap->task, SCHED_IDLE, &param);
	return 0;
}

5.2.3 ion_heap_deferred_free

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;
		}

		// 循环处理 ion_heap 中的 free_list
		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 驱动中会启动一个 IDLE 线程,然后在这个线程中有一个死循环,它会销毁heap->free_list中没用的 buffer 对象。

5.2.3 ion_heap_init_shrinker

初始化 shrinker 函数,这个函数其实就是

void ion_heap_init_shrinker(struct ion_heap *heap)
{
	heap->shrinker.count_objects = ion_heap_shrink_count;
	heap->shrinker.scan_objects = ion_heap_shrink_scan;
	heap->shrinker.seeks = DEFAULT_SEEKS;
	heap->shrinker.batch = 0;
	register_shrinker(&heap->shrinker);
}

这个函数就是在[4.1]中赋值的函数 ion_system_heap_shrink,它最后调用的是 ion_page_pool_shrink

5.2.4 ion_system_heap_shrink

static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
				  int nr_to_scan)
{
	struct ion_page_pool *uncached_pool;
	struct ion_page_pool *cached_pool;
	struct ion_system_heap *sys_heap;
	int nr_total = 0;
	int i, nr_freed;
	int only_scan = 0;

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

	if (!nr_to_scan)
		only_scan = 1;
		
	// 根据不同的order遍历
	for (i = 0; i < NUM_ORDERS; i++) {
		uncached_pool = sys_heap->uncached_pools[i];
		cached_pool = sys_heap->cached_pools[i];

		if (only_scan) {
			// 扫描,[见5.2.5]
			nr_total += ion_page_pool_shrink(uncached_pool,
							 gfp_mask,
							 nr_to_scan);

			nr_total += ion_page_pool_shrink(cached_pool,
							 gfp_mask,
							 nr_to_scan);
		} else {
			// 回收,[见5.2.5]
			nr_freed = ion_page_pool_shrink(uncached_pool,
							gfp_mask,
							nr_to_scan);
			nr_to_scan -= nr_freed;
			nr_total += nr_freed;
			if (nr_to_scan <= 0)
				break;
			nr_freed = ion_page_pool_shrink(cached_pool,
							gfp_mask,
							nr_to_scan);
			nr_to_scan -= nr_freed;
			nr_total += nr_freed;
			if (nr_to_scan <= 0)
				break;
		}
	}
	return nr_total;
}

5.2.5 ion_page_pool_shrink

它定义在ion_page_pool.c

int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
			 int nr_to_scan)
{
	int freed = 0;
	bool high;

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

	// 只扫描,返回page数量
	if (nr_to_scan == 0)
		return ion_page_pool_total(pool, high);

	// 回收 
	// 先将low的内存进行回收,如果没有low就从high中回收 
	// 回收的内存达到要求的 nr_to_scan 后停止,或者把pool中的全部回收完了也停止
	while (freed < nr_to_scan) {
		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);
		freed += (1 << pool->order);
	}

	// 返回回收的数量
	return freed;
}

六 总结

6.1 ION 中结构的总结

一个 ion_handle 关联一个 ion_client 和一个 ion_buffer 一个 ion_client 关联多个 ion_handle,以红黑树的数据结构进行管理 同一个 client 通过不同的 handle 来标识不同的 buffer 一个 ion_client 关联一个 ion_device

6.2 ION 的使用总结

ION的使用步骤

1 获取ION client

bool IonAlloc::Init() {
  if (ion_dev_fd_ == FD_INIT) {
    ion_dev_fd_ = open(kIonDevice, O_RDONLY);
  }

  if (ion_dev_fd_ < 0) {
    ALOGE("%s: Failed to open ion device - %s", __FUNCTION__, strerror(errno));
    ion_dev_fd_ = FD_INIT;
    return false;
  }

  return true;
}

通过 open 拿到的就是一个以 handle 形式的 file descriptor,这个 file descriptor 用来代表一个ION client

2 设置参数

[/system/core/libion/kernel-headers/linux/ion.h]

typedef int ion_user_handle_t;

struct ion_allocation_data {
 size_t len;
 size_t align;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 unsigned int heap_id_mask;
 unsigned int flags;
 ion_user_handle_t handle;	// 输出结果
};
int IonAlloc::AllocBuffer(AllocData *data) {
  int err = 0;
  struct ion_handle_data handle_data;
  struct ion_fd_data fd_data;
  struct ion_allocation_data ion_alloc_data;

  ion_alloc_data.len = data->size;
  ion_alloc_data.align = data->align;
  ion_alloc_data.heap_id_mask = data->heap_id;
  ion_alloc_data.flags = data->flags;
  ion_alloc_data.flags |= data->uncached ? 0 : ION_FLAG_CACHED;
}

创建一个 ion_handle_data 对象,存储传入的参数

3 通过 ioctl 接口与系统交互

  if (ioctl(ion_dev_fd_, INT(ION_IOC_ALLOC), &ion_alloc_data)) {
    err = -errno;
    return err;
  }

拿到的结果就是 ion_user_handle_t,它是一个文件描述符,可以跨进程传递。

到这里,ION驱动层的内存分配和释放逻辑就整理完毕了,ION层还有一些知识,后续再来补充。

参考资料

魅族内核团队有关ION的介绍

dmf_buf 的文章