Skip to content
Permalink
Browse files
bpf: btf: Introduce infrastructure for module helpers
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@bytedance.com>
  • Loading branch information
uarif1 authored and intel-lab-lkp committed Jan 21, 2022
1 parent 820e6e2 commit ca60b90025819a8a03818e86e2105bd15576d134
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
@@ -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
@@ -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)
{

0 comments on commit ca60b90

Please sign in to comment.