基础知识
一致性问题是由DMA引起的,在DDR和Cache之间发生的数据不一致问题


DMA
DMA是一种在外设与存储器或存储器与存储器之间快速移动数据的方式,比memcpy效率高(因为不需要cpu参与)。
DMA不能访问CPU的cache。
CPU和DDR、Cache之间的数据传输
CPU写内存的时候有两种方式:
- write through 写通: CPU直接写内存,不经过cache。(不存在一致性问题)
- write back 写回: CPU只写到cache中。cache的硬件使用LRU算法将cache里面的内容替换到内存。通常是这种方式。
对于写回机制:CPU访问内存,先判断访问的内容是否在Cache中,如果在则命中,直接使用该内容。否则,未命中,CPU去内存中把内容读到Cache再去使用该内容。
一致性问题
一致性:Cache中的数据与对应内存中的数据的一致性。
1、DMA从将数据从外设写到内存,CPU中cache中的数据(如果有的话)就是旧数据了,这时CPU在读内存的时候命中cache了,就是读到了旧数据(Dirty 脏)。
2、CPU写数据到内存时,如果只是先写到了cache,则内存里的数据就是旧数据了。
常规MCU的DMA
解决方法:一致性DMA映射(禁用Cache)
使用写通机制,不通过Cache,内存分配的API为 dma_alloc_coherent、dma_alloc_wc。
多数情况下,DMA coherent APIs以CMA区域为申请的后端,这个时候,dma alloc coherent本质上用__alloc_from_contiguous()从CMA区域获取内存,申请出来的内存显然是物理连续的。这一点,在设备树dts里面就可以轻松配置,要么配置一个自己特定的cma区域,要么从“linux,cma-default”指定的缺省的CMA池子里面取内存。(CMA:连续物理内存分配,可以在内核中配置 CONFIG_CMA=y 、CONFIG_DMA_CMA=y ) ——宋宝华
不带MMU(IOMMU)的DMA 申请的内存 都是通过CMA 申请的连续内存,但是带有MMU的DMA申请内存可以不连续。
通过在bootarg中传入参数,设置CMA的大小。( 扩大DMA申请空间方法)
cma=128M /* 设置一个128M的cma空间 */
cma=128M@0x8000000 /* 设置一个128M的cma空间并指定起始地址为0x8000000 */
cma=256M@0-4G /* 在0-4G范围内设置一个256M的cma空间 */
API
dma_alloc_wc

dma_alloc_coherent


解决方法:流式映射(存在Cache)
在DMA读内存(内存到设备方向)时,由于cache中可能有新的数据,因此要先将cache中的数据写回(FLUSH)到内存。
在DMA写内存(设备到内存方向)时,要先将cache无效(INVAILD)。
API
cpu去做刷新/失效,不需要手动我们操作,函数已经封装
dma_map_single (做一遍cache的flush ,将cache的内容flush到内存)
->flush() //在DMA把数据从DDR搬到device之前调用,目的是为了让device看到最新的数据
dma 发报文 (由于做过flush ,发的就是正确的包)
dma_unmap_single
dma_map_single (做一遍cache的invalid,将cache的内容无效,重新读取内存)
->invalid() //在cpu 访问DDR之前调用,目的是为了让cpu看到最新的数据
dma 发报文 (由于做过invalid ,收到的就是正确的包)
dma_unmap_single
dma_map_single 有一个方向的参数的,来决定是invalid 还是 flush
在map期间,CPU不能去操作这段内存
PS
到目前为止,我所接触到的内核TLB映射,做了uncached映射的只有2个:寄存器空间(ioremap)和一致性DMA缓冲区(dma_alloc_coherent),其他地址空间都是cached,来保证系统性能。
ZYNQ的PL端DMA
裸机一致性问题描述
如果在SDK环境下裸调,由于Dcache的存在,CPU和PL往往发现看到的是部份更新的数据,因此需要在代码中加入对Dcache的处理。
裸机解决
用Flush/Invalid,和普通mcu没有区别,只是xilinx官方封装了自己的函数。
Xil_DCacheFlushRange()将cache的数据更新到DDR,让设备能使用最新数据
Xil_DCacheInvalidateRange()将cache失效,从而将ddr的数据更新到cache,使cpu能使用最新数据
在linux下案例
在AXI_DMA的驱动中,使用的coherent分配内存
// Allocate the requested region a contiguous and uncached for DMA

关于缓存与磁盘同步fsync和fflush
同步缓存与磁盘:UNIX系统提供了sync、fsync和fdatasync三个函数。
或者open时给出绕过缓存:O_DIRECT 标志。
sync是系统级:将所有修改过的块缓冲区排入写队列。命令sync也调用sync函数。守护进程会周期性地(一般每隔30秒)调用sync函数。阻塞时间较长。
fsync是文件级:只对fd指定的单一文件起作用。
fflush:c库(用户)缓冲 刷新到 内核缓冲。注意这个fflush和上面的flush不是一个概念
fsync:内核数据刷到磁盘。

170万+

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



