Re: [PATCH bpf-next 06/11] libbpf: ensure btf_dump__new() and btf_dump_opts are future-proof

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

 



On Tue, Nov 9, 2021 at 9:40 AM Alexei Starovoitov
<alexei.starovoitov@xxxxxxxxx> wrote:
>
> On Tue, Nov 09, 2021 at 07:37:48AM -0800, Andrii Nakryiko wrote:
> > On Mon, Nov 8, 2021 at 7:38 PM Alexei Starovoitov
> > <alexei.starovoitov@xxxxxxxxx> wrote:
> > >
> > > On Sun, Nov 07, 2021 at 10:13:11PM -0800, Andrii Nakryiko wrote:
> > > > +#define btf_dump__new(a1, a2, a3, a4) __builtin_choose_expr(               \
> > > > +     __builtin_types_compatible_p(typeof(a4), btf_dump_printf_fn_t) +      \
> > > > +     __builtin_types_compatible_p(typeof(a4),                              \
> > > > +                                  void(void *, const char *, va_list)),    \
> > > > +     btf_dump__new_deprecated((void *)a1, (void *)a2, (void *)a3, (void *)a4),\
> > > > +     btf_dump__new((void *)a1, (void *)a2, (void *)a3, (void *)a4))
> > >
> > > why '+' in the above? The return type of __builtin_types_compatible_p() is bool.
> > > What is bool + bool ?
> > > It suppose to be logical 'OR', right?
> >
> > __builtin_types_compatible_p() is defined as returning 0 or 1 (not
> > true/false). And __builtin_choose_expr() is also defined as comparing
> > first argument against 0, not as true/false. But in practice it
> > doesn't matter because bool is converted to 0 or 1 in arithmetic
> > operations. So I can switch to || with no effect. Let me know if you
> > still prefer logical || and I'll change.
> >
> > But yes to your last question, it's logical OR.
>
> Interesting. Looking at LLVM code it does indeed returns 'int'.
> At least typeof(_builtin_types_compatible_p(..)) seems to be 'int'.
>
> At the same type LLVM tests are using this macro:
> #define check_same_type(type1, type2) __builtin_types_compatible_p(type1, type2) && __builtin_types_compatible_p(type1 *, type2 *)
>
> While kernel has this macro:
> #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
>
> Guessing that extra typeof() may resolve the difference between fn and &fn ?

no, btf_dump_printf_fn_t is already a type and typeof() doesn't seem
to change anything. I had a test like below. It produces the same
results with or without typeof.


static void test1(btf_dump_printf_fn_t arg, int val1, int val2, int
val3, int val4)
{
        fprintf(stderr, "TEST1 VAL %d %d %d %d\n", val1, val2, val3, val4);
}

static void test2(struct btf *arg, int val1, int val2, int val3, int val4)
{
        fprintf(stderr, "TEST2 VAL %d %d %d %d\n", val1, val2, val3, val4);
}

#define test_variad(arg) \
        __builtin_choose_expr(\
                __builtin_types_compatible_p(typeof(arg),
typeof(btf_dump_printf_fn_t)) + \
                __builtin_types_compatible_p(typeof(arg),
typeof(void(void *, const char *, va_list))),\
                test1((void *)arg,\
                      __builtin_types_compatible_p(typeof(arg),
typeof(btf_dump_printf_fn_t)),\
                      __builtin_types_compatible_p(typeof(arg),
typeof(void(void *, const char *, va_list))),\
                      __builtin_types_compatible_p(typeof(arg), struct btf *),\
                      __builtin_types_compatible_p(typeof(arg), void *)\
                ), \
                test2((void *)arg,\
                      __builtin_types_compatible_p(typeof(arg),
typeof(btf_dump_printf_fn_t)),\
                      __builtin_types_compatible_p(typeof(arg),
typeof(void(void *, const char *, va_list))),\
                      __builtin_types_compatible_p(typeof(arg), struct btf *),\
                      __builtin_types_compatible_p(typeof(arg), void *)\
                )\
        )

And then I call

        test_variad(codegen_btf_dump_printf);
        test_variad(&codegen_btf_dump_printf);
        test_variad(btf);
        test_variad(NULL);


I always get this (both with and without extra typeof()):

TEST1 VAL 0 1 0 0
TEST1 VAL 1 0 0 0
TEST2 VAL 0 0 1 0
TEST2 VAL 0 0 0 1


> Not sure why LLVM took that approach.

I think kernel's __same_type() doesn't handle this well. I've changed
kernel/bpf/hashtab.c like this:

        BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
-                    (void *(*)(struct bpf_map *map, void *key))NULL));
+                    void *(struct bpf_map *map, void *key)));

And it triggered compilation assertion.

Then I "fixed" it like this:

-       BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
-                    (void *(*)(struct bpf_map *map, void *key))NULL));
+       BUILD_BUG_ON(!__same_type(__htab_map_lookup_elem,
+                    void *(struct bpf_map *map, void *key)));

And it compiled just fine. So __same_type() is sensitive to &.

>
> Anyway it will be removed once we hit 1.0, so no need to dig too deep.
> I think changing + to || is still worth doing.

ok, I'll update in next revision

>
> > >
> > > Maybe checking for ops type would be more robust ?
> >
> > opts can be NULL. At which point it's actually only compatible with
> > void *.
>
> Assuming that fn pointer in btf_dump__new_deprecated() will never be NULL?

If fn pointer is NULL, btf_dump APIs will crash in runtime. I'll add
an explicit check and error out in such case with -EINVAL.

The way the check is written right now, if someone passes NULL we'll
choose non-deprecated btf_dump__new() variant, which seems to be a
good "default" (even though it will still crash later when calling a
callback).



[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