sfc: implement encap TSO on EF100
authorEdward Cree <ecree@solarflare.com>
Wed, 28 Oct 2020 20:43:39 +0000 (20:43 +0000)
committerJakub Kicinski <kuba@kernel.org>
Sat, 31 Oct 2020 00:42:53 +0000 (17:42 -0700)
The NIC only needs to know where the headers it has to edit (TCP and
 inner and outer IPv4) are, which fits GSO_PARTIAL nicely.
It also supports non-PARTIAL offload of UDP tunnels, again just
 needing to be told the outer transport offset so that it can edit
 the UDP length field.
(It's not clear to me whether the stack will ever use the non-PARTIAL
 version with the netdev feature flags we're setting here.)

Signed-off-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/sfc/ef100_nic.c
drivers/net/ethernet/sfc/ef100_tx.c

index 3148fe7703564561fd5d29fcd44f04ba3b8a4373..bf92cdc60cda7909d9ee02aebdf53c2bc00abddb 100644 (file)
@@ -182,8 +182,16 @@ static int efx_ef100_init_datapath_caps(struct efx_nic *efx)
        if (rc)
                return rc;
 
-       if (efx_ef100_has_cap(nic_data->datapath_caps2, TX_TSO_V3))
-               efx->net_dev->features |= NETIF_F_TSO | NETIF_F_TSO6;
+       if (efx_ef100_has_cap(nic_data->datapath_caps2, TX_TSO_V3)) {
+               struct net_device *net_dev = efx->net_dev;
+               netdev_features_t tso = NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_PARTIAL |
+                                       NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;
+
+               net_dev->features |= tso;
+               net_dev->hw_features |= tso;
+               net_dev->hw_enc_features |= tso;
+               net_dev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;
+       }
        efx->num_mac_stats = MCDI_WORD(outbuf,
                                       GET_CAPABILITIES_V4_OUT_MAC_STATS_NUM_STATS);
        netif_dbg(efx, probe, efx->net_dev,
@@ -1101,6 +1109,7 @@ static int ef100_probe_main(struct efx_nic *efx)
        nic_data->efx = efx;
        net_dev->features |= efx->type->offload_features;
        net_dev->hw_features |= efx->type->offload_features;
+       net_dev->hw_enc_features |= efx->type->offload_features;
 
        /* Populate design-parameter defaults */
        nic_data->tso_max_hdr_len = ESE_EF100_DP_GZ_TSO_MAX_HDR_LEN_DEFAULT;
index a90e5a9d2a37a2845711b7bc4534603a3779295b..d267b12bdaa08756d9e7af996b9c5843604121af 100644 (file)
@@ -54,8 +54,6 @@ static bool ef100_tx_can_tso(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
        struct efx_nic *efx = tx_queue->efx;
        struct ef100_nic_data *nic_data;
        struct efx_tx_buffer *buffer;
-       struct tcphdr *tcphdr;
-       struct iphdr *iphdr;
        size_t header_len;
        u32 mss;
 
@@ -98,20 +96,6 @@ static bool ef100_tx_can_tso(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
        buffer->unmap_len = 0;
        buffer->skb = skb;
        ++tx_queue->insert_count;
-
-       /* Adjust the TCP checksum to exclude the total length, since we set
-        * ED_INNER_IP_LEN in the descriptor.
-        */
-       tcphdr = tcp_hdr(skb);
-       if (skb_is_gso_v6(skb)) {
-               tcphdr->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                                &ipv6_hdr(skb)->daddr,
-                                                0, IPPROTO_TCP, 0);
-       } else {
-               iphdr = ip_hdr(skb);
-               tcphdr->check = ~csum_tcpudp_magic(iphdr->saddr, iphdr->daddr,
-                                                  0, IPPROTO_TCP, 0);
-       }
        return true;
 }
 
@@ -209,17 +193,35 @@ static void ef100_make_tso_desc(struct efx_nic *efx,
                ESE_GZ_TX_DESC_IP4_ID_INC_MOD16;
        u16 vlan_enable =  efx->net_dev->features & NETIF_F_HW_VLAN_CTAG_TX ?
                skb_vlan_tag_present(skb) : 0;
+       bool gso_partial = skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL;
        unsigned int len, ip_offset, tcp_offset, payload_segs;
+       unsigned int outer_ip_offset, outer_l4_offset;
        u16 vlan_tci = skb_vlan_tag_get(skb);
        u32 mss = skb_shinfo(skb)->gso_size;
+       bool encap = skb->encapsulation;
+       struct tcphdr *tcp;
+       u32 paylen;
 
        len = skb->len - buffer->len;
        /* We use 1 for the TSO descriptor and 1 for the header */
        payload_segs = segment_count - 2;
-       ip_offset =  skb_network_offset(skb);
-       tcp_offset = skb_transport_offset(skb);
+       if (encap) {
+               outer_ip_offset = skb_network_offset(skb);
+               outer_l4_offset = skb_transport_offset(skb);
+               ip_offset = skb_inner_network_offset(skb);
+               tcp_offset = skb_inner_transport_offset(skb);
+       } else {
+               ip_offset =  skb_network_offset(skb);
+               tcp_offset = skb_transport_offset(skb);
+               outer_ip_offset = outer_l4_offset = 0;
+       }
+
+       /* subtract TCP payload length from inner checksum */
+       tcp = (void *)skb->data + tcp_offset;
+       paylen = skb->len - tcp_offset;
+       csum_replace_by_diff(&tcp->check, (__force __wsum)htonl(paylen));
 
-       EFX_POPULATE_OWORD_13(*txd,
+       EFX_POPULATE_OWORD_17(*txd,
                              ESF_GZ_TX_DESC_TYPE, ESE_GZ_TX_DESC_TYPE_TSO,
                              ESF_GZ_TX_TSO_MSS, mss,
                              ESF_GZ_TX_TSO_HDR_NUM_SEGS, 1,
@@ -231,6 +233,11 @@ static void ef100_make_tso_desc(struct efx_nic *efx,
                              ESF_GZ_TX_TSO_INNER_L4_OFF_W, tcp_offset >> 1,
                              ESF_GZ_TX_TSO_ED_INNER_IP4_ID, mangleid,
                              ESF_GZ_TX_TSO_ED_INNER_IP_LEN, 1,
+                             ESF_GZ_TX_TSO_OUTER_L3_OFF_W, outer_ip_offset >> 1,
+                             ESF_GZ_TX_TSO_OUTER_L4_OFF_W, outer_l4_offset >> 1,
+                             ESF_GZ_TX_TSO_ED_OUTER_UDP_LEN, encap && !gso_partial,
+                             ESF_GZ_TX_TSO_ED_OUTER_IP4_ID, encap ? mangleid :
+                                                                    ESE_GZ_TX_DESC_IP4_ID_NO_OP,
                              ESF_GZ_TX_TSO_VLAN_INSERT_EN, vlan_enable,
                              ESF_GZ_TX_TSO_VLAN_INSERT_TCI, vlan_tci
                );