ION框架學習(二)

  第一章介紹:ION的框架和buffer的分配;
  第二章介紹:如何使用ION buffer;

前面一章介紹了ION的整體框架及ION_device,ION_client,ION_heap創建和關係;這章將介紹如使使用操作ION;


(一)ion_alloc

  用戶層已經通過ion_open創建好了client,這個clinet已經跟ION device綁定,現在可以通過ion_alloc()來申請分配內存:

216     if(ion_alloc(
217         mIonDrv,
218         pInfo->size,
219         0, //32 //alignment
220         ION_HEAP_MULTIMEDIA_MASK,
221         ion_prot_flags,
222         &pIonHandle))
system/core/libion/ion.c:

 70 int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
 71               unsigned int flags, ion_user_handle_t *handle)
 72 {
 73     int ret;
 74     struct ion_allocation_data data = {
 75         .len = len,
 76         .align = align,
 77         .heap_id_mask = heap_mask,//指定在那個heap分配;
 78         .flags = flags,
 79     };
 80 
 81     if (handle == NULL)
 82         return -EINVAL;
 83 
 84     ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
 85     if (ret < 0)
 86         return ret;
 87     *handle = data.handle;//獲取到handle之後,返回回去,這個值還不能進程間共享,只能當前進程訪問,因爲這是一個虛擬地址。
 88     return ret;
 89 }

  調用kernel這邊ion device驅動ioctrl()調用ion_alloc()來完成:

 450 struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
 451                  size_t align, unsigned int heap_id_mask,
 452                  unsigned int flags)
        //遍歷所有heap找到mask標識的heap,然後通過ion_buffer_create()創建buffer;
 488     plist_for_each_entry(heap, &dev->heaps, node) {
 489         /* if the caller didn't specify this heap id */
 490         if (!((1 << heap->id) & heap_id_mask))
 491             continue;
 492         buffer = ion_buffer_create(heap, dev, len, align, flags);
 493         if (!IS_ERR(buffer))
 494             break;
 495     } 
         //ion handle: 這裏每個ion handle映射到一個buffer中,每個buffer關聯一個heap。也就是說一個客戶端可以操作多塊buffer。
 508     handle = ion_handle_create(client, buffer);
 510     /*
 511      * ion_buffer_create will create a buffer with a ref_cnt of 1,
 512      * and ion_handle_create will take a second reference, drop one here
 513      */
 514     ion_buffer_put(buffer);
 522     ret = ion_handle_add(client, handle);//將創建的ion_handle加入到client的紅黑樹中;
 523     mutex_unlock(&client->lock);
 524     if (ret) {
 525         ion_handle_put(handle);
 526         handle = ERR_PTR(ret);
 527         IONMSG("%s ion handle add failed %d.\n", __func__, ret);
 528     }

  下面看看buffer的創建;

 107 static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
 108                      struct ion_device *dev,
 109                      unsigned long len,
 110                      unsigned long align,
 111                      unsigned long flags)

 118     buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
         //alloc
 128     ret = heap->ops->allocate(heap, buffer, len, align, flags);
         //map_dma
 144     table = heap->ops->map_dma(heap, buffer);

 154     if (ion_buffer_fault_user_mappings(buffer)) {
 155         int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
 156         struct scatterlist *sg;
 157         int i, j, k = 0;
 158 
 159         buffer->pages = vmalloc(sizeof(struct page *) * num_pages);
 160         if (!buffer->pages) {
 161             IONMSG("%s vamlloc failed pages is null.\n", __func__);
 162             ret = -ENOMEM;
 163             goto err;
 164         }
 165         //scatter/gather lists for DMA I/O operations;
 166         for_each_sg(table->sgl, sg, table->nents, i) {
 167             struct page *page = sg_page(sg);
 168 
 169             for (j = 0; j < sg->length / PAGE_SIZE; j++)
 170                 buffer->pages[k++] = page++;
 171         }
 172     }
 198     for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) {
 199         if ((heap->id == ION_HEAP_TYPE_MULTIMEDIA_MAP_MVA) && (align < PAGE_OFFSET)) {
 200             if (align < VMALLOC_START || align > VMALLOC_END) {
 201                 /*userspace va without vmalloc, has no page struct*/
 202                 sg->length = sg_dma_len(sg);
 203                 continue;
 204             }
 205         }
 206 
 207         sg_dma_address(sg) = sg_phys(sg);
 208         sg_dma_len(sg) = sg->length;
 209     }
 210     mutex_lock(&dev->buffer_lock);
 211     ion_buffer_add(dev, buffer);
 212     mutex_unlock(&dev->buffer_lock);                                                                                                      
 213     return buffer;

  MULTIMEDIA類型的heap最終調用該註冊的ion_heap_ops->ion_mm_heap_allocate()來實現分配:

 192 static int ion_mm_heap_allocate(struct ion_heap *heap,
 193                 struct ion_buffer *buffer, unsigned long size, unsigned long align,
 194         unsigned long flags)

 247 
 248     caller_pid = (unsigned int)current->pid;
 249     caller_tid = (unsigned int)current->tgid;
 250 
 251     while (size_remaining > 0) {
 252         info = alloc_largest_available(sys_heap, buffer, size_remaining, 
 253                            max_order);//每次申請最大的內存
 254         if (!info) {//申請的優先級2k->1K
 255             IONMSG("%s alloc largest available failed info is null.\n", __func__);
 256             goto err;
 257         }
 258         list_add_tail(&info->list, &pages);//每次申請到的page首地址都會添加到info->list鏈表中;
 259         size_remaining -= (1 << info->order) * PAGE_SIZE;
 260         max_order = info->order;
 261         i++;
 262     } 

 271     table = kzalloc(sizeof(*table), GFP_KERNEL);
 272     if (!table) {
 273         IONMSG("%s kzalloc failed table is null.\n", __func__);
 274         goto err;
 275     }
 276 
 277     ret = sg_alloc_table(table, i, GFP_KERNEL);
 278     if (ret) {
 279         IONMSG("%s sg alloc table failed %d.\n", __func__, ret);
 280         goto err1;
 281     }
 282 
 283     sg = table->sgl;
 284     list_for_each_entry_safe(info, tmp_info, &pages, list) {
 285         struct page *page = info->page;
 286 
 287         sg_set_page(sg, page, (1 << info->order) * PAGE_SIZE, 0);
 288         sg = sg_next(sg);
 289         list_del(&info->list);
 290         kfree(info);
 291     }


(二)ion_share

  ION 通過handle來管理buffer,驅動需要可以訪問到buffer的地址。ION通過下面的函數來達到這個目的:

  ion_phys: 返回buffer的物理地址(address)及大小(size);
  ion_map_kernel: 給指定的buffer創建內核內存映射;
  ion_unmap_kernel: 銷燬指定buffer的內核內存映射;
  ion_map_dma: 爲指定buffer創建dma 映射,返回;sglist(scatter/gather list)
  ion_unmap_dma: 銷燬指定buffer的dma映射;

  當我們想進程間共享這個buffer時,就需要使用ioctl(fd,ION_IOC_SHARE, &fd_data)來得到這個buffer的唯一id,其中fd_data就是struct ion_fd_data:

 54 struct ion_fd_data {                                                                                                                       
 55  ion_user_handle_t handle;
 56  int fd;
 57 };

  創建文件描述符來實現共享內存,ION_IOC_SHARE 及ION_IOC_IMPORT是基於DMABUF實現的,所以當共享進程獲取文件描述符後,可以直接調用mmap來操作共享內存。mmap實現由DMABUF子系統調用ION子系統中mmap回調函數完成。

  進程之間共享buffer時,也是通過binder機制將共享ion_buffer發送給對應的進程,然後對應的進程在根據這個fd將該塊buffer映射進自己的進程中。

  在kernel ion driver中可以發現,上層應用通過系統調用將我們想share的ion_handle傳下來(其實就是對應idr),然後通過這個handle找到真正的ion_handle。根據ion_buffer在dma buffer中找到其對應的fd,然後其它進程通過這個fd就可以找到對應的buffer了。

  ION是通過handle而非buffer地址來實現驅動間共享內存,用戶空間共享內存也是利用同樣原理。

ion_share: given a handle, obtain a buffer to pass to other clients
ion_import: given an buffer in another client, import it
ion_import_fd: given an fd obtained via ION_IOC_SHARE ioctl, import it

240     if(ion_share(
241         mIonDrv,
242         pIonHandle,
243         &IonBufFd))
244     {
245         printf("ion_share fail");
246         return -1;
247     }
1441     case ION_IOC_SHARE:
1442     case ION_IOC_MAP:
1443     {
1444         struct ion_handle *handle;
1445 
1446         handle = ion_handle_get_by_id(client, data.handle.handle);
1447         if (IS_ERR(handle)) {
1448             ret = PTR_ERR(handle);
1449             IONMSG("ION_IOC_SHARE handle is invalid. handle = %d, ret = %d.\n", data.handle.handle, ret);
1450             return ret;
1451         }
1452         data.fd.fd = ion_share_dma_buf_fd(client, handle);
1453         ion_handle_put(handle);
1454         if (data.fd.fd < 0) {
1455             IONMSG("ION_IOC_SHARE fd = %d.\n", data.fd.fd);
1456             ret = data.fd.fd;
1457         }
1458         break;
1459     }

  fd:當我們想共享這個buffer時,使用ION_IOC_SHARE kernel就會給我們返回一個唯一標識這個buffer的fd,並保存到fd域中;

1247 int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle)
1248 {
1249     struct dma_buf *dmabuf;
1250     int fd;
1254     dmabuf = ion_share_dma_buf(client, handle);
1255     if (IS_ERR(dmabuf)) {
1256         IONMSG("%s dmabuf is err 0x%p.\n", __func__, dmabuf);
1257         return PTR_ERR(dmabuf);
1258     }
1259 
1260     fd = dma_buf_fd(dmabuf, O_CLOEXEC);
1266 
1267     return fd;
1268 }


(三)ion_mmap

  通過ion_mmap()來Map FD到一個虛擬地址空間;
  mmap系統調用,可以讓進程的虛擬地址區間裏切分出一塊指定大小的虛擬地址區間vma_struct,並返回給用戶態進程,被mmap映射返回的虛擬地址,邏輯上被消耗了,直到用戶進程調用unmmap纔會收回來。

250     pInfo->virtAddr = (MUINTPTR)ion_mmap(mIonDrv,NULL, pInfo->size, PROT_READ|PROT_WRITE, MAP_SHARED, IonBufFd, 0);

138void* ion_mmap(int fd, void *addr, size_t length, int prot, int flags, int share_fd, off_t offset)
139{
140    void *mapping_address = NULL;
141
142    mapping_address =  mmap(addr, length, prot, flags, share_fd, offset);
143
144    if (mapping_address == MAP_FAILED) {
145        ALOGE("ion_mmap failed fd = %d, addr = 0x%p, len = %zu, prot = %d, flags = %d, share_fd = %d, 0x%p: %s\n", fd, addr, length,
146              prot, flags, share_fd, mapping_address, strerror(errno));
147    }
148
149    return mapping_address;
150}


(四)ion_import

  另外一個進程想操作buffer,就可以通過ion_import()來實現,通過傳入share已經文件描述符IonBufFd;

370             if(ion_import(
371                 mIonDrv,
372                 IonBufFd,
373                 &pIonHandle))
1460     case ION_IOC_IMPORT:
1461     {                                                                                                                                     
1462         struct ion_handle *handle;
1463 
1464         handle = ion_import_dma_buf(client, data.fd.fd);
1465         if (IS_ERR(handle)) {
1466             ret = PTR_ERR(handle);
1467             IONMSG("ion_import fail: fd=%d, ret=%d\n", data.fd.fd, ret);
1468         } else
1469             data.handle.handle = handle->id;
1470         break;
1471     }
1271 struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)                                                                  
1272 {
1273     struct dma_buf *dmabuf;
1274     struct ion_buffer *buffer;
1275     struct ion_handle *handle;
1276     int ret;
1277      //根據fd獲取dma buffer;
1278     dmabuf = dma_buf_get(fd);

1283     /* if this memory came from ion */
1284 

1291     buffer = dmabuf->priv;
1292 
1293     mutex_lock(&client->lock);
1294     /* if a handle exists for this buffer just take a reference to it */
1295     handle = ion_handle_lookup(client, buffer);
1296     if (!IS_ERR(handle)) {
1297         ion_handle_get(handle);
1298         mutex_unlock(&client->lock);
1299         goto end;
1300     }
         //重新創建一個handle;
1303     handle = ion_handle_create(client, buffer);
1304     if (IS_ERR(handle)) {
1305         mutex_unlock(&client->lock);
1306         IONMSG("%s handle is error 0x%p.\n", __func__, handle);
1307         goto end;
1308     }
1309 
1310     ret = ion_handle_add(client, handle);
1311     mutex_unlock(&client->lock);
1312 

1325     return handle;
1326 }
1327 EXPORT_SYMBOL(ion_import_dma_buf);

  用戶空間另一個進程也得到了對應的buffer Handle,client/buffer/handle之間連接起來了!然後另一個一個進程就也可以使用mmap來操作這塊heap buffer了。和一般的進程使用ION區別就是共享的進程之間struct ion_buffer是共享的,而struct ion_handle是各自的。





參考:

Rationalizing scatter/gather chains

Linux內存管理之mmap詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章