Re: [PATCH bpf-next 1/5] libbpf: generalize virtual __kconfig externs and use it for USDT

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jul 13, 2022 at 11:08 AM Andrii Nakryiko
<andrii.nakryiko@xxxxxxxxx> wrote:
>
> On Wed, Jul 13, 2022 at 9:24 AM <sdf@xxxxxxxxxx> wrote:
> >
> > On 07/12, Andrii Nakryiko wrote:
> > > Libbpf supports single virtual __kconfig extern currently:
> > > LINUX_KERNEL_VERSION.
> > > LINUX_KERNEL_VERSION isn't coming from /proc/kconfig.gz and is intead
> > > customly filled out by libbpf.
> >
> > > This patch generalizes this approach to support more such virtual
> > > __kconfig externs. One such extern added in this patch is
> > > LINUX_HAS_BPF_COOKIE which is used for BPF-side USDT supporting code in
> > > usdt.bpf.h instead of using CO-RE-based enum detection approach for
> > > detecting bpf_get_attach_cookie() BPF helper. This allows to remove
> > > otherwise not needed CO-RE dependency and keeps user-space and BPF-side
> > > parts of libbpf's USDT support strictly in sync in terms of their
> > > feature detection.
> >
> > > We'll use similar approach for syscall wrapper detection for
> > > BPF_KSYSCALL() BPF-side macro in follow up patch.
> >
> > > Generally, currently libbpf reserves CONFIG_ prefix for Kconfig values
> > > and LINUX_ for virtual libbpf-backed externs. In the future we might
> > > extend the set of prefixes that are supported. This can be done without
> > > any breaking changes, as currently any __kconfig extern with
> > > unrecognized name is rejected.
> >
> > > For LINUX_xxx externs we support the normal "weak rule": if libbpf
> > > doesn't recognize given LINUX_xxx extern but such extern is marked as
> > > __weak, it is not rejected and defaults to zero.  This follows
> > > CONFIG_xxx handling logic and will allow BPF applications to
> > > opportunistically use newer libbpf virtual externs without breaking on
> > > older libbpf versions unnecessarily.
> >
> > > Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx>
> > > ---
> > >   tools/lib/bpf/libbpf.c   | 69 +++++++++++++++++++++++++++++-----------
> > >   tools/lib/bpf/usdt.bpf.h | 16 ++--------
> > >   2 files changed, 52 insertions(+), 33 deletions(-)
> >
> > > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > > index cb49408eb298..4bae67767f82 100644
> > > --- a/tools/lib/bpf/libbpf.c
> > > +++ b/tools/lib/bpf/libbpf.c
> > > @@ -1800,11 +1800,18 @@ static bool is_kcfg_value_in_range(const struct
> > > extern_desc *ext, __u64 v)
> > >   static int set_kcfg_value_num(struct extern_desc *ext, void *ext_val,
> > >                             __u64 value)
> > >   {
> > > -     if (ext->kcfg.type != KCFG_INT && ext->kcfg.type != KCFG_CHAR) {
> > > -             pr_warn("extern (kcfg) %s=%llu should be integer\n",
> > > +     if (ext->kcfg.type != KCFG_INT && ext->kcfg.type != KCFG_CHAR &&
> > > +         ext->kcfg.type != KCFG_BOOL) {
> > > +             pr_warn("extern (kcfg) %s=%llu should be integer, char or boolean\n",
> > >                       ext->name, (unsigned long long)value);
> > >               return -EINVAL;
> > >       }
> > > +     if (ext->kcfg.type == KCFG_BOOL && value > 1) {
> > > +             pr_warn("extern (kcfg) %s=%llu value isn't boolean\n",
> > > +                     ext->name, (unsigned long long)value);
> > > +             return -EINVAL;
> > > +
> > > +     }
> > >       if (!is_kcfg_value_in_range(ext, value)) {
> > >               pr_warn("extern (kcfg) %s=%llu value doesn't fit in %d bytes\n",
> > >                       ext->name, (unsigned long long)value, ext->kcfg.sz);
> > > @@ -1870,10 +1877,13 @@ static int
> > > bpf_object__process_kconfig_line(struct bpf_object *obj,
> > >               /* assume integer */
> > >               err = parse_u64(value, &num);
> > >               if (err) {
> > > -                     pr_warn("extern (kcfg) %s=%s should be integer\n",
> > > -                             ext->name, value);
> > > +                     pr_warn("extern (kcfg) %s=%s should be integer\n", ext->name, value);
> > >                       return err;
> > >               }
> > > +             if (ext->kcfg.type != KCFG_INT && ext->kcfg.type != KCFG_CHAR) {
> > > +                     pr_warn("extern (kcfg) %s=%s should be integer\n", ext->name, value);
> > > +                     return -EINVAL;
> > > +             }
> > >               err = set_kcfg_value_num(ext, ext_val, num);
> > >               break;
> > >       }
> > > @@ -7493,26 +7503,47 @@ static int bpf_object__resolve_externs(struct
> > > bpf_object *obj,
> > >       for (i = 0; i < obj->nr_extern; i++) {
> > >               ext = &obj->externs[i];
> >
> > > -             if (ext->type == EXT_KCFG &&
> > > -                 strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) {
> > > -                     void *ext_val = kcfg_data + ext->kcfg.data_off;
> > > -                     __u32 kver = get_kernel_version();
> > > +             if (ext->type == EXT_KSYM) {
> > > +                     if (ext->ksym.type_id)
> > > +                             need_vmlinux_btf = true;
> > > +                     else
> > > +                             need_kallsyms = true;
> > > +                     continue;
> > > +             } else if (ext->type == EXT_KCFG) {
> > > +                     void *ext_ptr = kcfg_data + ext->kcfg.data_off;
> > > +                     __u64 value = 0;
> > > +
> > > +                     /* Kconfig externs need actual /proc/config.gz */
> > > +                     if (str_has_pfx(ext->name, "CONFIG_")) {
> > > +                             need_config = true;
> > > +                             continue;
> > > +                     }
> >
> > > -                     if (!kver) {
> > > -                             pr_warn("failed to get kernel version\n");
> > > +                     /* Virtual kcfg externs are customly handled by libbpf */
> > > +                     if (strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) {
> > > +                             value = get_kernel_version();
> > > +                             if (!value) {
> > > +                                     pr_warn("extern (kcfg) '%s': failed to get kernel version\n",
> > > ext->name);
> > > +                                     return -EINVAL;
> > > +                             }
> > > +                     } else if (strcmp(ext->name, "LINUX_HAS_BPF_COOKIE") == 0) {
> > > +                             value = kernel_supports(obj, FEAT_BPF_COOKIE);
> > > +                     } else if (!str_has_pfx(ext->name, "LINUX_") || !ext->is_weak) {
> > > +                             /* Currently libbpf supports only CONFIG_ and LINUX_ prefixed
> > > +                              * __kconfig externs, where LINUX_ ones are virtual and filled out
> > > +                              * customly by libbpf (their values don't come from Kconfig).
> > > +                              * If LINUX_xxx variable is not recognized by libbpf, but is marked
> > > +                              * __weak, it defaults to zero value, just like for CONFIG_xxx
> > > +                              * externs.
> > > +                              */
> > > +                             pr_warn("extern (kcfg) '%s': unrecognized virtual extern\n",
> > > ext->name);
> > >                               return -EINVAL;
> > >                       }
> > > -                     err = set_kcfg_value_num(ext, ext_val, kver);
> > > +
> > > +                     err = set_kcfg_value_num(ext, ext_ptr, value);
> > >                       if (err)
> > >                               return err;
> > > -                     pr_debug("extern (kcfg) %s=0x%x\n", ext->name, kver);
> > > -             } else if (ext->type == EXT_KCFG && str_has_pfx(ext->name, "CONFIG_"))
> > > {
> > > -                     need_config = true;
> > > -             } else if (ext->type == EXT_KSYM) {
> > > -                     if (ext->ksym.type_id)
> > > -                             need_vmlinux_btf = true;
> > > -                     else
> > > -                             need_kallsyms = true;
> > > +                     pr_debug("extern (kcfg) %s=0x%llx\n", ext->name, (long long)value);
> > >               } else {
> > >                       pr_warn("unrecognized extern '%s'\n", ext->name);
> > >                       return -EINVAL;
> > > diff --git a/tools/lib/bpf/usdt.bpf.h b/tools/lib/bpf/usdt.bpf.h
> > > index 4181fddb3687..4f2adc0bd6ca 100644
> > > --- a/tools/lib/bpf/usdt.bpf.h
> > > +++ b/tools/lib/bpf/usdt.bpf.h
> > > @@ -6,7 +6,6 @@
> > >   #include <linux/errno.h>
> > >   #include <bpf/bpf_helpers.h>
> > >   #include <bpf/bpf_tracing.h>
> > > -#include <bpf/bpf_core_read.h>
> >
> > >   /* Below types and maps are internal implementation details of libbpf's
> > > USDT
> > >    * support and are subjects to change. Also, bpf_usdt_xxx() API helpers
> > > should
> > > @@ -30,14 +29,6 @@
> > >   #ifndef BPF_USDT_MAX_IP_CNT
> > >   #define BPF_USDT_MAX_IP_CNT (4 * BPF_USDT_MAX_SPEC_CNT)
> > >   #endif
> > > -/* We use BPF CO-RE to detect support for BPF cookie from BPF side. This
> > > is
> > > - * the only dependency on CO-RE, so if it's undesirable, user can
> > > override
> > > - * BPF_USDT_HAS_BPF_COOKIE to specify whether to BPF cookie is supported
> > > or not.
> > > - */
> > > -#ifndef BPF_USDT_HAS_BPF_COOKIE
> > > -#define BPF_USDT_HAS_BPF_COOKIE \
> > > -     bpf_core_enum_value_exists(enum bpf_func_id___usdt,
> > > BPF_FUNC_get_attach_cookie___usdt)
> > > -#endif
> >
> > >   enum __bpf_usdt_arg_type {
> > >       BPF_USDT_ARG_CONST,
> > > @@ -83,15 +74,12 @@ struct {
> > >       __type(value, __u32);
> > >   } __bpf_usdt_ip_to_spec_id SEC(".maps") __weak;
> >
> > > -/* don't rely on user's BPF code to have latest definition of
> > > bpf_func_id */
> > > -enum bpf_func_id___usdt {
> > > -     BPF_FUNC_get_attach_cookie___usdt = 0xBAD, /* value doesn't matter */
> > > -};
> > > +extern const _Bool LINUX_HAS_BPF_COOKIE __kconfig;
> >
> > Could _Bool be a problem when used by c++? I remember that we can have
> > sizeof(bool) != sizeof(_Bool) when compiling with g++. If we were to
> > use 'int' instead I'm assuming we'll loose all the niceties of
> > KCFG_BOOL?
> >
> > Or shouldn't be a problem since it's not part of C/C++ api boundary?
>
> I actually don't know if C++ supports "_Bool", but in C, "bool" is an
> alias to _Bool. _Bool is *the type* of the boolean. The benefit of
> _Bool is that it doesn't require including stdbool.h, while "bool"
> itself needs extra header. So I try not to use bool in libbpf BPF API
> headers just to avoid extra header dependencies.
>
> But it shouldn't matter as this is BPF-side code, so it has to be
> compiled in C mode by Clang/GCC, so _Bool should always be there.

The program is fine, but these _Bool's can now leak into generated
skeletons, right?
And skeletons might be included into c++ and I'm not sure what happens
with _Bool over there.

My naive attempt to use it gives me the following:

$ cat tst.cc
int main(int argc, char *argv[])
{
    _Bool have_args = argc > 1;
    if (have_args)
        return 1;
    else
        return 0;
}
$ clang++ tst.cc
tst.cc:3:5: error: unknown type name '_Bool'
    _Bool have_args = argc > 1;
    ^
1 error generated.

> As for the size it seems like it's not even specified by the standard
> that sizeof(bool/_Bool) is 1, though it is in practice. I only
> remember some very-very-very old versions of Microsoft's Visual C++
> having sizeof(bool) == 4, but then they changed that anyways to
> sizeof(bool) == 1 (it was many-many years ago, so it might be an
> incomplete story).
>
> But either way it doesn't matter, because libbpf will support any
> size: 1, 2, 4, 8 and will just set 1 for true, 0 for false, with
> correct zero extension to match variable size.
>
> As for bool vs int, no real difference, but it is true/false
> conceptually, so seems cleaner to use bool. But using int will work
> just fine here as well (you still get 0 or 1 for both, effectively).

Yeah, so my question is more like: rather than trying to figure out if
_Bool is safe, might it be easier to stick to ints?


> > >   static __always_inline
> > >   int __bpf_usdt_spec_id(struct pt_regs *ctx)
> > >   {
> > > -     if (!BPF_USDT_HAS_BPF_COOKIE) {
> > > +     if (!LINUX_HAS_BPF_COOKIE) {
> > >               long ip = PT_REGS_IP(ctx);
> > >               int *spec_id_ptr;
> >
> > > --
> > > 2.30.2
> >



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux