Qualcomm MSM ION

chipset: MSM8X25Q node

codebase: Android 4.1 編程


ION基本概念

ION概念:

ION是Google的下一代內存管理器,用來支持不一樣的內存分配機制,如CARVOUT(PMEM),物理連續內存(kmalloc), 虛擬地址連續但物理不連續內存(vmalloc), IOMMU等。 cookie

ION與PMEM相似,管理一或多個內存池,其中有一些會在boot time的時候預先分配,以備給特殊的硬件使用(GPU,顯示控制器等)。它經過ION heaps來管理這些pool。 它能夠被userspace的process之間或者內核中的模塊之間進行內存共享

用戶空間和內核空間均可以使用ION,用戶空間是經過/dev/ion來建立client的。 app

說到client, 順便看下ION相關比較重要的幾個概念。 less

  • Heap: 用來表示內存分配的相關信息,包括id, type, name等。用struct ion_heap表示。 ide

  • Client: Ion的使用者,用戶空間和內核控件要使用ION的buffer,必須先建立一個client,一個client能夠有多個buffer,用struct ion_buffer表示。 函數

  • Handle: 將buffer該抽象出來,能夠認爲ION用handle來管理buffer,通常用戶直接拿到的是handle,而不是buffer。 用struct ion_handle表示。 ui

heap類型

因爲ION可使用多種memory分配機制,例如物理連續和不連續的,因此ION使用enum ion_heap_type表示。 this

/**
 * enum ion_heap_types - list of all possible types of heaps
 * @ION_HEAP_TYPE_SYSTEM:     memory allocated via vmalloc
 * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
 * @ION_HEAP_TYPE_CARVEOUT:     memory allocated from a prereserved
 *                  carveout heap, allocations are physically
 *                  contiguous
 * @ION_HEAP_TYPE_IOMMU: IOMMU memory
 * @ION_HEAP_TYPE_CP:     memory allocated from a prereserved
 *                carveout heap, allocations are physically
 *                contiguous. Used for content protection.
 * @ION_HEAP_TYPE_DMA:          memory allocated via DMA API
 * @ION_HEAP_END:        helper for iterating over heaps
 */
enum ion_heap_type {
    ION_HEAP_TYPE_SYSTEM,
    ION_HEAP_TYPE_SYSTEM_CONTIG,
    ION_HEAP_TYPE_CARVEOUT,
    ION_HEAP_TYPE_IOMMU,
    ION_HEAP_TYPE_CP,
    ION_HEAP_TYPE_DMA,
    ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
                 are at the end of this enum */
    ION_NUM_HEAPS,
};

代碼中的註釋很明確地說明了哪一種type對應的是分配哪一種memory。不一樣type的heap須要不一樣的method去分配,不過都是用struction_heap_ops來表示的。如如下例子: atom

static struct ion_heap_ops carveout_heap_ops = {
    .allocate = ion_carveout_heap_allocate,
    .free = ion_carveout_heap_free,
    .phys = ion_carveout_heap_phys,
    .map_user = ion_carveout_heap_map_user,
    .map_kernel = ion_carveout_heap_map_kernel,
    .unmap_user = ion_carveout_heap_unmap_user,
    .unmap_kernel = ion_carveout_heap_unmap_kernel,
    .map_dma = ion_carveout_heap_map_dma,
    .unmap_dma = ion_carveout_heap_unmap_dma,
    .cache_op = ion_carveout_cache_ops,
    .print_debug = ion_carveout_print_debug,
    .map_iommu = ion_carveout_heap_map_iommu,
    .unmap_iommu = ion_carveout_heap_unmap_iommu,
};

static struct ion_heap_ops kmalloc_ops = {
    .allocate = ion_system_contig_heap_allocate,
    .free = ion_system_contig_heap_free,
    .phys = ion_system_contig_heap_phys,
    .map_dma = ion_system_contig_heap_map_dma,
    .unmap_dma = ion_system_heap_unmap_dma,
    .map_kernel = ion_system_heap_map_kernel,
    .unmap_kernel = ion_system_heap_unmap_kernel,
    .map_user = ion_system_contig_heap_map_user,
    .cache_op = ion_system_contig_heap_cache_ops,
    .print_debug = ion_system_contig_print_debug,
    .map_iommu = ion_system_contig_heap_map_iommu,
    .unmap_iommu = ion_system_heap_unmap_iommu,
};

Heap ID:

同一種type的heap上固然能夠分爲若該幹個chunk供用戶使用,因此ION又使用ID來區分了。例如在type爲ION_HEAP_TYPE_CARVEOUT的heap上,audio和display部分都須要使用,ION就用ID來區分。

Heap id用enumion_heap_ids表示。

/**
 * These are the only ids that should be used for Ion heap ids.
 * The ids listed are the order in which allocation will be attempted
 * if specified. Don't swap the order of heap ids unless you know what
 * you are doing!
 * Id's are spaced by purpose to allow new Id's to be inserted in-between (for
 * possible fallbacks)
 */

enum ion_heap_ids {
    INVALID_HEAP_ID = -1,
    ION_CP_MM_HEAP_ID = 8,
    ION_CP_MFC_HEAP_ID = 12,
    ION_CP_WB_HEAP_ID = 16, /* 8660 only */
    ION_CAMERA_HEAP_ID = 20, /* 8660 only */
    ION_SF_HEAP_ID = 24,
    ION_IOMMU_HEAP_ID = 25,
    ION_QSECOM_HEAP_ID = 26,
    ION_AUDIO_HEAP_BL_ID = 27,
    ION_AUDIO_HEAP_ID = 28,

    ION_MM_FIRMWARE_HEAP_ID = 29,
    ION_SYSTEM_HEAP_ID = 30,

    ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_SECURE flag */
};

Heap 定義:

       瞭解了heaptype和id,看看如何被用到了,本平臺使用的文件爲board-qrd7627a.c,有以下定義:

/**
 * These heaps are listed in the order they will be allocated.
 * Don't swap the order unless you know what you are doing!
 */
struct ion_platform_heap msm7627a_heaps[] = {
        {
            .id    = ION_SYSTEM_HEAP_ID,
            .type    = ION_HEAP_TYPE_SYSTEM,
            .name    = ION_VMALLOC_HEAP_NAME,
        },
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
        /* PMEM_ADSP = CAMERA */
        {
            .id    = ION_CAMERA_HEAP_ID,
            .type    = CAMERA_HEAP_TYPE,
            .name    = ION_CAMERA_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_mm_ion_pdata,
            .priv    = (void *)&ion_cma_device.dev,
        },
        /* AUDIO HEAP 1*/
        {
            .id    = ION_AUDIO_HEAP_ID,
            .type    = ION_HEAP_TYPE_CARVEOUT,
            .name    = ION_AUDIO_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_ion_pdata,
        },
        /* PMEM_MDP = SF */
        {
            .id    = ION_SF_HEAP_ID,
            .type    = ION_HEAP_TYPE_CARVEOUT,
            .name    = ION_SF_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_ion_pdata,
        },
        /* AUDIO HEAP 2*/
        {
            .id    = ION_AUDIO_HEAP_BL_ID,
            .type  = ION_HEAP_TYPE_CARVEOUT,
            .name  = ION_AUDIO_BL_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_ion_pdata,
            .base = BOOTLOADER_BASE_ADDR,
        },

#endif
};

ION Handle:

當Ion client分配buffer時,相應的一個惟一的handle也會被指定,固然client能夠屢次申請ion buffer。申請好buffer以後,返回的是一個ion handle, 不過要知道Ion buffer才和實際的內存相關,包括size, address等信息。Struct ion_handle和struct ion_buffer以下:

/**
 * ion_handle - a client local reference to a buffer
 * @ref:        reference count
 * @client:        back pointer to the client the buffer resides in
 * @buffer:        pointer to the buffer
 * @node:        node in the client's handle rbtree
 * @kmap_cnt:        count of times this client has mapped to kernel
 * @dmap_cnt:        count of times this client has mapped for dma
 *
 * Modifications to node, map_cnt or mapping should be protected by the
 * lock in the client.  Other fields are never changed after initialization.
 */
struct ion_handle {
    struct kref ref;
    struct ion_client *client;
    struct ion_buffer *buffer;
    struct rb_node node;
    unsigned int kmap_cnt;
    unsigned int iommu_map_cnt;
};

/**
 * struct ion_buffer - metadata for a particular buffer
 * @ref:        refernce count
 * @node:        node in the ion_device buffers tree
 * @dev:        back pointer to the ion_device
 * @heap:        back pointer to the heap the buffer came from
 * @flags:        buffer specific flags
 * @size:        size of the buffer
 * @priv_virt:        private data to the buffer representable as
 *            a void *
 * @priv_phys:        private data to the buffer representable as
 *            an ion_phys_addr_t (and someday a phys_addr_t)
 * @lock:        protects the buffers cnt fields
 * @kmap_cnt:        number of times the buffer is mapped to the kernel
 * @vaddr:        the kenrel mapping if kmap_cnt is not zero
 * @dmap_cnt:        number of times the buffer is mapped for dma
 * @sg_table:        the sg table for the buffer if dmap_cnt is not zero
*/
struct ion_buffer {
    struct kref ref;
    struct rb_node node;
    struct ion_device *dev;
    struct ion_heap *heap;
    unsigned long flags;
    size_t size;
    union {
        void *priv_virt;
        ion_phys_addr_t priv_phys;
    };
    struct mutex lock;
    int kmap_cnt;
    void *vaddr;
    int dmap_cnt;
    struct sg_table *sg_table;
    int umap_cnt;
    unsigned int iommu_map_cnt;
    struct rb_root iommu_maps;
    int marked;
};

ION Client:

用戶空間和內核空間均可以成爲client,不過建立的方法稍稍有點區別,先了解下基本的操做流程吧。

 內核空間:

先建立client:

struct ion_client *ion_client_create(struct ion_device *dev,
                     unsigned int heap_mask,
                     const char *name)

heap_mask: 能夠分配的heap type,如carveout,system heap, iommu等。

高通使用msm_ion_client_create函數封裝了下。

 有了client以後就能夠分配內存:

struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
                 size_t align, unsigned int flags)

flags: 分配的heap id.

有了handle也就是buffer以後就準備使用了,不過仍是物理地址,須要map:

void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle,
            unsigned long flags)

用戶空間:

用戶空間若是想使用ION,也必須先要建立client,不過它是打開/dev/ion,實際上它最終也會調用ion_client_create。

不過和內核空間建立client的一點區別是,用戶空間不能選擇heap type,可是內核空間卻能夠。

另外,用戶空間是經過IOCTL來分配內存的,cmd爲ION_IOC_ALLOC.

ion_fd = open("/dev/ion", O_ RDONLY | O_SYNC); 
ioctl(ion_fd, ION_IOC_ALLOC, alloc);

alloc爲struct ion_allocation_data,len是申請buffer的長度,flags是heap id。

/**
 * struct ion_allocation_data - metadata passed from userspace for allocations
 * @len:    size of the allocation
 * @align:    required alignment of the allocation
 * @flags:    flags passed to heap
 * @handle:    pointer that will be populated with a cookie to use to refer
 *        to this allocation
 *
 * Provided by userspace as an argument to the ioctl
 */
struct ion_allocation_data {
    size_t len;
    size_t align;
    unsigned int flags;
    struct ion_handle *handle;
};

分配好了buffer以後,若是用戶空間想使用buffer,先須要mmap. ION是經過先調用IOCTL中的ION_IOC_SHARE/ION_IOC_MAP來獲得能夠mmap的fd,而後再執行mmap獲得bufferaddress.

而後,你也能夠將此fd傳給另外一個進程,如經過binder傳遞。在另外一個進程中經過ION_IOC_IMPORT這個IOCTL來獲得這塊共享buffer了。

來看一個例子:

進程A:
int ionfd = open("/dev/ion", O_RDONLY | O_DSYNC); 
alloc_data.len = 0x1000; 
alloc_data.align = 0x1000; 
alloc_data.flags = ION_HEAP(ION_CP_MM_HEAP_ID);  
rc = ioctl(ionfd,ION_IOC_ALLOC, &alloc_data); 
fd_data.handle = alloc_data.handle;  
rc = ioctl(ionfd,ION_IOC_SHARE,&fd_data);  
shared_fd = fd_data.fd; 

進程B:
fd_data.fd = shared_fd; 
rc = ioctl(ionfd,ION_IOC_IMPORT,&fd_data);


ION的運做流程

從上一篇ION基本概念中,咱們瞭解了heaptype, heap id, client, handle以及如何使用,本篇再從原理上分析下ION的運做流程。

MSM8x25Q平臺使用的是board-qrd7627.c,ION相關定義以下:

/**
 * These heaps are listed in the order they will be allocated.
 * Don't swap the order unless you know what you are doing!
 */
struct ion_platform_heap msm7627a_heaps[] = {
        {
            .id    = ION_SYSTEM_HEAP_ID,
            .type    = ION_HEAP_TYPE_SYSTEM,
            .name    = ION_VMALLOC_HEAP_NAME,
        },
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
        /* PMEM_ADSP = CAMERA */
        {
            .id    = ION_CAMERA_HEAP_ID,
            .type    = CAMERA_HEAP_TYPE,
            .name    = ION_CAMERA_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_mm_ion_pdata,
            .priv    = (void *)&ion_cma_device.dev,
        },
        /* AUDIO HEAP 1*/
        {
            .id    = ION_AUDIO_HEAP_ID,
            .type    = ION_HEAP_TYPE_CARVEOUT,
            .name    = ION_AUDIO_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_ion_pdata,
        },
        /* PMEM_MDP = SF */
        {
            .id    = ION_SF_HEAP_ID,
            .type    = ION_HEAP_TYPE_CARVEOUT,
            .name    = ION_SF_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_ion_pdata,
        },
        /* AUDIO HEAP 2*/
        {
            .id    = ION_AUDIO_HEAP_BL_ID,
            .type  = ION_HEAP_TYPE_CARVEOUT,
            .name  = ION_AUDIO_BL_HEAP_NAME,
            .memory_type = ION_EBI_TYPE,
            .extra_data = (void *)&co_ion_pdata,
            .base = BOOTLOADER_BASE_ADDR,
        },

#endif
};

static struct ion_co_heap_pdata co_ion_pdata = {
    .adjacent_mem_id = INVALID_HEAP_ID,
    .align = PAGE_SIZE,
};

static struct ion_co_heap_pdata co_mm_ion_pdata = {
    .adjacent_mem_id = INVALID_HEAP_ID,
    .align = PAGE_SIZE,
};

static u64 msm_dmamask = DMA_BIT_MASK(32);

static struct platform_device ion_cma_device = {
    .name = "ion-cma-device",
    .id = -1,
    .dev = {
        .dma_mask = &msm_dmamask,
        .coherent_dma_mask = DMA_BIT_MASK(32),
    }
};

Qualcomm提示了不要輕易調換順序,由於後面代碼處理是將順序定死了的,一旦你調換了,代碼就沒法正常運行了。

另外, 本系統中只使用了ION_HEAP_TYPE_CARVEOUT和 ION_HEAP_TYPE_SYSTEM這兩種heap type.

對於ION_HEAP_TYPE_CARVEOUT的內存分配,後面將會發現,其實就是以前講述過的使用mem pool來分配的。

Platform device以下,在msm_ion.c中用到。

static struct ion_platform_data ion_pdata = {
    .nr = MSM_ION_HEAP_NUM,
    .has_outer_cache = 1,
    .heaps = msm7627a_heaps,
};

static struct platform_device ion_dev = {
    .name = "ion-msm",
    .id = 1,
    .dev = { .platform_data = &ion_pdata },
};

ION初始化

轉到msm_ion.c,ion.c的某些函數也被從新封裝了下.萬事都從設備匹配開始:

static struct platform_driver msm_ion_driver = {
    .probe = msm_ion_probe,
    .remove = msm_ion_remove,
    .driver = { .name = "ion-msm" }
};
static int __init msm_ion_init(void)
{
    /*調用msm_ion_probe */
    return platform_driver_register(&msm_ion_driver);
}

static int msm_ion_probe(struct platform_device *pdev)
{
    /*即board-qrd7627a.c中的ion_pdata */
    struct ion_platform_data *pdata = pdev->dev.platform_data;
    int err;
    int i;

    /*heap數量*/
    num_heaps = pdata->nr;
    /*分配struct ion_heap */
    heaps = kcalloc(pdata->nr, sizeof(struct ion_heap *), GFP_KERNEL);

    if (!heaps) {
        err = -ENOMEM;
        goto out;
    }
    /*建立節點,最終是/dev/ion,供用戶空間操做。*/
    idev = ion_device_create(NULL);
    if (IS_ERR_OR_NULL(idev)) {
        err = PTR_ERR(idev);
        goto freeheaps;
    }
    /*最終是根據adjacent_mem_id 是否認義了來分配相鄰內存,
咱們沒用到,忽略此函數。*/
    msm_ion_heap_fixup(pdata->heaps, num_heaps);

    /* 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*/
        msm_ion_allocate(heap_data);

        heap_data->has_outer_cache = pdata->has_outer_cache;
        /*建立ion heap。*/
        heaps[i] = ion_heap_create(heap_data);
        if (IS_ERR_OR_NULL(heaps[i])) {
            heaps[i] = 0;
            continue;
        } else {
            if (heap_data->size)
                pr_info("ION heap %s created at %lx "
                    "with size %x\n", heap_data->name,
                              heap_data->base,
                              heap_data->size);
            else
                pr_info("ION heap %s created\n",
                              heap_data->name);
        }
        /*建立的heap添加到idev中,以便後續使用。*/
        ion_device_add_heap(idev, heaps[i]);
    }
    /*檢查heap之間是否有重疊部分*/
    check_for_heap_overlap(pdata->heaps, num_heaps);
    platform_set_drvdata(pdev, idev);
    return 0;

freeheaps:
    kfree(heaps);
out:
    return err;
}

經過ion_device_create建立/dev/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);
    if (!idev)
        return ERR_PTR(-ENOMEM);
    /*是個misc設備*/
    idev->dev.minor = MISC_DYNAMIC_MINOR;
    /*節點名字爲ion*/
    idev->dev.name = "ion";
    /*fops爲ion_fops,因此對應ion的操做都會調用ion_fops的函數指針。*/
    idev->dev.fops = &ion_fops;
    idev->dev.parent = NULL;
    ret = misc_register(&idev->dev);
    if (ret) {
        pr_err("ion: failed to register misc device.\n");
        return ERR_PTR(ret);
    }
    /*建立debugfs目錄,路徑爲/sys/kernel/debug/ion/*/
    idev->debug_root = debugfs_create_dir("ion", NULL);
    if (IS_ERR_OR_NULL(idev->debug_root))
        pr_err("ion: failed to create debug files.\n");

    idev->custom_ioctl = custom_ioctl;
    idev->buffers = RB_ROOT;
    mutex_init(&idev->lock);
    idev->heaps = RB_ROOT;
    idev->clients = RB_ROOT;
    /*在ion目錄下建立一個check_leaked_fds文件,用來檢查Ion的使用是否有內存泄漏。若是申請了ion以後不須要使用卻沒有釋放,就會致使memory leak.*/
    debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev,
                &debug_leak_fops);
    return idev;
}

msm_ion_allocate:
static void msm_ion_allocate(struct ion_platform_heap *heap)
{

    if (!heap->base && heap->extra_data) {
        unsigned int align = 0;
        switch (heap->type) {
        /*獲取align參數*/
        case ION_HEAP_TYPE_CARVEOUT:
            align =
            ((struct ion_co_heap_pdata *) heap->extra_data)->align;
            break;
        /*此type咱們沒使用到。*/
        case ION_HEAP_TYPE_CP:
        {
            struct ion_cp_heap_pdata *data =
                (struct ion_cp_heap_pdata *)
                heap->extra_data;
            if (data->reusable) {
                const struct fmem_data *fmem_info =
                    fmem_get_info();
                heap->base = fmem_info->phys;
                data->virt_addr = fmem_info->virt;
                pr_info("ION heap %s using FMEM\n", heap->name);
            } else if (data->mem_is_fmem) {
                const struct fmem_data *fmem_info =
                    fmem_get_info();
                heap->base = fmem_info->phys + fmem_info->size;
            }
            align = data->align;
            break;
        }
        default:
            break;
        }
        if (align && !heap->base) {
            /*獲取heap的base address。*/
            heap->base = msm_ion_get_base(heap->size,
                              heap->memory_type,
                              align);
            if (!heap->base)
                pr_err("%s: could not get memory for heap %s "
                   "(id %x)\n", __func__, heap->name, heap->id);
        }
    }
}

static unsigned long msm_ion_get_base(unsigned long size, int memory_type,
                    unsigned int align)
{
    switch (memory_type) {
    /*咱們定義的是ebi type,看見沒,此函數在mem pool中分析過了。
原理就是使用Mempool 來管理分配內存。*/
    case ION_EBI_TYPE:
        return allocate_contiguous_ebi_nomap(size, align);
        break;
    case ION_SMI_TYPE:
        return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI,
                            align);
        break;
    default:
        pr_err("%s: Unknown memory type %d\n", __func__, memory_type);
        return 0;
    }
}
ion_heap_create:
struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
{
    struct ion_heap *heap = NULL;
    /*根據Heap type調用相應的建立函數。*/
    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_IOMMU:
        heap = ion_iommu_heap_create(heap_data);
        break;
    case ION_HEAP_TYPE_CP:
        heap = ion_cp_heap_create(heap_data);
        break;
#ifdef CONFIG_CMA
    case ION_HEAP_TYPE_DMA:
        heap = ion_cma_heap_create(heap_data);
        break;
#endif
    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 %u\n",
               __func__, heap_data->name, heap_data->type,
               heap_data->base, heap_data->size);
        return ERR_PTR(-EINVAL);
    }
    /*保存Heap的name,id和私有數據。*/
    heap->name = heap_data->name;
    heap->id = heap_data->id;
    heap->priv = heap_data->priv;
    return heap;
}

從上面的代碼能夠得知,ION_HEAP_TYPE_SYSTEM_CONTIG使用kmalloc建立的,ION_HEAP_TYPE_SYSTEM使 用的是vmalloc,而ion_carveout_heap_create就是系統預分配了一片內存區域供其使用。Ion在申請使用的時候,會根據當前 的type來操做各自的heap->ops。分別看下三個函數:

struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *pheap)
{
    struct ion_heap *heap;

    heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
    if (!heap)
        return ERR_PTR(-ENOMEM);
    /*使用的是kmalloc_ops,上篇有提到哦*/
    heap->ops = &kmalloc_ops;
    heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
    system_heap_contig_has_outer_cache = pheap->has_outer_cache;
    return heap;
}
struct ion_heap *ion_system_heap_create(struct ion_platform_heap *pheap)
{
    struct ion_heap *heap;

    heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
    if (!heap)
        return ERR_PTR(-ENOMEM);
    /*和上面函數的區別僅在於ops*/
    heap->ops = &vmalloc_ops;
    heap->type = ION_HEAP_TYPE_SYSTEM;
    system_heap_has_outer_cache = pheap->has_outer_cache;
    return heap;
}
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
    struct ion_carveout_heap *carveout_heap;
    int ret;

    carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
    if (!carveout_heap)
        return ERR_PTR(-ENOMEM);
    /* 從新建立一個新的pool,這裏有點想不通的是爲何不直接使用全局的mempools呢?*/
    carveout_heap->pool = gen_pool_create(12, -1);
    if (!carveout_heap->pool) {
        kfree(carveout_heap);
        return ERR_PTR(-ENOMEM);
    }
    carveout_heap->base = heap_data->base;
    ret = gen_pool_add(carveout_heap->pool, carveout_heap->base,
            heap_data->size, -1);
    if (ret < 0) {
        gen_pool_destroy(carveout_heap->pool);
        kfree(carveout_heap);
        return ERR_PTR(-EINVAL);
    }
    carveout_heap->heap.ops = &carveout_heap_ops;
    carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
    carveout_heap->allocated_bytes = 0;
    carveout_heap->total_size = heap_data->size;
    carveout_heap->has_outer_cache = heap_data->has_outer_cache;

    if (heap_data->extra_data) {
        struct ion_co_heap_pdata *extra_data =
                heap_data->extra_data;

        if (extra_data->setup_region)
            carveout_heap->bus_id = extra_data->setup_region();
        if (extra_data->request_region)
            carveout_heap->request_region =
                    extra_data->request_region;
        if (extra_data->release_region)
            carveout_heap->release_region =
                    extra_data->release_region;
    }
    return &carveout_heap->heap;
}

Heap建立完成,而後保存到idev中:
void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
{
    struct rb_node **p = &dev->heaps.rb_node;
    struct rb_node *parent = NULL;
    struct ion_heap *entry;

    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__);

    heap->dev = dev;
    mutex_lock(&dev->lock);
    while (*p) {
        parent = *p;
        entry = rb_entry(parent, struct ion_heap, node);

        if (heap->id < entry->id) {
            p = &(*p)->rb_left;
        } else if (heap->id > entry->id ) {
            p = &(*p)->rb_right;
        } else {
            pr_err("%s: can not insert multiple heaps with "
                "id %d\n", __func__, heap->id);
            goto end;
        }
    }
    /*使用紅黑樹保存*/
    rb_link_node(&heap->node, parent, p);
    rb_insert_color(&heap->node, &dev->heaps);
    /*以heap name建立fs,位於ion目錄下。如vamlloc, camera_preview , audio 等*/
    debugfs_create_file(heap->name, 0664, dev->debug_root, heap,
                &debug_heap_fops);
end:
    mutex_unlock(&dev->lock);
}

到此,ION初始化已經完成了。接下來該如何使用呢?嗯,經過前面建立的misc設備也就是idev了!還記得裏面有個fops爲ion_fops嗎?先來看下用戶空間如何使用ION。

ION用戶空間使用

Ion_fops結構以下:
static const struct file_operations ion_fops = {
    .owner          = THIS_MODULE,
    .open           = ion_open,
    .release        = ion_release,
    .unlocked_ioctl = ion_ioctl,
};

用戶空間都是經過ioctl來控制。先看ion_open.

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

    pr_debug("%s: %d\n", __func__, __LINE__);
    snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
    /*根據idev和task pid爲name建立ion client*/
    client = ion_client_create(dev, -1, debug_name);
    if (IS_ERR_OR_NULL(client))
        return PTR_ERR(client);
    file->private_data = client;

    return 0;
}

前一篇文章有說到,要使用ION, 必需要先建立ionclient, 所以用戶空間在open ion的時候建立了client.

struct ion_client *ion_client_create(struct ion_device *dev,
                     unsigned int heap_mask,
                     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;
    unsigned int name_len;

    if (!name) {
        pr_err("%s: Name cannot be null\n", __func__);
        return ERR_PTR(-EINVAL);
    }
    name_len = strnlen(name, 64);

    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);
    /*分配ion client struct.*/
    client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
    if (!client) {
        if (task)
            put_task_struct(current->group_leader);
        return ERR_PTR(-ENOMEM);
    }
    /*下面就是保存一系列參數了。*/
    client->dev = dev;
    client->handles = RB_ROOT;
    mutex_init(&client->lock);

    client->name = kzalloc(name_len+1, GFP_KERNEL);
    if (!client->name) {
        put_task_struct(current->group_leader);
        kfree(client);
        return ERR_PTR(-ENOMEM);
    } else {
        strlcpy(client->name, name, name_len+1);
    }

    client->heap_mask = heap_mask;
    client->task = task;
    client->pid = pid;

    mutex_lock(&dev->lock);
    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;
    }
    /*當前client添加到idev的clients根樹上去。*/
    rb_link_node(&client->node, parent, p);
    rb_insert_color(&client->node, &dev->clients);

    /*在ION先建立的文件名字是以pid命名的。*/
    client->debug_root = debugfs_create_file(name, 0664,
                         dev->debug_root, client,
                         &debug_client_fops);
    mutex_unlock(&dev->lock);

    return client;
}

有了client以後,用戶程序就能夠開始申請分配ION buffer了!經過ioctl命令實現。

ion_ioct函數有若干個cmd,ION_IOC_ALLOC和ION_IOC_FREE相對應,表示申請和釋放buffer。用戶空間程序使 用前先要調用ION_IOC_MAP才能獲得buffer address,而ION_IOC_IMPORT是爲了將這塊內存共享給用戶空間另外一個進程。

static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct ion_client *client = filp->private_data;

    switch (cmd) {
    case ION_IOC_ALLOC:
    {
        struct ion_allocation_data data;

        if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
            return -EFAULT;
        /*分配buffer.*/
        data.handle = ion_alloc(client, data.len, data.align,
                         data.flags);

        if (IS_ERR(data.handle))
            return PTR_ERR(data.handle);

        if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
            ion_free(client, data.handle);
            return -EFAULT;
        }
        break;
    }
    case ION_IOC_FREE:
    {
        struct ion_handle_data data;
        bool valid;

        if (copy_from_user(&data, (void __user *)arg,
                   sizeof(struct ion_handle_data)))
            return -EFAULT;
        mutex_lock(&client->lock);
        valid = ion_handle_validate(client, data.handle);
        mutex_unlock(&client->lock);
        if (!valid)
            return -EINVAL;
        ion_free(client, data.handle);
        break;
    }
    case ION_IOC_MAP:
    case ION_IOC_SHARE:
    {
        struct ion_fd_data data;
        int ret;
        if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
            return -EFAULT;
        /*判斷當前cmd是否被調用過了,調用過就返回,不然設置flags.*/
        ret = ion_share_set_flags(client, data.handle, filp->f_flags);
        if (ret)
            return ret;

        data.fd = ion_share_dma_buf(client, data.handle);
        if (copy_to_user((void __user *)arg, &data, sizeof(data)))
            return -EFAULT;
        if (data.fd < 0)
            return data.fd;
        break;
    }
    case ION_IOC_IMPORT:
    {
        struct ion_fd_data data;
        int ret = 0;
        if (copy_from_user(&data, (void __user *)arg,
                   sizeof(struct ion_fd_data)))
            return -EFAULT;
        data.handle = ion_import_dma_buf(client, data.fd);
        if (IS_ERR(data.handle))
            data.handle = NULL;
        if (copy_to_user((void __user *)arg, &data,
                 sizeof(struct ion_fd_data)))
            return -EFAULT;
        if (ret < 0)
            return ret;
        break;
    }
    case ION_IOC_CUSTOM:
~~snip
    case ION_IOC_CLEAN_CACHES:
    case ION_IOC_INV_CACHES:
    case ION_IOC_CLEAN_INV_CACHES:
    ~~snip
    case ION_IOC_GET_FLAGS:
~~snip
    default:
        return -ENOTTY;
    }
    return 0;
}

下面分小節說明分配和共享的原理。

ION_IOC_ALLOC

struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
                 size_t align, unsigned int flags)
{
~~snip

    mutex_lock(&dev->lock);
    /*循環遍歷當前Heap鏈表。*/
    for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) {
        struct ion_heap *heap = rb_entry(n, struct ion_heap, node);
/*只有heap type和id都符合纔去建立buffer.*/
        /* if the client doesn't support this heap type */
        if (!((1 << heap->type) & client->heap_mask))
            continue;
        /* if the caller didn't specify this heap type */
        if (!((1 << heap->id) & flags))
            continue;
        /* Do not allow un-secure heap if secure is specified */
        if (secure_allocation && (heap->type != ION_HEAP_TYPE_CP))
            continue;
        buffer = ion_buffer_create(heap, dev, len, align, flags);
~~snip
    }
    mutex_unlock(&dev->lock);

~~snip
    /*建立了buffer以後,就相應地建立handle來管理buffer.*/
    handle = ion_handle_create(client, buffer);

~~snip
}

找到Heap以後調用ion_buffer_create:
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;
    int ret;
    /*分配struct ion buffer,用來管理buffer.*/
    buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
    if (!buffer)
        return ERR_PTR(-ENOMEM);

    buffer->heap = heap;
    kref_init(&buffer->ref);
    /*調用相應heap type的ops allocate。還記得前面有提到過不一樣種類的ops嗎,
如carveout_heap_ops ,vmalloc_ops 。*/
    ret = heap->ops->allocate(heap, buffer, len, align, flags);
    if (ret) {
        kfree(buffer);
        return ERR_PTR(ret);
    }

    buffer->dev = dev;
    buffer->size = len;
    /*http://lwn.net/Articles/263343/*/
    table = buffer->heap->ops->map_dma(buffer->heap, buffer);
    if (IS_ERR_OR_NULL(table)) {
        heap->ops->free(buffer);
        kfree(buffer);
        return ERR_PTR(PTR_ERR(table));
    }
    buffer->sg_table = table;

    mutex_init(&buffer->lock);
    /*將當前ion buffer添加到idev 的buffers 樹上統一管理。*/
    ion_buffer_add(dev, buffer);
    return buffer;
}

先拿heap type爲ION_HEAP_TYPE_CARVEOUT爲例,看下它是如何分配buffer的。

allocate對應ion_carveout_heap_allocate。

static int ion_carveout_heap_allocate(struct ion_heap *heap,
                      struct ion_buffer *buffer,
                      unsigned long size, unsigned long align,
                      unsigned long flags)
{
    buffer->priv_phys = ion_carveout_allocate(heap, size, align);
    return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0;
}
ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
                      unsigned long size,
                      unsigned long align)
{
    struct ion_carveout_heap *carveout_heap =
        container_of(heap, struct ion_carveout_heap, heap);
    /*經過建立的mem pool來管理buffer,因爲這塊buffer在初始化的
時候就預留了,如今只要從上面拿一塊區域就能夠了。*/
    unsigned long offset = gen_pool_alloc_aligned(carveout_heap->pool,
                            size, ilog2(align));
    /*分配不成功多是沒有內存空間可供分配了或者是有碎片致使的。*/
    if (!offset) {
        if ((carveout_heap->total_size -
              carveout_heap->allocated_bytes) >= size)
            pr_debug("%s: heap %s has enough memory (%lx) but"
                " the allocation of size %lx still failed."
                " Memory is probably fragmented.",
                __func__, heap->name,
                carveout_heap->total_size -
                carveout_heap->allocated_bytes, size);
        return ION_CARVEOUT_ALLOCATE_FAIL;
    }
    /*已經分配掉的內存字節。*/
    carveout_heap->allocated_bytes += size;
    return offset;
}

一樣地,對於heap type爲ION_HEAP_TYPE_SYSTEM的分配函數是ion_system_heap_allocate。

static int ion_system_contig_heap_allocate(struct ion_heap *heap,
                       struct ion_buffer *buffer,
                       unsigned long len,
                       unsigned long align,
                       unsigned long flags)
{
    /*經過kzalloc分配。*/
    buffer->priv_virt = kzalloc(len, GFP_KERNEL);
    if (!buffer->priv_virt)
        return -ENOMEM;
    atomic_add(len, &system_contig_heap_allocated);
    return 0;
}

其餘的幾種Heap type可自行研究,接着調用ion_buffer_add將buffer添加到dev的buffers樹上去。

static void ion_buffer_add(struct ion_device *dev,
               struct ion_buffer *buffer)
{
    struct rb_node **p = &dev->buffers.rb_node;
    struct rb_node *parent = NULL;
    struct ion_buffer *entry;

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

        if (buffer < entry) {
            p = &(*p)->rb_left;
        } else if (buffer > entry) {
            p = &(*p)->rb_right;
        } else {
            pr_err("%s: buffer already found.", __func__);
            BUG();
        }
    }
/*又是使用紅黑樹哦!*/
    rb_link_node(&buffer->node, parent, p);
    rb_insert_color(&buffer->node, &dev->buffers);
}

好了buffer建立完成,接下來就要建立Hanle來管理buffer了!

static struct ion_handle *ion_handle_create(struct ion_client *client,
                     struct ion_buffer *buffer)
{
    struct ion_handle *handle;
    /*分配struct ion_handle.*/
    handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL);
    if (!handle)
        return ERR_PTR(-ENOMEM);
    kref_init(&handle->ref);
    rb_init_node(&handle->node);
    handle->client = client;    //client放入handle中
    ion_buffer_get(buffer);    //引用計數加1 
    handle->buffer = buffer;    //buffer也放入handle中

    return handle;
}

建立handle也比較簡單,至此,已經獲得client和handle,buffer分配完成!

ION_IOC_MAP/ ION_IOC_SHARE

int ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle)
{
    struct ion_buffer *buffer;
    struct dma_buf *dmabuf;
    bool valid_handle;
    int fd;

    mutex_lock(&client->lock);
    valid_handle = ion_handle_validate(client, handle);
    mutex_unlock(&client->lock);
    if (!valid_handle) {
        WARN(1, "%s: invalid handle passed to share.\n", __func__);
        return -EINVAL;
    }

    buffer = handle->buffer;
    ion_buffer_get(buffer);
    /*生成一個新的file描述符*/
    dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR);
    if (IS_ERR(dmabuf)) {
        ion_buffer_put(buffer);
        return PTR_ERR(dmabuf);
    }
    /*將file轉換用戶空間識別的fd描述符。*/
    fd = dma_buf_fd(dmabuf, O_CLOEXEC);
    if (fd < 0)
        dma_buf_put(dmabuf);

    return fd;
}
struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops,
                size_t size, int flags)
{
    struct dma_buf *dmabuf;
    struct file *file;
~~snip
    /*分配struct dma_buf.*/
    dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL);
    if (dmabuf == NULL)
        return ERR_PTR(-ENOMEM);
    /*保存信息到dmabuf,注意ops爲dma_buf_ops,後面mmap爲調用到。*/
    dmabuf->priv = priv;
    dmabuf->ops = ops;
    dmabuf->size = size;
    /*產生新的file*/
    file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags);

    dmabuf->file = file;

    mutex_init(&dmabuf->lock);
    INIT_LIST_HEAD(&dmabuf->attachments);

    return dmabuf;
}

經過上述過程,用戶空間就獲得了新的fd,從新生成一個新的fd的目的是考慮了兩個用戶空間進程想共享這塊heap內存的狀況。而後再對fd做mmap, 相應地kernel空間就調用到了file 的dma_buf_fops中的dma_buf_mmap_internal。

static const struct file_operations dma_buf_fops = {
    .release    = dma_buf_release,
    .mmap        = dma_buf_mmap_internal,
};
static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
 {
     struct dma_buf *dmabuf;
 
     if (!is_dma_buf_file(file))
         return -EINVAL;
 
     dmabuf = file->private_data;
     /*檢查用戶空間要映射的size是否比目前dmabuf也就是當前heap的size
還要大,若是是就返回無效。*/
     /* check for overflowing the buffer's size */
     if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) >
         dmabuf->size >> PAGE_SHIFT)
         return -EINVAL;
     /*調用的是dma_buf_ops 的mmap函數*/
     return dmabuf->ops->mmap(dmabuf, vma);
 }

struct dma_buf_ops dma_buf_ops = {
    .map_dma_buf = ion_map_dma_buf,
    .unmap_dma_buf = ion_unmap_dma_buf,
    .mmap = ion_mmap,
    .release = ion_dma_buf_release,
    .begin_cpu_access = ion_dma_buf_begin_cpu_access,
    .end_cpu_access = ion_dma_buf_end_cpu_access,
    .kmap_atomic = ion_dma_buf_kmap,
    .kunmap_atomic = ion_dma_buf_kunmap,
    .kmap = ion_dma_buf_kmap,
    .kunmap = ion_dma_buf_kunmap,
};
static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
    struct ion_buffer *buffer = dmabuf->priv;
    int ret;

    if (!buffer->heap->ops->map_user) {
        pr_err("%s: this heap does not define a method for mapping "
               "to userspace\n", __func__);
        return -EINVAL;
    }

    mutex_lock(&buffer->lock);
    /* now map it to userspace */
    /*調用的是相應heap的map_user,如carveout_heap_ops 調用的是
ion_carveout_heap_map_user ,此函數就是通常的mmap實現,不追下去了。*/
    ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma);

    if (ret) {
        mutex_unlock(&buffer->lock);
        pr_err("%s: failure mapping buffer to userspace\n",
               __func__);
    } else {
        buffer->umap_cnt++;
        mutex_unlock(&buffer->lock);

        vma->vm_ops = &ion_vm_ops;
        /*
         * move the buffer into the vm_private_data so we can access it
         * from vma_open/close
         */
        vma->vm_private_data = buffer;
    }
    return ret;
}

至此,用戶空間就獲得了bufferaddress,而後可使用了!

ION_IOC_IMPORT

當用戶空間另外一個進程須要這塊heap的時候,ION_IOC_IMPORT就派上用處了!注意,

傳進去的fd爲在ION_IOC_SHARE中獲得的。

struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
{
    
    struct dma_buf *dmabuf;
    struct ion_buffer *buffer;
    struct ion_handle *handle;

    dmabuf = dma_buf_get(fd);
    if (IS_ERR_OR_NULL(dmabuf))
        return ERR_PTR(PTR_ERR(dmabuf));
    /* if this memory came from ion */
~~snip
    buffer = dmabuf->priv;

    mutex_lock(&client->lock);
    /* if a handle exists for this buffer just take a reference to it */
/*查找是否已經存在對應的handle了,沒有則建立。由於另一個進程只是
調用了open 接口,對應的只建立了client,並無handle。
*/
    handle = ion_handle_lookup(client, buffer);
    if (!IS_ERR_OR_NULL(handle)) {
        ion_handle_get(handle);
        goto end;
    }
    handle = ion_handle_create(client, buffer);
    if (IS_ERR_OR_NULL(handle))
        goto end;
    ion_handle_add(client, handle);
end:
    mutex_unlock(&client->lock);
    dma_buf_put(dmabuf);
    return handle;
}

這樣,用戶空間另外一個進程也獲得了對應的bufferHandle,client/buffer/handle之間鏈接起來了!而後另外一個一個進程就也可使用mmap來操做這塊heap buffer了。

和通常的進程使用ION區別就是共享的進程之間struction_buffer是共享的,而struct ion_handle是各自的。

 可見,ION的使用流程仍是比較清晰的。不過要記得的是,使用好了ION,必定要釋放掉,不然會致使內存泄露。

ION內核空間使用

內核空間使用ION也是大同小異,按照建立client,buffer,handle的流程,只是它的使用對用戶空間來講是透明的罷了!

ion_client_create在kernel空間被Qualcomm給封裝了下。

struct ion_client *msm_ion_client_create(unsigned int heap_mask,
                    const char *name)
{
    return ion_client_create(idev, heap_mask, name);
}

調用的流程也相似,不過map的時候調用的是heap對應的map_kernel()而不是map_user().

msm_ion_client_create -> ion_alloc ->ion_map_kernel

參考文檔:

http://lwn.net/Articles/480055/

《ARM體系結構與編程》存儲系統章節

相關文章
相關標籤/搜索