BPF updates
Subsystems (4)
Daniel Borkmann
maintainerv2 cover BPF updates
This set adds support for persistent maps/progs. Please see individual patches for further details. A man-page update to bpf(2) will be sent later on, also a iproute2 patch for support in tc.
Thanks!
v1 -> v2:
- Reworked most of patch 4 and 5
- Rebased to latest net-next
Daniel Borkmann (5):
bpf: abstract anon_inode_getfd invocations
bpf: align and clean bpf_{map,prog}_get helpers
bpf: consolidate bpf_prog_put{,_rcu} dismantle paths
bpf: add support for persistent maps/progs
bpf: add sample usages for persistent maps/progs
include/linux/bpf.h | 9 +- include/uapi/linux/bpf.h | 45 +----- include/uapi/linux/magic.h | 1 + kernel/bpf/Makefile | 4 +- kernel/bpf/core.c | 3 +- kernel/bpf/inode.c | 387 +++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 95 +++++++---- kernel/bpf/verifier.c | 3 +- samples/bpf/Makefile | 3 + samples/bpf/fds_example.c | 183 +++++++++++++++++++++ samples/bpf/libbpf.c | 19 +++ samples/bpf/libbpf.h | 3 + 12 files changed, 683 insertions(+), 72 deletions(-) create mode 100644 kernel/bpf/inode.c create mode 100644 samples/bpf/fds_example.c
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH v2 1/5 bpf: abstract anon_inode_getfd invocations
Since we're going to use anon_inode_getfd() invocations in more than just the current places, make a helper function for both, so that we only need to pass a map/prog pointer to the helper itself in order to get a fd. The new helpers are called bpf_map_new_fd() and bpf_prog_new_fd().
--- kernel/bpf/syscall.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
kernel/bpf/syscall.c +14 -3
@@ -111,6 +111,12 @@ static const struct file_operations bpf_map_fops = { .release = bpf_map_release, }; +static int bpf_map_new_fd (struct bpf_map *map) +{ +return anon_inode_getfd ("bpf-map" , &bpf_map_fops, map, +O_RDWR |O_CLOEXEC ); +} +/* helper macro to check that unused fields 'union bpf_attr' are zero */ #define CHECK_ATTR (CMD ) \ memchr_inv((void *) &attr->CMD##_LAST_FIELD + \
@@ -141,8 +147,7 @@ static int map_create(union bpf_attr *attr)if (err) goto free_map; - err =anon_inode_getfd ("bpf-map" , &bpf_map_fops, map,O_RDWR |O_CLOEXEC ); - + err =bpf_map_new_fd (map);if (err <0 )/* failed to allocate fd */ goto free_map;
@@ -538,6 +543,12 @@ static const struct file_operations bpf_prog_fops = { .release = bpf_prog_release, }; +static int bpf_prog_new_fd (struct bpf_prog *prog) +{ +return anon_inode_getfd ("bpf-prog" , &bpf_prog_fops, prog, +O_RDWR |O_CLOEXEC ); +} +static struct bpf_prog *get_prog (struct fd f) {struct bpf_prog *prog;
@@ -647,7 +658,7 @@ static int bpf_prog_load(union bpf_attr *attr)if (err <0 ) goto free_used_maps; - err =anon_inode_getfd ("bpf-prog" , &bpf_prog_fops, prog,O_RDWR |O_CLOEXEC ); + err =bpf_prog_new_fd (prog);if (err <0 )/* failed to allocate fd */ goto free_used_maps;
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH v2 2/5 bpf: align and clean bpf_{map,prog}_get helpers
In reply to Daniel BorkmannAdd a bpf_map_get() function that we're going to use later on and align/clean the remaining helpers a bit so that we have them a bit more consistent:
- __bpf_map_get() and __bpf_prog_get() that both work on the fd struct, check whether the descriptor is eBPF and return the pointer to the map/prog stored in the private data.
Also, we can return f.file->private_data directly, the function signature is enough of a documentation already.
- bpf_map_get() and bpf_prog_get() that both work on u32 user fd, call their respective __bpf_map_get()/__bpf_prog_get() variants, and take a reference.
--- include/linux/bpf.h | 2 +- kernel/bpf/syscall.c | 41 +++++++++++++++++++++++------------------ kernel/bpf/verifier.c | 3 +-- 3 files changed, 25 insertions(+), 21 deletions(-)
include/linux/bpf.h +1 -1
@@ -167,7 +167,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd);void bpf_prog_put (struct bpf_prog *prog);void bpf_prog_put_rcu (struct bpf_prog *prog); -struct bpf_map *bpf_map_get (struct fd f); +struct bpf_map *__bpf_map_get (struct fd f);void bpf_map_put (struct bpf_map *map);extern int sysctl_unprivileged_bpf_disabled;
kernel/bpf/syscall.c +23 -18
104 lines not on this page · raw message
kernel/bpf/verifier.c +1 -2
@@ -1989,8 +1989,7 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env) } f =fdget (insn->imm); - - map =bpf_map_get (f); + map =__bpf_map_get (f);if (IS_ERR (map)) {verbose ("fd %d is not pointing to valid bpf_map\n" , insn->imm);
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH v2 3/5 bpf: consolidate bpf_prog_put{,_rcu} dismantle paths
In reply to Daniel BorkmannWe currently have duplicated cleanup code in bpf_prog_put() and bpf_prog_put_rcu() cleanup paths. Back then we decided that it was not worth it to make it a common helper called by both, but with the recent addition of resource charging, we could have avoided the fix in commit ac00737f4e81 ("bpf: Need to call bpf_prog_uncharge_memlock from bpf_prog_put") if we would have had only a single, common path. We can simplify it further by assigning aux->prog only once during allocation time.
--- kernel/bpf/core.c | 3 ++- kernel/bpf/syscall.c | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-)
kernel/bpf/core.c +2 -1
@@ -92,6 +92,7 @@ struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags) fp->pages = size /PAGE_SIZE ; fp->aux = aux; + fp->aux->prog = fp;return fp; }
@@ -116,6 +117,7 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,memcpy (fp, fp_old, fp_old->pages *PAGE_SIZE ); fp->pages = size /PAGE_SIZE ; + fp->aux->prog = fp;/* We keep fp->aux from fp_old around in the new * reallocated structure.
@@ -726,7 +728,6 @@ void bpf_prog_free(struct bpf_prog *fp)struct bpf_prog_aux *aux = fp->aux;INIT_WORK (&aux->work, bpf_prog_free_deferred); - aux->prog = fp;schedule_work (&aux->work); }EXPORT_SYMBOL_GPL (bpf_prog_free);
kernel/bpf/syscall.c +5 -10
@@ -513,7 +513,7 @@ static void bpf_prog_uncharge_memlock(struct bpf_prog *prog)free_uid (user); } -static void __prog_put_rcu (struct rcu_head *rcu) +static void __prog_put_common (struct rcu_head *rcu) { struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
@@ -525,19 +525,14 @@ static void __prog_put_rcu(struct rcu_head *rcu)/* version of bpf_prog_put() that is called after a grace period */ void bpf_prog_put_rcu (struct bpf_prog *prog) { -if (atomic_dec_and_test (&prog->aux->refcnt)) { - prog->aux->prog = prog; -call_rcu (&prog->aux->rcu, __prog_put_rcu); - } +if (atomic_dec_and_test (&prog->aux->refcnt)) +call_rcu (&prog->aux->rcu, __prog_put_common); }void bpf_prog_put (struct bpf_prog *prog) { -if (atomic_dec_and_test (&prog->aux->refcnt)) { -free_used_maps (prog->aux); -bpf_prog_uncharge_memlock (prog); -bpf_prog_free (prog); - } +if (atomic_dec_and_test (&prog->aux->refcnt)) +__prog_put_common (&prog->aux->rcu); }EXPORT_SYMBOL_GPL (bpf_prog_put);
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH v2 4/5 bpf: add support for persistent maps/progs
In reply to Daniel BorkmannThis work adds support for "persistent" eBPF maps/programs. The term "persistent" is to be understood that maps/programs have a facility that lets them survive process termination. This is desired by various eBPF subsystem users.
Just to name one example: tc classifier/action. Whenever tc parses the ELF object, extracts and loads maps/progs into the kernel, these file descriptors will be out of reach after the tc instance exits. So a subsequent tc invocation won't be able to access/relocate on this resource, and therefore maps cannot easily be shared, f.e. between the ingress and egress networking data path.
The current workaround is that Unix domain sockets (UDS) need to be instrumented in order to pass the created eBPF map/program file descriptors to a third party management daemon through UDS' socket passing facility. This makes it a bit complicated to deploy shared eBPF maps or programs (programs f.e. for tail calls) among various processes.
We've been brainstorming on how we could tackle this issue and various approches have been tried out so far, which can be read up further in the below reference.
The architecture we eventually ended up with is a minimal file system that can hold map/prog objects. The file system is a per mount namespace singleton, and the default mount point is /sys/fs/bpf/. Any subsequent mounts within a given namespace will point to the same instance. The file system allows for creating a user-defined directory structure. The objects for maps/progs are created/fetched through bpf(2) with two new commands (BPF_OBJ_PIN/BPF_OBJ_GET). I.e. a bpf file descriptor along with a pathname is being passed to bpf(2) that in turn creates (we call it eBPF object pinning) the file system nodes. Only the pathname is being passed to bpf(2) for getting a new BPF file descriptor to an existing node. The user can use that to access maps and progs later on, through bpf(2). Removal of file system nodes is being managed through normal VFS functions such as unlink(2), etc. The file system code is kept to a very minimum and can be further extended later on.
The next step I'm working on is to add dump eBPF map/prog commands to bpf(2), so that a specification from a given file descriptor can be retrieved. This can be used by things like CRIU but also applications can inspect the meta data after calling BPF_OBJ_GET.
Big thanks also to Alexei and Hannes who significantly contributed in the design discussion that eventually let us end up with this architecture here.
Reference: https://lkml.org/lkml/2015/10/15/925
--- include/linux/bpf.h | 7 + include/uapi/linux/bpf.h | 45 +----- include/uapi/linux/magic.h | 1 + kernel/bpf/Makefile | 4 +- kernel/bpf/inode.c | 387 +++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 30 +++- 6 files changed, 433 insertions(+), 41 deletions(-) create mode 100644 kernel/bpf/inode.c
include/linux/bpf.h +7 -0
@@ -167,11 +167,18 @@ struct bpf_prog *bpf_prog_get(u32 ufd);void bpf_prog_put (struct bpf_prog *prog);void bpf_prog_put_rcu (struct bpf_prog *prog); +struct bpf_map *bpf_map_get (u32 ufd);struct bpf_map *__bpf_map_get (struct fd f);void bpf_map_put (struct bpf_map *map);extern int sysctl_unprivileged_bpf_disabled; +int bpf_map_new_fd (struct bpf_map *map); +int bpf_prog_new_fd (struct bpf_prog *prog); + +int bpf_obj_pin_user (u32 ufd,const char __user *pathname); +int bpf_obj_get_user (const char __user *pathname); +/* verify correctness of eBPF program */ int bpf_check (struct bpf_prog **fp,union bpf_attr *attr);#else
include/uapi/linux/bpf.h +8 -37
64 lines not on this page · raw message
include/uapi/linux/magic.h +1 -0
@@ -75,5 +75,6 @@#define ANON_INODE_FS_MAGIC 0x09041934#define BTRFS_TEST_MAGIC 0x73727279#define NSFS_MAGIC 0x6e736673 +#define BPF_FS_MAGIC 0xcafe4a11#endif /* __LINUX_MAGIC_H__ */
kernel/bpf/Makefile +3 -1
@@ -1,2 +1,4 @@ obj-y := core.o -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o hashtab.o arraymap.o helpers.o + +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o +obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o
kernel/bpf/inode.c new file +387 -0
387 lines not on this page · raw message
kernel/bpf/syscall.c +27 -3
@@ -111,7 +111,7 @@ static const struct file_operations bpf_map_fops = { .release = bpf_map_release, }; -static int bpf_map_new_fd (struct bpf_map *map) +int bpf_map_new_fd (struct bpf_map *map) {return anon_inode_getfd ("bpf-map" , &bpf_map_fops, map,O_RDWR |O_CLOEXEC );
@@ -174,7 +174,7 @@ struct bpf_map *__bpf_map_get(struct fd f)return f.file->private_data; } -static struct bpf_map *bpf_map_get (u32 ufd) +struct bpf_map *bpf_map_get (u32 ufd) {struct fd f =fdget (ufd);struct bpf_map *map;
@@ -548,7 +548,7 @@ static const struct file_operations bpf_prog_fops = { .release = bpf_prog_release, }; -static int bpf_prog_new_fd (struct bpf_prog *prog) +int bpf_prog_new_fd (struct bpf_prog *prog) {return anon_inode_getfd ("bpf-prog" , &bpf_prog_fops, prog,O_RDWR |O_CLOEXEC );
@@ -674,6 +674,24 @@ free_prog_nouncharge:return err; } +#define BPF_OBJ_LAST_FIELD bpf_fd + +static int bpf_obj_pin (const union bpf_attr *attr) +{ +if (CHECK_ATTR (BPF_OBJ )) +return -EINVAL ; + +return bpf_obj_pin_user (attr->bpf_fd,u64_to_ptr (attr->pathname)); +} + +static int bpf_obj_get (const union bpf_attr *attr) +{ +if (CHECK_ATTR (BPF_OBJ ) || attr->bpf_fd !=0 ) +return -EINVAL ; + +return bpf_obj_get_user (u64_to_ptr (attr->pathname)); +} + SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) {union bpf_attr attr = {};
@@ -734,6 +752,12 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, sizcase BPF_PROG_LOAD : err =bpf_prog_load (&attr);break ; +case BPF_OBJ_PIN : + err =bpf_obj_pin (&attr); +break ; +case BPF_OBJ_GET : + err =bpf_obj_get (&attr); +break ;default : err = -EINVAL ;break ;
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH v2 5/5 bpf: add sample usages for persistent maps/progs
In reply to Daniel BorkmannThis patch adds a couple of stand-alone examples on how BPF_OBJ_PIN and BPF_OBJ_GET commands can be used.
Example with maps:
# ./fds_example -F /sys/fs/bpf/m -P -m -k 1 -v 42 bpf: map fd:3 (Success) bpf: pin ret:(0,Success) bpf: fd:3 u->(1:42) ret:(0,Success) # ./fds_example -F /sys/fs/bpf/m -G -m -k 1 bpf: get fd:3 (Success) bpf: fd:3 l->(1):42 ret:(0,Success) # ./fds_example -F /sys/fs/bpf/m -G -m -k 1 -v 24 bpf: get fd:3 (Success) bpf: fd:3 u->(1:24) ret:(0,Success) # ./fds_example -F /sys/fs/bpf/m -G -m -k 1 bpf: get fd:3 (Success) bpf: fd:3 l->(1):24 ret:(0,Success)
# ./fds_example -F /sys/fs/bpf/m2 -P -m bpf: map fd:3 (Success) bpf: pin ret:(0,Success) # ./fds_example -F /sys/fs/bpf/m2 -G -m -k 1 bpf: get fd:3 (Success) bpf: fd:3 l->(1):0 ret:(0,Success) # ./fds_example -F /sys/fs/bpf/m2 -G -m bpf: get fd:3 (Success)
Example with progs:
# ./fds_example -F /sys/fs/bpf/p -P -p bpf: prog fd:3 (Success) bpf: pin ret:(0,Success) bpf sock:4 <- fd:3 attached ret:(0,Success) # ./fds_example -F /sys/fs/bpf/p -G -p bpf: get fd:3 (Success) bpf: sock:4 <- fd:3 attached ret:(0,Success)
# ./fds_example -F /sys/fs/bpf/p2 -P -p -o ./sockex1_kern.o bpf: prog fd:5 (Success) bpf: pin ret:(0,Success) bpf: sock:3 <- fd:5 attached ret:(0,Success) # ./fds_example -F /sys/fs/bpf/p2 -G -p bpf: get fd:3 (Success) bpf: sock:4 <- fd:3 attached ret:(0,Success)
--- samples/bpf/Makefile | 3 + samples/bpf/fds_example.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++ samples/bpf/libbpf.c | 19 +++++ samples/bpf/libbpf.h | 3 + 4 files changed, 208 insertions(+) create mode 100644 samples/bpf/fds_example.c
samples/bpf/Makefile +3 -0
@@ -4,6 +4,7 @@ obj- := dummy.o # List of programs to build hostprogs-y := test_verifier test_maps hostprogs-y += sock_example +hostprogs-y += fds_example hostprogs-y += sockex1 hostprogs-y += sockex2 hostprogs-y += sockex3
@@ -19,6 +20,7 @@ hostprogs-y += lathist test_verifier-objs := test_verifier.o libbpf.o test_maps-objs := test_maps.o libbpf.o sock_example-objs := sock_example.o libbpf.o +fds_example-objs := bpf_load.o libbpf.o fds_example.o sockex1-objs := bpf_load.o libbpf.o sockex1_user.o sockex2-objs := bpf_load.o libbpf.o sockex2_user.o sockex3-objs := bpf_load.o libbpf.o sockex3_user.o
@@ -49,6 +51,7 @@ always += lathist_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable +HOSTLOADLIBES_fds_example += -lelf HOSTLOADLIBES_sockex1 += -lelf HOSTLOADLIBES_sockex2 += -lelf HOSTLOADLIBES_sockex3 += -lelf
samples/bpf/fds_example.c new file +183 -0
183 lines not on this page · raw message
samples/bpf/libbpf.c +19 -0
@@ -103,6 +103,25 @@ int bpf_prog_load(enum bpf_prog_type prog_type,return syscall (__NR_bpf,BPF_PROG_LOAD , &attr,sizeof (attr)); } +int bpf_obj_pin (int fd,const char *pathname) +{ +union bpf_attr attr = { + .pathname =ptr_to_u64 ((void *)pathname), + .bpf_fd = fd, + }; + +return syscall (__NR_bpf,BPF_OBJ_PIN , &attr,sizeof (attr)); +} + +int bpf_obj_get (const char *pathname) +{ +union bpf_attr attr = { + .pathname =ptr_to_u64 ((void *)pathname), + }; + +return syscall (__NR_bpf,BPF_OBJ_GET , &attr,sizeof (attr)); +} +int open_raw_sock (const char *name) {struct sockaddr_ll sll;
samples/bpf/libbpf.h +3 -0
@@ -15,6 +15,9 @@ int bpf_prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, int insn_len, const char *license, int kern_version); +int bpf_obj_pin (int fd,const char *pathname); +int bpf_obj_get (const char *pathname); +#define LOG_BUF_SIZE 65536extern char bpf_log_buf[LOG_BUF_SIZE ];
Signature · 1 line
1.9.3
David Miller
maintainerFrom: Daniel Borkmann <[email protected]>
Date: Thu, 29 Oct 2015 14:58:05 +0100
Quoted 10 lines
This set adds support for persistent maps/progs. Please see individual patches for further details. A man-page update to bpf(2) will be sent later on, also a iproute2 patch for support in tc.
Thanks!
v1 -> v2:
- Reworked most of patch 4 and 5
- Rebased to latest net-next
I'm going to let this sit for a little bit of review time since this facility seems to be generating at least a little bit of controversy :-)
David Miller
maintainerFrom: Daniel Borkmann <[email protected]>
Date: Thu, 29 Oct 2015 14:58:05 +0100
Quoted 4 lines
This set adds support for persistent maps/progs. Please see individual patches for further details. A man-page update to bpf(2) will be sent later on, also a iproute2 patch for support in tc.
...
Quoted 3 lines
v1 -> v2:
- Reworked most of patch 4 and 5
- Rebased to latest net-next
Series applied, thanks.