1227 lines
32 KiB
C
1227 lines
32 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <unistd.h>
|
|
#include <test_progs.h>
|
|
#include <network_helpers.h>
|
|
#include "tailcall_poke.skel.h"
|
|
|
|
|
|
/* test_tailcall_1 checks basic functionality by patching multiple locations
|
|
* in a single program for a single tail call slot with nop->jmp, jmp->nop
|
|
* and jmp->jmp rewrites. Also checks for nop->nop.
|
|
*/
|
|
static void test_tailcall_1(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, i, j;
|
|
struct bpf_map *prog_array;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
char prog_name[32];
|
|
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("tailcall1.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
|
|
&prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, i, "tailcall retval");
|
|
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 3, "tailcall retval");
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_OK(topts.retval, "tailcall retval");
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
j = bpf_map__max_entries(prog_array) - 1 - i;
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", j);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
j = bpf_map__max_entries(prog_array) - 1 - i;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, j, "tailcall retval");
|
|
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 3, "tailcall retval");
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err >= 0 || errno != ENOENT))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 3, "tailcall retval");
|
|
}
|
|
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
/* test_tailcall_2 checks that patching multiple programs for a single
|
|
* tail call slot works. It also jumps through several programs and tests
|
|
* the tail call limit counter.
|
|
*/
|
|
static void test_tailcall_2(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, i;
|
|
struct bpf_map *prog_array;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
char prog_name[32];
|
|
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("tailcall2.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
|
|
&prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 2, "tailcall retval");
|
|
|
|
i = 2;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 1, "tailcall retval");
|
|
|
|
i = 0;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 3, "tailcall retval");
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
static void test_tailcall_count(const char *which, bool test_fentry,
|
|
bool test_fexit)
|
|
{
|
|
struct bpf_object *obj = NULL, *fentry_obj = NULL, *fexit_obj = NULL;
|
|
struct bpf_link *fentry_link = NULL, *fexit_link = NULL;
|
|
int err, map_fd, prog_fd, main_fd, data_fd, i, val;
|
|
struct bpf_map *prog_array, *data_map;
|
|
struct bpf_program *prog;
|
|
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(which, BPF_PROG_TYPE_SCHED_CLS, &obj,
|
|
&prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "classifier_0");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
if (test_fentry) {
|
|
fentry_obj = bpf_object__open_file("tailcall_bpf2bpf_fentry.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");
|
|
if (!ASSERT_OK_PTR(prog, "find fentry prog"))
|
|
goto out;
|
|
|
|
err = bpf_program__set_attach_target(prog, prog_fd,
|
|
"subprog_tail");
|
|
if (!ASSERT_OK(err, "set_attach_target subprog_tail"))
|
|
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;
|
|
}
|
|
|
|
if (test_fexit) {
|
|
fexit_obj = bpf_object__open_file("tailcall_bpf2bpf_fexit.bpf.o",
|
|
NULL);
|
|
if (!ASSERT_OK_PTR(fexit_obj, "open fexit_obj file"))
|
|
goto out;
|
|
|
|
prog = bpf_object__find_program_by_name(fexit_obj, "fexit");
|
|
if (!ASSERT_OK_PTR(prog, "find fexit prog"))
|
|
goto out;
|
|
|
|
err = bpf_program__set_attach_target(prog, prog_fd,
|
|
"subprog_tail");
|
|
if (!ASSERT_OK(err, "set_attach_target subprog_tail"))
|
|
goto out;
|
|
|
|
err = bpf_object__load(fexit_obj);
|
|
if (!ASSERT_OK(err, "load fexit_obj"))
|
|
goto out;
|
|
|
|
fexit_link = bpf_program__attach_trace(prog);
|
|
if (!ASSERT_OK_PTR(fexit_link, "attach_trace"))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 1, "tailcall retval");
|
|
|
|
data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
|
|
if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (CHECK_FAIL(data_fd < 0))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "tailcall count");
|
|
ASSERT_EQ(val, 33, "tailcall count");
|
|
|
|
if (test_fentry) {
|
|
data_map = bpf_object__find_map_by_name(fentry_obj, ".bss");
|
|
if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map),
|
|
"find tailcall_bpf2bpf_fentry.bss map"))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (!ASSERT_FALSE(data_fd < 0,
|
|
"find tailcall_bpf2bpf_fentry.bss map fd"))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "fentry count");
|
|
ASSERT_EQ(val, 33, "fentry count");
|
|
}
|
|
|
|
if (test_fexit) {
|
|
data_map = bpf_object__find_map_by_name(fexit_obj, ".bss");
|
|
if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map),
|
|
"find tailcall_bpf2bpf_fexit.bss map"))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (!ASSERT_FALSE(data_fd < 0,
|
|
"find tailcall_bpf2bpf_fexit.bss map fd"))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "fexit count");
|
|
ASSERT_EQ(val, 33, "fexit count");
|
|
}
|
|
|
|
i = 0;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_OK(topts.retval, "tailcall retval");
|
|
out:
|
|
bpf_link__destroy(fentry_link);
|
|
bpf_link__destroy(fexit_link);
|
|
bpf_object__close(fentry_obj);
|
|
bpf_object__close(fexit_obj);
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
/* test_tailcall_3 checks that the count value of the tail call limit
|
|
* enforcement matches with expectations. JIT uses direct jump.
|
|
*/
|
|
static void test_tailcall_3(void)
|
|
{
|
|
test_tailcall_count("tailcall3.bpf.o", false, false);
|
|
}
|
|
|
|
/* test_tailcall_6 checks that the count value of the tail call limit
|
|
* enforcement matches with expectations. JIT uses indirect jump.
|
|
*/
|
|
static void test_tailcall_6(void)
|
|
{
|
|
test_tailcall_count("tailcall6.bpf.o", false, false);
|
|
}
|
|
|
|
/* test_tailcall_4 checks that the kernel properly selects indirect jump
|
|
* for the case where the key is not known. Latter is passed via global
|
|
* data to select different targets we can compare return value of.
|
|
*/
|
|
static void test_tailcall_4(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, data_fd, i;
|
|
struct bpf_map *prog_array, *data_map;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
static const int zero = 0;
|
|
char buff[128] = {};
|
|
char prog_name[32];
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
.data_in = buff,
|
|
.data_size_in = sizeof(buff),
|
|
.repeat = 1,
|
|
);
|
|
|
|
err = bpf_prog_test_load("tailcall4.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
|
|
&prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
|
|
if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (CHECK_FAIL(data_fd < 0))
|
|
goto out;
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, i, "tailcall retval");
|
|
}
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 3, "tailcall retval");
|
|
}
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
/* test_tailcall_5 probes similarly to test_tailcall_4 that the kernel generates
|
|
* an indirect jump when the keys are const but different from different branches.
|
|
*/
|
|
static void test_tailcall_5(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, data_fd, i, key[] = { 1111, 1234, 5678 };
|
|
struct bpf_map *prog_array, *data_map;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
static const int zero = 0;
|
|
char buff[128] = {};
|
|
char prog_name[32];
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
.data_in = buff,
|
|
.data_size_in = sizeof(buff),
|
|
.repeat = 1,
|
|
);
|
|
|
|
err = bpf_prog_test_load("tailcall5.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
|
|
&prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
|
|
if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (CHECK_FAIL(data_fd < 0))
|
|
goto out;
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, i, "tailcall retval");
|
|
}
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 3, "tailcall retval");
|
|
}
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_1 purpose is to make sure that tailcalls are working
|
|
* correctly in correlation with BPF subprograms
|
|
*/
|
|
static void test_tailcall_bpf2bpf_1(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, i;
|
|
struct bpf_map *prog_array;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
char prog_name[32];
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
.data_in = &pkt_v4,
|
|
.data_size_in = sizeof(pkt_v4),
|
|
.repeat = 1,
|
|
);
|
|
|
|
err = bpf_prog_test_load("tailcall_bpf2bpf1.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
|
|
&obj, &prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
/* nop -> jmp */
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 1, "tailcall retval");
|
|
|
|
/* jmp -> nop, call subprog that will do tailcall */
|
|
i = 1;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_OK(topts.retval, "tailcall retval");
|
|
|
|
/* make sure that subprog can access ctx and entry prog that
|
|
* called this subprog can properly return
|
|
*/
|
|
i = 0;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval");
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_2 checks that the count value of the tail call limit
|
|
* enforcement matches with expectations when tailcall is preceded with
|
|
* bpf2bpf call.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_2(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, data_fd, i, val;
|
|
struct bpf_map *prog_array, *data_map;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
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_bpf2bpf2.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
|
|
&obj, &prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "classifier_0");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 1, "tailcall retval");
|
|
|
|
data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
|
|
if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (CHECK_FAIL(data_fd < 0))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "tailcall count");
|
|
ASSERT_EQ(val, 33, "tailcall count");
|
|
|
|
i = 0;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_OK(topts.retval, "tailcall retval");
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_3 checks that non-trivial amount of stack (up to
|
|
* 256 bytes) can be used within bpf subprograms that have the tailcalls
|
|
* in them
|
|
*/
|
|
static void test_tailcall_bpf2bpf_3(void)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, i;
|
|
struct bpf_map *prog_array;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
char prog_name[32];
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
.data_in = &pkt_v4,
|
|
.data_size_in = sizeof(pkt_v4),
|
|
.repeat = 1,
|
|
);
|
|
|
|
err = bpf_prog_test_load("tailcall_bpf2bpf3.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
|
|
&obj, &prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval");
|
|
|
|
i = 1;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, sizeof(pkt_v4), "tailcall retval");
|
|
|
|
i = 0;
|
|
err = bpf_map_delete_elem(map_fd, &i);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval");
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
#include "tailcall_bpf2bpf4.skel.h"
|
|
|
|
/* test_tailcall_bpf2bpf_4 checks that tailcall counter is correctly preserved
|
|
* across tailcalls combined with bpf2bpf calls. for making sure that tailcall
|
|
* counter behaves correctly, bpf program will go through following flow:
|
|
*
|
|
* entry -> entry_subprog -> tailcall0 -> bpf_func0 -> subprog0 ->
|
|
* -> tailcall1 -> bpf_func1 -> subprog1 -> tailcall2 -> bpf_func2 ->
|
|
* subprog2 [here bump global counter] --------^
|
|
*
|
|
* We go through first two tailcalls and start counting from the subprog2 where
|
|
* the loop begins. At the end of the test make sure that the global counter is
|
|
* equal to 31, because tailcall counter includes the first two tailcalls
|
|
* whereas global counter is incremented only on loop presented on flow above.
|
|
*
|
|
* The noise parameter is used to insert bpf_map_update calls into the logic
|
|
* to force verifier to patch instructions. This allows us to ensure jump
|
|
* logic remains correct with instruction movement.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_4(bool noise)
|
|
{
|
|
int err, map_fd, prog_fd, main_fd, data_fd, i;
|
|
struct tailcall_bpf2bpf4__bss val;
|
|
struct bpf_map *prog_array, *data_map;
|
|
struct bpf_program *prog;
|
|
struct bpf_object *obj;
|
|
char prog_name[32];
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
.data_in = &pkt_v4,
|
|
.data_size_in = sizeof(pkt_v4),
|
|
.repeat = 1,
|
|
);
|
|
|
|
err = bpf_prog_test_load("tailcall_bpf2bpf4.bpf.o", BPF_PROG_TYPE_SCHED_CLS,
|
|
&obj, &prog_fd);
|
|
if (CHECK_FAIL(err))
|
|
return;
|
|
|
|
prog = bpf_object__find_program_by_name(obj, "entry");
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
main_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(main_fd < 0))
|
|
goto out;
|
|
|
|
prog_array = bpf_object__find_map_by_name(obj, "jmp_table");
|
|
if (CHECK_FAIL(!prog_array))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(prog_array);
|
|
if (CHECK_FAIL(map_fd < 0))
|
|
goto out;
|
|
|
|
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
|
|
snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
|
|
|
|
prog = bpf_object__find_program_by_name(obj, prog_name);
|
|
if (CHECK_FAIL(!prog))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (CHECK_FAIL(prog_fd < 0))
|
|
goto out;
|
|
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
}
|
|
|
|
data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
|
|
if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (CHECK_FAIL(data_fd < 0))
|
|
goto out;
|
|
|
|
i = 0;
|
|
val.noise = noise;
|
|
val.count = 0;
|
|
err = bpf_map_update_elem(data_fd, &i, &val, BPF_ANY);
|
|
if (CHECK_FAIL(err))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval");
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "tailcall count");
|
|
ASSERT_EQ(val.count, 31, "tailcall count");
|
|
|
|
out:
|
|
bpf_object__close(obj);
|
|
}
|
|
|
|
#include "tailcall_bpf2bpf6.skel.h"
|
|
|
|
/* Tail call counting works even when there is data on stack which is
|
|
* not aligned to 8 bytes.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_6(void)
|
|
{
|
|
struct tailcall_bpf2bpf6 *obj;
|
|
int err, map_fd, prog_fd, main_fd, data_fd, i, val;
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
.data_in = &pkt_v4,
|
|
.data_size_in = sizeof(pkt_v4),
|
|
.repeat = 1,
|
|
);
|
|
|
|
obj = tailcall_bpf2bpf6__open_and_load();
|
|
if (!ASSERT_OK_PTR(obj, "open and load"))
|
|
return;
|
|
|
|
main_fd = bpf_program__fd(obj->progs.entry);
|
|
if (!ASSERT_GE(main_fd, 0, "entry prog fd"))
|
|
goto out;
|
|
|
|
map_fd = bpf_map__fd(obj->maps.jmp_table);
|
|
if (!ASSERT_GE(map_fd, 0, "jmp_table map fd"))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(obj->progs.classifier_0);
|
|
if (!ASSERT_GE(prog_fd, 0, "classifier_0 prog fd"))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (!ASSERT_OK(err, "jmp_table map update"))
|
|
goto out;
|
|
|
|
err = bpf_prog_test_run_opts(main_fd, &topts);
|
|
ASSERT_OK(err, "entry prog test run");
|
|
ASSERT_EQ(topts.retval, 0, "tailcall retval");
|
|
|
|
data_fd = bpf_map__fd(obj->maps.bss);
|
|
if (!ASSERT_GE(data_fd, 0, "bss map fd"))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "bss map lookup");
|
|
ASSERT_EQ(val, 1, "done flag is set");
|
|
|
|
out:
|
|
tailcall_bpf2bpf6__destroy(obj);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_fentry checks that the count value of the tail call
|
|
* limit enforcement matches with expectations when tailcall is preceded with
|
|
* bpf2bpf call, and the bpf2bpf call is traced by fentry.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_fentry(void)
|
|
{
|
|
test_tailcall_count("tailcall_bpf2bpf2.bpf.o", true, false);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_fexit checks that the count value of the tail call
|
|
* limit enforcement matches with expectations when tailcall is preceded with
|
|
* bpf2bpf call, and the bpf2bpf call is traced by fexit.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_fexit(void)
|
|
{
|
|
test_tailcall_count("tailcall_bpf2bpf2.bpf.o", false, true);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_fentry_fexit checks that the count value of the tail
|
|
* call limit enforcement matches with expectations when tailcall is preceded
|
|
* with bpf2bpf call, and the bpf2bpf call is traced by both fentry and fexit.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_fentry_fexit(void)
|
|
{
|
|
test_tailcall_count("tailcall_bpf2bpf2.bpf.o", true, true);
|
|
}
|
|
|
|
/* test_tailcall_bpf2bpf_fentry_entry checks that the count value of the tail
|
|
* call limit enforcement matches with expectations when tailcall is preceded
|
|
* with bpf2bpf call, and the bpf2bpf caller is traced by fentry.
|
|
*/
|
|
static void test_tailcall_bpf2bpf_fentry_entry(void)
|
|
{
|
|
struct bpf_object *tgt_obj = NULL, *fentry_obj = NULL;
|
|
int err, map_fd, prog_fd, data_fd, i, val;
|
|
struct bpf_map *prog_array, *data_map;
|
|
struct bpf_link *fentry_link = NULL;
|
|
struct bpf_program *prog;
|
|
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_bpf2bpf2.bpf.o",
|
|
BPF_PROG_TYPE_SCHED_CLS,
|
|
&tgt_obj, &prog_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, "classifier_0");
|
|
if (!ASSERT_OK_PTR(prog, "find classifier_0 prog"))
|
|
goto out;
|
|
|
|
prog_fd = bpf_program__fd(prog);
|
|
if (!ASSERT_FALSE(prog_fd < 0, "find classifier_0 prog fd"))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
|
|
if (!ASSERT_OK(err, "update jmp_table"))
|
|
goto out;
|
|
|
|
fentry_obj = bpf_object__open_file("tailcall_bpf2bpf_fentry.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");
|
|
if (!ASSERT_OK_PTR(prog, "find fentry prog"))
|
|
goto out;
|
|
|
|
err = bpf_program__set_attach_target(prog, prog_fd, "classifier_0");
|
|
if (!ASSERT_OK(err, "set_attach_target classifier_0"))
|
|
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;
|
|
|
|
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
|
ASSERT_OK(err, "tailcall");
|
|
ASSERT_EQ(topts.retval, 1, "tailcall retval");
|
|
|
|
data_map = bpf_object__find_map_by_name(tgt_obj, "tailcall.bss");
|
|
if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map),
|
|
"find tailcall.bss map"))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (!ASSERT_FALSE(data_fd < 0, "find tailcall.bss map fd"))
|
|
goto out;
|
|
|
|
i = 0;
|
|
err = bpf_map_lookup_elem(data_fd, &i, &val);
|
|
ASSERT_OK(err, "tailcall count");
|
|
ASSERT_EQ(val, 34, "tailcall count");
|
|
|
|
data_map = bpf_object__find_map_by_name(fentry_obj, ".bss");
|
|
if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map),
|
|
"find tailcall_bpf2bpf_fentry.bss map"))
|
|
goto out;
|
|
|
|
data_fd = bpf_map__fd(data_map);
|
|
if (!ASSERT_FALSE(data_fd < 0,
|
|
"find tailcall_bpf2bpf_fentry.bss map fd"))
|
|
goto out;
|
|
|
|
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;
|
|
|
|
static void *poke_update(void *arg)
|
|
{
|
|
__u32 zero = 0, prog1_fd, prog2_fd, map_fd;
|
|
struct tailcall_poke *call = arg;
|
|
|
|
map_fd = bpf_map__fd(call->maps.jmp_table);
|
|
prog1_fd = bpf_program__fd(call->progs.call1);
|
|
prog2_fd = bpf_program__fd(call->progs.call2);
|
|
|
|
while (!poke_thread_exit) {
|
|
bpf_map_update_elem(map_fd, &zero, &prog1_fd, BPF_ANY);
|
|
bpf_map_update_elem(map_fd, &zero, &prog2_fd, BPF_ANY);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* We are trying to hit prog array update during another program load
|
|
* that shares the same prog array map.
|
|
*
|
|
* For that we share the jmp_table map between two skeleton instances
|
|
* by pinning the jmp_table to same path. Then first skeleton instance
|
|
* periodically updates jmp_table in 'poke update' thread while we load
|
|
* the second skeleton instance in the main thread.
|
|
*/
|
|
static void test_tailcall_poke(void)
|
|
{
|
|
struct tailcall_poke *call, *test;
|
|
int err, cnt = 10;
|
|
pthread_t thread;
|
|
|
|
unlink(JMP_TABLE);
|
|
|
|
call = tailcall_poke__open_and_load();
|
|
if (!ASSERT_OK_PTR(call, "tailcall_poke__open"))
|
|
return;
|
|
|
|
err = bpf_map__pin(call->maps.jmp_table, JMP_TABLE);
|
|
if (!ASSERT_OK(err, "bpf_map__pin"))
|
|
goto out;
|
|
|
|
err = pthread_create(&thread, NULL, poke_update, call);
|
|
if (!ASSERT_OK(err, "new toggler"))
|
|
goto out;
|
|
|
|
while (cnt--) {
|
|
test = tailcall_poke__open();
|
|
if (!ASSERT_OK_PTR(test, "tailcall_poke__open"))
|
|
break;
|
|
|
|
err = bpf_map__set_pin_path(test->maps.jmp_table, JMP_TABLE);
|
|
if (!ASSERT_OK(err, "bpf_map__pin")) {
|
|
tailcall_poke__destroy(test);
|
|
break;
|
|
}
|
|
|
|
bpf_program__set_autoload(test->progs.test, true);
|
|
bpf_program__set_autoload(test->progs.call1, false);
|
|
bpf_program__set_autoload(test->progs.call2, false);
|
|
|
|
err = tailcall_poke__load(test);
|
|
tailcall_poke__destroy(test);
|
|
if (!ASSERT_OK(err, "tailcall_poke__load"))
|
|
break;
|
|
}
|
|
|
|
poke_thread_exit = 1;
|
|
ASSERT_OK(pthread_join(thread, NULL), "pthread_join");
|
|
|
|
out:
|
|
bpf_map__unpin(call->maps.jmp_table, JMP_TABLE);
|
|
tailcall_poke__destroy(call);
|
|
}
|
|
|
|
void test_tailcalls(void)
|
|
{
|
|
if (test__start_subtest("tailcall_1"))
|
|
test_tailcall_1();
|
|
if (test__start_subtest("tailcall_2"))
|
|
test_tailcall_2();
|
|
if (test__start_subtest("tailcall_3"))
|
|
test_tailcall_3();
|
|
if (test__start_subtest("tailcall_4"))
|
|
test_tailcall_4();
|
|
if (test__start_subtest("tailcall_5"))
|
|
test_tailcall_5();
|
|
if (test__start_subtest("tailcall_6"))
|
|
test_tailcall_6();
|
|
if (test__start_subtest("tailcall_bpf2bpf_1"))
|
|
test_tailcall_bpf2bpf_1();
|
|
if (test__start_subtest("tailcall_bpf2bpf_2"))
|
|
test_tailcall_bpf2bpf_2();
|
|
if (test__start_subtest("tailcall_bpf2bpf_3"))
|
|
test_tailcall_bpf2bpf_3();
|
|
if (test__start_subtest("tailcall_bpf2bpf_4"))
|
|
test_tailcall_bpf2bpf_4(false);
|
|
if (test__start_subtest("tailcall_bpf2bpf_5"))
|
|
test_tailcall_bpf2bpf_4(true);
|
|
if (test__start_subtest("tailcall_bpf2bpf_6"))
|
|
test_tailcall_bpf2bpf_6();
|
|
if (test__start_subtest("tailcall_bpf2bpf_fentry"))
|
|
test_tailcall_bpf2bpf_fentry();
|
|
if (test__start_subtest("tailcall_bpf2bpf_fexit"))
|
|
test_tailcall_bpf2bpf_fexit();
|
|
if (test__start_subtest("tailcall_bpf2bpf_fentry_fexit"))
|
|
test_tailcall_bpf2bpf_fentry_fexit();
|
|
if (test__start_subtest("tailcall_bpf2bpf_fentry_entry"))
|
|
test_tailcall_bpf2bpf_fentry_entry();
|
|
if (test__start_subtest("tailcall_poke"))
|
|
test_tailcall_poke();
|
|
}
|