DMA拼接

涉及到DMA应用的场景中,往往一方(比如FPGA)以固定Packet大小向DMA写入数据,另一方(比如CPU)也以固定Packet大小读出(或直接在DMA上操作)数据。一般情况下,为了提高DMA利用率,CPU处理的数据结构不一定正好是Packet大小的因子(packed模式),在这种场景下会涉及到数据的拼接问题。

我们以直接在DMA上操作数据为例。

/statics/dma-ring.png

在上图中,Packet是可以和DMA大小对齐的,但是被处理的数据结构却不是。这时候,最后一个数据结构DN就需要拼接:

  1. 计算数据结构的大小-data_size
  2. 计算packet 63中DN占用的大小-dn_leftover_size
  3. 拷贝dn_leftover_size到临时buffer
  4. packet 0来到,从packet 0拷贝data_size - dn_leftover_size和临时buffer拼接组成一个完整的数据结构

作为对比,对D1的操作是:

  1. 计算数据结构的大小-data_size
  2. 计算packet 0中D1占用的大小-d1_leftover_size
  3. Packet 1来到,指针向左偏移d1_leftover_size, 组成一个完整的数据结构

对比DN数据,D1可以避免内存拷贝。

对DN是否也可以呢?很自然的想法是,如果DMA是一个真正的环,那么就可以。能否做到呢?

这种设想如下图:

/statics/dma-ring2.png

要知道,CPU为了能够access DMA,需要做一次mmap映射,要解决这个问题,就关系到这种映射是否可以一对多。是可以的。

  1. 首先常规映射, 整块DMA映射到用户空间的某个起始地址,比如address_dma
  2. DMA最后一个packet,再映射到address_dma - packet_size

那么从CPU看来,packet 0向前推,就变成了 packet 63,连起来了!

从实现逻辑看,操作是:

  1. 计算DMA的大小-dma_size
  2. 计算一个Packet的大小-packet_size
  3. mmap一个MAP_ANONYMOUS | MAP_PRIVATE的内存,大小为dma_size + packet_size, 得到virtual_total_address
  4. 把packet 63以MAP_SHARED | MAP_FIXED方式mmap到virtual_total_address, 大小是packet_size
  5. 把DMA的起始地址以MAP_SHARED | MAP_FIXED方式mmap到virtual_total_address + packet_size, 大小是dma_size

这样,映射就完成了,从用户空间来看,packet 63和packet 0也是连起来的。

类似的,对于文件,我们也可以通过mmap映射的方法,做到虚拟拼接。把多个小文件,虚拟成一个大文件,或者一个大文件虚拟成多个小文件。