selftests/bpf: implement and test custom testmod_seq iterator
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 <andrii@kernel.org> Link: https://lore.kernel.org/r/20230308184121.1165081-9-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
f59b146092
commit
7e86a8c4ac
@ -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 (?)
|
||||
|
@ -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;
|
||||
};
|
||||
@ -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)
|
||||
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
@ -53,12 +54,53 @@ cleanup:
|
||||
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
tools/testing/selftests/bpf/progs/iters_testmod_seq.c
Normal file
79
tools/testing/selftests/bpf/progs/iters_testmod_seq.c
Normal file
@ -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";
|
Loading…
x
Reference in New Issue
Block a user