On Fri, Dec 22, 2023 at 5:15 AM Jiri Olsa <olsajiri@xxxxxxxxx> wrote: > > On Wed, Dec 20, 2023 at 03:31:26PM -0800, Andrii Nakryiko wrote: > > Out of all special global func arg tag annotations, __arg_ctx is > > practically is the most immediately useful and most critical to have > > working across multitude kernel version, if possible. This would allow > > end users to write much simpler code if __arg_ctx semantics worked for > > older kernels that don't natively understand btf_decl_tag("arg:ctx") in > > verifier logic. > > curious what's the workaround now.. having separate function for each > program type instead of just one global function? I wonder ebpf/cilium > library could do the same thing You mean what users do today? Something like this: /* static, so types don't matter */ static int common_logic(void *ctx, ...) { ... } /* global */ int kprobe_logic(struct bpf_user_pt_regs_t *ctx) { return common_logic(ctx); } /* global */ int perf_event_logic(struct bpf_perf_event_data *ctx) { return common_logic(ctx); } And so on. So it's not great, but it works. The problem arises when you have nested global functions that need to pass context. /* global */ int kprobe_logic_1(struct bpf_user_pt_regs_t *ctx) { ... } /* global */ int kprobe_logic_2(struct bpf_user_pt_regs_t *ctx) { int x; x = kprobe_logic_1(ctx); ... } With this nesting of global funcs the above trick doesn't work anymore because common_logic() can't call per-program global function anymore. > > whole patchset lgtm: > > Acked-by: Jiri Olsa <jolsa@xxxxxxxxxx> > Thanks! > jirka > > > > > Luckily, it is possible to ensure __arg_ctx works on old kernels through > > a bit of extra work done by libbpf, at least in a lot of common cases. > > > > To explain the overall idea, we need to go back at how context argument > > was supported in global funcs before __arg_ctx support was added. This > > was done based on special struct name checks in kernel. E.g., for > > BPF_PROG_TYPE_PERF_EVENT the expectation is that argument type `struct > > bpf_perf_event_data *` mark that argument as PTR_TO_CTX. This is all > > good as long as global function is used from the same BPF program types > > only, which is often not the case. If the same subprog has to be called > > from, say, kprobe and perf_event program types, there is no single > > definition that would satisfy BPF verifier. Subprog will have context > > argument either for kprobe (if using bpf_user_pt_regs_t struct name) or > > perf_event (with bpf_perf_event_data struct name), but not both. > > > > This limitation was the reason to add btf_decl_tag("arg:ctx"), making > > the actual argument type not important, so that user can just define > > "generic" signature: > > > > __noinline int global_subprog(void *ctx __arg_ctx) { ... } > > > > I won't belabor how libbpf is implementing subprograms, see a huge > > comment next to bpf_object__relocate_calls() function. The idea is that > > each main/entry BPF program gets its own copy of global_subprog's code > > appended. > > > > This per-program copy of global subprog code *and* associated func_info > > .BTF.ext information, pointing to FUNC -> FUNC_PROTO BTF type chain > > allows libbpf to simulate __arg_ctx behavior transparently, even if the > > kernel doesn't yet support __arg_ctx annotation natively. > > > > The idea is straightforward: each time we append global subprog's code > > and func_info information, we adjust its FUNC -> FUNC_PROTO type > > information, if necessary (that is, libbpf can detect the presence of > > btf_decl_tag("arg:ctx") just like BPF verifier would do it). > > > > The rest is just mechanical and somewhat painful BTF manipulation code. > > It's painful because we need to clone FUNC -> FUNC_PROTO, instead of > > reusing it, as same FUNC -> FUNC_PROTO chain might be used by another > > main BPF program within the same BPF object, so we can't just modify it > > in-place (and cloning BTF types within the same struct btf object is > > painful due to constant memory invalidation, see comments in code). > > Uploaded BPF object's BTF information has to work for all BPF > > programs at the same time. > > > > Once we have FUNC -> FUNC_PROTO clones, we make sure that instead of > > using some `void *ctx` parameter definition, we have an expected `struct > > bpf_perf_event_data *ctx` definition (as far as BPF verifier and kernel > > is concerned), which will mark it as context for BPF verifier. Same > > global subprog relocated and copied into another main BPF program will > > get different type information according to main program's type. It all > > works out in the end in a completely transparent way for end user. > > > > Libbpf maintains internal program type -> expected context struct name > > mapping internally. Note, not all BPF program types have named context > > struct, so this approach won't work for such programs (just like it > > didn't before __arg_ctx). So native __arg_ctx is still important to have > > in kernel to have generic context support across all BPF program types. > > > > Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx> > > --- > > tools/lib/bpf/libbpf.c | 239 +++++++++++++++++++++++++++++++++++++++-- > > 1 file changed, 231 insertions(+), 8 deletions(-) > > please trim [...]