Message ID | 20250305162132.1106080-13-aleksander.lobakin@intel.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | idpf: add XDP support | expand |
On Wed, Mar 05, 2025 at 05:21:28PM +0100, Alexander Lobakin wrote: > From: Michal Kubiak <michal.kubiak@intel.com> > > 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> > --- > drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 +- > drivers/net/ethernet/intel/idpf/xdp.h | 7 ++ > drivers/net/ethernet/intel/idpf/idpf_lib.c | 1 + > drivers/net/ethernet/intel/idpf/idpf_txrx.c | 4 + > drivers/net/ethernet/intel/idpf/xdp.c | 114 ++++++++++++++++++++ > 5 files changed, 129 insertions(+), 1 deletion(-) > (...) > + > +/** > + * idpf_xdp_setup_prog - handle XDP program install/remove requests > + * @vport: vport to configure > + * @xdp: request data (program, extack) > + * > + * Return: 0 on success, -errno on failure. > + */ > +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 (!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; > + } > + > + if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) || IN_PROG is a bit unfortunate here as it mixes with 'prog' :P > + !!vport->xdp_prog == !!prog) { > + if (np->state == __IDPF_VPORT_UP) > + idpf_copy_xdp_prog_to_qs(vport, prog); > + > + old = xchg(&vport->xdp_prog, prog); > + if (old) > + bpf_prog_put(old); > + > + cfg->user_config.xdp_prog = prog; > + > + return 0; > + } > + > + 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"); > + > + if (prog) > + bpf_prog_put(prog); aren't you missing this for prog->NULL conversion? you have this for hot-swap case (prog->prog). > + > + cfg->user_config.xdp_prog = old; > + } > + > + return ret; > +} > + > +/** > + * idpf_xdp - handle XDP-related requests > + * @dev: network device to configure > + * @xdp: request data (program, extack) > + * > + * Return: 0 on success, -errno on failure. > + */ > +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; > +} > -- > 2.48.1 >
From: Maciej Fijalkowski <maciej.fijalkowski@intel.com> Date: Fri, 7 Mar 2025 15:16:48 +0100 > On Wed, Mar 05, 2025 at 05:21:28PM +0100, Alexander Lobakin wrote: >> From: Michal Kubiak <michal.kubiak@intel.com> >> >> 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> >> --- >> drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 +- >> drivers/net/ethernet/intel/idpf/xdp.h | 7 ++ >> drivers/net/ethernet/intel/idpf/idpf_lib.c | 1 + >> drivers/net/ethernet/intel/idpf/idpf_txrx.c | 4 + >> drivers/net/ethernet/intel/idpf/xdp.c | 114 ++++++++++++++++++++ >> 5 files changed, 129 insertions(+), 1 deletion(-) >> > > (...) > >> + >> +/** >> + * idpf_xdp_setup_prog - handle XDP program install/remove requests >> + * @vport: vport to configure >> + * @xdp: request data (program, extack) >> + * >> + * Return: 0 on success, -errno on failure. >> + */ >> +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 (!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; >> + } >> + >> + if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) || > > IN_PROG is a bit unfortunate here as it mixes with 'prog' :P Authentic idpf dictionary ¯\_(ツ)_/¯ > >> + !!vport->xdp_prog == !!prog) { >> + if (np->state == __IDPF_VPORT_UP) >> + idpf_copy_xdp_prog_to_qs(vport, prog); >> + >> + old = xchg(&vport->xdp_prog, prog); >> + if (old) >> + bpf_prog_put(old); >> + >> + cfg->user_config.xdp_prog = prog; >> + >> + return 0; >> + } >> + >> + 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"); >> + >> + if (prog) >> + bpf_prog_put(prog); > > aren't you missing this for prog->NULL conversion? you have this for > hot-swap case (prog->prog). This path (soft_reset) handles NULL => prog and prog => NULL. This branch in particular handles errors during the soft reset, when we need to restore the original prog and put the new one. What you probably meant is that I don't have bpf_prog_put(old) in case everything went well below? Breh =\ > >> + >> + cfg->user_config.xdp_prog = old; >> + } >> + >> + return ret; >> +} Thanks, Olek
On Mon, Mar 17, 2025 at 03:58:12PM +0100, Alexander Lobakin wrote: > From: Maciej Fijalkowski <maciej.fijalkowski@intel.com> > Date: Fri, 7 Mar 2025 15:16:48 +0100 > > > On Wed, Mar 05, 2025 at 05:21:28PM +0100, Alexander Lobakin wrote: > >> From: Michal Kubiak <michal.kubiak@intel.com> > >> > >> 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> > >> --- > >> drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 +- > >> drivers/net/ethernet/intel/idpf/xdp.h | 7 ++ > >> drivers/net/ethernet/intel/idpf/idpf_lib.c | 1 + > >> drivers/net/ethernet/intel/idpf/idpf_txrx.c | 4 + > >> drivers/net/ethernet/intel/idpf/xdp.c | 114 ++++++++++++++++++++ > >> 5 files changed, 129 insertions(+), 1 deletion(-) > >> > > > > (...) > > > >> + > >> +/** > >> + * idpf_xdp_setup_prog - handle XDP program install/remove requests > >> + * @vport: vport to configure > >> + * @xdp: request data (program, extack) > >> + * > >> + * Return: 0 on success, -errno on failure. > >> + */ > >> +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 (!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; > >> + } > >> + > >> + if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) || > > > > IN_PROG is a bit unfortunate here as it mixes with 'prog' :P > > Authentic idpf dictionary ¯\_(ツ)_/¯ > > > > >> + !!vport->xdp_prog == !!prog) { > >> + if (np->state == __IDPF_VPORT_UP) > >> + idpf_copy_xdp_prog_to_qs(vport, prog); > >> + > >> + old = xchg(&vport->xdp_prog, prog); > >> + if (old) > >> + bpf_prog_put(old); > >> + > >> + cfg->user_config.xdp_prog = prog; > >> + > >> + return 0; > >> + } > >> + > >> + 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"); > >> + > >> + if (prog) > >> + bpf_prog_put(prog); > > > > aren't you missing this for prog->NULL conversion? you have this for > > hot-swap case (prog->prog). > > This path (soft_reset) handles NULL => prog and prog => NULL. This > branch in particular handles errors during the soft reset, when we need > to restore the original prog and put the new one. > > What you probably meant is that I don't have bpf_prog_put(old) in case > everything went well below? Breh =\ yes, best to check with bpftool if there are dangling bpf progs on system after using few xdp samples, for example. > > > > >> + > >> + cfg->user_config.xdp_prog = old; > >> + } > >> + > >> + return ret; > >> +} > > Thanks, > Olek
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h index 6d9eb6f4ab38..38ef0db08133 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h @@ -485,6 +485,7 @@ struct idpf_txq_stash { * @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. @@ -525,13 +526,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; diff --git a/drivers/net/ethernet/intel/idpf/xdp.h b/drivers/net/ethernet/intel/idpf/xdp.h index 8ace8384f348..a72a7638a6ea 100644 --- a/drivers/net/ethernet/intel/idpf/xdp.h +++ b/drivers/net/ethernet/intel/idpf/xdp.h @@ -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_copy_xdp_prog_to_qs(const struct idpf_vport *vport, + struct bpf_prog *xdp_prog); int idpf_vport_xdpq_get(const struct idpf_vport *vport); void idpf_vport_xdpq_put(const struct idpf_vport *vport); +int idpf_xdp(struct net_device *dev, struct netdev_bpf *xdp); + #endif /* _IDPF_XDP_H_ */ diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c index 0f4edc9cd1ad..84ca8c08bd56 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_lib.c +++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c @@ -2368,4 +2368,5 @@ static const struct net_device_ops idpf_netdev_ops = { .ndo_get_stats64 = idpf_get_stats64, .ndo_set_features = idpf_set_features, .ndo_tx_timeout = idpf_tx_timeout, + .ndo_bpf = idpf_xdp, }; diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c index 97513822d614..e152fbe4ebe3 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c +++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c @@ -1102,6 +1102,8 @@ static void idpf_vport_queue_grp_rel_all(struct idpf_vport *vport) */ void idpf_vport_queues_rel(struct idpf_vport *vport) { + idpf_copy_xdp_prog_to_qs(vport, NULL); + idpf_tx_desc_rel_all(vport); idpf_rx_desc_rel_all(vport); @@ -1664,6 +1666,8 @@ int idpf_vport_queues_alloc(struct idpf_vport *vport) if (err) goto err_out; + idpf_copy_xdp_prog_to_qs(vport, vport->xdp_prog); + return 0; err_out: diff --git a/drivers/net/ethernet/intel/idpf/xdp.c b/drivers/net/ethernet/intel/idpf/xdp.c index 8770249b5abe..c0322fa7bfee 100644 --- a/drivers/net/ethernet/intel/idpf/xdp.c +++ b/drivers/net/ethernet/intel/idpf/xdp.c @@ -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, @@ -115,6 +116,33 @@ 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 mutex *lock = &rxq->q_vector->vport->adapter->vport_ctrl_lock; + struct bpf_prog *prog = arg; + struct bpf_prog *old; + + if (prog) + bpf_prog_inc(prog); + + old = rcu_replace_pointer(rxq->xdp_prog, prog, lockdep_is_held(lock)); + if (old) + bpf_prog_put(old); + + return 0; +} + +/** + * idpf_copy_xdp_prog_to_qs - set pointers to XDP program for each Rx queue + * @vport: vport to setup XDP for + * @xdp_prog: XDP program that should be copied to all Rx queues + */ +void idpf_copy_xdp_prog_to_qs(const struct idpf_vport *vport, + struct bpf_prog *xdp_prog) +{ + idpf_rxq_for_each(vport, idpf_xdp_rxq_assign_prog, xdp_prog); +} + int idpf_vport_xdpq_get(const struct idpf_vport *vport) { struct libeth_xdpsq_timer **timers __free(kvfree) = NULL; @@ -187,3 +215,89 @@ void idpf_vport_xdpq_put(const struct idpf_vport *vport) idpf_queue_clear(NOIRQ, xdpq); } } + +/** + * idpf_xdp_setup_prog - handle XDP program install/remove requests + * @vport: vport to configure + * @xdp: request data (program, extack) + * + * Return: 0 on success, -errno on failure. + */ +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 (!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; + } + + if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) || + !!vport->xdp_prog == !!prog) { + if (np->state == __IDPF_VPORT_UP) + idpf_copy_xdp_prog_to_qs(vport, prog); + + old = xchg(&vport->xdp_prog, prog); + if (old) + bpf_prog_put(old); + + cfg->user_config.xdp_prog = prog; + + return 0; + } + + 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"); + + if (prog) + bpf_prog_put(prog); + + cfg->user_config.xdp_prog = old; + } + + return ret; +} + +/** + * idpf_xdp - handle XDP-related requests + * @dev: network device to configure + * @xdp: request data (program, extack) + * + * Return: 0 on success, -errno on failure. + */ +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; +}