1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include <linux/bpf.h> |
4 | #include <linux/bpf_trace.h> |
5 | #include <linux/filter.h> |
6 | |
7 | #include "lan966x_main.h" |
8 | |
9 | static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) |
10 | { |
11 | struct lan966x_port *port = netdev_priv(dev); |
12 | struct lan966x *lan966x = port->lan966x; |
13 | struct bpf_prog *old_prog; |
14 | bool old_xdp, new_xdp; |
15 | int err; |
16 | |
17 | if (!lan966x->fdma) { |
18 | NL_SET_ERR_MSG_MOD(xdp->extack, |
19 | "Allow to set xdp only when using fdma" ); |
20 | return -EOPNOTSUPP; |
21 | } |
22 | |
23 | old_xdp = lan966x_xdp_present(lan966x); |
24 | old_prog = xchg(&port->xdp_prog, xdp->prog); |
25 | new_xdp = lan966x_xdp_present(lan966x); |
26 | |
27 | if (old_xdp == new_xdp) |
28 | goto out; |
29 | |
30 | err = lan966x_fdma_reload_page_pool(lan966x); |
31 | if (err) { |
32 | xchg(&port->xdp_prog, old_prog); |
33 | return err; |
34 | } |
35 | |
36 | out: |
37 | if (old_prog) |
38 | bpf_prog_put(prog: old_prog); |
39 | |
40 | return 0; |
41 | } |
42 | |
43 | int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp) |
44 | { |
45 | switch (xdp->command) { |
46 | case XDP_SETUP_PROG: |
47 | return lan966x_xdp_setup(dev, xdp); |
48 | default: |
49 | return -EINVAL; |
50 | } |
51 | } |
52 | |
53 | int lan966x_xdp_xmit(struct net_device *dev, |
54 | int n, |
55 | struct xdp_frame **frames, |
56 | u32 flags) |
57 | { |
58 | struct lan966x_port *port = netdev_priv(dev); |
59 | int nxmit = 0; |
60 | |
61 | for (int i = 0; i < n; ++i) { |
62 | struct xdp_frame *xdpf = frames[i]; |
63 | int err; |
64 | |
65 | err = lan966x_fdma_xmit_xdpf(port, ptr: xdpf, len: 0); |
66 | if (err) |
67 | break; |
68 | |
69 | nxmit++; |
70 | } |
71 | |
72 | return nxmit; |
73 | } |
74 | |
75 | int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) |
76 | { |
77 | struct bpf_prog *xdp_prog = port->xdp_prog; |
78 | struct lan966x *lan966x = port->lan966x; |
79 | struct xdp_buff xdp; |
80 | u32 act; |
81 | |
82 | xdp_init_buff(xdp: &xdp, PAGE_SIZE << lan966x->rx.page_order, |
83 | rxq: &port->xdp_rxq); |
84 | xdp_prepare_buff(xdp: &xdp, page_address(page), |
85 | IFH_LEN_BYTES + XDP_PACKET_HEADROOM, |
86 | data_len: data_len - IFH_LEN_BYTES, meta_valid: false); |
87 | act = bpf_prog_run_xdp(prog: xdp_prog, xdp: &xdp); |
88 | switch (act) { |
89 | case XDP_PASS: |
90 | return FDMA_PASS; |
91 | case XDP_TX: |
92 | return lan966x_fdma_xmit_xdpf(port, ptr: page, |
93 | len: data_len - IFH_LEN_BYTES) ? |
94 | FDMA_DROP : FDMA_TX; |
95 | case XDP_REDIRECT: |
96 | if (xdp_do_redirect(dev: port->dev, xdp: &xdp, prog: xdp_prog)) |
97 | return FDMA_DROP; |
98 | |
99 | return FDMA_REDIRECT; |
100 | default: |
101 | bpf_warn_invalid_xdp_action(dev: port->dev, prog: xdp_prog, act); |
102 | fallthrough; |
103 | case XDP_ABORTED: |
104 | trace_xdp_exception(dev: port->dev, xdp: xdp_prog, act); |
105 | fallthrough; |
106 | case XDP_DROP: |
107 | return FDMA_DROP; |
108 | } |
109 | } |
110 | |
111 | bool lan966x_xdp_present(struct lan966x *lan966x) |
112 | { |
113 | for (int p = 0; p < lan966x->num_phys_ports; ++p) { |
114 | if (!lan966x->ports[p]) |
115 | continue; |
116 | |
117 | if (lan966x_xdp_port_present(port: lan966x->ports[p])) |
118 | return true; |
119 | } |
120 | |
121 | return false; |
122 | } |
123 | |
124 | int lan966x_xdp_port_init(struct lan966x_port *port) |
125 | { |
126 | struct lan966x *lan966x = port->lan966x; |
127 | |
128 | return xdp_rxq_info_reg(xdp_rxq: &port->xdp_rxq, dev: port->dev, queue_index: 0, |
129 | napi_id: lan966x->napi.napi_id); |
130 | } |
131 | |
132 | void lan966x_xdp_port_deinit(struct lan966x_port *port) |
133 | { |
134 | if (xdp_rxq_info_is_reg(xdp_rxq: &port->xdp_rxq)) |
135 | xdp_rxq_info_unreg(xdp_rxq: &port->xdp_rxq); |
136 | } |
137 | |