| From 15beab0a9d797be1b7c67458da007a62269be29a Mon Sep 17 00:00:00 2001 |
| From: Dmitry Bezrukov <dbezrukov@marvell.com> |
| Date: Fri, 14 Feb 2020 18:44:51 +0300 |
| Subject: net: atlantic: checksum compat issue |
| |
| From: Dmitry Bezrukov <dbezrukov@marvell.com> |
| |
| 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: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 3 ++- |
| drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 3 ++- |
| drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c | 5 +++-- |
| 3 files changed, 7 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| @@ -351,7 +351,8 @@ int aq_ring_rx_clean(struct aq_ring_s *s |
| 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, |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h |
| @@ -78,7 +78,8 @@ struct __packed aq_ring_buff_s { |
| u32 is_cleaned:1; |
| u32 is_error:1; |
| u32 is_vlan:1; |
| - u32 rsvd3:4; |
| + u32 is_lro:1; |
| + u32 rsvd3:3; |
| u16 eop_index; |
| u16 rsvd4; |
| }; |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| @@ -823,6 +823,8 @@ static int hw_atl_b0_hw_ring_rx_receive( |
| } |
| } |
| |
| + 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; |
| @@ -835,8 +837,7 @@ static int hw_atl_b0_hw_ring_rx_receive( |
| 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; |