i40e/i40evf: add PPRS bit to error bits and fix bug in Rx checksum
authorJesse Brandeburg <jesse.brandeburg@intel.com>
Tue, 20 May 2014 08:01:43 +0000 (08:01 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Mon, 9 Jun 2014 07:39:58 +0000 (00:39 -0700)
The driver was not marking packets with bad checksums
correctly, especially IPv6 packets with a bad checksum.
To do this correctly we need a define that may be set by
hardware in rare cases.

Change-ID: I1a997b72b491ded27a78ac3bce1197b2d2611130
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_type.h

index df3917b..b16c251 100644 (file)
@@ -119,6 +119,7 @@ static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("mac_local_faults", stats.mac_local_faults),
        I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
        I40E_PF_STAT("tx_timeout", tx_timeout_count),
+       I40E_PF_STAT("rx_csum_bad", hw_csum_rx_error),
        I40E_PF_STAT("rx_length_errors", stats.rx_length_errors),
        I40E_PF_STAT("link_xon_rx", stats.link_xon_rx),
        I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
index 2622a86..d84f427 100644 (file)
@@ -1193,10 +1193,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    u32 rx_error,
                                    u16 rx_ptype)
 {
+       struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
+       bool ipv4 = false, ipv6 = false;
        bool ipv4_tunnel, ipv6_tunnel;
        __wsum rx_udp_csum;
-       __sum16 csum;
        struct iphdr *iph;
+       __sum16 csum;
 
        ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
                      (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
@@ -1207,29 +1209,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Rx csum enabled and ip headers found? */
-       if (!(vsi->netdev->features & NETIF_F_RXCSUM &&
-             rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       /* did the hardware decode the packet and checksum? */
+       if (!(rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+               return;
+
+       /* both known and outer_ip must be set for the below code to work */
+       if (!(decoded.known && decoded.outer_ip))
                return;
 
+       if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+           decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)
+               ipv4 = true;
+       else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+                decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
+               ipv6 = true;
+
+       if (ipv4 &&
+           (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
+                        (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))))
+               goto checksum_fail;
+
        /* likely incorrect csum if alternate IP extension headers found */
-       if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+       if (ipv6 &&
+           decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP &&
+           rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) &&
+           rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+               /* don't increment checksum err here, non-fatal err */
                return;
 
-       /* IP or L4 or outmost IP checksum error */
-       if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
-               vsi->back->hw_csum_rx_error++;
+       /* there was some L4 error, count error and punt packet to the stack */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
+               goto checksum_fail;
+
+       /* handle packets that were not able to be checksummed due
+        * to arrival speed, in this case the stack can compute
+        * the csum.
+        */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
-       }
 
+       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+        * it in the driver, hardware does not do it for us.
+        * Since L3L4P bit was set we assume a valid IHL value (>=5)
+        * so the total length of IPv4 header is IHL*4 bytes
+        * The UDP_0 bit *may* bet set if the *inner* header is UDP
+        */
        if (ipv4_tunnel &&
+           (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
            !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
-               /* If VXLAN traffic has an outer UDPv4 checksum we need to check
-                * it in the driver, hardware does not do it for us.
-                * Since L3L4P bit was set we assume a valid IHL value (>=5)
-                * so the total length of IPv4 header is IHL*4 bytes
-                */
                skb->transport_header = skb->mac_header +
                                        sizeof(struct ethhdr) +
                                        (ip_hdr(skb)->ihl * 4);
@@ -1246,13 +1276,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                (skb->len - skb_transport_offset(skb)),
                                IPPROTO_UDP, rx_udp_csum);
 
-               if (udp_hdr(skb)->check != csum) {
-                       vsi->back->hw_csum_rx_error++;
-                       return;
-               }
+               if (udp_hdr(skb)->check != csum)
+                       goto checksum_fail;
        }
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
 }
 
 /**
@@ -1429,6 +1462,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
+                       /* TODO: shouldn't we increment a counter indicating the
+                        * drop?
+                        */
                        goto next_desc;
                }
 
index 6c977d2..42bfb2a 100644 (file)
@@ -541,7 +541,8 @@ enum i40e_rx_desc_error_bits {
        I40E_RX_DESC_ERROR_IPE_SHIFT            = 3,
        I40E_RX_DESC_ERROR_L4E_SHIFT            = 4,
        I40E_RX_DESC_ERROR_EIPE_SHIFT           = 5,
-       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6
+       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6,
+       I40E_RX_DESC_ERROR_PPRS_SHIFT           = 7
 };
 
 enum i40e_rx_desc_error_l3l4e_fcoe_masks {
index ae089df..48ebb6c 100644 (file)
@@ -728,10 +728,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    u32 rx_error,
                                    u16 rx_ptype)
 {
+       struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
+       bool ipv4 = false, ipv6 = false;
        bool ipv4_tunnel, ipv6_tunnel;
        __wsum rx_udp_csum;
-       __sum16 csum;
        struct iphdr *iph;
+       __sum16 csum;
 
        ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
                      (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
@@ -742,29 +744,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Rx csum enabled and ip headers found? */
-       if (!(vsi->netdev->features & NETIF_F_RXCSUM &&
-             rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       /* did the hardware decode the packet and checksum? */
+       if (!(rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+               return;
+
+       /* both known and outer_ip must be set for the below code to work */
+       if (!(decoded.known && decoded.outer_ip))
                return;
 
+       if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+           decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)
+               ipv4 = true;
+       else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+                decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
+               ipv6 = true;
+
+       if (ipv4 &&
+           (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
+                        (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))))
+               goto checksum_fail;
+
        /* likely incorrect csum if alternate IP extension headers found */
-       if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+       if (ipv6 &&
+           decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP &&
+           rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) &&
+           rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+               /* don't increment checksum err here, non-fatal err */
                return;
 
-       /* IP or L4 or outmost IP checksum error */
-       if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
-               vsi->back->hw_csum_rx_error++;
+       /* there was some L4 error, count error and punt packet to the stack */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
+               goto checksum_fail;
+
+       /* handle packets that were not able to be checksummed due
+        * to arrival speed, in this case the stack can compute
+        * the csum.
+        */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
-       }
 
+       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+        * it in the driver, hardware does not do it for us.
+        * Since L3L4P bit was set we assume a valid IHL value (>=5)
+        * so the total length of IPv4 header is IHL*4 bytes
+        * The UDP_0 bit *may* bet set if the *inner* header is UDP
+        */
        if (ipv4_tunnel &&
+           (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
            !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
-               /* If VXLAN traffic has an outer UDPv4 checksum we need to check
-                * it in the driver, hardware does not do it for us.
-                * Since L3L4P bit was set we assume a valid IHL value (>=5)
-                * so the total length of IPv4 header is IHL*4 bytes
-                */
                skb->transport_header = skb->mac_header +
                                        sizeof(struct ethhdr) +
                                        (ip_hdr(skb)->ihl * 4);
@@ -781,13 +811,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                (skb->len - skb_transport_offset(skb)),
                                IPPROTO_UDP, rx_udp_csum);
 
-               if (udp_hdr(skb)->check != csum) {
-                       vsi->back->hw_csum_rx_error++;
-                       return;
-               }
+               if (udp_hdr(skb)->check != csum)
+                       goto checksum_fail;
        }
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
 }
 
 /**
@@ -956,6 +989,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
+                       /* TODO: shouldn't we increment a counter indicating the
+                        * drop?
+                        */
                        goto next_desc;
                }
 
index e3c9ebb..0a7914d 100644 (file)
@@ -541,7 +541,8 @@ enum i40e_rx_desc_error_bits {
        I40E_RX_DESC_ERROR_IPE_SHIFT            = 3,
        I40E_RX_DESC_ERROR_L4E_SHIFT            = 4,
        I40E_RX_DESC_ERROR_EIPE_SHIFT           = 5,
-       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6
+       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6,
+       I40E_RX_DESC_ERROR_PPRS_SHIFT           = 7
 };
 
 enum i40e_rx_desc_error_l3l4e_fcoe_masks {