热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

dma_mmap_coherent()映射内存的零拷贝用户空间TCP发送

如何解决《dma_mmap_coherent()映射内存的零拷贝用户空间TCP发送》经验,为你挑选了1个好方法。

我正在Cyclone V SoC上运行Linux 5.1,这是一个FPGA,在一个芯片中具有两个ARMv7内核。我的目标是从外部接口收集大量数据,并通过TCP套接字流出(部分)这些数据。这里的挑战是数据速率非常高,并且可能接近饱和GbE接口。我有一个write()可行的实现,该实现只使用对套接字的调用,但其最高速度为55MB / s;大约是理论GbE限制的一半。我现在正在尝试使零拷贝TCP传输能够提高吞吐量,但是我遇到了麻烦。

为了将数据从FPGA传送到Linux用户空间,我编写了一个内核驱动程序。该驱动程序使用FPGA中的DMA模块将大量数据从外部接口复制到连接到ARMv7内核的DDR3存储器中。当使用dma_alloc_coherent()进行探测时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;
}

但是,这导致send: Bad address

谷歌搜索了一段时间之后,我的第二种方法是使用管道,splice()然后执行以下操作vmsplice()

ssize_t sent_bytes;
int pipes[2];
struct iovec iov = {
    .iov_base = buf,
    .iov_len = len
};

pipe(pipes);

sent_bytes = vmsplice(pipes[1], &iov, 1, 0);
if (sent_bytes <0) {
    perror("vmsplice");
    return -1;
}
sent_bytes = splice(pipes[0], 0, fd, 0, sent_bytes, SPLICE_F_MOVE);
if (sent_bytes <0) {
    perror("splice");
    return -1;
}

但是,结果是相同的:vmsplice: Bad address

请注意,如果我替换了对仅打印由(或不带)指向的数据的函数的调用vmsplice()或调用,则一切正常。因此用户空间可以访问数据,但是/ 调用似乎无法处理它。send()bufsend() MSG_ZEROCOPYvmsplice()send(..., MSG_ZEROCOPY)

我在这里想念什么?有什么方法可以使用零拷贝TCP发送,并使用从内核驱动程序获取的用户空间地址dma_mmap_coherent()?我可以使用另一种方法吗?

更新

因此,我深入sendmsg() MSG_ZEROCOPY研究了内核中的路径,最终失败的调用是get_user_pages_fast()。该调用返回-EFAULT是因为check_vma_flags()找到了中VM_PFNMAP设置的标志vma。当使用remap_pfn_range()或将页面映射到用户空间时,显然会设置此标志dma_mmap_coherent()。我的下一个方法是找到另一种访问mmap这些页面的方法。



1> rem..:

正如我在问题的更新中发布的那样,潜在的问题是,零复制网络不适用于使用映射的内存remap_pfn_range()(也dma_mmap_coherent()恰好在后台使用)。原因是这种类型的内存(VM_PFNMAP设置了标志)没有所需的struct page*与每个页面相关联的元数据。

然后将溶液是在一种方式分配存储器struct page*小号与所述存储器相关联。

现在对我来说分配内存的工作流程是:

    使用struct page* page = alloc_pages(GFP_USER, page_order);要分配的连续的物理存储器,在那里将被分配的连续页的数目由下式给出的块2**page_order

    通过调用,将高阶/复合页面分为0阶页面split_page(page, page_order);。现在,这意味着struct page* page已成为具有2**page_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);

现在,当我们想要实现时mmap(),我们真正要做的就是vm_insert_page()重复调用我们预分配的所有0阶页面:

static int my_mmap(struct file *file, struct vm_area_struct *vma) {
    int res;
...
    for (i = 0; i <2**page_order; ++i) {
        if ((res = vm_insert_page(vma, vma->vm_start + i*PAGE_SIZE, &page[i])) <0) {
            break;
        }
    }
    vma->vm_flags |= VM_LOCKED | VM_DONTCOPY | VM_DONTEXPAND | VM_DENYWRITE;
...
    return res;
}

关闭文件后,请不要忘记释放页面:

for (i = 0; i <2**page_order; ++i) {
    __free_page(&dev->shm[i].pages[i]);
}

实现mmap()这种方式现在允许一个插座使用该缓冲区sendmsg()MSG_ZEROCOPY标志。

尽管此方法可行,但有两种方法无法使我满意:

您只能使用此方法分配2的幂次方缓冲区,尽管您可以实现逻辑以alloc_pages减小的顺序按需要多次调用,以获取由大小不同的子缓冲区组成的任何大小的缓冲区。然后,这将需要一些逻辑,以将这些缓冲区关联在一起,mmap()并通过scatter-gather(sg)调用而不是DMA对其进行DMA single

split_page() 在其文档中说:

 * Note: this is probably too low level an operation for use in drivers.
 * Please consult with lkml before using this in your driver.

如果内核中有一些接口可以分配任意数量的连续物理页面,则可以轻松解决这些问题。我不知道为什么不存在,但是我不认为上述问题如此重要,以至于无法解释为什么不可用/如何实现它:-)


推荐阅读
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • MySQL缓存机制深度解析
    本文详细探讨了MySQL的缓存机制,包括主从复制、读写分离以及缓存同步策略等内容。通过理解这些概念和技术,读者可以更好地优化数据库性能。 ... [详细]
  • 优化联通光猫DNS服务器设置
    本文详细介绍了如何为联通光猫配置DNS服务器地址,以提高网络解析效率和访问体验。通过智能线路解析功能,域名解析可以根据访问者的IP来源和类型进行差异化处理,从而实现更优的网络性能。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 深入理解Java中的volatile、内存屏障与CPU指令
    本文详细探讨了Java中volatile关键字的作用机制,以及其与内存屏障和CPU指令之间的关系。通过具体示例和专业解析,帮助读者更好地理解多线程编程中的同步问题。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文深入探讨了如何通过调整InnoDB的关键配置参数来优化MySQL的随机IO性能,涵盖了缓存、日志文件、预读机制等多个方面,帮助读者全面提升数据库系统的性能。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 理解存储器的层次结构有助于程序员优化程序性能,通过合理安排数据在不同层级的存储位置,提升CPU的数据访问速度。本文详细探讨了静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)的工作原理及其应用场景,并介绍了存储器模块中的数据存取过程及局部性原理。 ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 本文详细介绍了 MySQL 的查询处理流程,包括从客户端连接到服务器、查询缓存检查、语句解析、查询优化及执行等步骤。同时,深入探讨了 MySQL 中的乐观锁机制及其在并发控制中的应用。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • PHP 5.5.0rc1 发布:深入解析 Zend OPcache
    2013年5月9日,PHP官方发布了PHP 5.5.0rc1和PHP 5.4.15正式版,这两个版本均支持64位环境。本文将详细介绍Zend OPcache的功能及其在Windows环境下的配置与测试。 ... [详细]
author-avatar
zx15899966868
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有