Android ION源碼淺析

Android ION

ION是Google在Android 4.0上推出的一個(gè)通用的內(nèi)存管理器,目的是為了解決眾多廠商的內(nèi)存管理器碎片化問(wèn)題。如高通的PMEM、NVIDIA的NVMAP等。用戶空間、內(nèi)核驅(qū)動(dòng)均可以使用ION分配內(nèi)存,除此之外,ION也提供多個(gè)Client之間共享內(nèi)存。通常,SurfaceFlinger/Camera/Audio等會(huì)使用ION。

本文以MSM平臺(tái)SYSTEM HEAP為例。


Android ION數(shù)據(jù)結(jié)構(gòu)

ion_device

struct ion_device {
    struct miscdevice dev;
    struct rb_root buffers;
    struct mutex buffer_lock;
    struct rw_semaphore lock;
    struct plist_head heaps;
    ...
    struct rb_root clients;
    ...
};
  • dev:具體平臺(tái)上的ION設(shè)備
  • buffers:通過(guò)紅黑樹管理所有通過(guò)ion分配的ion_buffer
  • heaps:ion設(shè)備所有的heaps鏈表,通過(guò)plist(priority-sorted list)管理
  • clients:紅黑樹管理使用ion的所有ion_client

ion_client

struct ion_client {
    struct rb_node node;
    struct ion_device *dev;
    struct rb_root handles;
    struct idr idr;
    struct mutex lock;
    char *name;
    char *display_name;
    int display_serial;
    struct task_struct *task;
    pid_t pid;
    struct dentry *debug_root;
};
  • ion_client每個(gè)進(jìn)程最多有一個(gè)
  • node:所有client所在的紅黑樹節(jié)點(diǎn)
  • dev:指向ion device
  • handles:該client所管理的所有ion_handle紅黑樹
  • task:client所屬進(jìn)程的task_struct

ion_handle

struct ion_handle {
    struct kref ref;
    struct ion_client *client;
    struct ion_buffer *buffer;
    struct rb_node node;
    unsigned int kmap_cnt;
    int id;
};
  • ion_handle用于連接client與buffer,修改該結(jié)構(gòu)需要lock所在的client
  • ref:引用計(jì)數(shù)
  • client:所屬的ion_client
  • buffer:該handle使用的ion_buffer
  • node:client handles紅黑樹節(jié)點(diǎn)
  • id:每個(gè)client內(nèi)唯一的id,由client->idr分配

ion_buffer

struct ion_buffer {
    struct kref ref;
    union {
        struct rb_node node;
        struct list_head list;
    };
    struct ion_device *dev;
    struct ion_heap *heap;
    unsigned long flags;
    unsigned long private_flags;
    size_t size;
    union {
        void *priv_virt;
        ion_phys_addr_t priv_phys;
    };
    struct mutex lock;
    int kmap_cnt;
    void *vaddr;
    struct sg_table *sg_table;
    struct page **pages;
    struct list_head vmas;
    /* used to track orphaned buffers */
    int handle_count;
    char task_comm[TASK_COMM_LEN];
    pid_t pid;
};
  • ref:引用計(jì)數(shù)
  • node:ion_device buffers紅黑樹節(jié)點(diǎn)
  • heap:buffer來(lái)自的heap
  • kmap_cnt:ion_buffer映射到內(nèi)核的次數(shù)
  • vaddr:如果kmap_cnt不為0,vaddr位內(nèi)核映射
  • dmap_cnt:ion_buffer映射到dma的次數(shù)
  • sg_table:如果dmap_cnt不為0,為sg table
  • vmas:映射到ion_buffer的vma列表
  • handle:應(yīng)用該ion_buffer的handle數(shù)
  • task_comm: 通過(guò)handle引用該ion_buffer的最后一個(gè)client所屬進(jìn)程

ion_heap

struct ion_heap {
    struct plist_node node;
    struct ion_device *dev;
    enum ion_heap_type type;
    struct ion_heap_ops *ops;
    unsigned long flags;
    unsigned int id;
    const char *name;
    struct shrinker shrinker;
    void *priv;
    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 *);
    atomic_t total_allocated;
    atomic_t total_handles;
};
  • node:ion device通過(guò)plist管理所有的ion_heap
  • type:ion heap類型
  • ops:ion heap的操作
  • id:ion heap id同時(shí)也表示優(yōu)先級(jí),id越小優(yōu)先級(jí)越高,參考plist
  • free_list:deferred free list
  • free_list_size:size of deferred free list
  • waitqueue:queue to wait on from deferred free thread
  • task:task struct of deferred free thread

ion_system_heap

struct ion_system_heap {
    struct ion_heap heap;
    struct ion_page_pool **uncached_pools;
    struct ion_page_pool **cached_pools;
};
  • heap:關(guān)聯(lián)的ion_heap
  • uncached_pools:空閑頁(yè)所在的pool,如果ion heap的flag為uncached從此分配和釋放
  • cached_pools: 空閑頁(yè)所在的pool,如果ion heap的flag為cached從此分配和釋放

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

  • high_count:pool中屬于高端內(nèi)存的內(nèi)存塊數(shù)目
  • low_count: pool中屬于低端內(nèi)存的內(nèi)存塊數(shù)目
  • hight_items:pool中屬于高端內(nèi)存的內(nèi)存塊鏈表
  • low_items: pool中屬于低端內(nèi)存的內(nèi)存塊鏈表
  • order:pool中內(nèi)存塊的階,也就是pool中內(nèi)存塊的大小為(1<<order) * PAGE_SIZE
  • list: plist node for list of pools

小結(jié)

ION數(shù)據(jù)結(jié)構(gòu)關(guān)系圖

Android ION設(shè)備初始化

ION設(shè)備初始化流程
  1. 解析ion dtsi配置獲取系統(tǒng)當(dāng)前配置的堆
  2. 創(chuàng)建ion設(shè)備,同時(shí)在debugfs中創(chuàng)建ion目錄以及ion目錄下的heaps以及clients目錄,最后 初始化ion device所管理的buffers/clients紅黑樹以及heaps優(yōu)先級(jí)鏈表
  3. 創(chuàng)建系統(tǒng)當(dāng)前配置的堆,以system heap為例,創(chuàng)建ion_system_heap以及所管理的page pool
  4. 根據(jù)堆的標(biāo)志以及有無(wú)shrink函數(shù),分別初始化ion heap的延遲釋放ion_buffer鏈表以及注冊(cè)shrink函數(shù),根據(jù)ion heap的ID也就是優(yōu)先級(jí)將其添加到設(shè)備的優(yōu)先級(jí)鏈表中,在debugfs的ion/heaps下創(chuàng)建以堆名命名的目錄
  5. 注冊(cè)show_mem_notifier,用于調(diào)試,當(dāng)cat /sys/kernel/debug/show_mem_notifier時(shí),調(diào)試信息將打印在kernel log中

ION system heap分配內(nèi)存

ION system heap分配內(nèi)存流程
  1. 在使用Ion heap之前,首先要open設(shè)備文件,在ion_open中為該進(jìn)程創(chuàng)建ion_client,初始化ion_client后將其插入到ion dev的clients紅黑樹中,并未改client創(chuàng)建debugfs
  2. 對(duì)于ion heap分配,我們從ion_alloc開始分析,在iondev的ion heap優(yōu)先級(jí)鏈表中搜索我們請(qǐng)求的目標(biāo)ion heap,如果找到調(diào)用ion_buffer_create
  3. 在ion_buffer_create中首先創(chuàng)建ion_buffer,然后調(diào)用請(qǐng)求堆的allocate函數(shù),這里我們只關(guān)心ion_system_heap_allocate
  4. 重點(diǎn)分析ion_system_heap_allocate
static int ion_system_heap_allocate(struct ion_heap         *heap, struct ion_buffer *buffer, unsigned long size, unsigned long align, unsigned long flags)
{
    ...
    struct list_head pages;
    struct list_head pages_from_pool;
    struct page_info *info, *tmp_info;
    int i = 0;
    unsigned int nents_sync = 0;
    unsigned long size_remaining = PAGE_ALIGN(size);
    unsigned int max_order = orders[0];
    struct pages_mem data;
    ...
  • Pages為從PCP/buddy中分配的page鏈表
  • pages_from_pool為從Ion system heap page pool分配的page鏈表
  • data用來(lái)記錄從PCP/buddy中分配的內(nèi)存的大小
  • size_remaining用來(lái)表示本次需要分配的內(nèi)存大小
    ...
    while (size_remaining > 0) {
        info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order);
        if (!info)
            goto err;

        sz = (1 << info->order) * PAGE_SIZE;

        if (info->from_pool) {
            list_add_tail(&info->list, &pages_from_pool);
        } else {
            list_add_tail(&info->list, &pages);
            data.size += sz;
            ++nents_sync;
        }
        size_remaining -= sz;
        max_order = info->order;
        i++;
    }
    ...
  • alloc_largest_available每次分配小于size_remaining的最大的2的order次冪的內(nèi)存,sz表示本次分配的內(nèi)存大小,如果本次是從pool中分配的添加到pages_from_pool鏈表中,否則添加到pages鏈表,i表示分配size_remaining大小的內(nèi)存用了多少次,nents_sync表示從PCP/buddy中分配內(nèi)存塊的個(gè)數(shù)(次數(shù)),這兩個(gè)值是為了后面創(chuàng)建scatterlist服務(wù),scatterlist可以認(rèn)為是一個(gè)內(nèi)存塊的鏈表,后面的do while循環(huán)主要是為了將我們分配得到的pages_from_pool和pages兩個(gè)鏈表進(jìn)行合并
    ...
    do {
        info = list_first_entry_or_null(&pages, struct page_info, list);
        tmp_info = list_first_entry_or_null(&pages_from_pool,
                            struct page_info, list);
        if (info && tmp_info) {
            if (info->order >= tmp_info->order) {
                i = process_info(info, sg, sg_sync, &data, i);
                sg_sync = sg_next(sg_sync);
            } else {
                i = process_info(tmp_info, sg, 0, 0, i);
            }
        } else if (info) {
            i = process_info(info, sg, sg_sync, &data, i);
            sg_sync = sg_next(sg_sync);
        } else if (tmp_info) {
            i = process_info(tmp_info, sg, 0, 0, i);
        } else {
            BUG();
        }
        sg = sg_next(sg);

    } while (sg);
    ...

  • 將pages以及pages_from_pool鏈表中物理地址連續(xù)的內(nèi)存塊按照order大小,分別添加到sg以及sg_sync兩個(gè)scatterlist中,其中pages中的內(nèi)存塊同時(shí)添加到兩個(gè)scatterlist中,pages_from_pool中的只添加到sg scatterlist中。同時(shí)將data->pages初始化為sg_sync scatterlist中所有page頁(yè)
  • 分配到的內(nèi)存的組織結(jié)構(gòu)圖


    ...
    ret = msm_ion_heap_pages_zero(data.pages, data.size >> PAGE_SHIFT);
    if (ret) {
        pr_err("Unable to zero pages\n");
        goto err_free_sg2;
    }

    if (nents_sync)
        dma_sync_sg_for_device(NULL, table_sync.sgl, table_sync.nents,
                       DMA_BIDIRECTIONAL);

    buffer->priv_virt = table;
    if (nents_sync)
        sg_free_table(&table_sync);
    msm_ion_heap_free_pages_mem(&data);
    return 0;
    ...

  • 利用data結(jié)構(gòu)體,將從buddy分配到的所有內(nèi)存塊清零
  • 對(duì)于dma_sync_sg_for_device,暫未搞懂
  • 最后釋放table_sync scatterlist以及data
  • 在真正分配完內(nèi)存之后,還需要?jiǎng)?chuàng)建ion_handle,初始化后添加到ion client所管理的handles紅黑樹中

ION system heap內(nèi)存釋放

當(dāng)沒(méi)有ion client引用ion buffer的時(shí)候,ion buffer就會(huì)被釋放,通過(guò)上文我們知道ion buffer的可用內(nèi)存實(shí)際上是由物理地址連續(xù)的多個(gè)內(nèi)存塊組成的scatterlist。釋放的時(shí)候,就是遍歷該scatterlist,釋放該內(nèi)存塊,如果沒(méi)有設(shè)置ION_PRIV_FLAG_SHRINKER_FREE標(biāo)志,將內(nèi)存塊釋放到ion system heap page pool;否則釋放到PCP/buddy

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。