参考:
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