selftests/bpf: implement and test custom testmod_seq iterator
authorAndrii Nakryiko <andrii@kernel.org>
Wed, 8 Mar 2023 18:41:21 +0000 (10:41 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 9 Mar 2023 00:19:51 +0000 (16:19 -0800)
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>
tools/testing/selftests/bpf/DENYLIST.s390x
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h
tools/testing/selftests/bpf/prog_tests/iters.c
tools/testing/selftests/bpf/progs/iters_testmod_seq.c [new file with mode: 0644]

index a02a085e7f32be42829050a645c6e53eac9b4f61..34cb8b2de8ca37a18f07a3f016fcaf056689dcb4 100644 (file)
@@ -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                       (?)
index 46500636d8cd93f81f7b02f948685e2148936113..5e6e85c8d77de3add8807d34e0617b4023a3d40d 100644 (file)
@@ -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)
index 0d71e2607832e5944ca4e95042c7f3331202f1b6..f32793efe095ba68bc1a388b46486dbb68d4c6a4 100644 (file)
@@ -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 */
index 2e7caff9523e1e63df08475e6841d97fcb136f3a..10804ae5ae972bea66cd03fae138c4c519fadd38 100644 (file)
@@ -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();
 }
diff --git a/tools/testing/selftests/bpf/progs/iters_testmod_seq.c b/tools/testing/selftests/bpf/progs/iters_testmod_seq.c
new file mode 100644 (file)
index 0000000..3873fb6
--- /dev/null
@@ -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";