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
|
dynptr/test_skb_readonly
|
||||||
fexit_sleep # fexit_skel_load fexit skeleton failed (trampoline)
|
fexit_sleep # fexit_skel_load fexit skeleton failed (trampoline)
|
||||||
get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace)
|
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_bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95
|
||||||
kprobe_multi_test # relies on fentry
|
kprobe_multi_test # relies on fentry
|
||||||
ksyms_module # test_ksyms_module__open_and_load unexpected error: -9 (?)
|
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;
|
*(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 {
|
struct bpf_testmod_btf_type_tag_1 {
|
||||||
int a;
|
int a;
|
||||||
};
|
};
|
||||||
@ -220,6 +248,17 @@ static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = {
|
|||||||
.write = bpf_testmod_test_write,
|
.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_SET8_START(bpf_testmod_check_kfunc_ids)
|
||||||
BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
|
BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
|
||||||
BTF_SET8_END(bpf_testmod_check_kfunc_ids)
|
BTF_SET8_END(bpf_testmod_check_kfunc_ids)
|
||||||
@ -235,7 +274,8 @@ static int bpf_testmod_init(void)
|
|||||||
{
|
{
|
||||||
int ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
if (bpf_fentry_test1(0) < 0)
|
if (bpf_fentry_test1(0) < 0)
|
||||||
|
@ -22,4 +22,10 @@ struct bpf_testmod_test_writable_ctx {
|
|||||||
int val;
|
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 */
|
#endif /* _BPF_TESTMOD_H */
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "iters_state_safety.skel.h"
|
#include "iters_state_safety.skel.h"
|
||||||
#include "iters_looping.skel.h"
|
#include "iters_looping.skel.h"
|
||||||
#include "iters_num.skel.h"
|
#include "iters_num.skel.h"
|
||||||
|
#include "iters_testmod_seq.skel.h"
|
||||||
|
|
||||||
static void subtest_num_iters(void)
|
static void subtest_num_iters(void)
|
||||||
{
|
{
|
||||||
@ -53,12 +54,53 @@ cleanup:
|
|||||||
iters_num__destroy(skel);
|
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)
|
void test_iters(void)
|
||||||
{
|
{
|
||||||
RUN_TESTS(iters_state_safety);
|
RUN_TESTS(iters_state_safety);
|
||||||
RUN_TESTS(iters_looping);
|
RUN_TESTS(iters_looping);
|
||||||
RUN_TESTS(iters);
|
RUN_TESTS(iters);
|
||||||
|
|
||||||
|
if (env.has_testmod)
|
||||||
|
RUN_TESTS(iters_testmod_seq);
|
||||||
|
|
||||||
if (test__start_subtest("num"))
|
if (test__start_subtest("num"))
|
||||||
subtest_num_iters();
|
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