162306a36Sopenharmony_ci/* Broadcom NetXtreme-C/E network driver. 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright (c) 2016-2017 Broadcom Limited 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 662306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 762306a36Sopenharmony_ci * the Free Software Foundation. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/if_vlan.h> 1562306a36Sopenharmony_ci#include <linux/bpf.h> 1662306a36Sopenharmony_ci#include <linux/bpf_trace.h> 1762306a36Sopenharmony_ci#include <linux/filter.h> 1862306a36Sopenharmony_ci#include <net/page_pool/helpers.h> 1962306a36Sopenharmony_ci#include "bnxt_hsi.h" 2062306a36Sopenharmony_ci#include "bnxt.h" 2162306a36Sopenharmony_ci#include "bnxt_xdp.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(bnxt_xdp_locking_key); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp, 2662306a36Sopenharmony_ci struct bnxt_tx_ring_info *txr, 2762306a36Sopenharmony_ci dma_addr_t mapping, u32 len, 2862306a36Sopenharmony_ci struct xdp_buff *xdp) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct skb_shared_info *sinfo; 3162306a36Sopenharmony_ci struct bnxt_sw_tx_bd *tx_buf; 3262306a36Sopenharmony_ci struct tx_bd *txbd; 3362306a36Sopenharmony_ci int num_frags = 0; 3462306a36Sopenharmony_ci u32 flags; 3562306a36Sopenharmony_ci u16 prod; 3662306a36Sopenharmony_ci int i; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (xdp && xdp_buff_has_frags(xdp)) { 3962306a36Sopenharmony_ci sinfo = xdp_get_shared_info_from_buff(xdp); 4062306a36Sopenharmony_ci num_frags = sinfo->nr_frags; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* fill up the first buffer */ 4462306a36Sopenharmony_ci prod = txr->tx_prod; 4562306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[prod]; 4662306a36Sopenharmony_ci tx_buf->nr_frags = num_frags; 4762306a36Sopenharmony_ci if (xdp) 4862306a36Sopenharmony_ci tx_buf->page = virt_to_head_page(xdp->data); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)]; 5162306a36Sopenharmony_ci flags = (len << TX_BD_LEN_SHIFT) | 5262306a36Sopenharmony_ci ((num_frags + 1) << TX_BD_FLAGS_BD_CNT_SHIFT) | 5362306a36Sopenharmony_ci bnxt_lhint_arr[len >> 9]; 5462306a36Sopenharmony_ci txbd->tx_bd_len_flags_type = cpu_to_le32(flags); 5562306a36Sopenharmony_ci txbd->tx_bd_opaque = prod; 5662306a36Sopenharmony_ci txbd->tx_bd_haddr = cpu_to_le64(mapping); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* now let us fill up the frags into the next buffers */ 5962306a36Sopenharmony_ci for (i = 0; i < num_frags ; i++) { 6062306a36Sopenharmony_ci skb_frag_t *frag = &sinfo->frags[i]; 6162306a36Sopenharmony_ci struct bnxt_sw_tx_bd *frag_tx_buf; 6262306a36Sopenharmony_ci dma_addr_t frag_mapping; 6362306a36Sopenharmony_ci int frag_len; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci prod = NEXT_TX(prod); 6662306a36Sopenharmony_ci WRITE_ONCE(txr->tx_prod, prod); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* first fill up the first buffer */ 6962306a36Sopenharmony_ci frag_tx_buf = &txr->tx_buf_ring[prod]; 7062306a36Sopenharmony_ci frag_tx_buf->page = skb_frag_page(frag); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)]; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci frag_len = skb_frag_size(frag); 7562306a36Sopenharmony_ci flags = frag_len << TX_BD_LEN_SHIFT; 7662306a36Sopenharmony_ci txbd->tx_bd_len_flags_type = cpu_to_le32(flags); 7762306a36Sopenharmony_ci frag_mapping = page_pool_get_dma_addr(skb_frag_page(frag)) + 7862306a36Sopenharmony_ci skb_frag_off(frag); 7962306a36Sopenharmony_ci txbd->tx_bd_haddr = cpu_to_le64(frag_mapping); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci len = frag_len; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci flags &= ~TX_BD_LEN; 8562306a36Sopenharmony_ci txbd->tx_bd_len_flags_type = cpu_to_le32(((len) << TX_BD_LEN_SHIFT) | flags | 8662306a36Sopenharmony_ci TX_BD_FLAGS_PACKET_END); 8762306a36Sopenharmony_ci /* Sync TX BD */ 8862306a36Sopenharmony_ci wmb(); 8962306a36Sopenharmony_ci prod = NEXT_TX(prod); 9062306a36Sopenharmony_ci WRITE_ONCE(txr->tx_prod, prod); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return tx_buf; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void __bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, 9662306a36Sopenharmony_ci dma_addr_t mapping, u32 len, u16 rx_prod, 9762306a36Sopenharmony_ci struct xdp_buff *xdp) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct bnxt_sw_tx_bd *tx_buf; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci tx_buf = bnxt_xmit_bd(bp, txr, mapping, len, xdp); 10262306a36Sopenharmony_ci tx_buf->rx_prod = rx_prod; 10362306a36Sopenharmony_ci tx_buf->action = XDP_TX; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void __bnxt_xmit_xdp_redirect(struct bnxt *bp, 10862306a36Sopenharmony_ci struct bnxt_tx_ring_info *txr, 10962306a36Sopenharmony_ci dma_addr_t mapping, u32 len, 11062306a36Sopenharmony_ci struct xdp_frame *xdpf) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct bnxt_sw_tx_bd *tx_buf; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci tx_buf = bnxt_xmit_bd(bp, txr, mapping, len, NULL); 11562306a36Sopenharmony_ci tx_buf->action = XDP_REDIRECT; 11662306a36Sopenharmony_ci tx_buf->xdpf = xdpf; 11762306a36Sopenharmony_ci dma_unmap_addr_set(tx_buf, mapping, mapping); 11862306a36Sopenharmony_ci dma_unmap_len_set(tx_buf, len, 0); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct bnxt_tx_ring_info *txr = bnapi->tx_ring; 12462306a36Sopenharmony_ci struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; 12562306a36Sopenharmony_ci bool rx_doorbell_needed = false; 12662306a36Sopenharmony_ci int nr_pkts = bnapi->tx_pkts; 12762306a36Sopenharmony_ci struct bnxt_sw_tx_bd *tx_buf; 12862306a36Sopenharmony_ci u16 tx_cons = txr->tx_cons; 12962306a36Sopenharmony_ci u16 last_tx_cons = tx_cons; 13062306a36Sopenharmony_ci int i, j, frags; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!budget) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < nr_pkts; i++) { 13662306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[tx_cons]; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (tx_buf->action == XDP_REDIRECT) { 13962306a36Sopenharmony_ci struct pci_dev *pdev = bp->pdev; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci dma_unmap_single(&pdev->dev, 14262306a36Sopenharmony_ci dma_unmap_addr(tx_buf, mapping), 14362306a36Sopenharmony_ci dma_unmap_len(tx_buf, len), 14462306a36Sopenharmony_ci DMA_TO_DEVICE); 14562306a36Sopenharmony_ci xdp_return_frame(tx_buf->xdpf); 14662306a36Sopenharmony_ci tx_buf->action = 0; 14762306a36Sopenharmony_ci tx_buf->xdpf = NULL; 14862306a36Sopenharmony_ci } else if (tx_buf->action == XDP_TX) { 14962306a36Sopenharmony_ci tx_buf->action = 0; 15062306a36Sopenharmony_ci rx_doorbell_needed = true; 15162306a36Sopenharmony_ci last_tx_cons = tx_cons; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci frags = tx_buf->nr_frags; 15462306a36Sopenharmony_ci for (j = 0; j < frags; j++) { 15562306a36Sopenharmony_ci tx_cons = NEXT_TX(tx_cons); 15662306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[tx_cons]; 15762306a36Sopenharmony_ci page_pool_recycle_direct(rxr->page_pool, tx_buf->page); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci bnxt_sched_reset_txr(bp, txr, i); 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci tx_cons = NEXT_TX(tx_cons); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci bnapi->tx_pkts = 0; 16762306a36Sopenharmony_ci WRITE_ONCE(txr->tx_cons, tx_cons); 16862306a36Sopenharmony_ci if (rx_doorbell_needed) { 16962306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[last_tx_cons]; 17062306a36Sopenharmony_ci bnxt_db_write(bp, &rxr->rx_db, tx_buf->rx_prod); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cibool bnxt_xdp_attached(struct bnxt *bp, struct bnxt_rx_ring_info *rxr) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct bpf_prog *xdp_prog = READ_ONCE(rxr->xdp_prog); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return !!xdp_prog; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_civoid bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, 18362306a36Sopenharmony_ci u16 cons, u8 *data_ptr, unsigned int len, 18462306a36Sopenharmony_ci struct xdp_buff *xdp) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 buflen = BNXT_RX_PAGE_SIZE; 18762306a36Sopenharmony_ci struct bnxt_sw_rx_bd *rx_buf; 18862306a36Sopenharmony_ci struct pci_dev *pdev; 18962306a36Sopenharmony_ci dma_addr_t mapping; 19062306a36Sopenharmony_ci u32 offset; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pdev = bp->pdev; 19362306a36Sopenharmony_ci rx_buf = &rxr->rx_buf_ring[cons]; 19462306a36Sopenharmony_ci offset = bp->rx_offset; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci mapping = rx_buf->mapping - bp->rx_dma_offset; 19762306a36Sopenharmony_ci dma_sync_single_for_cpu(&pdev->dev, mapping + offset, len, bp->rx_dir); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci xdp_init_buff(xdp, buflen, &rxr->xdp_rxq); 20062306a36Sopenharmony_ci xdp_prepare_buff(xdp, data_ptr - offset, offset, len, false); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_civoid bnxt_xdp_buff_frags_free(struct bnxt_rx_ring_info *rxr, 20462306a36Sopenharmony_ci struct xdp_buff *xdp) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct skb_shared_info *shinfo; 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!xdp || !xdp_buff_has_frags(xdp)) 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci shinfo = xdp_get_shared_info_from_buff(xdp); 21262306a36Sopenharmony_ci for (i = 0; i < shinfo->nr_frags; i++) { 21362306a36Sopenharmony_ci struct page *page = skb_frag_page(&shinfo->frags[i]); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci page_pool_recycle_direct(rxr->page_pool, page); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci shinfo->nr_frags = 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* returns the following: 22162306a36Sopenharmony_ci * true - packet consumed by XDP and new buffer is allocated. 22262306a36Sopenharmony_ci * false - packet should be passed to the stack. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cibool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, 22562306a36Sopenharmony_ci struct xdp_buff xdp, struct page *page, u8 **data_ptr, 22662306a36Sopenharmony_ci unsigned int *len, u8 *event) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct bpf_prog *xdp_prog = READ_ONCE(rxr->xdp_prog); 22962306a36Sopenharmony_ci struct bnxt_tx_ring_info *txr; 23062306a36Sopenharmony_ci struct bnxt_sw_rx_bd *rx_buf; 23162306a36Sopenharmony_ci struct pci_dev *pdev; 23262306a36Sopenharmony_ci dma_addr_t mapping; 23362306a36Sopenharmony_ci u32 tx_needed = 1; 23462306a36Sopenharmony_ci void *orig_data; 23562306a36Sopenharmony_ci u32 tx_avail; 23662306a36Sopenharmony_ci u32 offset; 23762306a36Sopenharmony_ci u32 act; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!xdp_prog) 24062306a36Sopenharmony_ci return false; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pdev = bp->pdev; 24362306a36Sopenharmony_ci offset = bp->rx_offset; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci txr = rxr->bnapi->tx_ring; 24662306a36Sopenharmony_ci /* BNXT_RX_PAGE_MODE(bp) when XDP enabled */ 24762306a36Sopenharmony_ci orig_data = xdp.data; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci act = bpf_prog_run_xdp(xdp_prog, &xdp); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci tx_avail = bnxt_tx_avail(bp, txr); 25262306a36Sopenharmony_ci /* If the tx ring is not full, we must not update the rx producer yet 25362306a36Sopenharmony_ci * because we may still be transmitting on some BDs. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci if (tx_avail != bp->tx_ring_size) 25662306a36Sopenharmony_ci *event &= ~BNXT_RX_EVENT; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci *len = xdp.data_end - xdp.data; 25962306a36Sopenharmony_ci if (orig_data != xdp.data) { 26062306a36Sopenharmony_ci offset = xdp.data - xdp.data_hard_start; 26162306a36Sopenharmony_ci *data_ptr = xdp.data_hard_start + offset; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci switch (act) { 26562306a36Sopenharmony_ci case XDP_PASS: 26662306a36Sopenharmony_ci return false; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci case XDP_TX: 26962306a36Sopenharmony_ci rx_buf = &rxr->rx_buf_ring[cons]; 27062306a36Sopenharmony_ci mapping = rx_buf->mapping - bp->rx_dma_offset; 27162306a36Sopenharmony_ci *event = 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (unlikely(xdp_buff_has_frags(&xdp))) { 27462306a36Sopenharmony_ci struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(&xdp); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci tx_needed += sinfo->nr_frags; 27762306a36Sopenharmony_ci *event = BNXT_AGG_EVENT; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (tx_avail < tx_needed) { 28162306a36Sopenharmony_ci trace_xdp_exception(bp->dev, xdp_prog, act); 28262306a36Sopenharmony_ci bnxt_xdp_buff_frags_free(rxr, &xdp); 28362306a36Sopenharmony_ci bnxt_reuse_rx_data(rxr, cons, page); 28462306a36Sopenharmony_ci return true; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dma_sync_single_for_device(&pdev->dev, mapping + offset, *len, 28862306a36Sopenharmony_ci bp->rx_dir); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci *event |= BNXT_TX_EVENT; 29162306a36Sopenharmony_ci __bnxt_xmit_xdp(bp, txr, mapping + offset, *len, 29262306a36Sopenharmony_ci NEXT_RX(rxr->rx_prod), &xdp); 29362306a36Sopenharmony_ci bnxt_reuse_rx_data(rxr, cons, page); 29462306a36Sopenharmony_ci return true; 29562306a36Sopenharmony_ci case XDP_REDIRECT: 29662306a36Sopenharmony_ci /* if we are calling this here then we know that the 29762306a36Sopenharmony_ci * redirect is coming from a frame received by the 29862306a36Sopenharmony_ci * bnxt_en driver. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci rx_buf = &rxr->rx_buf_ring[cons]; 30162306a36Sopenharmony_ci mapping = rx_buf->mapping - bp->rx_dma_offset; 30262306a36Sopenharmony_ci dma_unmap_page_attrs(&pdev->dev, mapping, 30362306a36Sopenharmony_ci BNXT_RX_PAGE_SIZE, bp->rx_dir, 30462306a36Sopenharmony_ci DMA_ATTR_WEAK_ORDERING); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* if we are unable to allocate a new buffer, abort and reuse */ 30762306a36Sopenharmony_ci if (bnxt_alloc_rx_data(bp, rxr, rxr->rx_prod, GFP_ATOMIC)) { 30862306a36Sopenharmony_ci trace_xdp_exception(bp->dev, xdp_prog, act); 30962306a36Sopenharmony_ci bnxt_xdp_buff_frags_free(rxr, &xdp); 31062306a36Sopenharmony_ci bnxt_reuse_rx_data(rxr, cons, page); 31162306a36Sopenharmony_ci return true; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (xdp_do_redirect(bp->dev, &xdp, xdp_prog)) { 31562306a36Sopenharmony_ci trace_xdp_exception(bp->dev, xdp_prog, act); 31662306a36Sopenharmony_ci page_pool_recycle_direct(rxr->page_pool, page); 31762306a36Sopenharmony_ci return true; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci *event |= BNXT_REDIRECT_EVENT; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci default: 32362306a36Sopenharmony_ci bpf_warn_invalid_xdp_action(bp->dev, xdp_prog, act); 32462306a36Sopenharmony_ci fallthrough; 32562306a36Sopenharmony_ci case XDP_ABORTED: 32662306a36Sopenharmony_ci trace_xdp_exception(bp->dev, xdp_prog, act); 32762306a36Sopenharmony_ci fallthrough; 32862306a36Sopenharmony_ci case XDP_DROP: 32962306a36Sopenharmony_ci bnxt_xdp_buff_frags_free(rxr, &xdp); 33062306a36Sopenharmony_ci bnxt_reuse_rx_data(rxr, cons, page); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci return true; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ciint bnxt_xdp_xmit(struct net_device *dev, int num_frames, 33762306a36Sopenharmony_ci struct xdp_frame **frames, u32 flags) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct bnxt *bp = netdev_priv(dev); 34062306a36Sopenharmony_ci struct bpf_prog *xdp_prog = READ_ONCE(bp->xdp_prog); 34162306a36Sopenharmony_ci struct pci_dev *pdev = bp->pdev; 34262306a36Sopenharmony_ci struct bnxt_tx_ring_info *txr; 34362306a36Sopenharmony_ci dma_addr_t mapping; 34462306a36Sopenharmony_ci int nxmit = 0; 34562306a36Sopenharmony_ci int ring; 34662306a36Sopenharmony_ci int i; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (!test_bit(BNXT_STATE_OPEN, &bp->state) || 34962306a36Sopenharmony_ci !bp->tx_nr_rings_xdp || 35062306a36Sopenharmony_ci !xdp_prog) 35162306a36Sopenharmony_ci return -EINVAL; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ring = smp_processor_id() % bp->tx_nr_rings_xdp; 35462306a36Sopenharmony_ci txr = &bp->tx_ring[ring]; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (READ_ONCE(txr->dev_state) == BNXT_DEV_STATE_CLOSING) 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (static_branch_unlikely(&bnxt_xdp_locking_key)) 36062306a36Sopenharmony_ci spin_lock(&txr->xdp_tx_lock); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci for (i = 0; i < num_frames; i++) { 36362306a36Sopenharmony_ci struct xdp_frame *xdp = frames[i]; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!bnxt_tx_avail(bp, txr)) 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci mapping = dma_map_single(&pdev->dev, xdp->data, xdp->len, 36962306a36Sopenharmony_ci DMA_TO_DEVICE); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (dma_mapping_error(&pdev->dev, mapping)) 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci __bnxt_xmit_xdp_redirect(bp, txr, mapping, xdp->len, xdp); 37562306a36Sopenharmony_ci nxmit++; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (flags & XDP_XMIT_FLUSH) { 37962306a36Sopenharmony_ci /* Sync BD data before updating doorbell */ 38062306a36Sopenharmony_ci wmb(); 38162306a36Sopenharmony_ci bnxt_db_write(bp, &txr->tx_db, txr->tx_prod); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (static_branch_unlikely(&bnxt_xdp_locking_key)) 38562306a36Sopenharmony_ci spin_unlock(&txr->xdp_tx_lock); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return nxmit; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* Under rtnl_lock */ 39162306a36Sopenharmony_cistatic int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct net_device *dev = bp->dev; 39462306a36Sopenharmony_ci int tx_xdp = 0, rc, tc; 39562306a36Sopenharmony_ci struct bpf_prog *old; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (prog && !prog->aux->xdp_has_frags && 39862306a36Sopenharmony_ci bp->dev->mtu > BNXT_MAX_PAGE_MODE_MTU) { 39962306a36Sopenharmony_ci netdev_warn(dev, "MTU %d larger than %d without XDP frag support.\n", 40062306a36Sopenharmony_ci bp->dev->mtu, BNXT_MAX_PAGE_MODE_MTU); 40162306a36Sopenharmony_ci return -EOPNOTSUPP; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci if (!(bp->flags & BNXT_FLAG_SHARED_RINGS)) { 40462306a36Sopenharmony_ci netdev_warn(dev, "ethtool rx/tx channels must be combined to support XDP.\n"); 40562306a36Sopenharmony_ci return -EOPNOTSUPP; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci if (prog) 40862306a36Sopenharmony_ci tx_xdp = bp->rx_nr_rings; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci tc = netdev_get_num_tc(dev); 41162306a36Sopenharmony_ci if (!tc) 41262306a36Sopenharmony_ci tc = 1; 41362306a36Sopenharmony_ci rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, 41462306a36Sopenharmony_ci true, tc, tx_xdp); 41562306a36Sopenharmony_ci if (rc) { 41662306a36Sopenharmony_ci netdev_warn(dev, "Unable to reserve enough TX rings to support XDP.\n"); 41762306a36Sopenharmony_ci return rc; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci if (netif_running(dev)) 42062306a36Sopenharmony_ci bnxt_close_nic(bp, true, false); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci old = xchg(&bp->xdp_prog, prog); 42362306a36Sopenharmony_ci if (old) 42462306a36Sopenharmony_ci bpf_prog_put(old); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (prog) { 42762306a36Sopenharmony_ci bnxt_set_rx_skb_mode(bp, true); 42862306a36Sopenharmony_ci xdp_features_set_redirect_target(dev, true); 42962306a36Sopenharmony_ci } else { 43062306a36Sopenharmony_ci int rx, tx; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci xdp_features_clear_redirect_target(dev); 43362306a36Sopenharmony_ci bnxt_set_rx_skb_mode(bp, false); 43462306a36Sopenharmony_ci bnxt_get_max_rings(bp, &rx, &tx, true); 43562306a36Sopenharmony_ci if (rx > 1) { 43662306a36Sopenharmony_ci bp->flags &= ~BNXT_FLAG_NO_AGG_RINGS; 43762306a36Sopenharmony_ci bp->dev->hw_features |= NETIF_F_LRO; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci bp->tx_nr_rings_xdp = tx_xdp; 44162306a36Sopenharmony_ci bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp; 44262306a36Sopenharmony_ci bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings); 44362306a36Sopenharmony_ci bnxt_set_tpa_flags(bp); 44462306a36Sopenharmony_ci bnxt_set_ring_params(bp); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (netif_running(dev)) 44762306a36Sopenharmony_ci return bnxt_open_nic(bp, true, false); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciint bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct bnxt *bp = netdev_priv(dev); 45562306a36Sopenharmony_ci int rc; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci switch (xdp->command) { 45862306a36Sopenharmony_ci case XDP_SETUP_PROG: 45962306a36Sopenharmony_ci rc = bnxt_xdp_set(bp, xdp->prog); 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci default: 46262306a36Sopenharmony_ci rc = -EINVAL; 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci return rc; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistruct sk_buff * 46962306a36Sopenharmony_cibnxt_xdp_build_skb(struct bnxt *bp, struct sk_buff *skb, u8 num_frags, 47062306a36Sopenharmony_ci struct page_pool *pool, struct xdp_buff *xdp, 47162306a36Sopenharmony_ci struct rx_cmp_ext *rxcmp1) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!skb) 47662306a36Sopenharmony_ci return NULL; 47762306a36Sopenharmony_ci skb_checksum_none_assert(skb); 47862306a36Sopenharmony_ci if (RX_CMP_L4_CS_OK(rxcmp1)) { 47962306a36Sopenharmony_ci if (bp->dev->features & NETIF_F_RXCSUM) { 48062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 48162306a36Sopenharmony_ci skb->csum_level = RX_CMP_ENCAP(rxcmp1); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci xdp_update_skb_shared_info(skb, num_frags, 48562306a36Sopenharmony_ci sinfo->xdp_frags_size, 48662306a36Sopenharmony_ci BNXT_RX_PAGE_SIZE * sinfo->nr_frags, 48762306a36Sopenharmony_ci xdp_buff_is_frag_pfmemalloc(xdp)); 48862306a36Sopenharmony_ci return skb; 48962306a36Sopenharmony_ci} 490