Commit 8a3c91cc authored by Jesse Brandeburg's avatar Jesse Brandeburg Committed by Jeff Kirsher

i40e/i40evf: add PPRS bit to error bits and fix bug in Rx checksum

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: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 9aa7e935
...@@ -119,6 +119,7 @@ static struct i40e_stats i40e_gstrings_stats[] = { ...@@ -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_local_faults", stats.mac_local_faults),
I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults), I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
I40E_PF_STAT("tx_timeout", tx_timeout_count), 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("rx_length_errors", stats.rx_length_errors),
I40E_PF_STAT("link_xon_rx", stats.link_xon_rx), I40E_PF_STAT("link_xon_rx", stats.link_xon_rx),
I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx), I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
......
...@@ -1193,10 +1193,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -1193,10 +1193,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
u32 rx_error, u32 rx_error,
u16 rx_ptype) 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; bool ipv4_tunnel, ipv6_tunnel;
__wsum rx_udp_csum; __wsum rx_udp_csum;
__sum16 csum;
struct iphdr *iph; struct iphdr *iph;
__sum16 csum;
ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) && ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
(rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4); (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
...@@ -1207,29 +1209,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -1207,29 +1209,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
/* Rx csum enabled and ip headers found? */ /* Rx csum enabled and ip headers found? */
if (!(vsi->netdev->features & NETIF_F_RXCSUM && if (!(vsi->netdev->features & NETIF_F_RXCSUM))
rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT))) 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; 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 */ /* 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; return;
/* IP or L4 or outmost IP checksum error */ /* there was some L4 error, count error and punt packet to the stack */
if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) | if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
(1 << I40E_RX_DESC_ERROR_L4E_SHIFT) | goto checksum_fail;
(1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
vsi->back->hw_csum_rx_error++; /* 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; 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 && if (ipv4_tunnel &&
(decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
!(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) { !(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 + skb->transport_header = skb->mac_header +
sizeof(struct ethhdr) + sizeof(struct ethhdr) +
(ip_hdr(skb)->ihl * 4); (ip_hdr(skb)->ihl * 4);
...@@ -1246,13 +1276,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -1246,13 +1276,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
(skb->len - skb_transport_offset(skb)), (skb->len - skb_transport_offset(skb)),
IPPROTO_UDP, rx_udp_csum); IPPROTO_UDP, rx_udp_csum);
if (udp_hdr(skb)->check != csum) { if (udp_hdr(skb)->check != csum)
vsi->back->hw_csum_rx_error++; goto checksum_fail;
return;
}
} }
skb->ip_summed = CHECKSUM_UNNECESSARY; 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) ...@@ -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 */ /* ERR_MASK will only have valid bits if EOP set */
if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) { if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
/* TODO: shouldn't we increment a counter indicating the
* drop?
*/
goto next_desc; goto next_desc;
} }
......
...@@ -541,7 +541,8 @@ enum i40e_rx_desc_error_bits { ...@@ -541,7 +541,8 @@ enum i40e_rx_desc_error_bits {
I40E_RX_DESC_ERROR_IPE_SHIFT = 3, I40E_RX_DESC_ERROR_IPE_SHIFT = 3,
I40E_RX_DESC_ERROR_L4E_SHIFT = 4, I40E_RX_DESC_ERROR_L4E_SHIFT = 4,
I40E_RX_DESC_ERROR_EIPE_SHIFT = 5, 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 { enum i40e_rx_desc_error_l3l4e_fcoe_masks {
......
...@@ -728,10 +728,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -728,10 +728,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
u32 rx_error, u32 rx_error,
u16 rx_ptype) 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; bool ipv4_tunnel, ipv6_tunnel;
__wsum rx_udp_csum; __wsum rx_udp_csum;
__sum16 csum;
struct iphdr *iph; struct iphdr *iph;
__sum16 csum;
ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) && ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
(rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4); (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
...@@ -742,29 +744,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -742,29 +744,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
/* Rx csum enabled and ip headers found? */ /* Rx csum enabled and ip headers found? */
if (!(vsi->netdev->features & NETIF_F_RXCSUM && if (!(vsi->netdev->features & NETIF_F_RXCSUM))
rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT))) 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; 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 */ /* 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; return;
/* IP or L4 or outmost IP checksum error */ /* there was some L4 error, count error and punt packet to the stack */
if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) | if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
(1 << I40E_RX_DESC_ERROR_L4E_SHIFT) | goto checksum_fail;
(1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
vsi->back->hw_csum_rx_error++; /* 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; 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 && if (ipv4_tunnel &&
(decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
!(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) { !(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 + skb->transport_header = skb->mac_header +
sizeof(struct ethhdr) + sizeof(struct ethhdr) +
(ip_hdr(skb)->ihl * 4); (ip_hdr(skb)->ihl * 4);
...@@ -781,13 +811,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -781,13 +811,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
(skb->len - skb_transport_offset(skb)), (skb->len - skb_transport_offset(skb)),
IPPROTO_UDP, rx_udp_csum); IPPROTO_UDP, rx_udp_csum);
if (udp_hdr(skb)->check != csum) { if (udp_hdr(skb)->check != csum)
vsi->back->hw_csum_rx_error++; goto checksum_fail;
return;
}
} }
skb->ip_summed = CHECKSUM_UNNECESSARY; 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) ...@@ -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 */ /* ERR_MASK will only have valid bits if EOP set */
if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) { if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
/* TODO: shouldn't we increment a counter indicating the
* drop?
*/
goto next_desc; goto next_desc;
} }
......
...@@ -541,7 +541,8 @@ enum i40e_rx_desc_error_bits { ...@@ -541,7 +541,8 @@ enum i40e_rx_desc_error_bits {
I40E_RX_DESC_ERROR_IPE_SHIFT = 3, I40E_RX_DESC_ERROR_IPE_SHIFT = 3,
I40E_RX_DESC_ERROR_L4E_SHIFT = 4, I40E_RX_DESC_ERROR_L4E_SHIFT = 4,
I40E_RX_DESC_ERROR_EIPE_SHIFT = 5, 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 { enum i40e_rx_desc_error_l3l4e_fcoe_masks {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment