Skip to content

Commit d71fa5c

Browse files
Alexei Starovoitovborkmann
authored andcommitted
bpf: Add kernel module with user mode driver that populates bpffs.
Add kernel module with user mode driver that populates bpffs with BPF iterators. $ mount bpffs /my/bpffs/ -t bpf $ ls -la /my/bpffs/ total 4 drwxrwxrwt 2 root root 0 Jul 2 00:27 . drwxr-xr-x 19 root root 4096 Jul 2 00:09 .. -rw------- 1 root root 0 Jul 2 00:27 maps.debug -rw------- 1 root root 0 Jul 2 00:27 progs.debug The user mode driver will load BPF Type Formats, create BPF maps, populate BPF maps, load two BPF programs, attach them to BPF iterators, and finally send two bpf_link IDs back to the kernel. The kernel will pin two bpf_links into newly mounted bpffs instance under names "progs.debug" and "maps.debug". These two files become human readable. $ cat /my/bpffs/progs.debug id name attached 11 dump_bpf_map bpf_iter_bpf_map 12 dump_bpf_prog bpf_iter_bpf_prog 27 test_pkt_access 32 test_main test_pkt_access test_pkt_access 33 test_subprog1 test_pkt_access_subprog1 test_pkt_access 34 test_subprog2 test_pkt_access_subprog2 test_pkt_access 35 test_subprog3 test_pkt_access_subprog3 test_pkt_access 36 new_get_skb_len get_skb_len test_pkt_access 37 new_get_skb_ifindex get_skb_ifindex test_pkt_access 38 new_get_constant get_constant test_pkt_access The BPF program dump_bpf_prog() in iterators.bpf.c is printing this data about all BPF programs currently loaded in the system. This information is unstable and will change from kernel to kernel as ".debug" suffix conveys. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20200819042759.51280-4-alexei.starovoitov@gmail.com
1 parent f0fdfef commit d71fa5c

File tree

13 files changed

+390
-6
lines changed

13 files changed

+390
-6
lines changed

init/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,8 @@ config BPF_JIT_DEFAULT_ON
17101710
def_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON
17111711
depends on HAVE_EBPF_JIT && BPF_JIT
17121712

1713+
source "kernel/bpf/preload/Kconfig"
1714+
17131715
config USERFAULTFD
17141716
bool "Enable userfaultfd() system call"
17151717
depends on MMU

kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ obj-y = fork.o exec_domain.o panic.o \
1212
notifier.o ksysfs.o cred.o reboot.o \
1313
async.o range.o smpboot.o ucount.o regset.o
1414

15-
obj-$(CONFIG_BPFILTER) += usermode_driver.o
15+
obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
1616
obj-$(CONFIG_MODULES) += kmod.o
1717
obj-$(CONFIG_MULTIUSER) += groups.o
1818

kernel/bpf/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ ifeq ($(CONFIG_BPF_JIT),y)
2929
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
3030
obj-${CONFIG_BPF_LSM} += bpf_lsm.o
3131
endif
32+
obj-$(CONFIG_BPF_PRELOAD) += preload/

kernel/bpf/inode.c

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/filter.h>
2121
#include <linux/bpf.h>
2222
#include <linux/bpf_trace.h>
23+
#include "preload/bpf_preload.h"
2324

2425
enum bpf_type {
2526
BPF_TYPE_UNSPEC = 0,
@@ -369,9 +370,10 @@ static struct dentry *
369370
bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags)
370371
{
371372
/* Dots in names (e.g. "/sys/fs/bpf/foo.bar") are reserved for future
372-
* extensions.
373+
* extensions. That allows popoulate_bpffs() create special files.
373374
*/
374-
if (strchr(dentry->d_name.name, '.'))
375+
if ((dir->i_mode & S_IALLUGO) &&
376+
strchr(dentry->d_name.name, '.'))
375377
return ERR_PTR(-EPERM);
376378

377379
return simple_lookup(dir, dentry, flags);
@@ -409,6 +411,27 @@ static const struct inode_operations bpf_dir_iops = {
409411
.unlink = simple_unlink,
410412
};
411413

414+
/* pin iterator link into bpffs */
415+
static int bpf_iter_link_pin_kernel(struct dentry *parent,
416+
const char *name, struct bpf_link *link)
417+
{
418+
umode_t mode = S_IFREG | S_IRUSR;
419+
struct dentry *dentry;
420+
int ret;
421+
422+
inode_lock(parent->d_inode);
423+
dentry = lookup_one_len(name, parent, strlen(name));
424+
if (IS_ERR(dentry)) {
425+
inode_unlock(parent->d_inode);
426+
return PTR_ERR(dentry);
427+
}
428+
ret = bpf_mkobj_ops(dentry, mode, link, &bpf_link_iops,
429+
&bpf_iter_fops);
430+
dput(dentry);
431+
inode_unlock(parent->d_inode);
432+
return ret;
433+
}
434+
412435
static int bpf_obj_do_pin(const char __user *pathname, void *raw,
413436
enum bpf_type type)
414437
{
@@ -638,6 +661,91 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
638661
return 0;
639662
}
640663

664+
struct bpf_preload_ops *bpf_preload_ops;
665+
EXPORT_SYMBOL_GPL(bpf_preload_ops);
666+
667+
static bool bpf_preload_mod_get(void)
668+
{
669+
/* If bpf_preload.ko wasn't loaded earlier then load it now.
670+
* When bpf_preload is built into vmlinux the module's __init
671+
* function will populate it.
672+
*/
673+
if (!bpf_preload_ops) {
674+
request_module("bpf_preload");
675+
if (!bpf_preload_ops)
676+
return false;
677+
}
678+
/* And grab the reference, so the module doesn't disappear while the
679+
* kernel is interacting with the kernel module and its UMD.
680+
*/
681+
if (!try_module_get(bpf_preload_ops->owner)) {
682+
pr_err("bpf_preload module get failed.\n");
683+
return false;
684+
}
685+
return true;
686+
}
687+
688+
static void bpf_preload_mod_put(void)
689+
{
690+
if (bpf_preload_ops)
691+
/* now user can "rmmod bpf_preload" if necessary */
692+
module_put(bpf_preload_ops->owner);
693+
}
694+
695+
static DEFINE_MUTEX(bpf_preload_lock);
696+
697+
static int populate_bpffs(struct dentry *parent)
698+
{
699+
struct bpf_preload_info objs[BPF_PRELOAD_LINKS] = {};
700+
struct bpf_link *links[BPF_PRELOAD_LINKS] = {};
701+
int err = 0, i;
702+
703+
/* grab the mutex to make sure the kernel interactions with bpf_preload
704+
* UMD are serialized
705+
*/
706+
mutex_lock(&bpf_preload_lock);
707+
708+
/* if bpf_preload.ko wasn't built into vmlinux then load it */
709+
if (!bpf_preload_mod_get())
710+
goto out;
711+
712+
if (!bpf_preload_ops->info.tgid) {
713+
/* preload() will start UMD that will load BPF iterator programs */
714+
err = bpf_preload_ops->preload(objs);
715+
if (err)
716+
goto out_put;
717+
for (i = 0; i < BPF_PRELOAD_LINKS; i++) {
718+
links[i] = bpf_link_by_id(objs[i].link_id);
719+
if (IS_ERR(links[i])) {
720+
err = PTR_ERR(links[i]);
721+
goto out_put;
722+
}
723+
}
724+
for (i = 0; i < BPF_PRELOAD_LINKS; i++) {
725+
err = bpf_iter_link_pin_kernel(parent,
726+
objs[i].link_name, links[i]);
727+
if (err)
728+
goto out_put;
729+
/* do not unlink successfully pinned links even
730+
* if later link fails to pin
731+
*/
732+
links[i] = NULL;
733+
}
734+
/* finish() will tell UMD process to exit */
735+
err = bpf_preload_ops->finish();
736+
if (err)
737+
goto out_put;
738+
}
739+
out_put:
740+
bpf_preload_mod_put();
741+
out:
742+
mutex_unlock(&bpf_preload_lock);
743+
for (i = 0; i < BPF_PRELOAD_LINKS && err; i++)
744+
if (!IS_ERR_OR_NULL(links[i]))
745+
bpf_link_put(links[i]);
746+
return err;
747+
}
748+
641749
static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
642750
{
643751
static const struct tree_descr bpf_rfiles[] = { { "" } };
@@ -654,8 +762,8 @@ static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
654762
inode = sb->s_root->d_inode;
655763
inode->i_op = &bpf_dir_iops;
656764
inode->i_mode &= ~S_IALLUGO;
765+
populate_bpffs(sb->s_root);
657766
inode->i_mode |= S_ISVTX | opts->mode;
658-
659767
return 0;
660768
}
661769

@@ -705,6 +813,8 @@ static int __init bpf_init(void)
705813
{
706814
int ret;
707815

816+
mutex_init(&bpf_preload_lock);
817+
708818
ret = sysfs_create_mount_point(fs_kobj, "bpf");
709819
if (ret)
710820
return ret;

kernel/bpf/preload/Kconfig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
config USERMODE_DRIVER
3+
bool
4+
default n
5+
6+
menuconfig BPF_PRELOAD
7+
bool "Preload BPF file system with kernel specific program and map iterators"
8+
depends on BPF
9+
select USERMODE_DRIVER
10+
help
11+
This builds kernel module with several embedded BPF programs that are
12+
pinned into BPF FS mount point as human readable files that are
13+
useful in debugging and introspection of BPF programs and maps.
14+
15+
if BPF_PRELOAD
16+
config BPF_PRELOAD_UMD
17+
tristate "bpf_preload kernel module with user mode driver"
18+
depends on CC_CAN_LINK
19+
depends on m || CC_CAN_LINK_STATIC
20+
default m
21+
help
22+
This builds bpf_preload kernel module with embedded user mode driver.
23+
endif

kernel/bpf/preload/Makefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
LIBBPF_SRCS = $(srctree)/tools/lib/bpf/
4+
LIBBPF_A = $(obj)/libbpf.a
5+
LIBBPF_OUT = $(abspath $(obj))
6+
7+
$(LIBBPF_A):
8+
$(Q)$(MAKE) -C $(LIBBPF_SRCS) OUTPUT=$(LIBBPF_OUT)/ $(LIBBPF_OUT)/libbpf.a
9+
10+
userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi \
11+
-I $(srctree)/tools/lib/ -Wno-unused-result
12+
13+
userprogs := bpf_preload_umd
14+
15+
bpf_preload_umd-objs := iterators/iterators.o
16+
bpf_preload_umd-userldlibs := $(LIBBPF_A) -lelf -lz
17+
18+
$(obj)/bpf_preload_umd: $(LIBBPF_A)
19+
20+
$(obj)/bpf_preload_umd_blob.o: $(obj)/bpf_preload_umd
21+
22+
obj-$(CONFIG_BPF_PRELOAD_UMD) += bpf_preload.o
23+
bpf_preload-objs += bpf_preload_kern.o bpf_preload_umd_blob.o

kernel/bpf/preload/bpf_preload.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _BPF_PRELOAD_H
3+
#define _BPF_PRELOAD_H
4+
5+
#include <linux/usermode_driver.h>
6+
#include "iterators/bpf_preload_common.h"
7+
8+
struct bpf_preload_ops {
9+
struct umd_info info;
10+
int (*preload)(struct bpf_preload_info *);
11+
int (*finish)(void);
12+
struct module *owner;
13+
};
14+
extern struct bpf_preload_ops *bpf_preload_ops;
15+
#define BPF_PRELOAD_LINKS 2
16+
#endif
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3+
#include <linux/init.h>
4+
#include <linux/module.h>
5+
#include <linux/pid.h>
6+
#include <linux/fs.h>
7+
#include <linux/sched/signal.h>
8+
#include "bpf_preload.h"
9+
10+
extern char bpf_preload_umd_start;
11+
extern char bpf_preload_umd_end;
12+
13+
static int preload(struct bpf_preload_info *obj);
14+
static int finish(void);
15+
16+
static struct bpf_preload_ops umd_ops = {
17+
.info.driver_name = "bpf_preload",
18+
.preload = preload,
19+
.finish = finish,
20+
.owner = THIS_MODULE,
21+
};
22+
23+
static int preload(struct bpf_preload_info *obj)
24+
{
25+
int magic = BPF_PRELOAD_START;
26+
loff_t pos = 0;
27+
int i, err;
28+
ssize_t n;
29+
30+
err = fork_usermode_driver(&umd_ops.info);
31+
if (err)
32+
return err;
33+
34+
/* send the start magic to let UMD proceed with loading BPF progs */
35+
n = kernel_write(umd_ops.info.pipe_to_umh,
36+
&magic, sizeof(magic), &pos);
37+
if (n != sizeof(magic))
38+
return -EPIPE;
39+
40+
/* receive bpf_link IDs and names from UMD */
41+
pos = 0;
42+
for (i = 0; i < BPF_PRELOAD_LINKS; i++) {
43+
n = kernel_read(umd_ops.info.pipe_from_umh,
44+
&obj[i], sizeof(*obj), &pos);
45+
if (n != sizeof(*obj))
46+
return -EPIPE;
47+
}
48+
return 0;
49+
}
50+
51+
static int finish(void)
52+
{
53+
int magic = BPF_PRELOAD_END;
54+
struct pid *tgid;
55+
loff_t pos = 0;
56+
ssize_t n;
57+
58+
/* send the last magic to UMD. It will do a normal exit. */
59+
n = kernel_write(umd_ops.info.pipe_to_umh,
60+
&magic, sizeof(magic), &pos);
61+
if (n != sizeof(magic))
62+
return -EPIPE;
63+
tgid = umd_ops.info.tgid;
64+
wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
65+
umd_ops.info.tgid = NULL;
66+
return 0;
67+
}
68+
69+
static int __init load_umd(void)
70+
{
71+
int err;
72+
73+
err = umd_load_blob(&umd_ops.info, &bpf_preload_umd_start,
74+
&bpf_preload_umd_end - &bpf_preload_umd_start);
75+
if (err)
76+
return err;
77+
bpf_preload_ops = &umd_ops;
78+
return err;
79+
}
80+
81+
static void __exit fini_umd(void)
82+
{
83+
bpf_preload_ops = NULL;
84+
/* kill UMD in case it's still there due to earlier error */
85+
kill_pid(umd_ops.info.tgid, SIGKILL, 1);
86+
umd_ops.info.tgid = NULL;
87+
umd_unload_blob(&umd_ops.info);
88+
}
89+
late_initcall(load_umd);
90+
module_exit(fini_umd);
91+
MODULE_LICENSE("GPL");
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
.section .init.rodata, "a"
3+
.global bpf_preload_umd_start
4+
bpf_preload_umd_start:
5+
.incbin "kernel/bpf/preload/bpf_preload_umd"
6+
.global bpf_preload_umd_end
7+
bpf_preload_umd_end:
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _BPF_PRELOAD_COMMON_H
3+
#define _BPF_PRELOAD_COMMON_H
4+
5+
#define BPF_PRELOAD_START 0x5555
6+
#define BPF_PRELOAD_END 0xAAAA
7+
8+
struct bpf_preload_info {
9+
char link_name[16];
10+
int link_id;
11+
};
12+
13+
#endif

0 commit comments

Comments
 (0)