chipset: MSM8X25Q node
codebase: Android 4.1 編程
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
因爲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基本概念中,咱們瞭解了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 }, };
轉到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_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; } 下面分小節說明分配和共享的原理。
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分配完成!
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,而後可使用了!
當用戶空間另外一個進程須要這塊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也是大同小異,按照建立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體系結構與編程》存儲系統章節