7. Mbuf Library

7. Mbuf Library

        mbuf库提供了分配和释放缓冲区(mbufs)的能力,DPDK应用程序可以使用它来存储消息(报文)。消息(报文)实际存储在mempool中,使用Mempool Library.

        rte_mbuf 结构体可以携带网络数据包或通用控制缓冲区(由CTRL_MBUF_FLAG标识)。也可以扩展到其他类型。rte_mbuf头结构保持尽可能小,目前只使用两个cache lines,经常使用的字段需要放在两个cache lines的第一个。

7.1. Design of Packet Buffers

为了存储数据包数据(包括协议头),考虑了两种方法(元数据只是某一功能的数据缓存的总称):

  1. 在mbuf结构体的存储空间里的分出一个固定大小缓存作为元数据来存储数据包。
  2. 分配一个独立的缓存空间存储数据包,并将缓存空间地址保存到mbuf的元数据。
        第一个方法的优点是,它只需要一次操作来分配/释放数据包的整个内存。另一方面,第二种方法更加灵活,允许将元数据结构的分配与数据包缓冲区的分配完全分离。
        第一个方法是为DPDK选择的。元数据包含控制信息,如消息类型、长度、数据偏移量(相对于数据起始处)和额外mbuf结构的指针,以便形成缓冲区链(当一个mbuf装不下一个数据包报文时,分成多个mbuf,每个mbuf通个这个指针形成链表)
        用于传输网络数据包的消息缓冲区可以处理需要多个缓冲区来保存完整数据包的缓冲区链。比如处理巨型帧的情况,巨型帧必须由多个mbuf通过next字段连接在一起组成。
        对于一个新分配的mbuf,数据包起始位置是在消息缓冲区起始位置+RTE_PKTMBUF_HEADROOM个字节后开始的,它是cache对齐的。消息缓冲区可以用于在系统中不同实体之间传输控制信息、数据包、事件等。消息缓冲区也可以使用它们的缓冲区指针指向其他消息缓冲区数据段或其他结构。
        Fig. 7.1 和 Fig. 7.2 展示了一些这样的情景。

        缓冲区管理器实现了一组相当标准的缓冲区访问函数来操作网络数据包。

7.2. Buffers Stored in Memory Pools

        缓冲区管理器使用 Mempool Library来分配缓冲区。因此,它确保了在L3处理中,包头在channels和ranks中最优交叉存取。一个mbuf包含一个字段,该字段表明它来自的哪个内存池。当调用rte_ctrlmbuf_free(m)或rte_pktmbuf_free(m)时,mbuf返回到其分配池中。

7.3. Constructors

        数据包操作和控制mbuf的操作函数由相应API提供。rte_pktmbuf_init()和rte_ctrlmbuf_init()函数初始化mbuf结构中一些字段,mbuf一旦被创建,这些字段就不能被用户修改(mbuf类型、分配池、缓冲区开始地址,等等)。这个函数作为一个回调函数参数,在内存池创建调用rte_mempool_create()时传入。

7.4. Allocating and Freeing mbufs

     分配一个新的mbuf需要用户指定使用哪个内存池。对于任何新分配的mbuf,它包含一个长度为0段。数据偏移量被初始化为指定大小(RTE_PKTMBUF_HEADROOM),以便在缓冲区中保留一些空间。

        释放mbuf意味着将其返回到分配内存池中。在内存池中存储时mbuf的内容不会被修改(作为free的mbuf)。构造函数初始化的字段不需要在mbuf再次分配时重新初始化。当释放的数据包含有多个mbuf时,所有这些mbuf都被释放并返回到自己所属的分配mempool。

7.5. Manipulating mbufs

        这个库提供了一些函数来处理数据包mbuf中的数据。例如:获取数据长度

  • 获取数据长度
  • 获取数据起始位置指针
  • 在数据前面添加数据
  • 在数据后面添加数据
  • 在缓冲区的开头删除数据(rte_pktmbuf_adj())
  • 在缓冲区的末尾删除数据(rte_pktmbuf_trim()),详细参考 DPDK API Reference
7.6. Meta Information
        一些信息由网络驱动程序返回,并存储在mbuf中以便使处理更容易。例如,VLAN、RSS hash 值(见 Poll Mode Driver)和一个指示校验和是否由硬件计算的标志。
        mbuf还包含输入端口(它来自哪里),以及链中的mbuf段的数量。
        对于链式缓冲区,只有链的第一个mbuf存储这个元信息。(链式就是多个mbuf存储一个大数据包的情况)
        例如,IEEE1588RX方向的 数据包时间戳机制、VLAN标记和IP校验和计算。
        在TX方向,应用程序也可以将一些处理委托给硬件,如果它支持的话。例如,PKT_TX_IP_CKSUM标志允许卸载IPv4校验和的计算(软件不计算,交给硬件计算)。
        下面的示例解释了如何在vxlan封装的tcp包上配置不同的TX卸载:out_eth/out_ip/out_udp/vxlan/in_eth/in_ip/in_tcp/payload
  • 计算out_ip的校验和
    mb->l2_len = len(out_eth)
    mb->l3_len = len(out_ip)
    mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM
    set out_ip checksum to 0 in the packet
    当硬件通告DEV_TX_OFFLOAD_IPV4_CKSUM时标明支持这个特性。
  • 计算out_ip和out_udp的校验和
    mb->l2_len = len(out_eth)
    mb->l3_len = len(out_ip)
    mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM | PKT_TX_UDP_CKSUM
    set out_ip checksum to 0 in the packet
    set out_udp checksum to pseudo header using rte_ipv4_phdr_cksum()
    当硬件通告DEV_TX_OFFLOAD_IPV4_CKSUM和DEV_TX_OFFLOAD_UDP_CKSUM时标明支持这个特性。
  • 计算in_ip的校验和
    mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth)
    mb->l3_len = len(in_ip)
    mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM
    set in_ip checksum to 0 in the packet
    这个和第一个案例类似,但是l2_len不同。当硬件通告DEV_TX_OFFLOAD_IPV4_CKSUM时标明支持这个特性。
    注意,只有当出方向L4的校验和设置为0才能生效。
  • 计算out_ip和out_tcp的校验和
    mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth)
    mb->l3_len = len(in_ip)
    mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CSUM | PKT_TX_TCP_CKSUM
    set in_ip checksum to 0 in the packet
    set in_tcp checksum to pseudo header using rte_ipv4_phdr_cksum()
    这个和第二个案例类似,但是l2_len不同。当硬件通告DEV_TX_OFFLOAD_IPV4_CKSUM 和 DEV_TX_OFFLOAD_TCP_CKSUM时标明支持这个特性。
    注意,只有当出方向L4的校验和设置为0才能生效。

  • tcp段
    mb->l2_len = len(out_eth + out_ip + out_udp + vxlan + in_eth)
    mb->l3_len = len(in_ip)
    mb->l4_len = len(in_tcp)
    mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM |
      PKT_TX_TCP_SEG;
    set in_ip checksum to 0 in the packet
    set in_tcp checksum to pseudo header without including the IP
      payload length using rte_ipv4_phdr_cksum()
    当硬件通告DEV_TX_OFFLOAD_TCP_TSO时标明支持这个特性。注意,只有当出方向L4的校验和设置为0才能生效。
  • 计算out_ip,in_ip,in_tcp校验和
    mb->outer_l2_len = len(out_eth)
    mb->outer_l3_len = len(out_ip)
    mb->l2_len = len(out_udp + vxlan + in_eth)
    mb->l3_len = len(in_ip)
    mb->ol_flags |= PKT_TX_OUTER_IPV4 | PKT_TX_OUTER_IP_CKSUM  | \
      PKT_TX_IP_CKSUM |  PKT_TX_TCP_CKSUM;
    set out_ip checksum to 0 in the packet
    set in_ip checksum to 0 in the packet
    set in_tcp checksum to pseudo header using rte_ipv4_phdr_cksum()
    当硬件通告DEV_TX_OFFLOAD_IPV4_CKSUM,DEV_TX_OFFLOAD_UDP_CKSUM 和 DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM时标明支持这个特性。
        在mbuf API文档(rte_mbuf.h)中描述了标记的列表及其确切含义。还可以参考testpmd源代码(特别是csumonly.c文件)的细节。
7.7. Direct and Indirect Buffers
        直接缓冲区是一个完全独立自主的缓冲区。间接缓冲区的行为类似于直接缓冲区,但它的缓冲区指针和数据偏移量指的是另一个直接缓冲区中的数据。这在需要复制或分散包的情况下很有用,因为间接缓冲区提供了跨多个缓冲区重用相同的数据包的方法。
        当使用rte_pktmbuf_attach()将缓冲区“关联”到一个直接缓冲区时,就变成了间接缓冲区(其实这类似于数据包空间只有一份,mbuf的管理信息有多份)。每个直接缓冲区都有一个引用计数器字段,当间接缓冲区被关联到一个直接缓冲区时,直接缓冲区的引用计数器就会增加。类似地,当间接缓冲区被分离时,直接缓冲区的引用计数器就会减少。如果结果引用计数器为0,则直接缓冲区将被释放,因为它不再被使用。
        在处理间接缓冲区时,需要记住一些事情。首先,间接缓冲不能关联到另一个间接缓冲。试图将缓冲区A关联到B,但B又关联到C的,rte_pktmbuf_attach()自动将A关联到C,高效地克隆B。其次,为了使缓冲区变成间接缓冲区,它的引用计数器必须等于1,也就是说,它不能被另一个间接缓冲区所引用。最后,不能将间接缓冲区再次关联到同一个直接缓冲区(除非它已经分离了)。
        虽然可以使用推荐的rte_pktmbuf_attach()和rte_pktmbuf_detach()函数直接调用实现关联/分离操作,但建议使用更高级别的rte_pktmbuf_clone()函数,该函数负责处理间接缓冲区的正确初始化,并可以正确克隆多个mbuf段(一个数据包装在多个mbuf的情况)。
       由于间接缓冲区实际上不保存任何数据,因此应该配置间接缓冲区的内存池,以减少内存消耗。在一些示例应用程序中(例如,IPv4组播示例应用程序)中可以找到用于间接缓冲区的内存池初始化的示例(以及间接缓冲区的用例示例)。
struct rte_mempool  *header_pool, *clone_pool;
header_pool = rte_pktmbuf_pool_create("header_pool", NB_HDR_MBUF, 32,
		0, HDR_MBUF_DATA_SIZE, rte_socket_id());

clone_pool = rte_pktmbuf_pool_create("clone_pool", NB_CLONE_MBUF, 32,
		0, 0, rte_socket_id());
        上面的代码中header_pool中mbuf的data大小是HDR_MBUF_DATA_SIZE,而clone_pool中data的大小是0。data是存储数据包内容的。


7.8. Debug
        在调试模式下(使能了CONFIG_RTE_MBUF_DEBUG),mbuf库函数在任何操作之前执行完整性检查(例如,缓冲区损坏、未识别类型等)。
7.9. Use Cases
        所有的网络应用程序都应该使用mbufs来传输网络数据包。

猜你喜欢

转载自blog.csdn.net/shaoyunzhe/article/details/79036231