| From 81558db486f097ba3a251514fd4ae3c0cafee5bb Mon Sep 17 00:00:00 2001 |
| From: Dmitry Bogdanov <dmitry.bogdanov@aquantia.com> |
| Date: Fri, 9 Nov 2018 11:54:01 +0000 |
| Subject: net: aquantia: invalid checksumm offload implementation |
| |
| [ Upstream commit ad703c2b9127f9acdef697ec4755f6da4beaa266 ] |
| |
| Packets with marked invalid IP/UDP/TCP checksums were considered as good |
| by the driver. The error was in a logic, processing offload bits in |
| RX descriptor. |
| |
| Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com> |
| Signed-off-by: Dmitry Bogdanov <dmitry.bogdanov@aquantia.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| .../net/ethernet/aquantia/atlantic/aq_ring.c | 35 +++++++++++------- |
| .../aquantia/atlantic/hw_atl/hw_atl_b0.c | 36 +++++++++---------- |
| 2 files changed, 41 insertions(+), 30 deletions(-) |
| |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| index d1e1a0ba8615..7134d0d4cdf7 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c |
| @@ -172,6 +172,27 @@ bool aq_ring_tx_clean(struct aq_ring_s *self) |
| return !!budget; |
| } |
| |
| +static void aq_rx_checksum(struct aq_ring_s *self, |
| + struct aq_ring_buff_s *buff, |
| + struct sk_buff *skb) |
| +{ |
| + if (!(self->aq_nic->ndev->features & NETIF_F_RXCSUM)) |
| + return; |
| + |
| + if (unlikely(buff->is_cso_err)) { |
| + ++self->stats.rx.errors; |
| + skb->ip_summed = CHECKSUM_NONE; |
| + return; |
| + } |
| + if (buff->is_ip_cso) { |
| + __skb_incr_checksum_unnecessary(skb); |
| + if (buff->is_udp_cso || buff->is_tcp_cso) |
| + __skb_incr_checksum_unnecessary(skb); |
| + } else { |
| + skb->ip_summed = CHECKSUM_NONE; |
| + } |
| +} |
| + |
| #define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) |
| int aq_ring_rx_clean(struct aq_ring_s *self, |
| struct napi_struct *napi, |
| @@ -267,18 +288,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self, |
| } |
| |
| skb->protocol = eth_type_trans(skb, ndev); |
| - if (unlikely(buff->is_cso_err)) { |
| - ++self->stats.rx.errors; |
| - skb->ip_summed = CHECKSUM_NONE; |
| - } else { |
| - if (buff->is_ip_cso) { |
| - __skb_incr_checksum_unnecessary(skb); |
| - if (buff->is_udp_cso || buff->is_tcp_cso) |
| - __skb_incr_checksum_unnecessary(skb); |
| - } else { |
| - skb->ip_summed = CHECKSUM_NONE; |
| - } |
| - } |
| + |
| + aq_rx_checksum(self, buff, skb); |
| |
| skb_set_hash(skb, buff->rss_hash, |
| buff->is_hash_l4 ? PKT_HASH_TYPE_L4 : |
| 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 0271a0fdfee8..88705dee5b95 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 |
| @@ -655,9 +655,9 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, |
| struct hw_atl_rxd_wb_s *rxd_wb = (struct hw_atl_rxd_wb_s *) |
| &ring->dx_ring[ring->hw_head * HW_ATL_B0_RXD_SIZE]; |
| |
| - unsigned int is_err = 1U; |
| unsigned int is_rx_check_sum_enabled = 0U; |
| unsigned int pkt_type = 0U; |
| + u8 rx_stat = 0U; |
| |
| if (!(rxd_wb->status & 0x1U)) { /* RxD is not done */ |
| break; |
| @@ -665,35 +665,35 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, |
| |
| buff = &ring->buff_ring[ring->hw_head]; |
| |
| - is_err = (0x0000003CU & rxd_wb->status); |
| + rx_stat = (0x0000003CU & rxd_wb->status) >> 2; |
| |
| is_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19); |
| - is_err &= ~0x20U; /* exclude validity bit */ |
| |
| pkt_type = 0xFFU & (rxd_wb->type >> 4); |
| |
| - if (is_rx_check_sum_enabled) { |
| - if (0x0U == (pkt_type & 0x3U)) |
| - buff->is_ip_cso = (is_err & 0x08U) ? 0U : 1U; |
| + if (is_rx_check_sum_enabled & BIT(0) && |
| + (0x0U == (pkt_type & 0x3U))) |
| + buff->is_ip_cso = (rx_stat & BIT(1)) ? 0U : 1U; |
| |
| + if (is_rx_check_sum_enabled & BIT(1)) { |
| if (0x4U == (pkt_type & 0x1CU)) |
| - buff->is_udp_cso = buff->is_cso_err ? 0U : 1U; |
| + buff->is_udp_cso = (rx_stat & BIT(2)) ? 0U : |
| + !!(rx_stat & BIT(3)); |
| else if (0x0U == (pkt_type & 0x1CU)) |
| - buff->is_tcp_cso = buff->is_cso_err ? 0U : 1U; |
| - |
| - /* Checksum offload workaround for small packets */ |
| - if (rxd_wb->pkt_len <= 60) { |
| - buff->is_ip_cso = 0U; |
| - buff->is_cso_err = 0U; |
| - } |
| + buff->is_tcp_cso = (rx_stat & BIT(2)) ? 0U : |
| + !!(rx_stat & BIT(3)); |
| + } |
| + buff->is_cso_err = !!(rx_stat & 0x6); |
| + /* Checksum offload workaround for small packets */ |
| + if (unlikely(rxd_wb->pkt_len <= 60)) { |
| + buff->is_ip_cso = 0U; |
| + buff->is_cso_err = 0U; |
| } |
| - |
| - is_err &= ~0x18U; |
| |
| dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE); |
| |
| - if (is_err || rxd_wb->type & 0x1000U) { |
| - /* status error or DMA error */ |
| + if ((rx_stat & BIT(0)) || rxd_wb->type & 0x1000U) { |
| + /* MAC error or DMA error */ |
| buff->is_error = 1U; |
| } else { |
| if (self->aq_nic_cfg->is_rss) { |
| -- |
| 2.17.1 |
| |