net/sfc: add 8000-series EF10 hardware TSO bug workaround
Checks
Commit Message
Innermost IP length and outer UDP datagram length must be
greater than or equal to the corresponding values derived
from the MSS; otherwise, the checksum offloads will break.
Fixes: c1ce2ba218f8 ("net/sfc: support tunnel TSO on EF10 native Tx datapath")
Fixes: 6bc985e41155 ("net/sfc: support TSO in EF10 Tx datapath")
Fixes: fec33d5bb3eb ("net/sfc: support firmware-assisted TSO")
Cc: stable@dpdk.org
Signed-off-by: Ivan Malov <ivan.malov@oktetlabs.ru>
Reviewed-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
---
drivers/net/sfc/sfc_ef10_tx.c | 19 +++++++++++++++++++
drivers/net/sfc/sfc_tso.c | 7 +++++++
drivers/net/sfc/sfc_tso.h | 30 ++++++++++++++++++++++++++++++
3 files changed, 56 insertions(+)
Comments
On 2/2/2021 3:23 PM, Ivan Malov wrote:
> Innermost IP length and outer UDP datagram length must be
> greater than or equal to the corresponding values derived
> from the MSS; otherwise, the checksum offloads will break.
>
> Fixes: c1ce2ba218f8 ("net/sfc: support tunnel TSO on EF10 native Tx datapath")
> Fixes: 6bc985e41155 ("net/sfc: support TSO in EF10 Tx datapath")
> Fixes: fec33d5bb3eb ("net/sfc: support firmware-assisted TSO")
> Cc: stable@dpdk.org
>
> Signed-off-by: Ivan Malov <ivan.malov@oktetlabs.ru>
> Reviewed-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Reviewed-by: Andy Moreton <amoreton@xilinx.com>
Applied to dpdk-next-net/main, thanks.
@@ -481,6 +481,25 @@ sfc_ef10_xmit_tso_pkt(struct sfc_ef10_txq * const txq, struct rte_mbuf *m_seg,
needed_desc--;
}
+ /*
+ * 8000-series EF10 hardware requires that innermost IP length
+ * be greater than or equal to the value which each segment is
+ * supposed to have; otherwise, TCP checksum will be incorrect.
+ *
+ * The same concern applies to outer UDP datagram length field.
+ */
+ switch (m_seg->ol_flags & PKT_TX_TUNNEL_MASK) {
+ case PKT_TX_TUNNEL_VXLAN:
+ /* FALLTHROUGH */
+ case PKT_TX_TUNNEL_GENEVE:
+ sfc_tso_outer_udp_fix_len(first_m_seg, hdr_addr);
+ break;
+ default:
+ break;
+ }
+
+ sfc_tso_innermost_ip_fix_len(first_m_seg, hdr_addr, iph_off);
+
/*
* Tx prepare has debug-only checks that offload flags are correctly
* filled in in TSO mbuf. Use zero IPID if there is no IPv4 flag.
@@ -140,6 +140,13 @@ sfc_efx_tso_do(struct sfc_efx_txq *txq, unsigned int idx,
tsoh = rte_pktmbuf_mtod(m, uint8_t *);
}
+ /*
+ * 8000-series EF10 hardware requires that innermost IP length
+ * be greater than or equal to the value which each segment is
+ * supposed to have; otherwise, TCP checksum will be incorrect.
+ */
+ sfc_tso_innermost_ip_fix_len(m, tsoh, nh_off);
+
/*
* Handle IP header. Tx prepare has debug-only checks that offload flags
* are correctly filled in in TSO mbuf. Use zero IPID if there is no
@@ -38,6 +38,36 @@ sfc_tso_ip4_get_ipid(const uint8_t *pkt_hdrp, size_t ip_hdr_off)
return rte_be_to_cpu_16(ipid);
}
+static inline void
+sfc_tso_outer_udp_fix_len(const struct rte_mbuf *m, uint8_t *tsoh)
+{
+ rte_be16_t len = rte_cpu_to_be_16(m->l2_len + m->l3_len + m->l4_len +
+ m->tso_segsz);
+
+ rte_memcpy(tsoh + m->outer_l2_len + m->outer_l3_len +
+ offsetof(struct rte_udp_hdr, dgram_len),
+ &len, sizeof(len));
+}
+
+static inline void
+sfc_tso_innermost_ip_fix_len(const struct rte_mbuf *m, uint8_t *tsoh,
+ size_t iph_ofst)
+{
+ size_t ip_payload_len = m->l4_len + m->tso_segsz;
+ size_t field_ofst;
+ rte_be16_t len;
+
+ if (m->ol_flags & PKT_TX_IPV4) {
+ field_ofst = offsetof(struct rte_ipv4_hdr, total_length);
+ len = rte_cpu_to_be_16(m->l3_len + ip_payload_len);
+ } else {
+ field_ofst = offsetof(struct rte_ipv6_hdr, payload_len);
+ len = rte_cpu_to_be_16(ip_payload_len);
+ }
+
+ rte_memcpy(tsoh + iph_ofst + field_ofst, &len, sizeof(len));
+}
+
unsigned int sfc_tso_prepare_header(uint8_t *tsoh, size_t header_len,
struct rte_mbuf **in_seg, size_t *in_off);