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/mutex.h> |
21 | #include <linux/rtnetlink.h> |
22 | #include <net/pkt_cls.h> |
23 | |
24 | #include "netdevsim.h" |
25 | |
26 | #define pr_vlog(env, fmt, ...) \ |
27 | bpf_verifier_log_write(env, "[netdevsim] " fmt, ##__VA_ARGS__) |
28 | |
29 | struct nsim_bpf_bound_prog { |
30 | struct nsim_dev *nsim_dev; |
31 | struct bpf_prog *prog; |
32 | struct dentry *ddir; |
33 | const char *state; |
34 | bool is_loaded; |
35 | struct list_head l; |
36 | }; |
37 | |
38 | #define NSIM_BPF_MAX_KEYS 2 |
39 | |
40 | struct nsim_bpf_bound_map { |
41 | struct netdevsim *ns; |
42 | struct bpf_offloaded_map *map; |
43 | struct mutex mutex; |
44 | struct nsim_map_entry { |
45 | void *key; |
46 | void *value; |
47 | } entry[NSIM_BPF_MAX_KEYS]; |
48 | struct list_head l; |
49 | }; |
50 | |
51 | static int nsim_bpf_string_show(struct seq_file *file, void *data) |
52 | { |
53 | const char **str = file->private; |
54 | |
55 | if (*str) |
56 | seq_printf(m: file, fmt: "%s\n" , *str); |
57 | |
58 | return 0; |
59 | } |
60 | DEFINE_SHOW_ATTRIBUTE(nsim_bpf_string); |
61 | |
62 | static int |
63 | nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn) |
64 | { |
65 | struct nsim_bpf_bound_prog *state; |
66 | int ret = 0; |
67 | |
68 | state = env->prog->aux->offload->dev_priv; |
69 | if (state->nsim_dev->bpf_bind_verifier_delay && !insn_idx) |
70 | msleep(msecs: state->nsim_dev->bpf_bind_verifier_delay); |
71 | |
72 | if (insn_idx == env->prog->len - 1) { |
73 | pr_vlog(env, "Hello from netdevsim!\n" ); |
74 | |
75 | if (!state->nsim_dev->bpf_bind_verifier_accept) |
76 | ret = -EOPNOTSUPP; |
77 | } |
78 | |
79 | return ret; |
80 | } |
81 | |
82 | static int nsim_bpf_finalize(struct bpf_verifier_env *env) |
83 | { |
84 | return 0; |
85 | } |
86 | |
87 | static bool nsim_xdp_offload_active(struct netdevsim *ns) |
88 | { |
89 | return ns->xdp_hw.prog; |
90 | } |
91 | |
92 | static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded) |
93 | { |
94 | struct nsim_bpf_bound_prog *state; |
95 | |
96 | if (!prog || !prog->aux->offload) |
97 | return; |
98 | |
99 | state = prog->aux->offload->dev_priv; |
100 | state->is_loaded = loaded; |
101 | } |
102 | |
103 | static int |
104 | nsim_bpf_offload(struct netdevsim *ns, struct bpf_prog *prog, bool oldprog) |
105 | { |
106 | nsim_prog_set_loaded(prog: ns->bpf_offloaded, loaded: false); |
107 | |
108 | WARN(!!ns->bpf_offloaded != oldprog, |
109 | "bad offload state, expected offload %sto be active" , |
110 | oldprog ? "" : "not " ); |
111 | ns->bpf_offloaded = prog; |
112 | ns->bpf_offloaded_id = prog ? prog->aux->id : 0; |
113 | nsim_prog_set_loaded(prog, loaded: true); |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type, |
119 | void *type_data, void *cb_priv) |
120 | { |
121 | struct tc_cls_bpf_offload *cls_bpf = type_data; |
122 | struct bpf_prog *prog = cls_bpf->prog; |
123 | struct netdevsim *ns = cb_priv; |
124 | struct bpf_prog *oldprog; |
125 | |
126 | if (type != TC_SETUP_CLSBPF) { |
127 | NSIM_EA(cls_bpf->common.extack, |
128 | "only offload of BPF classifiers supported" ); |
129 | return -EOPNOTSUPP; |
130 | } |
131 | |
132 | if (!tc_cls_can_offload_and_chain0(dev: ns->netdev, common: &cls_bpf->common)) |
133 | return -EOPNOTSUPP; |
134 | |
135 | if (cls_bpf->common.protocol != htons(ETH_P_ALL)) { |
136 | NSIM_EA(cls_bpf->common.extack, |
137 | "only ETH_P_ALL supported as filter protocol" ); |
138 | return -EOPNOTSUPP; |
139 | } |
140 | |
141 | if (!ns->bpf_tc_accept) { |
142 | NSIM_EA(cls_bpf->common.extack, |
143 | "netdevsim configured to reject BPF TC offload" ); |
144 | return -EOPNOTSUPP; |
145 | } |
146 | /* Note: progs without skip_sw will probably not be dev bound */ |
147 | if (prog && !prog->aux->offload && !ns->bpf_tc_non_bound_accept) { |
148 | NSIM_EA(cls_bpf->common.extack, |
149 | "netdevsim configured to reject unbound programs" ); |
150 | return -EOPNOTSUPP; |
151 | } |
152 | |
153 | if (cls_bpf->command != TC_CLSBPF_OFFLOAD) |
154 | return -EOPNOTSUPP; |
155 | |
156 | oldprog = cls_bpf->oldprog; |
157 | |
158 | /* Don't remove if oldprog doesn't match driver's state */ |
159 | if (ns->bpf_offloaded != oldprog) { |
160 | oldprog = NULL; |
161 | if (!cls_bpf->prog) |
162 | return 0; |
163 | if (ns->bpf_offloaded) { |
164 | NSIM_EA(cls_bpf->common.extack, |
165 | "driver and netdev offload states mismatch" ); |
166 | return -EBUSY; |
167 | } |
168 | } |
169 | |
170 | return nsim_bpf_offload(ns, prog: cls_bpf->prog, oldprog); |
171 | } |
172 | |
173 | int nsim_bpf_disable_tc(struct netdevsim *ns) |
174 | { |
175 | if (ns->bpf_offloaded && !nsim_xdp_offload_active(ns)) |
176 | return -EBUSY; |
177 | return 0; |
178 | } |
179 | |
180 | static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf) |
181 | { |
182 | if (!nsim_xdp_offload_active(ns) && !bpf->prog) |
183 | return 0; |
184 | if (!nsim_xdp_offload_active(ns) && bpf->prog && ns->bpf_offloaded) { |
185 | NSIM_EA(bpf->extack, "TC program is already loaded" ); |
186 | return -EBUSY; |
187 | } |
188 | |
189 | return nsim_bpf_offload(ns, prog: bpf->prog, oldprog: nsim_xdp_offload_active(ns)); |
190 | } |
191 | |
192 | static int |
193 | nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf, |
194 | struct xdp_attachment_info *xdp) |
195 | { |
196 | int err; |
197 | |
198 | if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) { |
199 | NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS" ); |
200 | return -EOPNOTSUPP; |
201 | } |
202 | if (bpf->command == XDP_SETUP_PROG_HW && !ns->bpf_xdpoffload_accept) { |
203 | NSIM_EA(bpf->extack, "XDP offload disabled in DebugFS" ); |
204 | return -EOPNOTSUPP; |
205 | } |
206 | |
207 | if (bpf->command == XDP_SETUP_PROG_HW) { |
208 | err = nsim_xdp_offload_prog(ns, bpf); |
209 | if (err) |
210 | return err; |
211 | } |
212 | |
213 | xdp_attachment_setup(info: xdp, bpf); |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev, |
219 | struct bpf_prog *prog) |
220 | { |
221 | struct nsim_bpf_bound_prog *state; |
222 | char name[16]; |
223 | int ret; |
224 | |
225 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
226 | if (!state) |
227 | return -ENOMEM; |
228 | |
229 | state->nsim_dev = nsim_dev; |
230 | state->prog = prog; |
231 | state->state = "verify" ; |
232 | |
233 | /* Program id is not populated yet when we create the state. */ |
234 | sprintf(buf: name, fmt: "%u" , nsim_dev->prog_id_gen++); |
235 | state->ddir = debugfs_create_dir(name, parent: nsim_dev->ddir_bpf_bound_progs); |
236 | if (IS_ERR(ptr: state->ddir)) { |
237 | ret = PTR_ERR(ptr: state->ddir); |
238 | kfree(objp: state); |
239 | return ret; |
240 | } |
241 | |
242 | debugfs_create_u32(name: "id" , mode: 0400, parent: state->ddir, value: &prog->aux->id); |
243 | debugfs_create_file(name: "state" , mode: 0400, parent: state->ddir, |
244 | data: &state->state, fops: &nsim_bpf_string_fops); |
245 | debugfs_create_bool(name: "loaded" , mode: 0400, parent: state->ddir, value: &state->is_loaded); |
246 | |
247 | list_add_tail(new: &state->l, head: &nsim_dev->bpf_bound_progs); |
248 | |
249 | prog->aux->offload->dev_priv = state; |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | static int nsim_bpf_verifier_prep(struct bpf_prog *prog) |
255 | { |
256 | struct nsim_dev *nsim_dev = |
257 | bpf_offload_dev_priv(offdev: prog->aux->offload->offdev); |
258 | |
259 | if (!nsim_dev->bpf_bind_accept) |
260 | return -EOPNOTSUPP; |
261 | |
262 | return nsim_bpf_create_prog(nsim_dev, prog); |
263 | } |
264 | |
265 | static int nsim_bpf_translate(struct bpf_prog *prog) |
266 | { |
267 | struct nsim_bpf_bound_prog *state = prog->aux->offload->dev_priv; |
268 | |
269 | state->state = "xlated" ; |
270 | return 0; |
271 | } |
272 | |
273 | static void nsim_bpf_destroy_prog(struct bpf_prog *prog) |
274 | { |
275 | struct nsim_bpf_bound_prog *state; |
276 | |
277 | state = prog->aux->offload->dev_priv; |
278 | WARN(state->is_loaded, |
279 | "offload state destroyed while program still bound" ); |
280 | debugfs_remove_recursive(dentry: state->ddir); |
281 | list_del(entry: &state->l); |
282 | kfree(objp: state); |
283 | } |
284 | |
285 | static const struct bpf_prog_offload_ops nsim_bpf_dev_ops = { |
286 | .insn_hook = nsim_bpf_verify_insn, |
287 | .finalize = nsim_bpf_finalize, |
288 | .prepare = nsim_bpf_verifier_prep, |
289 | .translate = nsim_bpf_translate, |
290 | .destroy = nsim_bpf_destroy_prog, |
291 | }; |
292 | |
293 | static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf) |
294 | { |
295 | if (bpf->prog && bpf->prog->aux->offload) { |
296 | NSIM_EA(bpf->extack, "attempt to load offloaded prog to drv" ); |
297 | return -EINVAL; |
298 | } |
299 | if (ns->netdev->mtu > NSIM_XDP_MAX_MTU) { |
300 | NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled" ); |
301 | return -EINVAL; |
302 | } |
303 | return 0; |
304 | } |
305 | |
306 | static int |
307 | nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf) |
308 | { |
309 | struct nsim_bpf_bound_prog *state; |
310 | |
311 | if (!bpf->prog) |
312 | return 0; |
313 | |
314 | if (!bpf->prog->aux->offload) { |
315 | NSIM_EA(bpf->extack, "xdpoffload of non-bound program" ); |
316 | return -EINVAL; |
317 | } |
318 | |
319 | state = bpf->prog->aux->offload->dev_priv; |
320 | if (WARN_ON(strcmp(state->state, "xlated" ))) { |
321 | NSIM_EA(bpf->extack, "offloading program in bad state" ); |
322 | return -EINVAL; |
323 | } |
324 | return 0; |
325 | } |
326 | |
327 | static bool |
328 | nsim_map_key_match(struct bpf_map *map, struct nsim_map_entry *e, void *key) |
329 | { |
330 | return e->key && !memcmp(p: key, q: e->key, size: map->key_size); |
331 | } |
332 | |
333 | static int nsim_map_key_find(struct bpf_offloaded_map *offmap, void *key) |
334 | { |
335 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
336 | unsigned int i; |
337 | |
338 | for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) |
339 | if (nsim_map_key_match(map: &offmap->map, e: &nmap->entry[i], key)) |
340 | return i; |
341 | |
342 | return -ENOENT; |
343 | } |
344 | |
345 | static int |
346 | nsim_map_alloc_elem(struct bpf_offloaded_map *offmap, unsigned int idx) |
347 | { |
348 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
349 | |
350 | nmap->entry[idx].key = kmalloc(size: offmap->map.key_size, |
351 | GFP_KERNEL_ACCOUNT | __GFP_NOWARN); |
352 | if (!nmap->entry[idx].key) |
353 | return -ENOMEM; |
354 | nmap->entry[idx].value = kmalloc(size: offmap->map.value_size, |
355 | GFP_KERNEL_ACCOUNT | __GFP_NOWARN); |
356 | if (!nmap->entry[idx].value) { |
357 | kfree(objp: nmap->entry[idx].key); |
358 | nmap->entry[idx].key = NULL; |
359 | return -ENOMEM; |
360 | } |
361 | |
362 | return 0; |
363 | } |
364 | |
365 | static int |
366 | nsim_map_get_next_key(struct bpf_offloaded_map *offmap, |
367 | void *key, void *next_key) |
368 | { |
369 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
370 | int idx = -ENOENT; |
371 | |
372 | mutex_lock(&nmap->mutex); |
373 | |
374 | if (key) |
375 | idx = nsim_map_key_find(offmap, key); |
376 | if (idx == -ENOENT) |
377 | idx = 0; |
378 | else |
379 | idx++; |
380 | |
381 | for (; idx < ARRAY_SIZE(nmap->entry); idx++) { |
382 | if (nmap->entry[idx].key) { |
383 | memcpy(next_key, nmap->entry[idx].key, |
384 | offmap->map.key_size); |
385 | break; |
386 | } |
387 | } |
388 | |
389 | mutex_unlock(lock: &nmap->mutex); |
390 | |
391 | if (idx == ARRAY_SIZE(nmap->entry)) |
392 | return -ENOENT; |
393 | return 0; |
394 | } |
395 | |
396 | static int |
397 | nsim_map_lookup_elem(struct bpf_offloaded_map *offmap, void *key, void *value) |
398 | { |
399 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
400 | int idx; |
401 | |
402 | mutex_lock(&nmap->mutex); |
403 | |
404 | idx = nsim_map_key_find(offmap, key); |
405 | if (idx >= 0) |
406 | memcpy(value, nmap->entry[idx].value, offmap->map.value_size); |
407 | |
408 | mutex_unlock(lock: &nmap->mutex); |
409 | |
410 | return idx < 0 ? idx : 0; |
411 | } |
412 | |
413 | static int |
414 | nsim_map_update_elem(struct bpf_offloaded_map *offmap, |
415 | void *key, void *value, u64 flags) |
416 | { |
417 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
418 | int idx, err = 0; |
419 | |
420 | mutex_lock(&nmap->mutex); |
421 | |
422 | idx = nsim_map_key_find(offmap, key); |
423 | if (idx < 0 && flags == BPF_EXIST) { |
424 | err = idx; |
425 | goto exit_unlock; |
426 | } |
427 | if (idx >= 0 && flags == BPF_NOEXIST) { |
428 | err = -EEXIST; |
429 | goto exit_unlock; |
430 | } |
431 | |
432 | if (idx < 0) { |
433 | for (idx = 0; idx < ARRAY_SIZE(nmap->entry); idx++) |
434 | if (!nmap->entry[idx].key) |
435 | break; |
436 | if (idx == ARRAY_SIZE(nmap->entry)) { |
437 | err = -E2BIG; |
438 | goto exit_unlock; |
439 | } |
440 | |
441 | err = nsim_map_alloc_elem(offmap, idx); |
442 | if (err) |
443 | goto exit_unlock; |
444 | } |
445 | |
446 | memcpy(nmap->entry[idx].key, key, offmap->map.key_size); |
447 | memcpy(nmap->entry[idx].value, value, offmap->map.value_size); |
448 | exit_unlock: |
449 | mutex_unlock(lock: &nmap->mutex); |
450 | |
451 | return err; |
452 | } |
453 | |
454 | static int nsim_map_delete_elem(struct bpf_offloaded_map *offmap, void *key) |
455 | { |
456 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
457 | int idx; |
458 | |
459 | if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) |
460 | return -EINVAL; |
461 | |
462 | mutex_lock(&nmap->mutex); |
463 | |
464 | idx = nsim_map_key_find(offmap, key); |
465 | if (idx >= 0) { |
466 | kfree(objp: nmap->entry[idx].key); |
467 | kfree(objp: nmap->entry[idx].value); |
468 | memset(&nmap->entry[idx], 0, sizeof(nmap->entry[idx])); |
469 | } |
470 | |
471 | mutex_unlock(lock: &nmap->mutex); |
472 | |
473 | return idx < 0 ? idx : 0; |
474 | } |
475 | |
476 | static const struct bpf_map_dev_ops nsim_bpf_map_ops = { |
477 | .map_get_next_key = nsim_map_get_next_key, |
478 | .map_lookup_elem = nsim_map_lookup_elem, |
479 | .map_update_elem = nsim_map_update_elem, |
480 | .map_delete_elem = nsim_map_delete_elem, |
481 | }; |
482 | |
483 | static int |
484 | nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap) |
485 | { |
486 | struct nsim_bpf_bound_map *nmap; |
487 | int i, err; |
488 | |
489 | if (WARN_ON(offmap->map.map_type != BPF_MAP_TYPE_ARRAY && |
490 | offmap->map.map_type != BPF_MAP_TYPE_HASH)) |
491 | return -EINVAL; |
492 | if (offmap->map.max_entries > NSIM_BPF_MAX_KEYS) |
493 | return -ENOMEM; |
494 | if (offmap->map.map_flags) |
495 | return -EINVAL; |
496 | |
497 | nmap = kzalloc(size: sizeof(*nmap), GFP_KERNEL_ACCOUNT); |
498 | if (!nmap) |
499 | return -ENOMEM; |
500 | |
501 | offmap->dev_priv = nmap; |
502 | nmap->ns = ns; |
503 | nmap->map = offmap; |
504 | mutex_init(&nmap->mutex); |
505 | |
506 | if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) { |
507 | for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) { |
508 | u32 *key; |
509 | |
510 | err = nsim_map_alloc_elem(offmap, idx: i); |
511 | if (err) |
512 | goto err_free; |
513 | key = nmap->entry[i].key; |
514 | *key = i; |
515 | memset(nmap->entry[i].value, 0, offmap->map.value_size); |
516 | } |
517 | } |
518 | |
519 | offmap->dev_ops = &nsim_bpf_map_ops; |
520 | list_add_tail(new: &nmap->l, head: &ns->nsim_dev->bpf_bound_maps); |
521 | |
522 | return 0; |
523 | |
524 | err_free: |
525 | while (--i >= 0) { |
526 | kfree(objp: nmap->entry[i].key); |
527 | kfree(objp: nmap->entry[i].value); |
528 | } |
529 | kfree(objp: nmap); |
530 | return err; |
531 | } |
532 | |
533 | static void nsim_bpf_map_free(struct bpf_offloaded_map *offmap) |
534 | { |
535 | struct nsim_bpf_bound_map *nmap = offmap->dev_priv; |
536 | unsigned int i; |
537 | |
538 | for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) { |
539 | kfree(objp: nmap->entry[i].key); |
540 | kfree(objp: nmap->entry[i].value); |
541 | } |
542 | list_del_init(entry: &nmap->l); |
543 | mutex_destroy(lock: &nmap->mutex); |
544 | kfree(objp: nmap); |
545 | } |
546 | |
547 | int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf) |
548 | { |
549 | struct netdevsim *ns = netdev_priv(dev); |
550 | int err; |
551 | |
552 | ASSERT_RTNL(); |
553 | |
554 | switch (bpf->command) { |
555 | case XDP_SETUP_PROG: |
556 | err = nsim_setup_prog_checks(ns, bpf); |
557 | if (err) |
558 | return err; |
559 | |
560 | return nsim_xdp_set_prog(ns, bpf, xdp: &ns->xdp); |
561 | case XDP_SETUP_PROG_HW: |
562 | err = nsim_setup_prog_hw_checks(ns, bpf); |
563 | if (err) |
564 | return err; |
565 | |
566 | return nsim_xdp_set_prog(ns, bpf, xdp: &ns->xdp_hw); |
567 | case BPF_OFFLOAD_MAP_ALLOC: |
568 | if (!ns->bpf_map_accept) |
569 | return -EOPNOTSUPP; |
570 | |
571 | return nsim_bpf_map_alloc(ns, offmap: bpf->offmap); |
572 | case BPF_OFFLOAD_MAP_FREE: |
573 | nsim_bpf_map_free(offmap: bpf->offmap); |
574 | return 0; |
575 | default: |
576 | return -EINVAL; |
577 | } |
578 | } |
579 | |
580 | int nsim_bpf_dev_init(struct nsim_dev *nsim_dev) |
581 | { |
582 | int err; |
583 | |
584 | INIT_LIST_HEAD(list: &nsim_dev->bpf_bound_progs); |
585 | INIT_LIST_HEAD(list: &nsim_dev->bpf_bound_maps); |
586 | |
587 | nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir(name: "bpf_bound_progs" , |
588 | parent: nsim_dev->ddir); |
589 | if (IS_ERR(ptr: nsim_dev->ddir_bpf_bound_progs)) |
590 | return PTR_ERR(ptr: nsim_dev->ddir_bpf_bound_progs); |
591 | |
592 | nsim_dev->bpf_dev = bpf_offload_dev_create(ops: &nsim_bpf_dev_ops, priv: nsim_dev); |
593 | err = PTR_ERR_OR_ZERO(ptr: nsim_dev->bpf_dev); |
594 | if (err) |
595 | return err; |
596 | |
597 | nsim_dev->bpf_bind_accept = true; |
598 | debugfs_create_bool(name: "bpf_bind_accept" , mode: 0600, parent: nsim_dev->ddir, |
599 | value: &nsim_dev->bpf_bind_accept); |
600 | debugfs_create_u32(name: "bpf_bind_verifier_delay" , mode: 0600, parent: nsim_dev->ddir, |
601 | value: &nsim_dev->bpf_bind_verifier_delay); |
602 | nsim_dev->bpf_bind_verifier_accept = true; |
603 | debugfs_create_bool(name: "bpf_bind_verifier_accept" , mode: 0600, parent: nsim_dev->ddir, |
604 | value: &nsim_dev->bpf_bind_verifier_accept); |
605 | return 0; |
606 | } |
607 | |
608 | void nsim_bpf_dev_exit(struct nsim_dev *nsim_dev) |
609 | { |
610 | WARN_ON(!list_empty(&nsim_dev->bpf_bound_progs)); |
611 | WARN_ON(!list_empty(&nsim_dev->bpf_bound_maps)); |
612 | bpf_offload_dev_destroy(offdev: nsim_dev->bpf_dev); |
613 | } |
614 | |
615 | int nsim_bpf_init(struct netdevsim *ns) |
616 | { |
617 | struct dentry *ddir = ns->nsim_dev_port->ddir; |
618 | int err; |
619 | |
620 | err = bpf_offload_dev_netdev_register(offdev: ns->nsim_dev->bpf_dev, |
621 | netdev: ns->netdev); |
622 | if (err) |
623 | return err; |
624 | |
625 | debugfs_create_u32(name: "bpf_offloaded_id" , mode: 0400, parent: ddir, |
626 | value: &ns->bpf_offloaded_id); |
627 | |
628 | ns->bpf_tc_accept = true; |
629 | debugfs_create_bool(name: "bpf_tc_accept" , mode: 0600, parent: ddir, |
630 | value: &ns->bpf_tc_accept); |
631 | debugfs_create_bool(name: "bpf_tc_non_bound_accept" , mode: 0600, parent: ddir, |
632 | value: &ns->bpf_tc_non_bound_accept); |
633 | ns->bpf_xdpdrv_accept = true; |
634 | debugfs_create_bool(name: "bpf_xdpdrv_accept" , mode: 0600, parent: ddir, |
635 | value: &ns->bpf_xdpdrv_accept); |
636 | ns->bpf_xdpoffload_accept = true; |
637 | debugfs_create_bool(name: "bpf_xdpoffload_accept" , mode: 0600, parent: ddir, |
638 | value: &ns->bpf_xdpoffload_accept); |
639 | |
640 | ns->bpf_map_accept = true; |
641 | debugfs_create_bool(name: "bpf_map_accept" , mode: 0600, parent: ddir, |
642 | value: &ns->bpf_map_accept); |
643 | |
644 | return 0; |
645 | } |
646 | |
647 | void nsim_bpf_uninit(struct netdevsim *ns) |
648 | { |
649 | WARN_ON(ns->xdp.prog); |
650 | WARN_ON(ns->xdp_hw.prog); |
651 | WARN_ON(ns->bpf_offloaded); |
652 | bpf_offload_dev_netdev_unregister(offdev: ns->nsim_dev->bpf_dev, netdev: ns->netdev); |
653 | } |
654 | |