Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad703c2b authored by Dmitry Bogdanov's avatar Dmitry Bogdanov Committed by David S. Miller
Browse files

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: 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>
parent bfaa9f85
Loading
Loading
Loading
Loading
+23 −12
Original line number Diff line number Diff line
@@ -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 :
+18 −18
Original line number Diff line number Diff line
@@ -660,9 +660,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;
@@ -670,35 +670,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;

				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 (rxd_wb->pkt_len <= 60) {
		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) {