Add a small tailcall target/probe pair and a tailcalls subtest that
attaches fentry to the callee program.

The test first runs the callee directly and checks that fentry fires
exactly once, then runs the entry program, tail-calls into the callee
and checks that the same fentry also fires exactly once on the tail-call
path.

This covers both sides of the x86 change: direct entry must not
double-fire, while tail-called entry must now be observable.

Signed-off-by: Takeru Hayasaka <[email protected]>
---
 .../selftests/bpf/prog_tests/tailcalls.c      | 110 ++++++++++++++++++
 .../bpf/progs/tailcall_fentry_probe.c         |  16 +++
 .../bpf/progs/tailcall_fentry_target.c        |  27 +++++
 3 files changed, 153 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/tailcall_fentry_probe.c
 create mode 100644 tools/testing/selftests/bpf/progs/tailcall_fentry_target.c

diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c 
b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
index 7d534fde0af9..ac05df65b666 100644
--- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c
+++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
@@ -1113,6 +1113,114 @@ static void test_tailcall_bpf2bpf_fentry_entry(void)
        bpf_object__close(tgt_obj);
 }
 
+static void test_tailcall_fentry_tailcallee(void)
+{
+       struct bpf_object *tgt_obj = NULL, *fentry_obj = NULL;
+       struct bpf_map *prog_array, *data_map;
+       struct bpf_link *fentry_link = NULL;
+       struct bpf_program *prog;
+       int err, map_fd, callee_fd, main_fd, data_fd, i, val;
+       char buff[128] = {};
+
+       LIBBPF_OPTS(bpf_test_run_opts, topts,
+                   .data_in = buff,
+                   .data_size_in = sizeof(buff),
+                   .repeat = 1,
+       );
+
+       err = bpf_prog_test_load("tailcall_fentry_target.bpf.o",
+                                BPF_PROG_TYPE_SCHED_CLS,
+                                &tgt_obj, &main_fd);
+       if (!ASSERT_OK(err, "load tgt_obj"))
+               return;
+
+       prog_array = bpf_object__find_map_by_name(tgt_obj, "jmp_table");
+       if (!ASSERT_OK_PTR(prog_array, "find jmp_table map"))
+               goto out;
+
+       map_fd = bpf_map__fd(prog_array);
+       if (!ASSERT_FALSE(map_fd < 0, "find jmp_table map fd"))
+               goto out;
+
+       prog = bpf_object__find_program_by_name(tgt_obj, "entry");
+       if (!ASSERT_OK_PTR(prog, "find entry prog"))
+               goto out;
+
+       main_fd = bpf_program__fd(prog);
+       if (!ASSERT_FALSE(main_fd < 0, "find entry prog fd"))
+               goto out;
+
+       prog = bpf_object__find_program_by_name(tgt_obj, "callee");
+       if (!ASSERT_OK_PTR(prog, "find callee prog"))
+               goto out;
+
+       callee_fd = bpf_program__fd(prog);
+       if (!ASSERT_FALSE(callee_fd < 0, "find callee prog fd"))
+               goto out;
+
+       i = 0;
+       err = bpf_map_update_elem(map_fd, &i, &callee_fd, BPF_ANY);
+       if (!ASSERT_OK(err, "update jmp_table"))
+               goto out;
+
+       fentry_obj = bpf_object__open_file("tailcall_fentry_probe.bpf.o", NULL);
+       if (!ASSERT_OK_PTR(fentry_obj, "open fentry_obj file"))
+               goto out;
+
+       prog = bpf_object__find_program_by_name(fentry_obj, "fentry_callee");
+       if (!ASSERT_OK_PTR(prog, "find fentry prog"))
+               goto out;
+
+       err = bpf_program__set_attach_target(prog, callee_fd, "callee");
+       if (!ASSERT_OK(err, "set_attach_target callee"))
+               goto out;
+
+       err = bpf_object__load(fentry_obj);
+       if (!ASSERT_OK(err, "load fentry_obj"))
+               goto out;
+
+       fentry_link = bpf_program__attach_trace(prog);
+       if (!ASSERT_OK_PTR(fentry_link, "attach_trace"))
+               goto out;
+
+       data_map = bpf_object__find_map_by_name(fentry_obj, ".bss");
+       if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map),
+                         "find tailcall_fentry_probe.bss map"))
+               goto out;
+
+       data_fd = bpf_map__fd(data_map);
+       if (!ASSERT_FALSE(data_fd < 0,
+                         "find tailcall_fentry_probe.bss map fd"))
+               goto out;
+
+       err = bpf_prog_test_run_opts(callee_fd, &topts);
+       ASSERT_OK(err, "direct callee");
+       ASSERT_EQ(topts.retval, 7, "direct callee retval");
+
+       i = 0;
+       err = bpf_map_lookup_elem(data_fd, &i, &val);
+       ASSERT_OK(err, "direct fentry count");
+       ASSERT_EQ(val, 1, "direct fentry count");
+
+       val = 0;
+       err = bpf_map_update_elem(data_fd, &i, &val, BPF_ANY);
+       ASSERT_OK(err, "reset fentry count");
+
+       err = bpf_prog_test_run_opts(main_fd, &topts);
+       ASSERT_OK(err, "tailcall");
+       ASSERT_EQ(topts.retval, 7, "tailcall retval");
+
+       i = 0;
+       err = bpf_map_lookup_elem(data_fd, &i, &val);
+       ASSERT_OK(err, "fentry count");
+       ASSERT_EQ(val, 1, "fentry count");
+
+out:
+       bpf_link__destroy(fentry_link);
+       bpf_object__close(fentry_obj);
+       bpf_object__close(tgt_obj);
+}
+
 #define JMP_TABLE "/sys/fs/bpf/jmp_table"
 
 static int poke_thread_exit;
@@ -1759,6 +1867,8 @@ void test_tailcalls(void)
                test_tailcall_bpf2bpf_fentry_fexit();
        if (test__start_subtest("tailcall_bpf2bpf_fentry_entry"))
                test_tailcall_bpf2bpf_fentry_entry();
+       if (test__start_subtest("tailcall_fentry_tailcallee"))
+               test_tailcall_fentry_tailcallee();
        if (test__start_subtest("tailcall_poke"))
                test_tailcall_poke();
        if (test__start_subtest("tailcall_bpf2bpf_hierarchy_1"))
diff --git a/tools/testing/selftests/bpf/progs/tailcall_fentry_probe.c 
b/tools/testing/selftests/bpf/progs/tailcall_fentry_probe.c
new file mode 100644
index 000000000000..b784aeffb316
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tailcall_fentry_probe.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+int count = 0;
+
+SEC("fentry/callee")
+int BPF_PROG(fentry_callee, struct sk_buff *skb)
+{
+       count++;
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/tailcall_fentry_target.c 
b/tools/testing/selftests/bpf/progs/tailcall_fentry_target.c
new file mode 100644
index 000000000000..46da06ce323c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tailcall_fentry_target.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+
+#include <bpf/bpf_helpers.h>
+#include "bpf_legacy.h"
+
+struct {
+       __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+       __uint(max_entries, 1);
+       __uint(key_size, sizeof(__u32));
+       __uint(value_size, sizeof(__u32));
+} jmp_table SEC(".maps");
+
+SEC("tc")
+int callee(struct __sk_buff *skb)
+{
+       return 7;
+}
+
+SEC("tc")
+int entry(struct __sk_buff *skb)
+{
+       bpf_tail_call_static(skb, &jmp_table, 0);
+       return 0;
+}
+
+char __license[] SEC("license") = "GPL";
-- 
2.43.0


Reply via email to