ION原理分析

来源:互联网 发布:有可靠的网络兼职吗 编辑:程序博客网 时间:2024/05/11 23:53

平台: MSM8x25Q

系统: Android4.1

从上一篇ION基本概念中,我们了解了heaptype, heap id, client, handle以及如何使用,本篇再从原理上分析下ION的运作流程。

         MSM8x25Q平台使用的是board-qrd7627.c,ION相关定义如下:

[html] view plaincopyprint?
  1. /** 
  2. * These heaps are listed in the order they will be allocated. 
  3. * Don't swap the order unless you know what you are doing! 
  4. */ 
  5. struct ion_platform_heap msm7627a_heaps[] = { 
  6.         { 
  7.             .id = ION_SYSTEM_HEAP_ID
  8.             .type   = ION_HEAP_TYPE_SYSTEM
  9.             .name   = ION_VMALLOC_HEAP_NAME
  10.         }, 
  11. #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION 
  12.         /* PMEM_ADSP = CAMERA */ 
  13.         { 
  14.             .id = ION_CAMERA_HEAP_ID
  15.             .type   = CAMERA_HEAP_TYPE
  16.             .name   = ION_CAMERA_HEAP_NAME
  17.             .memory_type =ION_EBI_TYPE
  18.             .extra_data = (void *)&co_mm_ion_pdata, 
  19.             .priv   = (void *)&ion_cma_device.dev, 
  20.         }, 
  21.         /* AUDIO HEAP 1*/ 
  22.         { 
  23.             .id = ION_AUDIO_HEAP_ID
  24.             .type   = ION_HEAP_TYPE_CARVEOUT
  25.             .name   = ION_AUDIO_HEAP_NAME
  26.             .memory_type = ION_EBI_TYPE
  27.             .extra_data = (void *)&co_ion_pdata, 
  28.         }, 
  29.         /* PMEM_MDP = SF */ 
  30.         { 
  31.             .id = ION_SF_HEAP_ID
  32.             .type   = ION_HEAP_TYPE_CARVEOUT
  33.             .name   = ION_SF_HEAP_NAME
  34.             .memory_type = ION_EBI_TYPE
  35.             .extra_data = (void *)&co_ion_pdata, 
  36.         }, 
  37.         /* AUDIO HEAP 2*/ 
  38.         { 
  39.             .id    = ION_AUDIO_HEAP_BL_ID
  40.             .type  = ION_HEAP_TYPE_CARVEOUT
  41.             .name  = ION_AUDIO_BL_HEAP_NAME
  42.             .memory_type = ION_EBI_TYPE
  43.             .extra_data = (void *)&co_ion_pdata, 
  44.             .base = BOOTLOADER_BASE_ADDR
  45.         }, 
  46.  
  47. #endif 
  48. }; 
  49.  
  50. static struct ion_co_heap_pdata co_ion_pdata = { 
  51.     .adjacent_mem_id =INVALID_HEAP_ID
  52.     .align = PAGE_SIZE
  53. }; 
  54.  
  55. static struct ion_co_heap_pdata co_mm_ion_pdata = { 
  56.     .adjacent_mem_id = INVALID_HEAP_ID
  57.     .align = PAGE_SIZE
  58. }; 
  59.  
  60. static u64 msm_dmamask = DMA_BIT_MASK(32); 
  61.  
  62. static struct platform_device ion_cma_device = { 
  63.     .name = "ion-cma-device"
  64.     .id = -1, 
  65.     .dev = { 
  66.         .dma_mask = &msm_dmamask, 
  67.         .coherent_dma_mask =DMA_BIT_MASK(32), 
  68.     } 
  69. }; 

Qualcomm提示了不要轻易调换顺序,因为后面代码处理是将顺序定死了的,一旦你调换了,代码就无法正常运行了。

另外, 本系统中只使用了ION_HEAP_TYPE_CARVEOUT和 ION_HEAP_TYPE_SYSTEM这两种heap type.

对于ION_HEAP_TYPE_CARVEOUT的内存分配,后面将会发现,其实就是之前讲述过的使用mem pool来分配的。

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

[html] view plaincopyprint?
  1. static struct ion_platform_data ion_pdata = { 
  2.     .nr = MSM_ION_HEAP_NUM
  3.     .has_outer_cache =1
  4.     .heaps = msm7627a_heaps
  5. }; 
  6.  
  7. static struct platform_device ion_dev = { 
  8.     .name = "ion-msm"
  9.     .id = 1
  10.     .dev = { .platform_data = &ion_pdata }, 
  11. }; 

ION初始化

转到msm_ion.c,ion.c的某些函数也被重新封装了下.万事都从设备匹配开始:

[html] view plaincopyprint?
  1. static struct platform_driver msm_ion_driver = { 
  2.     .probe = msm_ion_probe
  3.     .remove = msm_ion_remove
  4.     .driver = { .name ="ion-msm"
  5. }; 
  6. static int __init msm_ion_init(void) 
  7.     /*调用msm_ion_probe */ 
  8.     return platform_driver_register(&msm_ion_driver); 
  9.  
  10. static int msm_ion_probe(struct platform_device *pdev) 
  11.     /*即board-qrd7627a.c中的ion_pdata */ 
  12.     struct ion_platform_data *pdata =pdev->dev.platform_data; 
  13.     int err; 
  14.     int i; 
  15.  
  16.     /*heap数量*/ 
  17.     num_heaps = pdata->nr; 
  18.     /*分配struct ion_heap */ 
  19.     heaps = kcalloc(pdata->nr, sizeof(struct ion_heap *), GFP_KERNEL); 
  20.  
  21.     if (!heaps) { 
  22.         err = -ENOMEM; 
  23.         goto out; 
  24.     } 
  25.     /*创建节点,最终是/dev/ion,供用户空间操作。*/ 
  26.     idev = ion_device_create(NULL); 
  27.     if (IS_ERR_OR_NULL(idev)) { 
  28.         err = PTR_ERR(idev); 
  29.         goto freeheaps; 
  30.     } 
  31.     /*最终是根据adjacent_mem_id 是否定义了来分配相邻内存, 
  32. 我们没用到,忽略此函数。*/ 
  33.     msm_ion_heap_fixup(pdata->heaps, num_heaps); 
  34.  
  35.     /* create the heaps as specified in the board file */ 
  36.     for (i = 0; i< num_heaps; i++) { 
  37.         struct ion_platform_heap *heap_data = &pdata->heaps[i]; 
  38.         /*分配ion*/ 
  39.         msm_ion_allocate(heap_data); 
  40.  
  41.         heap_data->has_outer_cache =pdata->has_outer_cache; 
  42.         /*创建ion heap。*/ 
  43.         heaps[i] = ion_heap_create(heap_data); 
  44.         if (IS_ERR_OR_NULL(heaps[i])) { 
  45.             heaps[i] = 0; 
  46.             continue; 
  47.         } else { 
  48.             if (heap_data->size) 
  49.                 pr_info("ION heap %s created at %lx " 
  50.                     "with size %x\n", heap_data->name, 
  51.                               heap_data->base, 
  52.                               heap_data->size); 
  53.             else 
  54.                 pr_info("ION heap %s created\n", 
  55.                               heap_data->name); 
  56.         } 
  57.         /*创建的heap添加到idev中,以便后续使用。*/ 
  58.         ion_device_add_heap(idev, heaps[i]); 
  59.     } 
  60.     /*检查heap之间是否有重叠部分*/ 
  61.     check_for_heap_overlap(pdata->heaps, num_heaps); 
  62.     platform_set_drvdata(pdev, idev); 
  63.     return 0; 
  64.  
  65. freeheaps: 
  66.     kfree(heaps); 
  67. out: 
  68.     return err; 
  69.  
  70. 通过ion_device_create创建/dev/ion节点: 
  71. struct ion_device *ion_device_create(long (*custom_ioctl) 
  72.                      (struct ion_client *client, 
  73.                       unsigned int cmd, 
  74.                       unsigned long arg)) 
  75.     struct ion_device *idev; 
  76.     int ret; 
  77.      
  78.     idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL); 
  79.     if (!idev) 
  80.         return ERR_PTR(-ENOMEM); 
  81.     /*是个misc设备*/ 
  82.     idev->dev.minor =MISC_DYNAMIC_MINOR
  83.     /*节点名字为ion*/ 
  84.     idev->dev.name ="ion"
  85.     /*fops为ion_fops,所以对应ion的操作都会调用ion_fops的函数指针。*/ 
  86.     idev->dev.fops = &ion_fops; 
  87.     idev->dev.parent =NULL
  88.     ret = misc_register(&idev->dev); 
  89.     if (ret) { 
  90.         pr_err("ion: failed to register misc device.\n"); 
  91.         return ERR_PTR(ret); 
  92.     } 
  93.     /*创建debugfs目录,路径为/sys/kernel/debug/ion/*/ 
  94.     idev->debug_root =debugfs_create_dir("ion", NULL); 
  95.     if (IS_ERR_OR_NULL(idev->debug_root)) 
  96.         pr_err("ion: failed to create debug files.\n"); 
  97.  
  98.     idev->custom_ioctl =custom_ioctl
  99.     idev->buffers =RB_ROOT
  100.     mutex_init(&idev->lock); 
  101.     idev->heaps =RB_ROOT
  102.     idev->clients =RB_ROOT
  103.     /*在ion目录下创建一个check_leaked_fds文件,用来检查Ion的使用是否有内存泄漏。如果申请了ion之后不需要使用却没有释放,就会导致memory leak.*/ 
  104.     debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev, 
  105.                 &debug_leak_fops); 
  106.     return idev; 
  107.  
  108. msm_ion_allocate: 
  109. static void msm_ion_allocate(struct ion_platform_heap *heap) 
  110.  
  111.     if (!heap->base && heap->extra_data) { 
  112.         unsigned int align =0
  113.         switch (heap->type) { 
  114.         /*获取align参数*/ 
  115.         case ION_HEAP_TYPE_CARVEOUT: 
  116.             align
  117.             ((struct ion_co_heap_pdata *) heap->extra_data)->align; 
  118.             break; 
  119.         /*此type我们没使用到。*/ 
  120.         case ION_HEAP_TYPE_CP: 
  121.         { 
  122.             struct ion_cp_heap_pdata *data
  123.                 (struct ion_cp_heap_pdata *) 
  124.                 heap->extra_data; 
  125.             if (data->reusable) { 
  126.                 const struct fmem_data *fmem_info
  127.                     fmem_get_info(); 
  128.                 heap->base =fmem_info->phys; 
  129.                 data->virt_addr =fmem_info->virt; 
  130.                 pr_info("ION heap %s using FMEM\n", heap->name); 
  131.             } else if (data->mem_is_fmem) { 
  132.                 const struct fmem_data *fmem_info
  133.                     fmem_get_info(); 
  134.                 heap->base =fmem_info->phys + fmem_info->size; 
  135.             } 
  136.             align = data->align; 
  137.             break; 
  138.         } 
  139.         default: 
  140.             break; 
  141.         } 
  142.         if (align && !heap->base) { 
  143.             /*获取heap的base address。*/ 
  144.             heap->base =msm_ion_get_base(heap->size, 
  145.                               heap->memory_type, 
  146.                               align); 
  147.             if (!heap->base) 
  148.                 pr_err("%s: could not get memory for heap %s " 
  149.                    "(id %x)\n", __func__, heap->name, heap->id); 
  150.         } 
  151.     } 
  152.  
  153. static unsigned long msm_ion_get_base(unsigned long size, int memory_type, 
  154.                     unsigned int align) 
  155.     switch (memory_type) { 
  156.     /*我们定义的是ebi type,看见没,此函数在mem pool中分析过了。 
  157. 原理就是使用Mempool 来管理分配内存。*/ 
  158.     case ION_EBI_TYPE: 
  159.         return allocate_contiguous_ebi_nomap(size, align); 
  160.         break; 
  161.     case ION_SMI_TYPE: 
  162.         return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI, 
  163.                             align); 
  164.         break; 
  165.     default: 
  166.         pr_err("%s: Unknown memory type %d\n", __func__, memory_type); 
  167.         return 0; 
  168.     } 
  169. ion_heap_create: 
  170. struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) 
  171.     struct ion_heap *heap = NULL
  172.     /*根据Heap type调用相应的创建函数。*/ 
  173.     switch (heap_data->type) { 
  174.     case ION_HEAP_TYPE_SYSTEM_CONTIG: 
  175.         heap = ion_system_contig_heap_create(heap_data); 
  176.         break; 
  177.     case ION_HEAP_TYPE_SYSTEM: 
  178.         heap = ion_system_heap_create(heap_data); 
  179.         break; 
  180.     case ION_HEAP_TYPE_CARVEOUT: 
  181.         heap = ion_carveout_heap_create(heap_data); 
  182.         break; 
  183.     case ION_HEAP_TYPE_IOMMU: 
  184.         heap = ion_iommu_heap_create(heap_data); 
  185.         break; 
  186.     case ION_HEAP_TYPE_CP: 
  187.         heap = ion_cp_heap_create(heap_data); 
  188.         break; 
  189. #ifdef CONFIG_CMA 
  190.     case ION_HEAP_TYPE_DMA: 
  191.         heap = ion_cma_heap_create(heap_data); 
  192.         break; 
  193. #endif 
  194.     default: 
  195.         pr_err("%s: Invalid heap type %d\n", __func__, 
  196.                heap_data->type); 
  197.         return ERR_PTR(-EINVAL); 
  198.     } 
  199.  
  200.     if (IS_ERR_OR_NULL(heap)) { 
  201.         pr_err("%s: error creating heap %s type %d base %lu size %u\n", 
  202.                __func__, heap_data->name, heap_data->type, 
  203.                heap_data->base, heap_data->size); 
  204.         return ERR_PTR(-EINVAL); 
  205.     } 
  206.     /*保存Heap的name,id和私有数据。*/ 
  207.     heap->name =heap_data->name; 
  208.     heap->id =heap_data->id; 
  209.     heap->priv =heap_data->priv; 
  210.     return heap; 

从上面的代码可以得知,ION_HEAP_TYPE_SYSTEM_CONTIG使用kmalloc创建的,ION_HEAP_TYPE_SYSTEM使用的是vmalloc,而ion_carveout_heap_create就是系统预分配了一片内存区域供其使用。Ion在申请使用的时候,会根据当前的type来操作各自的heap->ops。分别看下三个函数:

[html] view plaincopyprint?
  1. struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *pheap) 
  2.     struct ion_heap *heap; 
  3.  
  4.     heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); 
  5.     if (!heap) 
  6.         return ERR_PTR(-ENOMEM); 
  7.     /*使用的是kmalloc_ops,上篇有提到哦*/ 
  8.     heap->ops = &kmalloc_ops; 
  9.     heap->type =ION_HEAP_TYPE_SYSTEM_CONTIG
  10.     system_heap_contig_has_outer_cache =pheap->has_outer_cache; 
  11.     return heap; 
  12. struct ion_heap *ion_system_heap_create(struct ion_platform_heap *pheap) 
  13.     struct ion_heap *heap; 
  14.  
  15.     heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); 
  16.     if (!heap) 
  17.         return ERR_PTR(-ENOMEM); 
  18.     /*和上面函数的区别仅在于ops*/ 
  19.     heap->ops = &vmalloc_ops; 
  20.     heap->type =ION_HEAP_TYPE_SYSTEM
  21.     system_heap_has_outer_cache = pheap->has_outer_cache; 
  22.     return heap; 
  23. struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) 
  24.     struct ion_carveout_heap *carveout_heap; 
  25.     int ret; 
  26.  
  27.     carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL); 
  28.     if (!carveout_heap) 
  29.         return ERR_PTR(-ENOMEM); 
  30.     /* 重新创建一个新的pool,这里有点想不通的是为什么不直接使用全局的mempools呢?*/ 
  31.     carveout_heap->pool =gen_pool_create(12, -1); 
  32.     if (!carveout_heap->pool) { 
  33.         kfree(carveout_heap); 
  34.         return ERR_PTR(-ENOMEM); 
  35.     } 
  36.     carveout_heap->base =heap_data->base; 
  37.     ret = gen_pool_add(carveout_heap->pool, carveout_heap->base, 
  38.             heap_data->size, -1); 
  39.     if (ret < 0) { 
  40.         gen_pool_destroy(carveout_heap->pool); 
  41.         kfree(carveout_heap); 
  42.         return ERR_PTR(-EINVAL); 
  43.     } 
  44.     carveout_heap->heap.ops = &carveout_heap_ops; 
  45.     carveout_heap->heap.type =ION_HEAP_TYPE_CARVEOUT
  46.     carveout_heap->allocated_bytes =0
  47.     carveout_heap->total_size =heap_data->size; 
  48.     carveout_heap->has_outer_cache =heap_data->has_outer_cache; 
  49.  
  50.     if (heap_data->extra_data) { 
  51.         struct ion_co_heap_pdata *extra_data
  52.                 heap_data->extra_data; 
  53.  
  54.         if (extra_data->setup_region) 
  55.             carveout_heap->bus_id =extra_data->setup_region(); 
  56.         if (extra_data->request_region) 
  57.             carveout_heap->request_region
  58.                     extra_data->request_region; 
  59.         if (extra_data->release_region) 
  60.             carveout_heap->release_region
  61.                     extra_data->release_region; 
  62.     } 
  63.     return &carveout_heap->heap; 
  64.  
  65. Heap创建完成,然后保存到idev中: 
  66. void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) 
  67.     struct rb_node **p = &dev->heaps.rb_node; 
  68.     struct rb_node *parent =NULL
  69.     struct ion_heap *entry; 
  70.  
  71.     if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma || 
  72.         !heap->ops->unmap_dma) 
  73.         pr_err("%s: can not add heap with invalid ops struct.\n", 
  74.                __func__); 
  75.  
  76.     heap->dev =dev
  77.     mutex_lock(&dev->lock); 
  78.     while (*p) { 
  79.         parent = *p; 
  80.         entry = rb_entry(parent, struct ion_heap, node); 
  81.  
  82.         if (heap->id < entry->id) { 
  83.             p = &(*p)->rb_left; 
  84.         } else if (heap->id > entry->id ) { 
  85.             p = &(*p)->rb_right; 
  86.         } else { 
  87.             pr_err("%s: can not insert multiple heaps with " 
  88.                 "id %d\n", __func__, heap->id); 
  89.             goto end; 
  90.         } 
  91.     } 
  92.     /*使用红黑树保存*/ 
  93.     rb_link_node(&heap->node, parent, p); 
  94.     rb_insert_color(&heap->node, &dev->heaps); 
  95.     /*以heap name创建fs,位于ion目录下。如vamlloc, camera_preview , audio 等*/ 
  96.     debugfs_create_file(heap->name, 0664, dev->debug_root, heap, 
  97.                 &debug_heap_fops); 
  98. end: 
  99.     mutex_unlock(&dev->lock); 

到此,ION初始化已经完成了。接下来该如何使用呢?嗯,通过前面创建的misc设备也就是idev了!还记得里面有个fops为ion_fops吗?先来看下用户空间如何使用ION。

ION用户空间使用

[html] view plaincopyprint?
  1. Ion_fops结构如下: 
  2. static const struct file_operations ion_fops = { 
  3.     .owner          = THIS_MODULE
  4.     .open           = ion_open
  5.     .release        = ion_release
  6.     .unlocked_ioctl = ion_ioctl
  7. }; 
  8.  
  9. 用户空间都是通过ioctl来控制。先看ion_open. 
  10.  
  11. static int ion_open(struct inode *inode, struct file *file) 
  12.     struct miscdevice *miscdev =file->private_data; 
  13.     struct ion_device *dev = container_of(miscdev, struct ion_device, dev); 
  14.     struct ion_client *client; 
  15.     char debug_name[64]; 
  16.  
  17.     pr_debug("%s: %d\n", __func__, __LINE__); 
  18.     snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader)); 
  19.     /*根据idev和task pid为name创建ion client*/ 
  20.     client = ion_client_create(dev, -1, debug_name); 
  21.     if (IS_ERR_OR_NULL(client)) 
  22.         return PTR_ERR(client); 
  23.     file->private_data =client
  24.  
  25.     return 0; 

前一篇文章有说到,要使用ION, 必须要先创建ionclient, 因此用户空间在open ion的时候创建了client.

[html] view plaincopyprint?
  1. struct ion_client *ion_client_create(struct ion_device *dev, 
  2.                      unsigned int heap_mask, 
  3.                      const char *name) 
  4.     struct ion_client *client; 
  5.     struct task_struct *task; 
  6.     struct rb_node **p; 
  7.     struct rb_node *parent = NULL
  8.     struct ion_client *entry; 
  9.     pid_t pid; 
  10.     unsigned int name_len; 
  11.  
  12.     if (!name) { 
  13.         pr_err("%s: Name cannot be null\n", __func__); 
  14.         return ERR_PTR(-EINVAL); 
  15.     } 
  16.     name_len = strnlen(name, 64); 
  17.  
  18.     get_task_struct(current->group_leader); 
  19.     task_lock(current->group_leader); 
  20.     pid = task_pid_nr(current->group_leader); 
  21.     /* don't bother to store task struct for kernel threads, 
  22.        they can't be killed anyway */ 
  23.     if (current->group_leader->flags & PF_KTHREAD) { 
  24.         put_task_struct(current->group_leader); 
  25.         task = NULL
  26.     } else { 
  27.         task = current->group_leader; 
  28.     } 
  29.     task_unlock(current->group_leader); 
  30.     /*分配ion client struct.*/ 
  31.     client = kzalloc(sizeof(struct ion_client), GFP_KERNEL); 
  32.     if (!client) { 
  33.         if (task) 
  34.             put_task_struct(current->group_leader); 
  35.         return ERR_PTR(-ENOMEM); 
  36.     } 
  37.     /*下面就是保存一系列参数了。*/ 
  38.     client->dev =dev
  39.     client->handles =RB_ROOT
  40.     mutex_init(&client->lock); 
  41.  
  42.     client->name =kzalloc(name_len+1, GFP_KERNEL); 
  43.     if (!client->name) { 
  44.         put_task_struct(current->group_leader); 
  45.         kfree(client); 
  46.         return ERR_PTR(-ENOMEM); 
  47.     } else { 
  48.         strlcpy(client->name, name, name_len+1); 
  49.     } 
  50.  
  51.     client->heap_mask =heap_mask
  52.     client->task =task
  53.     client->pid =pid
  54.  
  55.     mutex_lock(&dev->lock); 
  56.     p = &dev->clients.rb_node; 
  57.     while (*p) { 
  58.         parent = *p; 
  59.         entry = rb_entry(parent, struct ion_client, node); 
  60.  
  61.         if (client < entry
  62.             p = &(*p)->rb_left; 
  63.         else if (client > entry) 
  64.             p = &(*p)->rb_right; 
  65.     } 
  66.     /*当前client添加到idev的clients根树上去。*/ 
  67.     rb_link_node(&client->node, parent, p); 
  68.     rb_insert_color(&client->node, &dev->clients); 
  69.  
  70.     /*在ION先创建的文件名字是以pid命名的。*/ 
  71.     client->debug_root =debugfs_create_file(name, 0664, 
  72.                          dev->debug_root, client, 
  73.                          &debug_client_fops); 
  74.     mutex_unlock(&dev->lock); 
  75.  
  76.     return client; 

有了client之后,用户程序就可以开始申请分配ION buffer了!通过ioctl命令实现。

ion_ioct函数有若干个cmd,ION_IOC_ALLOC和ION_IOC_FREE相对应,表示申请和释放buffer。用户空间程序使用前先要调用ION_IOC_MAP才能得到buffer address,而ION_IOC_IMPORT是为了将这块内存共享给用户空间另一个进程。

[html] view plaincopyprint?
  1. static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 
  2.     struct ion_client *client =filp->private_data; 
  3.  
  4.     switch (cmd) { 
  5.     case ION_IOC_ALLOC: 
  6.     { 
  7.         struct ion_allocation_data data; 
  8.  
  9.         if (copy_from_user(&data, (void __user *)arg, sizeof(data))) 
  10.             return -EFAULT; 
  11.         /*分配buffer.*/ 
  12.         data.handle = ion_alloc(client, data.len, data.align, 
  13.                          data.flags); 
  14.  
  15.         if (IS_ERR(data.handle)) 
  16.             return PTR_ERR(data.handle); 
  17.  
  18.         if (copy_to_user((void __user *)arg, &data, sizeof(data))) { 
  19.             ion_free(client, data.handle); 
  20.             return -EFAULT; 
  21.         } 
  22.         break; 
  23.     } 
  24.     case ION_IOC_FREE: 
  25.     { 
  26.         struct ion_handle_data data; 
  27.         bool valid; 
  28.  
  29.         if (copy_from_user(&data, (void __user *)arg, 
  30.                    sizeof(struct ion_handle_data))) 
  31.             return -EFAULT; 
  32.         mutex_lock(&client->lock); 
  33.         valid = ion_handle_validate(client, data.handle); 
  34.         mutex_unlock(&client->lock); 
  35.         if (!valid) 
  36.             return -EINVAL; 
  37.         ion_free(client, data.handle); 
  38.         break; 
  39.     } 
  40.     case ION_IOC_MAP: 
  41.     case ION_IOC_SHARE: 
  42.     { 
  43.         struct ion_fd_data data; 
  44.         int ret; 
  45.         if (copy_from_user(&data, (void __user *)arg, sizeof(data))) 
  46.             return -EFAULT; 
  47.         /*判断当前cmd是否被调用过了,调用过就返回,否则设置flags.*/ 
  48.         ret = ion_share_set_flags(client, data.handle, filp->f_flags); 
  49.         if (ret) 
  50.             return ret; 
  51.  
  52.         data.fd = ion_share_dma_buf(client, data.handle); 
  53.         if (copy_to_user((void __user *)arg, &data, sizeof(data))) 
  54.             return -EFAULT; 
  55.         if (data.fd < 0
  56.             return data.fd; 
  57.         break; 
  58.     } 
  59.     case ION_IOC_IMPORT: 
  60.     { 
  61.         struct ion_fd_data data; 
  62.         int ret = 0
  63.         if (copy_from_user(&data, (void __user *)arg, 
  64.                    sizeof(struct ion_fd_data))) 
  65.             return -EFAULT; 
  66.         data.handle = ion_import_dma_buf(client, data.fd); 
  67.         if (IS_ERR(data.handle)) 
  68.             data.handle =NULL
  69.         if (copy_to_user((void __user *)arg, &data, 
  70.                  sizeof(struct ion_fd_data))) 
  71.             return -EFAULT; 
  72.         if (ret < 0
  73.             return ret; 
  74.         break; 
  75.     } 
  76.     case ION_IOC_CUSTOM: 
  77. ~~snip 
  78.     case ION_IOC_CLEAN_CACHES: 
  79.     case ION_IOC_INV_CACHES: 
  80.     case ION_IOC_CLEAN_INV_CACHES: 
  81.     ~~snip 
  82.     case ION_IOC_GET_FLAGS: 
  83. ~~snip 
  84.     default: 
  85.         return -ENOTTY; 
  86.     } 
  87.     return 0; 
  88.  
  89. 下面分小节说明分配和共享的原理。 

ION_IOC_ALLOC

[html] view plaincopyprint?
  1. struct ion_handle *ion_alloc(struct ion_client *client, size_t len, 
  2.                  size_t align, unsigned int flags) 
  3. ~~snip 
  4.  
  5.     mutex_lock(&dev->lock); 
  6.     /*循环遍历当前Heap链表。*/ 
  7.     for (n = rb_first(&dev->heaps); n != NULL;n = rb_next(n)) { 
  8.         struct ion_heap *heap =rb_entry(n, struct ion_heap, node); 
  9. /*只有heap type和id都符合才去创建buffer.*/ 
  10.         /* if the client doesn't support this heap type */ 
  11.         if (!((1 <<heap->type) & client->heap_mask)) 
  12.             continue; 
  13.         /* if the caller didn't specify this heap type */ 
  14.         if (!((1 <<heap->id) & flags)) 
  15.             continue; 
  16.         /* Do not allow un-secure heap if secure is specified */ 
  17.         if (secure_allocation && (heap->type != ION_HEAP_TYPE_CP)) 
  18.             continue; 
  19.         buffer = ion_buffer_create(heap, dev, len, align, flags); 
  20. ~~snip 
  21.     } 
  22.     mutex_unlock(&dev->lock); 
  23.  
  24. ~~snip 
  25.     /*创建了buffer之后,就相应地创建handle来管理buffer.*/ 
  26.     handle = ion_handle_create(client, buffer); 
  27.  
  28. ~~snip 
  29.  
  30. 找到Heap之后调用ion_buffer_create: 
  31. static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, 
  32.                      struct ion_device *dev, 
  33.                      unsigned long len, 
  34.                      unsigned long align, 
  35.                      unsigned long flags) 
  36.     struct ion_buffer *buffer; 
  37.     struct sg_table *table; 
  38.     int ret; 
  39.     /*分配struct ion buffer,用来管理buffer.*/ 
  40.     buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL); 
  41.     if (!buffer) 
  42.         return ERR_PTR(-ENOMEM); 
  43.  
  44.     buffer->heap =heap
  45.     kref_init(&buffer->ref); 
  46.     /*调用相应heap type的ops allocate。还记得前面有提到过不同种类的ops吗, 
  47. 如carveout_heap_ops ,vmalloc_ops 。*/ 
  48.     ret = heap->ops->allocate(heap, buffer, len, align, flags); 
  49.     if (ret) { 
  50.         kfree(buffer); 
  51.         return ERR_PTR(ret); 
  52.     } 
  53.  
  54.     buffer->dev =dev
  55.     buffer->size =len
  56.     /*http://lwn.net/Articles/263343/*/ 
  57.     table = buffer->heap->ops->map_dma(buffer->heap, buffer); 
  58.     if (IS_ERR_OR_NULL(table)) { 
  59.         heap->ops->free(buffer); 
  60.         kfree(buffer); 
  61.         return ERR_PTR(PTR_ERR(table)); 
  62.     } 
  63.     buffer->sg_table =table
  64.  
  65.     mutex_init(&buffer->lock); 
  66.     /*将当前ion buffer添加到idev 的buffers 树上统一管理。*/ 
  67.     ion_buffer_add(dev, buffer); 
  68.     return buffer; 

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

allocate对应ion_carveout_heap_allocate。

[html] view plaincopyprint?
  1. static int ion_carveout_heap_allocate(struct ion_heap *heap, 
  2.                       struct ion_buffer *buffer, 
  3.                       unsigned long size, unsigned long align, 
  4.                       unsigned long flags) 
  5.     buffer->priv_phys =ion_carveout_allocate(heap, size, align); 
  6.     return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; 
  7. ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, 
  8.                       unsigned long size, 
  9.                       unsigned long align) 
  10.     struct ion_carveout_heap *carveout_heap
  11.         container_of(heap, struct ion_carveout_heap, heap); 
  12.     /*通过创建的mem pool来管理buffer,由于这块buffer在初始化的 
  13. 时候就预留了,现在只要从上面拿一块区域就可以了。*/ 
  14.     unsigned long offset =gen_pool_alloc_aligned(carveout_heap->pool, 
  15.                             size, ilog2(align)); 
  16.     /*分配不成功可能是没有内存空间可供分配了或者是有碎片导致的。*/ 
  17.     if (!offset) { 
  18.         if ((carveout_heap->total_size - 
  19.               carveout_heap->allocated_bytes)>= size) 
  20.             pr_debug("%s: heap %s has enough memory (%lx) but" 
  21.                 " the allocation of size %lx still failed." 
  22.                 " Memory is probably fragmented.", 
  23.                 __func__, heap->name, 
  24.                 carveout_heap->total_size - 
  25.                 carveout_heap->allocated_bytes, size); 
  26.         return ION_CARVEOUT_ALLOCATE_FAIL; 
  27.     } 
  28.     /*已经分配掉的内存字节。*/ 
  29.     carveout_heap->allocated_bytes += size; 
  30.     return offset; 

同样地,对于heap type为ION_HEAP_TYPE_SYSTEM的分配函数是ion_system_heap_allocate。

[html] view plaincopyprint?
  1. static int ion_system_contig_heap_allocate(struct ion_heap *heap, 
  2.                        struct ion_buffer *buffer, 
  3.                        unsigned long len, 
  4.                        unsigned long align, 
  5.                        unsigned long flags) 
  6.     /*通过kzalloc分配。*/ 
  7.     buffer->priv_virt =kzalloc(len, GFP_KERNEL); 
  8.     if (!buffer->priv_virt) 
  9.         return -ENOMEM; 
  10.     atomic_add(len, &system_contig_heap_allocated); 
  11.     return 0; 
其他的几种Heap type可自行研究,接着调用ion_buffer_add将buffer添加到dev的buffers树上去。
[html] view plaincopyprint?
  1. static void ion_buffer_add(struct ion_device *dev, 
  2.                struct ion_buffer *buffer) 
  3.     struct rb_node **p = &dev->buffers.rb_node; 
  4.     struct rb_node *parent =NULL
  5.     struct ion_buffer *entry; 
  6.  
  7.     while (*p) { 
  8.         parent = *p; 
  9.         entry = rb_entry(parent, struct ion_buffer, node); 
  10.  
  11.         if (buffer < entry) { 
  12.             p = &(*p)->rb_left; 
  13.         } else if (buffer > entry) { 
  14.             p = &(*p)->rb_right; 
  15.         } else { 
  16.             pr_err("%s: buffer already found.", __func__); 
  17.             BUG(); 
  18.         } 
  19.     } 
  20. /*又是使用红黑树哦!*/ 
  21.     rb_link_node(&buffer->node, parent, p); 
  22.     rb_insert_color(&buffer->node, &dev->buffers); 

好了buffer创建完成,接下来就要创建Hanle来管理buffer了!

[html] view plaincopyprint?
  1. static struct ion_handle *ion_handle_create(struct ion_client *client, 
  2.                      struct ion_buffer *buffer) 
  3.     struct ion_handle *handle; 
  4.     /*分配struct ion_handle.*/ 
  5.     handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL); 
  6.     if (!handle) 
  7.         return ERR_PTR(-ENOMEM); 
  8.     kref_init(&handle->ref); 
  9.     rb_init_node(&handle->node); 
  10.     handle->client =client; //client放入handle中 
  11.     ion_buffer_get(buffer); //引用计数加1  
  12.     handle->buffer =buffer; //buffer也放入handle中 
  13.  
  14.     return handle; 

创建handle也比较简单,至此,已经得到client和handle,buffer分配完成!

ION_IOC_MAP/ ION_IOC_SHARE

[html] view plaincopyprint?
  1. int ion_share_dma_buf(struct ion_client *client, struct ion_handle *handle) 
  2.     struct ion_buffer *buffer; 
  3.     struct dma_buf *dmabuf; 
  4.     bool valid_handle; 
  5.     int fd; 
  6.  
  7.     mutex_lock(&client->lock); 
  8.     valid_handle = ion_handle_validate(client, handle); 
  9.     mutex_unlock(&client->lock); 
  10.     if (!valid_handle) { 
  11.         WARN(1, "%s: invalid handle passed to share.\n", __func__); 
  12.         return -EINVAL; 
  13.     } 
  14.  
  15.     buffer = handle->buffer; 
  16.     ion_buffer_get(buffer); 
  17.     /*生成一个新的file描述符*/ 
  18.     dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR); 
  19.     if (IS_ERR(dmabuf)) { 
  20.         ion_buffer_put(buffer); 
  21.         return PTR_ERR(dmabuf); 
  22.     } 
  23.     /*将file转换用户空间识别的fd描述符。*/ 
  24.     fd = dma_buf_fd(dmabuf, O_CLOEXEC); 
  25.     if (fd < 0
  26.         dma_buf_put(dmabuf); 
  27.  
  28.     return fd; 
  29. struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops, 
  30.                 size_t size, int flags) 
  31.     struct dma_buf *dmabuf; 
  32.     struct file *file; 
  33. ~~snip 
  34.     /*分配struct dma_buf.*/ 
  35.     dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL); 
  36.     if (dmabuf == NULL) 
  37.         return ERR_PTR(-ENOMEM); 
  38.     /*保存信息到dmabuf,注意ops为dma_buf_ops,后面mmap为调用到。*/ 
  39.     dmabuf->priv =priv
  40.     dmabuf->ops =ops
  41.     dmabuf->size =size
  42.     /*产生新的file*/ 
  43.     file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags); 
  44.  
  45.     dmabuf->file =file
  46.  
  47.     mutex_init(&dmabuf->lock); 
  48.     INIT_LIST_HEAD(&dmabuf->attachments); 
  49.  
  50.     return dmabuf; 
通过上述过程,用户空间就得到了新的fd,重新生成一个新的fd的目的是考虑了两个用户空间进程想共享这块heap内存的情况。然后再对fd作mmap,相应地kernel空间就调用到了file 的dma_buf_fops中的dma_buf_mmap_internal。
[html] view plaincopyprint?
  1. static const struct file_operations dma_buf_fops = { 
  2.     .release    = dma_buf_release
  3.     .mmap       = dma_buf_mmap_internal
  4. }; 
  5. static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) 
  6.     struct dma_buf *dmabuf; 
  7.   
  8.     if (!is_dma_buf_file(file)) 
  9.         return -EINVAL; 
  10.   
  11.     dmabuf = file->private_data; 
  12.     /*检查用户空间要映射的size是否比目前dmabuf也就是当前heap的size 
  13. 还要大,如果是就返回无效。*/ 
  14.     /* check for overflowing the buffer's size */ 
  15.     if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start)>> PAGE_SHIFT) > 
  16.         dmabuf->size >> PAGE_SHIFT) 
  17.         return -EINVAL; 
  18.     /*调用的是dma_buf_ops 的mmap函数*/ 
  19.     return dmabuf->ops->mmap(dmabuf, vma); 
  20.  
  21. struct dma_buf_ops dma_buf_ops = { 
  22.     .map_dma_buf = ion_map_dma_buf
  23.     .unmap_dma_buf = ion_unmap_dma_buf
  24.     .mmap = ion_mmap
  25.     .release = ion_dma_buf_release
  26.     .begin_cpu_access = ion_dma_buf_begin_cpu_access
  27.     .end_cpu_access = ion_dma_buf_end_cpu_access
  28.     .kmap_atomic = ion_dma_buf_kmap
  29.     .kunmap_atomic = ion_dma_buf_kunmap
  30.     .kmap = ion_dma_buf_kmap
  31.     .kunmap = ion_dma_buf_kunmap
  32. }; 
  33. static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) 
  34.     struct ion_buffer *buffer =dmabuf->priv; 
  35.     int ret; 
  36.  
  37.     if (!buffer->heap->ops->map_user) { 
  38.         pr_err("%s: this heap does not define a method for mapping " 
  39.                "to userspace\n", __func__); 
  40.         return -EINVAL; 
  41.     } 
  42.  
  43.     mutex_lock(&buffer->lock); 
  44.     /* now map it to userspace */ 
  45.     /*调用的是相应heap的map_user,如carveout_heap_ops 调用的是 
  46. ion_carveout_heap_map_user ,此函数就是一般的mmap实现,不追下去了。*/ 
  47.     ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma); 
  48.  
  49.     if (ret) { 
  50.         mutex_unlock(&buffer->lock); 
  51.         pr_err("%s: failure mapping buffer to userspace\n", 
  52.                __func__); 
  53.     } else { 
  54.         buffer->umap_cnt++; 
  55.         mutex_unlock(&buffer->lock); 
  56.  
  57.         vma->vm_ops = &ion_vm_ops; 
  58.         /* 
  59.          * move the buffer into the vm_private_data so we can access it 
  60.          * from vma_open/close 
  61.          */ 
  62.         vma->vm_private_data =buffer
  63.     } 
  64.     return ret; 

至此,用户空间就得到了bufferaddress,然后可以使用了!

ION_IOC_IMPORT

当用户空间另一个进程需要这块heap的时候,ION_IOC_IMPORT就派上用处了!注意,

传进去的fd为在ION_IOC_SHARE中得到的。

[html] view plaincopyprint?
  1. struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) 
  2.      
  3.     struct dma_buf *dmabuf; 
  4.     struct ion_buffer *buffer; 
  5.     struct ion_handle *handle; 
  6.  
  7.     dmabuf = dma_buf_get(fd); 
  8.     if (IS_ERR_OR_NULL(dmabuf)) 
  9.         return ERR_PTR(PTR_ERR(dmabuf)); 
  10.     /* if this memory came from ion */ 
  11. ~~snip 
  12.     buffer = dmabuf->priv; 
  13.  
  14.     mutex_lock(&client->lock); 
  15.     /* if a handle exists for this buffer just take a reference to it */ 
  16. /*查找是否已经存在对应的handle了,没有则创建。因为另外一个进程只是 
  17. 调用了open 接口,对应的只创建了client,并没有handle。 
  18. */ 
  19.     handle = ion_handle_lookup(client, buffer); 
  20.     if (!IS_ERR_OR_NULL(handle)) { 
  21.         ion_handle_get(handle); 
  22.         goto end; 
  23.     } 
  24.     handle = ion_handle_create(client, buffer); 
  25.     if (IS_ERR_OR_NULL(handle)) 
  26.         goto end; 
  27.     ion_handle_add(client, handle); 
  28. end: 
  29.     mutex_unlock(&client->lock); 
  30.     dma_buf_put(dmabuf); 
  31.     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给封装了下。

[html] view plaincopyprint?
  1. struct ion_client *msm_ion_client_create(unsigned int heap_mask, 
  2.                     const char *name) 
  3.     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体系结构与编程》存储系统章节



20130227