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
|
// 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_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;
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);
}
|