public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
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
>

  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