设备看到的地址未必是CPU地址
CPU代码使用的虚拟地址、CPU物理地址和设备使用的DMA地址可能属于不同空间。IOMMU或主桥可在设备地址与物理内存之间建立映射,因此不能把普通指针原样交给设备,就假定它会访问同一缓冲区。
Linux的DMA API为驱动建立适用映射并返回设备可用的DMA地址;驱动还要说明设备能寻址的范围。映射可能失败或受设备限制,建立映射、启动传输和确认完成是不同步骤,成功取得CPU内存并不等于设备已经可用。
参考:[1]
缓冲区怎样在CPU与设备间交接
Linux区分一致性映射与流式映射。一致性映射适合描述符环等长期共享结构;流式映射常围绕一次传输或一段使用期建立。传输方向是相对于设备读写内存而言,影响接口应进行的处理,不能随意填一个双向值代替判断。
重复使用流式缓冲区时,CPU和设备之间需要按接口完成同步与交接;最终不再使用还需解除映射。CPU不能因为设备已发出某种通知,就忽略平台要求的缓存同步,也不能在设备仍使用内存时提前释放或另作他用。
参考:[1]
缓存一致并不自动保证顺序
即使使用一致性DMA内存,CPU也可能重排不同字段的写入。先填描述符地址,再设置有效标记的协议,需要让设备按要求先看到内容、后看到标记;相应内存屏障与设备接口仍然必要。
DMA减少CPU逐个数据搬运的指令,却不消除准备描述符、处理错误、分配缓冲区和完成交接的成本。评价它的效果需要考虑传输规模、映射与同步开销及设备能力,不能简单理解为“完全不使用CPU”。