This commit introduces a new selftest for the BPF wakeup_source iterator to verify the functionality of open-coded iteration.
The test adds: - A new BPF map `test_ws_hash` to track iterated wakeup source names. - A BPF program `iter_ws_for_each` that iterates over wakeup sources and updates the `test_ws_hash` map with the names of found sources. - A new subtest `subtest_ws_iter_check_open_coded` to trigger the BPF program and assert that the expected wakeup sources are marked in the map.
Signed-off-by: Samuel Wu wusamuel@google.com --- .../testing/selftests/bpf/bpf_experimental.h | 5 ++ .../bpf/prog_tests/wakeup_source_iter.c | 42 +++++++++++++++++ .../selftests/bpf/progs/wakeup_source_iter.c | 47 +++++++++++++++++++ 3 files changed, 94 insertions(+)
diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 2cd9165c7348..e532999b91ca 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -598,6 +598,11 @@ extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym;
extern int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str, struct bpf_dynptr *value_p) __weak __ksym; +struct bpf_iter_wakeup_source; +extern int bpf_iter_wakeup_source_new(struct bpf_iter_wakeup_source *it) __weak __ksym; +extern struct wakeup_source *bpf_iter_wakeup_source_next( + struct bpf_iter_wakeup_source *it) __weak __ksym; +extern void bpf_iter_wakeup_source_destroy(struct bpf_iter_wakeup_source *it) __weak __ksym;
#define PREEMPT_BITS 8 #define SOFTIRQ_BITS 8 diff --git a/tools/testing/selftests/bpf/prog_tests/wakeup_source_iter.c b/tools/testing/selftests/bpf/prog_tests/wakeup_source_iter.c index 5cea4d4458f3..b2eaba38cc68 100644 --- a/tools/testing/selftests/bpf/prog_tests/wakeup_source_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/wakeup_source_iter.c @@ -241,9 +241,37 @@ static void subtest_ws_iter_check_no_infinite_reads( close(iter_fd); }
+static void subtest_ws_iter_check_open_coded(struct wakeup_source_iter *skel, + int map_fd) +{ + LIBBPF_OPTS(bpf_test_run_opts, topts); + char key[WAKEUP_SOURCE_NAME_LEN] = {0}; + int err, fd; + bool found = false; + + fd = bpf_program__fd(skel->progs.iter_ws_for_each); + + err = bpf_prog_test_run_opts(fd, &topts); + if (!ASSERT_OK(err, "test_run_opts err")) + return; + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) + return; + + strncpy(key, test_ws_name, WAKEUP_SOURCE_NAME_LEN - 1); + + if (!ASSERT_OK(bpf_map_lookup_elem(map_fd, key, &found), + "lookup test_ws_name")) + return; + + ASSERT_TRUE(found, "found test ws via bpf_for_each"); +} + void test_wakeup_source_iter(void) { struct wakeup_source_iter *skel = NULL; + int map_fd; + const bool found_val = false; + char key[WAKEUP_SOURCE_NAME_LEN] = {0};
if (geteuid() != 0) { fprintf(stderr, @@ -256,6 +284,17 @@ void test_wakeup_source_iter(void) if (!ASSERT_OK_PTR(skel, "wakeup_source_iter__open_and_load")) return;
+ map_fd = bpf_map__fd(skel->maps.test_ws_hash); + if (!ASSERT_OK_FD(map_fd, "map_fd")) + goto destroy_skel; + + /* Copy test name to key buffer, ensuring it's zero-padded */ + strncpy(key, test_ws_name, WAKEUP_SOURCE_NAME_LEN - 1); + + if (!ASSERT_OK(bpf_map_update_elem(map_fd, key, &found_val, BPF_ANY), + "insert test_ws_name")) + goto destroy_skel; + if (!ASSERT_OK(setup_test_ws(), "setup_test_ws")) goto destroy;
@@ -274,8 +313,11 @@ void test_wakeup_source_iter(void) subtest_ws_iter_check_sleep_times(skel); if (test__start_subtest("no_infinite_reads")) subtest_ws_iter_check_no_infinite_reads(skel); + if (test__start_subtest("open_coded")) + subtest_ws_iter_check_open_coded(skel, map_fd);
destroy: teardown_test_ws(); +destroy_skel: wakeup_source_iter__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/wakeup_source_iter.c b/tools/testing/selftests/bpf/progs/wakeup_source_iter.c index 8c1470f06740..7812e773aa0c 100644 --- a/tools/testing/selftests/bpf/progs/wakeup_source_iter.c +++ b/tools/testing/selftests/bpf/progs/wakeup_source_iter.c @@ -9,6 +9,13 @@
char _license[] SEC("license") = "GPL";
+struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, WAKEUP_SOURCE_NAME_LEN); + __type(value, bool); + __uint(max_entries, 5); +} test_ws_hash SEC(".maps"); + SEC("iter/wakeup_source") int wakeup_source_collector(struct bpf_iter__wakeup_source *ctx) { @@ -68,3 +75,43 @@ int wakeup_source_collector(struct bpf_iter__wakeup_source *ctx) wakeup_count); return 0; } + +SEC("syscall") +int iter_ws_for_each(const void *ctx) +{ + struct wakeup_source *ws; + + bpf_for_each(wakeup_source, ws) { + char name[WAKEUP_SOURCE_NAME_LEN]; + const char *pname; + bool *found; + long len; + int i; + + if (bpf_core_read(&pname, sizeof(pname), &ws->name)) + return 1; + + if (!pname) + continue; + + len = bpf_probe_read_kernel_str(name, sizeof(name), pname); + if (len < 0) + return 1; + + /* + * Clear the remainder of the buffer to ensure a stable key for + * the map lookup. + */ + bpf_for(i, len, WAKEUP_SOURCE_NAME_LEN) + name[i] = 0; + + found = bpf_map_lookup_elem(&test_ws_hash, name); + if (found) { + bool t = true; + + bpf_map_update_elem(&test_ws_hash, name, &t, BPF_EXIST); + } + } + + return 0; +}