目录
代码引入
5.6内核后引入
drivers\dma-buf\heaps\cma_heap.c
| kernel\drivers\dma-buf\dma-heap.c | 分配内存的入口 |
| /drivers/dma-buf/rk_heaps/rk-dma-heap.c | 瑞芯微分配内存的入口 |
| \kernel\drivers\dma-buf\dma-buf.c | dma buf mmap的入口 |
以下通过调用链来查看其主要功能。
调用链示例
核心结构体 dma_buf_ops
通过这个结构体,可以了解,cma_heap 作为dma buf的其中一个底层实现,实现了dma buf所要求的各个功能接口,进而在dma buf操作时回调cma heap的接口。
dma buf的底层实现有很多,cma heap只是其中一个。
此结构体的功能接口为: cma buf的应用,包括buffer的attach,用户态映射,内核态映射,以及cache 同步。
static const struct dma_buf_ops cma_heap_buf_ops = {
.attach = cma_heap_attach,
.detach = cma_heap_detach,
.map_dma_buf = cma_heap_map_dma_buf,
.unmap_dma_buf = cma_heap_unmap_dma_buf,
.begin_cpu_access = cma_heap_dma_buf_begin_cpu_access,
.end_cpu_access = cma_heap_dma_buf_end_cpu_access,
.begin_cpu_access_partial = cma_heap_dma_buf_begin_cpu_access_partial,
.end_cpu_access_partial = cma_heap_dma_buf_end_cpu_access_partial,
.mmap = cma_heap_mmap,
.vmap = cma_heap_vmap,
.vunmap = cma_heap_vunmap,
.release = cma_heap_dma_buf_release,
};
核心结构体 dma_heap_ops
通过这个结构体,可以了解到cma heap是dma heap的其中一个实现,驱动调用到dma heap时,通过回调,调用到cma heap的分配接口。
static const struct dma_heap_ops cma_heap_ops = {
.allocate = cma_heap_allocate,
#if IS_ENABLED(CONFIG_NO_GKI)
.get_phys = cma_heap_get_phys,
#endif
};
在系统中位置

调用示例
分配内存
源码位置: kernel\drivers\dma-buf\dma-heap.c
瑞芯微实现了自己的接口: /drivers/dma-buf/rk_heaps/rk-dma-heap.c
用户态应用通过此文件接口,分配所需的内存,示例代码:
struct dma_heap_allocation_data alloc_data = {
.len = dma_heap_size,
.fd = 0,
.fd_flags = (O_RDWR | O_CLOEXEC),
.heap_flags = 0,
};
if (ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &alloc_data)) {
perror("DMA_HEAP_IOCTL_ALLOC");
return EXIT_FAILURE;
}

上述接口会返回分配内存对应的句柄:即dma buf的fd
fd = dma_buf_fd(dmabuf, fd_flags);
内存使用
内存使用的流程相对较多,单独开一章节进行说明。
以v4l2的调用关系
\kernel\drivers\media\common\videobuf2\videobuf2-dma-contig.c
mmap
用户态采用分配接口获得的dma buf的fd进行映射。
dma_heap_start = mmap(NULL, dma_heap_size, PROT_READ | PROT_WRITE, MAP_SHARED, alloc_data.fd, 0);
内核态代码
\kernel\drivers\dma-buf\dma-buf.c
dma_buf_mmap_internal---》cma_heap_mmap 映射出用户态虚拟地址
static const struct file_operations dma_buf_fops = {
.release = dma_buf_file_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
.poll = dma_buf_poll,
.unlocked_ioctl = dma_buf_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.show_fdinfo = dma_buf_show_fdinfo,
};
另外一条映射到用户态的路径示例:
./drivers/rknpu/rknpu_drv.c
static const struct file_operations rknpu_drm_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.mmap = rknpu_gem_mmap,
\kernel\drivers\rknpu\rknpu_gem.c
rknpu_gem_mmap-》dma_buf_mmap-》cma_heap_mmap
延时映射
观察下述代码,映射到用户态时,并没有调用我们常见的remap_pfn_range 接口。
这里特别注意:除非特殊配置为uncached,否则即为cached。
static int cma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
struct cma_heap_buffer *buffer = dmabuf->priv;
if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
return -EINVAL;
if (buffer->uncached)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &dma_heap_vm_ops;
vma->vm_private_data = buffer;
return 0;
}
-
该函数只是设置了
vm_ops和vm_private_data,实际的物理内存映射会被延迟到页错误(page fault)发生时处理。 -
当进程首次访问该内存区域时,会触发页错误,内核会调用
vm_ops->fault回调函数来完成实际的映射。
vm_fault的实现
vmf->page = buffer->pages[vmf->pgoff];
get_page(vmf->page);
return 0;
当 fault 处理程序返回 0 且设置了 vmf->page 后,内核会自动完成:
-
页表项的建立
-
物理地址的映射
-
引用计数管理(这就是为什么需要
get_page)

至此用户程序已经完全可以访问此段内存了。
那么内核驱动如何访问此段内存,例如摄像头驱动如何将数据放入到此内存呢?毕竟到现在为止,内核驱动还不清楚此内存的存在。此外RK在驱动中实现了自己的一套dma heap,其背后原因又是什么呢?


1068

被折叠的 条评论
为什么被折叠?



