i40e: Add support for MPLS + TSO
authorJoe Damato <jdamato@fastly.com>
Thu, 3 Mar 2022 01:29:07 +0000 (17:29 -0800)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Tue, 12 Apr 2022 16:42:05 +0000 (09:42 -0700)
This change adds support for TSO of MPLS packets.

In my tests with tcpdump it seems to work. Note this test setup has
a 9000 byte MTU:

MPLS (label 100, exp 0, [S], ttl 64) IP srcip.50086 > dstip.1234:
  Flags [P.], seq 593345:644401, ack 0, win 420,
  options [nop,nop,TS val 45022534 ecr 1722291395], length 51056

IP dstip.1234 > srcip.50086: Flags [.], ack 593345, win 122,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

IP dstip.1234 > srcip.50086: Flags [.], ack 602289, win 105,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

IP dstip.1234 > srcip.50086: Flags [.], ack 620177, win 71,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

MPLS (label 100, exp 0, [S], ttl 64) IP srcip.50086 > dstip.1234:
  Flags [P.], seq 644401:655953, ack 0, win 420,
  options [nop,nop,TS val 45022534 ecr 1722291395], length 11552

IP dstip.1234 > srcip.50086: Flags [.], ack 638065, win 37,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

IP dstip.1234 > srcip.50086: Flags [.], ack 644401, win 25,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

IP dstip.1234 > srcip.50086: Flags [.], ack 653345, win 8,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

IP dstip.1234 > srcip.50086: Flags [.], ack 655953, win 3,
  options [nop,nop,TS val 1722291395 ecr 45022534], length 0

Signed-off-by: Joe Damato <jdamato@fastly.com>
Co-developed-by: Mike Gallo <mgallo@fastly.com>
Signed-off-by: Mike Gallo <mgallo@fastly.com>
Tested-by: Gurucharan <gurucharanx.g@intel.com> (A Contingent worker at Intel)
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c

index 6778df2..b7f11fd 100644 (file)
@@ -13436,8 +13436,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
        np->vsi = vsi;
 
        hw_enc_features = NETIF_F_SG                    |
-                         NETIF_F_IP_CSUM               |
-                         NETIF_F_IPV6_CSUM             |
+                         NETIF_F_HW_CSUM               |
                          NETIF_F_HIGHDMA               |
                          NETIF_F_SOFT_FEATURES         |
                          NETIF_F_TSO                   |
@@ -13468,6 +13467,23 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
        /* record features VLANs can make use of */
        netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID;
 
+#define I40E_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE |           \
+                                  NETIF_F_GSO_GRE_CSUM |       \
+                                  NETIF_F_GSO_IPXIP4 |         \
+                                  NETIF_F_GSO_IPXIP6 |         \
+                                  NETIF_F_GSO_UDP_TUNNEL |     \
+                                  NETIF_F_GSO_UDP_TUNNEL_CSUM)
+
+       netdev->gso_partial_features = I40E_GSO_PARTIAL_FEATURES;
+       netdev->features |= NETIF_F_GSO_PARTIAL |
+                           I40E_GSO_PARTIAL_FEATURES;
+
+       netdev->mpls_features |= NETIF_F_SG;
+       netdev->mpls_features |= NETIF_F_HW_CSUM;
+       netdev->mpls_features |= NETIF_F_TSO;
+       netdev->mpls_features |= NETIF_F_TSO6;
+       netdev->mpls_features |= I40E_GSO_PARTIAL_FEATURES;
+
        /* enable macvlan offloads */
        netdev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD;
 
index 0eae585..8b844ad 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/prefetch.h>
 #include <linux/bpf_trace.h>
+#include <net/mpls.h>
 #include <net/xdp.h>
 #include "i40e.h"
 #include "i40e_trace.h"
@@ -3015,6 +3016,7 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len,
 {
        struct sk_buff *skb = first->skb;
        u64 cd_cmd, cd_tso_len, cd_mss;
+       __be16 protocol;
        union {
                struct iphdr *v4;
                struct ipv6hdr *v6;
@@ -3026,7 +3028,7 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len,
                unsigned char *hdr;
        } l4;
        u32 paylen, l4_offset;
-       u16 gso_segs, gso_size;
+       u16 gso_size;
        int err;
 
        if (skb->ip_summed != CHECKSUM_PARTIAL)
@@ -3039,15 +3041,23 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len,
        if (err < 0)
                return err;
 
-       ip.hdr = skb_network_header(skb);
-       l4.hdr = skb_transport_header(skb);
+       protocol = vlan_get_protocol(skb);
+
+       if (eth_p_mpls(protocol))
+               ip.hdr = skb_inner_network_header(skb);
+       else
+               ip.hdr = skb_network_header(skb);
+       l4.hdr = skb_checksum_start(skb);
 
        /* initialize outer IP header fields */
        if (ip.v4->version == 4) {
                ip.v4->tot_len = 0;
                ip.v4->check = 0;
+
+               first->tx_flags |= I40E_TX_FLAGS_TSO;
        } else {
                ip.v6->payload_len = 0;
+               first->tx_flags |= I40E_TX_FLAGS_TSO;
        }
 
        if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE |
@@ -3100,10 +3110,9 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len,
 
        /* pull values out of skb_shinfo */
        gso_size = skb_shinfo(skb)->gso_size;
-       gso_segs = skb_shinfo(skb)->gso_segs;
 
        /* update GSO size and bytecount with header size */
-       first->gso_segs = gso_segs;
+       first->gso_segs = skb_shinfo(skb)->gso_segs;
        first->bytecount += (first->gso_segs - 1) * *hdr_len;
 
        /* find the field values */
@@ -3187,13 +3196,27 @@ static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
        unsigned char *exthdr;
        u32 offset, cmd = 0;
        __be16 frag_off;
+       __be16 protocol;
        u8 l4_proto = 0;
 
        if (skb->ip_summed != CHECKSUM_PARTIAL)
                return 0;
 
-       ip.hdr = skb_network_header(skb);
-       l4.hdr = skb_transport_header(skb);
+       protocol = vlan_get_protocol(skb);
+
+       if (eth_p_mpls(protocol))
+               ip.hdr = skb_inner_network_header(skb);
+       else
+               ip.hdr = skb_network_header(skb);
+       l4.hdr = skb_checksum_start(skb);
+
+       /* set the tx_flags to indicate the IP protocol type. this is
+        * required so that checksum header computation below is accurate.
+        */
+       if (ip.v4->version == 4)
+               *tx_flags |= I40E_TX_FLAGS_IPV4;
+       else
+               *tx_flags |= I40E_TX_FLAGS_IPV6;
 
        /* compute outer L2 header size */
        offset = ((ip.hdr - skb->data) / 2) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT;
@@ -3749,7 +3772,6 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        struct i40e_tx_buffer *first;
        u32 td_offset = 0;
        u32 tx_flags = 0;
-       __be16 protocol;
        u32 td_cmd = 0;
        u8 hdr_len = 0;
        int tso, count;
@@ -3791,15 +3813,6 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        if (i40e_tx_prepare_vlan_flags(skb, tx_ring, &tx_flags))
                goto out_drop;
 
-       /* obtain protocol of skb */
-       protocol = vlan_get_protocol(skb);
-
-       /* setup IPv4/IPv6 offloads */
-       if (protocol == htons(ETH_P_IP))
-               tx_flags |= I40E_TX_FLAGS_IPV4;
-       else if (protocol == htons(ETH_P_IPV6))
-               tx_flags |= I40E_TX_FLAGS_IPV6;
-
        tso = i40e_tso(first, &hdr_len, &cd_type_cmd_tso_mss);
 
        if (tso < 0)