Add initial mvneta XDP support for hardware buffer management enabled devices only.
Signed-off-by: Domagoj Pintaric <domagoj.pinta...@sartura.hr> CC: Luka Perkov <luka.per...@sartura.hr> CC: Thomas Petazzoni <thomas.petazz...@bootlin.com> CC: David S. Miller <da...@davemloft.net> --- v2: - fixed unused value warning - added additional hardware buffer management check --- drivers/net/ethernet/marvell/mvneta.c | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 61b23497f836..00c890bdf31e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -31,6 +31,7 @@ #include <linux/phylink.h> #include <linux/platform_device.h> #include <linux/skbuff.h> +#include <linux/bpf.h> #include <net/hwbm.h> #include "mvneta_bm.h" #include <net/ip.h> @@ -454,6 +455,8 @@ struct mvneta_port { bool neta_armada3700; u16 rx_offset_correction; const struct mbus_dram_target_info *dram_target_info; + + struct bpf_prog *xdp_prog; }; /* The mvneta_tx_desc and mvneta_rx_desc structures describe the @@ -626,6 +629,8 @@ struct mvneta_rx_queue { /* error counters */ u32 skb_alloc_err; u32 refill_err; + + struct bpf_prog *xdp_prog; }; static enum cpuhp_state online_hpstate; @@ -2099,6 +2104,7 @@ static int mvneta_rx_hwbm(struct napi_struct *napi, int rx_done; u32 rcvd_pkts = 0; u32 rcvd_bytes = 0; + struct bpf_prog *xdp_prog; /* Get number of received packets */ rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq); @@ -2140,6 +2146,29 @@ static int mvneta_rx_hwbm(struct napi_struct *napi, continue; } + xdp_prog = READ_ONCE(rxq->xdp_prog); + if (xdp_prog) { + struct xdp_buff xdp; + enum xdp_action act; + + xdp.data = data + MVNETA_MH_SIZE + NET_SKB_PAD; + xdp.data_end = xdp.data + rx_bytes; + + act = bpf_prog_run_xdp(xdp_prog, &xdp); + switch (act) { + case XDP_PASS: + break; + default: + bpf_warn_invalid_xdp_action(act); + /* fall through */ + case XDP_DROP: + /* Return the buffer to the pool */ + mvneta_bm_pool_put_bp(pp->bm_priv, bm_pool, + rx_desc->buf_phys_addr); + continue; + } + } + if (rx_bytes <= rx_copybreak) { /* better copy a small frame and not unmap the DMA region */ skb = netdev_alloc_skb_ip_align(dev, rx_bytes); @@ -2935,6 +2964,8 @@ static int mvneta_rxq_init(struct mvneta_port *pp, mvneta_rxq_hw_init(pp, rxq); + rxq->xdp_prog = pp->xdp_prog; + return 0; } @@ -2961,6 +2992,7 @@ static void mvneta_rxq_deinit(struct mvneta_port *pp, rxq->refill_num = 0; rxq->skb = NULL; rxq->left_size = 0; + rxq->xdp_prog = NULL; } static int mvneta_txq_sw_init(struct mvneta_port *pp, @@ -3878,6 +3910,43 @@ static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return phylink_mii_ioctl(pp->phylink, ifr, cmd); } +static int mvneta_xdp_set(struct net_device *dev, struct bpf_prog *xdp_prog) +{ + struct mvneta_port *pp = netdev_priv(dev); + struct bpf_prog *xdp_prog_old; + int queue; + + if (!pp->bm_priv) + return -EINVAL; + + xdp_prog_old = xchg(&pp->xdp_prog, xdp_prog); + + for (queue = 0; queue < rxq_number; queue++) { + struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; + (void)xchg(&rxq->xdp_prog, pp->xdp_prog); + } + + if (xdp_prog_old) + bpf_prog_put(xdp_prog_old); + + return 0; +} + +static int mvneta_bpf(struct net_device *dev, struct netdev_bpf *bpf) +{ + struct mvneta_port *pp = netdev_priv(dev); + + switch (bpf->command) { + case XDP_SETUP_PROG: + return mvneta_xdp_set(dev, bpf->prog); + case XDP_QUERY_PROG: + bpf->prog_id = pp->xdp_prog ? pp->xdp_prog->aux->id : 0; + return 0; + default: + return -EINVAL; + } +} + /* Ethtool methods */ /* Set link ksettings (phy address, speed) for ethtools */ @@ -4275,6 +4344,7 @@ static const struct net_device_ops mvneta_netdev_ops = { .ndo_fix_features = mvneta_fix_features, .ndo_get_stats64 = mvneta_get_stats64, .ndo_do_ioctl = mvneta_ioctl, + .ndo_bpf = mvneta_bpf, }; static const struct ethtool_ops mvneta_eth_tool_ops = { -- 2.19.1