外设数据到昇腾310推理卡 之七cma_heap内核使用

  

目录

cma_heap_attach

map_dma_buf

总体流程

cache的同步

    begin_cpu_access

invalidate_kernel_vmap_range

dma_sync_sgtable_for_cpu

dma_sync_single_for_cpu

end_cpu_access

cache.S 

同步关系图

总结

   参考资料


         在前一篇文章中,描述了cma heap的分配及映射到用户空间。那么内核态的驱动如何使用呢?    

cma_heap_attach

基本流程

当用户态调用到VIDIOC_QBUF,即将buffer填充到摄像头控制器等设备的缓存队列中

drivers\media\common\videobuf2\videobuf2-core.c

  vb2_core_qbuf-》__buf_prepare-》__prepare_dmabuf

attach_dmabuf-》cma_heap_attach

设备与buffer建立联系

其中table是dma buffer 散列化后的table

dev 为摄像头控制器对应的dev

struct dma_heap_attachment {
	struct device *dev;
	struct sg_table *table;
	struct list_head list;
	bool mapped;

	bool uncached;
};

设备buffer队列与buffer建立联系

此处将dbuf传递给设备vb queue中。

vb->planes[plane].dbuf = dbuf;
vb->planes[plane].mem_priv = mem_priv;

最终效果如上,此时设备已经和 cma heap buffer建立关联,但设备还不能使用,因为dma buf目前只分配了物理地址以及映射了用户态虚拟地址,而总线地址和内核态虚拟地址还没有映射

map_dma_buf

当用户态调用到VIDIOC_QBUF,如下紫色部分为两个回调接口,其后的函数为具体实现

1)./drivers/media/common/videobuf2/videobuf2-core.c:1275:         ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
__prepare_dmabuf--》map_dmabuf--》vb2_dc_map_dmabuf--》

2)\drivers\media\common\videobuf2\videobuf2-dma-contig.c

vb2_dc_map_dmabuf--》dma_buf_map_attachment

3)\kernel\drivers\dma-buf\dma-buf.c : 

dma_buf_map_attachment-》map_dma_buf-cma_heap_map_dma_buf

.4)最终调用接口,这里主要完成将物理地址映射成dma地址。

dma_addr_t dma_addr = phys_to_dma(dev, phys);

int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
		enum dma_data_direction dir, unsigned long attrs)
{
	int i;
	struct scatterlist *sg;

	for_each_sg(sgl, sg, nents, i) {
		sg->dma_address = dma_direct_map_page(dev, sg_page(sg),
				sg->offset, sg->length, dir, attrs);
		if (sg->dma_address == DMA_MAPPING_ERROR)
			goto out_unmap;
		sg_dma_len(sg) = sg->length;
	}

	return nents;

       完成此步骤后,设备已经具有了dma 总线地址,可以将这些地址写入到控制器的寄存器中或者描述符队列中,以便设备DMA时使用了。至此,外部设备就可以开始正常工作,往soc发送数据,应用层通过mmap出的地址可以读取数据了。

cma_heap_unmap_dma_buf和cma_heap_detach 在释放内存资源的时候调用,这里就不再赘述。

总体流程

上图从右到左

1) 用户应用通过 dma heap 分配所需的内存

2) 通过dma buf 将分配的内存映射到用户空间,默认带cache

3)将内存通过dma buf接口挂接到v4l2设备上,并将内存映射成总线地址。

4) 启动设备传输流程,设备即可以使用此段内存。

5)用户程序进行cache的同步,而后读取数据

cache的同步

    由于映射到用户空间默认是带cache的,当dma完毕后,用户获取数据时要进行cache的同步操作。

    begin_cpu_access

  作用为外设DMA完毕后,如果外设不支持dma 一致性,则需要在cpu访问内存前,进行invalide的操作。

代码如下: 

struct cma_heap_buffer *buffer = dmabuf->priv;
	struct dma_heap_attachment *a;

	if (buffer->vmap_cnt)
		invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);

	if (buffer->uncached)
		return 0;

	mutex_lock(&buffer->lock);
	list_for_each_entry(a, &buffer->attachments, list) {
		if (!a->mapped)
			continue;
		dma_sync_sgtable_for_cpu(a->dev, &a->table, direction);
	}
	
	mutex_unlock(&buffer->lock);

对于5.10内核,其还有如下一段

if (list_empty(&buffer->attachments)) {
		phys_addr_t phys = page_to_phys(buffer->cma_pages);

		dma_sync_single_for_cpu(dma_heap_get_dev(buffer->heap->heap),
					phys + offset,
					len,
					direction);
	}

总体分为三个部分

invalidate_kernel_vmap_range

  对于此段内存,如果映射到内核虚拟地址,则调用此接口。

       此函数的实现,除了上图的少数几个CPU 架构,其余实现都是空的。在我们应用中,内存也不会映射到内核,此段忽略。

dma_sync_sgtable_for_cpu

  最终对于sg table中的每个sg调用接口

void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
		enum dma_data_direction dir)
{
	__dma_unmap_area(phys_to_virt(paddr), size, dir);
}

dma_sync_single_for_cpu

void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
		enum dma_data_direction dir)
{
	__dma_unmap_area(phys_to_virt(paddr), size, dir);
}

end_cpu_access

总体流程和 begin cpu access 类似,即内核虚拟地址的flush,这部分接口涉及到的cpu架构和begin 一致。 以及attach 的sg的flush。

void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
		enum dma_data_direction dir)
{
	__dma_map_area(phys_to_virt(paddr), size, dir);
}

cache.S 

cache.S    arch\arm64\mm   

cache的最终同步接口。这里使用内核虚拟地址。

/*
 *	__dma_map_area(start, size, dir)
 *	- start	- kernel virtual start address
 *	- size	- size of region
 *	- dir	- DMA direction
 */
SYM_FUNC_START_PI(__dma_map_area)
	cmp	w2, #DMA_FROM_DEVICE
	b.eq	__dma_inv_area
	b	__dma_clean_area
SYM_FUNC_END_PI(__dma_map_area)

/*
 *	__dma_unmap_area(start, size, dir)
 *	- start	- kernel virtual start address
 *	- size	- size of region
 *	- dir	- DMA direction
 */
SYM_FUNC_START_PI(__dma_unmap_area)
	cmp	w2, #DMA_TO_DEVICE
	b.ne	__dma_inv_area
	ret
SYM_FUNC_END_PI(__dma_unmap_area)

 

armasm

add	x1, x1, x0			// x1 = 结束地址(start + size)
dcache_line_size x2, x3		// x2 = 缓存行大小(如 64 字节)
sub	x3, x2, #1			// x3 = 缓存行掩码(如 0x3F)

armasm

tst	x1, x3				// 检查结束地址是否对齐缓存行
bic	x1, x1, x3			// 对齐结束地址(向下舍入)
b.eq	1f				// 如果已对齐,跳过
dc	civac, x1			// 清理并失效未对齐的结束行
1:

armasm

tst	x0, x3				// 检查起始地址是否对齐缓存行
bic	x0, x0, x3			// 对齐起始地址(向下舍入)
b.eq	2f				// 如果已对齐,跳过
dc	civac, x0			// 清理并失效未对齐的起始行
b	3f
2:
dc	ivac, x0			// 仅失效已对齐的缓存行
3:

armasm

add	x0, x0, x2			// 移动到下一缓存行
cmp	x0, x1				// 是否到达结束地址?
b.lo	2b				// 未到达则循环
dsb	sy				// 等待所有操作完成
ret					// 返回

同步关系图

最终看起来为上图:

1) cma heap 分配内存。

2)mmap映射到用户态虚拟地址

3)通过内核态虚拟地址进行cache同步

4) 通过用户态虚拟地址进行访问。

phys_to_virt(paddr)和 vmap映射出的地址有什么差异?为何还需要invalidate_kernel_vmap_range操作?是因为架构的需求?

一段物理内存,如果需要同步cache,获取其对应的内核地址,在内核空间进行同步。

总结

    老一辈的内核书籍介绍DMA时,区分一致性接口dma_coherent接口和流式映射dma_map_single接口,都是由驱动主导内存的分配。

    而dma heap相关接口,提供用户态程序分配内存的接口,进而将这些内存通过dma buf机制attach到对应的设备。同时提供了cache同步接口。 内存的管理完全交给了用户态应用程序。

   分配内存的本质,分配一块物理内存;而后根据需要映射成用户态虚拟地址、内核态虚拟地址、总线地址。当然并不需要全部映射。通常的应用中,并不需要映射成内核态虚拟地址。

   假设NPU 能访问整个内存空间,则通过NPU的映射机制将这段内存映射到NPU的地址空间,NPU程序可以直接读写此段内存,则也完全不需要拷贝了。

   所以问题的核心在于 

  1) soc /fpga/npu/gpu等访问的物理内存地址范围。

   2)物理地址映射到某个SOC NPU GPU的方式。 例如简单些: 

    

   参考资料

以下资料介绍了调用示例,并对比mmap和dma heap时的延时。

解决 v4L2 流式 I/O 在 dma-heap #Linux 性能不佳的问题 - 奇塔

<think>好的,用户现在的问题是想要了解DRM或DMABUF_HEAPS_ROCKCHIP_CMA_HEAP相关的依赖关系和配置。我需要结合之前提供的引用内容来解答这个问题。首先,我需要回顾一下用户提到的引用内容。 引用[1]中提到了ION驱动中的几种堆类型,包括ION_DRM_HEAP_ID,属于ION_HEAP_TYPE_DRM类型。这说明DRM堆是ION框架的一部分,可能与DMA-BUF机制有关联。引用[2]提到cma-heap是从cma_alloc分配内存,适用于没有IOMMU的外设直接访问,而DRM-GEM是另一个分配器。这可能意味着DRM堆和CMA堆在内存分配方式上有不同的应用场景。引用[3]则涉及解决RGA MMU不支持超过4G内存的问题,使用了特定的DMA堆路径,这可能与CMA或DRM堆的配置有关。 接下来,用户的问题是关于DRM或DMABUF_HEAPS_ROCKCHIP_CMA_HEAP的依赖和配置。首先,我需要确认这两个配置选项在Linux内核中的位置和相关依赖。根据经验,DRM通常依赖DMA-BUF和IOMMU子系统,而CMA堆则依赖于CMA(连续内存分配器)的配置。 对于DMABUF_HEAPS_ROCKCHIP_CMA_HEAP,这可能是一个针对Rockchip平台的CMA堆实现,需要确保CONFIG_CMA配置开启,并且Rockchip特定的DMA堆支持被启用。同时,DRM堆可能依赖DRM子系统的支持,比如CONFIG_DRM和CONFIG_DRM_ROCKCHIP等配置选项。 还需要考虑设备树中的配置,因为Rockchip平台通常需要在设备树中定义CMA区域或相关的DMA堆节点。例如,引用[3]提到的路径/dev/dma_heap/system-uncached-dma32可能对应设备树中的一个节点,用于分配特定类型的内存。 此外,用户可能需要在内核配置中启用相关的选项,如CONFIG_DMABUF_HEAPS,CONFIG_DMABUF_HEAPS_ROCKCHIP_CMA等。依赖关系可能包括DMA_BUF、CMA、以及特定于Rockchip的驱动模块。 需要确保用户了解如何在内核配置中查找这些选项,并正确配置设备树以支持所需的堆类型。同时,可能需要模块或内置驱动的加载,以确保相关功能可用。 最后,用户可能需要验证配置是否正确,例如通过检查/proc/iomem或使用dma_heap工具来确认堆是否可用。此外,引用[3]中的代码示例展示了如何使用特定的DMA堆路径进行内存分配,这可能对用户调试和验证配置有帮助。</think>关于DRM和DMABUF_HEAPS_ROCKCHIP_CMA_HEAP的依赖关系及配置,结合内核驱动实现分析如下: #### 一、DRM相关配置 1. **基础依赖** - 必须启用DRM核心框架: `CONFIG_DRM=y` - Rockchip平台需启用专用DRM驱动: `CONFIG_DRM_ROCKCHIP=y` - 需要DMA-BUF共享机制支持: `CONFIG_DMA_SHARED_BUFFER=y` `CONFIG_SYNC_FILE=y`[^1][^2] 2. **GEM内存管理** 显存分配依赖ION或DMA-Heap子系统: ```c // 如引用[1]所示ION_DRM_HEAP_ID对应DRM专用内存池 struct ion_heap_desc ion_heap_meta[] = { { .id = ION_DRM_HEAP_ID, .type = ION_HEAP_TYPE_DRM } }; ``` 需配置`CONFIG_ION_DRM_HEAP=y`或`CONFIG_DMABUF_HEAPS_ROCKCHIP_DRM=y` #### 二、DMABUF_HEAPS_ROCKCHIP_CMA_HEAP配置 1. **基础依赖链** ``` CONFIG_CMA ➔ CONFIG_DMABUF_HEAPS ➔ CONFIG_DMABUF_HEAPS_CMA ➔ CONFIG_DMABUF_HEAPS_ROCKCHIP_CMA ``` 2. **关键配置项** - CMA连续内存分配器: `CONFIG_CMA=y` 且需在设备树定义CMA区域大小 - DMA-Heap子系统: `CONFIG_DMABUF_HEAPS=y` - Rockchip专用CMA堆: `CONFIG_DMABUF_HEAPS_ROCKCHIP_CMA=y` 3. **设备树配置示例** ```dts reserved-memory { cma_region: cma-region { compatible = "shared-dma-pool"; reusable; size = <0x10000000>; // 256MB CMA区域 linux,cma-default; }; }; dma-heap { compatible = "rockchip,dma-heap-cma"; memory-region = <&cma_region>; }; ``` #### 三、硬件加速关联 RGA等硬件模块使用时需确保物理地址连续性(如引用[3]通过`system-uncached-dma32`分配4G内地址): ```c #define DMA_HEAP_DMA32_UNCACHED_PATH "/dev/dma_heap/system-uncached-dma32" dma_buf_alloc(DMA_HEAP_DMA32_UNCACHED_PATH, size, &fd, &dma_buf); // 确保物理地址<4G ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

proware

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

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

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

打赏作者

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

抵扣说明:

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

余额充值