net: lan966x: Add basic XDP support
authorHoratiu Vultur <horatiu.vultur@microchip.com>
Wed, 9 Nov 2022 20:46:12 +0000 (21:46 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Nov 2022 10:49:34 +0000 (10:49 +0000)
Introduce basic XDP support to lan966x driver. Currently the driver
supports only the actions XDP_PASS, XDP_DROP and XDP_ABORTED.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/lan966x/Makefile
drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
drivers/net/ethernet/microchip/lan966x/lan966x_main.c
drivers/net/ethernet/microchip/lan966x/lan966x_main.h
drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c [new file with mode: 0644]

index 962f7c5..251a7d5 100644 (file)
@@ -11,4 +11,5 @@ lan966x-switch-objs  := lan966x_main.o lan966x_phylink.o lan966x_port.o \
                        lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \
                        lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \
                        lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \
-                       lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o
+                       lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \
+                       lan966x_xdp.o
index d37765d..fa4198c 100644 (file)
@@ -423,6 +423,7 @@ static bool lan966x_fdma_rx_more_frames(struct lan966x_rx *rx)
 static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port)
 {
        struct lan966x *lan966x = rx->lan966x;
+       struct lan966x_port *port;
        struct lan966x_db *db;
        struct page *page;
 
@@ -443,7 +444,11 @@ static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port)
        if (WARN_ON(*src_port >= lan966x->num_phys_ports))
                return FDMA_ERROR;
 
-       return FDMA_PASS;
+       port = lan966x->ports[*src_port];
+       if (!lan966x_xdp_port_present(port))
+               return FDMA_PASS;
+
+       return lan966x_xdp_run(port, page, FDMA_DCB_STATUS_BLOCKL(db->status));
 }
 
 static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx,
@@ -524,6 +529,10 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight)
                        lan966x_fdma_rx_free_page(rx);
                        lan966x_fdma_rx_advance_dcb(rx);
                        goto allocate_new;
+               case FDMA_DROP:
+                       lan966x_fdma_rx_free_page(rx);
+                       lan966x_fdma_rx_advance_dcb(rx);
+                       continue;
                }
 
                skb = lan966x_fdma_rx_get_frame(rx, src_port);
index 1a27946..42be5d0 100644 (file)
@@ -468,6 +468,7 @@ static const struct net_device_ops lan966x_port_netdev_ops = {
        .ndo_get_port_parent_id         = lan966x_port_get_parent_id,
        .ndo_eth_ioctl                  = lan966x_port_ioctl,
        .ndo_setup_tc                   = lan966x_tc_setup,
+       .ndo_bpf                        = lan966x_xdp,
 };
 
 bool lan966x_netdevice_check(const struct net_device *dev)
@@ -694,6 +695,7 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x)
                if (port->dev)
                        unregister_netdev(port->dev);
 
+               lan966x_xdp_port_deinit(port);
                if (lan966x->fdma && lan966x->fdma_ndev == port->dev)
                        lan966x_fdma_netdev_deinit(lan966x, port->dev);
 
@@ -1136,6 +1138,9 @@ static int lan966x_probe(struct platform_device *pdev)
                lan966x->ports[p]->serdes = serdes;
 
                lan966x_port_init(lan966x->ports[p]);
+               err = lan966x_xdp_port_init(lan966x->ports[p]);
+               if (err)
+                       goto cleanup_ports;
        }
 
        lan966x_mdb_init(lan966x);
index 464fb5e..b3b0619 100644 (file)
@@ -103,10 +103,12 @@ enum macaccess_entry_type {
 /* FDMA return action codes for checking if the frame is valid
  * FDMA_PASS, frame is valid and can be used
  * FDMA_ERROR, something went wrong, stop getting more frames
+ * FDMA_DROP, frame is dropped, but continue to get more frames
  */
 enum lan966x_fdma_action {
        FDMA_PASS = 0,
        FDMA_ERROR,
+       FDMA_DROP,
 };
 
 struct lan966x_port;
@@ -329,6 +331,9 @@ struct lan966x_port {
        enum netdev_lag_hash hash_type;
 
        struct lan966x_port_tc tc;
+
+       struct bpf_prog *xdp_prog;
+       struct xdp_rxq_info xdp_rxq;
 };
 
 extern const struct phylink_mac_ops lan966x_phylink_mac_ops;
@@ -536,6 +541,17 @@ void lan966x_mirror_port_stats(struct lan966x_port *port,
                               struct flow_stats *stats,
                               bool ingress);
 
+int lan966x_xdp_port_init(struct lan966x_port *port);
+void lan966x_xdp_port_deinit(struct lan966x_port *port);
+int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp);
+int lan966x_xdp_run(struct lan966x_port *port,
+                   struct page *page,
+                   u32 data_len);
+static inline bool lan966x_xdp_port_present(struct lan966x_port *port)
+{
+       return !!port->xdp_prog;
+}
+
 static inline void __iomem *lan_addr(void __iomem *base[],
                                     int id, int tinst, int tcnt,
                                     int gbase, int ginst,
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c
new file mode 100644 (file)
index 0000000..e77d9f2
--- /dev/null
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/bpf.h>
+#include <linux/bpf_trace.h>
+#include <linux/filter.h>
+
+#include "lan966x_main.h"
+
+static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       struct lan966x_port *port = netdev_priv(dev);
+       struct lan966x *lan966x = port->lan966x;
+       struct bpf_prog *old_prog;
+
+       if (!lan966x->fdma) {
+               NL_SET_ERR_MSG_MOD(xdp->extack,
+                                  "Allow to set xdp only when using fdma");
+               return -EOPNOTSUPP;
+       }
+
+       old_prog = xchg(&port->xdp_prog, xdp->prog);
+       if (old_prog)
+               bpf_prog_put(old_prog);
+
+       return 0;
+}
+
+int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp)
+{
+       switch (xdp->command) {
+       case XDP_SETUP_PROG:
+               return lan966x_xdp_setup(dev, xdp);
+       default:
+               return -EINVAL;
+       }
+}
+
+int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len)
+{
+       struct bpf_prog *xdp_prog = port->xdp_prog;
+       struct lan966x *lan966x = port->lan966x;
+       struct xdp_buff xdp;
+       u32 act;
+
+       xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order,
+                     &port->xdp_rxq);
+       xdp_prepare_buff(&xdp, page_address(page), IFH_LEN_BYTES,
+                        data_len - IFH_LEN_BYTES, false);
+       act = bpf_prog_run_xdp(xdp_prog, &xdp);
+       switch (act) {
+       case XDP_PASS:
+               return FDMA_PASS;
+       default:
+               bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act);
+               fallthrough;
+       case XDP_ABORTED:
+               trace_xdp_exception(port->dev, xdp_prog, act);
+               fallthrough;
+       case XDP_DROP:
+               return FDMA_DROP;
+       }
+}
+
+int lan966x_xdp_port_init(struct lan966x_port *port)
+{
+       struct lan966x *lan966x = port->lan966x;
+
+       return xdp_rxq_info_reg(&port->xdp_rxq, port->dev, 0,
+                               lan966x->napi.napi_id);
+}
+
+void lan966x_xdp_port_deinit(struct lan966x_port *port)
+{
+       if (xdp_rxq_info_is_reg(&port->xdp_rxq))
+               xdp_rxq_info_unreg(&port->xdp_rxq);
+}