162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/bpf.h> 462306a36Sopenharmony_ci#include <linux/bpf_trace.h> 562306a36Sopenharmony_ci#include <linux/filter.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "lan966x_main.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) 1062306a36Sopenharmony_ci{ 1162306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 1262306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 1362306a36Sopenharmony_ci struct bpf_prog *old_prog; 1462306a36Sopenharmony_ci bool old_xdp, new_xdp; 1562306a36Sopenharmony_ci int err; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci if (!lan966x->fdma) { 1862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(xdp->extack, 1962306a36Sopenharmony_ci "Allow to set xdp only when using fdma"); 2062306a36Sopenharmony_ci return -EOPNOTSUPP; 2162306a36Sopenharmony_ci } 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci old_xdp = lan966x_xdp_present(lan966x); 2462306a36Sopenharmony_ci old_prog = xchg(&port->xdp_prog, xdp->prog); 2562306a36Sopenharmony_ci new_xdp = lan966x_xdp_present(lan966x); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (old_xdp == new_xdp) 2862306a36Sopenharmony_ci goto out; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci err = lan966x_fdma_reload_page_pool(lan966x); 3162306a36Sopenharmony_ci if (err) { 3262306a36Sopenharmony_ci xchg(&port->xdp_prog, old_prog); 3362306a36Sopenharmony_ci return err; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciout: 3762306a36Sopenharmony_ci if (old_prog) 3862306a36Sopenharmony_ci bpf_prog_put(old_prog); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciint lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci switch (xdp->command) { 4662306a36Sopenharmony_ci case XDP_SETUP_PROG: 4762306a36Sopenharmony_ci return lan966x_xdp_setup(dev, xdp); 4862306a36Sopenharmony_ci default: 4962306a36Sopenharmony_ci return -EINVAL; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciint lan966x_xdp_xmit(struct net_device *dev, 5462306a36Sopenharmony_ci int n, 5562306a36Sopenharmony_ci struct xdp_frame **frames, 5662306a36Sopenharmony_ci u32 flags) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct lan966x_port *port = netdev_priv(dev); 5962306a36Sopenharmony_ci int nxmit = 0; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci for (int i = 0; i < n; ++i) { 6262306a36Sopenharmony_ci struct xdp_frame *xdpf = frames[i]; 6362306a36Sopenharmony_ci int err; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci err = lan966x_fdma_xmit_xdpf(port, xdpf, 0); 6662306a36Sopenharmony_ci if (err) 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci nxmit++; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return nxmit; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciint lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct bpf_prog *xdp_prog = port->xdp_prog; 7862306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 7962306a36Sopenharmony_ci struct xdp_buff xdp; 8062306a36Sopenharmony_ci u32 act; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order, 8362306a36Sopenharmony_ci &port->xdp_rxq); 8462306a36Sopenharmony_ci xdp_prepare_buff(&xdp, page_address(page), 8562306a36Sopenharmony_ci IFH_LEN_BYTES + XDP_PACKET_HEADROOM, 8662306a36Sopenharmony_ci data_len - IFH_LEN_BYTES, false); 8762306a36Sopenharmony_ci act = bpf_prog_run_xdp(xdp_prog, &xdp); 8862306a36Sopenharmony_ci switch (act) { 8962306a36Sopenharmony_ci case XDP_PASS: 9062306a36Sopenharmony_ci return FDMA_PASS; 9162306a36Sopenharmony_ci case XDP_TX: 9262306a36Sopenharmony_ci return lan966x_fdma_xmit_xdpf(port, page, 9362306a36Sopenharmony_ci data_len - IFH_LEN_BYTES) ? 9462306a36Sopenharmony_ci FDMA_DROP : FDMA_TX; 9562306a36Sopenharmony_ci case XDP_REDIRECT: 9662306a36Sopenharmony_ci if (xdp_do_redirect(port->dev, &xdp, xdp_prog)) 9762306a36Sopenharmony_ci return FDMA_DROP; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return FDMA_REDIRECT; 10062306a36Sopenharmony_ci default: 10162306a36Sopenharmony_ci bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act); 10262306a36Sopenharmony_ci fallthrough; 10362306a36Sopenharmony_ci case XDP_ABORTED: 10462306a36Sopenharmony_ci trace_xdp_exception(port->dev, xdp_prog, act); 10562306a36Sopenharmony_ci fallthrough; 10662306a36Sopenharmony_ci case XDP_DROP: 10762306a36Sopenharmony_ci return FDMA_DROP; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cibool lan966x_xdp_present(struct lan966x *lan966x) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci for (int p = 0; p < lan966x->num_phys_ports; ++p) { 11462306a36Sopenharmony_ci if (!lan966x->ports[p]) 11562306a36Sopenharmony_ci continue; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (lan966x_xdp_port_present(lan966x->ports[p])) 11862306a36Sopenharmony_ci return true; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return false; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciint lan966x_xdp_port_init(struct lan966x_port *port) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct lan966x *lan966x = port->lan966x; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return xdp_rxq_info_reg(&port->xdp_rxq, port->dev, 0, 12962306a36Sopenharmony_ci lan966x->napi.napi_id); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_civoid lan966x_xdp_port_deinit(struct lan966x_port *port) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci if (xdp_rxq_info_is_reg(&port->xdp_rxq)) 13562306a36Sopenharmony_ci xdp_rxq_info_unreg(&port->xdp_rxq); 13662306a36Sopenharmony_ci} 137