乐趣区

关于linux:DMA那些事儿

在 VIP 群里,大家探讨热烈,齐全能够看得出来大家都 Linux 技术和嵌入式技术等渴望和赤诚,欢送大家退出笨叔的 VIP 私密群,一起钻研技术,一起奔跑,一起成长。

对于 DMA

有敌人问无关 DMA 的相干问题,那么大家在编写驱动的时候须要额定小心敬慎。因为 DMA 的问题次要会影响 cache 的一致性。那 linux 内核外面提供了不少的 API 来帮忙大家实现 DMA 的操作。

咱们晓得 DMA 应用的物理内存,那么在内核中次要有两类的接口来调配物理内存

  • __get_free_page() 和 alloc_page() 来调配以页为单位的物理内存
  • kmalloc() 和 kmem_cache_alloc() 来调配以字节为单位的物理内存

上述两个接口调配的内存都能够作为 DMA buffer 的原材料。然而咱们零碎调配的物理内存都是 cachable 的,也就是关上 cache 性能的。另外 DMA engine 在做 DMA 传输的时候是不须要 CPU 参加的,所以从这个角度来看,同一个 cache line 有可能 CPU 和 DMA engine 都能拜访到,那就会导致 cache line 被毁坏了,那如何去保障 DMA 操作的时候,cache 这个捣蛋鬼不来捣鬼呢?

一个简略的想法就是,咱们在做 DMA 传输的时候,这段 buffer 我把 cache 给敞开了,这个就是 kernel 实现的一致性的 DMA buffer mapping 的(英文叫做:Consistent DMA mappings)。这里 consistent 的意思是 synchronize 或者 conherent 的意思,CPU 和 DMA 这两个哥们都能同时看到数据的扭转,不须要软件做额定的一些 flush 动作。外围函数是:

dma_addr_t dma_handle;

cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);


这个函数返回两个值,其中 cpu_addr 是虚拟地址,CPU 能够通过这个地址来拜访这段 buffer,另外一个 dma_handle 物理地址,能够传递给 DMA engine。

这里调配的大小以 PAGE_SIZE 为单位。

另外这个函数会调用 alloc_page() 来调配物理页面,所以不要在中断上下文中应用该 API

大家能够想一下一致性的 DMA 有什么毛病? 很显著的一个毛病就是 cache 始终都是敞开的,所以性能就会很低下。比方 DMA 传输实现之后,CPU 去把这个 DMA buffer 的数据取过去,这时候 cache 敞开的,CPU 去读写就变得很慢。那有没有方法能够保障 DMA 的传输的一致性,又能进步性能呢?特地是 CPU 去拜访这段 DMA buffer 的性能呢?

办法是有,那就是 streaming 的 DMA,streaming DAM 有的书上翻译成流式 DMA,从字面上不好了解。其实上大家能够把 DMA 传输合成一下,比方:

设施的 FIFO -> DMA buffer。

这个就是代码里说的 DMA_FROM_DEVICE。数据流向是从设施到物理内存,留神 DMA buffer 是在主存储器,即 DDR 里,此时从 CPU 角度来看,CPU 的工作是读取来自设施的数据,也就是咱们常说的“DMA 读”。

DMA buffer -> 设施的 FIFO。

这个就是代码说的 DMA_TO_DEVICE。数据流向是从主存取器 DDR 到设施,留神 DMA buffer 是在主存储器,即 DDR 里,此时从 CPU 角度来看,CPU 的工作是把数据写入设施,也就是咱们常说的“DMA 写”。

(DMA 读或者写这个说法 不是很谨严,只是大家口头禅都这么说,代码里的 DMA_FROM_DEVICE 和 DMA_TO_DEVICE 这两个宏比拟谨严一些)

那很多人就纳闷了,那 DMA 读和 DMA 写,怎么和 cache 的操作间断在一起呢?到底 DMA 读是要 invalid cache 还是 flush cache 呢?咱们来看一下这个图。

从图里看到,CPU 须要进行 DMA 写操作,也就是把内存中的 buffer A 写入到设施的 FIFO A 外面,那么有可能 cache 外面的数据还没有齐全写入到内存的 buffer A 中,那这时候启动 DMA 的话,最终传递到设施 FIFO A 的数据其实不是 CPU 想写的,因为还有一部分数据早 埋伏在 cache A 中没有 sync 到内存里。这个场景有点相似,咱们拷贝内存到 U 盘,马上插入,而后发现 U 盘没有货色。

咱们来看一下 DMA 读的状况,CPU 想把设施的 FIFO B 的数据读到内存 buffer B 中。那如果在开启 DMA 传输的时候没有去把内存 buffer B 的相应的 cache invalid 的话,那么 DMA 把数据从 FIFO B 到了内存 Buffer B 之后,CPU 去读这个内存 Buffer B 的数据,那么会把之前的 残留在 cache line 的内容 先读到了 CPU,那 CPU 其实是没有读到最新的 FIFO B 的数据的。

总结:

DMA 写,须要 flush cache line。

DMA 读,须要 invalid cache line。

流式 DMA 的罕用的 API 有:

dma_handle = dma_map_single(dev, addr, size, direction);

dma_unmap_single(dev, dma_handle, size, direction);

dma_sync_sg_for_cpu()

dma_sync_sg_for_device()

流式 DMA 映射对于 CPU 何时能够操作 DMA 缓冲区有严格的要求,只能等到 dma_unmap_single 后 CPU 才能够操作该缓冲区。
究其原因,是因为流式 DMA 缓冲区是 cached,在 map 时刷了下 cache,在设施 DMA 实现 unmap 时再刷 cache(依据数据流向写回或者有效),来保障了 cache 数据一致性,在 unmap 之前 CPU 操作缓冲区是不能保证数据统一的。因而 kernel 须要严格保障操作时序。

如果你建设的这个 DMA buffer 须要屡次的来回操作,比方须要在 CPU 去同步拜访这个 buffer,以及设施去拜访这个 buffer。那么能够应用 kernel 也提供函数 dma_sync_single_for_cpu 与 dma_sync_single_for_device 来进行同步操作,而不须要 频繁的去创立和开释 DMA buffer。

当然内核除了反对 single page 的 DMA mapping,还反对 scatterlists 形式的 DMA mapping,这个在应用过程中就要简单一些了。

ARM 外面怎么操作 cache?

有同学问 ARM 外面怎么操作 cache 的?咱们反对 cache 的操作有两种,一个 flush,另外一个 invalid。这外面每一种都能够 flush 整个 cache,也能够依照 range 来操作。

咱们先看一下代码门路:

dma_map_single() -> (ops->map_page) ->arm_dma_map_page() -> __dma_page_cpu_to_dev() ->outer_inv_range() -> v7_dma_inv_range

咱们来看一下这段代码,外面次要是几个 mcr 操作处理器的操作。

有同学问了,这几个协解决操作啥鸟意思?

ok,笨叔以 ARM® Architecture Reference Manual 这文件为例子,看看 ARM 芯片手册上是怎么形容的。

首先咱们翻到 B 5.8.1 这一章外面,咱们看这里有形容 CP15 这个协处理器的形容,咱们晓得它是干啥的,有哪些东东。

从图中红色圈进去的能够看到 CP15 外面的 C7 次要是做 cache 治理的,还有地址转换等。

  1. 咱们持续去看 c7 的形容,翻到 1765 页。

从这张形容 C7 寄存器的图里看到 CRm 为 c14,opc2 为 1 的是做什么用的,这里有一个寄存器名字,DCCIMVAC,它次要是依照 MVA(这里能够了解为虚拟地址或者物理地址)来 clean 和有效 data cache line 的。另外 opc2 为 2 的寄存器是 DCCISW,次要是依照 set 和 way 来有效 cache line 的。

  1. 咱们能够在手册中搜寻“DCCIMVAC”找到更加具体的形容,比方在 B 6.2 外面有更加具体的介绍。

更多更乏味的问题

VIP 私密群里还有很多很乏味的问题:

请问匿名页面是存在于用户地址空间,page cache 是存在于内核空间的,这样子了解对吗?如果是这样子的话,那么如果要统计一个用户过程占据的物理内存时,是否应该要退出 page cache 这部分耗费的内存?

malloc 申请的内存,如果先从 highmem 调配,到了 highmem 的 min 水位,是间接被堵住进行 direct reclaim, 还是跳到 lowmem(假如 lowmem 内存短缺)持续调配?

如果先运行 A,在运行 B,这个时候 A 过程 cachemiss,B 过程 cache 命中,在回到 A,A 如果 invalid,有可能是 invalid B 命中的那个 cache?

欢送大家退出笨叔的 VIP 私密群

退出移动版