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; +}
On 11/11/26 20:59, T.J. Mercier wrote:
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
Acked-by: Christian König christian.koenig@amd.com
Let me know if you already have drm-misc commit rights.
Regards, Christian.
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;
+}
On Thu, Nov 12, 2026 at 1:35 AM Christian König christian.koenig@amd.com wrote:
On 11/11/26 20:59, T.J. Mercier wrote:
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
Acked-by: Christian König christian.koenig@amd.com
Let me know if you already have drm-misc commit rights.
Regards, Christian.
Thanks! Still waiting but no rush.
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;+}
linaro-mm-sig@lists.linaro.org