天天看點

ION架構學習(一)

第一章介紹:ION的架構和buffer的配置設定;

第二章介紹:如何使用ION buffer;

  ION是google在Android4.0 為了解決記憶體碎片管理而引入的通用記憶體管理器,用來支援不同的記憶體配置設定機制,如CARVOUT(PMEM),實體連續記憶體(kmalloc), 虛拟位址連續但實體不連續記憶體(vmalloc), IOMMU等

(一)ION 概括

  Heap: 用來表示記憶體配置設定的相關資訊,包括id, type, name等。用struct ion_heap表示。

  Client: Ion的使用者,使用者空間和核心控件要使用ION的buffer,必須先建立一個client,一個client可以有多個buffer,用struct ion_buffer表示。

  Handle: 将buffer該抽象出來,可以認為ION用handle來管理buffer,一般使用者直接拿到的是handle,而不是buffer。 用struct ion_handle表示。

  下面是整個架構的類圖:

ION架構學習(一)

說明如下:

  1. 在driver初始化過程中會建立ion_device,并提供給使用者層ioctrl接口;不同平台根據定義好的platform data heap類型建立heap,同時指定這寫heap操作使用的方法接口;不同類型的heap連接配接成連結清單,儲存在通過plist_head heap;
  2. 需要使用heap,先注冊client,不同的heap有多個client來使用,連接配接成連結清單儲存在rb_root client;不同的client跟不同的程序pid綁定;
  3. ion_handle管理實際ion_buffer, 成員id:唯一表示ion_buffer的整型變量,通過核心的idr機制加上這個id就可以在其它程序中通路到這塊buffer。而且上層在傳下來的struct ion_allocation_data結構體中也隻是儲存了這個id。

  使用者空間和核心空間都可以成為client,不過建立的方法稍稍有點差別;

  1. 核心空間:先建立client,有了client之後就可以配置設定記憶體,有了handle也就是buffer之後就準備使用了,不過還是實體位址,需要map:
  2. 使用者空間:使用者空間如果想使用ION,也必須先要建立client,不過它是打開/dev/ion,實際上它最終也會調用ion_client_create。不過和核心空間建立client的一點差別是,使用者空間不能選擇heap type(使用預訂的heap id隐含heap type),但是核心空間卻可以。另外,使用者空間是通過IOCTL來配置設定記憶體的,cmd為ION_IOC_ALLOC.
ion_fd = open("/dev/ion", O_ RDONLY | O_SYNC);   
    ioctl(ion_fd, ION_IOC_ALLOC, alloc);  
           

  配置設定好了buffer之後,如果使用者空間想使用buffer,先需要mmap. ION是通過先調用IOCTL中的ION_IOC_SHARE/ION_IOC_MAP來得到可以mmap的fd,然後再執行mmap得到buffer address.然後你也可以将此fd傳給另一個程序,如通過binder傳遞。在另一個程序中通過ION_IOC_IMPORT這個IOCTL來得到這塊共享buffer了。

(二)ION Device和Heap建立

  通過ION device和driver比對之後走入ion_drv_probe()函數:

kernel-/drivers/staging/android/ion/mtk/ion_drv.c

 static int ion_drv_probe(struct platform_device *pdev)
        //建立ion device,name為ion,并提供應用層操作接口ion_fops;建立調試節點:/sys/kernel/debug/ion
     g_ion_device = ion_device_create(ion_custom_ioctl);
       //ion_custom_ioctl soc自定義的ioctl操作;
 
     /* create the heaps as specified in the board file */
     for (i = ; i < num_heaps; i++) {
         struct ion_platform_heap *heap_data = &pdata->heaps[i];
         struct ion_heap *heap;
 
         if (heap_data->type == ION_HEAP_TYPE_CARVEOUT && heap_data->base == ) {
             /* reserve for carveout heap failed */
             heap_data->size = ;
             continue;
         }
 
         heap = ion_mtk_heap_create(heap_data);
 
         ion_device_add_heap(g_ion_device, heap);
     }
           

  這個函數首先建立一個裝置ion_device,通過ion_device_create()來建立,傳入一個給到使用者層面用的ioctl函數:

static long _ion_ioctl(struct ion_client *client, unsigned int cmd,
                unsigned long arg, int from_kernel) {
     long ret = ;
 
     ION_FUNC_ENTER;
     switch (cmd) {
     case ION_CMD_SYSTEM:
         ret = ion_sys_ioctl(client, cmd, arg, from_kernel);
         break;
     case ION_CMD_MULTIMEDIA:
         ret = ion_mm_ioctl(client, cmd, arg, from_kernel);
         break;
     }                                                                                                                                      
     ION_FUNC_LEAVE;
     return ret;
 }
           

  處理的ioctrl指令有:

enum ION_MM_CMDS {
      ION_MM_CONFIG_BUFFER,                                                                                                                  
      ION_MM_SET_DEBUG_INFO,      
      ION_MM_GET_DEBUG_INFO,
      ION_MM_SET_SF_BUF_INFO,
      ION_MM_GET_SF_BUF_INFO,
      ION_MM_CONFIG_BUFFER_EXT
  };           
  
  enum ION_SYS_CMDS { 
      ION_SYS_CACHE_SYNC,
      ION_SYS_GET_PHYS,
      ION_SYS_GET_CLIENT, 
      ION_SYS_SET_HANDLE_BACKTRACE,
      ION_SYS_SET_CLIENT_NAME,
      ION_SYS_DMA_OP, 
  };
           

  不同的平台實作這些方式不一樣,然後根據ion_platform_data表示平台定義的各種類型的heap資料類型來配置設定heap,以高通平台是定義在dts裡面,mtk平台定義在驅動當中,舉例如下:

static struct ion_platform_heap ion_drv_platform_heaps[] = {
         {
                 .type = ION_HEAP_TYPE_SYSTEM_CONTIG,
                 .id = ION_HEAP_TYPE_SYSTEM_CONTIG,
                 .name = "ion_system_contig_heap",
                 .base = ,
                 .size = ,
                 .align = ,
                 .priv = NULL,
         },
         {
                 .type = ION_HEAP_TYPE_MULTIMEDIA,
                 .id = ION_HEAP_TYPE_MULTIMEDIA,
                 .name = "ion_mm_heap",
                 .base = ,
                 .size = ,
                 .align = ,
                 .priv = NULL,
         },
         {
                 .type = ION_HEAP_TYPE_MULTIMEDIA,
                 .id = ION_HEAP_TYPE_MULTIMEDIA_FOR_CAMERA,
                 .name = "ion_mm_heap_for_camera",
                 .base = ,
                 .size = ,
                 .align = ,
                 .priv = NULL,
         },
           

  根據ion_platform_data定義的heap類型然後接下來配置設定ion heap結構,不同的heap類型代表不同的配置設定方式:

enum ion_heap_type {                                                                                                                       
      ION_HEAP_TYPE_SYSTEM, //通過vmalloc配置設定記憶體;
      ION_HEAP_TYPE_SYSTEM_CONTIG, //通過kmalloc配置設定記憶體;
      ION_HEAP_TYPE_CARVEOUT, //在保留記憶體塊中(reserve memory)配置設定記憶體;
      ION_HEAP_TYPE_CHUNK, //子產品;
      ION_HEAP_TYPE_DMA,
      ION_HEAP_TYPE_CUSTOM, //由客戶自己定義
      ION_NUM_HEAPS = ,
  };
           

  mtk平台又根據heap data存儲的資料對象分為:

enum mtk_ion_heap_type {
      ION_HEAP_TYPE_MULTIMEDIA = ,
      ION_HEAP_TYPE_FB = ,
      ION_HEAP_TYPE_MULTIMEDIA_FOR_CAMERA = ,
      ION_HEAP_TYPE_MULTIMEDIA_SEC = ,
      ION_HEAP_TYPE_MULTIMEDIA_MAP_MVA = ,
      ION_HEAP_TYPE_MULTIMEDIA_PA2MVA = ,
  };
           

  以ION_HEAP_TYPE_MULTIMEDIA資料類型為例,heap的建立最後通過ion_mm_heap_create()實作:

//不同type的heap需要不同的method去配置設定,不過都是用struct ion_heap_ops來表示;
  static struct ion_heap_ops system_heap_ops = {
          .allocate = ion_mm_heap_allocate,
          .free = ion_mm_heap_free,
          .map_dma = ion_mm_heap_map_dma,//map the memory for dma to a scatterlist;
          .unmap_dma = ion_mm_heap_unmap_dma,
          .map_kernel = ion_heap_map_kernel,//map memory to the kernel;
          .unmap_kernel = ion_heap_unmap_kernel,
          .map_user = ion_heap_map_user,
          .phys = ion_mm_heap_phys,//get physical address of a buffer;
          .shrink = ion_mm_heap_shrink,
          .page_pool_total = ion_mm_heap_pool_total,
  };

 static const unsigned int orders[] = { ,  };  //兩個記憶體池,為别為2^1k和2^0k;

  struct ion_heap *ion_mm_heap_create(struct ion_platform_heap *unused)
  {                                                                                                                                         
      struct ion_system_heap *heap;
      int i;
  
      heap = kzalloc(sizeof(*heap), GFP_KERNEL);
      if (!heap) {
          IONMSG("%s kzalloc failed heap is null.\n", __func__);
          return ERR_PTR(-ENOMEM);
      }
      heap->heap.ops = &system_heap_ops;
      heap->heap.type = ION_HEAP_TYPE_MULTIMEDIA;
      heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
      heap->pools = kcalloc(num_orders, sizeof(struct ion_page_pool *), GFP_KERNEL);
      if (!heap->pools)
          goto err_alloc_pools;
      heap->cached_pools = kcalloc(num_orders, sizeof(struct ion_page_pool *), GFP_KERNEL);
      if (!heap->cached_pools) {
          kfree(heap->pools);
          goto err_alloc_pools;
      }
      //建立兩種大小類型的pool,後面的alloc就會根據order在這裡面配置設定;
      for (i = ; i < num_orders; i++) {
          struct ion_page_pool *pool;
          gfp_t gfp_flags = low_order_gfp_flags;
  
          if (orders[i] > )
              gfp_flags = high_order_gfp_flags;
  
          if (unused->id == ION_HEAP_TYPE_MULTIMEDIA_FOR_CAMERA)
              gfp_flags |= __GFP_HIGHMEM | __GFP_MOVABLE;
           pool = ion_page_pool_create(gfp_flags, orders[i]);//主要初始化這個pool;
          if (!pool)
              goto err_create_pool;
          heap->pools[i] = pool;
  
          pool = ion_page_pool_create(gfp_flags, orders[i]);
          if (!pool)
              goto err_create_pool;
          heap->cached_pools[i] = pool;
      } 
  }

 struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
 {                                                                                                                                          
     struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
                          GFP_KERNEL); 
     if (!pool) {
         IONMSG("%s kmalloc failed pool is null.\n", __func__);
         return NULL;
     }
     pool->high_count = ;
     pool->low_count = ; 
     INIT_LIST_HEAD(&pool->low_items); //每一個pool都包含了高位址和低位址的雙向連結清單;
     INIT_LIST_HEAD(&pool->high_items);
     pool->gfp_mask = gfp_mask | __GFP_COMP;
     pool->order = order;
     mutex_init(&pool->mutex);
     plist_node_init(&pool->list, order);
 
     return pool;
 }
           

(三)添加ION client

  核心和使用者空間都會可以建立ion client,使用者空間open ion裝置來建立client,MTK封裝了一層mt_ion_open(),最終調用ion_client_create()來建立;差別在與使用者層建立的name是根據pid:

pIon_client = ion_client_create(g_ion_device, "camera_isp"); 
           
kernel-/drivers/staging/android/ion/ion.c:

 struct ion_client *ion_client_create(struct ion_device *dev,
                       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;
  
      get_task_struct(current->group_leader);
      task_lock(current->group_leader);
      pid = task_pid_nr(current->group_leader);
      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);
  
      client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
      if (!client)
         goto err_put_task_struct;
  
      client->dev = dev;
      client->handles = RB_ROOT;
      idr_init(&client->idr);
      mutex_init(&client->lock);
      client->task = task;
      client->pid = pid;
      client->name = kstrdup(name, GFP_KERNEL);
      if (!client->name)
          goto err_free_client;
  
      down_write(&dev->lock);
      client->display_serial = ion_get_client_serial(&dev->clients, name);
      client->display_name = kasprintf(
          GFP_KERNEL, "%s-%d", name, client->display_serial);
      if (!client->display_name) {
          up_write(&dev->lock);
          goto err_free_client_name;
      }
    //把該clinet放到ion device的紅黑樹清單裡面;
      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;
      }
      rb_link_node(&client->node, parent, p);
      rb_insert_color(&client->node, &dev->clients);
  
      client->debug_root = debugfs_create_file(client->display_name, ,
                          dev->clients_debug_root,
                          client, &debug_client_fops);
      if (!client->debug_root) {
          char buf[], *path;
 896 
 897         path = dentry_path(dev->clients_debug_root, buf, 256);
 898         pr_err("Failed to create client debugfs at %s/%s\n",
 899             path, client->display_name);
 900     }
 901 
 902     up_write(&dev->lock);
 903 
 904     return client;
 905 
 914 }
           

繼續閱讀