【问题标题】:Is a copy necessary for RX/TX with packet_mmap?带有 packet_mmap 的 RX/TX 是否需要副本?
【发布时间】:2017-04-28 20:20:13
【问题描述】:

我已经阅读了我可以阅读的文档,例如 (https://www.mjmwired.net/kernel/Documentation/networking/packet_mmap.txt) 和其他各种博客等。

我仍然不清楚映射到内核的 RX 缓冲区是否也可用于 TX 操作,或者我是否需要在设置发送状态标志和调用 sendto() 之前从 RX 缓冲区复制到 TX .

从文档中,以下是状态:

// RX statuses
#define TP_STATUS_KERNEL        0
#define TP_STATUS_USER          1

// TX statuses
#define TP_STATUS_AVAILABLE        0 // Frame is available
#define TP_STATUS_SEND_REQUEST     1 // Frame will be sent on next send()
#define TP_STATUS_SENDING          2 // Frame is currently in transmission
#define TP_STATUS_WRONG_FORMAT     4 // Frame format is not correct 

流程似乎是:

  1. 数据包通过 TP_STATUS_USER 从内核到达 RX 缓冲区
  2. 我有办法处理数据包,然后设置 TP_STATUS_SEND_REQUEST
  3. 内核设置 TP_STATUS_SENDING,同时发送
  4. 发送完成后内核设置 TP_STATUS_AVAILABLE
  5. 我设置了 TP_STATUS_KERNEL 来告诉内核我已经完成了这些数据包,它们可能会被覆盖

但是,由于 TP_STATUS_AVAILABLE 与 TP_STATUS_KERNEL 保持相同的值,如果此流程正确,内核将在完成 TX 操作后立即清除数据包。在我看来,可能需要复制到单独的 TX 缓冲区。关于使用 packet_mmap 和 packet_tx_ring 的信息有限。我发现的例子主要是 RX(我有工作 atm)。我想要一个 RX/TX 场景的示例(例如桥接原始数据包)。

有人对此有更好的理解吗?示例桥接也是最有帮助的!

【问题讨论】:

    标签: c++ c linux network-programming


    【解决方案1】:

    之前的注释:我仍在阅读和学习PACKET_MMAPaf_packet.c 中的功能,所以我可能错了!

    我认为您想要做的事情在本机上是不可能的,我认为您必须破解该功能。

    在 Rx 模式下使用 TPACKET v3,每个块可以接收成批的帧。所以一个问题可能是,对于 Rx,您需要使用 TPACKET v2 以使 Tx 和 Rx 共享同一个环(但是,只支持 Rx 的 v3 应该更快!)。

    我设置了 TP_STATUS_KERNEL 来告诉内核我已经完成了这些 数据包,它们可能会被覆盖

    在 Tx 模式下使用 TPACKET v2(Tx 不支持 v3,仅 Rx),您可以为每个块编写单个帧并将块状态设置为 TP_STATUS_SEND_REQUEST 并调用 sendto()。传输完成后,内核将块状态转换为TP_STATUS_SENDING,然后转换为TP_STATUS_AVAILABLE

    另一个问题可能是环指针的头部。如果您收到 5 个放入环块中的帧,则内核将环头移动到第 6 个块(下一个空闲空间),准备将可能到达的任何其他帧放入下一个空闲块。同时在用户空间中,您通过poll() 拾取前5 帧,例如,调用do_stuff() 直接在环内编辑其块中的帧,然后如果您尝试对同一个环调用sendto(),您'd 需要将环形指针的头部向后移动 5 个位置。如果某些帧异步到达并且环指针的头部已移回,则您将在发送之前覆盖已修改的帧。

    正如我所说,我相信你可以破解这些东西,但如果你必须修改数据,你不妨设置两个环。我知道现在会在戒指之间引入一个副本,但我猜你想避免。我一直在阅读PACKET_MMAP 源代码,我的理解是它应该减少副本,但它似乎根本没有这样做。我们可以通过调用sendto() 来减少系统调用的数量,以获取“一堆”数据包(环)。

    您可以阅读我在调试 Tx 路径 herehere 时发现的内容。我现在要开始跟踪 Rx 路径。这些链接中关于 Tx 路径的信息摘要是,在用应用程序数据(帧)填充 Tx 环并调用 sendto() 后,内核将每个数据包/帧复制到 sk_buff 中,然后 NIC 将数据复制到它是硬件队列(通过 DMA)在线上发送数据包/帧。所以这是两份副本(尽管需要 DMA 副本!),而且您必须将应用程序数据复制到 Tx 环中,以便生成三份副本。这与没有PACKET_MMAPAF_PACKET 的情况相同,只是我们针对成批的帧而不是针对每个单独的帧启动该过程(因此我们保存了一些上下文切换和系统调用)。

    一旦我跟踪了 Rx 路径,接下来我的列表就是使用 sendmmsg()recvmmsg() 再次比较 Tx 和 Rx。

    【讨论】:

    • 我打算使用这种方法来桥接和更改数据包,以代替 Pcap(在 Linux 上速度非常慢)。我也注意到 TPACKETv3 不支持 TX。使用 v3(针对单个系统调用)获取然后复制到 v2 TX 的另一个自助餐是否有意义(性能明智)?我不确定那里的副本是否比仅使用 RX 和 TX 的 v2 自助餐便宜。想法?
    • @TrunkFullOfGoats 您很幸运,从今天开始(!)使用 TPACKET v3 的 Tx 已添加到最新的内核版本(4.11)中,请参见此处:git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/… 因此,您应该能够使用一个套接字发送和接收,而无需在环之间复制帧(这是提交评论所建议的)。
    • 完美!这当然解决了我的问题。我想补丁细节还解释了 4.11 之前内核中的 RX/TX 操作如何需要单独的描述符。非常感谢您的帮助!
    • @jwbensley:我对 PACKET_MMAP 的 Rx 方面很感兴趣......你有没有写过一些东西和/或你碰巧有一个很好的参考?谢谢!
    • @Nemo 我没有在 ebd 中写下 Rx 路径,它与 Rx 不太相似,但相反。基本上只是阅读内核源代码,它当然是关于这一切是如何工作的真相的来源,一旦你进入它,它并不那么复杂。数据包进入 NIC,被 DMA 写入内存,内核切换上下文以获取数据包并复制到 SKB,SKB 复制到 RX 环,然后您的用户级应用程序可以访问 RX 环中的数据包。
    猜你喜欢
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 2012-11-11
    • 2015-07-25
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 2013-07-26
    相关资源
    最近更新 更多