]> Git Repo - linux.git/blame - drivers/net/netdevsim/bpf.c
netdevsim: add bpf offload support
[linux.git] / drivers / net / netdevsim / bpf.c
CommitLineData
31d3ad83
JK
1/*
2 * Copyright (C) 2017 Netronome Systems, Inc.
3 *
4 * This software is licensed under the GNU General License Version 2,
5 * June 1991 as shown in the file COPYING in the top-level directory of this
6 * source tree.
7 *
8 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
9 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
10 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
12 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
13 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
14 */
15
16#include <linux/bpf.h>
17#include <linux/bpf_verifier.h>
18#include <linux/debugfs.h>
19#include <linux/kernel.h>
20#include <linux/rtnetlink.h>
21#include <net/pkt_cls.h>
22
23#include "netdevsim.h"
24
25struct nsim_bpf_bound_prog {
26 struct netdevsim *ns;
27 struct bpf_prog *prog;
28 struct dentry *ddir;
29 const char *state;
30 bool is_loaded;
31 struct list_head l;
32};
33
34static int nsim_debugfs_bpf_string_read(struct seq_file *file, void *data)
35{
36 const char **str = file->private;
37
38 if (*str)
39 seq_printf(file, "%s\n", *str);
40
41 return 0;
42}
43
44static int nsim_debugfs_bpf_string_open(struct inode *inode, struct file *f)
45{
46 return single_open(f, nsim_debugfs_bpf_string_read, inode->i_private);
47}
48
49static const struct file_operations nsim_bpf_string_fops = {
50 .owner = THIS_MODULE,
51 .open = nsim_debugfs_bpf_string_open,
52 .release = single_release,
53 .read = seq_read,
54 .llseek = seq_lseek
55};
56
57static int
58nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)
59{
60 struct nsim_bpf_bound_prog *state;
61
62 state = env->prog->aux->offload->dev_priv;
63 if (state->ns->bpf_bind_verifier_delay && !insn_idx)
64 msleep(state->ns->bpf_bind_verifier_delay);
65
66 return 0;
67}
68
69static const struct bpf_ext_analyzer_ops nsim_bpf_analyzer_ops = {
70 .insn_hook = nsim_bpf_verify_insn,
71};
72
73static bool nsim_xdp_offload_active(struct netdevsim *ns)
74{
75 return ns->xdp_prog_mode == XDP_ATTACHED_HW;
76}
77
78static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded)
79{
80 struct nsim_bpf_bound_prog *state;
81
82 if (!prog || !prog->aux->offload)
83 return;
84
85 state = prog->aux->offload->dev_priv;
86 state->is_loaded = loaded;
87}
88
89static int
90nsim_bpf_offload(struct netdevsim *ns, struct bpf_prog *prog, bool oldprog)
91{
92 nsim_prog_set_loaded(ns->bpf_offloaded, false);
93
94 WARN(!!ns->bpf_offloaded != oldprog,
95 "bad offload state, expected offload %sto be active",
96 oldprog ? "" : "not ");
97 ns->bpf_offloaded = prog;
98 ns->bpf_offloaded_id = prog ? prog->aux->id : 0;
99 nsim_prog_set_loaded(prog, true);
100
101 return 0;
102}
103
104int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,
105 void *type_data, void *cb_priv)
106{
107 struct tc_cls_bpf_offload *cls_bpf = type_data;
108 struct bpf_prog *prog = cls_bpf->prog;
109 struct netdevsim *ns = cb_priv;
110 bool skip_sw;
111
112 if (type != TC_SETUP_CLSBPF ||
113 !tc_can_offload(ns->netdev) ||
114 cls_bpf->common.protocol != htons(ETH_P_ALL) ||
115 cls_bpf->common.chain_index)
116 return -EOPNOTSUPP;
117
118 skip_sw = cls_bpf->gen_flags & TCA_CLS_FLAGS_SKIP_SW;
119
120 if (nsim_xdp_offload_active(ns))
121 return -EBUSY;
122
123 if (!ns->bpf_tc_accept)
124 return -EOPNOTSUPP;
125 /* Note: progs without skip_sw will probably not be dev bound */
126 if (prog && !prog->aux->offload && !ns->bpf_tc_non_bound_accept)
127 return -EOPNOTSUPP;
128
129 switch (cls_bpf->command) {
130 case TC_CLSBPF_REPLACE:
131 return nsim_bpf_offload(ns, prog, true);
132 case TC_CLSBPF_ADD:
133 return nsim_bpf_offload(ns, prog, false);
134 case TC_CLSBPF_DESTROY:
135 return nsim_bpf_offload(ns, NULL, true);
136 default:
137 return -EOPNOTSUPP;
138 }
139}
140
141int nsim_bpf_disable_tc(struct netdevsim *ns)
142{
143 if (ns->bpf_offloaded && !nsim_xdp_offload_active(ns))
144 return -EBUSY;
145 return 0;
146}
147
148static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
149{
150 if (!nsim_xdp_offload_active(ns) && !bpf->prog)
151 return 0;
152 if (!nsim_xdp_offload_active(ns) && bpf->prog && ns->bpf_offloaded) {
153 NSIM_EA(bpf->extack, "TC program is already loaded");
154 return -EBUSY;
155 }
156
157 return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns));
158}
159
160static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
161{
162 int err;
163
164 if (ns->xdp_prog && (bpf->flags ^ ns->xdp_flags) & XDP_FLAGS_MODES) {
165 NSIM_EA(bpf->extack, "program loaded with different flags");
166 return -EBUSY;
167 }
168
169 if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) {
170 NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS");
171 return -EOPNOTSUPP;
172 }
173 if (bpf->command == XDP_SETUP_PROG_HW && !ns->bpf_xdpoffload_accept) {
174 NSIM_EA(bpf->extack, "XDP offload disabled in DebugFS");
175 return -EOPNOTSUPP;
176 }
177
178 if (bpf->command == XDP_SETUP_PROG_HW) {
179 err = nsim_xdp_offload_prog(ns, bpf);
180 if (err)
181 return err;
182 }
183
184 if (ns->xdp_prog)
185 bpf_prog_put(ns->xdp_prog);
186
187 ns->xdp_prog = bpf->prog;
188 ns->xdp_flags = bpf->flags;
189
190 if (!bpf->prog)
191 ns->xdp_prog_mode = XDP_ATTACHED_NONE;
192 else if (bpf->command == XDP_SETUP_PROG)
193 ns->xdp_prog_mode = XDP_ATTACHED_DRV;
194 else
195 ns->xdp_prog_mode = XDP_ATTACHED_HW;
196
197 return 0;
198}
199
200int nsim_bpf_create_prog(struct netdevsim *ns, struct bpf_prog *prog)
201{
202 struct nsim_bpf_bound_prog *state;
203 char name[16];
204 int err;
205
206 state = kzalloc(sizeof(*state), GFP_KERNEL);
207 if (!state)
208 return -ENOMEM;
209
210 state->ns = ns;
211 state->prog = prog;
212 state->state = "verify";
213
214 /* Program id is not populated yet when we create the state. */
215 sprintf(name, "%u", ns->prog_id_gen++);
216 state->ddir = debugfs_create_dir(name, ns->ddir_bpf_bound_progs);
217 if (IS_ERR(state->ddir)) {
218 err = PTR_ERR(state->ddir);
219 kfree(state);
220 return err;
221 }
222
223 debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id);
224 debugfs_create_file("state", 0400, state->ddir,
225 &state->state, &nsim_bpf_string_fops);
226 debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded);
227
228 list_add_tail(&state->l, &ns->bpf_bound_progs);
229
230 prog->aux->offload->dev_priv = state;
231
232 return 0;
233}
234
235void nsim_bpf_destroy_prog(struct bpf_prog *prog)
236{
237 struct nsim_bpf_bound_prog *state;
238
239 state = prog->aux->offload->dev_priv;
240 WARN(state->is_loaded,
241 "offload state destroyed while program still bound");
242 debugfs_remove_recursive(state->ddir);
243 list_del(&state->l);
244 kfree(state);
245}
246
247static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
248{
249 if (bpf->prog && bpf->prog->aux->offload) {
250 NSIM_EA(bpf->extack, "attempt to load offloaded prog to drv");
251 return -EINVAL;
252 }
253 if (ns->netdev->mtu > NSIM_XDP_MAX_MTU) {
254 NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled");
255 return -EINVAL;
256 }
257 if (nsim_xdp_offload_active(ns)) {
258 NSIM_EA(bpf->extack, "xdp offload active, can't load drv prog");
259 return -EBUSY;
260 }
261 return 0;
262}
263
264static int
265nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
266{
267 struct nsim_bpf_bound_prog *state;
268
269 if (!bpf->prog)
270 return 0;
271
272 if (!bpf->prog->aux->offload) {
273 NSIM_EA(bpf->extack, "xdpoffload of non-bound program");
274 return -EINVAL;
275 }
276 if (bpf->prog->aux->offload->netdev != ns->netdev) {
277 NSIM_EA(bpf->extack, "program bound to different dev");
278 return -EINVAL;
279 }
280
281 state = bpf->prog->aux->offload->dev_priv;
282 if (WARN_ON(strcmp(state->state, "xlated"))) {
283 NSIM_EA(bpf->extack, "offloading program in bad state");
284 return -EINVAL;
285 }
286 return 0;
287}
288
289int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
290{
291 struct netdevsim *ns = netdev_priv(dev);
292 struct nsim_bpf_bound_prog *state;
293 int err;
294
295 ASSERT_RTNL();
296
297 switch (bpf->command) {
298 case BPF_OFFLOAD_VERIFIER_PREP:
299 if (!ns->bpf_bind_accept)
300 return -EOPNOTSUPP;
301
302 err = nsim_bpf_create_prog(ns, bpf->verifier.prog);
303 if (err)
304 return err;
305
306 bpf->verifier.ops = &nsim_bpf_analyzer_ops;
307 return 0;
308 case BPF_OFFLOAD_TRANSLATE:
309 state = bpf->offload.prog->aux->offload->dev_priv;
310
311 state->state = "xlated";
312 return 0;
313 case BPF_OFFLOAD_DESTROY:
314 nsim_bpf_destroy_prog(bpf->offload.prog);
315 return 0;
316 case XDP_QUERY_PROG:
317 bpf->prog_attached = ns->xdp_prog_mode;
318 bpf->prog_id = ns->xdp_prog ? ns->xdp_prog->aux->id : 0;
319 bpf->prog_flags = ns->xdp_prog ? ns->xdp_flags : 0;
320 return 0;
321 case XDP_SETUP_PROG:
322 err = nsim_setup_prog_checks(ns, bpf);
323 if (err)
324 return err;
325
326 return nsim_xdp_set_prog(ns, bpf);
327 case XDP_SETUP_PROG_HW:
328 err = nsim_setup_prog_hw_checks(ns, bpf);
329 if (err)
330 return err;
331
332 return nsim_xdp_set_prog(ns, bpf);
333 default:
334 return -EINVAL;
335 }
336}
337
338int nsim_bpf_init(struct netdevsim *ns)
339{
340 INIT_LIST_HEAD(&ns->bpf_bound_progs);
341
342 debugfs_create_u32("bpf_offloaded_id", 0400, ns->ddir,
343 &ns->bpf_offloaded_id);
344
345 ns->bpf_bind_accept = true;
346 debugfs_create_bool("bpf_bind_accept", 0600, ns->ddir,
347 &ns->bpf_bind_accept);
348 debugfs_create_u32("bpf_bind_verifier_delay", 0600, ns->ddir,
349 &ns->bpf_bind_verifier_delay);
350 ns->ddir_bpf_bound_progs =
351 debugfs_create_dir("bpf_bound_progs", ns->ddir);
352
353 ns->bpf_tc_accept = true;
354 debugfs_create_bool("bpf_tc_accept", 0600, ns->ddir,
355 &ns->bpf_tc_accept);
356 debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ns->ddir,
357 &ns->bpf_tc_non_bound_accept);
358 ns->bpf_xdpdrv_accept = true;
359 debugfs_create_bool("bpf_xdpdrv_accept", 0600, ns->ddir,
360 &ns->bpf_xdpdrv_accept);
361 ns->bpf_xdpoffload_accept = true;
362 debugfs_create_bool("bpf_xdpoffload_accept", 0600, ns->ddir,
363 &ns->bpf_xdpoffload_accept);
364
365 return 0;
366}
367
368void nsim_bpf_uninit(struct netdevsim *ns)
369{
370 WARN_ON(!list_empty(&ns->bpf_bound_progs));
371 WARN_ON(ns->xdp_prog);
372 WARN_ON(ns->bpf_offloaded);
373}
This page took 0.066426 seconds and 4 git commands to generate.