From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: Usama Arif <usama.arif@bytedance.com>
Cc: bpf@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net,
andrii.nakryiko@gmail.com, fam.zheng@bytedance.com,
cong.wang@bytedance.com, song@kernel.org
Subject: Re: [RFC bpf-next 1/3] bpf: btf: Introduce infrastructure for module helpers
Date: Sat, 22 Jan 2022 08:53:39 +0530 [thread overview]
Message-ID: <20220122032053.pb7lk5wvrfg2bo75@thp> (raw)
In-Reply-To: <20220121193956.198120-2-usama.arif@bytedance.com>
On Sat, Jan 22, 2022 at 01:09:54AM IST, Usama Arif wrote:
> 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>
> ---
> 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)) \
> +}
This macro needs to be outside the ifdef, otherwise when
CONFIG_DEBUG_INFO_BTF_MODULES is not set, code will fail to compile. Also, I
would use static and const on the variable, so that compiler can optimize it out
when the config option is disabled.
> +#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;
> +}
In the else block, these need to be static inline functions, otherwise you'll
get a warning and link time error because the same symbol will be present in
multiple objects.
> +#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;
What will free this struct when btf goes away? I don't see any cleanup code in
btf_free.
>
> /* 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)) {
You need to check for IS_ERR_OR_NULL before calling btf_is_module.
Also, this would fail if the module is compiled as built-in, because
mod_helper->owner will be NULL, and btf_get_module_btf will return btf_vmlinux.
> + 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;
Here, you are not doing btf_put for module btf. Also, you'll have to guard the
btf_put with `if (btf_is_module(btf))` because reference is not raised for
btf_vmlinux.
> + 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
No C++ style comments.
> + 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;
Need to free mod_helper_list and conditionally btf_put before returning.
> + }
> + }
> + 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)) {
Same error as above, need the IS_ERR_OR_NULL check to be before this.
> + 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) {
If there is only one BTF ID, I think you can just use BTF_ID_LIST instead.
> + 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
>
next prev parent reply other threads:[~2022-01-22 3:23 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-01-21 19:39 [RFC bpf-next 0/3] bpf: Introduce module helper functions Usama Arif
2022-01-21 19:39 ` [RFC bpf-next 1/3] bpf: btf: Introduce infrastructure for module helpers Usama Arif
2022-01-22 3:23 ` Kumar Kartikeya Dwivedi [this message]
2022-01-21 19:39 ` [RFC bpf-next 2/3] bpf: add support for module helpers in verifier Usama Arif
2022-01-22 3:31 ` Kumar Kartikeya Dwivedi
2022-01-22 3:56 ` Kumar Kartikeya Dwivedi
2022-01-24 16:23 ` Usama Arif
2022-01-21 19:39 ` [RFC bpf-next 3/3] selftests/bpf: add test for module helper Usama Arif
2022-01-21 22:48 ` [RFC bpf-next 0/3] bpf: Introduce module helper functions Alexei Starovoitov
2022-01-22 4:04 ` Kumar Kartikeya Dwivedi
2022-01-24 16:33 ` [External] " Usama Arif
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220122032053.pb7lk5wvrfg2bo75@thp \
--to=memxor@gmail.com \
--cc=andrii.nakryiko@gmail.com \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=cong.wang@bytedance.com \
--cc=daniel@iogearbox.net \
--cc=fam.zheng@bytedance.com \
--cc=song@kernel.org \
--cc=usama.arif@bytedance.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox