The current registration method consisting in setting the bpf_preload_ops global variable is not suitable for preloading multiple eBPF programs, as each eBPF program would overwrite the global variable with its own method. Implement a new registration method in two steps. First, introduce bpf_init_preload_list() to populate at kernel initialization time the new linked list with an element for each of the desired eBPF programs to preload. Second, introduce bpf_preload_set_ops() to allow an eBPF program to set its preload method in the corresponding item of the linked list. The condition for a successful registration is that the item in the linked list should already exist. Return a boolean value to report if registration was successful or not. Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> --- include/linux/bpf_preload.h | 7 +++ kernel/bpf/inode.c | 107 +++++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf_preload.h b/include/linux/bpf_preload.h index e604933b3daa..bdbe75c22fcb 100644 --- a/include/linux/bpf_preload.h +++ b/include/linux/bpf_preload.h @@ -19,12 +19,19 @@ extern struct bpf_preload_ops *bpf_preload_ops; int bpf_obj_do_pin_kernel(struct dentry *parent, const char *name, void *raw, enum bpf_type type); +bool bpf_preload_set_ops(const char *name, struct module *owner, + struct bpf_preload_ops *ops); #else static inline int bpf_obj_do_pin_kernel(struct dentry *parent, const char *name, void *raw, enum bpf_type type) { return -EOPNOTSUPP; } + +static inline bool bpf_preload_set_ops(const char *name, struct module *owner, + struct bpf_preload_ops *ops) +{ +} #endif /*CONFIG_BPF_SYSCALL*/ #endif diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 0a6e83d32360..440ea517cc29 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -22,6 +22,8 @@ #include <linux/bpf_trace.h> #include <linux/bpf_preload.h> +static char *bpf_preload_list_str; + static void *bpf_any_get(void *raw, enum bpf_type type) { switch (type) { @@ -855,6 +857,100 @@ static struct file_system_type bpf_fs_type = { .kill_sb = kill_litter_super, }; +static struct bpf_preload_ops_item * +bpf_preload_list_lookup_entry(const char *obj_name) +{ + struct bpf_preload_ops_item *cur; + + list_for_each_entry(cur, &preload_list, list) + if (!strcmp(obj_name, cur->obj_name)) + return cur; + + return NULL; +} + +static int bpf_preload_list_add_entry(const char *obj_name, + struct bpf_preload_ops *ops) +{ + struct bpf_preload_ops_item *new; + + if (!*obj_name) + return 0; + + new = kzalloc(sizeof(*new), GFP_NOFS); + if (!new) + return -ENOMEM; + + new->obj_name = kstrdup(obj_name, GFP_NOFS); + if (!new->obj_name) { + kfree(new); + return -ENOMEM; + } + + new->ops = ops; + + list_add(&new->list, &preload_list); + return 0; +} + +bool bpf_preload_set_ops(const char *obj_name, struct module *owner, + struct bpf_preload_ops *ops) +{ + struct bpf_preload_ops_item *found_item; + bool set = false; + + mutex_lock(&bpf_preload_lock); + + found_item = bpf_preload_list_lookup_entry(obj_name); + if (found_item) { + if (!found_item->ops || + (found_item->ops && found_item->ops->owner == owner)) { + found_item->ops = ops; + set = true; + } + } + + mutex_unlock(&bpf_preload_lock); + return set; +} +EXPORT_SYMBOL_GPL(bpf_preload_set_ops); + +static int __init bpf_init_preload_list(void) +{ + char *str_ptr = bpf_preload_list_str, *str_end; + struct bpf_preload_ops_item *cur, *tmp; + char obj_name[NAME_MAX + 1]; + int ret; + + while (str_ptr && *str_ptr) { + str_end = strchrnul(str_ptr, ','); + + snprintf(obj_name, sizeof(obj_name), "%.*s", + (int)(str_end - str_ptr), str_ptr); + + if (!bpf_preload_list_lookup_entry(obj_name)) { + ret = bpf_preload_list_add_entry(obj_name, NULL); + if (ret) + goto out; + } + + if (!*str_end) + break; + + str_ptr = str_end + 1; + } + + return 0; +out: + list_for_each_entry_safe(cur, tmp, &preload_list, list) { + list_del(&cur->list); + kfree(cur->obj_name); + kfree(cur); + } + + return ret; +} + static int __init bpf_init(void) { int ret; @@ -864,8 +960,17 @@ static int __init bpf_init(void) return ret; ret = register_filesystem(&bpf_fs_type); - if (ret) + if (ret) { sysfs_remove_mount_point(fs_kobj, "bpf"); + return ret; + } + + ret = bpf_init_preload_list(); + if (ret) { + unregister_filesystem(&bpf_fs_type); + sysfs_remove_mount_point(fs_kobj, "bpf"); + return ret; + } return ret; } -- 2.32.0