162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2015-2019 Netronome Systems, Inc. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bpf_trace.h>
562306a36Sopenharmony_ci#include <linux/netdevice.h>
662306a36Sopenharmony_ci#include <linux/bitfield.h>
762306a36Sopenharmony_ci#include <net/xfrm.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "../nfp_app.h"
1062306a36Sopenharmony_ci#include "../nfp_net.h"
1162306a36Sopenharmony_ci#include "../nfp_net_dp.h"
1262306a36Sopenharmony_ci#include "../nfp_net_xsk.h"
1362306a36Sopenharmony_ci#include "../crypto/crypto.h"
1462306a36Sopenharmony_ci#include "../crypto/fw.h"
1562306a36Sopenharmony_ci#include "nfd3.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Transmit processing
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * One queue controller peripheral queue is used for transmit.  The
2062306a36Sopenharmony_ci * driver en-queues packets for transmit by advancing the write
2162306a36Sopenharmony_ci * pointer.  The device indicates that packets have transmitted by
2262306a36Sopenharmony_ci * advancing the read pointer.  The driver maintains a local copy of
2362306a36Sopenharmony_ci * the read and write pointer in @struct nfp_net_tx_ring.  The driver
2462306a36Sopenharmony_ci * keeps @wr_p in sync with the queue controller write pointer and can
2562306a36Sopenharmony_ci * determine how many packets have been transmitted by comparing its
2662306a36Sopenharmony_ci * copy of the read pointer @rd_p with the read pointer maintained by
2762306a36Sopenharmony_ci * the queue controller peripheral.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Wrappers for deciding when to stop and restart TX queues */
3162306a36Sopenharmony_cistatic int nfp_nfd3_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int nfp_nfd3_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS + 1);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * nfp_nfd3_tx_ring_stop() - stop tx ring
4362306a36Sopenharmony_ci * @nd_q:    netdev queue
4462306a36Sopenharmony_ci * @tx_ring: driver tx queue structure
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * Safely stop TX ring.  Remember that while we are running .start_xmit()
4762306a36Sopenharmony_ci * someone else may be cleaning the TX ring completions so we need to be
4862306a36Sopenharmony_ci * extra careful here.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic void
5162306a36Sopenharmony_cinfp_nfd3_tx_ring_stop(struct netdev_queue *nd_q,
5262306a36Sopenharmony_ci		      struct nfp_net_tx_ring *tx_ring)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	netif_tx_stop_queue(nd_q);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* We can race with the TX completion out of NAPI so recheck */
5762306a36Sopenharmony_ci	smp_mb();
5862306a36Sopenharmony_ci	if (unlikely(nfp_nfd3_tx_ring_should_wake(tx_ring)))
5962306a36Sopenharmony_ci		netif_tx_start_queue(nd_q);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * nfp_nfd3_tx_tso() - Set up Tx descriptor for LSO
6462306a36Sopenharmony_ci * @r_vec: per-ring structure
6562306a36Sopenharmony_ci * @txbuf: Pointer to driver soft TX descriptor
6662306a36Sopenharmony_ci * @txd: Pointer to HW TX descriptor
6762306a36Sopenharmony_ci * @skb: Pointer to SKB
6862306a36Sopenharmony_ci * @md_bytes: Prepend length
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * Set up Tx descriptor for LSO, do nothing for non-LSO skbs.
7162306a36Sopenharmony_ci * Return error on packet header greater than maximum supported LSO header size.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic void
7462306a36Sopenharmony_cinfp_nfd3_tx_tso(struct nfp_net_r_vector *r_vec, struct nfp_nfd3_tx_buf *txbuf,
7562306a36Sopenharmony_ci		struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb, u32 md_bytes)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	u32 l3_offset, l4_offset, hdrlen;
7862306a36Sopenharmony_ci	u16 mss;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!skb_is_gso(skb))
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!skb->encapsulation) {
8462306a36Sopenharmony_ci		l3_offset = skb_network_offset(skb);
8562306a36Sopenharmony_ci		l4_offset = skb_transport_offset(skb);
8662306a36Sopenharmony_ci		hdrlen = skb_tcp_all_headers(skb);
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		l3_offset = skb_inner_network_offset(skb);
8962306a36Sopenharmony_ci		l4_offset = skb_inner_transport_offset(skb);
9062306a36Sopenharmony_ci		hdrlen = skb_inner_tcp_all_headers(skb);
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	txbuf->pkt_cnt = skb_shinfo(skb)->gso_segs;
9462306a36Sopenharmony_ci	txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	mss = skb_shinfo(skb)->gso_size & NFD3_DESC_TX_MSS_MASK;
9762306a36Sopenharmony_ci	txd->l3_offset = l3_offset - md_bytes;
9862306a36Sopenharmony_ci	txd->l4_offset = l4_offset - md_bytes;
9962306a36Sopenharmony_ci	txd->lso_hdrlen = hdrlen - md_bytes;
10062306a36Sopenharmony_ci	txd->mss = cpu_to_le16(mss);
10162306a36Sopenharmony_ci	txd->flags |= NFD3_DESC_TX_LSO;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->tx_sync);
10462306a36Sopenharmony_ci	r_vec->tx_lso++;
10562306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->tx_sync);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/**
10962306a36Sopenharmony_ci * nfp_nfd3_tx_csum() - Set TX CSUM offload flags in TX descriptor
11062306a36Sopenharmony_ci * @dp:  NFP Net data path struct
11162306a36Sopenharmony_ci * @r_vec: per-ring structure
11262306a36Sopenharmony_ci * @txbuf: Pointer to driver soft TX descriptor
11362306a36Sopenharmony_ci * @txd: Pointer to TX descriptor
11462306a36Sopenharmony_ci * @skb: Pointer to SKB
11562306a36Sopenharmony_ci *
11662306a36Sopenharmony_ci * This function sets the TX checksum flags in the TX descriptor based
11762306a36Sopenharmony_ci * on the configuration and the protocol of the packet to be transmitted.
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_cistatic void
12062306a36Sopenharmony_cinfp_nfd3_tx_csum(struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec,
12162306a36Sopenharmony_ci		 struct nfp_nfd3_tx_buf *txbuf, struct nfp_nfd3_tx_desc *txd,
12262306a36Sopenharmony_ci		 struct sk_buff *skb)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct ipv6hdr *ipv6h;
12562306a36Sopenharmony_ci	struct iphdr *iph;
12662306a36Sopenharmony_ci	u8 l4_hdr;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (!(dp->ctrl & NFP_NET_CFG_CTRL_TXCSUM))
12962306a36Sopenharmony_ci		return;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (skb->ip_summed != CHECKSUM_PARTIAL)
13262306a36Sopenharmony_ci		return;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	txd->flags |= NFD3_DESC_TX_CSUM;
13562306a36Sopenharmony_ci	if (skb->encapsulation)
13662306a36Sopenharmony_ci		txd->flags |= NFD3_DESC_TX_ENCAP;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
13962306a36Sopenharmony_ci	ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (iph->version == 4) {
14262306a36Sopenharmony_ci		txd->flags |= NFD3_DESC_TX_IP4_CSUM;
14362306a36Sopenharmony_ci		l4_hdr = iph->protocol;
14462306a36Sopenharmony_ci	} else if (ipv6h->version == 6) {
14562306a36Sopenharmony_ci		l4_hdr = ipv6h->nexthdr;
14662306a36Sopenharmony_ci	} else {
14762306a36Sopenharmony_ci		nn_dp_warn(dp, "partial checksum but ipv=%x!\n", iph->version);
14862306a36Sopenharmony_ci		return;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	switch (l4_hdr) {
15262306a36Sopenharmony_ci	case IPPROTO_TCP:
15362306a36Sopenharmony_ci		txd->flags |= NFD3_DESC_TX_TCP_CSUM;
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	case IPPROTO_UDP:
15662306a36Sopenharmony_ci		txd->flags |= NFD3_DESC_TX_UDP_CSUM;
15762306a36Sopenharmony_ci		break;
15862306a36Sopenharmony_ci	default:
15962306a36Sopenharmony_ci		nn_dp_warn(dp, "partial checksum but l4 proto=%x!\n", l4_hdr);
16062306a36Sopenharmony_ci		return;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->tx_sync);
16462306a36Sopenharmony_ci	if (skb->encapsulation)
16562306a36Sopenharmony_ci		r_vec->hw_csum_tx_inner += txbuf->pkt_cnt;
16662306a36Sopenharmony_ci	else
16762306a36Sopenharmony_ci		r_vec->hw_csum_tx += txbuf->pkt_cnt;
16862306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->tx_sync);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int nfp_nfd3_prep_tx_meta(struct nfp_net_dp *dp, struct sk_buff *skb,
17262306a36Sopenharmony_ci				 u64 tls_handle, bool *ipsec)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct metadata_dst *md_dst = skb_metadata_dst(skb);
17562306a36Sopenharmony_ci	struct nfp_ipsec_offload offload_info;
17662306a36Sopenharmony_ci	unsigned char *data;
17762306a36Sopenharmony_ci	bool vlan_insert;
17862306a36Sopenharmony_ci	u32 meta_id = 0;
17962306a36Sopenharmony_ci	int md_bytes;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#ifdef CONFIG_NFP_NET_IPSEC
18262306a36Sopenharmony_ci	if (xfrm_offload(skb))
18362306a36Sopenharmony_ci		*ipsec = nfp_net_ipsec_tx_prep(dp, skb, &offload_info);
18462306a36Sopenharmony_ci#endif
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (unlikely(md_dst && md_dst->type != METADATA_HW_PORT_MUX))
18762306a36Sopenharmony_ci		md_dst = NULL;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	vlan_insert = skb_vlan_tag_present(skb) && (dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN_V2);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (!(md_dst || tls_handle || vlan_insert || *ipsec))
19262306a36Sopenharmony_ci		return 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	md_bytes = sizeof(meta_id) +
19562306a36Sopenharmony_ci		   (!!md_dst ? NFP_NET_META_PORTID_SIZE : 0) +
19662306a36Sopenharmony_ci		   (!!tls_handle ? NFP_NET_META_CONN_HANDLE_SIZE : 0) +
19762306a36Sopenharmony_ci		   (vlan_insert ? NFP_NET_META_VLAN_SIZE : 0) +
19862306a36Sopenharmony_ci		   (*ipsec ? NFP_NET_META_IPSEC_FIELD_SIZE : 0);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (unlikely(skb_cow_head(skb, md_bytes)))
20162306a36Sopenharmony_ci		return -ENOMEM;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	data = skb_push(skb, md_bytes) + md_bytes;
20462306a36Sopenharmony_ci	if (md_dst) {
20562306a36Sopenharmony_ci		data -= NFP_NET_META_PORTID_SIZE;
20662306a36Sopenharmony_ci		put_unaligned_be32(md_dst->u.port_info.port_id, data);
20762306a36Sopenharmony_ci		meta_id = NFP_NET_META_PORTID;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	if (tls_handle) {
21062306a36Sopenharmony_ci		/* conn handle is opaque, we just use u64 to be able to quickly
21162306a36Sopenharmony_ci		 * compare it to zero
21262306a36Sopenharmony_ci		 */
21362306a36Sopenharmony_ci		data -= NFP_NET_META_CONN_HANDLE_SIZE;
21462306a36Sopenharmony_ci		memcpy(data, &tls_handle, sizeof(tls_handle));
21562306a36Sopenharmony_ci		meta_id <<= NFP_NET_META_FIELD_SIZE;
21662306a36Sopenharmony_ci		meta_id |= NFP_NET_META_CONN_HANDLE;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	if (vlan_insert) {
21962306a36Sopenharmony_ci		data -= NFP_NET_META_VLAN_SIZE;
22062306a36Sopenharmony_ci		/* data type of skb->vlan_proto is __be16
22162306a36Sopenharmony_ci		 * so it fills metadata without calling put_unaligned_be16
22262306a36Sopenharmony_ci		 */
22362306a36Sopenharmony_ci		memcpy(data, &skb->vlan_proto, sizeof(skb->vlan_proto));
22462306a36Sopenharmony_ci		put_unaligned_be16(skb_vlan_tag_get(skb), data + sizeof(skb->vlan_proto));
22562306a36Sopenharmony_ci		meta_id <<= NFP_NET_META_FIELD_SIZE;
22662306a36Sopenharmony_ci		meta_id |= NFP_NET_META_VLAN;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	if (*ipsec) {
22962306a36Sopenharmony_ci		data -= NFP_NET_META_IPSEC_SIZE;
23062306a36Sopenharmony_ci		put_unaligned_be32(offload_info.seq_hi, data);
23162306a36Sopenharmony_ci		data -= NFP_NET_META_IPSEC_SIZE;
23262306a36Sopenharmony_ci		put_unaligned_be32(offload_info.seq_low, data);
23362306a36Sopenharmony_ci		data -= NFP_NET_META_IPSEC_SIZE;
23462306a36Sopenharmony_ci		put_unaligned_be32(offload_info.handle - 1, data);
23562306a36Sopenharmony_ci		meta_id <<= NFP_NET_META_IPSEC_FIELD_SIZE;
23662306a36Sopenharmony_ci		meta_id |= NFP_NET_META_IPSEC << 8 | NFP_NET_META_IPSEC << 4 | NFP_NET_META_IPSEC;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	data -= sizeof(meta_id);
24062306a36Sopenharmony_ci	put_unaligned_be32(meta_id, data);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return md_bytes;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/**
24662306a36Sopenharmony_ci * nfp_nfd3_tx() - Main transmit entry point
24762306a36Sopenharmony_ci * @skb:    SKB to transmit
24862306a36Sopenharmony_ci * @netdev: netdev structure
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Return: NETDEV_TX_OK on success.
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cinetdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct nfp_net *nn = netdev_priv(netdev);
25562306a36Sopenharmony_ci	int f, nr_frags, wr_idx, md_bytes;
25662306a36Sopenharmony_ci	struct nfp_net_tx_ring *tx_ring;
25762306a36Sopenharmony_ci	struct nfp_net_r_vector *r_vec;
25862306a36Sopenharmony_ci	struct nfp_nfd3_tx_buf *txbuf;
25962306a36Sopenharmony_ci	struct nfp_nfd3_tx_desc *txd;
26062306a36Sopenharmony_ci	struct netdev_queue *nd_q;
26162306a36Sopenharmony_ci	const skb_frag_t *frag;
26262306a36Sopenharmony_ci	struct nfp_net_dp *dp;
26362306a36Sopenharmony_ci	dma_addr_t dma_addr;
26462306a36Sopenharmony_ci	unsigned int fsize;
26562306a36Sopenharmony_ci	u64 tls_handle = 0;
26662306a36Sopenharmony_ci	bool ipsec = false;
26762306a36Sopenharmony_ci	u16 qidx;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	dp = &nn->dp;
27062306a36Sopenharmony_ci	qidx = skb_get_queue_mapping(skb);
27162306a36Sopenharmony_ci	tx_ring = &dp->tx_rings[qidx];
27262306a36Sopenharmony_ci	r_vec = tx_ring->r_vec;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) {
27762306a36Sopenharmony_ci		nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n",
27862306a36Sopenharmony_ci			   qidx, tx_ring->wr_p, tx_ring->rd_p);
27962306a36Sopenharmony_ci		nd_q = netdev_get_tx_queue(dp->netdev, qidx);
28062306a36Sopenharmony_ci		netif_tx_stop_queue(nd_q);
28162306a36Sopenharmony_ci		nfp_net_tx_xmit_more_flush(tx_ring);
28262306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->tx_sync);
28362306a36Sopenharmony_ci		r_vec->tx_busy++;
28462306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->tx_sync);
28562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	skb = nfp_net_tls_tx(dp, r_vec, skb, &tls_handle, &nr_frags);
28962306a36Sopenharmony_ci	if (unlikely(!skb)) {
29062306a36Sopenharmony_ci		nfp_net_tx_xmit_more_flush(tx_ring);
29162306a36Sopenharmony_ci		return NETDEV_TX_OK;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	md_bytes = nfp_nfd3_prep_tx_meta(dp, skb, tls_handle, &ipsec);
29562306a36Sopenharmony_ci	if (unlikely(md_bytes < 0))
29662306a36Sopenharmony_ci		goto err_flush;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Start with the head skbuf */
29962306a36Sopenharmony_ci	dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb),
30062306a36Sopenharmony_ci				  DMA_TO_DEVICE);
30162306a36Sopenharmony_ci	if (dma_mapping_error(dp->dev, dma_addr))
30262306a36Sopenharmony_ci		goto err_dma_err;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	wr_idx = D_IDX(tx_ring, tx_ring->wr_p);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* Stash the soft descriptor of the head then initialize it */
30762306a36Sopenharmony_ci	txbuf = &tx_ring->txbufs[wr_idx];
30862306a36Sopenharmony_ci	txbuf->skb = skb;
30962306a36Sopenharmony_ci	txbuf->dma_addr = dma_addr;
31062306a36Sopenharmony_ci	txbuf->fidx = -1;
31162306a36Sopenharmony_ci	txbuf->pkt_cnt = 1;
31262306a36Sopenharmony_ci	txbuf->real_len = skb->len;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Build TX descriptor */
31562306a36Sopenharmony_ci	txd = &tx_ring->txds[wr_idx];
31662306a36Sopenharmony_ci	txd->offset_eop = (nr_frags ? 0 : NFD3_DESC_TX_EOP) | md_bytes;
31762306a36Sopenharmony_ci	txd->dma_len = cpu_to_le16(skb_headlen(skb));
31862306a36Sopenharmony_ci	nfp_desc_set_dma_addr_40b(txd, dma_addr);
31962306a36Sopenharmony_ci	txd->data_len = cpu_to_le16(skb->len);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	txd->flags = 0;
32262306a36Sopenharmony_ci	txd->mss = 0;
32362306a36Sopenharmony_ci	txd->lso_hdrlen = 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* Do not reorder - tso may adjust pkt cnt, vlan may override fields */
32662306a36Sopenharmony_ci	nfp_nfd3_tx_tso(r_vec, txbuf, txd, skb, md_bytes);
32762306a36Sopenharmony_ci	if (ipsec)
32862306a36Sopenharmony_ci		nfp_nfd3_ipsec_tx(txd, skb);
32962306a36Sopenharmony_ci	else
33062306a36Sopenharmony_ci		nfp_nfd3_tx_csum(dp, r_vec, txbuf, txd, skb);
33162306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) {
33262306a36Sopenharmony_ci		txd->flags |= NFD3_DESC_TX_VLAN;
33362306a36Sopenharmony_ci		txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb));
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Gather DMA */
33762306a36Sopenharmony_ci	if (nr_frags > 0) {
33862306a36Sopenharmony_ci		__le64 second_half;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		/* all descs must match except for in addr, length and eop */
34162306a36Sopenharmony_ci		second_half = txd->vals8[1];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		for (f = 0; f < nr_frags; f++) {
34462306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[f];
34562306a36Sopenharmony_ci			fsize = skb_frag_size(frag);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci			dma_addr = skb_frag_dma_map(dp->dev, frag, 0,
34862306a36Sopenharmony_ci						    fsize, DMA_TO_DEVICE);
34962306a36Sopenharmony_ci			if (dma_mapping_error(dp->dev, dma_addr))
35062306a36Sopenharmony_ci				goto err_unmap;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci			wr_idx = D_IDX(tx_ring, wr_idx + 1);
35362306a36Sopenharmony_ci			tx_ring->txbufs[wr_idx].skb = skb;
35462306a36Sopenharmony_ci			tx_ring->txbufs[wr_idx].dma_addr = dma_addr;
35562306a36Sopenharmony_ci			tx_ring->txbufs[wr_idx].fidx = f;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci			txd = &tx_ring->txds[wr_idx];
35862306a36Sopenharmony_ci			txd->dma_len = cpu_to_le16(fsize);
35962306a36Sopenharmony_ci			nfp_desc_set_dma_addr_40b(txd, dma_addr);
36062306a36Sopenharmony_ci			txd->offset_eop = md_bytes |
36162306a36Sopenharmony_ci				((f == nr_frags - 1) ? NFD3_DESC_TX_EOP : 0);
36262306a36Sopenharmony_ci			txd->vals8[1] = second_half;
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->tx_sync);
36662306a36Sopenharmony_ci		r_vec->tx_gather++;
36762306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->tx_sync);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	skb_tx_timestamp(skb);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	tx_ring->wr_p += nr_frags + 1;
37562306a36Sopenharmony_ci	if (nfp_nfd3_tx_ring_should_stop(tx_ring))
37662306a36Sopenharmony_ci		nfp_nfd3_tx_ring_stop(nd_q, tx_ring);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	tx_ring->wr_ptr_add += nr_frags + 1;
37962306a36Sopenharmony_ci	if (__netdev_tx_sent_queue(nd_q, txbuf->real_len, netdev_xmit_more()))
38062306a36Sopenharmony_ci		nfp_net_tx_xmit_more_flush(tx_ring);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return NETDEV_TX_OK;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cierr_unmap:
38562306a36Sopenharmony_ci	while (--f >= 0) {
38662306a36Sopenharmony_ci		frag = &skb_shinfo(skb)->frags[f];
38762306a36Sopenharmony_ci		dma_unmap_page(dp->dev, tx_ring->txbufs[wr_idx].dma_addr,
38862306a36Sopenharmony_ci			       skb_frag_size(frag), DMA_TO_DEVICE);
38962306a36Sopenharmony_ci		tx_ring->txbufs[wr_idx].skb = NULL;
39062306a36Sopenharmony_ci		tx_ring->txbufs[wr_idx].dma_addr = 0;
39162306a36Sopenharmony_ci		tx_ring->txbufs[wr_idx].fidx = -2;
39262306a36Sopenharmony_ci		wr_idx = wr_idx - 1;
39362306a36Sopenharmony_ci		if (wr_idx < 0)
39462306a36Sopenharmony_ci			wr_idx += tx_ring->cnt;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	dma_unmap_single(dp->dev, tx_ring->txbufs[wr_idx].dma_addr,
39762306a36Sopenharmony_ci			 skb_headlen(skb), DMA_TO_DEVICE);
39862306a36Sopenharmony_ci	tx_ring->txbufs[wr_idx].skb = NULL;
39962306a36Sopenharmony_ci	tx_ring->txbufs[wr_idx].dma_addr = 0;
40062306a36Sopenharmony_ci	tx_ring->txbufs[wr_idx].fidx = -2;
40162306a36Sopenharmony_cierr_dma_err:
40262306a36Sopenharmony_ci	nn_dp_warn(dp, "Failed to map DMA TX buffer\n");
40362306a36Sopenharmony_cierr_flush:
40462306a36Sopenharmony_ci	nfp_net_tx_xmit_more_flush(tx_ring);
40562306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->tx_sync);
40662306a36Sopenharmony_ci	r_vec->tx_errors++;
40762306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->tx_sync);
40862306a36Sopenharmony_ci	nfp_net_tls_tx_undo(skb, tls_handle);
40962306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
41062306a36Sopenharmony_ci	return NETDEV_TX_OK;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/**
41462306a36Sopenharmony_ci * nfp_nfd3_tx_complete() - Handled completed TX packets
41562306a36Sopenharmony_ci * @tx_ring:	TX ring structure
41662306a36Sopenharmony_ci * @budget:	NAPI budget (only used as bool to determine if in NAPI context)
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_civoid nfp_nfd3_tx_complete(struct nfp_net_tx_ring *tx_ring, int budget)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
42162306a36Sopenharmony_ci	struct nfp_net_dp *dp = &r_vec->nfp_net->dp;
42262306a36Sopenharmony_ci	u32 done_pkts = 0, done_bytes = 0;
42362306a36Sopenharmony_ci	struct netdev_queue *nd_q;
42462306a36Sopenharmony_ci	u32 qcp_rd_p;
42562306a36Sopenharmony_ci	int todo;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (tx_ring->wr_p == tx_ring->rd_p)
42862306a36Sopenharmony_ci		return;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Work out how many descriptors have been transmitted */
43162306a36Sopenharmony_ci	qcp_rd_p = nfp_net_read_tx_cmpl(tx_ring, dp);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (qcp_rd_p == tx_ring->qcp_rd_p)
43462306a36Sopenharmony_ci		return;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	while (todo--) {
43962306a36Sopenharmony_ci		const skb_frag_t *frag;
44062306a36Sopenharmony_ci		struct nfp_nfd3_tx_buf *tx_buf;
44162306a36Sopenharmony_ci		struct sk_buff *skb;
44262306a36Sopenharmony_ci		int fidx, nr_frags;
44362306a36Sopenharmony_ci		int idx;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		idx = D_IDX(tx_ring, tx_ring->rd_p++);
44662306a36Sopenharmony_ci		tx_buf = &tx_ring->txbufs[idx];
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		skb = tx_buf->skb;
44962306a36Sopenharmony_ci		if (!skb)
45062306a36Sopenharmony_ci			continue;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		nr_frags = skb_shinfo(skb)->nr_frags;
45362306a36Sopenharmony_ci		fidx = tx_buf->fidx;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		if (fidx == -1) {
45662306a36Sopenharmony_ci			/* unmap head */
45762306a36Sopenharmony_ci			dma_unmap_single(dp->dev, tx_buf->dma_addr,
45862306a36Sopenharmony_ci					 skb_headlen(skb), DMA_TO_DEVICE);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci			done_pkts += tx_buf->pkt_cnt;
46162306a36Sopenharmony_ci			done_bytes += tx_buf->real_len;
46262306a36Sopenharmony_ci		} else {
46362306a36Sopenharmony_ci			/* unmap fragment */
46462306a36Sopenharmony_ci			frag = &skb_shinfo(skb)->frags[fidx];
46562306a36Sopenharmony_ci			dma_unmap_page(dp->dev, tx_buf->dma_addr,
46662306a36Sopenharmony_ci				       skb_frag_size(frag), DMA_TO_DEVICE);
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		/* check for last gather fragment */
47062306a36Sopenharmony_ci		if (fidx == nr_frags - 1)
47162306a36Sopenharmony_ci			napi_consume_skb(skb, budget);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		tx_buf->dma_addr = 0;
47462306a36Sopenharmony_ci		tx_buf->skb = NULL;
47562306a36Sopenharmony_ci		tx_buf->fidx = -2;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	tx_ring->qcp_rd_p = qcp_rd_p;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->tx_sync);
48162306a36Sopenharmony_ci	r_vec->tx_bytes += done_bytes;
48262306a36Sopenharmony_ci	r_vec->tx_pkts += done_pkts;
48362306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->tx_sync);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (!dp->netdev)
48662306a36Sopenharmony_ci		return;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx);
48962306a36Sopenharmony_ci	netdev_tx_completed_queue(nd_q, done_pkts, done_bytes);
49062306a36Sopenharmony_ci	if (nfp_nfd3_tx_ring_should_wake(tx_ring)) {
49162306a36Sopenharmony_ci		/* Make sure TX thread will see updated tx_ring->rd_p */
49262306a36Sopenharmony_ci		smp_mb();
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		if (unlikely(netif_tx_queue_stopped(nd_q)))
49562306a36Sopenharmony_ci			netif_tx_wake_queue(nd_q);
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt,
49962306a36Sopenharmony_ci		  "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n",
50062306a36Sopenharmony_ci		  tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic bool nfp_nfd3_xdp_complete(struct nfp_net_tx_ring *tx_ring)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
50662306a36Sopenharmony_ci	struct nfp_net_dp *dp = &r_vec->nfp_net->dp;
50762306a36Sopenharmony_ci	u32 done_pkts = 0, done_bytes = 0;
50862306a36Sopenharmony_ci	bool done_all;
50962306a36Sopenharmony_ci	int idx, todo;
51062306a36Sopenharmony_ci	u32 qcp_rd_p;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Work out how many descriptors have been transmitted */
51362306a36Sopenharmony_ci	qcp_rd_p = nfp_net_read_tx_cmpl(tx_ring, dp);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (qcp_rd_p == tx_ring->qcp_rd_p)
51662306a36Sopenharmony_ci		return true;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	todo = D_IDX(tx_ring, qcp_rd_p - tx_ring->qcp_rd_p);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	done_all = todo <= NFP_NET_XDP_MAX_COMPLETE;
52162306a36Sopenharmony_ci	todo = min(todo, NFP_NET_XDP_MAX_COMPLETE);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	tx_ring->qcp_rd_p = D_IDX(tx_ring, tx_ring->qcp_rd_p + todo);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	done_pkts = todo;
52662306a36Sopenharmony_ci	while (todo--) {
52762306a36Sopenharmony_ci		idx = D_IDX(tx_ring, tx_ring->rd_p);
52862306a36Sopenharmony_ci		tx_ring->rd_p++;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		done_bytes += tx_ring->txbufs[idx].real_len;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->tx_sync);
53462306a36Sopenharmony_ci	r_vec->tx_bytes += done_bytes;
53562306a36Sopenharmony_ci	r_vec->tx_pkts += done_pkts;
53662306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->tx_sync);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt,
53962306a36Sopenharmony_ci		  "XDP TX ring corruption rd_p=%u wr_p=%u cnt=%u\n",
54062306a36Sopenharmony_ci		  tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return done_all;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/* Receive processing
54662306a36Sopenharmony_ci */
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void *
54962306a36Sopenharmony_cinfp_nfd3_napi_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	void *frag;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (!dp->xdp_prog) {
55462306a36Sopenharmony_ci		frag = napi_alloc_frag(dp->fl_bufsz);
55562306a36Sopenharmony_ci		if (unlikely(!frag))
55662306a36Sopenharmony_ci			return NULL;
55762306a36Sopenharmony_ci	} else {
55862306a36Sopenharmony_ci		struct page *page;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		page = dev_alloc_page();
56162306a36Sopenharmony_ci		if (unlikely(!page))
56262306a36Sopenharmony_ci			return NULL;
56362306a36Sopenharmony_ci		frag = page_address(page);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	*dma_addr = nfp_net_dma_map_rx(dp, frag);
56762306a36Sopenharmony_ci	if (dma_mapping_error(dp->dev, *dma_addr)) {
56862306a36Sopenharmony_ci		nfp_net_free_frag(frag, dp->xdp_prog);
56962306a36Sopenharmony_ci		nn_dp_warn(dp, "Failed to map DMA RX buffer\n");
57062306a36Sopenharmony_ci		return NULL;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return frag;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/**
57762306a36Sopenharmony_ci * nfp_nfd3_rx_give_one() - Put mapped skb on the software and hardware rings
57862306a36Sopenharmony_ci * @dp:		NFP Net data path struct
57962306a36Sopenharmony_ci * @rx_ring:	RX ring structure
58062306a36Sopenharmony_ci * @frag:	page fragment buffer
58162306a36Sopenharmony_ci * @dma_addr:	DMA address of skb mapping
58262306a36Sopenharmony_ci */
58362306a36Sopenharmony_cistatic void
58462306a36Sopenharmony_cinfp_nfd3_rx_give_one(const struct nfp_net_dp *dp,
58562306a36Sopenharmony_ci		     struct nfp_net_rx_ring *rx_ring,
58662306a36Sopenharmony_ci		     void *frag, dma_addr_t dma_addr)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	unsigned int wr_idx;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	wr_idx = D_IDX(rx_ring, rx_ring->wr_p);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	nfp_net_dma_sync_dev_rx(dp, dma_addr);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* Stash SKB and DMA address away */
59562306a36Sopenharmony_ci	rx_ring->rxbufs[wr_idx].frag = frag;
59662306a36Sopenharmony_ci	rx_ring->rxbufs[wr_idx].dma_addr = dma_addr;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/* Fill freelist descriptor */
59962306a36Sopenharmony_ci	rx_ring->rxds[wr_idx].fld.reserved = 0;
60062306a36Sopenharmony_ci	rx_ring->rxds[wr_idx].fld.meta_len_dd = 0;
60162306a36Sopenharmony_ci	/* DMA address is expanded to 48-bit width in freelist for NFP3800,
60262306a36Sopenharmony_ci	 * so the *_48b macro is used accordingly, it's also OK to fill
60362306a36Sopenharmony_ci	 * a 40-bit address since the top 8 bits are get set to 0.
60462306a36Sopenharmony_ci	 */
60562306a36Sopenharmony_ci	nfp_desc_set_dma_addr_48b(&rx_ring->rxds[wr_idx].fld,
60662306a36Sopenharmony_ci				  dma_addr + dp->rx_dma_off);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	rx_ring->wr_p++;
60962306a36Sopenharmony_ci	if (!(rx_ring->wr_p % NFP_NET_FL_BATCH)) {
61062306a36Sopenharmony_ci		/* Update write pointer of the freelist queue. Make
61162306a36Sopenharmony_ci		 * sure all writes are flushed before telling the hardware.
61262306a36Sopenharmony_ci		 */
61362306a36Sopenharmony_ci		wmb();
61462306a36Sopenharmony_ci		nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, NFP_NET_FL_BATCH);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci/**
61962306a36Sopenharmony_ci * nfp_nfd3_rx_ring_fill_freelist() - Give buffers from the ring to FW
62062306a36Sopenharmony_ci * @dp:	     NFP Net data path struct
62162306a36Sopenharmony_ci * @rx_ring: RX ring to fill
62262306a36Sopenharmony_ci */
62362306a36Sopenharmony_civoid nfp_nfd3_rx_ring_fill_freelist(struct nfp_net_dp *dp,
62462306a36Sopenharmony_ci				    struct nfp_net_rx_ring *rx_ring)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	unsigned int i;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx))
62962306a36Sopenharmony_ci		return nfp_net_xsk_rx_ring_fill_freelist(rx_ring);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	for (i = 0; i < rx_ring->cnt - 1; i++)
63262306a36Sopenharmony_ci		nfp_nfd3_rx_give_one(dp, rx_ring, rx_ring->rxbufs[i].frag,
63362306a36Sopenharmony_ci				     rx_ring->rxbufs[i].dma_addr);
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci/**
63762306a36Sopenharmony_ci * nfp_nfd3_rx_csum_has_errors() - group check if rxd has any csum errors
63862306a36Sopenharmony_ci * @flags: RX descriptor flags field in CPU byte order
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_cistatic int nfp_nfd3_rx_csum_has_errors(u16 flags)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	u16 csum_all_checked, csum_all_ok;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL;
64562306a36Sopenharmony_ci	csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/**
65162306a36Sopenharmony_ci * nfp_nfd3_rx_csum() - set SKB checksum field based on RX descriptor flags
65262306a36Sopenharmony_ci * @dp:  NFP Net data path struct
65362306a36Sopenharmony_ci * @r_vec: per-ring structure
65462306a36Sopenharmony_ci * @rxd: Pointer to RX descriptor
65562306a36Sopenharmony_ci * @meta: Parsed metadata prepend
65662306a36Sopenharmony_ci * @skb: Pointer to SKB
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_civoid
65962306a36Sopenharmony_cinfp_nfd3_rx_csum(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec,
66062306a36Sopenharmony_ci		 const struct nfp_net_rx_desc *rxd,
66162306a36Sopenharmony_ci		 const struct nfp_meta_parsed *meta, struct sk_buff *skb)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	skb_checksum_none_assert(skb);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (!(dp->netdev->features & NETIF_F_RXCSUM))
66662306a36Sopenharmony_ci		return;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (meta->csum_type) {
66962306a36Sopenharmony_ci		skb->ip_summed = meta->csum_type;
67062306a36Sopenharmony_ci		skb->csum = meta->csum;
67162306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->rx_sync);
67262306a36Sopenharmony_ci		r_vec->hw_csum_rx_complete++;
67362306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->rx_sync);
67462306a36Sopenharmony_ci		return;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (nfp_nfd3_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) {
67862306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->rx_sync);
67962306a36Sopenharmony_ci		r_vec->hw_csum_rx_error++;
68062306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->rx_sync);
68162306a36Sopenharmony_ci		return;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* Assume that the firmware will never report inner CSUM_OK unless outer
68562306a36Sopenharmony_ci	 * L4 headers were successfully parsed. FW will always report zero UDP
68662306a36Sopenharmony_ci	 * checksum as CSUM_OK.
68762306a36Sopenharmony_ci	 */
68862306a36Sopenharmony_ci	if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK ||
68962306a36Sopenharmony_ci	    rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) {
69062306a36Sopenharmony_ci		__skb_incr_checksum_unnecessary(skb);
69162306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->rx_sync);
69262306a36Sopenharmony_ci		r_vec->hw_csum_rx_ok++;
69362306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->rx_sync);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK ||
69762306a36Sopenharmony_ci	    rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) {
69862306a36Sopenharmony_ci		__skb_incr_checksum_unnecessary(skb);
69962306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->rx_sync);
70062306a36Sopenharmony_ci		r_vec->hw_csum_rx_inner_ok++;
70162306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->rx_sync);
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void
70662306a36Sopenharmony_cinfp_nfd3_set_hash(struct net_device *netdev, struct nfp_meta_parsed *meta,
70762306a36Sopenharmony_ci		  unsigned int type, __be32 *hash)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	if (!(netdev->features & NETIF_F_RXHASH))
71062306a36Sopenharmony_ci		return;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	switch (type) {
71362306a36Sopenharmony_ci	case NFP_NET_RSS_IPV4:
71462306a36Sopenharmony_ci	case NFP_NET_RSS_IPV6:
71562306a36Sopenharmony_ci	case NFP_NET_RSS_IPV6_EX:
71662306a36Sopenharmony_ci		meta->hash_type = PKT_HASH_TYPE_L3;
71762306a36Sopenharmony_ci		break;
71862306a36Sopenharmony_ci	default:
71962306a36Sopenharmony_ci		meta->hash_type = PKT_HASH_TYPE_L4;
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	meta->hash = get_unaligned_be32(hash);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic void
72762306a36Sopenharmony_cinfp_nfd3_set_hash_desc(struct net_device *netdev, struct nfp_meta_parsed *meta,
72862306a36Sopenharmony_ci		       void *data, struct nfp_net_rx_desc *rxd)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct nfp_net_rx_hash *rx_hash = data;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS))
73362306a36Sopenharmony_ci		return;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	nfp_nfd3_set_hash(netdev, meta, get_unaligned_be32(&rx_hash->hash_type),
73662306a36Sopenharmony_ci			  &rx_hash->hash);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cibool
74062306a36Sopenharmony_cinfp_nfd3_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta,
74162306a36Sopenharmony_ci		    void *data, void *pkt, unsigned int pkt_len, int meta_len)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	u32 meta_info, vlan_info;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	meta_info = get_unaligned_be32(data);
74662306a36Sopenharmony_ci	data += 4;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	while (meta_info) {
74962306a36Sopenharmony_ci		switch (meta_info & NFP_NET_META_FIELD_MASK) {
75062306a36Sopenharmony_ci		case NFP_NET_META_HASH:
75162306a36Sopenharmony_ci			meta_info >>= NFP_NET_META_FIELD_SIZE;
75262306a36Sopenharmony_ci			nfp_nfd3_set_hash(netdev, meta,
75362306a36Sopenharmony_ci					  meta_info & NFP_NET_META_FIELD_MASK,
75462306a36Sopenharmony_ci					  (__be32 *)data);
75562306a36Sopenharmony_ci			data += 4;
75662306a36Sopenharmony_ci			break;
75762306a36Sopenharmony_ci		case NFP_NET_META_MARK:
75862306a36Sopenharmony_ci			meta->mark = get_unaligned_be32(data);
75962306a36Sopenharmony_ci			data += 4;
76062306a36Sopenharmony_ci			break;
76162306a36Sopenharmony_ci		case NFP_NET_META_VLAN:
76262306a36Sopenharmony_ci			vlan_info = get_unaligned_be32(data);
76362306a36Sopenharmony_ci			if (FIELD_GET(NFP_NET_META_VLAN_STRIP, vlan_info)) {
76462306a36Sopenharmony_ci				meta->vlan.stripped = true;
76562306a36Sopenharmony_ci				meta->vlan.tpid = FIELD_GET(NFP_NET_META_VLAN_TPID_MASK,
76662306a36Sopenharmony_ci							    vlan_info);
76762306a36Sopenharmony_ci				meta->vlan.tci = FIELD_GET(NFP_NET_META_VLAN_TCI_MASK,
76862306a36Sopenharmony_ci							   vlan_info);
76962306a36Sopenharmony_ci			}
77062306a36Sopenharmony_ci			data += 4;
77162306a36Sopenharmony_ci			break;
77262306a36Sopenharmony_ci		case NFP_NET_META_PORTID:
77362306a36Sopenharmony_ci			meta->portid = get_unaligned_be32(data);
77462306a36Sopenharmony_ci			data += 4;
77562306a36Sopenharmony_ci			break;
77662306a36Sopenharmony_ci		case NFP_NET_META_CSUM:
77762306a36Sopenharmony_ci			meta->csum_type = CHECKSUM_COMPLETE;
77862306a36Sopenharmony_ci			meta->csum =
77962306a36Sopenharmony_ci				(__force __wsum)__get_unaligned_cpu32(data);
78062306a36Sopenharmony_ci			data += 4;
78162306a36Sopenharmony_ci			break;
78262306a36Sopenharmony_ci		case NFP_NET_META_RESYNC_INFO:
78362306a36Sopenharmony_ci			if (nfp_net_tls_rx_resync_req(netdev, data, pkt,
78462306a36Sopenharmony_ci						      pkt_len))
78562306a36Sopenharmony_ci				return false;
78662306a36Sopenharmony_ci			data += sizeof(struct nfp_net_tls_resync_req);
78762306a36Sopenharmony_ci			break;
78862306a36Sopenharmony_ci#ifdef CONFIG_NFP_NET_IPSEC
78962306a36Sopenharmony_ci		case NFP_NET_META_IPSEC:
79062306a36Sopenharmony_ci			/* Note: IPsec packet will have zero saidx, so need add 1
79162306a36Sopenharmony_ci			 * to indicate packet is IPsec packet within driver.
79262306a36Sopenharmony_ci			 */
79362306a36Sopenharmony_ci			meta->ipsec_saidx = get_unaligned_be32(data) + 1;
79462306a36Sopenharmony_ci			data += 4;
79562306a36Sopenharmony_ci			break;
79662306a36Sopenharmony_ci#endif
79762306a36Sopenharmony_ci		default:
79862306a36Sopenharmony_ci			return true;
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		meta_info >>= NFP_NET_META_FIELD_SIZE;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return data != pkt;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic void
80862306a36Sopenharmony_cinfp_nfd3_rx_drop(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec,
80962306a36Sopenharmony_ci		 struct nfp_net_rx_ring *rx_ring, struct nfp_net_rx_buf *rxbuf,
81062306a36Sopenharmony_ci		 struct sk_buff *skb)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->rx_sync);
81362306a36Sopenharmony_ci	r_vec->rx_drops++;
81462306a36Sopenharmony_ci	/* If we have both skb and rxbuf the replacement buffer allocation
81562306a36Sopenharmony_ci	 * must have failed, count this as an alloc failure.
81662306a36Sopenharmony_ci	 */
81762306a36Sopenharmony_ci	if (skb && rxbuf)
81862306a36Sopenharmony_ci		r_vec->rx_replace_buf_alloc_fail++;
81962306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->rx_sync);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/* skb is build based on the frag, free_skb() would free the frag
82262306a36Sopenharmony_ci	 * so to be able to reuse it we need an extra ref.
82362306a36Sopenharmony_ci	 */
82462306a36Sopenharmony_ci	if (skb && rxbuf && skb->head == rxbuf->frag)
82562306a36Sopenharmony_ci		page_ref_inc(virt_to_head_page(rxbuf->frag));
82662306a36Sopenharmony_ci	if (rxbuf)
82762306a36Sopenharmony_ci		nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr);
82862306a36Sopenharmony_ci	if (skb)
82962306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic bool
83362306a36Sopenharmony_cinfp_nfd3_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring,
83462306a36Sopenharmony_ci		    struct nfp_net_tx_ring *tx_ring,
83562306a36Sopenharmony_ci		    struct nfp_net_rx_buf *rxbuf, unsigned int dma_off,
83662306a36Sopenharmony_ci		    unsigned int pkt_len, bool *completed)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	unsigned int dma_map_sz = dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA;
83962306a36Sopenharmony_ci	struct nfp_nfd3_tx_buf *txbuf;
84062306a36Sopenharmony_ci	struct nfp_nfd3_tx_desc *txd;
84162306a36Sopenharmony_ci	int wr_idx;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* Reject if xdp_adjust_tail grow packet beyond DMA area */
84462306a36Sopenharmony_ci	if (pkt_len + dma_off > dma_map_sz)
84562306a36Sopenharmony_ci		return false;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (unlikely(nfp_net_tx_full(tx_ring, 1))) {
84862306a36Sopenharmony_ci		if (!*completed) {
84962306a36Sopenharmony_ci			nfp_nfd3_xdp_complete(tx_ring);
85062306a36Sopenharmony_ci			*completed = true;
85162306a36Sopenharmony_ci		}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		if (unlikely(nfp_net_tx_full(tx_ring, 1))) {
85462306a36Sopenharmony_ci			nfp_nfd3_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf,
85562306a36Sopenharmony_ci					 NULL);
85662306a36Sopenharmony_ci			return false;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	wr_idx = D_IDX(tx_ring, tx_ring->wr_p);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* Stash the soft descriptor of the head then initialize it */
86362306a36Sopenharmony_ci	txbuf = &tx_ring->txbufs[wr_idx];
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	nfp_nfd3_rx_give_one(dp, rx_ring, txbuf->frag, txbuf->dma_addr);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	txbuf->frag = rxbuf->frag;
86862306a36Sopenharmony_ci	txbuf->dma_addr = rxbuf->dma_addr;
86962306a36Sopenharmony_ci	txbuf->fidx = -1;
87062306a36Sopenharmony_ci	txbuf->pkt_cnt = 1;
87162306a36Sopenharmony_ci	txbuf->real_len = pkt_len;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + dma_off,
87462306a36Sopenharmony_ci				   pkt_len, DMA_BIDIRECTIONAL);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/* Build TX descriptor */
87762306a36Sopenharmony_ci	txd = &tx_ring->txds[wr_idx];
87862306a36Sopenharmony_ci	txd->offset_eop = NFD3_DESC_TX_EOP;
87962306a36Sopenharmony_ci	txd->dma_len = cpu_to_le16(pkt_len);
88062306a36Sopenharmony_ci	nfp_desc_set_dma_addr_40b(txd, rxbuf->dma_addr + dma_off);
88162306a36Sopenharmony_ci	txd->data_len = cpu_to_le16(pkt_len);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	txd->flags = 0;
88462306a36Sopenharmony_ci	txd->mss = 0;
88562306a36Sopenharmony_ci	txd->lso_hdrlen = 0;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	tx_ring->wr_p++;
88862306a36Sopenharmony_ci	tx_ring->wr_ptr_add++;
88962306a36Sopenharmony_ci	return true;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci/**
89362306a36Sopenharmony_ci * nfp_nfd3_rx() - receive up to @budget packets on @rx_ring
89462306a36Sopenharmony_ci * @rx_ring:   RX ring to receive from
89562306a36Sopenharmony_ci * @budget:    NAPI budget
89662306a36Sopenharmony_ci *
89762306a36Sopenharmony_ci * Note, this function is separated out from the napi poll function to
89862306a36Sopenharmony_ci * more cleanly separate packet receive code from other bookkeeping
89962306a36Sopenharmony_ci * functions performed in the napi poll function.
90062306a36Sopenharmony_ci *
90162306a36Sopenharmony_ci * Return: Number of packets received.
90262306a36Sopenharmony_ci */
90362306a36Sopenharmony_cistatic int nfp_nfd3_rx(struct nfp_net_rx_ring *rx_ring, int budget)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
90662306a36Sopenharmony_ci	struct nfp_net_dp *dp = &r_vec->nfp_net->dp;
90762306a36Sopenharmony_ci	struct nfp_net_tx_ring *tx_ring;
90862306a36Sopenharmony_ci	struct bpf_prog *xdp_prog;
90962306a36Sopenharmony_ci	int idx, pkts_polled = 0;
91062306a36Sopenharmony_ci	bool xdp_tx_cmpl = false;
91162306a36Sopenharmony_ci	unsigned int true_bufsz;
91262306a36Sopenharmony_ci	struct sk_buff *skb;
91362306a36Sopenharmony_ci	struct xdp_buff xdp;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	xdp_prog = READ_ONCE(dp->xdp_prog);
91662306a36Sopenharmony_ci	true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz;
91762306a36Sopenharmony_ci	xdp_init_buff(&xdp, PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM,
91862306a36Sopenharmony_ci		      &rx_ring->xdp_rxq);
91962306a36Sopenharmony_ci	tx_ring = r_vec->xdp_ring;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	while (pkts_polled < budget) {
92262306a36Sopenharmony_ci		unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off;
92362306a36Sopenharmony_ci		struct nfp_net_rx_buf *rxbuf;
92462306a36Sopenharmony_ci		struct nfp_net_rx_desc *rxd;
92562306a36Sopenharmony_ci		struct nfp_meta_parsed meta;
92662306a36Sopenharmony_ci		bool redir_egress = false;
92762306a36Sopenharmony_ci		struct net_device *netdev;
92862306a36Sopenharmony_ci		dma_addr_t new_dma_addr;
92962306a36Sopenharmony_ci		u32 meta_len_xdp = 0;
93062306a36Sopenharmony_ci		void *new_frag;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci		idx = D_IDX(rx_ring, rx_ring->rd_p);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		rxd = &rx_ring->rxds[idx];
93562306a36Sopenharmony_ci		if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD))
93662306a36Sopenharmony_ci			break;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		/* Memory barrier to ensure that we won't do other reads
93962306a36Sopenharmony_ci		 * before the DD bit.
94062306a36Sopenharmony_ci		 */
94162306a36Sopenharmony_ci		dma_rmb();
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		memset(&meta, 0, sizeof(meta));
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		rx_ring->rd_p++;
94662306a36Sopenharmony_ci		pkts_polled++;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		rxbuf =	&rx_ring->rxbufs[idx];
94962306a36Sopenharmony_ci		/*         < meta_len >
95062306a36Sopenharmony_ci		 *  <-- [rx_offset] -->
95162306a36Sopenharmony_ci		 *  ---------------------------------------------------------
95262306a36Sopenharmony_ci		 * | [XX] |  metadata  |             packet           | XXXX |
95362306a36Sopenharmony_ci		 *  ---------------------------------------------------------
95462306a36Sopenharmony_ci		 *         <---------------- data_len --------------->
95562306a36Sopenharmony_ci		 *
95662306a36Sopenharmony_ci		 * The rx_offset is fixed for all packets, the meta_len can vary
95762306a36Sopenharmony_ci		 * on a packet by packet basis. If rx_offset is set to zero
95862306a36Sopenharmony_ci		 * (_RX_OFFSET_DYNAMIC) metadata starts at the beginning of the
95962306a36Sopenharmony_ci		 * buffer and is immediately followed by the packet (no [XX]).
96062306a36Sopenharmony_ci		 */
96162306a36Sopenharmony_ci		meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK;
96262306a36Sopenharmony_ci		data_len = le16_to_cpu(rxd->rxd.data_len);
96362306a36Sopenharmony_ci		pkt_len = data_len - meta_len;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off;
96662306a36Sopenharmony_ci		if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC)
96762306a36Sopenharmony_ci			pkt_off += meta_len;
96862306a36Sopenharmony_ci		else
96962306a36Sopenharmony_ci			pkt_off += dp->rx_offset;
97062306a36Sopenharmony_ci		meta_off = pkt_off - meta_len;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		/* Stats update */
97362306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->rx_sync);
97462306a36Sopenharmony_ci		r_vec->rx_pkts++;
97562306a36Sopenharmony_ci		r_vec->rx_bytes += pkt_len;
97662306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->rx_sync);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		if (unlikely(meta_len > NFP_NET_MAX_PREPEND ||
97962306a36Sopenharmony_ci			     (dp->rx_offset && meta_len > dp->rx_offset))) {
98062306a36Sopenharmony_ci			nn_dp_warn(dp, "oversized RX packet metadata %u\n",
98162306a36Sopenharmony_ci				   meta_len);
98262306a36Sopenharmony_ci			nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL);
98362306a36Sopenharmony_ci			continue;
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off,
98762306a36Sopenharmony_ci					data_len);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		if (!dp->chained_metadata_format) {
99062306a36Sopenharmony_ci			nfp_nfd3_set_hash_desc(dp->netdev, &meta,
99162306a36Sopenharmony_ci					       rxbuf->frag + meta_off, rxd);
99262306a36Sopenharmony_ci		} else if (meta_len) {
99362306a36Sopenharmony_ci			if (unlikely(nfp_nfd3_parse_meta(dp->netdev, &meta,
99462306a36Sopenharmony_ci							 rxbuf->frag + meta_off,
99562306a36Sopenharmony_ci							 rxbuf->frag + pkt_off,
99662306a36Sopenharmony_ci							 pkt_len, meta_len))) {
99762306a36Sopenharmony_ci				nn_dp_warn(dp, "invalid RX packet metadata\n");
99862306a36Sopenharmony_ci				nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf,
99962306a36Sopenharmony_ci						 NULL);
100062306a36Sopenharmony_ci				continue;
100162306a36Sopenharmony_ci			}
100262306a36Sopenharmony_ci		}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		if (xdp_prog && !meta.portid) {
100562306a36Sopenharmony_ci			void *orig_data = rxbuf->frag + pkt_off;
100662306a36Sopenharmony_ci			unsigned int dma_off;
100762306a36Sopenharmony_ci			int act;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci			xdp_prepare_buff(&xdp,
101062306a36Sopenharmony_ci					 rxbuf->frag + NFP_NET_RX_BUF_HEADROOM,
101162306a36Sopenharmony_ci					 pkt_off - NFP_NET_RX_BUF_HEADROOM,
101262306a36Sopenharmony_ci					 pkt_len, true);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci			act = bpf_prog_run_xdp(xdp_prog, &xdp);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci			pkt_len = xdp.data_end - xdp.data;
101762306a36Sopenharmony_ci			pkt_off += xdp.data - orig_data;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci			switch (act) {
102062306a36Sopenharmony_ci			case XDP_PASS:
102162306a36Sopenharmony_ci				meta_len_xdp = xdp.data - xdp.data_meta;
102262306a36Sopenharmony_ci				break;
102362306a36Sopenharmony_ci			case XDP_TX:
102462306a36Sopenharmony_ci				dma_off = pkt_off - NFP_NET_RX_BUF_HEADROOM;
102562306a36Sopenharmony_ci				if (unlikely(!nfp_nfd3_tx_xdp_buf(dp, rx_ring,
102662306a36Sopenharmony_ci								  tx_ring,
102762306a36Sopenharmony_ci								  rxbuf,
102862306a36Sopenharmony_ci								  dma_off,
102962306a36Sopenharmony_ci								  pkt_len,
103062306a36Sopenharmony_ci								  &xdp_tx_cmpl)))
103162306a36Sopenharmony_ci					trace_xdp_exception(dp->netdev,
103262306a36Sopenharmony_ci							    xdp_prog, act);
103362306a36Sopenharmony_ci				continue;
103462306a36Sopenharmony_ci			default:
103562306a36Sopenharmony_ci				bpf_warn_invalid_xdp_action(dp->netdev, xdp_prog, act);
103662306a36Sopenharmony_ci				fallthrough;
103762306a36Sopenharmony_ci			case XDP_ABORTED:
103862306a36Sopenharmony_ci				trace_xdp_exception(dp->netdev, xdp_prog, act);
103962306a36Sopenharmony_ci				fallthrough;
104062306a36Sopenharmony_ci			case XDP_DROP:
104162306a36Sopenharmony_ci				nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag,
104262306a36Sopenharmony_ci						     rxbuf->dma_addr);
104362306a36Sopenharmony_ci				continue;
104462306a36Sopenharmony_ci			}
104562306a36Sopenharmony_ci		}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci		if (likely(!meta.portid)) {
104862306a36Sopenharmony_ci			netdev = dp->netdev;
104962306a36Sopenharmony_ci		} else if (meta.portid == NFP_META_PORT_ID_CTRL) {
105062306a36Sopenharmony_ci			struct nfp_net *nn = netdev_priv(dp->netdev);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci			nfp_app_ctrl_rx_raw(nn->app, rxbuf->frag + pkt_off,
105362306a36Sopenharmony_ci					    pkt_len);
105462306a36Sopenharmony_ci			nfp_nfd3_rx_give_one(dp, rx_ring, rxbuf->frag,
105562306a36Sopenharmony_ci					     rxbuf->dma_addr);
105662306a36Sopenharmony_ci			continue;
105762306a36Sopenharmony_ci		} else {
105862306a36Sopenharmony_ci			struct nfp_net *nn;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci			nn = netdev_priv(dp->netdev);
106162306a36Sopenharmony_ci			netdev = nfp_app_dev_get(nn->app, meta.portid,
106262306a36Sopenharmony_ci						 &redir_egress);
106362306a36Sopenharmony_ci			if (unlikely(!netdev)) {
106462306a36Sopenharmony_ci				nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf,
106562306a36Sopenharmony_ci						 NULL);
106662306a36Sopenharmony_ci				continue;
106762306a36Sopenharmony_ci			}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci			if (nfp_netdev_is_nfp_repr(netdev))
107062306a36Sopenharmony_ci				nfp_repr_inc_rx_stats(netdev, pkt_len);
107162306a36Sopenharmony_ci		}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		skb = build_skb(rxbuf->frag, true_bufsz);
107462306a36Sopenharmony_ci		if (unlikely(!skb)) {
107562306a36Sopenharmony_ci			nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL);
107662306a36Sopenharmony_ci			continue;
107762306a36Sopenharmony_ci		}
107862306a36Sopenharmony_ci		new_frag = nfp_nfd3_napi_alloc_one(dp, &new_dma_addr);
107962306a36Sopenharmony_ci		if (unlikely(!new_frag)) {
108062306a36Sopenharmony_ci			nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, skb);
108162306a36Sopenharmony_ci			continue;
108262306a36Sopenharmony_ci		}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci		nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		nfp_nfd3_rx_give_one(dp, rx_ring, new_frag, new_dma_addr);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		skb_reserve(skb, pkt_off);
108962306a36Sopenharmony_ci		skb_put(skb, pkt_len);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		skb->mark = meta.mark;
109262306a36Sopenharmony_ci		skb_set_hash(skb, meta.hash, meta.hash_type);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		skb_record_rx_queue(skb, rx_ring->idx);
109562306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, netdev);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		nfp_nfd3_rx_csum(dp, r_vec, rxd, &meta, skb);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci#ifdef CONFIG_TLS_DEVICE
110062306a36Sopenharmony_ci		if (rxd->rxd.flags & PCIE_DESC_RX_DECRYPTED) {
110162306a36Sopenharmony_ci			skb->decrypted = true;
110262306a36Sopenharmony_ci			u64_stats_update_begin(&r_vec->rx_sync);
110362306a36Sopenharmony_ci			r_vec->hw_tls_rx++;
110462306a36Sopenharmony_ci			u64_stats_update_end(&r_vec->rx_sync);
110562306a36Sopenharmony_ci		}
110662306a36Sopenharmony_ci#endif
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci		if (unlikely(!nfp_net_vlan_strip(skb, rxd, &meta))) {
110962306a36Sopenharmony_ci			nfp_nfd3_rx_drop(dp, r_vec, rx_ring, NULL, skb);
111062306a36Sopenharmony_ci			continue;
111162306a36Sopenharmony_ci		}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci#ifdef CONFIG_NFP_NET_IPSEC
111462306a36Sopenharmony_ci		if (meta.ipsec_saidx != 0 && unlikely(nfp_net_ipsec_rx(&meta, skb))) {
111562306a36Sopenharmony_ci			nfp_nfd3_rx_drop(dp, r_vec, rx_ring, NULL, skb);
111662306a36Sopenharmony_ci			continue;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci#endif
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci		if (meta_len_xdp)
112162306a36Sopenharmony_ci			skb_metadata_set(skb, meta_len_xdp);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		if (likely(!redir_egress)) {
112462306a36Sopenharmony_ci			napi_gro_receive(&rx_ring->r_vec->napi, skb);
112562306a36Sopenharmony_ci		} else {
112662306a36Sopenharmony_ci			skb->dev = netdev;
112762306a36Sopenharmony_ci			skb_reset_network_header(skb);
112862306a36Sopenharmony_ci			__skb_push(skb, ETH_HLEN);
112962306a36Sopenharmony_ci			dev_queue_xmit(skb);
113062306a36Sopenharmony_ci		}
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (xdp_prog) {
113462306a36Sopenharmony_ci		if (tx_ring->wr_ptr_add)
113562306a36Sopenharmony_ci			nfp_net_tx_xmit_more_flush(tx_ring);
113662306a36Sopenharmony_ci		else if (unlikely(tx_ring->wr_p != tx_ring->rd_p) &&
113762306a36Sopenharmony_ci			 !xdp_tx_cmpl)
113862306a36Sopenharmony_ci			if (!nfp_nfd3_xdp_complete(tx_ring))
113962306a36Sopenharmony_ci				pkts_polled = budget;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	return pkts_polled;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/**
114662306a36Sopenharmony_ci * nfp_nfd3_poll() - napi poll function
114762306a36Sopenharmony_ci * @napi:    NAPI structure
114862306a36Sopenharmony_ci * @budget:  NAPI budget
114962306a36Sopenharmony_ci *
115062306a36Sopenharmony_ci * Return: number of packets polled.
115162306a36Sopenharmony_ci */
115262306a36Sopenharmony_ciint nfp_nfd3_poll(struct napi_struct *napi, int budget)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct nfp_net_r_vector *r_vec =
115562306a36Sopenharmony_ci		container_of(napi, struct nfp_net_r_vector, napi);
115662306a36Sopenharmony_ci	unsigned int pkts_polled = 0;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (r_vec->tx_ring)
115962306a36Sopenharmony_ci		nfp_nfd3_tx_complete(r_vec->tx_ring, budget);
116062306a36Sopenharmony_ci	if (r_vec->rx_ring)
116162306a36Sopenharmony_ci		pkts_polled = nfp_nfd3_rx(r_vec->rx_ring, budget);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (pkts_polled < budget)
116462306a36Sopenharmony_ci		if (napi_complete_done(napi, pkts_polled))
116562306a36Sopenharmony_ci			nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (r_vec->nfp_net->rx_coalesce_adapt_on && r_vec->rx_ring) {
116862306a36Sopenharmony_ci		struct dim_sample dim_sample = {};
116962306a36Sopenharmony_ci		unsigned int start;
117062306a36Sopenharmony_ci		u64 pkts, bytes;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		do {
117362306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&r_vec->rx_sync);
117462306a36Sopenharmony_ci			pkts = r_vec->rx_pkts;
117562306a36Sopenharmony_ci			bytes = r_vec->rx_bytes;
117662306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&r_vec->rx_sync, start));
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci		dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample);
117962306a36Sopenharmony_ci		net_dim(&r_vec->rx_dim, dim_sample);
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (r_vec->nfp_net->tx_coalesce_adapt_on && r_vec->tx_ring) {
118362306a36Sopenharmony_ci		struct dim_sample dim_sample = {};
118462306a36Sopenharmony_ci		unsigned int start;
118562306a36Sopenharmony_ci		u64 pkts, bytes;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		do {
118862306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&r_vec->tx_sync);
118962306a36Sopenharmony_ci			pkts = r_vec->tx_pkts;
119062306a36Sopenharmony_ci			bytes = r_vec->tx_bytes;
119162306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&r_vec->tx_sync, start));
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample);
119462306a36Sopenharmony_ci		net_dim(&r_vec->tx_dim, dim_sample);
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	return pkts_polled;
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci/* Control device data path
120162306a36Sopenharmony_ci */
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cibool
120462306a36Sopenharmony_cinfp_nfd3_ctrl_tx_one(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
120562306a36Sopenharmony_ci		     struct sk_buff *skb, bool old)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	unsigned int real_len = skb->len, meta_len = 0;
120862306a36Sopenharmony_ci	struct nfp_net_tx_ring *tx_ring;
120962306a36Sopenharmony_ci	struct nfp_nfd3_tx_buf *txbuf;
121062306a36Sopenharmony_ci	struct nfp_nfd3_tx_desc *txd;
121162306a36Sopenharmony_ci	struct nfp_net_dp *dp;
121262306a36Sopenharmony_ci	dma_addr_t dma_addr;
121362306a36Sopenharmony_ci	int wr_idx;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	dp = &r_vec->nfp_net->dp;
121662306a36Sopenharmony_ci	tx_ring = r_vec->tx_ring;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (WARN_ON_ONCE(skb_shinfo(skb)->nr_frags)) {
121962306a36Sopenharmony_ci		nn_dp_warn(dp, "Driver's CTRL TX does not implement gather\n");
122062306a36Sopenharmony_ci		goto err_free;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	if (unlikely(nfp_net_tx_full(tx_ring, 1))) {
122462306a36Sopenharmony_ci		u64_stats_update_begin(&r_vec->tx_sync);
122562306a36Sopenharmony_ci		r_vec->tx_busy++;
122662306a36Sopenharmony_ci		u64_stats_update_end(&r_vec->tx_sync);
122762306a36Sopenharmony_ci		if (!old)
122862306a36Sopenharmony_ci			__skb_queue_tail(&r_vec->queue, skb);
122962306a36Sopenharmony_ci		else
123062306a36Sopenharmony_ci			__skb_queue_head(&r_vec->queue, skb);
123162306a36Sopenharmony_ci		return true;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (nfp_app_ctrl_has_meta(nn->app)) {
123562306a36Sopenharmony_ci		if (unlikely(skb_headroom(skb) < 8)) {
123662306a36Sopenharmony_ci			nn_dp_warn(dp, "CTRL TX on skb without headroom\n");
123762306a36Sopenharmony_ci			goto err_free;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci		meta_len = 8;
124062306a36Sopenharmony_ci		put_unaligned_be32(NFP_META_PORT_ID_CTRL, skb_push(skb, 4));
124162306a36Sopenharmony_ci		put_unaligned_be32(NFP_NET_META_PORTID, skb_push(skb, 4));
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* Start with the head skbuf */
124562306a36Sopenharmony_ci	dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb),
124662306a36Sopenharmony_ci				  DMA_TO_DEVICE);
124762306a36Sopenharmony_ci	if (dma_mapping_error(dp->dev, dma_addr))
124862306a36Sopenharmony_ci		goto err_dma_warn;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	wr_idx = D_IDX(tx_ring, tx_ring->wr_p);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	/* Stash the soft descriptor of the head then initialize it */
125362306a36Sopenharmony_ci	txbuf = &tx_ring->txbufs[wr_idx];
125462306a36Sopenharmony_ci	txbuf->skb = skb;
125562306a36Sopenharmony_ci	txbuf->dma_addr = dma_addr;
125662306a36Sopenharmony_ci	txbuf->fidx = -1;
125762306a36Sopenharmony_ci	txbuf->pkt_cnt = 1;
125862306a36Sopenharmony_ci	txbuf->real_len = real_len;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/* Build TX descriptor */
126162306a36Sopenharmony_ci	txd = &tx_ring->txds[wr_idx];
126262306a36Sopenharmony_ci	txd->offset_eop = meta_len | NFD3_DESC_TX_EOP;
126362306a36Sopenharmony_ci	txd->dma_len = cpu_to_le16(skb_headlen(skb));
126462306a36Sopenharmony_ci	nfp_desc_set_dma_addr_40b(txd, dma_addr);
126562306a36Sopenharmony_ci	txd->data_len = cpu_to_le16(skb->len);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	txd->flags = 0;
126862306a36Sopenharmony_ci	txd->mss = 0;
126962306a36Sopenharmony_ci	txd->lso_hdrlen = 0;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	tx_ring->wr_p++;
127262306a36Sopenharmony_ci	tx_ring->wr_ptr_add++;
127362306a36Sopenharmony_ci	nfp_net_tx_xmit_more_flush(tx_ring);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	return false;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cierr_dma_warn:
127862306a36Sopenharmony_ci	nn_dp_warn(dp, "Failed to DMA map TX CTRL buffer\n");
127962306a36Sopenharmony_cierr_free:
128062306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->tx_sync);
128162306a36Sopenharmony_ci	r_vec->tx_errors++;
128262306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->tx_sync);
128362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
128462306a36Sopenharmony_ci	return false;
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic void __nfp_ctrl_tx_queued(struct nfp_net_r_vector *r_vec)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	struct sk_buff *skb;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	while ((skb = __skb_dequeue(&r_vec->queue)))
129262306a36Sopenharmony_ci		if (nfp_nfd3_ctrl_tx_one(r_vec->nfp_net, r_vec, skb, true))
129362306a36Sopenharmony_ci			return;
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic bool
129762306a36Sopenharmony_cinfp_ctrl_meta_ok(struct nfp_net *nn, void *data, unsigned int meta_len)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	u32 meta_type, meta_tag;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (!nfp_app_ctrl_has_meta(nn->app))
130262306a36Sopenharmony_ci		return !meta_len;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	if (meta_len != 8)
130562306a36Sopenharmony_ci		return false;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	meta_type = get_unaligned_be32(data);
130862306a36Sopenharmony_ci	meta_tag = get_unaligned_be32(data + 4);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	return (meta_type == NFP_NET_META_PORTID &&
131162306a36Sopenharmony_ci		meta_tag == NFP_META_PORT_ID_CTRL);
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic bool
131562306a36Sopenharmony_cinfp_ctrl_rx_one(struct nfp_net *nn, struct nfp_net_dp *dp,
131662306a36Sopenharmony_ci		struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring)
131762306a36Sopenharmony_ci{
131862306a36Sopenharmony_ci	unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off;
131962306a36Sopenharmony_ci	struct nfp_net_rx_buf *rxbuf;
132062306a36Sopenharmony_ci	struct nfp_net_rx_desc *rxd;
132162306a36Sopenharmony_ci	dma_addr_t new_dma_addr;
132262306a36Sopenharmony_ci	struct sk_buff *skb;
132362306a36Sopenharmony_ci	void *new_frag;
132462306a36Sopenharmony_ci	int idx;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	idx = D_IDX(rx_ring, rx_ring->rd_p);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	rxd = &rx_ring->rxds[idx];
132962306a36Sopenharmony_ci	if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD))
133062306a36Sopenharmony_ci		return false;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* Memory barrier to ensure that we won't do other reads
133362306a36Sopenharmony_ci	 * before the DD bit.
133462306a36Sopenharmony_ci	 */
133562306a36Sopenharmony_ci	dma_rmb();
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	rx_ring->rd_p++;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	rxbuf =	&rx_ring->rxbufs[idx];
134062306a36Sopenharmony_ci	meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK;
134162306a36Sopenharmony_ci	data_len = le16_to_cpu(rxd->rxd.data_len);
134262306a36Sopenharmony_ci	pkt_len = data_len - meta_len;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off;
134562306a36Sopenharmony_ci	if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC)
134662306a36Sopenharmony_ci		pkt_off += meta_len;
134762306a36Sopenharmony_ci	else
134862306a36Sopenharmony_ci		pkt_off += dp->rx_offset;
134962306a36Sopenharmony_ci	meta_off = pkt_off - meta_len;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* Stats update */
135262306a36Sopenharmony_ci	u64_stats_update_begin(&r_vec->rx_sync);
135362306a36Sopenharmony_ci	r_vec->rx_pkts++;
135462306a36Sopenharmony_ci	r_vec->rx_bytes += pkt_len;
135562306a36Sopenharmony_ci	u64_stats_update_end(&r_vec->rx_sync);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off,	data_len);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (unlikely(!nfp_ctrl_meta_ok(nn, rxbuf->frag + meta_off, meta_len))) {
136062306a36Sopenharmony_ci		nn_dp_warn(dp, "incorrect metadata for ctrl packet (%d)\n",
136162306a36Sopenharmony_ci			   meta_len);
136262306a36Sopenharmony_ci		nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL);
136362306a36Sopenharmony_ci		return true;
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	skb = build_skb(rxbuf->frag, dp->fl_bufsz);
136762306a36Sopenharmony_ci	if (unlikely(!skb)) {
136862306a36Sopenharmony_ci		nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL);
136962306a36Sopenharmony_ci		return true;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci	new_frag = nfp_nfd3_napi_alloc_one(dp, &new_dma_addr);
137262306a36Sopenharmony_ci	if (unlikely(!new_frag)) {
137362306a36Sopenharmony_ci		nfp_nfd3_rx_drop(dp, r_vec, rx_ring, rxbuf, skb);
137462306a36Sopenharmony_ci		return true;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	nfp_nfd3_rx_give_one(dp, rx_ring, new_frag, new_dma_addr);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	skb_reserve(skb, pkt_off);
138262306a36Sopenharmony_ci	skb_put(skb, pkt_len);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	nfp_app_ctrl_rx(nn->app, skb);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	return true;
138762306a36Sopenharmony_ci}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_cistatic bool nfp_ctrl_rx(struct nfp_net_r_vector *r_vec)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring;
139262306a36Sopenharmony_ci	struct nfp_net *nn = r_vec->nfp_net;
139362306a36Sopenharmony_ci	struct nfp_net_dp *dp = &nn->dp;
139462306a36Sopenharmony_ci	unsigned int budget = 512;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	while (nfp_ctrl_rx_one(nn, dp, r_vec, rx_ring) && budget--)
139762306a36Sopenharmony_ci		continue;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	return budget;
140062306a36Sopenharmony_ci}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_civoid nfp_nfd3_ctrl_poll(struct tasklet_struct *t)
140362306a36Sopenharmony_ci{
140462306a36Sopenharmony_ci	struct nfp_net_r_vector *r_vec = from_tasklet(r_vec, t, tasklet);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	spin_lock(&r_vec->lock);
140762306a36Sopenharmony_ci	nfp_nfd3_tx_complete(r_vec->tx_ring, 0);
140862306a36Sopenharmony_ci	__nfp_ctrl_tx_queued(r_vec);
140962306a36Sopenharmony_ci	spin_unlock(&r_vec->lock);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (nfp_ctrl_rx(r_vec)) {
141262306a36Sopenharmony_ci		nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry);
141362306a36Sopenharmony_ci	} else {
141462306a36Sopenharmony_ci		tasklet_schedule(&r_vec->tasklet);
141562306a36Sopenharmony_ci		nn_dp_warn(&r_vec->nfp_net->dp,
141662306a36Sopenharmony_ci			   "control message budget exceeded!\n");
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci}
1419