第一章介绍: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详解