selftests/bpf: Add notion of auxiliary programs for test_loader
In order to express test cases that use bpf_tail_call() intrinsic it is necessary to have several programs to be loaded at a time. This commit adds __auxiliary annotation to the set of annotations supported by test_loader.c. Programs marked as auxiliary are always loaded but are not treated as a separate test. For example: void dummy_prog1(void); struct { __uint(type, BPF_MAP_TYPE_PROG_ARRAY); __uint(max_entries, 4); __uint(key_size, sizeof(int)); __array(values, void (void)); } prog_map SEC(".maps") = { .values = { [0] = (void *) &dummy_prog1, }, }; SEC("tc") __auxiliary __naked void dummy_prog1(void) { asm volatile ("r0 = 42; exit;"); } SEC("tc") __description("reference tracking: check reference or tail call") __success __retval(0) __naked void check_reference_or_tail_call(void) { asm volatile ( "r2 = %[prog_map] ll;" "r3 = 0;" "call %[bpf_tail_call];" "r0 = 0;" "exit;" :: __imm(bpf_tail_call), : __clobber_all); } Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20230421174234.2391278-2-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
d7a799ec78
commit
63bb645b9d
@ -53,6 +53,10 @@
|
||||
* - A numeric value.
|
||||
* Multiple __flag attributes could be specified, the final flags
|
||||
* value is derived by applying binary "or" to all specified values.
|
||||
*
|
||||
* __auxiliary Annotated program is not a separate test, but used as auxiliary
|
||||
* for some other test cases and should always be loaded.
|
||||
* __auxiliary_unpriv Same, but load program in unprivileged mode.
|
||||
*/
|
||||
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg)))
|
||||
#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
|
||||
@ -65,6 +69,8 @@
|
||||
#define __flag(flag) __attribute__((btf_decl_tag("comment:test_prog_flags="#flag)))
|
||||
#define __retval(val) __attribute__((btf_decl_tag("comment:test_retval="#val)))
|
||||
#define __retval_unpriv(val) __attribute__((btf_decl_tag("comment:test_retval_unpriv="#val)))
|
||||
#define __auxiliary __attribute__((btf_decl_tag("comment:test_auxiliary")))
|
||||
#define __auxiliary_unpriv __attribute__((btf_decl_tag("comment:test_auxiliary_unpriv")))
|
||||
|
||||
/* Convenience macro for use with 'asm volatile' blocks */
|
||||
#define __naked __attribute__((naked))
|
||||
|
@ -25,6 +25,8 @@
|
||||
#define TEST_TAG_DESCRIPTION_PFX "comment:test_description="
|
||||
#define TEST_TAG_RETVAL_PFX "comment:test_retval="
|
||||
#define TEST_TAG_RETVAL_PFX_UNPRIV "comment:test_retval_unpriv="
|
||||
#define TEST_TAG_AUXILIARY "comment:test_auxiliary"
|
||||
#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv"
|
||||
|
||||
/* Warning: duplicated in bpf_misc.h */
|
||||
#define POINTER_VALUE 0xcafe4all
|
||||
@ -59,6 +61,8 @@ struct test_spec {
|
||||
int log_level;
|
||||
int prog_flags;
|
||||
int mode_mask;
|
||||
bool auxiliary;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
static int tester_init(struct test_loader *tester)
|
||||
@ -87,6 +91,11 @@ static void free_test_spec(struct test_spec *spec)
|
||||
free(spec->unpriv.name);
|
||||
free(spec->priv.expect_msgs);
|
||||
free(spec->unpriv.expect_msgs);
|
||||
|
||||
spec->priv.name = NULL;
|
||||
spec->unpriv.name = NULL;
|
||||
spec->priv.expect_msgs = NULL;
|
||||
spec->unpriv.expect_msgs = NULL;
|
||||
}
|
||||
|
||||
static int push_msg(const char *msg, struct test_subspec *subspec)
|
||||
@ -204,6 +213,12 @@ static int parse_test_spec(struct test_loader *tester,
|
||||
spec->unpriv.expect_failure = false;
|
||||
spec->mode_mask |= UNPRIV;
|
||||
has_unpriv_result = true;
|
||||
} else if (strcmp(s, TEST_TAG_AUXILIARY) == 0) {
|
||||
spec->auxiliary = true;
|
||||
spec->mode_mask |= PRIV;
|
||||
} else if (strcmp(s, TEST_TAG_AUXILIARY_UNPRIV) == 0) {
|
||||
spec->auxiliary = true;
|
||||
spec->mode_mask |= UNPRIV;
|
||||
} else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) {
|
||||
msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1;
|
||||
err = push_msg(msg, &spec->priv);
|
||||
@ -314,6 +329,8 @@ static int parse_test_spec(struct test_loader *tester,
|
||||
}
|
||||
}
|
||||
|
||||
spec->valid = true;
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
@ -516,16 +533,18 @@ void run_subtest(struct test_loader *tester,
|
||||
struct bpf_object_open_opts *open_opts,
|
||||
const void *obj_bytes,
|
||||
size_t obj_byte_cnt,
|
||||
struct test_spec *specs,
|
||||
struct test_spec *spec,
|
||||
bool unpriv)
|
||||
{
|
||||
struct test_subspec *subspec = unpriv ? &spec->unpriv : &spec->priv;
|
||||
struct bpf_program *tprog, *tprog_iter;
|
||||
struct test_spec *spec_iter;
|
||||
struct cap_state caps = {};
|
||||
struct bpf_program *tprog;
|
||||
struct bpf_object *tobj;
|
||||
struct bpf_map *map;
|
||||
int retval;
|
||||
int err;
|
||||
int retval, err, i;
|
||||
bool should_load;
|
||||
|
||||
if (!test__start_subtest(subspec->name))
|
||||
return;
|
||||
@ -546,15 +565,23 @@ void run_subtest(struct test_loader *tester,
|
||||
if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */
|
||||
goto subtest_cleanup;
|
||||
|
||||
bpf_object__for_each_program(tprog, tobj)
|
||||
bpf_program__set_autoload(tprog, false);
|
||||
i = 0;
|
||||
bpf_object__for_each_program(tprog_iter, tobj) {
|
||||
spec_iter = &specs[i++];
|
||||
should_load = false;
|
||||
|
||||
bpf_object__for_each_program(tprog, tobj) {
|
||||
/* only load specified program */
|
||||
if (strcmp(bpf_program__name(tprog), spec->prog_name) == 0) {
|
||||
bpf_program__set_autoload(tprog, true);
|
||||
break;
|
||||
if (spec_iter->valid) {
|
||||
if (strcmp(bpf_program__name(tprog_iter), spec->prog_name) == 0) {
|
||||
tprog = tprog_iter;
|
||||
should_load = true;
|
||||
}
|
||||
|
||||
if (spec_iter->auxiliary &&
|
||||
spec_iter->mode_mask & (unpriv ? UNPRIV : PRIV))
|
||||
should_load = true;
|
||||
}
|
||||
|
||||
bpf_program__set_autoload(tprog_iter, should_load);
|
||||
}
|
||||
|
||||
prepare_case(tester, spec, tobj, tprog);
|
||||
@ -617,11 +644,12 @@ static void process_subtest(struct test_loader *tester,
|
||||
skel_elf_bytes_fn elf_bytes_factory)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_object_open_opts, open_opts, .object_name = skel_name);
|
||||
struct test_spec *specs = NULL;
|
||||
struct bpf_object *obj = NULL;
|
||||
struct bpf_program *prog;
|
||||
const void *obj_bytes;
|
||||
int err, i, nr_progs;
|
||||
size_t obj_byte_cnt;
|
||||
int err;
|
||||
|
||||
if (tester_init(tester) < 0)
|
||||
return; /* failed to initialize tester */
|
||||
@ -631,25 +659,42 @@ static void process_subtest(struct test_loader *tester,
|
||||
if (!ASSERT_OK_PTR(obj, "obj_open_mem"))
|
||||
return;
|
||||
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
struct test_spec spec;
|
||||
nr_progs = 0;
|
||||
bpf_object__for_each_program(prog, obj)
|
||||
++nr_progs;
|
||||
|
||||
/* if we can't derive test specification, go to the next test */
|
||||
err = parse_test_spec(tester, obj, prog, &spec);
|
||||
if (err) {
|
||||
specs = calloc(nr_progs, sizeof(struct test_spec));
|
||||
if (!ASSERT_OK_PTR(specs, "Can't alloc specs array"))
|
||||
return;
|
||||
|
||||
i = 0;
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
/* ignore tests for which we can't derive test specification */
|
||||
err = parse_test_spec(tester, obj, prog, &specs[i++]);
|
||||
if (err)
|
||||
PRINT_FAIL("Can't parse test spec for program '%s'\n",
|
||||
bpf_program__name(prog));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spec.mode_mask & PRIV)
|
||||
run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt, &spec, false);
|
||||
if (spec.mode_mask & UNPRIV)
|
||||
run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt, &spec, true);
|
||||
|
||||
free_test_spec(&spec);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
struct test_spec *spec = &specs[i++];
|
||||
|
||||
if (!spec->valid || spec->auxiliary)
|
||||
continue;
|
||||
|
||||
if (spec->mode_mask & PRIV)
|
||||
run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt,
|
||||
specs, spec, false);
|
||||
if (spec->mode_mask & UNPRIV)
|
||||
run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt,
|
||||
specs, spec, true);
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_progs; ++i)
|
||||
free_test_spec(&specs[i]);
|
||||
free(specs);
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user