DMA拼接
涉及到DMA应用的场景中,往往一方(比如FPGA)以固定Packet大小向DMA写入数据,另一方(比如CPU)也以固定Packet大小读出(或直接在DMA上操作)数据。一般情况下,为了提高DMA利用率,CPU处理的数据结构不一定正好是Packet大小的因子(packed模式),在这种场景下会涉及到数据的拼接问题。
我们以直接在DMA上操作数据为例。
在上图中,Packet是可以和DMA大小对齐的,但是被处理的数据结构却不是。这时候,最后一个数据结构DN就需要拼接:
- 计算数据结构的大小-
data_size
- 计算packet 63中DN占用的大小-
dn_leftover_size
- 拷贝
dn_leftover_size
到临时buffer - packet 0来到,从packet 0拷贝
data_size - dn_leftover_size
和临时buffer拼接组成一个完整的数据结构
作为对比,对D1的操作是:
- 计算数据结构的大小-
data_size
- 计算packet 0中D1占用的大小-
d1_leftover_size
- Packet 1来到,指针向左偏移
d1_leftover_size
, 组成一个完整的数据结构
对比DN数据,D1可以避免内存拷贝。
对DN是否也可以呢?很自然的想法是,如果DMA是一个真正的环,那么就可以。能否做到呢?
这种设想如下图:
要知道,CPU为了能够access DMA,需要做一次mmap映射,要解决这个问题,就关系到这种映射是否可以一对多。是可以的。
- 首先常规映射, 整块DMA映射到用户空间的某个起始地址,比如
address_dma
- DMA最后一个packet,再映射到
address_dma - packet_size
那么从CPU看来,packet 0向前推,就变成了 packet 63,连起来了!
从实现逻辑看,操作是:
- 计算DMA的大小-
dma_size
- 计算一个Packet的大小-
packet_size
- mmap一个
MAP_ANONYMOUS | MAP_PRIVATE
的内存,大小为dma_size + packet_size
, 得到virtual_total_address
- 把packet 63以
MAP_SHARED | MAP_FIXED
方式mmap到virtual_total_address
, 大小是packet_size
- 把DMA的起始地址以
MAP_SHARED | MAP_FIXED
方式mmap到virtual_total_address + packet_size
, 大小是dma_size
这样,映射就完成了,从用户空间来看,packet 63和packet 0也是连起来的。
类似的,对于文件,我们也可以通过mmap映射的方法,做到虚拟拼接。把多个小文件,虚拟成一个大文件,或者一个大文件虚拟成多个小文件。