Commit 63bb645b authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Alexei Starovoitov

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: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20230421174234.2391278-2-eddyz87@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent d7a799ec
......@@ -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;
if (spec_iter->valid) {
if (strcmp(bpf_program__name(tprog_iter), spec->prog_name) == 0) {
tprog = tprog_iter;
should_load = true;
}
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->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;
specs = calloc(nr_progs, sizeof(struct test_spec));
if (!ASSERT_OK_PTR(specs, "Can't alloc specs array"))
return;
/* if we can't derive test specification, go to the next test */
err = parse_test_spec(tester, obj, prog, &spec);
if (err) {
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));
}
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, &spec, false);
if (spec.mode_mask & UNPRIV)
run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt, &spec, true);
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);
free_test_spec(&spec);
}
for (i = 0; i < nr_progs; ++i)
free_test_spec(&specs[i]);
free(specs);
bpf_object__close(obj);
}
......
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