]>
Commit | Line | Data |
---|---|---|
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 | ||
25 | struct 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 | ||
34 | static 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 | ||
44 | static 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 | ||
49 | static 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 | ||
57 | static int | |
58 | nsim_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 | ||
69 | static const struct bpf_ext_analyzer_ops nsim_bpf_analyzer_ops = { | |
70 | .insn_hook = nsim_bpf_verify_insn, | |
71 | }; | |
72 | ||
73 | static bool nsim_xdp_offload_active(struct netdevsim *ns) | |
74 | { | |
75 | return ns->xdp_prog_mode == XDP_ATTACHED_HW; | |
76 | } | |
77 | ||
78 | static 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 | ||
89 | static int | |
90 | nsim_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 | ||
104 | int 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 | ||
141 | int 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 | ||
148 | static 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 | ||
160 | static 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 | ||
200 | int 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 | ||
235 | void 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 | ||
247 | static 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 | ||
264 | static int | |
265 | nsim_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 | ||
289 | int 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 | ||
338 | int 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 | ||
368 | void 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 | } |