外设数据到昇腾310推理卡 之六cma_heap分配与映射

目录

代码引入

调用链示例

核心结构体  dma_buf_ops

 核心结构体 dma_heap_ops

在系统中位置

调用示例

分配内存

内存使用

mmap

延时映射

vm_fault的实现


代码引入

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.cdma 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,其背后原因又是什么呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

proware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值