igc: Add tx_csum offload functionality
authorSasha Neftin <sasha.neftin@intel.com>
Thu, 8 Aug 2019 06:12:23 +0000 (09:12 +0300)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Mon, 9 Sep 2019 18:37:14 +0000 (11:37 -0700)
Add IP generic TX checksum offload functionality.

Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_base.h
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_main.c

index 0f5534ce27b013c697727cf37d2fb38d0ce9d34c..7e16345d836e1e73be8fcd02afa5a6dc941cbef0 100644 (file)
@@ -135,6 +135,9 @@ extern char igc_driver_version[];
 /* How many Rx Buffers do we bundle into one write to the hardware ? */
 #define IGC_RX_BUFFER_WRITE    16 /* Must be power of 2 */
 
+/* VLAN info */
+#define IGC_TX_FLAGS_VLAN_MASK 0xffff0000
+
 /* igc_test_staterr - tests bits within Rx descriptor status and error fields */
 static inline __le32 igc_test_staterr(union igc_adv_rx_desc *rx_desc,
                                      const u32 stat_err_bits)
@@ -254,6 +257,7 @@ struct igc_ring {
        u16 count;                      /* number of desc. in the ring */
        u8 queue_index;                 /* logical index of the ring*/
        u8 reg_idx;                     /* physical index of the ring */
+       bool launchtime_enable;         /* true if LaunchTime is enabled */
 
        /* everything past this point are written often */
        u16 next_to_clean;
index 58d1109d7f3fe8ecb509714585e810989ef6beab..ea627ce52525ac3c6b44e84b7f6dc1d1fd645212 100644 (file)
@@ -22,6 +22,14 @@ union igc_adv_tx_desc {
        } wb;
 };
 
+/* Context descriptors */
+struct igc_adv_tx_context_desc {
+       __le32 vlan_macip_lens;
+       __le32 launch_time;
+       __le32 type_tucmd_mlhl;
+       __le32 mss_l4len_idx;
+};
+
 /* Adv Transmit Descriptor Config Masks */
 #define IGC_ADVTXD_MAC_TSTAMP  0x00080000 /* IEEE1588 Timestamp packet */
 #define IGC_ADVTXD_DTYP_CTXT   0x00200000 /* Advanced Context Descriptor */
index 549134ecd10572dc7a42168002288c6e3f424fa2..f3f2325fe5671f10647ef6a660feb390b502356b 100644 (file)
 #define IGC_VLAPQF_P_VALID(_n) (0x1 << (3 + (_n) * 4))
 #define IGC_VLAPQF_QUEUE_MASK  0x03
 
+#define IGC_ADVTXD_MACLEN_SHIFT                9  /* Adv ctxt desc mac len shift */
+#define IGC_ADVTXD_TUCMD_IPV4          0x00000400  /* IP Packet Type:1=IPv4 */
+#define IGC_ADVTXD_TUCMD_L4T_TCP       0x00000800  /* L4 Packet Type of TCP */
+#define IGC_ADVTXD_TUCMD_L4T_SCTP      0x00001000 /* L4 packet TYPE of SCTP */
+
 #endif /* _IGC_DEFINES_H_ */
index 965d1c939f0f13db917a597e3ae64acd49a46bb1..63b62d74f9610c744fbd7f6c0a1e2e768291dc19 100644 (file)
@@ -5,6 +5,11 @@
 #include <linux/types.h>
 #include <linux/if_vlan.h>
 #include <linux/aer.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/ip.h>
+
+#include <net/ipv6.h>
 
 #include "igc.h"
 #include "igc_hw.h"
@@ -790,8 +795,96 @@ static int igc_set_mac(struct net_device *netdev, void *p)
        return 0;
 }
 
+static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,
+                           struct igc_tx_buffer *first,
+                           u32 vlan_macip_lens, u32 type_tucmd,
+                           u32 mss_l4len_idx)
+{
+       struct igc_adv_tx_context_desc *context_desc;
+       u16 i = tx_ring->next_to_use;
+       struct timespec64 ts;
+
+       context_desc = IGC_TX_CTXTDESC(tx_ring, i);
+
+       i++;
+       tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
+
+       /* set bits to identify this as an advanced context descriptor */
+       type_tucmd |= IGC_TXD_CMD_DEXT | IGC_ADVTXD_DTYP_CTXT;
+
+       /* For 82575, context index must be unique per ring. */
+       if (test_bit(IGC_RING_FLAG_TX_CTX_IDX, &tx_ring->flags))
+               mss_l4len_idx |= tx_ring->reg_idx << 4;
+
+       context_desc->vlan_macip_lens   = cpu_to_le32(vlan_macip_lens);
+       context_desc->type_tucmd_mlhl   = cpu_to_le32(type_tucmd);
+       context_desc->mss_l4len_idx     = cpu_to_le32(mss_l4len_idx);
+
+       /* We assume there is always a valid Tx time available. Invalid times
+        * should have been handled by the upper layers.
+        */
+       if (tx_ring->launchtime_enable) {
+               ts = ns_to_timespec64(first->skb->tstamp);
+               first->skb->tstamp = 0;
+               context_desc->launch_time = cpu_to_le32(ts.tv_nsec / 32);
+       } else {
+               context_desc->launch_time = 0;
+       }
+}
+
+static inline bool igc_ipv6_csum_is_sctp(struct sk_buff *skb)
+{
+       unsigned int offset = 0;
+
+       ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL);
+
+       return offset == skb_checksum_start_offset(skb);
+}
+
 static void igc_tx_csum(struct igc_ring *tx_ring, struct igc_tx_buffer *first)
 {
+       struct sk_buff *skb = first->skb;
+       u32 vlan_macip_lens = 0;
+       u32 type_tucmd = 0;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL) {
+csum_failed:
+               if (!(first->tx_flags & IGC_TX_FLAGS_VLAN) &&
+                   !tx_ring->launchtime_enable)
+                       return;
+               goto no_csum;
+       }
+
+       switch (skb->csum_offset) {
+       case offsetof(struct tcphdr, check):
+               type_tucmd = IGC_ADVTXD_TUCMD_L4T_TCP;
+               /* fall through */
+       case offsetof(struct udphdr, check):
+               break;
+       case offsetof(struct sctphdr, checksum):
+               /* validate that this is actually an SCTP request */
+               if ((first->protocol == htons(ETH_P_IP) &&
+                    (ip_hdr(skb)->protocol == IPPROTO_SCTP)) ||
+                   (first->protocol == htons(ETH_P_IPV6) &&
+                    igc_ipv6_csum_is_sctp(skb))) {
+                       type_tucmd = IGC_ADVTXD_TUCMD_L4T_SCTP;
+                       break;
+               }
+               /* fall through */
+       default:
+               skb_checksum_help(skb);
+               goto csum_failed;
+       }
+
+       /* update TX checksum flag */
+       first->tx_flags |= IGC_TX_FLAGS_CSUM;
+       vlan_macip_lens = skb_checksum_start_offset(skb) -
+                         skb_network_offset(skb);
+no_csum:
+       vlan_macip_lens |= skb_network_offset(skb) << IGC_ADVTXD_MACLEN_SHIFT;
+       vlan_macip_lens |= first->tx_flags & IGC_TX_FLAGS_VLAN_MASK;
+
+       igc_tx_ctxtdesc(tx_ring, first, vlan_macip_lens, type_tucmd, 0);
 }
 
 static int __igc_maybe_stop_tx(struct igc_ring *tx_ring, const u16 size)
@@ -4116,6 +4209,9 @@ static int igc_probe(struct pci_dev *pdev,
        if (err)
                goto err_sw_init;
 
+       /* Add supported features to the features list*/
+       netdev->features |= NETIF_F_HW_CSUM;
+
        /* setup the private structure */
        err = igc_sw_init(adapter);
        if (err)
@@ -4123,6 +4219,7 @@ static int igc_probe(struct pci_dev *pdev,
 
        /* copy netdev features into list of user selectable features */
        netdev->hw_features |= NETIF_F_NTUPLE;
+       netdev->hw_features |= netdev->features;
 
        /* MTU range: 68 - 9216 */
        netdev->min_mtu = ETH_MIN_MTU;