1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// 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;
bool old_xdp, new_xdp;
int err;
if (!lan966x->fdma) {
NL_SET_ERR_MSG_MOD(xdp->extack,
"Allow to set xdp only when using fdma");
return -EOPNOTSUPP;
}
old_xdp = lan966x_xdp_present(lan966x);
old_prog = xchg(&port->xdp_prog, xdp->prog);
new_xdp = lan966x_xdp_present(lan966x);
if (old_xdp == new_xdp)
goto out;
err = lan966x_fdma_reload_page_pool(lan966x);
if (err) {
xchg(&port->xdp_prog, old_prog);
return err;
}
out:
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_xmit(struct net_device *dev,
int n,
struct xdp_frame **frames,
u32 flags)
{
struct lan966x_port *port = netdev_priv(dev);
int nxmit = 0;
for (int i = 0; i < n; ++i) {
struct xdp_frame *xdpf = frames[i];
int err;
err = lan966x_fdma_xmit_xdpf(port, xdpf, 0);
if (err)
break;
nxmit++;
}
return nxmit;
}
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 + XDP_PACKET_HEADROOM,
data_len - IFH_LEN_BYTES, false);
act = bpf_prog_run_xdp(xdp_prog, &xdp);
switch (act) {
case XDP_PASS:
return FDMA_PASS;
case XDP_TX:
return lan966x_fdma_xmit_xdpf(port, page,
data_len - IFH_LEN_BYTES) ?
FDMA_DROP : FDMA_TX;
case XDP_REDIRECT:
if (xdp_do_redirect(port->dev, &xdp, xdp_prog))
return FDMA_DROP;
return FDMA_REDIRECT;
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;
}
}
bool lan966x_xdp_present(struct lan966x *lan966x)
{
for (int p = 0; p < lan966x->num_phys_ports; ++p) {
if (!lan966x->ports[p])
continue;
if (lan966x_xdp_port_present(lan966x->ports[p]))
return true;
}
return false;
}
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);
}
|