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