ION框架学习(二)

来源:互联网 发布:直销大师软件 编辑:程序博客网 时间:2024/06/05 18:02

  第一章介绍: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 //alignment220         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;141142    mapping_address =  mmap(addr, length, prot, flags, share_fd, offset);143144    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    }148149    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         } else1469             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详解