[Android禅修之路] 解读 GraphicBuffer 之 Ion 驱动层
一 前言
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;
}
在注册驱动的时候,我们关心的函数只有三个
- ion_device_create : ion_device 的创建
- ion_heap_create : ion_heap 数组指针的创建
- 将 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, ¶m);
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层还有一些知识,后续再来补充。
参考资料