]> www.infradead.org Git - users/hch/misc.git/commitdiff
idpf: implement XDP_SETUP_PROG in ndo_bpf for splitq
authorMichal Kubiak <michal.kubiak@intel.com>
Tue, 26 Aug 2025 15:55:03 +0000 (17:55 +0200)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Mon, 8 Sep 2025 18:05:17 +0000 (11:05 -0700)
Implement loading/removing XDP program using .ndo_bpf callback
in the split queue mode. Reconfigure and restart the queues if needed
(!!old_prog != !!new_prog), otherwise, just update the pointers.

Signed-off-by: Michal Kubiak <michal.kubiak@intel.com>
Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Tested-by: Ramu R <ramu.r@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/idpf/idpf_lib.c
drivers/net/ethernet/intel/idpf/idpf_txrx.c
drivers/net/ethernet/intel/idpf/idpf_txrx.h
drivers/net/ethernet/intel/idpf/xdp.c
drivers/net/ethernet/intel/idpf/xdp.h

index 7db1042782af2fbd135c601d294648fabc8f7ad7..2ee887de88e63453341ddffa27507141756b294c 100644 (file)
@@ -2614,4 +2614,5 @@ static const struct net_device_ops idpf_netdev_ops = {
        .ndo_tx_timeout = idpf_tx_timeout,
        .ndo_hwtstamp_get = idpf_hwtstamp_get,
        .ndo_hwtstamp_set = idpf_hwtstamp_set,
+       .ndo_bpf = idpf_xdp,
 };
index 80dd7c3433af6a826fb8bd53c2f34498bf54943c..c17fd475eaf3678da2b502495b8ba8e71bf6e626 100644 (file)
@@ -762,6 +762,8 @@ int idpf_rx_bufs_init_all(struct idpf_vport *vport)
        bool split = idpf_is_queue_model_split(vport->rxq_model);
        int i, j, err;
 
+       idpf_xdp_copy_prog_to_rqs(vport, vport->xdp_prog);
+
        for (i = 0; i < vport->num_rxq_grp; i++) {
                struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
                u32 truesize = 0;
@@ -1040,6 +1042,8 @@ static void idpf_vport_queue_grp_rel_all(struct idpf_vport *vport)
  */
 void idpf_vport_queues_rel(struct idpf_vport *vport)
 {
+       idpf_xdp_copy_prog_to_rqs(vport, NULL);
+
        idpf_tx_desc_rel_all(vport);
        idpf_rx_desc_rel_all(vport);
 
index 6bc204b68d9e2b070a49626005bf4763875deef4..f898a9c8de1d8917b1294c860cd4d3f55c5b67ab 100644 (file)
@@ -467,6 +467,7 @@ struct idpf_tx_queue_stats {
  * @desc_ring: virtual descriptor ring address
  * @bufq_sets: Pointer to the array of buffer queues in splitq mode
  * @napi: NAPI instance corresponding to this queue (splitq)
+ * @xdp_prog: attached XDP program
  * @rx_buf: See struct &libeth_fqe
  * @pp: Page pool pointer in singleq mode
  * @tail: Tail offset. Used for both queue models single and split.
@@ -508,13 +509,14 @@ struct idpf_rx_queue {
                struct {
                        struct idpf_bufq_set *bufq_sets;
                        struct napi_struct *napi;
+                       struct bpf_prog __rcu *xdp_prog;
                };
                struct {
                        struct libeth_fqe *rx_buf;
                        struct page_pool *pp;
+                       void __iomem *tail;
                };
        };
-       void __iomem *tail;
 
        DECLARE_BITMAP(flags, __IDPF_Q_FLAGS_NBITS);
        u16 idx;
index 98bdccc0c9577a94d81d1e44a508109fb52e662c..02f63810632f48211306a2297efad74bd839185f 100644 (file)
@@ -4,6 +4,7 @@
 #include <net/libeth/xdp.h>
 
 #include "idpf.h"
+#include "idpf_virtchnl.h"
 #include "xdp.h"
 
 static int idpf_rxq_for_each(const struct idpf_vport *vport,
@@ -91,6 +92,27 @@ void idpf_xdp_rxq_info_deinit_all(const struct idpf_vport *vport)
                          (void *)(size_t)vport->rxq_model);
 }
 
+static int idpf_xdp_rxq_assign_prog(struct idpf_rx_queue *rxq, void *arg)
+{
+       struct bpf_prog *prog = arg;
+       struct bpf_prog *old;
+
+       if (prog)
+               bpf_prog_inc(prog);
+
+       old = rcu_replace_pointer(rxq->xdp_prog, prog, lockdep_rtnl_is_held());
+       if (old)
+               bpf_prog_put(old);
+
+       return 0;
+}
+
+void idpf_xdp_copy_prog_to_rqs(const struct idpf_vport *vport,
+                              struct bpf_prog *xdp_prog)
+{
+       idpf_rxq_for_each(vport, idpf_xdp_rxq_assign_prog, xdp_prog);
+}
+
 int idpf_xdpsqs_get(const struct idpf_vport *vport)
 {
        struct libeth_xdpsq_timer **timers __free(kvfree) = NULL;
@@ -166,3 +188,78 @@ void idpf_xdpsqs_put(const struct idpf_vport *vport)
                idpf_queue_clear(NOIRQ, xdpsq);
        }
 }
+
+static int idpf_xdp_setup_prog(struct idpf_vport *vport,
+                              const struct netdev_bpf *xdp)
+{
+       const struct idpf_netdev_priv *np = netdev_priv(vport->netdev);
+       struct bpf_prog *old, *prog = xdp->prog;
+       struct idpf_vport_config *cfg;
+       int ret;
+
+       cfg = vport->adapter->vport_config[vport->idx];
+
+       if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) ||
+           !test_bit(IDPF_VPORT_REG_NETDEV, cfg->flags) ||
+           !!vport->xdp_prog == !!prog) {
+               if (np->state == __IDPF_VPORT_UP)
+                       idpf_xdp_copy_prog_to_rqs(vport, prog);
+
+               old = xchg(&vport->xdp_prog, prog);
+               if (old)
+                       bpf_prog_put(old);
+
+               cfg->user_config.xdp_prog = prog;
+
+               return 0;
+       }
+
+       if (!vport->num_xdp_txq && vport->num_txq == cfg->max_q.max_txq) {
+               NL_SET_ERR_MSG_MOD(xdp->extack,
+                                  "No Tx queues available for XDP, please decrease the number of regular SQs");
+               return -ENOSPC;
+       }
+
+       old = cfg->user_config.xdp_prog;
+       cfg->user_config.xdp_prog = prog;
+
+       ret = idpf_initiate_soft_reset(vport, IDPF_SR_Q_CHANGE);
+       if (ret) {
+               NL_SET_ERR_MSG_MOD(xdp->extack,
+                                  "Could not reopen the vport after XDP setup");
+
+               cfg->user_config.xdp_prog = old;
+               old = prog;
+       }
+
+       if (old)
+               bpf_prog_put(old);
+
+       return ret;
+}
+
+int idpf_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       struct idpf_vport *vport;
+       int ret;
+
+       idpf_vport_ctrl_lock(dev);
+       vport = idpf_netdev_to_vport(dev);
+
+       if (!idpf_is_queue_model_split(vport->txq_model))
+               goto notsupp;
+
+       switch (xdp->command) {
+       case XDP_SETUP_PROG:
+               ret = idpf_xdp_setup_prog(vport, xdp);
+               break;
+       default:
+notsupp:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       idpf_vport_ctrl_unlock(dev);
+
+       return ret;
+}
index cf6823b24ba592bd3a836ffe094bd33cb83ac377..47553ce5f81ac02acdace21ffd41d639611d0673 100644 (file)
@@ -6,12 +6,19 @@
 
 #include <linux/types.h>
 
+struct bpf_prog;
 struct idpf_vport;
+struct net_device;
+struct netdev_bpf;
 
 int idpf_xdp_rxq_info_init_all(const struct idpf_vport *vport);
 void idpf_xdp_rxq_info_deinit_all(const struct idpf_vport *vport);
+void idpf_xdp_copy_prog_to_rqs(const struct idpf_vport *vport,
+                              struct bpf_prog *xdp_prog);
 
 int idpf_xdpsqs_get(const struct idpf_vport *vport);
 void idpf_xdpsqs_put(const struct idpf_vport *vport);
 
+int idpf_xdp(struct net_device *dev, struct netdev_bpf *xdp);
+
 #endif /* _IDPF_XDP_H_ */