diff options
| author | Dmitry Bogdanov <dmitry.bogdanov@aquantia.com> | 2018-11-09 06:54:01 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2018-11-09 18:38:10 -0500 |
| commit | ad703c2b9127f9acdef697ec4755f6da4beaa266 (patch) | |
| tree | 6413bae24bb34adff365b8fcef9d0e5a27cf683e | |
| parent | bfaa9f8553d5c20703781e63f4fc8cb4792f18fd (diff) | |
net: aquantia: invalid checksumm offload implementation
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>
| -rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 35 | ||||
| -rw-r--r-- | drivers/net/ethernet/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 3db91446cc67..74550ccc7a20 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) | |||
| 172 | return !!budget; | 172 | return !!budget; |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | static void aq_rx_checksum(struct aq_ring_s *self, | ||
| 176 | struct aq_ring_buff_s *buff, | ||
| 177 | struct sk_buff *skb) | ||
| 178 | { | ||
| 179 | if (!(self->aq_nic->ndev->features & NETIF_F_RXCSUM)) | ||
| 180 | return; | ||
| 181 | |||
| 182 | if (unlikely(buff->is_cso_err)) { | ||
| 183 | ++self->stats.rx.errors; | ||
| 184 | skb->ip_summed = CHECKSUM_NONE; | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | if (buff->is_ip_cso) { | ||
| 188 | __skb_incr_checksum_unnecessary(skb); | ||
| 189 | if (buff->is_udp_cso || buff->is_tcp_cso) | ||
| 190 | __skb_incr_checksum_unnecessary(skb); | ||
| 191 | } else { | ||
| 192 | skb->ip_summed = CHECKSUM_NONE; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 175 | #define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) | 196 | #define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) |
| 176 | int aq_ring_rx_clean(struct aq_ring_s *self, | 197 | int aq_ring_rx_clean(struct aq_ring_s *self, |
| 177 | struct napi_struct *napi, | 198 | struct napi_struct *napi, |
| @@ -267,18 +288,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self, | |||
| 267 | } | 288 | } |
| 268 | 289 | ||
| 269 | skb->protocol = eth_type_trans(skb, ndev); | 290 | skb->protocol = eth_type_trans(skb, ndev); |
| 270 | if (unlikely(buff->is_cso_err)) { | 291 | |
| 271 | ++self->stats.rx.errors; | 292 | aq_rx_checksum(self, buff, skb); |
| 272 | skb->ip_summed = CHECKSUM_NONE; | ||
| 273 | } else { | ||
| 274 | if (buff->is_ip_cso) { | ||
| 275 | __skb_incr_checksum_unnecessary(skb); | ||
| 276 | if (buff->is_udp_cso || buff->is_tcp_cso) | ||
| 277 | __skb_incr_checksum_unnecessary(skb); | ||
| 278 | } else { | ||
| 279 | skb->ip_summed = CHECKSUM_NONE; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | 293 | ||
| 283 | skb_set_hash(skb, buff->rss_hash, | 294 | skb_set_hash(skb, buff->rss_hash, |
| 284 | buff->is_hash_l4 ? PKT_HASH_TYPE_L4 : | 295 | 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 3aec56623bf5..179ce12fe4d8 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 | |||
| @@ -660,9 +660,9 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, | |||
| 660 | struct hw_atl_rxd_wb_s *rxd_wb = (struct hw_atl_rxd_wb_s *) | 660 | struct hw_atl_rxd_wb_s *rxd_wb = (struct hw_atl_rxd_wb_s *) |
| 661 | &ring->dx_ring[ring->hw_head * HW_ATL_B0_RXD_SIZE]; | 661 | &ring->dx_ring[ring->hw_head * HW_ATL_B0_RXD_SIZE]; |
| 662 | 662 | ||
| 663 | unsigned int is_err = 1U; | ||
| 664 | unsigned int is_rx_check_sum_enabled = 0U; | 663 | unsigned int is_rx_check_sum_enabled = 0U; |
| 665 | unsigned int pkt_type = 0U; | 664 | unsigned int pkt_type = 0U; |
| 665 | u8 rx_stat = 0U; | ||
| 666 | 666 | ||
| 667 | if (!(rxd_wb->status & 0x1U)) { /* RxD is not done */ | 667 | if (!(rxd_wb->status & 0x1U)) { /* RxD is not done */ |
| 668 | break; | 668 | break; |
| @@ -670,35 +670,35 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, | |||
| 670 | 670 | ||
| 671 | buff = &ring->buff_ring[ring->hw_head]; | 671 | buff = &ring->buff_ring[ring->hw_head]; |
| 672 | 672 | ||
| 673 | is_err = (0x0000003CU & rxd_wb->status); | 673 | rx_stat = (0x0000003CU & rxd_wb->status) >> 2; |
| 674 | 674 | ||
| 675 | is_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19); | 675 | is_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19); |
| 676 | is_err &= ~0x20U; /* exclude validity bit */ | ||
| 677 | 676 | ||
| 678 | pkt_type = 0xFFU & (rxd_wb->type >> 4); | 677 | pkt_type = 0xFFU & (rxd_wb->type >> 4); |
| 679 | 678 | ||
| 680 | if (is_rx_check_sum_enabled) { | 679 | if (is_rx_check_sum_enabled & BIT(0) && |
| 681 | if (0x0U == (pkt_type & 0x3U)) | 680 | (0x0U == (pkt_type & 0x3U))) |
| 682 | buff->is_ip_cso = (is_err & 0x08U) ? 0U : 1U; | 681 | buff->is_ip_cso = (rx_stat & BIT(1)) ? 0U : 1U; |
| 683 | 682 | ||
| 683 | if (is_rx_check_sum_enabled & BIT(1)) { | ||
| 684 | if (0x4U == (pkt_type & 0x1CU)) | 684 | if (0x4U == (pkt_type & 0x1CU)) |
| 685 | buff->is_udp_cso = buff->is_cso_err ? 0U : 1U; | 685 | buff->is_udp_cso = (rx_stat & BIT(2)) ? 0U : |
| 686 | !!(rx_stat & BIT(3)); | ||
| 686 | else if (0x0U == (pkt_type & 0x1CU)) | 687 | else if (0x0U == (pkt_type & 0x1CU)) |
| 687 | buff->is_tcp_cso = buff->is_cso_err ? 0U : 1U; | 688 | buff->is_tcp_cso = (rx_stat & BIT(2)) ? 0U : |
| 688 | 689 | !!(rx_stat & BIT(3)); | |
| 689 | /* Checksum offload workaround for small packets */ | 690 | } |
| 690 | if (rxd_wb->pkt_len <= 60) { | 691 | buff->is_cso_err = !!(rx_stat & 0x6); |
| 691 | buff->is_ip_cso = 0U; | 692 | /* Checksum offload workaround for small packets */ |
| 692 | buff->is_cso_err = 0U; | 693 | if (unlikely(rxd_wb->pkt_len <= 60)) { |
| 693 | } | 694 | buff->is_ip_cso = 0U; |
| 695 | buff->is_cso_err = 0U; | ||
| 694 | } | 696 | } |
| 695 | |||
| 696 | is_err &= ~0x18U; | ||
| 697 | 697 | ||
| 698 | dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE); | 698 | dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE); |
| 699 | 699 | ||
| 700 | if (is_err || rxd_wb->type & 0x1000U) { | 700 | if ((rx_stat & BIT(0)) || rxd_wb->type & 0x1000U) { |
| 701 | /* status error or DMA error */ | 701 | /* MAC error or DMA error */ |
| 702 | buff->is_error = 1U; | 702 | buff->is_error = 1U; |
| 703 | } else { | 703 | } else { |
| 704 | if (self->aq_nic_cfg->is_rss) { | 704 | if (self->aq_nic_cfg->is_rss) { |
