Skip to content

Commit 15e541e

Browse files
committed
Merge branch 'bpf-array-map-offload-and-tests'
Jakub Kicinski says: ==================== This set brings in the rest of map offload code held up by urgent fixes and improvements to the BPF arrays. The first 3 patches take care of array map offload, similarly to hash maps the attribute validation is split out to a separate map op, and used for both offloaded and non-offloaded case (allocation only happens if map is on the host). Offload support comes down to allowing this map type through the offload check in the core. NFP driver also rejects the delete operation in case of array maps. Subsequent patches add reporting of target device in a very similar way target device of programs is reported (ifindex+netns dev/ino). Netdevsim is extended with a trivial map implementation allowing us to test the offload in test_offload.py. Last patch adds a small busy wait to NFP map IO, this improves the response times which is especially useful for map dumps. ==================== Signed-off-by: Daniel Borkmann <[email protected]>
2 parents 0c91c42 + ca027a1 commit 15e541e

File tree

14 files changed

+583
-48
lines changed

14 files changed

+583
-48
lines changed

drivers/net/ethernet/netronome/nfp/bpf/cmsg.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,14 @@ nfp_bpf_cmsg_wait_reply(struct nfp_app_bpf *bpf, enum nfp_bpf_cmsg_type type,
157157
int tag)
158158
{
159159
struct sk_buff *skb;
160-
int err;
160+
int i, err;
161+
162+
for (i = 0; i < 50; i++) {
163+
udelay(4);
164+
skb = nfp_bpf_reply(bpf, tag);
165+
if (skb)
166+
return skb;
167+
}
161168

162169
err = wait_event_interruptible_timeout(bpf->cmsg_wq,
163170
skb = nfp_bpf_reply(bpf, tag),

drivers/net/ethernet/netronome/nfp/bpf/offload.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ nfp_bpf_map_get_next_key(struct bpf_offloaded_map *offmap,
176176
static int
177177
nfp_bpf_map_delete_elem(struct bpf_offloaded_map *offmap, void *key)
178178
{
179+
if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY)
180+
return -EINVAL;
179181
return nfp_bpf_ctrl_del_entry(offmap, key);
180182
}
181183

drivers/net/netdevsim/bpf.c

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/bpf_verifier.h>
1818
#include <linux/debugfs.h>
1919
#include <linux/kernel.h>
20+
#include <linux/mutex.h>
2021
#include <linux/rtnetlink.h>
2122
#include <net/pkt_cls.h>
2223

@@ -31,6 +32,19 @@ struct nsim_bpf_bound_prog {
3132
struct list_head l;
3233
};
3334

35+
#define NSIM_BPF_MAX_KEYS 2
36+
37+
struct nsim_bpf_bound_map {
38+
struct netdevsim *ns;
39+
struct bpf_offloaded_map *map;
40+
struct mutex mutex;
41+
struct nsim_map_entry {
42+
void *key;
43+
void *value;
44+
} entry[NSIM_BPF_MAX_KEYS];
45+
struct list_head l;
46+
};
47+
3448
static int nsim_debugfs_bpf_string_read(struct seq_file *file, void *data)
3549
{
3650
const char **str = file->private;
@@ -284,6 +298,224 @@ nsim_setup_prog_hw_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
284298
return 0;
285299
}
286300

301+
static bool
302+
nsim_map_key_match(struct bpf_map *map, struct nsim_map_entry *e, void *key)
303+
{
304+
return e->key && !memcmp(key, e->key, map->key_size);
305+
}
306+
307+
static int nsim_map_key_find(struct bpf_offloaded_map *offmap, void *key)
308+
{
309+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
310+
unsigned int i;
311+
312+
for (i = 0; i < ARRAY_SIZE(nmap->entry); i++)
313+
if (nsim_map_key_match(&offmap->map, &nmap->entry[i], key))
314+
return i;
315+
316+
return -ENOENT;
317+
}
318+
319+
static int
320+
nsim_map_alloc_elem(struct bpf_offloaded_map *offmap, unsigned int idx)
321+
{
322+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
323+
324+
nmap->entry[idx].key = kmalloc(offmap->map.key_size, GFP_USER);
325+
if (!nmap->entry[idx].key)
326+
return -ENOMEM;
327+
nmap->entry[idx].value = kmalloc(offmap->map.value_size, GFP_USER);
328+
if (!nmap->entry[idx].value) {
329+
kfree(nmap->entry[idx].key);
330+
nmap->entry[idx].key = NULL;
331+
return -ENOMEM;
332+
}
333+
334+
return 0;
335+
}
336+
337+
static int
338+
nsim_map_get_next_key(struct bpf_offloaded_map *offmap,
339+
void *key, void *next_key)
340+
{
341+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
342+
int idx = -ENOENT;
343+
344+
mutex_lock(&nmap->mutex);
345+
346+
if (key)
347+
idx = nsim_map_key_find(offmap, key);
348+
if (idx == -ENOENT)
349+
idx = 0;
350+
else
351+
idx++;
352+
353+
for (; idx < ARRAY_SIZE(nmap->entry); idx++) {
354+
if (nmap->entry[idx].key) {
355+
memcpy(next_key, nmap->entry[idx].key,
356+
offmap->map.key_size);
357+
break;
358+
}
359+
}
360+
361+
mutex_unlock(&nmap->mutex);
362+
363+
if (idx == ARRAY_SIZE(nmap->entry))
364+
return -ENOENT;
365+
return 0;
366+
}
367+
368+
static int
369+
nsim_map_lookup_elem(struct bpf_offloaded_map *offmap, void *key, void *value)
370+
{
371+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
372+
int idx;
373+
374+
mutex_lock(&nmap->mutex);
375+
376+
idx = nsim_map_key_find(offmap, key);
377+
if (idx >= 0)
378+
memcpy(value, nmap->entry[idx].value, offmap->map.value_size);
379+
380+
mutex_unlock(&nmap->mutex);
381+
382+
return idx < 0 ? idx : 0;
383+
}
384+
385+
static int
386+
nsim_map_update_elem(struct bpf_offloaded_map *offmap,
387+
void *key, void *value, u64 flags)
388+
{
389+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
390+
int idx, err = 0;
391+
392+
mutex_lock(&nmap->mutex);
393+
394+
idx = nsim_map_key_find(offmap, key);
395+
if (idx < 0 && flags == BPF_EXIST) {
396+
err = idx;
397+
goto exit_unlock;
398+
}
399+
if (idx >= 0 && flags == BPF_NOEXIST) {
400+
err = -EEXIST;
401+
goto exit_unlock;
402+
}
403+
404+
if (idx < 0) {
405+
for (idx = 0; idx < ARRAY_SIZE(nmap->entry); idx++)
406+
if (!nmap->entry[idx].key)
407+
break;
408+
if (idx == ARRAY_SIZE(nmap->entry)) {
409+
err = -E2BIG;
410+
goto exit_unlock;
411+
}
412+
413+
err = nsim_map_alloc_elem(offmap, idx);
414+
if (err)
415+
goto exit_unlock;
416+
}
417+
418+
memcpy(nmap->entry[idx].key, key, offmap->map.key_size);
419+
memcpy(nmap->entry[idx].value, value, offmap->map.value_size);
420+
exit_unlock:
421+
mutex_unlock(&nmap->mutex);
422+
423+
return err;
424+
}
425+
426+
static int nsim_map_delete_elem(struct bpf_offloaded_map *offmap, void *key)
427+
{
428+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
429+
int idx;
430+
431+
if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY)
432+
return -EINVAL;
433+
434+
mutex_lock(&nmap->mutex);
435+
436+
idx = nsim_map_key_find(offmap, key);
437+
if (idx >= 0) {
438+
kfree(nmap->entry[idx].key);
439+
kfree(nmap->entry[idx].value);
440+
memset(&nmap->entry[idx], 0, sizeof(nmap->entry[idx]));
441+
}
442+
443+
mutex_unlock(&nmap->mutex);
444+
445+
return idx < 0 ? idx : 0;
446+
}
447+
448+
static const struct bpf_map_dev_ops nsim_bpf_map_ops = {
449+
.map_get_next_key = nsim_map_get_next_key,
450+
.map_lookup_elem = nsim_map_lookup_elem,
451+
.map_update_elem = nsim_map_update_elem,
452+
.map_delete_elem = nsim_map_delete_elem,
453+
};
454+
455+
static int
456+
nsim_bpf_map_alloc(struct netdevsim *ns, struct bpf_offloaded_map *offmap)
457+
{
458+
struct nsim_bpf_bound_map *nmap;
459+
unsigned int i;
460+
int err;
461+
462+
if (WARN_ON(offmap->map.map_type != BPF_MAP_TYPE_ARRAY &&
463+
offmap->map.map_type != BPF_MAP_TYPE_HASH))
464+
return -EINVAL;
465+
if (offmap->map.max_entries > NSIM_BPF_MAX_KEYS)
466+
return -ENOMEM;
467+
if (offmap->map.map_flags)
468+
return -EINVAL;
469+
470+
nmap = kzalloc(sizeof(*nmap), GFP_USER);
471+
if (!nmap)
472+
return -ENOMEM;
473+
474+
offmap->dev_priv = nmap;
475+
nmap->ns = ns;
476+
nmap->map = offmap;
477+
mutex_init(&nmap->mutex);
478+
479+
if (offmap->map.map_type == BPF_MAP_TYPE_ARRAY) {
480+
for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) {
481+
u32 *key;
482+
483+
err = nsim_map_alloc_elem(offmap, i);
484+
if (err)
485+
goto err_free;
486+
key = nmap->entry[i].key;
487+
*key = i;
488+
}
489+
}
490+
491+
offmap->dev_ops = &nsim_bpf_map_ops;
492+
list_add_tail(&nmap->l, &ns->bpf_bound_maps);
493+
494+
return 0;
495+
496+
err_free:
497+
while (--i) {
498+
kfree(nmap->entry[i].key);
499+
kfree(nmap->entry[i].value);
500+
}
501+
kfree(nmap);
502+
return err;
503+
}
504+
505+
static void nsim_bpf_map_free(struct bpf_offloaded_map *offmap)
506+
{
507+
struct nsim_bpf_bound_map *nmap = offmap->dev_priv;
508+
unsigned int i;
509+
510+
for (i = 0; i < ARRAY_SIZE(nmap->entry); i++) {
511+
kfree(nmap->entry[i].key);
512+
kfree(nmap->entry[i].value);
513+
}
514+
list_del_init(&nmap->l);
515+
mutex_destroy(&nmap->mutex);
516+
kfree(nmap);
517+
}
518+
287519
int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
288520
{
289521
struct netdevsim *ns = netdev_priv(dev);
@@ -328,6 +560,14 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
328560
return err;
329561

330562
return nsim_xdp_set_prog(ns, bpf);
563+
case BPF_OFFLOAD_MAP_ALLOC:
564+
if (!ns->bpf_map_accept)
565+
return -EOPNOTSUPP;
566+
567+
return nsim_bpf_map_alloc(ns, bpf->offmap);
568+
case BPF_OFFLOAD_MAP_FREE:
569+
nsim_bpf_map_free(bpf->offmap);
570+
return 0;
331571
default:
332572
return -EINVAL;
333573
}
@@ -336,6 +576,7 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
336576
int nsim_bpf_init(struct netdevsim *ns)
337577
{
338578
INIT_LIST_HEAD(&ns->bpf_bound_progs);
579+
INIT_LIST_HEAD(&ns->bpf_bound_maps);
339580

340581
debugfs_create_u32("bpf_offloaded_id", 0400, ns->ddir,
341582
&ns->bpf_offloaded_id);
@@ -362,12 +603,17 @@ int nsim_bpf_init(struct netdevsim *ns)
362603
debugfs_create_bool("bpf_xdpoffload_accept", 0600, ns->ddir,
363604
&ns->bpf_xdpoffload_accept);
364605

606+
ns->bpf_map_accept = true;
607+
debugfs_create_bool("bpf_map_accept", 0600, ns->ddir,
608+
&ns->bpf_map_accept);
609+
365610
return 0;
366611
}
367612

368613
void nsim_bpf_uninit(struct netdevsim *ns)
369614
{
370615
WARN_ON(!list_empty(&ns->bpf_bound_progs));
616+
WARN_ON(!list_empty(&ns->bpf_bound_maps));
371617
WARN_ON(ns->xdp_prog);
372618
WARN_ON(ns->bpf_offloaded);
373619
}

drivers/net/netdevsim/netdevsim.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ struct netdevsim {
6161
bool bpf_tc_non_bound_accept;
6262
bool bpf_xdpdrv_accept;
6363
bool bpf_xdpoffload_accept;
64+
65+
bool bpf_map_accept;
66+
struct list_head bpf_bound_maps;
6467
};
6568

6669
extern struct dentry *nsim_ddir;

include/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,8 @@ void bpf_prog_offload_destroy(struct bpf_prog *prog);
586586
int bpf_prog_offload_info_fill(struct bpf_prog_info *info,
587587
struct bpf_prog *prog);
588588

589+
int bpf_map_offload_info_fill(struct bpf_map_info *info, struct bpf_map *map);
590+
589591
int bpf_map_offload_lookup_elem(struct bpf_map *map, void *key, void *value);
590592
int bpf_map_offload_update_elem(struct bpf_map *map,
591593
void *key, void *value, u64 flags);

include/uapi/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,9 @@ struct bpf_map_info {
938938
__u32 max_entries;
939939
__u32 map_flags;
940940
char name[BPF_OBJ_NAME_LEN];
941+
__u32 ifindex;
942+
__u64 netns_dev;
943+
__u64 netns_ino;
941944
} __attribute__((aligned(8)));
942945

943946
/* User bpf_sock_ops struct to access socket values and specify request ops

0 commit comments

Comments
 (0)