| From cf06efd49c1ab93a5ffcd5f71d0d370d8fa984ba Mon Sep 17 00:00:00 2001 |
| From: Dmitry Bezrukov <dbezrukov@marvell.com> |
| Date: Fri, 14 Feb 2020 18:44:51 +0300 |
| Subject: [PATCH] net: atlantic: checksum compat issue |
| |
| commit 15beab0a9d797be1b7c67458da007a62269be29a upstream. |
| |
| Yet another checksum offload compatibility issue was found. |
| |
| The known issue is that AQC HW marks tcp packets with 0xFFFF checksum |
| as invalid (1). This is workarounded in driver, passing all the suspicious |
| packets up to the stack for further csum validation. |
| |
| Another HW problem (2) is that it hides invalid csum of LRO aggregated |
| packets inside of the individual descriptors. That was workarounded |
| by forced scan of all LRO descriptors for checksum errors. |
| |
| However the scan logic was joint for both LRO and multi-descriptor |
| packets (jumbos). And this causes the issue. |
| |
| We have to drop LRO packets with the detected bad checksum |
| because of (2), but we have to pass jumbo packets to stack because of (1). |
| |
| When using windows tcp partner with jumbo frames but with LSO disabled |
| driver discards such frames as bad checksummed. But only LRO frames |
| should be dropped, not jumbos. |
| |
| On such a configurations tcp stream have a chance of drops and stucks. |
| |
| (1) 76f254d4afe2 ("net: aquantia: tcp checksum 0xffff being handled incorrectly") |
| (2) d08b9a0a3ebd ("net: aquantia: do not pass lro session with invalid tcp checksum") |
| |
| Fixes: d08b9a0a3ebd ("net: aquantia: do not pass lro session with invalid tcp checksum") |
| Signed-off-by: Dmitry Bezrukov <dbezrukov@marvell.com> |
| Signed-off-by: Igor Russkikh <irusskikh@marvell.com> |
| Signed-off-by: Dmitry Bogdanov <dbogdanov@marvell.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| index 5d650c58e6c5..e0f24580f50e 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| @@ -321,7 +321,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self, |
| err = 0; |
| goto err_exit; |
| } |
| - if (buff->is_error || buff->is_cso_err) { |
| + if (buff->is_error || |
| + (buff->is_lro && buff->is_cso_err)) { |
| buff_ = buff; |
| do { |
| next_ = buff_->next, |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h |
| index 6bd67210d0b7..e1c3a2e3b969 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h |
| @@ -74,7 +74,8 @@ struct __packed aq_ring_buff_s { |
| u32 is_mapped:1; |
| u32 is_cleaned:1; |
| u32 is_error:1; |
| - u32 rsvd3:6; |
| + u32 is_lro:1; |
| + u32 rsvd3:5; |
| u16 eop_index; |
| u16 rsvd4; |
| }; |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| index 84914297e326..40dfd5d23974 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| @@ -721,6 +721,8 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, |
| } |
| } |
| |
| + buff->is_lro = !!(HW_ATL_B0_RXD_WB_STAT2_RSCCNT & |
| + rxd_wb->status); |
| if (HW_ATL_B0_RXD_WB_STAT2_EOP & rxd_wb->status) { |
| buff->len = rxd_wb->pkt_len % |
| AQ_CFG_RX_FRAME_MAX; |
| @@ -733,8 +735,7 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, |
| rxd_wb->pkt_len > AQ_CFG_RX_FRAME_MAX ? |
| AQ_CFG_RX_FRAME_MAX : rxd_wb->pkt_len; |
| |
| - if (HW_ATL_B0_RXD_WB_STAT2_RSCCNT & |
| - rxd_wb->status) { |
| + if (buff->is_lro) { |
| /* LRO */ |
| buff->next = rxd_wb->next_desc_ptr; |
| ++ring->stats.rx.lro_packets; |
| -- |
| 2.7.4 |
| |