Let bnxt_en act as a knod NIC: register with the knod core, feed
received packets to the accelerator over the per-queue SPSC ring, and
transmit accelerator verdicts back through the XDP TX ring.  Restricted
to page-mode MTU.

Signed-off-by: Taehee Yoo <[email protected]>
(cherry picked from commit c9ef892dd5859cc71b0f8a546d87032cf70bb442)
---
 drivers/net/ethernet/broadcom/bnxt/bnxt.c     | 155 +++++++++-
 drivers/net/ethernet/broadcom/bnxt/bnxt.h     |  12 +-
 drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 274 +++++++++++++++++-
 drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h |  12 +-
 4 files changed, 442 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c 
b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 7513618793da..6ddf4bafc3d4 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -993,11 +993,36 @@ static inline u8 *__bnxt_alloc_rx_frag(struct bnxt *bp, 
dma_addr_t *mapping,
        return page_address(page) + offset;
 }
 
+static int bnxt_alloc_rx_off_netmem(struct bnxt *bp,
+                                   struct bnxt_rx_ring_info *rxr,
+                                   u16 prod, gfp_t gfp)
+{
+       struct rx_bd *rxbd =
+               &rxr->rx_desc_ring[RX_RING(bp, prod)][RX_IDX(prod)];
+       struct bnxt_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[RING_RX(bp, prod)];
+       unsigned int offset = 0;
+       dma_addr_t mapping;
+       netmem_ref netmem;
+
+       netmem = __bnxt_alloc_rx_netmem(bp, &mapping, rxr, &offset, gfp);
+       if (!netmem)
+               return -ENOMEM;
+
+       mapping += bp->rx_dma_offset;
+       rx_buf->data = (void *)netmem;
+       rx_buf->data_ptr = netmem_address(netmem) + bp->rx_offset;
+       rx_buf->mapping = mapping;
+
+       rxbd->rx_bd_haddr = cpu_to_le64(mapping);
+       return 0;
+}
+
 int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
                       u16 prod, gfp_t gfp)
 {
        struct rx_bd *rxbd = &rxr->rx_desc_ring[RX_RING(bp, 
prod)][RX_IDX(prod)];
        struct bnxt_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[RING_RX(bp, prod)];
+       unsigned int offset = 0;
        dma_addr_t mapping;
 
        if (BNXT_RX_PAGE_MODE(bp)) {
@@ -1012,6 +1037,16 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct 
bnxt_rx_ring_info *rxr,
                rx_buf->data = page;
                rx_buf->data_ptr = page_address(page) + offset + bp->rx_offset;
                rx_buf->offset = offset;
+       } else if (BNXT_RX_OFFLOAD_MODE(bp)) {
+               netmem_ref netmem = __bnxt_alloc_rx_netmem(bp, &mapping, rxr,
+                                                          &offset, gfp);
+               if (!netmem)
+                       return -ENOMEM;
+
+               mapping += bp->rx_dma_offset;
+               rx_buf->data = (void *)netmem;
+               rx_buf->data_ptr = netmem_address(netmem) + bp->rx_offset;
+               rx_buf->offset = offset;
        } else {
                u8 *data = __bnxt_alloc_rx_frag(bp, &mapping, rxr, gfp);
 
@@ -1250,6 +1285,25 @@ static struct sk_buff *bnxt_rx_page_skb(struct bnxt *bp,
        return skb;
 }
 
+static struct sk_buff *bnxt_rx_offload_netmem(struct bnxt *bp,
+                                             struct bnxt_rx_ring_info *rxr,
+                                             u16 cons, void *data,
+                                             u8 *data_ptr,
+                                             dma_addr_t dma_addr,
+                                             unsigned int offset_and_len)
+{
+       u16 prod = rxr->rx_prod;
+       int err;
+
+       err = bnxt_alloc_rx_off_netmem(bp, rxr, prod, GFP_ATOMIC);
+       if (unlikely(err)) {
+               bnxt_reuse_rx_data(rxr, cons, data);
+               return NULL;
+       }
+
+       return NULL;
+}
+
 static struct sk_buff *bnxt_rx_skb(struct bnxt *bp,
                                   struct bnxt_rx_ring_info *rxr, u16 cons,
                                   void *data, u8 *data_ptr,
@@ -2249,6 +2303,36 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct 
bnxt_cp_ring_info *cpr,
        len = flags >> RX_CMP_LEN_SHIFT;
        dma_addr = rx_buf->mapping;
 
+       if (BNXT_RX_OFFLOAD_MODE(bp)) {
+               if (bnxt_alloc_rx_off_netmem(bp, rxr, rxr->rx_prod,
+                                            GFP_ATOMIC)) {
+                       bnxt_reuse_rx_data(rxr, cons, data);
+                       bnapi->cp_ring.sw_stats->rx.rx_buf_errors++;
+               } else {
+                       struct knod_work_priv *wpriv;
+                       struct spsc_bd *bd;
+
+                       if (bnapi->index >= KNOD_SPSC_MAX) {
+                               rc = 1;
+                               goto next_rx;
+                       }
+
+                       wpriv = &bp->knodev->wpriv[bnapi->index];
+                       if (spsc_produce(&wpriv->spsc_bds, (void **)&bd)) {
+                               bnxt_reuse_rx_data(rxr, cons, data);
+                       } else {
+                               bd->netmem = (netmem_ref)data;
+                               bd->len = len;
+                               bd->off = bp->rx_offset;
+                               bd->page_idx = net_iov_binding_idx(
+                                       netmem_to_net_iov((netmem_ref)data));
+                               spsc_produce_commit(&wpriv->spsc_bds);
+                       }
+               }
+               rc = 1;
+               goto next_rx;
+       }
+
        if (bnxt_xdp_attached(bp, rxr)) {
                bnxt_xdp.rxcmp = rxcmp;
                bnxt_xdp.rxcmp1 = rxcmp1;
@@ -3272,6 +3356,11 @@ static int bnxt_poll(struct napi_struct *napi, int 
budget)
                napi_complete(napi);
                return 0;
        }
+
+       if (BNXT_RX_OFFLOAD_MODE(bp))
+               work_done += bnxt_rx_offload_act_handler(bnapi,
+                                                        budget - work_done);
+
        while (1) {
                work_done += bnxt_poll_work(bp, cpr, budget - work_done);
 
@@ -3296,6 +3385,7 @@ static int bnxt_poll(struct napi_struct *napi, int budget)
                                  &dim_sample);
                net_dim(&cpr->dim, &dim_sample);
        }
+
        return work_done;
 }
 
@@ -3363,6 +3453,11 @@ static int bnxt_poll_p5(struct napi_struct *napi, int 
budget)
                cpr->has_more_work = 0;
                work_done = __bnxt_poll_cqs(bp, bnapi, budget);
        }
+
+       if (BNXT_RX_OFFLOAD_MODE(bp))
+               work_done += bnxt_rx_offload_act_handler(bnapi,
+                                                        budget - work_done);
+
        while (1) {
                u16 type;
 
@@ -3427,6 +3522,7 @@ static int bnxt_poll_p5(struct napi_struct *napi, int 
budget)
                                  &dim_sample);
                net_dim(&cpr->dim, &dim_sample);
        }
+
        return work_done;
 }
 
@@ -3458,6 +3554,13 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
                        tx_buf->xdpf = NULL;
                        i++;
                        continue;
+               } else if (tx_buf->action == BNXT_NETMEM_TX) {
+                       page_pool_recycle_direct_netmem(
+                               netmem_get_pp(tx_buf->netmem),
+                               tx_buf->netmem);
+                       tx_buf->action = 0;
+                       tx_buf->netmem = 0;
+                       continue;
                }
 
                skb = tx_buf->skb;
@@ -3551,6 +3654,10 @@ static void bnxt_free_one_rx_ring(struct bnxt *bp, 
struct bnxt_rx_ring_info *rxr
                rx_buf->data = NULL;
                if (BNXT_RX_PAGE_MODE(bp))
                        page_pool_recycle_direct(rxr->page_pool, data);
+               else if (BNXT_RX_OFFLOAD_MODE(bp))
+                       page_pool_put_full_netmem(
+                               netmem_get_pp((netmem_ref)data),
+                               (netmem_ref)data, false);
                else
                        page_pool_free_va(rxr->head_pool, data, true);
        }
@@ -3905,7 +4012,7 @@ static int bnxt_alloc_rx_page_pool(struct bnxt *bp,
        struct page_pool *pool;
 
        pp.pool_size = bnxt_rx_agg_ring_fill_level(bp, rxr) / agg_size_fac;
-       if (BNXT_RX_PAGE_MODE(bp))
+       if (BNXT_RX_PAGE_MODE(bp) || BNXT_RX_OFFLOAD_MODE(bp))
                pp.pool_size += bp->rx_ring_size / rx_size_fac;
 
        pp.order = get_order(rxr->rx_page_size);
@@ -3923,8 +4030,10 @@ static int bnxt_alloc_rx_page_pool(struct bnxt *bp,
                return PTR_ERR(pool);
        rxr->page_pool = pool;
 
-       rxr->need_head_pool = page_pool_is_unreadable(pool);
-       rxr->need_head_pool |= !!pp.order;
+       if (!BNXT_RX_OFFLOAD_MODE(bp)) {
+               rxr->need_head_pool = page_pool_is_unreadable(pool);
+               rxr->need_head_pool |= !!pp.order;
+       }
        if (bnxt_separate_head_pool(rxr)) {
                pp.order = 0;
                pp.max_len = PAGE_SIZE;
@@ -4673,6 +4782,9 @@ static int bnxt_init_rx_rings(struct bnxt *bp)
        if (BNXT_RX_PAGE_MODE(bp)) {
                bp->rx_offset = NET_IP_ALIGN + XDP_PACKET_HEADROOM;
                bp->rx_dma_offset = XDP_PACKET_HEADROOM;
+       } else if (BNXT_RX_OFFLOAD_MODE(bp)) {
+               bp->rx_offset = NET_IP_ALIGN + XDP_PACKET_HEADROOM;
+               bp->rx_dma_offset = XDP_PACKET_HEADROOM;
        } else {
                bp->rx_offset = BNXT_RX_OFFSET;
                bp->rx_dma_offset = BNXT_RX_DMA_OFFSET;
@@ -4961,12 +5073,14 @@ void bnxt_set_ring_params(struct bnxt *bp)
 /* Changing allocation mode of RX rings.
  * TODO: Update when extending xdp_rxq_info to support allocation modes.
  */
-static void __bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode)
+static void __bnxt_set_rx_skb_mode(struct bnxt *bp, int page_mode)
 {
        struct net_device *dev = bp->dev;
 
-       if (page_mode) {
-               bp->flags &= ~(BNXT_FLAG_AGG_RINGS | BNXT_FLAG_NO_AGG_RINGS);
+       if (page_mode & BNXT_FLAG_RX_PAGE_MODE) {
+               bp->flags &= ~(BNXT_FLAG_AGG_RINGS |
+                              BNXT_FLAG_NO_AGG_RINGS |
+                              BNXT_FLAG_RX_OFFLOAD_MODE);
                bp->flags |= BNXT_FLAG_RX_PAGE_MODE;
 
                if (bp->xdp_prog->aux->xdp_has_frags)
@@ -4982,15 +5096,24 @@ static void __bnxt_set_rx_skb_mode(struct bnxt *bp, 
bool page_mode)
                        bp->rx_skb_func = bnxt_rx_page_skb;
                }
                bp->rx_dir = DMA_BIDIRECTIONAL;
+       } else if (page_mode & BNXT_FLAG_RX_OFFLOAD_MODE) {
+               bp->flags &= ~(BNXT_FLAG_AGG_RINGS |
+                              BNXT_FLAG_RX_PAGE_MODE);
+               bp->flags |= (BNXT_FLAG_RX_OFFLOAD_MODE |
+                             BNXT_FLAG_NO_AGG_RINGS);
+               dev->max_mtu = min_t(u16, bp->max_mtu, BNXT_MAX_PAGE_MODE_MTU);
+               bp->rx_skb_func = bnxt_rx_offload_netmem;
+               bp->rx_dir = DMA_BIDIRECTIONAL;
        } else {
                dev->max_mtu = bp->max_mtu;
                bp->flags &= ~BNXT_FLAG_RX_PAGE_MODE;
+               bp->flags &= ~BNXT_FLAG_RX_OFFLOAD_MODE;
                bp->rx_dir = DMA_FROM_DEVICE;
                bp->rx_skb_func = bnxt_rx_skb;
        }
 }
 
-void bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode)
+void bnxt_set_rx_skb_mode(struct bnxt *bp, int page_mode)
 {
        __bnxt_set_rx_skb_mode(bp, page_mode);
 
@@ -5663,6 +5786,12 @@ static int bnxt_alloc_mem(struct bnxt *bp, bool 
irq_re_init)
                                        BNXT_RING_TO_TC(bp, txr->txq_index);
                                bnapi2->tx_ring[txr->tx_napi_idx] = txr;
                                bnapi2->tx_int = bnxt_tx_int;
+                       } else if (BNXT_RX_OFFLOAD_MODE(bp)) {
+                               bnapi2 = bp->bnapi[j];
+                               bnapi2->flags |= BNXT_NAPI_FLAG_XDP;
+                               bnapi2->tx_ring[0] = txr;
+                               bnapi2->tx_int = bnxt_tx_int_xdp;
+                               j++;
                        } else {
                                bnapi2 = bp->bnapi[j];
                                bnapi2->flags |= BNXT_NAPI_FLAG_XDP;
@@ -11958,6 +12087,8 @@ static void bnxt_del_napi(struct bnxt *bp)
        if (!bp->bnapi)
                return;
 
+       bnxt_rx_offload_clear_napi(bp);
+
        for (i = 0; i < bp->rx_nr_rings; i++)
                netif_queue_set_napi(bp->dev, i, NETDEV_QUEUE_TYPE_RX, NULL);
        for (i = 0; i < bp->tx_nr_rings - bp->tx_nr_rings_xdp; i++)
@@ -11997,6 +12128,8 @@ static void bnxt_init_napi(struct bnxt *bp)
                bnapi = bp->bnapi[cp_nr_rings];
                netif_napi_add_locked(bp->dev, &bnapi->napi, bnxt_poll_nitroa0);
        }
+
+       bnxt_rx_offload_set_napi(bp);
 }
 
 static void bnxt_disable_napi(struct bnxt *bp)
@@ -13356,6 +13489,7 @@ static int bnxt_open(struct net_device *dev)
                                                   BNXT_RESTART_ULP_SP_EVENT);
                }
        }
+       bnxt_rx_offload_start(bp);
 
        return rc;
 }
@@ -13443,6 +13577,7 @@ static int bnxt_close(struct net_device *dev)
 {
        struct bnxt *bp = netdev_priv(dev);
 
+       bnxt_rx_offload_stop(bp);
        bnxt_close_nic(bp, true, true);
        bnxt_hwrm_shutdown_link(bp);
        bnxt_hwrm_if_change(bp, false);
@@ -13643,6 +13778,8 @@ bnxt_get_stats64(struct net_device *dev, struct 
rtnl_link_stats64 *stats)
                stats->tx_errors = BNXT_GET_TX_PORT_STATS64(tx, tx_err);
        }
        clear_bit(BNXT_STATE_READ_STATS, &bp->state);
+       if (BNXT_RX_OFFLOAD_MODE(bp))
+               knod_dev_get_stats64(bp->knodev, stats);
 }
 
 static void bnxt_get_one_ring_drv_stats(struct bnxt *bp,
@@ -16518,6 +16655,7 @@ static void bnxt_remove_one(struct pci_dev *pdev)
 
        bnxt_aux_devices_del(bp);
 
+       bnxt_knod_uninit(bp);
        unregister_netdev(dev);
        bnxt_ptp_clear(bp);
 
@@ -17080,7 +17218,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const 
struct pci_device_id *ent)
        }
 
        dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
-                           NETDEV_XDP_ACT_RX_SG;
+                           NETDEV_XDP_ACT_RX_SG | NETDEV_XDP_ACT_HW_OFFLOAD;
 
 #ifdef CONFIG_BNXT_SRIOV
        init_waitqueue_head(&bp->sriov_cfg_wait);
@@ -17198,6 +17336,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const 
struct pci_device_id *ent)
        bnxt_print_device_info(bp);
 
        pci_save_state(pdev);
+       bnxt_knod_init(bp);
 
        return 0;
 init_err_cleanup:
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h 
b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 6335dfc14c98..d99590db6efa 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -29,6 +29,8 @@
 #include <net/devlink.h>
 #include <net/dst_metadata.h>
 #include <net/xdp.h>
+#include <net/knod.h>
+#include <net/spsc_ring.h>
 #include <linux/dim.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/bnxt/ulp.h>
@@ -884,10 +886,14 @@ struct nqe_cn {
 #define BNXT_REDIRECT_EVENT    8
 #define BNXT_TX_CMP_EVENT      0x10
 
+#define BNXT_NETMEM_ACT                0xf0
+#define BNXT_NETMEM_TX         (XDP_TX + BNXT_NETMEM_ACT)
+
 struct bnxt_sw_tx_bd {
        union {
                struct sk_buff          *skb;
                struct xdp_frame        *xdpf;
+               netmem_ref              netmem;
        };
        DEFINE_DMA_UNMAP_ADDR(mapping);
        DEFINE_DMA_UNMAP_LEN(len);
@@ -2300,6 +2306,7 @@ struct bnxt {
        #define BNXT_FLAG_TX_COAL_CMPL  0x8000000
        #define BNXT_FLAG_PORT_STATS_EXT        0x10000000
        #define BNXT_FLAG_HDS           0x20000000
+       #define BNXT_FLAG_RX_OFFLOAD_MODE       0x40000000
        #define BNXT_FLAG_AGG_RINGS     (BNXT_FLAG_JUMBO | BNXT_FLAG_GRO | \
                                         BNXT_FLAG_LRO | BNXT_FLAG_HDS)
 
@@ -2324,6 +2331,7 @@ struct bnxt {
                                 (bp)->link_info.phy_state == 
BNXT_PHY_STATE_ENABLED)
 #define BNXT_CHIP_TYPE_NITRO_A0(bp) ((bp)->flags & BNXT_FLAG_CHIP_NITRO_A0)
 #define BNXT_RX_PAGE_MODE(bp)  ((bp)->flags & BNXT_FLAG_RX_PAGE_MODE)
+#define BNXT_RX_OFFLOAD_MODE(bp)       ((bp)->flags & 
BNXT_FLAG_RX_OFFLOAD_MODE)
 #define BNXT_SUPPORTS_TPA(bp)  (!BNXT_CHIP_TYPE_NITRO_A0(bp) &&        \
                                 (!((bp)->flags & BNXT_FLAG_CHIP_P5_PLUS) ||\
                                  (bp)->max_tpa_v2) && !is_kdump_kernel())
@@ -2748,6 +2756,8 @@ struct bnxt {
 #define BNXT_DUMP_LIVE_WITH_CTX_L1_CACHE       3
 
        struct bpf_prog         *xdp_prog;
+       struct knod_netdev      *knetdev;
+       struct knod_dev *knodev;
 
        struct bnxt_ptp_cfg     *ptp_cfg;
        u8                      ptp_all_rx_tstamp;
@@ -2964,7 +2974,7 @@ u32 bnxt_fw_health_readl(struct bnxt *bp, int reg_idx);
 bool bnxt_bs_trace_avail(struct bnxt *bp, u16 type);
 void bnxt_set_tpa_flags(struct bnxt *bp);
 void bnxt_set_ring_params(struct bnxt *);
-void bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode);
+void bnxt_set_rx_skb_mode(struct bnxt *bp, int page_mode);
 void bnxt_insert_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr);
 void bnxt_del_one_usr_fltr(struct bnxt *bp, struct bnxt_filter_base *fltr);
 int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp, unsigned long *bmap,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c 
b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
index 9e5009be8e98..a264a2cf8302 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
@@ -156,6 +156,11 @@ void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi 
*bnapi, int budget)
                                tx_buf = &txr->tx_buf_ring[RING_TX(bp, 
tx_cons)];
                                page_pool_recycle_direct(rxr->page_pool, 
tx_buf->page);
                        }
+               } else if (tx_buf->action == BNXT_NETMEM_TX) {
+                       page_pool_recycle_direct_netmem(rxr->page_pool,
+                                                       tx_buf->netmem);
+                       tx_buf->action = 0;
+                       tx_buf->netmem = 0;
                } else {
                        bnxt_sched_reset_txr(bp, txr, tx_cons);
                        return;
@@ -423,7 +428,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog 
*prog)
                bpf_prog_put(old);
 
        if (prog) {
-               bnxt_set_rx_skb_mode(bp, true);
+               bnxt_set_rx_skb_mode(bp, BNXT_FLAG_RX_PAGE_MODE);
                xdp_features_set_redirect_target_locked(dev, true);
        } else {
                xdp_features_clear_redirect_target_locked(dev);
@@ -441,19 +446,123 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog 
*prog)
        return 0;
 }
 
+
+static int bnxt_xdp_offload_set(struct bnxt *bp, int enable)
+{
+       struct net_device *dev = bp->dev;
+       int tx_xdp = 0, tx_cp, rc, tc;
+
+       netdev_assert_locked(dev);
+
+       if (!(bp->flags & BNXT_FLAG_SHARED_RINGS)) {
+               netdev_warn(dev, "ethtool rx/tx channels must be combined to 
support XDP.\n");
+               return -EOPNOTSUPP;
+       }
+       if (enable && dev->mtu > BNXT_MAX_PAGE_MODE_MTU) {
+               netdev_warn(dev, "MTU %d larger than %d for single-page RX 
offload.\n",
+                           dev->mtu, BNXT_MAX_PAGE_MODE_MTU);
+               return -EOPNOTSUPP;
+       }
+       if (enable)
+               tx_xdp = bp->rx_nr_rings;
+
+       tc = bp->num_tc;
+       if (!tc)
+               tc = 1;
+       rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings,
+                             true, tc, tx_xdp);
+       if (rc) {
+               netdev_warn(dev, "Unable to reserve enough TX rings to support 
XDP.\n");
+               return rc;
+       }
+       if (netif_running(dev))
+               bnxt_close_nic(bp, true, false);
+
+       if (enable) {
+               bnxt_set_rx_skb_mode(bp, BNXT_FLAG_RX_OFFLOAD_MODE);
+               xdp_features_set_redirect_target_locked(dev, true);
+       } else {
+               xdp_features_clear_redirect_target_locked(dev);
+               bnxt_set_rx_skb_mode(bp, false);
+       }
+       bp->tx_nr_rings_xdp = tx_xdp;
+       bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tc + tx_xdp;
+       tx_cp = bnxt_num_tx_to_cp(bp, bp->tx_nr_rings);
+       bp->cp_nr_rings = max_t(int, tx_cp, bp->rx_nr_rings);
+       bnxt_set_tpa_flags(bp);
+       bnxt_set_ring_params(bp);
+
+       if (netif_running(dev))
+               return bnxt_open_nic(bp, true, false);
+
+       return 0;
+}
+
+void bnxt_rx_offload_set_napi(struct bnxt *bp)
+{
+       struct knod_dev *knodev = bp->knodev;
+       int i;
+
+       if (!BNXT_RX_OFFLOAD_MODE(bp))
+               return;
+
+       for (i = 0; i < KNOD_SPSC_MAX && i < bp->cp_nr_rings; i++)
+               WRITE_ONCE(knodev->wpriv[i].napi, &bp->bnapi[i]->napi);
+}
+
+void bnxt_rx_offload_clear_napi(struct bnxt *bp)
+{
+       struct knod_dev *knodev = bp->knodev;
+       int i;
+
+       if (!BNXT_RX_OFFLOAD_MODE(bp))
+               return;
+
+       for (i = 0; i < KNOD_SPSC_MAX; i++)
+               WRITE_ONCE(knodev->wpriv[i].napi, NULL);
+}
+
+void bnxt_rx_offload_start(struct bnxt *bp)
+{
+       if (!BNXT_RX_OFFLOAD_MODE(bp))
+               return;
+
+       knod_dev_start(bp->knodev);
+}
+
+void bnxt_rx_offload_stop(struct bnxt *bp)
+{
+       if (!BNXT_RX_OFFLOAD_MODE(bp))
+               return;
+
+       knod_dev_stop(bp->knodev);
+}
+
 int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp)
 {
        struct bnxt *bp = netdev_priv(dev);
+       struct knod_dev *knodev;
        int rc;
 
+       knodev = bp->knodev;
+
        switch (xdp->command) {
        case XDP_SETUP_PROG:
                rc = bnxt_xdp_set(bp, xdp->prog);
                break;
+       case XDP_SETUP_PROG_HW:
+       case BPF_OFFLOAD_MAP_ALLOC:
+       case BPF_OFFLOAD_MAP_FREE:
+               if (!knodev)
+                       return -EOPNOTSUPP;
+
+               rc = knod_dev_xdp_install(knodev, xdp);
+               break;
        default:
                rc = -EINVAL;
                break;
        }
+
        return rc;
 }
 
@@ -528,3 +637,166 @@ int bnxt_xdp_rx_hash(const struct xdp_md *ctx, u32 *hash,
        *rss_type = hash_type;
        return 0;
 }
+
+int bnxt_rx_offload_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
+                       u16 cons, void *data, unsigned int len, int index,
+                       u8 *event)
+{
+       struct knod_dev *knodev = READ_ONCE(bp->knodev);
+
+       return knodev->accel_ops->xdp_ops->rx_netmem(bp->knodev, 
(netmem_ref)data, len,
+                                         bp->rx_offset, index);
+}
+
+static int bnxt_rx_offload_xdp_attach(struct knod_dev *knodev)
+{
+       struct bnxt *bp = netdev_priv(knodev->netdev);
+       int rc;
+
+       WRITE_ONCE(bp->knodev, knodev);
+       rc = bnxt_xdp_offload_set(bp, true);
+       if (rc)
+               WRITE_ONCE(bp->knodev, NULL);
+
+       return rc;
+}
+
+static int bnxt_rx_offload_xdp_detach(struct knod_dev *knodev)
+{
+       struct bnxt *bp = netdev_priv(knodev->netdev);
+
+       WRITE_ONCE(bp->knodev, NULL);
+       bnxt_xdp_offload_set(bp, false);
+
+       return 0;
+}
+
+static void __bnxt_xmit_netmem(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
+                              dma_addr_t mapping, u32 len,
+                              netmem_ref netmem)
+{
+       struct bnxt_sw_tx_bd *tx_buf;
+
+       tx_buf = bnxt_xmit_bd(bp, txr, mapping, len, NULL);
+       tx_buf->action = BNXT_NETMEM_TX;
+       tx_buf->netmem = netmem;
+       netmem_dma_unmap_addr_set(netmem, tx_buf, mapping, mapping);
+       dma_unmap_len_set(tx_buf, len, 0);
+}
+
+struct knod_nic_ops nic_ops = {
+       .attach = bnxt_rx_offload_xdp_attach,
+       .detach = bnxt_rx_offload_xdp_detach,
+};
+
+int bnxt_rx_offload_act_handler(struct bnxt_napi *bnapi, int budget)
+{
+       struct bnxt_tx_ring_info *txr = bnapi->tx_ring[0];
+       struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
+       struct spsc_bd *bds[NAPI_POLL_WEIGHT];
+       u32 tx_avail, cnt, i, nxmit = 0;
+       struct knod_dev *knodev;
+       struct knod_work_priv *wpriv;
+       struct napi_struct *napi;
+       struct bnxt *bp = bnapi->bp;
+       dma_addr_t mapping;
+
+       knodev = bp->knodev;
+       if (!knodev)
+               return 0;
+
+       if (bnapi->index >= bp->dev->real_num_tx_queues) {
+               this_cpu_inc(knodev->stats->tx_dropped);
+               return 0;
+       }
+
+       wpriv = &knodev->wpriv[bnapi->index];
+       napi = READ_ONCE(wpriv->napi);
+       if (!napi)
+               return 0;
+
+       tx_avail = bnxt_tx_avail(bp, txr);
+       cnt = min_t(u32, NAPI_POLL_WEIGHT, tx_avail);
+       cnt = min_t(u32, cnt, budget);
+       if (!cnt)
+               return 0;
+
+       spsc_release(&wpriv->spsc_bds, (void **)bds, cnt, &cnt);
+       if (!cnt)
+               return 0;
+
+       for (i = 0; i < cnt; i++) {
+               switch (bds[i]->act) {
+               case KNOD_ACT_INFLIGHT:
+               case KNOD_IPSEC_INFLIGHT:
+                       goto stop_release;
+               case XDP_TX:
+                       mapping = 
netmem_to_net_iov(bds[i]->netmem)->desc.dma_addr +
+                                 bds[i]->off;
+                       __bnxt_xmit_netmem(bp, txr, mapping, bds[i]->len,
+                                          bds[i]->netmem);
+                       nxmit++;
+                       break;
+               case XDP_ABORTED:
+                       fallthrough;
+               case XDP_DROP:
+                       fallthrough;
+               case XDP_PASS:
+                       fallthrough;
+               case XDP_REDIRECT:
+                       fallthrough;
+               default:
+                       page_pool_recycle_direct_netmem(rxr->page_pool,
+                                                       bds[i]->netmem);
+                       this_cpu_inc(knodev->stats->tx_dropped);
+                       break;
+               }
+       }
+stop_release:
+       if (nxmit) {
+               wmb();
+               bnxt_db_write(bp, &txr->tx_db, txr->tx_prod);
+       }
+       spsc_release_commit(&wpriv->spsc_bds, i);
+
+       /*
+        * Device->host delivery: drain the framework pending ring for this NIC
+        * RX queue and push the built skbs up the stack.  Covers all features
+        * (bpf/none deliver directly, ipsec via knod_dev->post_copy); no-op
+        * when nothing is attached.
+        */
+       knod_dev_xdp_drain_pass(knodev, napi, bnapi->index, budget);
+
+       return i;
+}
+
+int bnxt_knod_init(struct bnxt *bp)
+{
+       struct knod_netdev *knetdev;
+
+       knetdev = kzalloc_obj(struct knod_netdev, GFP_KERNEL);
+       if (!knetdev) {
+               pr_debug("Failed to allocate knetdev\n");
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&knetdev->list);
+       knetdev->dev = bp->dev;
+       knetdev->priv = bp;
+       knetdev->nic_ops = &nic_ops;
+       knetdev->owner = THIS_MODULE;
+       knetdev->flags |= KNOD_FLAGS_XDP;
+       knod_netdev_register(knetdev);
+       bp->knetdev = knetdev;
+
+       return 0;
+}
+
+void bnxt_knod_uninit(struct bnxt *bp)
+{
+       knod_netdev_unregister(bp->knetdev);
+       kfree(bp->knetdev);
+       bp->knetdev = NULL;
+       kfree(bp->knodev);
+       WRITE_ONCE(bp->knodev, NULL);
+}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h 
b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h
index fb4f9143929f..7f2a850e4787 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h
@@ -24,9 +24,14 @@ struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp,
                                   dma_addr_t mapping, u32 len,
                                   struct xdp_buff *xdp);
 void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget);
+void bnxt_tx_int_offload(struct bnxt *bp, struct bnxt_napi *bnapi, int budget);
 bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
                 struct xdp_buff *xdp, struct page *page, u8 **data_ptr,
                 unsigned int *len, u8 *event);
+void bnxt_rx_offload_stop(struct bnxt *bp);
+void bnxt_rx_offload_start(struct bnxt *bp);
+void bnxt_rx_offload_set_napi(struct bnxt *bp);
+void bnxt_rx_offload_clear_napi(struct bnxt *bp);
 int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp);
 int bnxt_xdp_xmit(struct net_device *dev, int num_frames,
                  struct xdp_frame **frames, u32 flags);
@@ -43,5 +48,10 @@ struct sk_buff *bnxt_xdp_build_skb(struct bnxt *bp, struct 
sk_buff *skb,
                                   struct xdp_buff *xdp);
 int bnxt_xdp_rx_hash(const struct xdp_md *ctx, u32 *hash,
                     enum xdp_rss_hash_type *rss_type);
-
+int bnxt_rx_offload_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
+                       u16 cons, void *data, unsigned int len, int index,
+                       u8 *event);
+int bnxt_rx_offload_act_handler(struct bnxt_napi *bnapi, int budget);
+int bnxt_knod_init(struct bnxt *bp);
+void bnxt_knod_uninit(struct bnxt *bp);
 #endif
-- 
2.43.0


Reply via email to