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详解

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