DPDK:UDP 校验和的硬件卸载计算不起作用

DPDK: HW offloaded calculation of UDP checksum not working

我在 Linux 上使用 Intel X722 NIC 使用 DPDK 18.11.8 稳定版。

如果我在软件中计算 IP 和 UDP 校验和,我的应用程序工作正常,但如果我在硬件中计算,我会遇到分段错误。这是我的代码:

local_port_conf.txmode.offloads  = local_port_conf.txmode.offloads | DEV_TX_OFFLOAD_IPV4_CKSUM  | DEV_TX_OFFLOAD_UDP_CKSUM;
mb->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_UDP_CKSUM; 
mb->l2_len = sizeof(struct ether_hdr);
mb->l3_len = sizeof(struct ipv4_hdr);
mb->l4_len = sizeof(struct udp_hdr);        
p_ip_hdr->hdr_checksum = 0;
p_udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum((const ipv4_hdr*)(mb->l3_len), mb->ol_flags);

rte_ipv4_phdr_cksum() 调用很神秘,我是否理解正确操作?

可以理解,C++编译器给出警告:

warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
         p_udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum((const ipv4_hdr*)(ptMbuf->l3_len), ptMbuf->ol_flags);
                                                                                      ^

我的代码有什么问题?

以下是 DPDK 中 IP 和 UDP 的硬件校验和卸载所需的步骤:

  1. 检查硬件是否支持硬件校验和卸载。
  2. 准备数据包 IP 和 UDP header 字段。
  3. 初始化校验和字段。在部分 UDP 校验和卸载支持的情况下,使用 IP pseudo-header.
  4. 的校验和初始化 UDP 校验和

注意:Intel NIC X553、X710 和 X722(可能还有其他)仅支持部分 UDP 校验和卸载,

[根据@maxschlepzig 评论中的查询进行编辑] 是否有支持完全 UDP 校验和卸载的 NIC?如果是,是否有一些 DPDK 方法来检查完全支持?

[Answer] 是的,有一个简单的方法可以检查。在NIC features TABLE 1.1 highlights if checksum offload is available, partial or absent. But the real catch is to check release notes for NIC specific firmware中作为HW ASIC或固定功能的启用或禁用相同。

需要这样的代码段:

/* during port configuration */
txmode = {
    .offloads = DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM
};

/* during device configuration */
if (!(dev_info.tx_offload_capa & DEV_TX_OFFLOAD_UDP_CKSUM) || !(dev_info.tx_offload_capa & DEV_TX_OFFLOAD_IPV4_CKSUM)) {
    rte_panic(" offload not supported");
}

/* mb is mbuf packet to transmit */
mb->ol_flags = PKT_TX_IPV4 | PKT_TX_IP_CKSUM | PKT_TX_UDP_CKSUM;
mb->l2_len = sizeof(struct ether_hdr);
mb->l3_len = sizeof(struct ipv4_hdr);      

struct rte_ether_hdr *eth_hdr = rte_pktmbuf_mtod(mb, struct rte_ether_hdr *);
struct ipv4_hdr *p_ipv4_hdr = (struct ipv4_hdr*) ((char *)eth_hdr + sizeof(struct ether_hdr));
struct udp_hdr *p_udp_hdr = (struct udp_hdr *)((unsigned char *) + sizeof(struct ipv4_hdr));

/* update v4 header fields with version, length, ttl, ip and others */
/* update udp headers */

/* in case hardware offload is unavailable */ 
p_udp_hdr->dgram_cksum = rte_ipv4_udptcp_cksum(p_ip_hdr, p_udp_hdr);
p_ip_hdr->hdr_checksum = rte_ipv4_cksum(p_ip_hdr)

/* otherwise, equivalent offloaded checksum computation */ 
p_udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum(p_ipv4_hdr, ol_flags);
p_ip_hdr->hdr_checksum = 0; 

[EDIT-2] 感谢@maxschlepzig 指出 l4_len 不需要 UDP 校验和卸载。这是正确的信息 l4_len 只需要 TX.

的 TCP 段卸载

根据@DavidA 的实时调试,段错误原因被确定为不正确的使用,在评论中,代码更新为

@VipinVarghese Thanks so I now have: struct ipv4_hdr* p_ipv4_hdr = rte_pktmbuf_mtod(mb, struct ipv4_hdr *) + mb->l2_len; p_udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum(p_ipv4_hdr, mb->ol_flags); but the udp checksum is still incorrect.

Hi David, I believe you have not st the right values. Will update in couple of hours – Vipin Varghese

与 David 分享的行动项目之一是更新伪代码,因为真正的问题围绕着 DPDk: HW offloaded calculation of UDP checksum not working。谢谢,@maxschlepzig 解释了这一切,因为许多人很容易错过评论和实时调试

函数rte_ipv4_phdr_cksum()记录为:

/**
 * Process the pseudo-header checksum of an IPv4 header.
 *
 * The checksum field must be set to 0 by the caller.
 *
 * Depending on the ol_flags, the pseudo-header checksum expected by the
 * drivers is not the same. For instance, when TSO is enabled, the IP
 * payload length must not be included in the packet.
 *
 * When ol_flags is 0, it computes the standard pseudo-header checksum.
 *
 * @param ipv4_hdr
 *   The pointer to the contiguous IPv4 header.
 * @param ol_flags
 *   The ol_flags of the associated mbuf.
 * @return
 *   The non-complemented checksum to set in the L4 header.
 */
static inline uint16_t
rte_ipv4_phdr_cksum(const struct rte_ipv4_hdr *ipv4_hdr, uint64_t ol_flags)

所以当你像这样投射时

p_udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum((const ipv4_hdr*)(mb->l3_len),
        mb->ol_flags);

ipv4_hdr 参数包含一个无意义的地址。因此,这行不通。这是您的分段错误的根本原因(-> 取消引用无效地址)。

为了计算 checksum of the pseudo-header 你必须提供你的原始 IP header,因此,只需这样称呼它:

p_ip_hdr->hdr_checksum = 0;
p_udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum(p_ip_hdr, mb->ol_flags);

然后 rte_ipv4_phdr_cksum() 在内部构建一个临时伪 header(基于提供的 p_ip_hdr)以计算其校验和。

注意:您不必为 UDP 校验和卸载分配给 mb->l4_len。设置 l2_lenl3_len 就足够了。