This adds support for calling helper functions in eBPF applications that have been declared in a kernel module. The corresponding verifier changes for module helpers will be added in a later patch. Module helpers are useful as: - They support more argument and return types when compared to module kfunc. - This adds a way to have helper functions that would be too specialized for a specific usecase to merge upstream, but are functions that can have a constant API and can be maintained in-kernel modules. - The number of in-kernel helpers have grown to a large number (187 at the time of writing this commit). Having module helper functions could possibly reduce the number of in-kernel helper functions growing in the future and maintained upstream. When the kernel module registers the helper, the module owner, BTF id set of the function and function proto is stored as part of a btf_mod_helper entry in a btf_mod_helper_list which is part of struct btf. This entry can be removed in the unregister function while exiting the module, and can be used by the bpf verifier to check the helper call and get function proto. Signed-off-by: Usama Arif <usama.arif@xxxxxxxxxxxxx> --- include/linux/btf.h | 44 +++++++++++++++++++++++ kernel/bpf/btf.c | 88 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/include/linux/btf.h b/include/linux/btf.h index b12cfe3b12bb..c3a814404112 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -40,6 +40,18 @@ struct btf_kfunc_id_set { }; }; +struct btf_mod_helper { + struct list_head list; + struct module *owner; + struct btf_id_set *set; + struct bpf_func_proto *func_proto; +}; + +struct btf_mod_helper_list { + struct list_head list; + struct mutex mutex; +}; + extern const struct file_operations btf_fops; void btf_get(struct btf *btf); @@ -359,4 +371,36 @@ static inline int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, } #endif +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES +int register_mod_helper(struct btf_mod_helper *mod_helper); +int unregister_mod_helper(struct btf_mod_helper *mod_helper); +const struct bpf_func_proto *get_mod_helper_proto(const struct btf *btf, + const u32 kfunc_btf_id); + +#define DEFINE_MOD_HELPER(mod_helper, owner, helper_func, func_proto) \ +BTF_SET_START(helper_func##__id_set) \ +BTF_ID(func, helper_func) \ +BTF_SET_END(helper_func##__id_set) \ +struct btf_mod_helper mod_helper = { \ + LIST_HEAD_INIT(mod_helper.list), \ + (owner), \ + (&(helper_func##__id_set)), \ + (&(func_proto)) \ +} +#else +int register_mod_helper(struct btf_mod_helper *mod_helper) +{ + return -EPERM; +} +int unregister_mod_helper(struct btf_mod_helper *mod_helper) +{ + return -EPERM; +} +const struct bpf_func_proto *get_mod_helper_proto(const struct btf *btf, + const u32 kfunc_btf_id) +{ + return NULL; +} +#endif + #endif diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 57f5fd5af2f9..f9aa6ba85f3f 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -228,6 +228,7 @@ struct btf { u32 id; struct rcu_head rcu; struct btf_kfunc_set_tab *kfunc_set_tab; + struct btf_mod_helper_list *mod_helper_list; /* split BTF support */ struct btf *base_btf; @@ -6752,6 +6753,93 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, } EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set); +#ifdef CONFIG_DEBUG_INFO_BTF_MODULES +int register_mod_helper(struct btf_mod_helper *mod_helper) +{ + struct btf_mod_helper *s; + struct btf *btf; + struct btf_mod_helper_list *mod_helper_list; + + btf = btf_get_module_btf(mod_helper->owner); + if (!btf_is_module(btf)) { + pr_err("%s can only be called from kernel module", __func__); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(btf)) + return btf ? PTR_ERR(btf) : -ENOENT; + + mod_helper_list = btf->mod_helper_list; + if (!mod_helper_list) { + mod_helper_list = kzalloc(sizeof(*mod_helper_list), GFP_KERNEL | __GFP_NOWARN); + if (!mod_helper_list) + return -ENOMEM; + INIT_LIST_HEAD(&mod_helper_list->list); + mutex_init(&mod_helper_list->mutex); + btf->mod_helper_list = mod_helper_list; + } + + // Check if btf id is already registered + mutex_lock(&mod_helper_list->mutex); + list_for_each_entry(s, &mod_helper_list->list, list) { + if (mod_helper->set->ids[0] == s->set->ids[0]) { + pr_warn("Dynamic helper %u is already registered\n", s->set->ids[0]); + mutex_unlock(&mod_helper_list->mutex); + return -EINVAL; + } + } + list_add(&mod_helper->list, &mod_helper_list->list); + mutex_unlock(&mod_helper_list->mutex); + btf_put(btf); + + return 0; +} +EXPORT_SYMBOL_GPL(register_mod_helper); + +int unregister_mod_helper(struct btf_mod_helper *mod_helper) +{ + struct btf *btf; + struct btf_mod_helper_list *mod_helper_list; + + btf = btf_get_module_btf(mod_helper->owner); + if (!btf_is_module(btf)) { + pr_err("%s can only be called from kernel module", __func__); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(btf)) + return btf ? PTR_ERR(btf) : -ENOENT; + + mod_helper_list = btf->mod_helper_list; + mutex_lock(&mod_helper_list->mutex); + list_del(&mod_helper->list); + mutex_unlock(&mod_helper_list->mutex); + btf_put(btf); + + return 0; +} +EXPORT_SYMBOL_GPL(unregister_mod_helper); +const struct bpf_func_proto *get_mod_helper_proto(const struct btf *btf, const u32 kfunc_btf_id) +{ + struct btf_mod_helper *s; + struct btf_mod_helper_list *mod_helper_list; + + mod_helper_list = btf->mod_helper_list; + if (!mod_helper_list) + return NULL; + + mutex_lock(&mod_helper_list->mutex); + list_for_each_entry(s, &mod_helper_list->list, list) { + if (s->set->ids[0] == kfunc_btf_id) { + mutex_unlock(&mod_helper_list->mutex); + return s->func_proto; + } + } + mutex_unlock(&mod_helper_list->mutex); + return NULL; +} +#endif /* CONFIG_DEBUG_INFO_BTF_MODULES */ + int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf, __u32 targ_id) { -- 2.25.1