public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
From: Yonghong Song <yhs@fb.com>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	kernel-team@fb.com
Subject: Re: [PATCH bpf-next v4 5/8] libbpf: Add new BPF_PROG2 macro
Date: Fri, 9 Sep 2022 09:31:28 -0700	[thread overview]
Message-ID: <c97c123d-9d4a-6aff-e8d3-31ccf3b3e9df@fb.com> (raw)
In-Reply-To: <CAEf4BzZ9PopF-9jL4XXTXPNHRMCpKuR0Yc=HZTiRMTaRA-SqUw@mail.gmail.com>



On 9/8/22 5:11 PM, Andrii Nakryiko wrote:
> On Wed, Aug 31, 2022 at 8:27 AM Yonghong Song <yhs@fb.com> wrote:
>>
>> To support struct arguments in trampoline based programs,
>> existing BPF_PROG doesn't work any more since
>> the type size is needed to find whether a parameter
>> takes one or two registers. So this patch added a new
>> BPF_PROG2 macro to support such trampoline programs.
>>
>> The idea is suggested by Andrii. For example, if the
>> to-be-traced function has signature like
>>    typedef struct {
>>         void *x;
>>         int t;
>>    } sockptr;
>>    int blah(sockptr x, char y);
>>
>> In the new BPF_PROG2 macro, the argument can be
>> represented as
>>    __bpf_prog_call(
>>       ({ union {
>>            struct { __u64 x, y; } ___z;
>>            sockptr x;
>>          } ___tmp = { .___z = { ctx[0], ctx[1] }};
>>          ___tmp.x;
>>       }),
>>       ({ union {
>>            struct { __u8 x; } ___z;
>>            char y;
>>          } ___tmp = { .___z = { ctx[2] }};
>>          ___tmp.y;
>>       }));
>> In the above, the values stored on the stack are properly
>> assigned to the actual argument type value by using 'union'
>> magic. Note that the macro also works even if no arguments
>> are with struct types.
>>
>> Note that new BPF_PROG2 works for both llvm16 and pre-llvm16
>> compilers where llvm16 supports bpf target passing value
>> with struct up to 16 byte size and pre-llvm16 will pass
>> by reference by storing values on the stack. With static functions
>> with struct argument as always inline, the compiler is able
>> to optimize and remove additional stack saving of struct values.
>>
>> Signed-off-by: Yonghong Song <yhs@fb.com>
>> ---
>>   tools/lib/bpf/bpf_tracing.h | 79 +++++++++++++++++++++++++++++++++++++
>>   1 file changed, 79 insertions(+)
>>
>> diff --git a/tools/lib/bpf/bpf_tracing.h b/tools/lib/bpf/bpf_tracing.h
>> index 5fdb93da423b..8d4bdd18cb3d 100644
>> --- a/tools/lib/bpf/bpf_tracing.h
>> +++ b/tools/lib/bpf/bpf_tracing.h
>> @@ -438,6 +438,85 @@ typeof(name(0)) name(unsigned long long *ctx)                                  \
>>   static __always_inline typeof(name(0))                                     \
>>   ____##name(unsigned long long *ctx, ##args)
>>
>> +#ifndef ____bpf_nth
>> +#define ____bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, N, ...) N
>> +#endif
> 
> we already have ___bpf_nth (triple underscore) variant, wouldn't
> extending that one to support up to 24 argument work? It's quite
> confusing to have ___bpf_nth and ____bpf_nth. Maybe let's consolidate?
> 
> And I'd totally wrap this long line :)

I tried earlier and it doesn't work. I will try again. If it still not 
works, I will change the name to ___bpf_nth2 similar to ___bpf_narg2 below.

> 
> 
>> +#ifndef ____bpf_narg
>> +#define ____bpf_narg(...) ____bpf_nth(_, ##__VA_ARGS__, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0)
>> +#endif
> 
> similar confusiong with triple underscore bpf_narg. Given this is used
> in BPF_PROG2, how about renaming it to bpf_narg2 to make this
> connection? And also note that all similar macros use triple
> underscore, while you added quad underscores everywhere. Can you
> please follow up with a rename to use triple underscore for
> consistency?

Sounds good. Will change to ___bpf_narg2.

> 
>> +
>> +#define BPF_REG_CNT(t) \
> 
> this looks like a "public API", but I don't think this was the
> intention, right? Let's rename it to ___bpf_reg_cnt()?

Yes, I can do it.

> 
>> +       (__builtin_choose_expr(sizeof(t) == 1 || sizeof(t) == 2 || sizeof(t) == 4 || sizeof(t) == 8, 1, \
> 
> nit: seeing ____bpf_union_arg implementation below I prefer one case
> per line there as well. How about doing one __builtin_choose_expr per
> each supported size?

Actually, I did have each individual case per line in the beginning. But 
later on I changed to the above based on Kui-Feng's comment. I can 
change back to each case per line in the next revision to be aligned
with other usages.

> 
>> +        __builtin_choose_expr(sizeof(t) == 16, 2,                                                      \
>> +                              (void)0)))
>> +
>> +#define ____bpf_reg_cnt0()                     (0)
>> +#define ____bpf_reg_cnt1(t, x)                 (____bpf_reg_cnt0() + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt2(t, x, args...)                (____bpf_reg_cnt1(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt3(t, x, args...)                (____bpf_reg_cnt2(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt4(t, x, args...)                (____bpf_reg_cnt3(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt5(t, x, args...)                (____bpf_reg_cnt4(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt6(t, x, args...)                (____bpf_reg_cnt5(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt7(t, x, args...)                (____bpf_reg_cnt6(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt8(t, x, args...)                (____bpf_reg_cnt7(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt9(t, x, args...)                (____bpf_reg_cnt8(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt10(t, x, args...)       (____bpf_reg_cnt9(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt11(t, x, args...)       (____bpf_reg_cnt10(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt12(t, x, args...)       (____bpf_reg_cnt11(args) + BPF_REG_CNT(t))
>> +#define ____bpf_reg_cnt(args...)        ___bpf_apply(____bpf_reg_cnt, ____bpf_narg(args))(args)
>> +
>> +#define ____bpf_union_arg(t, x, n) \
>> +       __builtin_choose_expr(sizeof(t) == 1, ({ union { struct { __u8 x; } ___z; t x; } ___tmp = { .___z = {ctx[n]}}; ___tmp.x; }), \
>> +       __builtin_choose_expr(sizeof(t) == 2, ({ union { struct { __u16 x; } ___z; t x; } ___tmp = { .___z = {ctx[n]} }; ___tmp.x; }), \
>> +       __builtin_choose_expr(sizeof(t) == 4, ({ union { struct { __u32 x; } ___z; t x; } ___tmp = { .___z = {ctx[n]} }; ___tmp.x; }), \
>> +       __builtin_choose_expr(sizeof(t) == 8, ({ union { struct { __u64 x; } ___z; t x; } ___tmp = {.___z = {ctx[n]} }; ___tmp.x; }), \
>> +       __builtin_choose_expr(sizeof(t) == 16, ({ union { struct { __u64 x, y; } ___z; t x; } ___tmp = {.___z = {ctx[n], ctx[n + 1]} }; ___tmp.x; }), \
>> +                             (void)0)))))
> 
> looking at this again, we can do a bit better by using arrays, please
> consider using that. At the very least results in shorter lines:
> 
>   #define ____bpf_union_arg(t, x, n) \
> -       __builtin_choose_expr(sizeof(t) == 1, ({ union { struct { __u8
> x; } ___z; t x; } ___tmp = { .___z = {ctx[n]}}; ___tmp.x; }), \
> -       __builtin_choose_expr(sizeof(t) == 2, ({ union { struct {
> __u16 x; } ___z; t x; } ___tmp = { .___z = {ctx[n]} }; ___tmp.x; }), \
> -       __builtin_choose_expr(sizeof(t) == 4, ({ union { struct {
> __u32 x; } ___z; t x; } ___tmp = { .___z = {ctx[n]} }; ___tmp.x; }), \
> -       __builtin_choose_expr(sizeof(t) == 8, ({ union { struct {
> __u64 x; } ___z; t x; } ___tmp = {.___z = {ctx[n]} }; ___tmp.x; }), \
> -       __builtin_choose_expr(sizeof(t) == 16, ({ union { struct {
> __u64 x, y; } ___z; t x; } ___tmp = {.___z = {ctx[n], ctx[n + 1]} };
> ___tmp.x; }), \
> +       __builtin_choose_expr(sizeof(t) == 1, ({ union { __u8 z[1]; t
> x; } ___tmp = { .z = {ctx[n]} }; ___tmp.x; }), \
> +       __builtin_choose_expr(sizeof(t) == 2, ({ union { __u16 z[1]; t
> x; } ___tmp = { .z = {ctx[n]} }; ___tmp.x; }), \
> +       __builtin_choose_expr(sizeof(t) == 4, ({ union { __u32 z[1]; t
> x; } ___tmp = { .z = {ctx[n]} }; ___tmp.x; }), \
> +       __builtin_choose_expr(sizeof(t) == 8, ({ union { __u64 z[1]; t
> x; } ___tmp = {.z = {ctx[n]} }; ___tmp.x; }), \
> +       __builtin_choose_expr(sizeof(t) == 16, ({ union { __u64 z[2];
> t x; } ___tmp = {.z = {ctx[n], ctx[n + 1]} }; ___tmp.x; }), \
>                                (void)0)))))
> 
> It is using one- or two-element arrays, and it also has uniform
> {ctx[n]} or {ctx[n], ctx[n + 1]} initialization syntax. Seems a bit
> nicer than union { struct { ... combo.

Sounds good. Will try to do this.

> 
>> +
>> +#define ____bpf_ctx_arg0(n, args...)
>> +#define ____bpf_ctx_arg1(n, t, x)              , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt1(t, x))
>> +#define ____bpf_ctx_arg2(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt2(t, x, args)) ____bpf_ctx_arg1(n, args)
>> +#define ____bpf_ctx_arg3(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt3(t, x, args)) ____bpf_ctx_arg2(n, args)
>> +#define ____bpf_ctx_arg4(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt4(t, x, args)) ____bpf_ctx_arg3(n, args)
>> +#define ____bpf_ctx_arg5(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt5(t, x, args)) ____bpf_ctx_arg4(n, args)
>> +#define ____bpf_ctx_arg6(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt6(t, x, args)) ____bpf_ctx_arg5(n, args)
>> +#define ____bpf_ctx_arg7(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt7(t, x, args)) ____bpf_ctx_arg6(n, args)
>> +#define ____bpf_ctx_arg8(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt8(t, x, args)) ____bpf_ctx_arg7(n, args)
>> +#define ____bpf_ctx_arg9(n, t, x, args...)     , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt9(t, x, args)) ____bpf_ctx_arg8(n, args)
>> +#define ____bpf_ctx_arg10(n, t, x, args...)    , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt10(t, x, args)) ____bpf_ctx_arg9(n, args)
>> +#define ____bpf_ctx_arg11(n, t, x, args...)    , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt11(t, x, args)) ____bpf_ctx_arg10(n, args)
>> +#define ____bpf_ctx_arg12(n, t, x, args...)    , ____bpf_union_arg(t, x, n - ____bpf_reg_cnt12(t, x, args)) ____bpf_ctx_arg11(n, args)
>> +#define ____bpf_ctx_arg(n, args...)    ___bpf_apply(____bpf_ctx_arg, ____bpf_narg(args))(n, args)
>> +
>> +#define ____bpf_ctx_decl0()
>> +#define ____bpf_ctx_decl1(t, x)                        , t x
>> +#define ____bpf_ctx_decl2(t, x, args...)       , t x ____bpf_ctx_decl1(args)
>> +#define ____bpf_ctx_decl3(t, x, args...)       , t x ____bpf_ctx_decl2(args)
>> +#define ____bpf_ctx_decl4(t, x, args...)       , t x ____bpf_ctx_decl3(args)
>> +#define ____bpf_ctx_decl5(t, x, args...)       , t x ____bpf_ctx_decl4(args)
>> +#define ____bpf_ctx_decl6(t, x, args...)       , t x ____bpf_ctx_decl5(args)
>> +#define ____bpf_ctx_decl7(t, x, args...)       , t x ____bpf_ctx_decl6(args)
>> +#define ____bpf_ctx_decl8(t, x, args...)       , t x ____bpf_ctx_decl7(args)
>> +#define ____bpf_ctx_decl9(t, x, args...)       , t x ____bpf_ctx_decl8(args)
>> +#define ____bpf_ctx_decl10(t, x, args...)      , t x ____bpf_ctx_decl9(args)
>> +#define ____bpf_ctx_decl11(t, x, args...)      , t x ____bpf_ctx_decl10(args)
>> +#define ____bpf_ctx_decl12(t, x, args...)      , t x ____bpf_ctx_decl11(args)
>> +#define ____bpf_ctx_decl(args...)      ___bpf_apply(____bpf_ctx_decl, ____bpf_narg(args))(args)
>> +
>> +/*
>> + * BPF_PROG2 can handle struct arguments.
> 
> We have to expand comment here. Let's not slack on this. Point out
> that it's the similar use and idea as with BPF_PROG, but emphasize the
> difference in syntax between BPF_PROG and BPF_PROG2. I'd show two
> simple examples of the same function with BPF_PROG and BPF_PROG2 here.
> Please follow up.

Will add detailed comments to explain why BPF_PROG2 and its core usage
and difference from BPF_PROG in the followup patch.

> 
>> + */
>> +#define BPF_PROG2(name, args...)                                               \
>> +name(unsigned long long *ctx);                                                 \
>> +static __always_inline typeof(name(0))                                         \
>> +____##name(unsigned long long *ctx ____bpf_ctx_decl(args));                    \
>> +typeof(name(0)) name(unsigned long long *ctx)                                  \
>> +{                                                                              \
>> +       return ____##name(ctx ____bpf_ctx_arg(____bpf_reg_cnt(args), args));    \
> 
> nit: you could have simplified this by doing ____bpf_reg_cnt() call
> inside ____bpf_ctx_decl(args...) definition. I think it's a bit more
> self-contained that way.

Will do this in the follow-up patch.

> 
>> +}                                                                              \
>> +static __always_inline typeof(name(0))                                         \
>> +____##name(unsigned long long *ctx ____bpf_ctx_decl(args))
>> +
>>   struct pt_regs;
>>
>>   #define ___bpf_kprobe_args0()           ctx
>> --
>> 2.30.2
>>

  reply	other threads:[~2022-09-09 16:31 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-31 15:26 [PATCH bpf-next v4 0/8] bpf: Support struct argument for trampoline base progs Yonghong Song
2022-08-31 15:26 ` [PATCH bpf-next v4 1/8] bpf: Allow struct argument in trampoline based programs Yonghong Song
2022-08-31 15:26 ` [PATCH bpf-next v4 2/8] bpf: x86: Support in-register struct arguments in trampoline programs Yonghong Song
2022-09-06 16:40   ` Kui-Feng Lee
2022-09-06 19:30     ` Yonghong Song
2022-09-07  3:00   ` Alexei Starovoitov
2022-09-07 18:04     ` Yonghong Song
2022-08-31 15:26 ` [PATCH bpf-next v4 3/8] bpf: Update descriptions for helpers bpf_get_func_arg[_cnt]() Yonghong Song
2022-09-02  7:56   ` Jiri Olsa
2022-09-06 16:12     ` Yonghong Song
2022-08-31 15:27 ` [PATCH bpf-next v4 4/8] bpf: arm64: No support of struct argument in trampoline programs Yonghong Song
2022-08-31 15:27 ` [PATCH bpf-next v4 5/8] libbpf: Add new BPF_PROG2 macro Yonghong Song
2022-09-09  0:11   ` Andrii Nakryiko
2022-09-09 16:31     ` Yonghong Song [this message]
2022-09-09 18:07       ` Andrii Nakryiko
2022-08-31 15:27 ` [PATCH bpf-next v4 6/8] selftests/bpf: Add struct argument tests with fentry/fexit programs Yonghong Song
2022-08-31 15:27 ` [PATCH bpf-next v4 7/8] selftests/bpf: Use BPF_PROG2 for some fentry programs without struct arguments Yonghong Song
2022-08-31 15:27 ` [PATCH bpf-next v4 8/8] selftests/bpf: Add tracing_struct test in DENYLIST.s390x Yonghong Song
2022-09-07  3:10 ` [PATCH bpf-next v4 0/8] bpf: Support struct argument for trampoline base progs patchwork-bot+netdevbpf

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=c97c123d-9d4a-6aff-e8d3-31ccf3b3e9df@fb.com \
    --to=yhs@fb.com \
    --cc=andrii.nakryiko@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kernel-team@fb.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