2024-05-13 18:39:23

by Daniel Xu

[permalink] [raw]
Subject: [PATCH bpf-next v3 0/2] bpf: bpftool: Support dumping kfunc prototypes from BTF

This patchset enables both detecting as well as dumping compilable
prototypes for kfuncs.

Users will be able to look at BTF inside vmlinux (or modules) and check
if the kfunc they want is available.

For developer convenience, we also support dumping kfunc prototypes from
bpftool.

=== Changelog ===
From v2:
* Update Makefile.btf with pahole flag
* More error checking
* Output formatting changes
* Drop already-merged commit

From v1:
* Add __weak annotation
* Use btf_dump for kfunc prototypes
* Update kernel bpf_rdonly_cast() signature

Daniel Xu (2):
kbuild: bpf: Tell pahole to DECL_TAG kfuncs
bpftool: Support dumping kfunc prototypes from BTF

scripts/Makefile.btf | 2 +-
tools/bpf/bpftool/btf.c | 54 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 1 deletion(-)

--
2.44.0



2024-05-13 18:39:38

by Daniel Xu

[permalink] [raw]
Subject: [PATCH bpf-next v3 1/2] kbuild: bpf: Tell pahole to DECL_TAG kfuncs

With [0], pahole can now discover kfuncs and inject DECL_TAG
into BTF. With this commit, we will start shipping said DECL_TAGs
to downstream consumers if pahole supports it.

This is useful for feature probing kfuncs as well as generating
compilable prototypes. This is particularly important as kfuncs
do not have stable ABI.

[0]: https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?h=next&id=72e88f29c6f7e14201756e65bd66157427a61aaf

Signed-off-by: Daniel Xu <[email protected]>
---
scripts/Makefile.btf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index 2d6e5ed9081e..d9ec6c85ffa7 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -21,7 +21,7 @@ endif
else

# Switch to using --btf_features for v1.26 and later.
-pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func
+pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs

endif

--
2.44.0


2024-05-13 18:39:54

by Daniel Xu

[permalink] [raw]
Subject: [PATCH bpf-next v3 2/2] bpftool: Support dumping kfunc prototypes from BTF

This patch enables dumping kfunc prototypes from bpftool. This is useful
b/c with this patch, end users will no longer have to manually define
kfunc prototypes. For the kernel tree, this also means we can optionally
drop kfunc prototypes from:

tools/testing/selftests/bpf/bpf_kfuncs.h
tools/testing/selftests/bpf/bpf_experimental.h

Example usage:

$ make PAHOLE=/home/dxu/dev/pahole/build/pahole -j30 vmlinux

$ ./tools/bpf/bpftool/bpftool btf dump file ./vmlinux format c | rg "__ksym;" | head -3
extern void cgroup_rstat_updated(struct cgroup *cgrp, int cpu) __weak __ksym;
extern void cgroup_rstat_flush(struct cgroup *cgrp) __weak __ksym;
extern struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) __weak __ksym;

Signed-off-by: Daniel Xu <[email protected]>
---
tools/bpf/bpftool/btf.c | 54 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)

diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 91fcb75babe3..884af6589f0d 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -20,6 +20,8 @@
#include "json_writer.h"
#include "main.h"

+#define KFUNC_DECL_TAG "bpf_kfunc"
+
static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_UNKN] = "UNKNOWN",
[BTF_KIND_INT] = "INT",
@@ -454,6 +456,48 @@ static int dump_btf_raw(const struct btf *btf,
return 0;
}

+static int dump_btf_kfuncs(struct btf_dump *d, const struct btf *btf)
+{
+ LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts);
+ int cnt = btf__type_cnt(btf);
+ int i;
+
+ printf("\n/* BPF kfuncs */\n");
+
+ for (i = 1; i < cnt; i++) {
+ const struct btf_type *t = btf__type_by_id(btf, i);
+ const char *name;
+ int err;
+
+ if (!btf_is_decl_tag(t))
+ continue;
+
+ if (btf_decl_tag(t)->component_idx != -1)
+ continue;
+
+ name = btf__name_by_offset(btf, t->name_off);
+ if (strncmp(name, KFUNC_DECL_TAG, sizeof(KFUNC_DECL_TAG)))
+ continue;
+
+ t = btf__type_by_id(btf, t->type);
+ if (!btf_is_func(t))
+ continue;
+
+ printf("extern ");
+
+ opts.field_name = btf__name_by_offset(btf, t->name_off);
+ err = btf_dump__emit_type_decl(d, t->type, &opts);
+ if (err)
+ return err;
+
+ printf(" __weak __ksym;\n");
+ }
+
+ printf("\n");
+
+ return 0;
+}
+
static void __printf(2, 0) btf_dump_printf(void *ctx,
const char *fmt, va_list args)
{
@@ -476,6 +520,12 @@ static int dump_btf_c(const struct btf *btf,
printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n");
printf("#endif\n\n");
+ printf("#ifndef __ksym\n");
+ printf("#define __ksym __attribute__((section(\".ksyms\")))\n");
+ printf("#endif\n\n");
+ printf("#ifndef __weak\n");
+ printf("#define __weak __attribute__((weak))\n");
+ printf("#endif\n\n");

if (root_type_cnt) {
for (i = 0; i < root_type_cnt; i++) {
@@ -491,6 +541,10 @@ static int dump_btf_c(const struct btf *btf,
if (err)
goto done;
}
+
+ err = dump_btf_kfuncs(d, btf);
+ if (err)
+ goto done;
}

printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
--
2.44.0


2024-05-13 21:15:00

by Daniel Xu

[permalink] [raw]
Subject: Re: [PATCH bpf-next v3 2/2] bpftool: Support dumping kfunc prototypes from BTF

On Mon, May 13, 2024 at 12:38:59PM GMT, Daniel Xu wrote:
> This patch enables dumping kfunc prototypes from bpftool. This is useful
> b/c with this patch, end users will no longer have to manually define
> kfunc prototypes. For the kernel tree, this also means we can optionally
> drop kfunc prototypes from:
>
> tools/testing/selftests/bpf/bpf_kfuncs.h
> tools/testing/selftests/bpf/bpf_experimental.h
>
> Example usage:
>
> $ make PAHOLE=/home/dxu/dev/pahole/build/pahole -j30 vmlinux
>
> $ ./tools/bpf/bpftool/bpftool btf dump file ./vmlinux format c | rg "__ksym;" | head -3
> extern void cgroup_rstat_updated(struct cgroup *cgrp, int cpu) __weak __ksym;
> extern void cgroup_rstat_flush(struct cgroup *cgrp) __weak __ksym;
> extern struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) __weak __ksym;
>
> Signed-off-by: Daniel Xu <[email protected]>
> ---
> tools/bpf/bpftool/btf.c | 54 +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 54 insertions(+)
>
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index 91fcb75babe3..884af6589f0d 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
> @@ -20,6 +20,8 @@
> #include "json_writer.h"
> #include "main.h"
>
> +#define KFUNC_DECL_TAG "bpf_kfunc"
> +
> static const char * const btf_kind_str[NR_BTF_KINDS] = {
> [BTF_KIND_UNKN] = "UNKNOWN",
> [BTF_KIND_INT] = "INT",
> @@ -454,6 +456,48 @@ static int dump_btf_raw(const struct btf *btf,
> return 0;
> }
>
> +static int dump_btf_kfuncs(struct btf_dump *d, const struct btf *btf)
> +{
> + LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts);
> + int cnt = btf__type_cnt(btf);
> + int i;
> +
> + printf("\n/* BPF kfuncs */\n");
> +
> + for (i = 1; i < cnt; i++) {
> + const struct btf_type *t = btf__type_by_id(btf, i);
> + const char *name;
> + int err;
> +
> + if (!btf_is_decl_tag(t))
> + continue;
> +
> + if (btf_decl_tag(t)->component_idx != -1)
> + continue;
> +
> + name = btf__name_by_offset(btf, t->name_off);
> + if (strncmp(name, KFUNC_DECL_TAG, sizeof(KFUNC_DECL_TAG)))
> + continue;
> +
> + t = btf__type_by_id(btf, t->type);
> + if (!btf_is_func(t))
> + continue;
> +
> + printf("extern ");
> +
> + opts.field_name = btf__name_by_offset(btf, t->name_off);
> + err = btf_dump__emit_type_decl(d, t->type, &opts);
> + if (err)
> + return err;
> +
> + printf(" __weak __ksym;\n");
> + }
> +
> + printf("\n");
> +
> + return 0;
> +}
> +
> static void __printf(2, 0) btf_dump_printf(void *ctx,
> const char *fmt, va_list args)
> {
> @@ -476,6 +520,12 @@ static int dump_btf_c(const struct btf *btf,
> printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
> printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n");
> printf("#endif\n\n");
> + printf("#ifndef __ksym\n");
> + printf("#define __ksym __attribute__((section(\".ksyms\")))\n");
> + printf("#endif\n\n");
> + printf("#ifndef __weak\n");
> + printf("#define __weak __attribute__((weak))\n");
> + printf("#endif\n\n");
>
> if (root_type_cnt) {
> for (i = 0; i < root_type_cnt; i++) {
> @@ -491,6 +541,10 @@ static int dump_btf_c(const struct btf *btf,
> if (err)
> goto done;
> }
> +
> + err = dump_btf_kfuncs(d, btf);
> + if (err)
> + goto done;
> }
>
> printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
> --
> 2.44.0
>
>

Oh, looks like selftests fail to build. I will fix that for v4.