我正在Cyclone V SoC上运行Linux 5.1,这是一个FPGA,在一个芯片中具有两个ARMv7内核。我的目标是从外部接口收集大量数据,并通过TCP套接字流出(部分)这些数据。这里的挑战是数据速率非常高,并且可能接近饱和GbE接口。我有一个write()可行的实现,该实现只使用对套接字的调用,但其最高速度为55MB / s;大约是理论GbE限制的一半。我现在正在尝试使零拷贝TCP传输能够提高吞吐量,但是我遇到了麻烦。
为了将数据从FPGA传送到Linux用户空间,我编写了一个内核驱动程序。该驱动程序使用FPGA中的DMA块将大量数据从外部接口复制到附加到ARMv7内核的DDR3存储器中。当使用dma_alloc_coherent()with 进行探测时GFP_USER,驱动程序将该内存分配为一堆连续的1MB缓冲区,并通过mmap()在文件中实现并将这些/dev/地址返回给应用程序使用dma_mmap_coherent()预分配的缓冲区,将这些缓冲区公开给用户空间应用程序。
到目前为止,一切都很好; 用户空间应用程序正在查看有效数据,吞吐率大于360MB / s时,还有足够的余量(外部接口的速度不够快,无法真正看到上限)。
为了实现零拷贝TCP网络,我的第一种方法是SO_ZEROCOPY在套接字上使用:
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
正如我在问题的更新中发布的那样,潜在的问题是,零复制网络不适用于已使用映射的内存remap_pfn_range()(也dma_mmap_coherent()恰好在后台使用)。原因是这种类型的内存(VM_PFNMAP设置了标志)没有struct page*与所需的每个页面相关联的元数据。
然后将溶液是在一种方式分配存储器struct page*小号都与所述存储器相关联。
现在对我来说分配内存的工作流程是:
使用struct page* page = alloc_pages(GFP_USER, page_order);要分配的连续的物理存储器,在那里将被分配的连续页的数目由下式给出的块2page_order。 通过调用,将高阶/复合页面分为0阶页面split_page(page, page_order);。现在,这意味着struct page* page已成为具有2page_order条目的数组。 现在将这样的区域提交给DMA(用于数据接收):
dma_addr = dma_map_page(dev, page, 0, length, DMA_FROM_DEVICE); dma_desc = dmaengine_prep_slave_single(dma_chan, dma_addr, length, DMA_DEV_TO_MEM, 0); dmaengine_submit(dma_desc); 当我们从DMA接收到回调已完成传输时,我们需要取消映射该区域,以将该内存块的所有权转移回CPU,这将负责缓存以确保我们不会读取过时的数据:
dma_unmap_page(dev, dma_addr, length, DMA_FROM_DEVICE);
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。