Hardware checksum offload needs the pseudo-header checksum precomputed by
the driver. Compute the inner/outer pseudo-header checksums, including
walking IPv6 extension headers, in the Tx prepare path.

Signed-off-by: Jiacheng Ye <[email protected]>
---
 drivers/net/hinic3/hinic3_ethdev.c |   1 +
 drivers/net/hinic3/hinic3_ethdev.h |   1 +
 drivers/net/hinic3/hinic3_tx.c     | 179 ++++++++++++++++++++++++++++-
 drivers/net/hinic3/hinic3_tx.h     |  11 ++
 4 files changed, 186 insertions(+), 6 deletions(-)

diff --git a/drivers/net/hinic3/hinic3_ethdev.c 
b/drivers/net/hinic3/hinic3_ethdev.c
index ffbd080ede..0f7a653594 100644
--- a/drivers/net/hinic3/hinic3_ethdev.c
+++ b/drivers/net/hinic3/hinic3_ethdev.c
@@ -871,6 +871,7 @@ hinic3_dev_infos_get(struct rte_eth_dev *dev, struct 
rte_eth_dev_info *info)
                                RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
                                RTE_ETH_TX_OFFLOAD_SCTP_CKSUM |
                                RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |
+                               RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM |
                                RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO |
                                RTE_ETH_TX_OFFLOAD_TCP_TSO |
                                RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
diff --git a/drivers/net/hinic3/hinic3_ethdev.h 
b/drivers/net/hinic3/hinic3_ethdev.h
index 399c9c9342..1afd7cd77a 100644
--- a/drivers/net/hinic3/hinic3_ethdev.h
+++ b/drivers/net/hinic3/hinic3_ethdev.h
@@ -49,6 +49,7 @@
 #define HINIC3_PKT_TX_OUTER_IP_CKSUM   RTE_MBUF_F_TX_OUTER_IP_CKSUM
 #define HINIC3_PKT_TX_OUTER_UDP_CKSUM  RTE_MBUF_F_TX_OUTER_UDP_CKSUM
 #define HINIC3_PKT_TX_OUTER_IPV6       RTE_MBUF_F_TX_OUTER_IPV6
+#define HINIC3_PKT_TX_OUTER_IPV4       RTE_MBUF_F_TX_OUTER_IPV4
 #define HINIC3_PKT_RX_LRO              RTE_MBUF_F_RX_LRO
 #define HINIC3_PKT_TX_L4_NO_CKSUM      RTE_MBUF_F_TX_L4_NO_CKSUM
 
diff --git a/drivers/net/hinic3/hinic3_tx.c b/drivers/net/hinic3/hinic3_tx.c
index c3f053f869..bf79b1bd28 100644
--- a/drivers/net/hinic3/hinic3_tx.c
+++ b/drivers/net/hinic3/hinic3_tx.c
@@ -31,7 +31,7 @@
        (HINIC3_PKT_TX_IP_CKSUM | HINIC3_PKT_TX_TCP_CKSUM |   \
         HINIC3_PKT_TX_UDP_CKSUM | HINIC3_PKT_TX_SCTP_CKSUM | \
         HINIC3_PKT_TX_OUTER_IP_CKSUM | HINIC3_PKT_TX_OUTER_UDP_CKSUM | \
-        HINIC3_PKT_TX_TCP_SEG)
+        HINIC3_PKT_TX_TCP_SEG | HINIC3_PKT_TX_IPV6)
 
 static inline uint16_t
 hinic3_get_sq_free_wqebbs(struct hinic3_txq *sq)
@@ -366,6 +366,68 @@ hinic3_tx_done_cleanup(void *txq, uint32_t free_cnt)
        return hinic3_xmit_mbuf_cleanup(tx_queue, try_free_cnt);
 }
 
+static void hinic3_get_ipv6_len_proto(const void *hdr, uint16_t *hdr_len, 
uint8_t *proto)
+{
+       const struct hinic3_ipv6_ext_hdr *xh = NULL;
+       uint8_t i;
+
+       *proto = ((const struct rte_ipv6_hdr *)hdr)->proto;
+       *hdr_len = sizeof(struct rte_ipv6_hdr);
+
+       /*
+        * Consistent with ucode's IPV6_MAX_EXT_HDRS,
+        * maximum parsing up to IPV6_MAX_EXT_HDRS layer.
+        */
+       for (i = 0; i < IPV6_MAX_EXT_HDRS; i++) {
+               xh = (const struct hinic3_ipv6_ext_hdr *)((const uint8_t *)hdr 
+ *hdr_len);
+               switch (*proto) {
+               case IPPROTO_HOPOPTS:
+               case IPPROTO_ROUTING:
+               case IPPROTO_DSTOPTS:
+                       /* hdr len is a multiple of 8, excluding the first 8 
bytes */
+                       *hdr_len += FIXED_EXT_HDR_LEN + xh->len * UNIT_BYTES_U;
+                       *proto = xh->next_hdr;
+                       break;
+               case IPPROTO_AH:
+                       /* hdr len is a multiple of 4, excluding the first 8 
bytes */
+                       *hdr_len += FIXED_EXT_HDR_LEN + xh->len * UNIT_BYTES_AH;
+                       *proto = xh->next_hdr;
+                       break;
+               case IPPROTO_FRAGMENT:
+                       /* hdr len is fixed 8 bytes */
+                       *hdr_len += FIXED_EXT_HDR_LEN;
+                       *proto = xh->next_hdr;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static uint16_t
+hinic3_ipv6_phdr_cksum(const struct rte_ipv6_hdr *ipv6_hdr, uint64_t ol_flags)
+{
+       uint32_t   sum;
+       uint8_t    proto;
+       uint16_t   l3_len;
+       rte_be32_t l4_len;
+       rte_be32_t l4_proto;
+
+       hinic3_get_ipv6_len_proto((const void *)ipv6_hdr, &l3_len, &proto);
+       l4_proto = rte_cpu_to_be_16(proto);
+       if (ol_flags & HINIC3_PKT_TX_TCP_SEG)
+               l4_len = 0;
+       else
+               l4_len = 
rte_cpu_to_be_16(rte_be_to_cpu_16(ipv6_hdr->payload_len) -
+                                                          l3_len + 
sizeof(*ipv6_hdr));
+
+       sum = __rte_raw_cksum(ipv6_hdr->src_addr.a,
+                             sizeof(ipv6_hdr->src_addr.a) + 
sizeof(ipv6_hdr->dst_addr.a), 0);
+       sum = __rte_raw_cksum(&l4_len, sizeof(l4_len), sum);
+       sum = __rte_raw_cksum(&l4_proto, sizeof(l4_proto), sum);
+
+       return __rte_raw_cksum_reduce(sum);
+}
 /**
  * Prepare the data packet to be sent and calculate the internal L3 offset.
  *
@@ -378,6 +440,90 @@ hinic3_tx_done_cleanup(void *txq, uint32_t free_cnt)
  * @return
  * 0 as success, -EINVAL as failure.
  */
+static inline uint16_t
+hinic3_calculate_l4_cksum(struct rte_mbuf *mbuf, uint16_t inner_l3_offset)
+{
+       struct rte_ipv4_hdr *ipv4_hdr;
+       struct rte_ipv6_hdr *ipv6_hdr;
+       uint64_t ol_flags = mbuf->ol_flags;
+
+       if (ol_flags & HINIC3_PKT_TX_IPV4) {
+               ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *,
+                                                  inner_l3_offset);
+
+               if (ol_flags & HINIC3_PKT_TX_IP_CKSUM)
+                       ipv4_hdr->hdr_checksum = 0;
+
+               return rte_ipv4_phdr_cksum(ipv4_hdr, ol_flags);
+       }
+
+       ipv6_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv6_hdr *,
+                                          inner_l3_offset);
+
+       return hinic3_ipv6_phdr_cksum(ipv6_hdr, ol_flags);
+}
+
+static inline void hinic3_calculate_outer_udp_checksum(struct rte_mbuf *mbuf,
+                                                      uint16_t outer_l3_offset)
+{
+       struct rte_ipv4_hdr *ipv4_hdr;
+       struct rte_ipv6_hdr *ipv6_hdr;
+       struct rte_udp_hdr *udp_hdr;
+       uint64_t ol_flags = mbuf->ol_flags;
+
+       if (ol_flags & HINIC3_PKT_TX_OUTER_IPV4) {
+               ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr *, 
outer_l3_offset);
+               if (ol_flags & HINIC3_PKT_TX_OUTER_IP_CKSUM)
+                       ipv4_hdr->hdr_checksum = 0;
+               udp_hdr = (struct rte_udp_hdr *)((char *)ipv4_hdr + 
mbuf->outer_l3_len);
+               if (udp_hdr->dgram_cksum != 0x0)
+                       udp_hdr->dgram_cksum = rte_ipv4_phdr_cksum(ipv4_hdr, 
ol_flags);
+       } else if (ol_flags & HINIC3_PKT_TX_OUTER_IPV6) {
+               ipv6_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv6_hdr *, 
outer_l3_offset);
+               udp_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_udp_hdr *,
+                                                 (outer_l3_offset + 
mbuf->outer_l3_len));
+               udp_hdr->dgram_cksum = hinic3_ipv6_phdr_cksum(ipv6_hdr, 
ol_flags);
+       }
+}
+
+static inline void hinic3_calculate_outer_phdr_checksum(struct rte_mbuf *mbuf,
+                                                       uint16_t 
outer_l3_offset)
+{
+       uint64_t ol_flags = mbuf->ol_flags;
+
+       if (ol_flags & HINIC3_PKT_TX_OUTER_UDP_CKSUM)
+               hinic3_calculate_outer_udp_checksum(mbuf, outer_l3_offset);
+}
+
+static inline void hinic3_calculate_phdr_checksum(struct rte_mbuf *mbuf,
+                                                 uint16_t inner_l3_offset)
+{
+       uint64_t ol_flags = mbuf->ol_flags;
+       struct rte_tcp_hdr *tcp_hdr;
+       struct rte_udp_hdr *udp_hdr;
+       uint16_t l4_offset = inner_l3_offset + mbuf->l3_len;
+
+       switch (ol_flags & HINIC3_PKT_TX_L4_MASK) {
+       case HINIC3_PKT_TX_UDP_CKSUM:
+               udp_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_udp_hdr *, 
l4_offset);
+               udp_hdr->dgram_cksum = hinic3_calculate_l4_cksum(mbuf, 
inner_l3_offset);
+               break;
+       case HINIC3_PKT_TX_TCP_CKSUM:
+               tcp_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_tcp_hdr *, 
l4_offset);
+               tcp_hdr->cksum = hinic3_calculate_l4_cksum(mbuf, 
inner_l3_offset);
+               break;
+       case HINIC3_PKT_TX_SCTP_CKSUM:
+               /* Sctp csum no need to calculate pseudo-header */
+               break;
+       default:
+               if (ol_flags & HINIC3_PKT_TX_TCP_SEG) {
+                       tcp_hdr = rte_pktmbuf_mtod_offset(mbuf, struct 
rte_tcp_hdr *, l4_offset);
+                       tcp_hdr->cksum = hinic3_calculate_l4_cksum(mbuf, 
inner_l3_offset);
+               }
+               break;
+       }
+}
+
 static int
 hinic3_tx_offload_pkt_prepare(struct hinic3_nic_dev *nic_dev, struct rte_mbuf 
*mbuf,
                              uint16_t *inner_l3_offset)
@@ -389,7 +535,7 @@ hinic3_tx_offload_pkt_prepare(struct hinic3_nic_dev 
*nic_dev, struct rte_mbuf *m
                                HINIC3_SUPPORT_VXLAN_OFFLOAD(nic_dev)) ||
                      ((ol_flags & HINIC3_PKT_TX_TUNNEL_GENEVE) &&
                                HINIC3_SUPPORT_GENEVE_OFFLOAD(nic_dev)) ||
-                     ((ol_flags & HINIC3_PKT_TX_TUNNEL_IPIP) &&
+                     (((ol_flags & HINIC3_PKT_TX_TUNNEL_IPIP) == 
HINIC3_PKT_TX_TUNNEL_IPIP) &&
                                HINIC3_SUPPORT_IPXIP_OFFLOAD(nic_dev))))
                        return -EINVAL;
        }
@@ -400,15 +546,33 @@ hinic3_tx_offload_pkt_prepare(struct hinic3_nic_dev 
*nic_dev, struct rte_mbuf *m
 #endif
        /* Support tunnel. */
        if ((ol_flags & HINIC3_PKT_TX_TUNNEL_MASK)) {
-               if ((ol_flags & HINIC3_PKT_TX_OUTER_IP_CKSUM) ||
-                   (ol_flags & HINIC3_PKT_TX_OUTER_IPV6) ||
-                   (ol_flags & HINIC3_PKT_TX_TCP_SEG)) {
+               /*
+                * For IPv6-in-IPv6 packets, ol_flags cannot be used to 
determine
+                * whether the delivered port offload value contains UDP and IP
+                * hardware offload flags, so it is impossible to determine how
+                * l2_len is populated. Here, the property that l2_len is 0 for
+                * IP-in-IP is used to distinguish population modes. If
+                * mbuf->l2_len == 0, the upper layer has enabled UDP and IP
+                * hardware offload flags; the inner L3 offset is then
+                * outer_l2_len + outer_l3_len.
+                */
+               if ((ol_flags & HINIC3_PKT_TX_TUNNEL_IPIP) == 
HINIC3_PKT_TX_TUNNEL_IPIP) {
+                       if (mbuf->l2_len == 0)
+                               *inner_l3_offset = mbuf->outer_l2_len + 
mbuf->outer_l3_len;
+                       else
+                               *inner_l3_offset = mbuf->l2_len;
+               } else if ((ol_flags & HINIC3_PKT_TX_OUTER_IP_CKSUM) ||
+                         (ol_flags & HINIC3_PKT_TX_OUTER_UDP_CKSUM) ||
+                         (ol_flags & HINIC3_PKT_TX_TCP_SEG)) {
                        /*
                         * For this senmatic, l2_len of mbuf means
-                        * len(out_udp + vxlan + in_eth).
+                        * len(out_udp + vxlan/geneve + in_eth).
                         */
                        *inner_l3_offset = mbuf->l2_len + mbuf->outer_l2_len +
                                           mbuf->outer_l3_len;
+                       if (!HINIC3_IS_SP230_NIC(nic_dev))
+                               /* Process the outer UDP pseudo-header checksum 
*/
+                               hinic3_calculate_outer_phdr_checksum(mbuf, 
mbuf->outer_l2_len);
                } else {
                        /*
                         * For this senmatic, l2_len of mbuf means
@@ -420,6 +584,9 @@ hinic3_tx_offload_pkt_prepare(struct hinic3_nic_dev 
*nic_dev, struct rte_mbuf *m
                /* For non-tunnel type pkts. */
                *inner_l3_offset = mbuf->l2_len;
        }
+       if (!HINIC3_IS_SP230_NIC(nic_dev))
+               /* Process the pseudo-header checksum */
+               hinic3_calculate_phdr_checksum(mbuf, *inner_l3_offset);
 
        return 0;
 }
diff --git a/drivers/net/hinic3/hinic3_tx.h b/drivers/net/hinic3/hinic3_tx.h
index 10ea174ea2..2fafb400b5 100644
--- a/drivers/net/hinic3/hinic3_tx.h
+++ b/drivers/net/hinic3/hinic3_tx.h
@@ -408,6 +408,17 @@ struct __rte_cache_aligned hinic3_txq {
        uint64_t prof_tx_end_tsc;
 #endif
 };
+#define IPV4_VERSION 4
+#define IPV6_VERSION 6
+#define FIXED_EXT_HDR_LEN  8
+#define UNIT_BYTES_U  8
+#define UNIT_BYTES_AH  4
+#define IPV6_MAX_EXT_HDRS 9
+
+struct hinic3_ipv6_ext_hdr {
+       uint8_t next_hdr;
+       uint8_t len;
+};
 
 void hinic3_flush_txqs(struct hinic3_nic_dev *nic_dev);
 void hinic3_free_txq_mbufs(struct hinic3_txq *txq);
-- 
2.33.0


Reply via email to