Skip to content

Commit 31d3ad8

Browse files
Jakub Kicinskiborkmann
authored andcommitted
netdevsim: add bpf offload support
Add support for loading programs for netdevsim devices and expose the related information via DebugFS. Both offload of XDP and cls_bpf programs is supported. Signed-off-by: Jakub Kicinski <[email protected]> Reviewed-by: Simon Horman <[email protected]> Reviewed-by: Quentin Monnet <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent 83c9e13 commit 31d3ad8

File tree

4 files changed

+529
-1
lines changed

4 files changed

+529
-1
lines changed

drivers/net/netdevsim/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ obj-$(CONFIG_NETDEVSIM) += netdevsim.o
44

55
netdevsim-objs := \
66
netdev.o \
7+
bpf.o \

drivers/net/netdevsim/bpf.c

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
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+
}

0 commit comments

Comments
 (0)