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