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