开发中遇到的缓存一致性问题

基础知识

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

DMA

DMA是一种在外设与存储器或存储器与存储器之间快速移动数据的方式,比memcpy效率高(因为不需要cpu参与)。

DMA不能访问CPU的cache。

CPU和DDR、Cache之间的数据传输

CPU写内存的时候有两种方式:

  1. write through 写通: CPU直接写内存,不经过cache。(不存在一致性问题)
  2. 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:内核数据刷到磁盘。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值