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 的硬件校验和卸载所需的步骤:
- 检查硬件是否支持硬件校验和卸载。
- 准备数据包 IP 和 UDP header 字段。
- 初始化校验和字段。在部分 UDP 校验和卸载支持的情况下,使用 IP pseudo-header.
的校验和初始化 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_len
和 l3_len
就足够了。
我在 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 的硬件校验和卸载所需的步骤:
- 检查硬件是否支持硬件校验和卸载。
- 准备数据包 IP 和 UDP header 字段。
- 初始化校验和字段。在部分 UDP 校验和卸载支持的情况下,使用 IP pseudo-header. 的校验和初始化 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.
根据@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_len
和 l3_len
就足够了。