前言
上一篇文章,在解析初始化GraphicBuffer中,遇到一个ion驱动,对图元进行管理。首先看看ion是怎么使用的:
1.打开驱动:
mIonFd = open(ION_DEVICE, O_RDONLY);
2.ioctl 发送ION_IOC_ALLOC命令
if(ioctl(mIonFd, ION_IOC_ALLOC, &ionAllocData)) {
err = -errno;
ALOGE("ION_IOC_ALLOC failed with error - %s", strerror(errno));
return err;
}
3.ioctl发送ION_IOC_MAP命令
ioctl(mIonFd, ION_IOC_MAP, &fd_data)
4.mmap 映射一段共享内存
base = mmap(0, ionAllocData.len, PROT_READ|PROT_WRITE,
MAP_SHARED, fd_data.fd, 0);
5.ioctl ION_IOC_FREE 释放底层的句柄
ioctl(mIonFd, ION_IOC_FREE, &handle_data);
我们按照这个流程分析ion的源码。
本文基于Android的Linux内核版本3.1.8
正文
什么是ion?如果是音视频,Camera的工程师会对这个驱动比较熟悉。最早的GPU和其他驱动协作申请一块内存进行绘制是使用比较粗暴的共享内存。在Android系统中使用的是匿名内存。最早由三星实现了一个Display和Camera共享内存的问题,曾经在Linux社区掀起过一段时间。之后各路大牛不断的改进之下,就成为了dma_buf驱动。并在 Linux-3.3 主线版本合入主线。现在已经广泛的运用到各大多媒体开发中。
首先介绍dma_buf的2个角色,importer和exporter。importer是dma_buf驱动中的图元消费者,exporter是dma_buf驱动中的图元生产者。
这里借用大佬的图片:
dma_buf.png
ion是基于dma_buf设计完成的。经过阅读源码,其实不少思路和Android的匿名内存有点相似。阅读本文之前就算不知道dma_buf的设计思想也没关系,我不会仔细到每一行,我会注重其在gralloc服务中的申请流程,看看ion是如何管理共享内存,为什么要抛弃ashmem。
ion初始化
我们先来看看ion的file_operation:
static const struct file_operations ion_fops = {
.owner = THIS_MODULE,
.open = ion_open,
.release = ion_release,
.unlocked_ioctl = ion_ioctl,
.compat_ioctl = compat_ion_ioctl,
};
只有一个open和ioctl函数。但是没有mmap映射。因此mmap映射的时候一定其他对象在工作。
static struct platform_driver ion_driver = {
.probe = tegra_ion_probe,
.remove = tegra_ion_remove,
.driver = { .name = "ion-tegra" }
};
module_platform_driver(ion_driver);
module_platform_driver实际上就是我之前经常提到过的module_init的一个宏,多了一个register注册到对应名字的平台中的步骤。在这里面注册了一个probe方法指针,probe指向的tegra_ion_probe是加载内核模块注册的时候调用。
static struct ion_device *idev;
static int num_heaps;
static struct ion_heap **heaps;
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_kzalloc(&pdev->dev,
sizeof(struct ion_heap *) * pdata->nr,
GFP_KERNEL);
idev = ion_device_create(NULL);
if (IS_ERR_OR_NULL(idev))
return PTR_ERR(idev);
/* 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);
if (IS_ERR_OR_NULL(heaps[i])) {
err = PTR_ERR(heaps[i]);
goto err;
}
ion_device_add_heap(idev, heaps[i]);
}
platform_set_drvdata(pdev, idev);
return 0;
err:
for (i = 0; i < num_heaps; i++) {
if (heaps[i])
ion_heap_destroy(heaps[i]);
}
return err;
}
先来看看对应的结构体:
struct ion_platform_data {
int nr;/*有多少ion_platform_heap*/
struct ion_platform_heap *heaps;/*ion_platform_heap 指针数组*/
};
再来看看对应ion内的堆结构体:
struct ion_platform_heap {
enum ion_heap_type type;/*heap 类型*/
unsigned int id;
const char *name;
ion_phys_addr_t base;/*heap 起始地址*/
size_t size;/*heap 大小*/
ion_phys_addr_t align;/*heap需要对齐*/
void *priv;
};
完成的事情如下几个步骤:
1.ion_device_create 初始化注册ion驱动
2.ion_heap_create和ion_device_add_heap 初始化ion_platform_data中申请内存的堆,并添加到ion驱动中管理
ion_device_create 初始化注册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;
idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL);
..
idev->dev.minor = MISC_DYNAMIC_MINOR;
idev->dev.name = "ion";
idev->dev.fops = &ion_fops;
idev->dev.parent = NULL;
ret = misc_register(&idev->dev);
...
idev->debug_root = debugfs_create_dir("ion", NULL);
if (!idev->debug_root) {
...
goto debugfs_done;
}
...
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;
return idev;
}
我们不关注debug模式。其实整个就是我们分析了很多次的方法。把这个对象注册miscdevice中。等到insmod就会把整个整个内核模块从dev_t的map中关联出来。
我们来看看这个驱动结构体:
struct ion_device {
struct miscdevice dev;/*驱动设备符*/
struct rb_root buffers;/*ion_buffer ion内核缓冲区 红黑树*/
struct mutex buffer_lock;
struct rw_semaphore lock;
struct plist_head heaps;/*ion_buffer ion内核堆链表*/
long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
unsigned long arg);
struct rb_root clients;/*每一个open进来的对象*/
struct dentry *debug_root;
struct dentry *heaps_debug_root;
struct dentry *clients_debug_root;
};
ion_heap_create 创建ion内存申请堆
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:
return ERR_PTR(-EINVAL);
}
...
heap->name = heap_data->name;
heap->id = heap_data->id;
return heap;
}
这里有四个不同堆会申请出来,我们主要来看看默认的ION_HEAP_TYPE_SYSTEM对应的heap流程。
ion_system_heap_create
static const unsigned int orders[] = {8, 4, 0};
static struct ion_heap_ops system_heap_ops = {
.allocate = ion_system_heap_allocate,
.free = ion_system_heap_free,
.map_dma = ion_system_heap_map_dma,
.unmap_dma = ion_system_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_heap_map_user,
.shrink = ion_system_heap_shrink,
};
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
{
struct ion_system_heap *heap;
int i;
heap = kzalloc(sizeof(struct ion_system_heap) +
sizeof(struct ion_page_pool *) * num_orders,
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;
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;
pool = ion_page_pool_create(gfp_flags, orders[i]);
if (!pool)
goto destroy_pools;
heap->pools[i] = pool;
}
heap->heap.debug_show = ion_system_heap_debug_show;
return &heap->heap;
....
}
struct ion_system_heap {
struct ion_heap heap;
struct ion_page_pool *pools[0];
};
其实真正象征ion的内存堆是下面这个结构体
struct ion_heap {
struct plist_node node;
struct ion_device *dev;
enum ion_heap_type type;/*类型*/
struct ion_heap_ops *ops;/*堆操作*/
unsigned long flags;/*此时是ION_HEAP_FLAG_DEFER_FREE*/
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 *);
};
不管原来的那个heap,会新建3个ion_system_heap,分别order为8,4,0,大于4为大内存。意思就是这个heap中持有一个ion_page_pool 页资源池子,里面只有对应order的2的次幂,内存块。其实就和伙伴系统有点相似。
还会设置flag为ION_HEAP_FLAG_DEFER_FREE,这个标志位后面会用到。
ion_page_pool_create 创建页资源池
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
{
struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_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;
mutex_init(&pool->mutex);
plist_node_init(&pool->list, order);
return pool;
}
struct ion_page_pool {
int high_count;
int low_count;
struct list_head high_items;
struct list_head low_items;
struct mutex mutex;
gfp_t gfp_mask;
unsigned int order;
struct plist_node list;
};
在pool中分为2个链表一个是high_items,另一个是low_items。他们之间的区分在此时就是以2为底4的次幂为分界线。
ion_device_add_heap
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
{
struct dentry *debug_file;
....
if (heap->flags & 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);
heap->dev = dev;
down_write(&dev->lock);
plist_node_init(&heap->node, -heap->id);
plist_add(&heap->node, &dev->heaps);
...
#ifdef DEBUG_HEAP_SHRINKER
...
#endif
up_write(&dev->lock);
}
因为打开了标志位ION_HEAP_FLAG_DEFER_FREE和heap存在shrink方法。因此会初始化两个回收函数。
1.ion_heap_init_deferred_free
2.ion_heap_init_shrinker
3.ion_heap 添加到ion_device的heap红黑树中。
ion_heap_init_deferred_free 启动销毁heap中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;
}
buffer = list_first_entry(&am