在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私密群