Skip to content

Commit

Permalink
selftests/bpf: implement and test custom testmod_seq iterator
Browse files Browse the repository at this point in the history
Implement a trivial iterator returning same specified integer value
N times as part of bpf_testmod kernel module. Add selftests to validate
everything works end to end.

We also reuse these tests as "verification-only" tests to validate that
kernel prints the state of custom kernel module-defined iterator correctly:

  fp-16=iter_testmod_seq(ref_id=1,state=drained,depth=0)

"testmod_seq" part is an iterator type, and is coming from module's BTF
data dynamically at runtime.

Signed-off-by: Andrii Nakryiko <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Alexei Starovoitov <[email protected]>
  • Loading branch information
anakryiko authored and Alexei Starovoitov committed Mar 9, 2023
1 parent f59b146 commit 7e86a8c
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 1 deletion.
1 change: 1 addition & 0 deletions tools/testing/selftests/bpf/DENYLIST.s390x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dynptr/test_dynptr_skb_data
dynptr/test_skb_readonly
fexit_sleep # fexit_skel_load fexit skeleton failed (trampoline)
get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace)
iters/testmod_seq* # s390x doesn't support kfuncs in modules yet
kprobe_multi_bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95
kprobe_multi_test # relies on fentry
ksyms_module # test_ksyms_module__open_and_load unexpected error: -9 (?)
Expand Down
42 changes: 41 additions & 1 deletion tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ bpf_testmod_test_mod_kfunc(int i)
*(int *)this_cpu_ptr(&bpf_testmod_ksym_percpu) = i;
}

__bpf_kfunc int bpf_iter_testmod_seq_new(struct bpf_iter_testmod_seq *it, s64 value, int cnt)
{
if (cnt < 0) {
it->cnt = 0;
return -EINVAL;
}

it->value = value;
it->cnt = cnt;

return 0;
}

__bpf_kfunc s64 *bpf_iter_testmod_seq_next(struct bpf_iter_testmod_seq* it)
{
if (it->cnt <= 0)
return NULL;

it->cnt--;

return &it->value;
}

__bpf_kfunc void bpf_iter_testmod_seq_destroy(struct bpf_iter_testmod_seq *it)
{
it->cnt = 0;
}

struct bpf_testmod_btf_type_tag_1 {
int a;
};
Expand Down Expand Up @@ -220,6 +248,17 @@ static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = {
.write = bpf_testmod_test_write,
};

BTF_SET8_START(bpf_testmod_common_kfunc_ids)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_new, KF_ITER_NEW)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY)
BTF_SET8_END(bpf_testmod_common_kfunc_ids)

static const struct btf_kfunc_id_set bpf_testmod_common_kfunc_set = {
.owner = THIS_MODULE,
.set = &bpf_testmod_common_kfunc_ids,
};

BTF_SET8_START(bpf_testmod_check_kfunc_ids)
BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
BTF_SET8_END(bpf_testmod_check_kfunc_ids)
Expand All @@ -235,7 +274,8 @@ static int bpf_testmod_init(void)
{
int ret;

ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_testmod_kfunc_set);
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_testmod_common_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_testmod_kfunc_set);
if (ret < 0)
return ret;
if (bpf_fentry_test1(0) < 0)
Expand Down
6 changes: 6 additions & 0 deletions tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ struct bpf_testmod_test_writable_ctx {
int val;
};

/* BPF iter that returns *value* *n* times in a row */
struct bpf_iter_testmod_seq {
s64 value;
int cnt;
};

#endif /* _BPF_TESTMOD_H */
42 changes: 42 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/iters.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "iters_state_safety.skel.h"
#include "iters_looping.skel.h"
#include "iters_num.skel.h"
#include "iters_testmod_seq.skel.h"

static void subtest_num_iters(void)
{
Expand Down Expand Up @@ -53,12 +54,53 @@ static void subtest_num_iters(void)
iters_num__destroy(skel);
}

static void subtest_testmod_seq_iters(void)
{
struct iters_testmod_seq *skel;
int err;

if (!env.has_testmod) {
test__skip();
return;
}

skel = iters_testmod_seq__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
return;

err = iters_testmod_seq__attach(skel);
if (!ASSERT_OK(err, "skel_attach"))
goto cleanup;

usleep(1);
iters_testmod_seq__detach(skel);

#define VALIDATE_CASE(case_name) \
ASSERT_EQ(skel->bss->res_##case_name, \
skel->rodata->exp_##case_name, \
#case_name)

VALIDATE_CASE(empty);
VALIDATE_CASE(full);
VALIDATE_CASE(truncated);

#undef VALIDATE_CASE

cleanup:
iters_testmod_seq__destroy(skel);
}

void test_iters(void)
{
RUN_TESTS(iters_state_safety);
RUN_TESTS(iters_looping);
RUN_TESTS(iters);

if (env.has_testmod)
RUN_TESTS(iters_testmod_seq);

if (test__start_subtest("num"))
subtest_num_iters();
if (test__start_subtest("testmod_seq"))
subtest_testmod_seq_iters();
}
79 changes: 79 additions & 0 deletions tools/testing/selftests/bpf/progs/iters_testmod_seq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"

struct bpf_iter_testmod_seq {
u64 :64;
u64 :64;
};

extern int bpf_iter_testmod_seq_new(struct bpf_iter_testmod_seq *it, s64 value, int cnt) __ksym;
extern s64 *bpf_iter_testmod_seq_next(struct bpf_iter_testmod_seq *it) __ksym;
extern void bpf_iter_testmod_seq_destroy(struct bpf_iter_testmod_seq *it) __ksym;

const volatile __s64 exp_empty = 0 + 1;
__s64 res_empty;

SEC("raw_tp/sys_enter")
__success __log_level(2)
__msg("fp-16_w=iter_testmod_seq(ref_id=1,state=active,depth=0)")
__msg("fp-16=iter_testmod_seq(ref_id=1,state=drained,depth=0)")
__msg("call bpf_iter_testmod_seq_destroy")
int testmod_seq_empty(const void *ctx)
{
__s64 sum = 0, *i;

bpf_for_each(testmod_seq, i, 1000, 0) sum += *i;
res_empty = 1 + sum;

return 0;
}

const volatile __s64 exp_full = 1000000;
__s64 res_full;

SEC("raw_tp/sys_enter")
__success __log_level(2)
__msg("fp-16_w=iter_testmod_seq(ref_id=1,state=active,depth=0)")
__msg("fp-16=iter_testmod_seq(ref_id=1,state=drained,depth=0)")
__msg("call bpf_iter_testmod_seq_destroy")
int testmod_seq_full(const void *ctx)
{
__s64 sum = 0, *i;

bpf_for_each(testmod_seq, i, 1000, 1000) sum += *i;
res_full = sum;

return 0;
}

const volatile __s64 exp_truncated = 10 * 1000000;
__s64 res_truncated;

static volatile int zero = 0;

SEC("raw_tp/sys_enter")
__success __log_level(2)
__msg("fp-16_w=iter_testmod_seq(ref_id=1,state=active,depth=0)")
__msg("fp-16=iter_testmod_seq(ref_id=1,state=drained,depth=0)")
__msg("call bpf_iter_testmod_seq_destroy")
int testmod_seq_truncated(const void *ctx)
{
__s64 sum = 0, *i;
int cnt = zero;

bpf_for_each(testmod_seq, i, 10, 2000000) {
sum += *i;
cnt++;
if (cnt >= 1000000)
break;
}
res_truncated = sum;

return 0;
}

char _license[] SEC("license") = "GPL";

0 comments on commit 7e86a8c

Please sign in to comment.