From: Kui-Feng Lee <thinker.li@xxxxxxxxx> Provide registration functions to add/remove struct_ops types. Currently, it does linear search to find a struct_ops type. It should be fine for now since we don't have many struct_ops types. Signed-off-by: Kui-Feng Lee <thinker.li@xxxxxxxxx> --- include/linux/bpf.h | 13 ++++ include/linux/btf.h | 1 + kernel/bpf/bpf_struct_ops.c | 115 ++++++++++++++++++++++++++++++++++-- kernel/bpf/btf.c | 2 +- 4 files changed, 126 insertions(+), 5 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 024e8b28c34b..10f98f0ddc77 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1561,6 +1561,8 @@ struct btf_member; * @init: A callback that is invoked a single time, and before any other * callback, to initialize the structure. A nonzero return value means * the subsystem could not be initialized. + * @uninit: A callback that is invoked after any other + * callback to release resources used by the subsystem. * @check_member: When defined, a callback invoked by the verifier to allow * the subsystem to determine if an entry in the struct_ops map * is valid. A nonzero return value means that the map is @@ -1601,6 +1603,7 @@ struct btf_member; struct bpf_struct_ops { const struct bpf_verifier_ops *verifier_ops; int (*init)(struct btf *btf); + int (*uninit)(void); int (*check_member)(const struct btf_type *t, const struct btf_member *member, const struct bpf_prog *prog); @@ -1619,6 +1622,11 @@ struct bpf_struct_ops { u32 value_id; }; +struct bpf_struct_ops_mod { + struct module *owner; + struct bpf_struct_ops *st_ops; +}; + #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL) #define BPF_MODULE_OWNER ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA)) const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id); @@ -3183,4 +3191,9 @@ static inline gfp_t bpf_memcg_flags(gfp_t flags) return flags; } +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES +int register_bpf_struct_ops(struct bpf_struct_ops_mod *mod); +int unregister_bpf_struct_ops(struct bpf_struct_ops_mod *mod); +#endif + #endif /* _LINUX_BPF_H */ diff --git a/include/linux/btf.h b/include/linux/btf.h index 928113a80a95..d6ed3d99ba41 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -200,6 +200,7 @@ u32 btf_obj_id(const struct btf *btf); bool btf_is_kernel(const struct btf *btf); bool btf_is_module(const struct btf *btf); struct module *btf_try_get_module(const struct btf *btf); +struct btf *btf_get_module_btf(const struct module *module); u32 btf_nr_types(const struct btf *btf); bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, const struct btf_member *m, diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index 1662875e0ebe..9be6e07ccba5 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -92,12 +92,15 @@ enum { __NR_BPF_STRUCT_OPS_TYPE, }; -static struct bpf_struct_ops * const bpf_struct_ops[] = { +static struct bpf_struct_ops *bpf_struct_ops_static[] = { #define BPF_STRUCT_OPS_TYPE(_name) \ [BPF_STRUCT_OPS_TYPE_##_name] = &bpf_##_name, #include "bpf_struct_ops_types.h" #undef BPF_STRUCT_OPS_TYPE }; +static struct bpf_struct_ops **bpf_struct_ops; +static int bpf_struct_ops_num; +static int bpf_struct_ops_capacity; const struct bpf_verifier_ops bpf_struct_ops_verifier_ops = { }; @@ -212,12 +215,116 @@ void bpf_struct_ops_init(struct btf *btf, struct bpf_verifier_log *log) } module_type = btf_type_by_id(btf, module_id); - for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) { + bpf_struct_ops_num = ARRAY_SIZE(bpf_struct_ops_static); + bpf_struct_ops_capacity = bpf_struct_ops_num; + bpf_struct_ops = bpf_struct_ops_static; + + for (i = 0; i < bpf_struct_ops_num; i++) { st_ops = bpf_struct_ops[i]; bpf_struct_ops_init_one(st_ops, btf, log); } } +static int add_struct_ops(struct bpf_struct_ops *st_ops) +{ + struct bpf_struct_ops **new_ops; + int i; + + for (i = 0; i < bpf_struct_ops_num; i++) { + if (bpf_struct_ops[i] == st_ops) + return -EEXIST; + if (strcmp(bpf_struct_ops[i]->name, st_ops->name) == 0) + return -EEXIST; + } + + if (bpf_struct_ops_num == bpf_struct_ops_capacity) { + if (bpf_struct_ops == bpf_struct_ops_static) { + new_ops = kmalloc_array(((bpf_struct_ops_capacity + 0x7) & ~0x7) * 2, + sizeof(*new_ops), + GFP_KERNEL); + if (!new_ops) + return -ENOMEM; + memcpy(new_ops, bpf_struct_ops, + sizeof(*new_ops) * bpf_struct_ops_num); + } else { + new_ops = krealloc_array(bpf_struct_ops, + bpf_struct_ops_capacity * 2, + sizeof(*new_ops), + GFP_KERNEL); + if (!new_ops) + return -ENOMEM; + } + bpf_struct_ops = new_ops; + bpf_struct_ops_capacity *= 2; + } + + bpf_struct_ops[bpf_struct_ops_num++] = st_ops; + return 0; +} + +static int remove_struct_ops(struct bpf_struct_ops *st_ops) +{ + int i; + + for (i = 0; i < bpf_struct_ops_num; i++) { + if (bpf_struct_ops[i] == st_ops) { + bpf_struct_ops_num--; + bpf_struct_ops[i] = bpf_struct_ops[bpf_struct_ops_num]; + return 0; + } + } + + return -ENOENT; +} + +int register_bpf_struct_ops(struct bpf_struct_ops_mod *mod) +{ + struct bpf_struct_ops *st_ops = mod->st_ops; + struct bpf_verifier_log *log; + struct btf *btf; + int err; + + if (mod->st_ops == NULL || + mod->owner == NULL) + return -EINVAL; + + log = kzalloc(sizeof(*log), GFP_KERNEL | __GFP_NOWARN); + if (!log) { + err = -ENOMEM; + goto errout; + } + + log->level = BPF_LOG_KERNEL; + + btf = btf_get_module_btf(mod->owner); + if (!btf) { + err = -EINVAL; + goto errout; + } + + bpf_struct_ops_init_one(st_ops, btf, log); + err = add_struct_ops(st_ops); + +errout: + kfree(log); + + return err; +} +EXPORT_SYMBOL(register_bpf_struct_ops); + +int unregister_bpf_struct_ops(struct bpf_struct_ops_mod *mod) +{ + struct bpf_struct_ops *st_ops = mod->st_ops; + int err; + + err = remove_struct_ops(st_ops); + if (!err && st_ops->uninit) + err = st_ops->uninit(); + + return err; +} +EXPORT_SYMBOL(unregister_bpf_struct_ops); + extern struct btf *btf_vmlinux; static const struct bpf_struct_ops * @@ -228,7 +335,7 @@ bpf_struct_ops_find_value(u32 value_id) if (!value_id || !btf_vmlinux) return NULL; - for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) { + for (i = 0; i < bpf_struct_ops_num; i++) { if (bpf_struct_ops[i]->value_id == value_id) return bpf_struct_ops[i]; } @@ -243,7 +350,7 @@ const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id) if (!type_id || !btf_vmlinux) return NULL; - for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) { + for (i = 0; i < bpf_struct_ops_num; i++) { if (bpf_struct_ops[i]->type_id == type_id) return bpf_struct_ops[i]; } diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1095bbe29859..55d76d85c6ec 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -7498,7 +7498,7 @@ struct module *btf_try_get_module(const struct btf *btf) /* Returns struct btf corresponding to the struct module. * This function can return NULL or ERR_PTR. */ -static struct btf *btf_get_module_btf(const struct module *module) +struct btf *btf_get_module_btf(const struct module *module) { #ifdef CONFIG_DEBUG_INFO_BTF_MODULES struct btf_module *btf_mod, *tmp; -- 2.34.1