The BPF iterator docs in bpf_iterators.rst specify that the next function of an open-coded iterator should keep returning NULL after it has been returned once.
Add a subtest that checks this for the dmabuf iterator. It drains an iterator, calls bpf_iter_dmabuf_next() once more and checks that the result is NULL. The dma-bufs the test creates keep the list from being empty, so an iterator that starts over would return one of them.
The existing open_coded subtest uses bpf_for_each(), which stops at the first NULL and never calls next() on a drained iterator.
Signed-off-by: T.J. Mercier tjmercier@google.com --- This depends on Kang Chen's fix and fails without it: https://lore.kernel.org/bpf/20261110125106.2935677-1-k.chen@smail.nju.edu.cn... Kang, feel free to carry it as 2/2 in your v2.
.../selftests/bpf/prog_tests/dmabuf_iter.c | 19 +++++++++++++++++++ .../testing/selftests/bpf/progs/dmabuf_iter.c | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c b/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c index 1a5dc1adae73..c1f81b5dccdc 100644 --- a/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/dmabuf_iter.c @@ -265,6 +265,23 @@ static void subtest_dmabuf_iter_check_open_coded(struct dmabuf_iter *skel, int m } while (bpf_map_get_next_key(map_fd, key, key)); }
+static void subtest_dmabuf_iter_check_next_after_drain(struct dmabuf_iter *skel) +{ + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err, fd; + + fd = bpf_program__fd(skel->progs.iter_dmabuf_next_after_drain); + + 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; + + /* The test buffers exist, so a restarted iteration would find one */ + ASSERT_EQ(skel->data->next_after_drain, 0, "next_after_drain"); +} + void test_dmabuf_iter(void) { struct dmabuf_iter *skel = NULL; @@ -315,6 +332,8 @@ void test_dmabuf_iter(void) } if (test__start_subtest("open_coded")) subtest_dmabuf_iter_check_open_coded(skel, map_fd); + if (test__start_subtest("next_after_drain")) + subtest_dmabuf_iter_check_next_after_drain(skel);
destroy: destroy_test_buffers(); diff --git a/tools/testing/selftests/bpf/progs/dmabuf_iter.c b/tools/testing/selftests/bpf/progs/dmabuf_iter.c index 9cbb7442646e..d5421b75c39c 100644 --- a/tools/testing/selftests/bpf/progs/dmabuf_iter.c +++ b/tools/testing/selftests/bpf/progs/dmabuf_iter.c @@ -99,3 +99,22 @@ int iter_dmabuf_for_each(const void *ctx)
return 0; } + +/* Result of a bpf_iter_dmabuf_next() call after the iterator was drained */ +__u64 next_after_drain = -1; + +SEC("syscall") +int iter_dmabuf_next_after_drain(const void *ctx) +{ + struct bpf_iter_dmabuf it; + + bpf_iter_dmabuf_new(&it); + while (bpf_iter_dmabuf_next(&it)) + ; + + /* Store the raw value; the verifier would remove a branch on it */ + next_after_drain = (__u64)bpf_iter_dmabuf_next(&it); + bpf_iter_dmabuf_destroy(&it); + + return 0; +}