xref: /linux-6.15/drivers/net/netdevsim/bpf.c (revision e016cf5f)
131d3ad83SJakub Kicinski /*
231d3ad83SJakub Kicinski  * Copyright (C) 2017 Netronome Systems, Inc.
331d3ad83SJakub Kicinski  *
431d3ad83SJakub Kicinski  * This software is licensed under the GNU General License Version 2,
531d3ad83SJakub Kicinski  * June 1991 as shown in the file COPYING in the top-level directory of this
631d3ad83SJakub Kicinski  * source tree.
731d3ad83SJakub Kicinski  *
831d3ad83SJakub Kicinski  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
931d3ad83SJakub Kicinski  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
1031d3ad83SJakub Kicinski  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1131d3ad83SJakub Kicinski  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
1231d3ad83SJakub Kicinski  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
1331d3ad83SJakub Kicinski  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
1431d3ad83SJakub Kicinski  */
1531d3ad83SJakub Kicinski 
1631d3ad83SJakub Kicinski #include <linux/bpf.h>
1731d3ad83SJakub Kicinski #include <linux/bpf_verifier.h>
1831d3ad83SJakub Kicinski #include <linux/debugfs.h>
1931d3ad83SJakub Kicinski #include <linux/kernel.h>
20395cacb5SJakub Kicinski #include <linux/mutex.h>
2131d3ad83SJakub Kicinski #include <linux/rtnetlink.h>
2231d3ad83SJakub Kicinski #include <net/pkt_cls.h>
2331d3ad83SJakub Kicinski 
2431d3ad83SJakub Kicinski #include "netdevsim.h"
2531d3ad83SJakub Kicinski 
269045bdc8SQuentin Monnet #define pr_vlog(env, fmt, ...)	\
279045bdc8SQuentin Monnet 	bpf_verifier_log_write(env, "[netdevsim] " fmt, ##__VA_ARGS__)
289045bdc8SQuentin Monnet 
2931d3ad83SJakub Kicinski struct nsim_bpf_bound_prog {
30d514f41eSJiri Pirko 	struct nsim_dev *nsim_dev;
3131d3ad83SJakub Kicinski 	struct bpf_prog *prog;
3231d3ad83SJakub Kicinski 	struct dentry *ddir;
3331d3ad83SJakub Kicinski 	const char *state;
3431d3ad83SJakub Kicinski 	bool is_loaded;
3531d3ad83SJakub Kicinski 	struct list_head l;
3631d3ad83SJakub Kicinski };
3731d3ad83SJakub Kicinski 
38395cacb5SJakub Kicinski #define NSIM_BPF_MAX_KEYS		2
39395cacb5SJakub Kicinski 
40395cacb5SJakub Kicinski struct nsim_bpf_bound_map {
41395cacb5SJakub Kicinski 	struct netdevsim *ns;
42395cacb5SJakub Kicinski 	struct bpf_offloaded_map *map;
43395cacb5SJakub Kicinski 	struct mutex mutex;
44395cacb5SJakub Kicinski 	struct nsim_map_entry {
45395cacb5SJakub Kicinski 		void *key;
46395cacb5SJakub Kicinski 		void *value;
47395cacb5SJakub Kicinski 	} entry[NSIM_BPF_MAX_KEYS];
48395cacb5SJakub Kicinski 	struct list_head l;
49395cacb5SJakub Kicinski };
50395cacb5SJakub Kicinski 
nsim_bpf_string_show(struct seq_file * file,void * data)51e6652f0fSYangtao Li static int nsim_bpf_string_show(struct seq_file *file, void *data)
5231d3ad83SJakub Kicinski {
5331d3ad83SJakub Kicinski 	const char **str = file->private;
5431d3ad83SJakub Kicinski 
5531d3ad83SJakub Kicinski 	if (*str)
5631d3ad83SJakub Kicinski 		seq_printf(file, "%s\n", *str);
5731d3ad83SJakub Kicinski 
5831d3ad83SJakub Kicinski 	return 0;
5931d3ad83SJakub Kicinski }
60e6652f0fSYangtao Li DEFINE_SHOW_ATTRIBUTE(nsim_bpf_string);
6131d3ad83SJakub Kicinski 
6231d3ad83SJakub Kicinski static int
nsim_bpf_verify_insn(struct bpf_verifier_env * env,int insn_idx,int prev_insn)6331d3ad83SJakub Kicinski nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)
6431d3ad83SJakub Kicinski {
6531d3ad83SJakub Kicinski 	struct nsim_bpf_bound_prog *state;
66e4ff5aa4SToke Høiland-Jørgensen 	int ret = 0;
6731d3ad83SJakub Kicinski 
6831d3ad83SJakub Kicinski 	state = env->prog->aux->offload->dev_priv;
69d514f41eSJiri Pirko 	if (state->nsim_dev->bpf_bind_verifier_delay && !insn_idx)
70d514f41eSJiri Pirko 		msleep(state->nsim_dev->bpf_bind_verifier_delay);
7131d3ad83SJakub Kicinski 
72e4ff5aa4SToke Høiland-Jørgensen 	if (insn_idx == env->prog->len - 1) {
739045bdc8SQuentin Monnet 		pr_vlog(env, "Hello from netdevsim!\n");
749045bdc8SQuentin Monnet 
75e4ff5aa4SToke Høiland-Jørgensen 		if (!state->nsim_dev->bpf_bind_verifier_accept)
76e4ff5aa4SToke Høiland-Jørgensen 			ret = -EOPNOTSUPP;
77e4ff5aa4SToke Høiland-Jørgensen 	}
78e4ff5aa4SToke Høiland-Jørgensen 
79e4ff5aa4SToke Høiland-Jørgensen 	return ret;
8031d3ad83SJakub Kicinski }
8131d3ad83SJakub Kicinski 
nsim_bpf_finalize(struct bpf_verifier_env * env)82c941ce9cSQuentin Monnet static int nsim_bpf_finalize(struct bpf_verifier_env *env)
83c941ce9cSQuentin Monnet {
84c941ce9cSQuentin Monnet 	return 0;
85c941ce9cSQuentin Monnet }
86c941ce9cSQuentin Monnet 
nsim_xdp_offload_active(struct netdevsim * ns)8731d3ad83SJakub Kicinski static bool nsim_xdp_offload_active(struct netdevsim *ns)
8831d3ad83SJakub Kicinski {
89799e173dSJakub Kicinski 	return ns->xdp_hw.prog;
9031d3ad83SJakub Kicinski }
9131d3ad83SJakub Kicinski 
nsim_prog_set_loaded(struct bpf_prog * prog,bool loaded)9231d3ad83SJakub Kicinski static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded)
9331d3ad83SJakub Kicinski {
9431d3ad83SJakub Kicinski 	struct nsim_bpf_bound_prog *state;
9531d3ad83SJakub Kicinski 
96c0c6bde5SStanislav Fomichev 	if (!prog || !bpf_prog_is_offloaded(prog->aux))
9731d3ad83SJakub Kicinski 		return;
9831d3ad83SJakub Kicinski 
9931d3ad83SJakub Kicinski 	state = prog->aux->offload->dev_priv;
10031d3ad83SJakub Kicinski 	state->is_loaded = loaded;
10131d3ad83SJakub Kicinski }
10231d3ad83SJakub Kicinski 
10331d3ad83SJakub Kicinski static int
nsim_bpf_offload(struct netdevsim * ns,struct bpf_prog * prog,bool oldprog)10431d3ad83SJakub Kicinski nsim_bpf_offload(struct netdevsim *ns, struct bpf_prog *prog, bool oldprog)
10531d3ad83SJakub Kicinski {
10631d3ad83SJakub Kicinski 	nsim_prog_set_loaded(ns->bpf_offloaded, false);
10731d3ad83SJakub Kicinski 
10831d3ad83SJakub Kicinski 	WARN(!!ns->bpf_offloaded != oldprog,
10931d3ad83SJakub Kicinski 	     "bad offload state, expected offload %sto be active",
11031d3ad83SJakub Kicinski 	     oldprog ? "" : "not ");
11131d3ad83SJakub Kicinski 	ns->bpf_offloaded = prog;
11231d3ad83SJakub Kicinski 	ns->bpf_offloaded_id = prog ? prog->aux->id : 0;
11331d3ad83SJakub Kicinski 	nsim_prog_set_loaded(prog, true);
11431d3ad83SJakub Kicinski 
11531d3ad83SJakub Kicinski 	return 0;
11631d3ad83SJakub Kicinski }
11731d3ad83SJakub Kicinski 
nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)11831d3ad83SJakub Kicinski int nsim_bpf_setup_tc_block_cb(enum tc_setup_type type,
11931d3ad83SJakub Kicinski 			       void *type_data, void *cb_priv)
12031d3ad83SJakub Kicinski {
12131d3ad83SJakub Kicinski 	struct tc_cls_bpf_offload *cls_bpf = type_data;
12231d3ad83SJakub Kicinski 	struct bpf_prog *prog = cls_bpf->prog;
12331d3ad83SJakub Kicinski 	struct netdevsim *ns = cb_priv;
124fba961abSDavid S. Miller 	struct bpf_prog *oldprog;
12531d3ad83SJakub Kicinski 
126728461f2SQuentin Monnet 	if (type != TC_SETUP_CLSBPF) {
127728461f2SQuentin Monnet 		NSIM_EA(cls_bpf->common.extack,
128728461f2SQuentin Monnet 			"only offload of BPF classifiers supported");
129728461f2SQuentin Monnet 		return -EOPNOTSUPP;
130728461f2SQuentin Monnet 	}
131728461f2SQuentin Monnet 
132a2b212a5SJakub Kicinski 	if (!tc_cls_can_offload_and_chain0(ns->netdev, &cls_bpf->common))
13331d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
13431d3ad83SJakub Kicinski 
135728461f2SQuentin Monnet 	if (cls_bpf->common.protocol != htons(ETH_P_ALL)) {
136728461f2SQuentin Monnet 		NSIM_EA(cls_bpf->common.extack,
137728461f2SQuentin Monnet 			"only ETH_P_ALL supported as filter protocol");
13831d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
139728461f2SQuentin Monnet 	}
140728461f2SQuentin Monnet 
141728461f2SQuentin Monnet 	if (!ns->bpf_tc_accept) {
142728461f2SQuentin Monnet 		NSIM_EA(cls_bpf->common.extack,
143728461f2SQuentin Monnet 			"netdevsim configured to reject BPF TC offload");
144728461f2SQuentin Monnet 		return -EOPNOTSUPP;
145728461f2SQuentin Monnet 	}
14631d3ad83SJakub Kicinski 	/* Note: progs without skip_sw will probably not be dev bound */
147728461f2SQuentin Monnet 	if (prog && !prog->aux->offload && !ns->bpf_tc_non_bound_accept) {
148728461f2SQuentin Monnet 		NSIM_EA(cls_bpf->common.extack,
149728461f2SQuentin Monnet 			"netdevsim configured to reject unbound programs");
15031d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
151728461f2SQuentin Monnet 	}
15231d3ad83SJakub Kicinski 
153fba961abSDavid S. Miller 	if (cls_bpf->command != TC_CLSBPF_OFFLOAD)
15431d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
155fba961abSDavid S. Miller 
156fba961abSDavid S. Miller 	oldprog = cls_bpf->oldprog;
157fba961abSDavid S. Miller 
158fba961abSDavid S. Miller 	/* Don't remove if oldprog doesn't match driver's state */
159fba961abSDavid S. Miller 	if (ns->bpf_offloaded != oldprog) {
160fba961abSDavid S. Miller 		oldprog = NULL;
161fba961abSDavid S. Miller 		if (!cls_bpf->prog)
162fba961abSDavid S. Miller 			return 0;
163728461f2SQuentin Monnet 		if (ns->bpf_offloaded) {
164728461f2SQuentin Monnet 			NSIM_EA(cls_bpf->common.extack,
165728461f2SQuentin Monnet 				"driver and netdev offload states mismatch");
166fba961abSDavid S. Miller 			return -EBUSY;
16731d3ad83SJakub Kicinski 		}
168728461f2SQuentin Monnet 	}
169fba961abSDavid S. Miller 
170fba961abSDavid S. Miller 	return nsim_bpf_offload(ns, cls_bpf->prog, oldprog);
17131d3ad83SJakub Kicinski }
17231d3ad83SJakub Kicinski 
nsim_bpf_disable_tc(struct netdevsim * ns)17331d3ad83SJakub Kicinski int nsim_bpf_disable_tc(struct netdevsim *ns)
17431d3ad83SJakub Kicinski {
17531d3ad83SJakub Kicinski 	if (ns->bpf_offloaded && !nsim_xdp_offload_active(ns))
17631d3ad83SJakub Kicinski 		return -EBUSY;
17731d3ad83SJakub Kicinski 	return 0;
17831d3ad83SJakub Kicinski }
17931d3ad83SJakub Kicinski 
nsim_xdp_offload_prog(struct netdevsim * ns,struct netdev_bpf * bpf)18031d3ad83SJakub Kicinski static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
18131d3ad83SJakub Kicinski {
18231d3ad83SJakub Kicinski 	if (!nsim_xdp_offload_active(ns) && !bpf->prog)
18331d3ad83SJakub Kicinski 		return 0;
18431d3ad83SJakub Kicinski 	if (!nsim_xdp_offload_active(ns) && bpf->prog && ns->bpf_offloaded) {
18531d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "TC program is already loaded");
18631d3ad83SJakub Kicinski 		return -EBUSY;
18731d3ad83SJakub Kicinski 	}
18831d3ad83SJakub Kicinski 
18931d3ad83SJakub Kicinski 	return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns));
19031d3ad83SJakub Kicinski }
19131d3ad83SJakub Kicinski 
192799e173dSJakub Kicinski static int
nsim_xdp_set_prog(struct netdevsim * ns,struct netdev_bpf * bpf,struct xdp_attachment_info * xdp)193799e173dSJakub Kicinski nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf,
194799e173dSJakub Kicinski 		  struct xdp_attachment_info *xdp)
19531d3ad83SJakub Kicinski {
19631d3ad83SJakub Kicinski 	int err;
19731d3ad83SJakub Kicinski 
19831d3ad83SJakub Kicinski 	if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) {
19931d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS");
20031d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
20131d3ad83SJakub Kicinski 	}
20231d3ad83SJakub Kicinski 	if (bpf->command == XDP_SETUP_PROG_HW && !ns->bpf_xdpoffload_accept) {
20331d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "XDP offload disabled in DebugFS");
20431d3ad83SJakub Kicinski 		return -EOPNOTSUPP;
20531d3ad83SJakub Kicinski 	}
20631d3ad83SJakub Kicinski 
20731d3ad83SJakub Kicinski 	if (bpf->command == XDP_SETUP_PROG_HW) {
20831d3ad83SJakub Kicinski 		err = nsim_xdp_offload_prog(ns, bpf);
20931d3ad83SJakub Kicinski 		if (err)
21031d3ad83SJakub Kicinski 			return err;
21131d3ad83SJakub Kicinski 	}
21231d3ad83SJakub Kicinski 
213799e173dSJakub Kicinski 	xdp_attachment_setup(xdp, bpf);
21431d3ad83SJakub Kicinski 
21531d3ad83SJakub Kicinski 	return 0;
21631d3ad83SJakub Kicinski }
21731d3ad83SJakub Kicinski 
nsim_bpf_create_prog(struct nsim_dev * nsim_dev,struct bpf_prog * prog)218d514f41eSJiri Pirko static int nsim_bpf_create_prog(struct nsim_dev *nsim_dev,
219b26b6946SJiri Pirko 				struct bpf_prog *prog)
22031d3ad83SJakub Kicinski {
22131d3ad83SJakub Kicinski 	struct nsim_bpf_bound_prog *state;
22231d3ad83SJakub Kicinski 	char name[16];
2236556ff32STaehee Yoo 	int ret;
22431d3ad83SJakub Kicinski 
22531d3ad83SJakub Kicinski 	state = kzalloc(sizeof(*state), GFP_KERNEL);
22631d3ad83SJakub Kicinski 	if (!state)
22731d3ad83SJakub Kicinski 		return -ENOMEM;
22831d3ad83SJakub Kicinski 
229d514f41eSJiri Pirko 	state->nsim_dev = nsim_dev;
23031d3ad83SJakub Kicinski 	state->prog = prog;
23131d3ad83SJakub Kicinski 	state->state = "verify";
23231d3ad83SJakub Kicinski 
23331d3ad83SJakub Kicinski 	/* Program id is not populated yet when we create the state. */
234d514f41eSJiri Pirko 	sprintf(name, "%u", nsim_dev->prog_id_gen++);
235d514f41eSJiri Pirko 	state->ddir = debugfs_create_dir(name, nsim_dev->ddir_bpf_bound_progs);
2366556ff32STaehee Yoo 	if (IS_ERR(state->ddir)) {
2376556ff32STaehee Yoo 		ret = PTR_ERR(state->ddir);
23831d3ad83SJakub Kicinski 		kfree(state);
2396556ff32STaehee Yoo 		return ret;
24031d3ad83SJakub Kicinski 	}
24131d3ad83SJakub Kicinski 
24231d3ad83SJakub Kicinski 	debugfs_create_u32("id", 0400, state->ddir, &prog->aux->id);
24331d3ad83SJakub Kicinski 	debugfs_create_file("state", 0400, state->ddir,
24431d3ad83SJakub Kicinski 			    &state->state, &nsim_bpf_string_fops);
24531d3ad83SJakub Kicinski 	debugfs_create_bool("loaded", 0400, state->ddir, &state->is_loaded);
24631d3ad83SJakub Kicinski 
247d514f41eSJiri Pirko 	list_add_tail(&state->l, &nsim_dev->bpf_bound_progs);
24831d3ad83SJakub Kicinski 
24931d3ad83SJakub Kicinski 	prog->aux->offload->dev_priv = state;
25031d3ad83SJakub Kicinski 
25131d3ad83SJakub Kicinski 	return 0;
25231d3ad83SJakub Kicinski }
25331d3ad83SJakub Kicinski 
nsim_bpf_verifier_prep(struct bpf_prog * prog)25416a8cb5cSQuentin Monnet static int nsim_bpf_verifier_prep(struct bpf_prog *prog)
25500db12c3SQuentin Monnet {
256d514f41eSJiri Pirko 	struct nsim_dev *nsim_dev =
257b26b6946SJiri Pirko 			bpf_offload_dev_priv(prog->aux->offload->offdev);
25800db12c3SQuentin Monnet 
259d514f41eSJiri Pirko 	if (!nsim_dev->bpf_bind_accept)
26000db12c3SQuentin Monnet 		return -EOPNOTSUPP;
26100db12c3SQuentin Monnet 
262d514f41eSJiri Pirko 	return nsim_bpf_create_prog(nsim_dev, prog);
26300db12c3SQuentin Monnet }
26400db12c3SQuentin Monnet 
nsim_bpf_translate(struct bpf_prog * prog)26516a8cb5cSQuentin Monnet static int nsim_bpf_translate(struct bpf_prog *prog)
266b07ade27SQuentin Monnet {
267b07ade27SQuentin Monnet 	struct nsim_bpf_bound_prog *state = prog->aux->offload->dev_priv;
268b07ade27SQuentin Monnet 
269b07ade27SQuentin Monnet 	state->state = "xlated";
270b07ade27SQuentin Monnet 	return 0;
271b07ade27SQuentin Monnet }
272b07ade27SQuentin Monnet 
nsim_bpf_destroy_prog(struct bpf_prog * prog)2731dfc2663SColin Ian King static void nsim_bpf_destroy_prog(struct bpf_prog *prog)
27431d3ad83SJakub Kicinski {
27531d3ad83SJakub Kicinski 	struct nsim_bpf_bound_prog *state;
27631d3ad83SJakub Kicinski 
27731d3ad83SJakub Kicinski 	state = prog->aux->offload->dev_priv;
27831d3ad83SJakub Kicinski 	WARN(state->is_loaded,
27931d3ad83SJakub Kicinski 	     "offload state destroyed while program still bound");
28031d3ad83SJakub Kicinski 	debugfs_remove_recursive(state->ddir);
28131d3ad83SJakub Kicinski 	list_del(&state->l);
28231d3ad83SJakub Kicinski 	kfree(state);
28331d3ad83SJakub Kicinski }
28431d3ad83SJakub Kicinski 
28500db12c3SQuentin Monnet static const struct bpf_prog_offload_ops nsim_bpf_dev_ops = {
28600db12c3SQuentin Monnet 	.insn_hook	= nsim_bpf_verify_insn,
28700db12c3SQuentin Monnet 	.finalize	= nsim_bpf_finalize,
28800db12c3SQuentin Monnet 	.prepare	= nsim_bpf_verifier_prep,
289b07ade27SQuentin Monnet 	.translate	= nsim_bpf_translate,
290eb911947SQuentin Monnet 	.destroy	= nsim_bpf_destroy_prog,
29100db12c3SQuentin Monnet };
29200db12c3SQuentin Monnet 
nsim_setup_prog_checks(struct netdevsim * ns,struct netdev_bpf * bpf)29331d3ad83SJakub Kicinski static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
29431d3ad83SJakub Kicinski {
29531d3ad83SJakub Kicinski 	if (bpf->prog && bpf->prog->aux->offload) {
29631d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "attempt to load offloaded prog to drv");
29731d3ad83SJakub Kicinski 		return -EINVAL;
29831d3ad83SJakub Kicinski 	}
299*e016cf5fSJakub Kicinski 	if (bpf->prog && !bpf->prog->aux->xdp_has_frags &&
300*e016cf5fSJakub Kicinski 	    ns->netdev->mtu > NSIM_XDP_MAX_MTU) {
30131d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled");
30231d3ad83SJakub Kicinski 		return -EINVAL;
30331d3ad83SJakub Kicinski 	}
30431d3ad83SJakub Kicinski 	return 0;
30531d3ad83SJakub Kicinski }
30631d3ad83SJakub Kicinski 
30731d3ad83SJakub Kicinski static int
nsim_setup_prog_hw_checks(struct netdevsim * ns,struct netdev_bpf * bpf)30831d3ad83SJakub Kicinski nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
30931d3ad83SJakub Kicinski {
31031d3ad83SJakub Kicinski 	struct nsim_bpf_bound_prog *state;
31131d3ad83SJakub Kicinski 
31231d3ad83SJakub Kicinski 	if (!bpf->prog)
31331d3ad83SJakub Kicinski 		return 0;
31431d3ad83SJakub Kicinski 
315c0c6bde5SStanislav Fomichev 	if (!bpf_prog_is_offloaded(bpf->prog->aux)) {
31631d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "xdpoffload of non-bound program");
31731d3ad83SJakub Kicinski 		return -EINVAL;
31831d3ad83SJakub Kicinski 	}
31931d3ad83SJakub Kicinski 
32031d3ad83SJakub Kicinski 	state = bpf->prog->aux->offload->dev_priv;
32131d3ad83SJakub Kicinski 	if (WARN_ON(strcmp(state->state, "xlated"))) {
32231d3ad83SJakub Kicinski 		NSIM_EA(bpf->extack, "offloading program in bad state");
32331d3ad83SJakub Kicinski 		return -EINVAL;
32431d3ad83SJakub Kicinski 	}
32531d3ad83SJakub Kicinski 	return 0;
32631d3ad83SJakub Kicinski }
32731d3ad83SJakub Kicinski 
328395cacb5SJakub Kicinski static bool
nsim_map_key_match(struct bpf_map * map,struct nsim_map_entry * e,void * key)329395cacb5SJakub Kicinski nsim_map_key_match(struct bpf_map *map, struct nsim_map_entry *e, void *key)
330395cacb5SJakub Kicinski {
331395cacb5SJakub Kicinski 	return e->key && !memcmp(key, e->key, map->key_size);
332395cacb5SJakub Kicinski }
333395cacb5SJakub Kicinski 
nsim_map_key_find(struct bpf_offloaded_map * offmap,void * key)334395cacb5SJakub Kicinski static int nsim_map_key_find(struct bpf_offloaded_map *offmap, void *key)
335395cacb5SJakub Kicinski {
336395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
337395cacb5SJakub Kicinski 	unsigned int i;
338395cacb5SJakub Kicinski 
339395cacb5SJakub Kicinski 	for (i = 0; i < ARRAY_SIZE(nmap->entry); i++)
340395cacb5SJakub Kicinski 		if (nsim_map_key_match(&offmap->map, &nmap->entry[i], key))
341395cacb5SJakub Kicinski 			return i;
342395cacb5SJakub Kicinski 
343395cacb5SJakub Kicinski 	return -ENOENT;
344395cacb5SJakub Kicinski }
345395cacb5SJakub Kicinski 
346395cacb5SJakub Kicinski static int
nsim_map_alloc_elem(struct bpf_offloaded_map * offmap,unsigned int idx)347395cacb5SJakub Kicinski nsim_map_alloc_elem(struct bpf_offloaded_map *offmap, unsigned int idx)
348395cacb5SJakub Kicinski {
349395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
350395cacb5SJakub Kicinski 
351d0b80a9eSJakub Kicinski 	nmap->entry[idx].key = kmalloc(offmap->map.key_size,
352d0b80a9eSJakub Kicinski 				       GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
353395cacb5SJakub Kicinski 	if (!nmap->entry[idx].key)
354395cacb5SJakub Kicinski 		return -ENOMEM;
355d0b80a9eSJakub Kicinski 	nmap->entry[idx].value = kmalloc(offmap->map.value_size,
356d0b80a9eSJakub Kicinski 					 GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
357395cacb5SJakub Kicinski 	if (!nmap->entry[idx].value) {
358395cacb5SJakub Kicinski 		kfree(nmap->entry[idx].key);
359395cacb5SJakub Kicinski 		nmap->entry[idx].key = NULL;
360395cacb5SJakub Kicinski 		return -ENOMEM;
361395cacb5SJakub Kicinski 	}
362395cacb5SJakub Kicinski 
363395cacb5SJakub Kicinski 	return 0;
364395cacb5SJakub Kicinski }
365395cacb5SJakub Kicinski 
366395cacb5SJakub Kicinski static int
nsim_map_get_next_key(struct bpf_offloaded_map * offmap,void * key,void * next_key)367395cacb5SJakub Kicinski nsim_map_get_next_key(struct bpf_offloaded_map *offmap,
368395cacb5SJakub Kicinski 		      void *key, void *next_key)
369395cacb5SJakub Kicinski {
370395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
371395cacb5SJakub Kicinski 	int idx = -ENOENT;
372395cacb5SJakub Kicinski 
373395cacb5SJakub Kicinski 	mutex_lock(&nmap->mutex);
374395cacb5SJakub Kicinski 
375395cacb5SJakub Kicinski 	if (key)
376395cacb5SJakub Kicinski 		idx = nsim_map_key_find(offmap, key);
377395cacb5SJakub Kicinski 	if (idx == -ENOENT)
378395cacb5SJakub Kicinski 		idx = 0;
379395cacb5SJakub Kicinski 	else
380395cacb5SJakub Kicinski 		idx++;
381395cacb5SJakub Kicinski 
382395cacb5SJakub Kicinski 	for (; idx < ARRAY_SIZE(nmap->entry); idx++) {
383395cacb5SJakub Kicinski 		if (nmap->entry[idx].key) {
384395cacb5SJakub Kicinski 			memcpy(next_key, nmap->entry[idx].key,
385395cacb5SJakub Kicinski 			       offmap->map.key_size);
386395cacb5SJakub Kicinski 			break;
387395cacb5SJakub Kicinski 		}
388395cacb5SJakub Kicinski 	}
389395cacb5SJakub Kicinski 
390395cacb5SJakub Kicinski 	mutex_unlock(&nmap->mutex);
391395cacb5SJakub Kicinski 
392395cacb5SJakub Kicinski 	if (idx == ARRAY_SIZE(nmap->entry))
393395cacb5SJakub Kicinski 		return -ENOENT;
394395cacb5SJakub Kicinski 	return 0;
395395cacb5SJakub Kicinski }
396395cacb5SJakub Kicinski 
397395cacb5SJakub Kicinski static int
nsim_map_lookup_elem(struct bpf_offloaded_map * offmap,void * key,void * value)398395cacb5SJakub Kicinski nsim_map_lookup_elem(struct bpf_offloaded_map *offmap, void *key, void *value)
399395cacb5SJakub Kicinski {
400395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
401395cacb5SJakub Kicinski 	int idx;
402395cacb5SJakub Kicinski 
403395cacb5SJakub Kicinski 	mutex_lock(&nmap->mutex);
404395cacb5SJakub Kicinski 
405395cacb5SJakub Kicinski 	idx = nsim_map_key_find(offmap, key);
406395cacb5SJakub Kicinski 	if (idx >= 0)
407395cacb5SJakub Kicinski 		memcpy(value, nmap->entry[idx].value, offmap->map.value_size);
408395cacb5SJakub Kicinski 
409395cacb5SJakub Kicinski 	mutex_unlock(&nmap->mutex);
410395cacb5SJakub Kicinski 
411395cacb5SJakub Kicinski 	return idx < 0 ? idx : 0;
412395cacb5SJakub Kicinski }
413395cacb5SJakub Kicinski 
414395cacb5SJakub Kicinski static int
nsim_map_update_elem(struct bpf_offloaded_map * offmap,void * key,void * value,u64 flags)415395cacb5SJakub Kicinski nsim_map_update_elem(struct bpf_offloaded_map *offmap,
416395cacb5SJakub Kicinski 		     void *key, void *value, u64 flags)
417395cacb5SJakub Kicinski {
418395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
419395cacb5SJakub Kicinski 	int idx, err = 0;
420395cacb5SJakub Kicinski 
421395cacb5SJakub Kicinski 	mutex_lock(&nmap->mutex);
422395cacb5SJakub Kicinski 
423395cacb5SJakub Kicinski 	idx = nsim_map_key_find(offmap, key);
424395cacb5SJakub Kicinski 	if (idx < 0 && flags == BPF_EXIST) {
425395cacb5SJakub Kicinski 		err = idx;
426395cacb5SJakub Kicinski 		goto exit_unlock;
427395cacb5SJakub Kicinski 	}
428395cacb5SJakub Kicinski 	if (idx >= 0 && flags == BPF_NOEXIST) {
429395cacb5SJakub Kicinski 		err = -EEXIST;
430395cacb5SJakub Kicinski 		goto exit_unlock;
431395cacb5SJakub Kicinski 	}
432395cacb5SJakub Kicinski 
433395cacb5SJakub Kicinski 	if (idx < 0) {
434395cacb5SJakub Kicinski 		for (idx = 0; idx < ARRAY_SIZE(nmap->entry); idx++)
435395cacb5SJakub Kicinski 			if (!nmap->entry[idx].key)
436395cacb5SJakub Kicinski 				break;
437395cacb5SJakub Kicinski 		if (idx == ARRAY_SIZE(nmap->entry)) {
438395cacb5SJakub Kicinski 			err = -E2BIG;
439395cacb5SJakub Kicinski 			goto exit_unlock;
440395cacb5SJakub Kicinski 		}
441395cacb5SJakub Kicinski 
442395cacb5SJakub Kicinski 		err = nsim_map_alloc_elem(offmap, idx);
443395cacb5SJakub Kicinski 		if (err)
444395cacb5SJakub Kicinski 			goto exit_unlock;
445395cacb5SJakub Kicinski 	}
446395cacb5SJakub Kicinski 
447395cacb5SJakub Kicinski 	memcpy(nmap->entry[idx].key, key, offmap->map.key_size);
448395cacb5SJakub Kicinski 	memcpy(nmap->entry[idx].value, value, offmap->map.value_size);
449395cacb5SJakub Kicinski exit_unlock:
450395cacb5SJakub Kicinski 	mutex_unlock(&nmap->mutex);
451395cacb5SJakub Kicinski 
452395cacb5SJakub Kicinski 	return err;
453395cacb5SJakub Kicinski }
454395cacb5SJakub Kicinski 
nsim_map_delete_elem(struct bpf_offloaded_map * offmap,void * key)455395cacb5SJakub Kicinski static int nsim_map_delete_elem(struct bpf_offloaded_map *offmap, void *key)
456395cacb5SJakub Kicinski {
457395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
458395cacb5SJakub Kicinski 	int idx;
459395cacb5SJakub Kicinski 
460395cacb5SJakub Kicinski 	if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY)
461395cacb5SJakub Kicinski 		return -EINVAL;
462395cacb5SJakub Kicinski 
463395cacb5SJakub Kicinski 	mutex_lock(&nmap->mutex);
464395cacb5SJakub Kicinski 
465395cacb5SJakub Kicinski 	idx = nsim_map_key_find(offmap, key);
466395cacb5SJakub Kicinski 	if (idx >= 0) {
467395cacb5SJakub Kicinski 		kfree(nmap->entry[idx].key);
468395cacb5SJakub Kicinski 		kfree(nmap->entry[idx].value);
469395cacb5SJakub Kicinski 		memset(&nmap->entry[idx], 0, sizeof(nmap->entry[idx]));
470395cacb5SJakub Kicinski 	}
471395cacb5SJakub Kicinski 
472395cacb5SJakub Kicinski 	mutex_unlock(&nmap->mutex);
473395cacb5SJakub Kicinski 
474395cacb5SJakub Kicinski 	return idx < 0 ? idx : 0;
475395cacb5SJakub Kicinski }
476395cacb5SJakub Kicinski 
477395cacb5SJakub Kicinski static const struct bpf_map_dev_ops nsim_bpf_map_ops = {
478395cacb5SJakub Kicinski 	.map_get_next_key	= nsim_map_get_next_key,
479395cacb5SJakub Kicinski 	.map_lookup_elem	= nsim_map_lookup_elem,
480395cacb5SJakub Kicinski 	.map_update_elem	= nsim_map_update_elem,
481395cacb5SJakub Kicinski 	.map_delete_elem	= nsim_map_delete_elem,
482395cacb5SJakub Kicinski };
483395cacb5SJakub Kicinski 
484395cacb5SJakub Kicinski static int
nsim_bpf_map_alloc(struct netdevsim * ns,struct bpf_offloaded_map * offmap)485395cacb5SJakub Kicinski nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap)
486395cacb5SJakub Kicinski {
487395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap;
488e029f541SJakub Kicinski 	int i, err;
489395cacb5SJakub Kicinski 
490395cacb5SJakub Kicinski 	if (WARN_ON(offmap->map.map_type != BPF_MAP_TYPE_ARRAY &&
491395cacb5SJakub Kicinski 		    offmap->map.map_type != BPF_MAP_TYPE_HASH))
492395cacb5SJakub Kicinski 		return -EINVAL;
493395cacb5SJakub Kicinski 	if (offmap->map.max_entries > NSIM_BPF_MAX_KEYS)
494395cacb5SJakub Kicinski 		return -ENOMEM;
495395cacb5SJakub Kicinski 	if (offmap->map.map_flags)
496395cacb5SJakub Kicinski 		return -EINVAL;
497395cacb5SJakub Kicinski 
498d0b80a9eSJakub Kicinski 	nmap = kzalloc(sizeof(*nmap), GFP_KERNEL_ACCOUNT);
499395cacb5SJakub Kicinski 	if (!nmap)
500395cacb5SJakub Kicinski 		return -ENOMEM;
501395cacb5SJakub Kicinski 
502395cacb5SJakub Kicinski 	offmap->dev_priv = nmap;
503395cacb5SJakub Kicinski 	nmap->ns = ns;
504395cacb5SJakub Kicinski 	nmap->map = offmap;
505395cacb5SJakub Kicinski 	mutex_init(&nmap->mutex);
506395cacb5SJakub Kicinski 
507395cacb5SJakub Kicinski 	if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) {
508395cacb5SJakub Kicinski 		for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) {
509395cacb5SJakub Kicinski 			u32 *key;
510395cacb5SJakub Kicinski 
511395cacb5SJakub Kicinski 			err = nsim_map_alloc_elem(offmap, i);
512395cacb5SJakub Kicinski 			if (err)
513395cacb5SJakub Kicinski 				goto err_free;
514395cacb5SJakub Kicinski 			key = nmap->entry[i].key;
515395cacb5SJakub Kicinski 			*key = i;
51648122177SHaimin Zhang 			memset(nmap->entry[i].value, 0, offmap->map.value_size);
517395cacb5SJakub Kicinski 		}
518395cacb5SJakub Kicinski 	}
519395cacb5SJakub Kicinski 
520395cacb5SJakub Kicinski 	offmap->dev_ops = &nsim_bpf_map_ops;
521d514f41eSJiri Pirko 	list_add_tail(&nmap->l, &ns->nsim_dev->bpf_bound_maps);
522395cacb5SJakub Kicinski 
523395cacb5SJakub Kicinski 	return 0;
524395cacb5SJakub Kicinski 
525395cacb5SJakub Kicinski err_free:
526e029f541SJakub Kicinski 	while (--i >= 0) {
527395cacb5SJakub Kicinski 		kfree(nmap->entry[i].key);
528395cacb5SJakub Kicinski 		kfree(nmap->entry[i].value);
529395cacb5SJakub Kicinski 	}
530395cacb5SJakub Kicinski 	kfree(nmap);
531395cacb5SJakub Kicinski 	return err;
532395cacb5SJakub Kicinski }
533395cacb5SJakub Kicinski 
nsim_bpf_map_free(struct bpf_offloaded_map * offmap)534395cacb5SJakub Kicinski static void nsim_bpf_map_free(struct bpf_offloaded_map *offmap)
535395cacb5SJakub Kicinski {
536395cacb5SJakub Kicinski 	struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
537395cacb5SJakub Kicinski 	unsigned int i;
538395cacb5SJakub Kicinski 
539395cacb5SJakub Kicinski 	for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) {
540395cacb5SJakub Kicinski 		kfree(nmap->entry[i].key);
541395cacb5SJakub Kicinski 		kfree(nmap->entry[i].value);
542395cacb5SJakub Kicinski 	}
543395cacb5SJakub Kicinski 	list_del_init(&nmap->l);
544395cacb5SJakub Kicinski 	mutex_destroy(&nmap->mutex);
545395cacb5SJakub Kicinski 	kfree(nmap);
546395cacb5SJakub Kicinski }
547395cacb5SJakub Kicinski 
nsim_bpf(struct net_device * dev,struct netdev_bpf * bpf)54831d3ad83SJakub Kicinski int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
54931d3ad83SJakub Kicinski {
55031d3ad83SJakub Kicinski 	struct netdevsim *ns = netdev_priv(dev);
55131d3ad83SJakub Kicinski 	int err;
55231d3ad83SJakub Kicinski 
55331d3ad83SJakub Kicinski 	ASSERT_RTNL();
55431d3ad83SJakub Kicinski 
55531d3ad83SJakub Kicinski 	switch (bpf->command) {
55631d3ad83SJakub Kicinski 	case XDP_SETUP_PROG:
55731d3ad83SJakub Kicinski 		err = nsim_setup_prog_checks(ns, bpf);
55831d3ad83SJakub Kicinski 		if (err)
55931d3ad83SJakub Kicinski 			return err;
56031d3ad83SJakub Kicinski 
561799e173dSJakub Kicinski 		return nsim_xdp_set_prog(ns, bpf, &ns->xdp);
56231d3ad83SJakub Kicinski 	case XDP_SETUP_PROG_HW:
56331d3ad83SJakub Kicinski 		err = nsim_setup_prog_hw_checks(ns, bpf);
56431d3ad83SJakub Kicinski 		if (err)
56531d3ad83SJakub Kicinski 			return err;
56631d3ad83SJakub Kicinski 
567799e173dSJakub Kicinski 		return nsim_xdp_set_prog(ns, bpf, &ns->xdp_hw);
568395cacb5SJakub Kicinski 	case BPF_OFFLOAD_MAP_ALLOC:
569395cacb5SJakub Kicinski 		if (!ns->bpf_map_accept)
570395cacb5SJakub Kicinski 			return -EOPNOTSUPP;
571395cacb5SJakub Kicinski 
572395cacb5SJakub Kicinski 		return nsim_bpf_map_alloc(ns, bpf->offmap);
573395cacb5SJakub Kicinski 	case BPF_OFFLOAD_MAP_FREE:
574395cacb5SJakub Kicinski 		nsim_bpf_map_free(bpf->offmap);
575395cacb5SJakub Kicinski 		return 0;
57631d3ad83SJakub Kicinski 	default:
57731d3ad83SJakub Kicinski 		return -EINVAL;
57831d3ad83SJakub Kicinski 	}
57931d3ad83SJakub Kicinski }
58031d3ad83SJakub Kicinski 
nsim_bpf_dev_init(struct nsim_dev * nsim_dev)581d514f41eSJiri Pirko int nsim_bpf_dev_init(struct nsim_dev *nsim_dev)
5824b3a84bcSJiri Pirko {
5834b3a84bcSJiri Pirko 	int err;
5844b3a84bcSJiri Pirko 
585d514f41eSJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->bpf_bound_progs);
586d514f41eSJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->bpf_bound_maps);
5874b3a84bcSJiri Pirko 
588d514f41eSJiri Pirko 	nsim_dev->ddir_bpf_bound_progs = debugfs_create_dir("bpf_bound_progs",
589d514f41eSJiri Pirko 							    nsim_dev->ddir);
5906556ff32STaehee Yoo 	if (IS_ERR(nsim_dev->ddir_bpf_bound_progs))
5916556ff32STaehee Yoo 		return PTR_ERR(nsim_dev->ddir_bpf_bound_progs);
5924b3a84bcSJiri Pirko 
593d514f41eSJiri Pirko 	nsim_dev->bpf_dev = bpf_offload_dev_create(&nsim_bpf_dev_ops, nsim_dev);
594d514f41eSJiri Pirko 	err = PTR_ERR_OR_ZERO(nsim_dev->bpf_dev);
5954b3a84bcSJiri Pirko 	if (err)
5964b3a84bcSJiri Pirko 		return err;
5974b3a84bcSJiri Pirko 
598d514f41eSJiri Pirko 	nsim_dev->bpf_bind_accept = true;
599d514f41eSJiri Pirko 	debugfs_create_bool("bpf_bind_accept", 0600, nsim_dev->ddir,
600d514f41eSJiri Pirko 			    &nsim_dev->bpf_bind_accept);
601d514f41eSJiri Pirko 	debugfs_create_u32("bpf_bind_verifier_delay", 0600, nsim_dev->ddir,
602d514f41eSJiri Pirko 			   &nsim_dev->bpf_bind_verifier_delay);
603e4ff5aa4SToke Høiland-Jørgensen 	nsim_dev->bpf_bind_verifier_accept = true;
604e4ff5aa4SToke Høiland-Jørgensen 	debugfs_create_bool("bpf_bind_verifier_accept", 0600, nsim_dev->ddir,
605e4ff5aa4SToke Høiland-Jørgensen 			    &nsim_dev->bpf_bind_verifier_accept);
6064b3a84bcSJiri Pirko 	return 0;
6074b3a84bcSJiri Pirko }
6084b3a84bcSJiri Pirko 
nsim_bpf_dev_exit(struct nsim_dev * nsim_dev)609d514f41eSJiri Pirko void nsim_bpf_dev_exit(struct nsim_dev *nsim_dev)
6104b3a84bcSJiri Pirko {
611d514f41eSJiri Pirko 	WARN_ON(!list_empty(&nsim_dev->bpf_bound_progs));
612d514f41eSJiri Pirko 	WARN_ON(!list_empty(&nsim_dev->bpf_bound_maps));
613d514f41eSJiri Pirko 	bpf_offload_dev_destroy(nsim_dev->bpf_dev);
6144b3a84bcSJiri Pirko }
6154b3a84bcSJiri Pirko 
nsim_bpf_init(struct netdevsim * ns)61631d3ad83SJakub Kicinski int nsim_bpf_init(struct netdevsim *ns)
61731d3ad83SJakub Kicinski {
618e05b2d14SJiri Pirko 	struct dentry *ddir = ns->nsim_dev_port->ddir;
6199fd7c555SJakub Kicinski 	int err;
6209fd7c555SJakub Kicinski 
621d514f41eSJiri Pirko 	err = bpf_offload_dev_netdev_register(ns->nsim_dev->bpf_dev,
622d514f41eSJiri Pirko 					      ns->netdev);
6239fd7c555SJakub Kicinski 	if (err)
6249fd7c555SJakub Kicinski 		return err;
6259fd7c555SJakub Kicinski 
626e05b2d14SJiri Pirko 	debugfs_create_u32("bpf_offloaded_id", 0400, ddir,
62731d3ad83SJakub Kicinski 			   &ns->bpf_offloaded_id);
62831d3ad83SJakub Kicinski 
62931d3ad83SJakub Kicinski 	ns->bpf_tc_accept = true;
630e05b2d14SJiri Pirko 	debugfs_create_bool("bpf_tc_accept", 0600, ddir,
63131d3ad83SJakub Kicinski 			    &ns->bpf_tc_accept);
632e05b2d14SJiri Pirko 	debugfs_create_bool("bpf_tc_non_bound_accept", 0600, ddir,
63331d3ad83SJakub Kicinski 			    &ns->bpf_tc_non_bound_accept);
63431d3ad83SJakub Kicinski 	ns->bpf_xdpdrv_accept = true;
635e05b2d14SJiri Pirko 	debugfs_create_bool("bpf_xdpdrv_accept", 0600, ddir,
63631d3ad83SJakub Kicinski 			    &ns->bpf_xdpdrv_accept);
63731d3ad83SJakub Kicinski 	ns->bpf_xdpoffload_accept = true;
638e05b2d14SJiri Pirko 	debugfs_create_bool("bpf_xdpoffload_accept", 0600, ddir,
63931d3ad83SJakub Kicinski 			    &ns->bpf_xdpoffload_accept);
64031d3ad83SJakub Kicinski 
641395cacb5SJakub Kicinski 	ns->bpf_map_accept = true;
642e05b2d14SJiri Pirko 	debugfs_create_bool("bpf_map_accept", 0600, ddir,
643395cacb5SJakub Kicinski 			    &ns->bpf_map_accept);
644395cacb5SJakub Kicinski 
64531d3ad83SJakub Kicinski 	return 0;
64631d3ad83SJakub Kicinski }
64731d3ad83SJakub Kicinski 
nsim_bpf_uninit(struct netdevsim * ns)64831d3ad83SJakub Kicinski void nsim_bpf_uninit(struct netdevsim *ns)
64931d3ad83SJakub Kicinski {
65005296620SJakub Kicinski 	WARN_ON(ns->xdp.prog);
651799e173dSJakub Kicinski 	WARN_ON(ns->xdp_hw.prog);
65231d3ad83SJakub Kicinski 	WARN_ON(ns->bpf_offloaded);
653d514f41eSJiri Pirko 	bpf_offload_dev_netdev_unregister(ns->nsim_dev->bpf_dev, ns->netdev);
65431d3ad83SJakub Kicinski }
655