linux dma_alloc_coherent cache一致性问题

发布时间 2023-10-10 17:02:33作者: 别给我提bug了

1. 问题背景

想提升vpu编解码帧率,在vpu的设备树节点添加dma-coherent属性,vpu编解码timeout(失败);

2. 所做尝试

2.1 vpu内存分配接口

b->virt = dma_alloc_coherent(dev, PAGE_ALIGN(size), &b->dma, GFP_DMA | GFP_KERNEL

2.2 分析

  • 增加了dma-coherent属性之后,vpu编解码失败;
  • vpu是一个与DDR强交互的ip(从ddr中取数据进行编解码,将编解码成功的数据返回给ddr)
    所以进而考虑到可能是cache 一致性问题;

2.3 尝试

增加刷cache接口

vpu获取ddr数据

将cpu中的数据刷到ddr中:
vb2_ops->buf_queue: dma_sync_single_for_device(flush cache:刷写cache中内容到内存。即flush cache)

vpu返回数据给ddr

vb2_ops->buf_finish:dma_sync_single_for_cpu(invalid cache:从内存更新数据到cache,即invalid cache中的内容)

结果

vpu 编解码任然失败,

3. 学习dma cache一致性知识

参考:https://blog.csdn.net/juS3Ve/article/details/79135998

DMA ZONE

产生背景

  • DMA可以直接在内存和外设之间进行数据搬移,对于内存的存取来讲,它和CPU一样,是一个访问master,可以直接访问内存。

  • DMA ZONE产生的本质原因是:不一定所有的DMA都可以访问到所有的内存,这本质上是硬件的设计限制。所以我们划分一个zone,保证这个范围的内存是dma硬件绝对可以访问到的。

根据本质原理,我们可以了解,DMA ZONE的大小是取决于dma硬件的寻址能力的,这个区域要不要,要的话范围是多少,根据dma的寻址能力判断。

DMA ZONE的内存只能做DMA吗?

DMA ZONE的内存做什么都可以。DMA ZONE的作用是让有缺陷的DMA对应的外设驱动申请DMA buffer的时候从这个区域申请而已,但是它不是专有的。其他所有人的内存(包括应用程序和内核)也可以来自这个区域。

dma_alloc_coherent()申请的内存来自DMA ZONE?

dma_alloc_coherent()申请的内存来自于哪里,不是因为它的名字前面带了个dma_就来自DMA ZONE的,本质上取决于对应的DMA硬件是谁。

        if (mask < 0xffffffffULL)

                gfp |= GFP_DMA;

(这段代码存在arm架构,arm64已经不存在了)

dma_alloc_coherent()申请的内存是非cache的吗?

  • 缺省情况下,dma_alloc_coherent()申请的内存缺省是进行uncache配置的。

  • 也可以带cache

    If the machine sets arm_coherent_dma_ops rather than arm_dma_ops,
    
    the memory will be cacheable,
    
    • 那我们什么时候选择带cache的分配方式呢?当可以用硬件做CPU和外设的cache coherence时候。(硬件自动同步cache)
    • 怎么选择这种分配方式呢?猜测是使用dma-coherent属性;

4. 最终解决

vpu获取ddr数据

将cpu中的数据刷到ddr中:
vb2_ops->buf_queue:arch_flush_dcache(flush cache:刷写cache中内容到内存。即flush cache)

vpu返回数据给ddr

vb2_ops->buf_finish:arch_invalidate_dcache(invalid cache:从内存更新数据到cache,即invalid cache中的内容)

为什么dma_sync_single_for_cpu/device不行

因为当有coherent属性的时候,这时候分配的内存带有cache了(没有coherent属性默认地址是不带cache的);而且系统会默认咱们硬件会处理cache一致性;

使用dma_sync_single_for_cpu/device时候,由于系统默认咱们处理了cache一致性,此时这两个函数并没有真正去刷cache,所以出现了问题