162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Atlantic Network Driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2014-2019 aQuantia Corporation
562306a36Sopenharmony_ci * Copyright (C) 2019-2020 Marvell International Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* File aq_ring.c: Definition of functions for Rx/Tx rings. */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "aq_nic.h"
1162306a36Sopenharmony_ci#include "aq_hw.h"
1262306a36Sopenharmony_ci#include "aq_hw_utils.h"
1362306a36Sopenharmony_ci#include "aq_ptp.h"
1462306a36Sopenharmony_ci#include "aq_vec.h"
1562306a36Sopenharmony_ci#include "aq_main.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <net/xdp.h>
1862306a36Sopenharmony_ci#include <linux/filter.h>
1962306a36Sopenharmony_ci#include <linux/bpf_trace.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <linux/etherdevice.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void aq_get_rxpages_xdp(struct aq_ring_buff_s *buff,
2462306a36Sopenharmony_ci			       struct xdp_buff *xdp)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct skb_shared_info *sinfo;
2762306a36Sopenharmony_ci	int i;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (xdp_buff_has_frags(xdp)) {
3062306a36Sopenharmony_ci		sinfo = xdp_get_shared_info_from_buff(xdp);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		for (i = 0; i < sinfo->nr_frags; i++) {
3362306a36Sopenharmony_ci			skb_frag_t *frag = &sinfo->frags[i];
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci			page_ref_inc(skb_frag_page(frag));
3662306a36Sopenharmony_ci		}
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci	page_ref_inc(buff->rxdata.page);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic inline void aq_free_rxpage(struct aq_rxpage *rxpage, struct device *dev)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	unsigned int len = PAGE_SIZE << rxpage->order;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	dma_unmap_page(dev, rxpage->daddr, len, DMA_FROM_DEVICE);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* Drop the ref for being in the ring. */
4862306a36Sopenharmony_ci	__free_pages(rxpage->page, rxpage->order);
4962306a36Sopenharmony_ci	rxpage->page = NULL;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int aq_alloc_rxpages(struct aq_rxpage *rxpage, struct aq_ring_s *rx_ring)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct device *dev = aq_nic_get_dev(rx_ring->aq_nic);
5562306a36Sopenharmony_ci	unsigned int order = rx_ring->page_order;
5662306a36Sopenharmony_ci	struct page *page;
5762306a36Sopenharmony_ci	int ret = -ENOMEM;
5862306a36Sopenharmony_ci	dma_addr_t daddr;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	page = dev_alloc_pages(order);
6162306a36Sopenharmony_ci	if (unlikely(!page))
6262306a36Sopenharmony_ci		goto err_exit;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	daddr = dma_map_page(dev, page, 0, PAGE_SIZE << order,
6562306a36Sopenharmony_ci			     DMA_FROM_DEVICE);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(dev, daddr)))
6862306a36Sopenharmony_ci		goto free_page;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	rxpage->page = page;
7162306a36Sopenharmony_ci	rxpage->daddr = daddr;
7262306a36Sopenharmony_ci	rxpage->order = order;
7362306a36Sopenharmony_ci	rxpage->pg_off = rx_ring->page_offset;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cifree_page:
7862306a36Sopenharmony_ci	__free_pages(page, order);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cierr_exit:
8162306a36Sopenharmony_ci	return ret;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int aq_get_rxpages(struct aq_ring_s *self, struct aq_ring_buff_s *rxbuf)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	unsigned int order = self->page_order;
8762306a36Sopenharmony_ci	u16 page_offset = self->page_offset;
8862306a36Sopenharmony_ci	u16 frame_max = self->frame_max;
8962306a36Sopenharmony_ci	u16 tail_size = self->tail_size;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (rxbuf->rxdata.page) {
9362306a36Sopenharmony_ci		/* One means ring is the only user and can reuse */
9462306a36Sopenharmony_ci		if (page_ref_count(rxbuf->rxdata.page) > 1) {
9562306a36Sopenharmony_ci			/* Try reuse buffer */
9662306a36Sopenharmony_ci			rxbuf->rxdata.pg_off += frame_max + page_offset +
9762306a36Sopenharmony_ci						tail_size;
9862306a36Sopenharmony_ci			if (rxbuf->rxdata.pg_off + frame_max + tail_size <=
9962306a36Sopenharmony_ci			    (PAGE_SIZE << order)) {
10062306a36Sopenharmony_ci				u64_stats_update_begin(&self->stats.rx.syncp);
10162306a36Sopenharmony_ci				self->stats.rx.pg_flips++;
10262306a36Sopenharmony_ci				u64_stats_update_end(&self->stats.rx.syncp);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci			} else {
10562306a36Sopenharmony_ci				/* Buffer exhausted. We have other users and
10662306a36Sopenharmony_ci				 * should release this page and realloc
10762306a36Sopenharmony_ci				 */
10862306a36Sopenharmony_ci				aq_free_rxpage(&rxbuf->rxdata,
10962306a36Sopenharmony_ci					       aq_nic_get_dev(self->aq_nic));
11062306a36Sopenharmony_ci				u64_stats_update_begin(&self->stats.rx.syncp);
11162306a36Sopenharmony_ci				self->stats.rx.pg_losts++;
11262306a36Sopenharmony_ci				u64_stats_update_end(&self->stats.rx.syncp);
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci		} else {
11562306a36Sopenharmony_ci			rxbuf->rxdata.pg_off = page_offset;
11662306a36Sopenharmony_ci			u64_stats_update_begin(&self->stats.rx.syncp);
11762306a36Sopenharmony_ci			self->stats.rx.pg_reuses++;
11862306a36Sopenharmony_ci			u64_stats_update_end(&self->stats.rx.syncp);
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (!rxbuf->rxdata.page) {
12362306a36Sopenharmony_ci		ret = aq_alloc_rxpages(&rxbuf->rxdata, self);
12462306a36Sopenharmony_ci		if (ret) {
12562306a36Sopenharmony_ci			u64_stats_update_begin(&self->stats.rx.syncp);
12662306a36Sopenharmony_ci			self->stats.rx.alloc_fails++;
12762306a36Sopenharmony_ci			u64_stats_update_end(&self->stats.rx.syncp);
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci		return ret;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int aq_ring_alloc(struct aq_ring_s *self,
13662306a36Sopenharmony_ci			 struct aq_nic_s *aq_nic)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	int err = 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	self->buff_ring =
14162306a36Sopenharmony_ci		kcalloc(self->size, sizeof(struct aq_ring_buff_s), GFP_KERNEL);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!self->buff_ring) {
14462306a36Sopenharmony_ci		err = -ENOMEM;
14562306a36Sopenharmony_ci		goto err_exit;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	self->dx_ring = dma_alloc_coherent(aq_nic_get_dev(aq_nic),
14962306a36Sopenharmony_ci					   self->size * self->dx_size,
15062306a36Sopenharmony_ci					   &self->dx_ring_pa, GFP_KERNEL);
15162306a36Sopenharmony_ci	if (!self->dx_ring) {
15262306a36Sopenharmony_ci		err = -ENOMEM;
15362306a36Sopenharmony_ci		goto err_exit;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cierr_exit:
15762306a36Sopenharmony_ci	if (err < 0) {
15862306a36Sopenharmony_ci		aq_ring_free(self);
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return err;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciint aq_ring_tx_alloc(struct aq_ring_s *self,
16562306a36Sopenharmony_ci		     struct aq_nic_s *aq_nic,
16662306a36Sopenharmony_ci		     unsigned int idx,
16762306a36Sopenharmony_ci		     struct aq_nic_cfg_s *aq_nic_cfg)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	self->aq_nic = aq_nic;
17062306a36Sopenharmony_ci	self->idx = idx;
17162306a36Sopenharmony_ci	self->size = aq_nic_cfg->txds;
17262306a36Sopenharmony_ci	self->dx_size = aq_nic_cfg->aq_hw_caps->txd_size;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return aq_ring_alloc(self, aq_nic);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciint aq_ring_rx_alloc(struct aq_ring_s *self,
17862306a36Sopenharmony_ci		     struct aq_nic_s *aq_nic,
17962306a36Sopenharmony_ci		     unsigned int idx,
18062306a36Sopenharmony_ci		     struct aq_nic_cfg_s *aq_nic_cfg)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	self->aq_nic = aq_nic;
18362306a36Sopenharmony_ci	self->idx = idx;
18462306a36Sopenharmony_ci	self->size = aq_nic_cfg->rxds;
18562306a36Sopenharmony_ci	self->dx_size = aq_nic_cfg->aq_hw_caps->rxd_size;
18662306a36Sopenharmony_ci	self->xdp_prog = aq_nic->xdp_prog;
18762306a36Sopenharmony_ci	self->frame_max = AQ_CFG_RX_FRAME_MAX;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Only order-2 is allowed if XDP is enabled */
19062306a36Sopenharmony_ci	if (READ_ONCE(self->xdp_prog)) {
19162306a36Sopenharmony_ci		self->page_offset = AQ_XDP_HEADROOM;
19262306a36Sopenharmony_ci		self->page_order = AQ_CFG_XDP_PAGEORDER;
19362306a36Sopenharmony_ci		self->tail_size = AQ_XDP_TAILROOM;
19462306a36Sopenharmony_ci	} else {
19562306a36Sopenharmony_ci		self->page_offset = 0;
19662306a36Sopenharmony_ci		self->page_order = fls(self->frame_max / PAGE_SIZE +
19762306a36Sopenharmony_ci				       (self->frame_max % PAGE_SIZE ? 1 : 0)) - 1;
19862306a36Sopenharmony_ci		if (aq_nic_cfg->rxpageorder > self->page_order)
19962306a36Sopenharmony_ci			self->page_order = aq_nic_cfg->rxpageorder;
20062306a36Sopenharmony_ci		self->tail_size = 0;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return aq_ring_alloc(self, aq_nic);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ciint
20762306a36Sopenharmony_ciaq_ring_hwts_rx_alloc(struct aq_ring_s *self, struct aq_nic_s *aq_nic,
20862306a36Sopenharmony_ci		      unsigned int idx, unsigned int size, unsigned int dx_size)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct device *dev = aq_nic_get_dev(aq_nic);
21162306a36Sopenharmony_ci	size_t sz = size * dx_size + AQ_CFG_RXDS_DEF;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	memset(self, 0, sizeof(*self));
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	self->aq_nic = aq_nic;
21662306a36Sopenharmony_ci	self->idx = idx;
21762306a36Sopenharmony_ci	self->size = size;
21862306a36Sopenharmony_ci	self->dx_size = dx_size;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	self->dx_ring = dma_alloc_coherent(dev, sz, &self->dx_ring_pa,
22162306a36Sopenharmony_ci					   GFP_KERNEL);
22262306a36Sopenharmony_ci	if (!self->dx_ring) {
22362306a36Sopenharmony_ci		aq_ring_free(self);
22462306a36Sopenharmony_ci		return -ENOMEM;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ciint aq_ring_init(struct aq_ring_s *self, const enum atl_ring_type ring_type)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	self->hw_head = 0;
23362306a36Sopenharmony_ci	self->sw_head = 0;
23462306a36Sopenharmony_ci	self->sw_tail = 0;
23562306a36Sopenharmony_ci	self->ring_type = ring_type;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (self->ring_type == ATL_RING_RX)
23862306a36Sopenharmony_ci		u64_stats_init(&self->stats.rx.syncp);
23962306a36Sopenharmony_ci	else
24062306a36Sopenharmony_ci		u64_stats_init(&self->stats.tx.syncp);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return 0;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic inline bool aq_ring_dx_in_range(unsigned int h, unsigned int i,
24662306a36Sopenharmony_ci				       unsigned int t)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	return (h < t) ? ((h < i) && (i < t)) : ((h < i) || (i < t));
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_civoid aq_ring_update_queue_state(struct aq_ring_s *ring)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	if (aq_ring_avail_dx(ring) <= AQ_CFG_SKB_FRAGS_MAX)
25462306a36Sopenharmony_ci		aq_ring_queue_stop(ring);
25562306a36Sopenharmony_ci	else if (aq_ring_avail_dx(ring) > AQ_CFG_RESTART_DESC_THRES)
25662306a36Sopenharmony_ci		aq_ring_queue_wake(ring);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_civoid aq_ring_queue_wake(struct aq_ring_s *ring)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct net_device *ndev = aq_nic_get_ndev(ring->aq_nic);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (__netif_subqueue_stopped(ndev,
26462306a36Sopenharmony_ci				     AQ_NIC_RING2QMAP(ring->aq_nic,
26562306a36Sopenharmony_ci						      ring->idx))) {
26662306a36Sopenharmony_ci		netif_wake_subqueue(ndev,
26762306a36Sopenharmony_ci				    AQ_NIC_RING2QMAP(ring->aq_nic, ring->idx));
26862306a36Sopenharmony_ci		u64_stats_update_begin(&ring->stats.tx.syncp);
26962306a36Sopenharmony_ci		ring->stats.tx.queue_restarts++;
27062306a36Sopenharmony_ci		u64_stats_update_end(&ring->stats.tx.syncp);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_civoid aq_ring_queue_stop(struct aq_ring_s *ring)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct net_device *ndev = aq_nic_get_ndev(ring->aq_nic);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (!__netif_subqueue_stopped(ndev,
27962306a36Sopenharmony_ci				      AQ_NIC_RING2QMAP(ring->aq_nic,
28062306a36Sopenharmony_ci						       ring->idx)))
28162306a36Sopenharmony_ci		netif_stop_subqueue(ndev,
28262306a36Sopenharmony_ci				    AQ_NIC_RING2QMAP(ring->aq_nic, ring->idx));
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cibool aq_ring_tx_clean(struct aq_ring_s *self)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct device *dev = aq_nic_get_dev(self->aq_nic);
28862306a36Sopenharmony_ci	unsigned int budget;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	for (budget = AQ_CFG_TX_CLEAN_BUDGET;
29162306a36Sopenharmony_ci	     budget && self->sw_head != self->hw_head; budget--) {
29262306a36Sopenharmony_ci		struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (likely(buff->is_mapped)) {
29562306a36Sopenharmony_ci			if (unlikely(buff->is_sop)) {
29662306a36Sopenharmony_ci				if (!buff->is_eop &&
29762306a36Sopenharmony_ci				    buff->eop_index != 0xffffU &&
29862306a36Sopenharmony_ci				    (!aq_ring_dx_in_range(self->sw_head,
29962306a36Sopenharmony_ci						buff->eop_index,
30062306a36Sopenharmony_ci						self->hw_head)))
30162306a36Sopenharmony_ci					break;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci				dma_unmap_single(dev, buff->pa, buff->len,
30462306a36Sopenharmony_ci						 DMA_TO_DEVICE);
30562306a36Sopenharmony_ci			} else {
30662306a36Sopenharmony_ci				dma_unmap_page(dev, buff->pa, buff->len,
30762306a36Sopenharmony_ci					       DMA_TO_DEVICE);
30862306a36Sopenharmony_ci			}
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		if (likely(!buff->is_eop))
31262306a36Sopenharmony_ci			goto out;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		if (buff->skb) {
31562306a36Sopenharmony_ci			u64_stats_update_begin(&self->stats.tx.syncp);
31662306a36Sopenharmony_ci			++self->stats.tx.packets;
31762306a36Sopenharmony_ci			self->stats.tx.bytes += buff->skb->len;
31862306a36Sopenharmony_ci			u64_stats_update_end(&self->stats.tx.syncp);
31962306a36Sopenharmony_ci			dev_kfree_skb_any(buff->skb);
32062306a36Sopenharmony_ci		} else if (buff->xdpf) {
32162306a36Sopenharmony_ci			u64_stats_update_begin(&self->stats.tx.syncp);
32262306a36Sopenharmony_ci			++self->stats.tx.packets;
32362306a36Sopenharmony_ci			self->stats.tx.bytes += xdp_get_frame_len(buff->xdpf);
32462306a36Sopenharmony_ci			u64_stats_update_end(&self->stats.tx.syncp);
32562306a36Sopenharmony_ci			xdp_return_frame_rx_napi(buff->xdpf);
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ciout:
32962306a36Sopenharmony_ci		buff->skb = NULL;
33062306a36Sopenharmony_ci		buff->xdpf = NULL;
33162306a36Sopenharmony_ci		buff->pa = 0U;
33262306a36Sopenharmony_ci		buff->eop_index = 0xffffU;
33362306a36Sopenharmony_ci		self->sw_head = aq_ring_next_dx(self, self->sw_head);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return !!budget;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void aq_rx_checksum(struct aq_ring_s *self,
34062306a36Sopenharmony_ci			   struct aq_ring_buff_s *buff,
34162306a36Sopenharmony_ci			   struct sk_buff *skb)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	if (!(self->aq_nic->ndev->features & NETIF_F_RXCSUM))
34462306a36Sopenharmony_ci		return;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (unlikely(buff->is_cso_err)) {
34762306a36Sopenharmony_ci		u64_stats_update_begin(&self->stats.rx.syncp);
34862306a36Sopenharmony_ci		++self->stats.rx.errors;
34962306a36Sopenharmony_ci		u64_stats_update_end(&self->stats.rx.syncp);
35062306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_NONE;
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci	if (buff->is_ip_cso) {
35462306a36Sopenharmony_ci		__skb_incr_checksum_unnecessary(skb);
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_NONE;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (buff->is_udp_cso || buff->is_tcp_cso)
36062306a36Sopenharmony_ci		__skb_incr_checksum_unnecessary(skb);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ciint aq_xdp_xmit(struct net_device *dev, int num_frames,
36462306a36Sopenharmony_ci		struct xdp_frame **frames, u32 flags)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(dev);
36762306a36Sopenharmony_ci	unsigned int vec, i, drop = 0;
36862306a36Sopenharmony_ci	int cpu = smp_processor_id();
36962306a36Sopenharmony_ci	struct aq_nic_cfg_s *aq_cfg;
37062306a36Sopenharmony_ci	struct aq_ring_s *ring;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	aq_cfg = aq_nic_get_cfg(aq_nic);
37362306a36Sopenharmony_ci	vec = cpu % aq_cfg->vecs;
37462306a36Sopenharmony_ci	ring = aq_nic->aq_ring_tx[AQ_NIC_CFG_TCVEC2RING(aq_cfg, 0, vec)];
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	for (i = 0; i < num_frames; i++) {
37762306a36Sopenharmony_ci		struct xdp_frame *xdpf = frames[i];
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		if (aq_nic_xmit_xdpf(aq_nic, ring, xdpf) == NETDEV_TX_BUSY)
38062306a36Sopenharmony_ci			drop++;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return num_frames - drop;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic struct sk_buff *aq_xdp_build_skb(struct xdp_buff *xdp,
38762306a36Sopenharmony_ci					struct net_device *dev,
38862306a36Sopenharmony_ci					struct aq_ring_buff_s *buff)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct xdp_frame *xdpf;
39162306a36Sopenharmony_ci	struct sk_buff *skb;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	xdpf = xdp_convert_buff_to_frame(xdp);
39462306a36Sopenharmony_ci	if (unlikely(!xdpf))
39562306a36Sopenharmony_ci		return NULL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	skb = xdp_build_skb_from_frame(xdpf, dev);
39862306a36Sopenharmony_ci	if (!skb)
39962306a36Sopenharmony_ci		return NULL;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	aq_get_rxpages_xdp(buff, xdp);
40262306a36Sopenharmony_ci	return skb;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic struct sk_buff *aq_xdp_run_prog(struct aq_nic_s *aq_nic,
40662306a36Sopenharmony_ci				       struct xdp_buff *xdp,
40762306a36Sopenharmony_ci				       struct aq_ring_s *rx_ring,
40862306a36Sopenharmony_ci				       struct aq_ring_buff_s *buff)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	int result = NETDEV_TX_BUSY;
41162306a36Sopenharmony_ci	struct aq_ring_s *tx_ring;
41262306a36Sopenharmony_ci	struct xdp_frame *xdpf;
41362306a36Sopenharmony_ci	struct bpf_prog *prog;
41462306a36Sopenharmony_ci	u32 act = XDP_ABORTED;
41562306a36Sopenharmony_ci	struct sk_buff *skb;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	u64_stats_update_begin(&rx_ring->stats.rx.syncp);
41862306a36Sopenharmony_ci	++rx_ring->stats.rx.packets;
41962306a36Sopenharmony_ci	rx_ring->stats.rx.bytes += xdp_get_buff_len(xdp);
42062306a36Sopenharmony_ci	u64_stats_update_end(&rx_ring->stats.rx.syncp);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	prog = READ_ONCE(rx_ring->xdp_prog);
42362306a36Sopenharmony_ci	if (!prog)
42462306a36Sopenharmony_ci		return aq_xdp_build_skb(xdp, aq_nic->ndev, buff);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	prefetchw(xdp->data_hard_start); /* xdp_frame write */
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* single buffer XDP program, but packet is multi buffer, aborted */
42962306a36Sopenharmony_ci	if (xdp_buff_has_frags(xdp) && !prog->aux->xdp_has_frags)
43062306a36Sopenharmony_ci		goto out_aborted;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
43362306a36Sopenharmony_ci	switch (act) {
43462306a36Sopenharmony_ci	case XDP_PASS:
43562306a36Sopenharmony_ci		skb = aq_xdp_build_skb(xdp, aq_nic->ndev, buff);
43662306a36Sopenharmony_ci		if (!skb)
43762306a36Sopenharmony_ci			goto out_aborted;
43862306a36Sopenharmony_ci		u64_stats_update_begin(&rx_ring->stats.rx.syncp);
43962306a36Sopenharmony_ci		++rx_ring->stats.rx.xdp_pass;
44062306a36Sopenharmony_ci		u64_stats_update_end(&rx_ring->stats.rx.syncp);
44162306a36Sopenharmony_ci		return skb;
44262306a36Sopenharmony_ci	case XDP_TX:
44362306a36Sopenharmony_ci		xdpf = xdp_convert_buff_to_frame(xdp);
44462306a36Sopenharmony_ci		if (unlikely(!xdpf))
44562306a36Sopenharmony_ci			goto out_aborted;
44662306a36Sopenharmony_ci		tx_ring = aq_nic->aq_ring_tx[rx_ring->idx];
44762306a36Sopenharmony_ci		result = aq_nic_xmit_xdpf(aq_nic, tx_ring, xdpf);
44862306a36Sopenharmony_ci		if (result == NETDEV_TX_BUSY)
44962306a36Sopenharmony_ci			goto out_aborted;
45062306a36Sopenharmony_ci		u64_stats_update_begin(&rx_ring->stats.rx.syncp);
45162306a36Sopenharmony_ci		++rx_ring->stats.rx.xdp_tx;
45262306a36Sopenharmony_ci		u64_stats_update_end(&rx_ring->stats.rx.syncp);
45362306a36Sopenharmony_ci		aq_get_rxpages_xdp(buff, xdp);
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case XDP_REDIRECT:
45662306a36Sopenharmony_ci		if (xdp_do_redirect(aq_nic->ndev, xdp, prog) < 0)
45762306a36Sopenharmony_ci			goto out_aborted;
45862306a36Sopenharmony_ci		xdp_do_flush();
45962306a36Sopenharmony_ci		u64_stats_update_begin(&rx_ring->stats.rx.syncp);
46062306a36Sopenharmony_ci		++rx_ring->stats.rx.xdp_redirect;
46162306a36Sopenharmony_ci		u64_stats_update_end(&rx_ring->stats.rx.syncp);
46262306a36Sopenharmony_ci		aq_get_rxpages_xdp(buff, xdp);
46362306a36Sopenharmony_ci		break;
46462306a36Sopenharmony_ci	default:
46562306a36Sopenharmony_ci		fallthrough;
46662306a36Sopenharmony_ci	case XDP_ABORTED:
46762306a36Sopenharmony_ciout_aborted:
46862306a36Sopenharmony_ci		u64_stats_update_begin(&rx_ring->stats.rx.syncp);
46962306a36Sopenharmony_ci		++rx_ring->stats.rx.xdp_aborted;
47062306a36Sopenharmony_ci		u64_stats_update_end(&rx_ring->stats.rx.syncp);
47162306a36Sopenharmony_ci		trace_xdp_exception(aq_nic->ndev, prog, act);
47262306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(aq_nic->ndev, prog, act);
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	case XDP_DROP:
47562306a36Sopenharmony_ci		u64_stats_update_begin(&rx_ring->stats.rx.syncp);
47662306a36Sopenharmony_ci		++rx_ring->stats.rx.xdp_drop;
47762306a36Sopenharmony_ci		u64_stats_update_end(&rx_ring->stats.rx.syncp);
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return ERR_PTR(-result);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic bool aq_add_rx_fragment(struct device *dev,
48562306a36Sopenharmony_ci			       struct aq_ring_s *ring,
48662306a36Sopenharmony_ci			       struct aq_ring_buff_s *buff,
48762306a36Sopenharmony_ci			       struct xdp_buff *xdp)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
49062306a36Sopenharmony_ci	struct aq_ring_buff_s *buff_ = buff;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	memset(sinfo, 0, sizeof(*sinfo));
49362306a36Sopenharmony_ci	do {
49462306a36Sopenharmony_ci		skb_frag_t *frag;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS))
49762306a36Sopenharmony_ci			return true;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		frag = &sinfo->frags[sinfo->nr_frags++];
50062306a36Sopenharmony_ci		buff_ = &ring->buff_ring[buff_->next];
50162306a36Sopenharmony_ci		dma_sync_single_range_for_cpu(dev,
50262306a36Sopenharmony_ci					      buff_->rxdata.daddr,
50362306a36Sopenharmony_ci					      buff_->rxdata.pg_off,
50462306a36Sopenharmony_ci					      buff_->len,
50562306a36Sopenharmony_ci					      DMA_FROM_DEVICE);
50662306a36Sopenharmony_ci		sinfo->xdp_frags_size += buff_->len;
50762306a36Sopenharmony_ci		skb_frag_fill_page_desc(frag, buff_->rxdata.page,
50862306a36Sopenharmony_ci					buff_->rxdata.pg_off,
50962306a36Sopenharmony_ci					buff_->len);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		buff_->is_cleaned = 1;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		buff->is_ip_cso &= buff_->is_ip_cso;
51462306a36Sopenharmony_ci		buff->is_udp_cso &= buff_->is_udp_cso;
51562306a36Sopenharmony_ci		buff->is_tcp_cso &= buff_->is_tcp_cso;
51662306a36Sopenharmony_ci		buff->is_cso_err |= buff_->is_cso_err;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		if (page_is_pfmemalloc(buff_->rxdata.page))
51962306a36Sopenharmony_ci			xdp_buff_set_frag_pfmemalloc(xdp);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	} while (!buff_->is_eop);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	xdp_buff_set_frags_flag(xdp);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return false;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic int __aq_ring_rx_clean(struct aq_ring_s *self, struct napi_struct *napi,
52962306a36Sopenharmony_ci			      int *work_done, int budget)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct net_device *ndev = aq_nic_get_ndev(self->aq_nic);
53262306a36Sopenharmony_ci	int err = 0;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	for (; (self->sw_head != self->hw_head) && budget;
53562306a36Sopenharmony_ci		self->sw_head = aq_ring_next_dx(self, self->sw_head),
53662306a36Sopenharmony_ci		--budget, ++(*work_done)) {
53762306a36Sopenharmony_ci		struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
53862306a36Sopenharmony_ci		bool is_ptp_ring = aq_ptp_ring(self->aq_nic, self);
53962306a36Sopenharmony_ci		struct aq_ring_buff_s *buff_ = NULL;
54062306a36Sopenharmony_ci		struct sk_buff *skb = NULL;
54162306a36Sopenharmony_ci		unsigned int next_ = 0U;
54262306a36Sopenharmony_ci		unsigned int i = 0U;
54362306a36Sopenharmony_ci		u16 hdr_len;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		if (buff->is_cleaned)
54662306a36Sopenharmony_ci			continue;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		if (!buff->is_eop) {
54962306a36Sopenharmony_ci			unsigned int frag_cnt = 0U;
55062306a36Sopenharmony_ci			buff_ = buff;
55162306a36Sopenharmony_ci			do {
55262306a36Sopenharmony_ci				bool is_rsc_completed = true;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci				if (buff_->next >= self->size) {
55562306a36Sopenharmony_ci					err = -EIO;
55662306a36Sopenharmony_ci					goto err_exit;
55762306a36Sopenharmony_ci				}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci				frag_cnt++;
56062306a36Sopenharmony_ci				next_ = buff_->next,
56162306a36Sopenharmony_ci				buff_ = &self->buff_ring[next_];
56262306a36Sopenharmony_ci				is_rsc_completed =
56362306a36Sopenharmony_ci					aq_ring_dx_in_range(self->sw_head,
56462306a36Sopenharmony_ci							    next_,
56562306a36Sopenharmony_ci							    self->hw_head);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci				if (unlikely(!is_rsc_completed) ||
56862306a36Sopenharmony_ci						frag_cnt > MAX_SKB_FRAGS) {
56962306a36Sopenharmony_ci					err = 0;
57062306a36Sopenharmony_ci					goto err_exit;
57162306a36Sopenharmony_ci				}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci				buff->is_error |= buff_->is_error;
57462306a36Sopenharmony_ci				buff->is_cso_err |= buff_->is_cso_err;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci			} while (!buff_->is_eop);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci			if (buff->is_error ||
57962306a36Sopenharmony_ci			    (buff->is_lro && buff->is_cso_err)) {
58062306a36Sopenharmony_ci				buff_ = buff;
58162306a36Sopenharmony_ci				do {
58262306a36Sopenharmony_ci					if (buff_->next >= self->size) {
58362306a36Sopenharmony_ci						err = -EIO;
58462306a36Sopenharmony_ci						goto err_exit;
58562306a36Sopenharmony_ci					}
58662306a36Sopenharmony_ci					next_ = buff_->next,
58762306a36Sopenharmony_ci					buff_ = &self->buff_ring[next_];
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci					buff_->is_cleaned = true;
59062306a36Sopenharmony_ci				} while (!buff_->is_eop);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci				u64_stats_update_begin(&self->stats.rx.syncp);
59362306a36Sopenharmony_ci				++self->stats.rx.errors;
59462306a36Sopenharmony_ci				u64_stats_update_end(&self->stats.rx.syncp);
59562306a36Sopenharmony_ci				continue;
59662306a36Sopenharmony_ci			}
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		if (buff->is_error) {
60062306a36Sopenharmony_ci			u64_stats_update_begin(&self->stats.rx.syncp);
60162306a36Sopenharmony_ci			++self->stats.rx.errors;
60262306a36Sopenharmony_ci			u64_stats_update_end(&self->stats.rx.syncp);
60362306a36Sopenharmony_ci			continue;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		dma_sync_single_range_for_cpu(aq_nic_get_dev(self->aq_nic),
60762306a36Sopenharmony_ci					      buff->rxdata.daddr,
60862306a36Sopenharmony_ci					      buff->rxdata.pg_off,
60962306a36Sopenharmony_ci					      buff->len, DMA_FROM_DEVICE);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		skb = napi_alloc_skb(napi, AQ_CFG_RX_HDR_SIZE);
61262306a36Sopenharmony_ci		if (unlikely(!skb)) {
61362306a36Sopenharmony_ci			u64_stats_update_begin(&self->stats.rx.syncp);
61462306a36Sopenharmony_ci			self->stats.rx.skb_alloc_fails++;
61562306a36Sopenharmony_ci			u64_stats_update_end(&self->stats.rx.syncp);
61662306a36Sopenharmony_ci			err = -ENOMEM;
61762306a36Sopenharmony_ci			goto err_exit;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci		if (is_ptp_ring)
62062306a36Sopenharmony_ci			buff->len -=
62162306a36Sopenharmony_ci				aq_ptp_extract_ts(self->aq_nic, skb_hwtstamps(skb),
62262306a36Sopenharmony_ci						  aq_buf_vaddr(&buff->rxdata),
62362306a36Sopenharmony_ci						  buff->len);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		hdr_len = buff->len;
62662306a36Sopenharmony_ci		if (hdr_len > AQ_CFG_RX_HDR_SIZE)
62762306a36Sopenharmony_ci			hdr_len = eth_get_headlen(skb->dev,
62862306a36Sopenharmony_ci						  aq_buf_vaddr(&buff->rxdata),
62962306a36Sopenharmony_ci						  AQ_CFG_RX_HDR_SIZE);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		memcpy(__skb_put(skb, hdr_len), aq_buf_vaddr(&buff->rxdata),
63262306a36Sopenharmony_ci		       ALIGN(hdr_len, sizeof(long)));
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		if (buff->len - hdr_len > 0) {
63562306a36Sopenharmony_ci			skb_add_rx_frag(skb, i++, buff->rxdata.page,
63662306a36Sopenharmony_ci					buff->rxdata.pg_off + hdr_len,
63762306a36Sopenharmony_ci					buff->len - hdr_len,
63862306a36Sopenharmony_ci					self->frame_max);
63962306a36Sopenharmony_ci			page_ref_inc(buff->rxdata.page);
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		if (!buff->is_eop) {
64362306a36Sopenharmony_ci			buff_ = buff;
64462306a36Sopenharmony_ci			do {
64562306a36Sopenharmony_ci				next_ = buff_->next;
64662306a36Sopenharmony_ci				buff_ = &self->buff_ring[next_];
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci				dma_sync_single_range_for_cpu(aq_nic_get_dev(self->aq_nic),
64962306a36Sopenharmony_ci							      buff_->rxdata.daddr,
65062306a36Sopenharmony_ci							      buff_->rxdata.pg_off,
65162306a36Sopenharmony_ci							      buff_->len,
65262306a36Sopenharmony_ci							      DMA_FROM_DEVICE);
65362306a36Sopenharmony_ci				skb_add_rx_frag(skb, i++,
65462306a36Sopenharmony_ci						buff_->rxdata.page,
65562306a36Sopenharmony_ci						buff_->rxdata.pg_off,
65662306a36Sopenharmony_ci						buff_->len,
65762306a36Sopenharmony_ci						self->frame_max);
65862306a36Sopenharmony_ci				page_ref_inc(buff_->rxdata.page);
65962306a36Sopenharmony_ci				buff_->is_cleaned = 1;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci				buff->is_ip_cso &= buff_->is_ip_cso;
66262306a36Sopenharmony_ci				buff->is_udp_cso &= buff_->is_udp_cso;
66362306a36Sopenharmony_ci				buff->is_tcp_cso &= buff_->is_tcp_cso;
66462306a36Sopenharmony_ci				buff->is_cso_err |= buff_->is_cso_err;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci			} while (!buff_->is_eop);
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		if (buff->is_vlan)
67062306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
67162306a36Sopenharmony_ci					       buff->vlan_rx_tag);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, ndev);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		aq_rx_checksum(self, buff, skb);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		skb_set_hash(skb, buff->rss_hash,
67862306a36Sopenharmony_ci			     buff->is_hash_l4 ? PKT_HASH_TYPE_L4 :
67962306a36Sopenharmony_ci			     PKT_HASH_TYPE_NONE);
68062306a36Sopenharmony_ci		/* Send all PTP traffic to 0 queue */
68162306a36Sopenharmony_ci		skb_record_rx_queue(skb,
68262306a36Sopenharmony_ci				    is_ptp_ring ? 0
68362306a36Sopenharmony_ci						: AQ_NIC_RING2QMAP(self->aq_nic,
68462306a36Sopenharmony_ci								   self->idx));
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		u64_stats_update_begin(&self->stats.rx.syncp);
68762306a36Sopenharmony_ci		++self->stats.rx.packets;
68862306a36Sopenharmony_ci		self->stats.rx.bytes += skb->len;
68962306a36Sopenharmony_ci		u64_stats_update_end(&self->stats.rx.syncp);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cierr_exit:
69562306a36Sopenharmony_ci	return err;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int __aq_ring_xdp_clean(struct aq_ring_s *rx_ring,
69962306a36Sopenharmony_ci			       struct napi_struct *napi, int *work_done,
70062306a36Sopenharmony_ci			       int budget)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	int frame_sz = rx_ring->page_offset + rx_ring->frame_max +
70362306a36Sopenharmony_ci		       rx_ring->tail_size;
70462306a36Sopenharmony_ci	struct aq_nic_s *aq_nic = rx_ring->aq_nic;
70562306a36Sopenharmony_ci	bool is_rsc_completed = true;
70662306a36Sopenharmony_ci	struct device *dev;
70762306a36Sopenharmony_ci	int err = 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	dev = aq_nic_get_dev(aq_nic);
71062306a36Sopenharmony_ci	for (; (rx_ring->sw_head != rx_ring->hw_head) && budget;
71162306a36Sopenharmony_ci		rx_ring->sw_head = aq_ring_next_dx(rx_ring, rx_ring->sw_head),
71262306a36Sopenharmony_ci		--budget, ++(*work_done)) {
71362306a36Sopenharmony_ci		struct aq_ring_buff_s *buff = &rx_ring->buff_ring[rx_ring->sw_head];
71462306a36Sopenharmony_ci		bool is_ptp_ring = aq_ptp_ring(rx_ring->aq_nic, rx_ring);
71562306a36Sopenharmony_ci		struct aq_ring_buff_s *buff_ = NULL;
71662306a36Sopenharmony_ci		u16 ptp_hwtstamp_len = 0;
71762306a36Sopenharmony_ci		struct skb_shared_hwtstamps shhwtstamps;
71862306a36Sopenharmony_ci		struct sk_buff *skb = NULL;
71962306a36Sopenharmony_ci		unsigned int next_ = 0U;
72062306a36Sopenharmony_ci		struct xdp_buff xdp;
72162306a36Sopenharmony_ci		void *hard_start;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if (buff->is_cleaned)
72462306a36Sopenharmony_ci			continue;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		if (!buff->is_eop) {
72762306a36Sopenharmony_ci			buff_ = buff;
72862306a36Sopenharmony_ci			do {
72962306a36Sopenharmony_ci				if (buff_->next >= rx_ring->size) {
73062306a36Sopenharmony_ci					err = -EIO;
73162306a36Sopenharmony_ci					goto err_exit;
73262306a36Sopenharmony_ci				}
73362306a36Sopenharmony_ci				next_ = buff_->next;
73462306a36Sopenharmony_ci				buff_ = &rx_ring->buff_ring[next_];
73562306a36Sopenharmony_ci				is_rsc_completed =
73662306a36Sopenharmony_ci					aq_ring_dx_in_range(rx_ring->sw_head,
73762306a36Sopenharmony_ci							    next_,
73862306a36Sopenharmony_ci							    rx_ring->hw_head);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci				if (unlikely(!is_rsc_completed))
74162306a36Sopenharmony_ci					break;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci				buff->is_error |= buff_->is_error;
74462306a36Sopenharmony_ci				buff->is_cso_err |= buff_->is_cso_err;
74562306a36Sopenharmony_ci			} while (!buff_->is_eop);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci			if (!is_rsc_completed) {
74862306a36Sopenharmony_ci				err = 0;
74962306a36Sopenharmony_ci				goto err_exit;
75062306a36Sopenharmony_ci			}
75162306a36Sopenharmony_ci			if (buff->is_error ||
75262306a36Sopenharmony_ci			    (buff->is_lro && buff->is_cso_err)) {
75362306a36Sopenharmony_ci				buff_ = buff;
75462306a36Sopenharmony_ci				do {
75562306a36Sopenharmony_ci					if (buff_->next >= rx_ring->size) {
75662306a36Sopenharmony_ci						err = -EIO;
75762306a36Sopenharmony_ci						goto err_exit;
75862306a36Sopenharmony_ci					}
75962306a36Sopenharmony_ci					next_ = buff_->next;
76062306a36Sopenharmony_ci					buff_ = &rx_ring->buff_ring[next_];
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci					buff_->is_cleaned = true;
76362306a36Sopenharmony_ci				} while (!buff_->is_eop);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci				u64_stats_update_begin(&rx_ring->stats.rx.syncp);
76662306a36Sopenharmony_ci				++rx_ring->stats.rx.errors;
76762306a36Sopenharmony_ci				u64_stats_update_end(&rx_ring->stats.rx.syncp);
76862306a36Sopenharmony_ci				continue;
76962306a36Sopenharmony_ci			}
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		if (buff->is_error) {
77362306a36Sopenharmony_ci			u64_stats_update_begin(&rx_ring->stats.rx.syncp);
77462306a36Sopenharmony_ci			++rx_ring->stats.rx.errors;
77562306a36Sopenharmony_ci			u64_stats_update_end(&rx_ring->stats.rx.syncp);
77662306a36Sopenharmony_ci			continue;
77762306a36Sopenharmony_ci		}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		dma_sync_single_range_for_cpu(dev,
78062306a36Sopenharmony_ci					      buff->rxdata.daddr,
78162306a36Sopenharmony_ci					      buff->rxdata.pg_off,
78262306a36Sopenharmony_ci					      buff->len, DMA_FROM_DEVICE);
78362306a36Sopenharmony_ci		hard_start = page_address(buff->rxdata.page) +
78462306a36Sopenharmony_ci			     buff->rxdata.pg_off - rx_ring->page_offset;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		if (is_ptp_ring) {
78762306a36Sopenharmony_ci			ptp_hwtstamp_len = aq_ptp_extract_ts(rx_ring->aq_nic, &shhwtstamps,
78862306a36Sopenharmony_ci							     aq_buf_vaddr(&buff->rxdata),
78962306a36Sopenharmony_ci							     buff->len);
79062306a36Sopenharmony_ci			buff->len -= ptp_hwtstamp_len;
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
79462306a36Sopenharmony_ci		xdp_prepare_buff(&xdp, hard_start, rx_ring->page_offset,
79562306a36Sopenharmony_ci				 buff->len, false);
79662306a36Sopenharmony_ci		if (!buff->is_eop) {
79762306a36Sopenharmony_ci			if (aq_add_rx_fragment(dev, rx_ring, buff, &xdp)) {
79862306a36Sopenharmony_ci				u64_stats_update_begin(&rx_ring->stats.rx.syncp);
79962306a36Sopenharmony_ci				++rx_ring->stats.rx.packets;
80062306a36Sopenharmony_ci				rx_ring->stats.rx.bytes += xdp_get_buff_len(&xdp);
80162306a36Sopenharmony_ci				++rx_ring->stats.rx.xdp_aborted;
80262306a36Sopenharmony_ci				u64_stats_update_end(&rx_ring->stats.rx.syncp);
80362306a36Sopenharmony_ci				continue;
80462306a36Sopenharmony_ci			}
80562306a36Sopenharmony_ci		}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		skb = aq_xdp_run_prog(aq_nic, &xdp, rx_ring, buff);
80862306a36Sopenharmony_ci		if (IS_ERR(skb) || !skb)
80962306a36Sopenharmony_ci			continue;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		if (ptp_hwtstamp_len > 0)
81262306a36Sopenharmony_ci			*skb_hwtstamps(skb) = shhwtstamps;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		if (buff->is_vlan)
81562306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
81662306a36Sopenharmony_ci					       buff->vlan_rx_tag);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		aq_rx_checksum(rx_ring, buff, skb);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		skb_set_hash(skb, buff->rss_hash,
82162306a36Sopenharmony_ci			     buff->is_hash_l4 ? PKT_HASH_TYPE_L4 :
82262306a36Sopenharmony_ci			     PKT_HASH_TYPE_NONE);
82362306a36Sopenharmony_ci		/* Send all PTP traffic to 0 queue */
82462306a36Sopenharmony_ci		skb_record_rx_queue(skb,
82562306a36Sopenharmony_ci				    is_ptp_ring ? 0
82662306a36Sopenharmony_ci						: AQ_NIC_RING2QMAP(rx_ring->aq_nic,
82762306a36Sopenharmony_ci								   rx_ring->idx));
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cierr_exit:
83362306a36Sopenharmony_ci	return err;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ciint aq_ring_rx_clean(struct aq_ring_s *self,
83762306a36Sopenharmony_ci		     struct napi_struct *napi,
83862306a36Sopenharmony_ci		     int *work_done,
83962306a36Sopenharmony_ci		     int budget)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	if (static_branch_unlikely(&aq_xdp_locking_key))
84262306a36Sopenharmony_ci		return __aq_ring_xdp_clean(self, napi, work_done, budget);
84362306a36Sopenharmony_ci	else
84462306a36Sopenharmony_ci		return __aq_ring_rx_clean(self, napi, work_done, budget);
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_civoid aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
85062306a36Sopenharmony_ci	while (self->sw_head != self->hw_head) {
85162306a36Sopenharmony_ci		u64 ns;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		aq_nic->aq_hw_ops->extract_hwts(aq_nic->aq_hw,
85462306a36Sopenharmony_ci						self->dx_ring +
85562306a36Sopenharmony_ci						(self->sw_head * self->dx_size),
85662306a36Sopenharmony_ci						self->dx_size, &ns);
85762306a36Sopenharmony_ci		aq_ptp_tx_hwtstamp(aq_nic, ns);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		self->sw_head = aq_ring_next_dx(self, self->sw_head);
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci#endif
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ciint aq_ring_rx_fill(struct aq_ring_s *self)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct aq_ring_buff_s *buff = NULL;
86762306a36Sopenharmony_ci	int err = 0;
86862306a36Sopenharmony_ci	int i = 0;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (aq_ring_avail_dx(self) < min_t(unsigned int, AQ_CFG_RX_REFILL_THRES,
87162306a36Sopenharmony_ci					   self->size / 2))
87262306a36Sopenharmony_ci		return err;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	for (i = aq_ring_avail_dx(self); i--;
87562306a36Sopenharmony_ci		self->sw_tail = aq_ring_next_dx(self, self->sw_tail)) {
87662306a36Sopenharmony_ci		buff = &self->buff_ring[self->sw_tail];
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		buff->flags = 0U;
87962306a36Sopenharmony_ci		buff->len = self->frame_max;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		err = aq_get_rxpages(self, buff);
88262306a36Sopenharmony_ci		if (err)
88362306a36Sopenharmony_ci			goto err_exit;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		buff->pa = aq_buf_daddr(&buff->rxdata);
88662306a36Sopenharmony_ci		buff = NULL;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cierr_exit:
89062306a36Sopenharmony_ci	return err;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_civoid aq_ring_rx_deinit(struct aq_ring_s *self)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	if (!self)
89662306a36Sopenharmony_ci		return;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	for (; self->sw_head != self->sw_tail;
89962306a36Sopenharmony_ci		self->sw_head = aq_ring_next_dx(self, self->sw_head)) {
90062306a36Sopenharmony_ci		struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci		aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic));
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_civoid aq_ring_free(struct aq_ring_s *self)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	if (!self)
90962306a36Sopenharmony_ci		return;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	kfree(self->buff_ring);
91262306a36Sopenharmony_ci	self->buff_ring = NULL;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (self->dx_ring) {
91562306a36Sopenharmony_ci		dma_free_coherent(aq_nic_get_dev(self->aq_nic),
91662306a36Sopenharmony_ci				  self->size * self->dx_size, self->dx_ring,
91762306a36Sopenharmony_ci				  self->dx_ring_pa);
91862306a36Sopenharmony_ci		self->dx_ring = NULL;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_civoid aq_ring_hwts_rx_free(struct aq_ring_s *self)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	if (!self)
92562306a36Sopenharmony_ci		return;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (self->dx_ring) {
92862306a36Sopenharmony_ci		dma_free_coherent(aq_nic_get_dev(self->aq_nic),
92962306a36Sopenharmony_ci				  self->size * self->dx_size + AQ_CFG_RXDS_DEF,
93062306a36Sopenharmony_ci				  self->dx_ring, self->dx_ring_pa);
93162306a36Sopenharmony_ci		self->dx_ring = NULL;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ciunsigned int aq_ring_fill_stats_data(struct aq_ring_s *self, u64 *data)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	unsigned int count;
93862306a36Sopenharmony_ci	unsigned int start;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (self->ring_type == ATL_RING_RX) {
94162306a36Sopenharmony_ci		/* This data should mimic aq_ethtool_queue_rx_stat_names structure */
94262306a36Sopenharmony_ci		do {
94362306a36Sopenharmony_ci			count = 0;
94462306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&self->stats.rx.syncp);
94562306a36Sopenharmony_ci			data[count] = self->stats.rx.packets;
94662306a36Sopenharmony_ci			data[++count] = self->stats.rx.jumbo_packets;
94762306a36Sopenharmony_ci			data[++count] = self->stats.rx.lro_packets;
94862306a36Sopenharmony_ci			data[++count] = self->stats.rx.errors;
94962306a36Sopenharmony_ci			data[++count] = self->stats.rx.alloc_fails;
95062306a36Sopenharmony_ci			data[++count] = self->stats.rx.skb_alloc_fails;
95162306a36Sopenharmony_ci			data[++count] = self->stats.rx.polls;
95262306a36Sopenharmony_ci			data[++count] = self->stats.rx.pg_flips;
95362306a36Sopenharmony_ci			data[++count] = self->stats.rx.pg_reuses;
95462306a36Sopenharmony_ci			data[++count] = self->stats.rx.pg_losts;
95562306a36Sopenharmony_ci			data[++count] = self->stats.rx.xdp_aborted;
95662306a36Sopenharmony_ci			data[++count] = self->stats.rx.xdp_drop;
95762306a36Sopenharmony_ci			data[++count] = self->stats.rx.xdp_pass;
95862306a36Sopenharmony_ci			data[++count] = self->stats.rx.xdp_tx;
95962306a36Sopenharmony_ci			data[++count] = self->stats.rx.xdp_invalid;
96062306a36Sopenharmony_ci			data[++count] = self->stats.rx.xdp_redirect;
96162306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&self->stats.rx.syncp, start));
96262306a36Sopenharmony_ci	} else {
96362306a36Sopenharmony_ci		/* This data should mimic aq_ethtool_queue_tx_stat_names structure */
96462306a36Sopenharmony_ci		do {
96562306a36Sopenharmony_ci			count = 0;
96662306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&self->stats.tx.syncp);
96762306a36Sopenharmony_ci			data[count] = self->stats.tx.packets;
96862306a36Sopenharmony_ci			data[++count] = self->stats.tx.queue_restarts;
96962306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&self->stats.tx.syncp, start));
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return ++count;
97362306a36Sopenharmony_ci}
974