Commit c040e902 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'libbpf-side-__arg_ctx-fallback-support'

Andrii Nakryiko says:

====================
Libbpf-side __arg_ctx fallback support

Support __arg_ctx global function argument tag semantics even on older kernels
that don't natively support it through btf_decl_tag("arg:ctx").

Patches #2-#6 are preparatory work to allow to postpone BTF loading into the
kernel until after all the BPF program relocations (including global func
appending to main programs) are done. Patch #4 is perhaps the most important
and establishes pre-created stable placeholder FDs, so that relocations can
embed valid map FDs into ldimm64 instructions.

Once BTF is done after relocation, what's left is to adjust BTF information to
have each main program's copy of each used global subprog to point to its own
adjusted FUNC -> FUNC_PROTO type chain (if they use __arg_ctx) in such a way
as to satisfy type expectations of BPF verifier regarding the PTR_TO_CTX
argument definition. See patch #8 for details.

Patch #8 adds few more __arg_ctx use cases (edge cases like multiple arguments
having __arg_ctx, etc) to test_global_func_ctx_args.c, to make it simple to
validate that this logic indeed works on old kernels. It does. But just to be
100% sure patch #9 adds a test validating that libbpf uploads func_info with
properly modified BTF data.

v2->v3:
  - drop renaming patch (Alexei, Eduard);
  - use memfd_create() instead of /dev/null for placeholder FD (Eduard);
  - add one more test for validating BTF rewrite logic (Eduard);
  - fixed wrong -errno usage, reshuffled some BTF rewrite bits (Eduard);
v1->v2:
  - do internal functions renaming in patch #1 (Alexei);
  - extract cloning of FUNC -> FUNC_PROTO information into separate function
    (Alexei);
====================
Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20240104013847.3875810-1-andrii@kernel.orgSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents f8506c57 95226f5a
This diff is collapsed.
......@@ -555,6 +555,20 @@ static inline int ensure_good_fd(int fd)
return fd;
}
/* Point *fixed_fd* to the same file that *tmp_fd* points to.
* Regardless of success, *tmp_fd* is closed.
* Whatever *fixed_fd* pointed to is closed silently.
*/
static inline int reuse_fd(int fixed_fd, int tmp_fd)
{
int err;
err = dup2(tmp_fd, fixed_fd);
err = err < 0 ? -errno : 0;
close(tmp_fd); /* clean up temporary FD */
return err;
}
/* The following two functions are exposed to bpftool */
int bpf_core_add_cands(struct bpf_core_cand *local_cand,
size_t local_essent_len,
......
......@@ -20,6 +20,109 @@
#include "test_global_func17.skel.h"
#include "test_global_func_ctx_args.skel.h"
#include "bpf/libbpf_internal.h"
#include "btf_helpers.h"
static void check_ctx_arg_type(const struct btf *btf, const struct btf_param *p)
{
const struct btf_type *t;
const char *s;
t = btf__type_by_id(btf, p->type);
if (!ASSERT_EQ(btf_kind(t), BTF_KIND_PTR, "ptr_t"))
return;
s = btf_type_raw_dump(btf, t->type);
if (!ASSERT_HAS_SUBSTR(s, "STRUCT 'bpf_perf_event_data' size=0 vlen=0",
"ctx_struct_t"))
return;
}
static void subtest_ctx_arg_rewrite(void)
{
struct test_global_func_ctx_args *skel = NULL;
struct bpf_prog_info info;
char func_info_buf[1024] __attribute__((aligned(8)));
struct bpf_func_info_min *rec;
struct btf *btf = NULL;
__u32 info_len = sizeof(info);
int err, fd, i;
skel = test_global_func_ctx_args__open();
if (!ASSERT_OK_PTR(skel, "skel_open"))
return;
bpf_program__set_autoload(skel->progs.arg_tag_ctx_perf, true);
err = test_global_func_ctx_args__load(skel);
if (!ASSERT_OK(err, "skel_load"))
goto out;
memset(&info, 0, sizeof(info));
info.func_info = ptr_to_u64(&func_info_buf);
info.nr_func_info = 3;
info.func_info_rec_size = sizeof(struct bpf_func_info_min);
fd = bpf_program__fd(skel->progs.arg_tag_ctx_perf);
err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
if (!ASSERT_OK(err, "prog_info"))
goto out;
if (!ASSERT_EQ(info.nr_func_info, 3, "nr_func_info"))
goto out;
btf = btf__load_from_kernel_by_id(info.btf_id);
if (!ASSERT_OK_PTR(btf, "obj_kern_btf"))
goto out;
rec = (struct bpf_func_info_min *)func_info_buf;
for (i = 0; i < info.nr_func_info; i++, rec = (void *)rec + info.func_info_rec_size) {
const struct btf_type *fn_t, *proto_t;
const char *name;
if (rec->insn_off == 0)
continue; /* main prog, skip */
fn_t = btf__type_by_id(btf, rec->type_id);
if (!ASSERT_OK_PTR(fn_t, "fn_type"))
goto out;
if (!ASSERT_EQ(btf_kind(fn_t), BTF_KIND_FUNC, "fn_type_kind"))
goto out;
proto_t = btf__type_by_id(btf, fn_t->type);
if (!ASSERT_OK_PTR(proto_t, "proto_type"))
goto out;
name = btf__name_by_offset(btf, fn_t->name_off);
if (strcmp(name, "subprog_ctx_tag") == 0) {
/* int subprog_ctx_tag(void *ctx __arg_ctx) */
if (!ASSERT_EQ(btf_vlen(proto_t), 1, "arg_cnt"))
goto out;
/* arg 0 is PTR -> STRUCT bpf_perf_event_data */
check_ctx_arg_type(btf, &btf_params(proto_t)[0]);
} else if (strcmp(name, "subprog_multi_ctx_tags") == 0) {
/* int subprog_multi_ctx_tags(void *ctx1 __arg_ctx,
* struct my_struct *mem,
* void *ctx2 __arg_ctx)
*/
if (!ASSERT_EQ(btf_vlen(proto_t), 3, "arg_cnt"))
goto out;
/* arg 0 is PTR -> STRUCT bpf_perf_event_data */
check_ctx_arg_type(btf, &btf_params(proto_t)[0]);
/* arg 2 is PTR -> STRUCT bpf_perf_event_data */
check_ctx_arg_type(btf, &btf_params(proto_t)[2]);
} else {
ASSERT_FAIL("unexpected subprog %s", name);
goto out;
}
}
out:
btf__free(btf);
test_global_func_ctx_args__destroy(skel);
}
void test_test_global_funcs(void)
{
RUN_TESTS(test_global_func1);
......@@ -40,4 +143,7 @@ void test_test_global_funcs(void)
RUN_TESTS(test_global_func16);
RUN_TESTS(test_global_func17);
RUN_TESTS(test_global_func_ctx_args);
if (test__start_subtest("ctx_arg_rewrite"))
subtest_ctx_arg_rewrite();
}
......@@ -102,3 +102,52 @@ int perf_event_ctx(void *ctx)
{
return perf_event_ctx_subprog(ctx);
}
/* this global subprog can be now called from many types of entry progs, each
* with different context type
*/
__weak int subprog_ctx_tag(void *ctx __arg_ctx)
{
return bpf_get_stack(ctx, stack, sizeof(stack), 0);
}
struct my_struct { int x; };
__weak int subprog_multi_ctx_tags(void *ctx1 __arg_ctx,
struct my_struct *mem,
void *ctx2 __arg_ctx)
{
if (!mem)
return 0;
return bpf_get_stack(ctx1, stack, sizeof(stack), 0) +
mem->x +
bpf_get_stack(ctx2, stack, sizeof(stack), 0);
}
SEC("?raw_tp")
__success __log_level(2)
int arg_tag_ctx_raw_tp(void *ctx)
{
struct my_struct x = { .x = 123 };
return subprog_ctx_tag(ctx) + subprog_multi_ctx_tags(ctx, &x, ctx);
}
SEC("?perf_event")
__success __log_level(2)
int arg_tag_ctx_perf(void *ctx)
{
struct my_struct x = { .x = 123 };
return subprog_ctx_tag(ctx) + subprog_multi_ctx_tags(ctx, &x, ctx);
}
SEC("?kprobe")
__success __log_level(2)
int arg_tag_ctx_kprobe(void *ctx)
{
struct my_struct x = { .x = 123 };
return subprog_ctx_tag(ctx) + subprog_multi_ctx_tags(ctx, &x, ctx);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment