Commit 30fc828c authored by Dmitry Bogdanov's avatar Dmitry Bogdanov Committed by Greg Kroah-Hartman

net: aquantia: invalid checksumm offload implementation

[ Upstream commit ad703c2b ]

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: default avatarIgor Russkikh <igor.russkikh@aquantia.com>
Signed-off-by: default avatarDmitry Bogdanov <dmitry.bogdanov@aquantia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent fb7c179f
...@@ -172,6 +172,27 @@ bool aq_ring_tx_clean(struct aq_ring_s *self) ...@@ -172,6 +172,27 @@ bool aq_ring_tx_clean(struct aq_ring_s *self)
return !!budget; 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)) #define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info))
int aq_ring_rx_clean(struct aq_ring_s *self, int aq_ring_rx_clean(struct aq_ring_s *self,
struct napi_struct *napi, struct napi_struct *napi,
...@@ -267,18 +288,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self, ...@@ -267,18 +288,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self,
} }
skb->protocol = eth_type_trans(skb, ndev); skb->protocol = eth_type_trans(skb, ndev);
if (unlikely(buff->is_cso_err)) {
++self->stats.rx.errors; aq_rx_checksum(self, buff, skb);
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;
}
}
skb_set_hash(skb, buff->rss_hash, skb_set_hash(skb, buff->rss_hash,
buff->is_hash_l4 ? PKT_HASH_TYPE_L4 : buff->is_hash_l4 ? PKT_HASH_TYPE_L4 :
......
...@@ -655,9 +655,9 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, ...@@ -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 *) 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]; &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 is_rx_check_sum_enabled = 0U;
unsigned int pkt_type = 0U; unsigned int pkt_type = 0U;
u8 rx_stat = 0U;
if (!(rxd_wb->status & 0x1U)) { /* RxD is not done */ if (!(rxd_wb->status & 0x1U)) { /* RxD is not done */
break; break;
...@@ -665,35 +665,35 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, ...@@ -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]; 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_rx_check_sum_enabled = (rxd_wb->type) & (0x3U << 19);
is_err &= ~0x20U; /* exclude validity bit */
pkt_type = 0xFFU & (rxd_wb->type >> 4); pkt_type = 0xFFU & (rxd_wb->type >> 4);
if (is_rx_check_sum_enabled) { if (is_rx_check_sum_enabled & BIT(0) &&
if (0x0U == (pkt_type & 0x3U)) (0x0U == (pkt_type & 0x3U)))
buff->is_ip_cso = (is_err & 0x08U) ? 0U : 1U; buff->is_ip_cso = (rx_stat & BIT(1)) ? 0U : 1U;
if (is_rx_check_sum_enabled & BIT(1)) {
if (0x4U == (pkt_type & 0x1CU)) 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)) else if (0x0U == (pkt_type & 0x1CU))
buff->is_tcp_cso = buff->is_cso_err ? 0U : 1U; 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 */ /* Checksum offload workaround for small packets */
if (rxd_wb->pkt_len <= 60) { if (unlikely(rxd_wb->pkt_len <= 60)) {
buff->is_ip_cso = 0U; buff->is_ip_cso = 0U;
buff->is_cso_err = 0U; buff->is_cso_err = 0U;
} }
}
is_err &= ~0x18U;
dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE); dma_unmap_page(ndev, buff->pa, buff->len, DMA_FROM_DEVICE);
if (is_err || rxd_wb->type & 0x1000U) { if ((rx_stat & BIT(0)) || rxd_wb->type & 0x1000U) {
/* status error or DMA error */ /* MAC error or DMA error */
buff->is_error = 1U; buff->is_error = 1U;
} else { } else {
if (self->aq_nic_cfg->is_rss) { if (self->aq_nic_cfg->is_rss) {
......
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