DPDK19.11.10:带有 VLAN 标记的 IPV4 的硬件卸载工作不正常

DPDK19.11.10: HW offload for IPV4 with VLAN tag is not working properly

我在centos上用的是DPDK19.11.10

如果我只发送 IPV4 数据包而不发送 VLAN header.

,则应用程序可以正常使用硬件卸载

如果我将 VLAN header 添加到 IPV4,硬件卸载将不起作用。 如果在 ubuntu 网关上捕获 pcap,即使我们没有对 IP 数据包进行分段,IP header 也会被 Fragmented IP packet 损坏。

我们验证了这样的能力:

if (!(dev->tx_offload_capa & DEV_TX_OFFLOAD_VLAN_INSERT)) {
          rte_panic(" VLAN offload not supported");
}

下面是我的代码:

.offloads = (DEV_TX_OFFLOAD_IPV4_CKSUM |
                              DEV_TX_OFFLOAD_UDP_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM | DEV_TX_OFFLOAD_VLAN_INSERT),
m->l2_len = L2_HDR_SIZE;
m->l3_len = L3_IPV4_HDR_SIZE;
ip_hdr->check       = 0;
m->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM;
ip_hdr = rte_pktmbuf_mtod(m, struct iphdr *);
vlan1_hdr = (struct vlan1_hdr *) rte_pktmbuf_prepend(m, sizeof(struct vlan1_hdr));
eth_hdr = (struct ethernet_hdr *) rte_pktmbuf_prepend(m, (uint16_t)sizeof(struct ethernet_hdr));

一旦我在 ubuntu 网关中收到数据包,IP 数据包就会被损坏为碎片化的 IP 数据包。

如果我删除 VLAN header,相同的代码可以正常工作。 还有什么需要补充的吗?

听起来,

  1. 您可能误解了 HW Tx VLAN 卸载的工作方式;
  2. 您的代码在插入 VLAN header 时不会更新 m->l2_len

首先,您的代码启用了对 HW Tx VLAN 卸载的支持,但奇怪的是,它实际上并没有尝试使用它。如果要使用硬件 Tx VLAN卸载,他们应该在m->ol_flags中设置PKT_TX_VLAN并填写m->vlan_tci。 VLAN header 将由 硬件 添加。

但是,您的代码将 header 本身放在前面,就像一开始就没有使用 硬件 卸载的意图一样。您的代码确实 m->l2_len = L2_HDR_SIZE;,正如我推测的那样,它仅对以太网 header 有效。当您的代码前置 VLAN header 时,必须相应地更新此变量:

m->l2_len += sizeof(struct rte_vlan_hdr);

大多数 DPDK NIC PMD 支持 HW VLAN 卸载(RX 方向)。但有限数量的 PMD 支持 DEV_TX_OFFLOAD_VLAN_INSERT 功能即

  1. 大西洋水族馆
  2. Marvell OCTEON CN9K/CN10K SoC
  3. Cisco VIC 适配器
  4. Pensando 网卡
  5. OCTEON TX2
  6. 网迅10G以太网网卡和
  7. 英特尔网卡 - i40e、ice、iavf、ixgbe、igb

要启用 HW VLAN INSERT,需要检查

  1. if DEV_TX_OFFLOAD_VLAN_INSERT 通过检查 get_dev_info
  2. 为 DEV_TX_OFFLOAD_VLAN_INSERT
  3. 端口配置 tx 卸载
  4. 使用 ol_flags = PKT_TX_VLANvlan_tci = [desired TCI in big-endian format]
  5. 启用 MBUF 描述符

这将允许 xmit 函数中的驱动程序代码检查 PKT_TX_VLAN 的 mbuf 描述符 ol_flags 并通过在之前向数据包描述符注册适当的命令来启用 VLAN 插入卸载到硬件DMA.

来自DPDK的条件要满足

  1. 在给定的实例中,应该只有 1 个线程访问和更新 mbuf。
  2. 不对 mbuf 进行任何修改(带有有效负载)。

如果打算通过 SW 执行 VLAN 插入(特别是如果 HW 或虚拟 NIC PMD 不支持),在 dpdk 中必须执行以下操作

  • 确保 refcnt 为 1 以防止多线程访问和修改预期缓冲区。
  • 有足够的余量将数据包移动 4 个字节以适应以太网类型和 VLAN 值。
  • 确保 pkt_len 和 data_len 在边界内(大于 60 字节且小于 4 字节的 MTU)
  • 未为 PKT_TX_VLAN
  • 启用 MBUF 卸载描述符
  • 在修改后的 MBUF 上更新 data_len 4 字节。
  • 将总数 pkt_len 更新为 4。
  • (出于性能考虑可选)预取mbuf内存地址的mtod之前的4个字节

注意:以上所有事情都可以通过使用DPDK函数rte_vlan_insert轻松实现。要使用相同的方法,请按照

的步骤操作
  • 不要配置端口DEV_TX_OFFLOAD_VLAN_INSERT。

  • 更新 ol_flags 为 PKT_TX_VLAN 和 vlan_tci 所需值。

  • 使用 mbuf

    调用 rte_vlan_insert

示例代码:

/* Get burst of RX packets, from first port of pair. */
struct rte_mbuf *bufs[BURST_SIZE];
const uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs, BURST_SIZE);

if (unlikely(nb_rx == 0))
        continue;

for (int i = 0; i < nb_rx; i++) {
        bufs[i]->ol_flags = PKT_TX_VLAN;
        bufs[i]->vlan_tci = 0x10;

        rte_vlan_insert(&bufs[i]);
}

/* Send burst of TX packets, to second port of pair. */
const uint16_t nb_tx = rte_eth_tx_burst(port, 0,
                bufs, nb_rx);

/* Free any unsent packets. */
if (unlikely(nb_tx < nb_rx)) {
        uint16_t buf;
        for (buf = nb_tx; buf < nb_rx; buf++)
                rte_pktmbuf_free(bufs[buf]);
}