From: David Howells <dhowells@redhat.com>
To: Christian Brauner <christian@brauner.io>
Cc: David Howells <dhowells@redhat.com>,
Paulo Alcantara <pc@manguebit.org>,
Matthew Wilcox <willy@infradead.org>,
Namjae Jeon <linkinjeon@kernel.org>,
Marc Dionne <marc.dionne@auristor.com>,
Stefan Metzmacher <metze@samba.org>,
Eric Van Hensbergen <ericvh@kernel.org>,
Dominique Martinet <asmadeus@codewreck.org>,
Ilya Dryomov <idryomov@gmail.com>,
netfs@lists.linux.dev, linux-afs@lists.infradead.org,
linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org,
ceph-devel@vger.kernel.org, v9fs@lists.linux.dev,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
Jens Axboe <axboe@kernel.dk>,
Christoph Hellwig <hch@infradead.org>,
linux-block@vger.kernel.org
Subject: [PATCH v16 05/15] iov_iter: Add a bvecq kunit test
Date: Sat, 7 Nov 2026 16:08:08 +0000 [thread overview]
Message-ID: <20261107160823.1465553-6-dhowells@redhat.com> (raw)
In-Reply-To: <20261107160823.1465553-1-dhowells@redhat.com>
Add a bvecq kunit test to exercise the bvecq iterator in a bunch of
different ways. Each kunit test sets up a series of folios of different
sizes (1, 2, 4 or 8 pages) and fills each subpage thereof with a distinct
predictable pattern that it can then sample to check that the iterator
appears to be in the right place.
The folios are then assembled into a bvecq chain depending on a particular
recipe and partialness list and then advance, revert and copy-out is
tested, along with checking iov_iter_npages() at each point.
Each test is a combination of a bvecq chain recipe:
- A single bvecq with 16 folios with no gaps (zero-len bio_vec).
- A chain of two bvecqs, each with 8 folios and no gaps.
- As the previous, but with a gap at the end of the first chain.
- A chain of four bvecqs, each with 4 folios and scattered gaps,
including at the front and back.
- A chain of five bvecqs, 3 with folios and two with no folios, one empty
and one with a couple of gaps.
- A chain of seven bvecqs, 3 with folios, 3 completely empty (including
first and last bvecqs in the chain) and 1 with just a couple of gaps.
and a selection of folio parts:
- Whole folios only.
- Scattered parts of folios, with some contiguities.
- Every folio with the first and last bytes excluded.
and a test algorithm:
- For each possible starting position in the bvecq, do a small advance,
check fill, a couple of small reverts, each checked and then advance to
the next starting position.
- Starting at the beginning, advance in jumps by a largish step forward,
checking after each jump; then when the end is hit revert in jumps by a
slightly larger step back, checking each time; then double the step and
repeat until the step exceeds the size of the iterator.
iov_iter_alignment() is compared at the beginning of one of the test sets
to a compound of alignments from an ITER_BVEC. Note that the ITER_BVEC
alignment check malfunctions if given a zero-length bio_vec array.
Exercising extraction, copy-from and copy-to is done in kunit_iov_iter.c,
with those bits added in a different patch.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Paulo Alcantara (Red Hat) <pc@manguebit.org>
cc: Matthew Wilcox <willy@infradead.org>
cc: Jens Axboe <axboe@kernel.dk>
cc: Christoph Hellwig <hch@infradead.org>
cc: linux-block@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
---
MAINTAINERS | 1 +
lib/Kconfig.debug | 13 +
lib/tests/Makefile | 1 +
lib/tests/kunit_bvecq.c | 599 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 614 insertions(+)
create mode 100644 lib/tests/kunit_bvecq.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 9cb3785a5bd2..bdda4ad58ab4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10391,6 +10391,7 @@ F: include/linux/fscache*.h
F: include/linux/netfs.h
F: include/trace/events/fscache.h
F: include/trace/events/netfs.h
+F: lib/tests/kunit_bvecq.c
FILESYSTEMS [STACKABLE]
M: Miklos Szeredi <miklos@szeredi.hu>
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2d22638ec13a..06b4546421a1 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2385,6 +2385,19 @@ config TEST_IOV_ITER
If unsure, say N.
+config TEST_BVECQ
+ tristate "Test bvecq operation" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ depends on MMU
+ depends on NETFS_SUPPORT
+ default KUNIT_ALL_TESTS
+ help
+ Enable this to turn on testing of the operation of the bvecq
+ structure. This test is executed only once during system
+ boot (so affects only boot time), or at module load time.
+
+ If unsure, say N.
+
config KPROBES_SANITY_TEST
tristate "Kprobes sanity tests" if !KUNIT_ALL_TESTS
depends on DEBUG_KERNEL
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index df0537e1b91f..85e3c5e1f7d2 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_GLOB_KUNIT_TEST) += glob_kunit.o
obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o
obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o
+obj-$(CONFIG_TEST_BVECQ) += kunit_bvecq.o
obj-$(CONFIG_IS_SIGNED_TYPE_KUNIT_TEST) += is_signed_type_kunit.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
diff --git a/lib/tests/kunit_bvecq.c b/lib/tests/kunit_bvecq.c
new file mode 100644
index 000000000000..7cdd455df692
--- /dev/null
+++ b/lib/tests/kunit_bvecq.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* bvecq iterator tests. Exercises:
+ * - advance
+ * - revert
+ * - npages
+ * - alignment
+ *
+ * See also kunit_iov_iter.c for bvecq copy and extract tests.
+ *
+ * Copyright (C) 2026 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/uio.h>
+#include <linux/bvecq.h>
+#include <kunit/test.h>
+
+MODULE_DESCRIPTION("bvecq testing");
+MODULE_AUTHOR("David Howells <dhowells@redhat.com>");
+MODULE_LICENSE("GPL");
+
+/*
+ * Info for the fill pattern generator.
+ */
+static const u8 bvecq_kunit_orders[16] = {
+ 0, 1, 2, 3,
+ 0, 1, 3, 2,
+ 0, 2, 1, 3,
+ 2, 0, 3, 1
+};
+
+static const s8 bvecq_kunit_fills[16] = {
+ 1, 2, 3, 5, 7, 11, 13, 17,
+ -1, -2, -3, -5, -7, -11, -13, -17
+};
+static struct folio *bvecq_kunit_folios[16];
+
+/*
+ * Info for the fill pattern predictor.
+ */
+struct bvecq_test_pagemap {
+ u32 pos;
+ u32 offset;
+ u8 folio;
+ u8 subpage;
+};
+
+static struct bvecq_test_pagemap bvecq_kunit_pagemap[64];
+static int bvecq_kunit_pagemap_nr;
+
+/*
+ * bvecq assembly recipes.
+ */
+enum bvecq_test_cmd {
+ BVECQ_KUNIT_STOP, /* Stop loading */
+ BVECQ_KUNIT_ALLOC, /* Append new bvecq of size count and move to it */
+ BVECQ_KUNIT_LOAD, /* Load count segments into current bvecq */
+ BVECQ_KUNIT_EMPTY, /* Add count empty segments into current bvecq */
+} __mode(byte);
+
+struct bvecq_test_recipe {
+ enum bvecq_test_cmd cmd;
+ u8 count;
+};
+
+#define ALLOC(C) { .cmd = BVECQ_KUNIT_ALLOC, .count = C }
+#define LOAD(C) { .cmd = BVECQ_KUNIT_LOAD, .count = C }
+#define EMPTY(C) { .cmd = BVECQ_KUNIT_EMPTY, .count = C }
+#define END { .cmd = BVECQ_KUNIT_STOP, }
+
+static const struct bvecq_test_recipe bvecq_test_recipe_0[] = {
+ ALLOC(16), LOAD(16),
+ END
+};
+
+static const struct bvecq_test_recipe bvecq_test_recipe_1[] = {
+ ALLOC(8), LOAD(8),
+ ALLOC(8), LOAD(8),
+ END
+};
+
+static const struct bvecq_test_recipe bvecq_test_recipe_2[] = {
+ ALLOC(9), LOAD(8), EMPTY(1),
+ ALLOC(8), LOAD(8),
+ END
+};
+
+static const struct bvecq_test_recipe bvecq_test_recipe_3[] = {
+ ALLOC(9), EMPTY(1), LOAD(2), EMPTY(2), LOAD(2), EMPTY(1),
+ ALLOC(8), LOAD(4), EMPTY(2),
+ ALLOC(8), EMPTY(1), LOAD(4),
+ ALLOC(8), EMPTY(2), LOAD(4), EMPTY(1),
+ END
+};
+
+static const struct bvecq_test_recipe bvecq_test_recipe_4[] = {
+ ALLOC(9), LOAD(2), EMPTY(1),
+ ALLOC(2),
+ ALLOC(2), LOAD(2),
+ ALLOC(2), EMPTY(2),
+ ALLOC(8), LOAD(8),
+ END
+};
+
+static const struct bvecq_test_recipe bvecq_test_recipe_5[] = {
+ ALLOC(2),
+ ALLOC(9), LOAD(2), EMPTY(1),
+ ALLOC(2),
+ ALLOC(2), LOAD(2),
+ ALLOC(2), EMPTY(2),
+ ALLOC(8), LOAD(8),
+ ALLOC(2),
+ END
+};
+
+static const struct bvecq_test_recipe bvecq_test_recipe_6[] = {
+ ALLOC(2), EMPTY(1),
+ ALLOC(2),
+ ALLOC(9), LOAD(2), EMPTY(1),
+ ALLOC(2),
+ ALLOC(2), LOAD(2),
+ ALLOC(2), EMPTY(2),
+ ALLOC(8), LOAD(8),
+ ALLOC(2),
+ ALLOC(2), EMPTY(1),
+ END
+};
+
+/*
+ * Sets of variations of how much of each folio is added to the bvecq chain,
+ * allowing a part of the folio to be excluded from each end.
+ */
+struct bvecq_test_partials {
+ u16 add_start; /* Amount to exclude from the low end */
+ u16 sub_end; /* Amount to exclude from the high end */
+};
+
+/* All whole folios. */
+static const struct bvecq_test_partials bvecq_test_partials_full[16] = {
+ [0 ... 15] = { 0, 0 }
+};
+
+/* Scattered partial folios, with some folios contiguous. */
+static const struct bvecq_test_partials bvecq_test_partials_scattered[16] = {
+ //0, 1, 2, 3,
+ { 0x277, 0x136 },
+ { 0x999, 0x1011 },
+ { 0x2020, 0x0 },
+ { 0x0, 0x1123 },
+ //0, 1, 3, 2,
+ { 0x0, 0x136 },
+ { 0x27, 0x777 },
+ { 0x771, 0x0 },
+ { 0x555, 0x0 },
+ //0, 2, 1, 3,
+ { 0x277, 0x136 },
+ { 0x1123, 0x0 },
+ { 0x0, 0x0 },
+ { 0, 0x5011 },
+ //2, 0, 3, 1
+ { 0x2020, 0x0 },
+ { 0x277, 0x136 },
+ { 0x1999, 0x1011 },
+ { 0x0, 0x1010 },
+};
+
+/* Partial folios, all with a byte excluded from each end. */
+static const struct bvecq_test_partials bvecq_test_partials_edges[16] = {
+ [0 ... 15] = { 1, 1 }
+};
+
+/*
+ * Clean up routines.
+ */
+static void bvecq_kunit_release_folios(void *data)
+{
+ struct folio **folios = data;
+
+ release_pages(folios, ARRAY_SIZE(bvecq_kunit_folios));
+ memset(bvecq_kunit_folios, 0, sizeof(bvecq_kunit_folios));
+}
+
+static void bvecq_kunit_bvecq_put(void *data)
+{
+ bvecq_put(data);
+}
+
+/*
+ * Calculate the fill pattern, for a particular byte of a particular subpage of
+ * a particular folio.
+ */
+static inline u8 bvecq_kunit_pattern(unsigned int folio, int subpage, int offset)
+{
+ int fill_step = bvecq_kunit_fills[folio];
+
+ return (subpage + offset * fill_step) & 0xff;
+}
+
+/*
+ * Calculate the fill pattern, given the 'byte position' in the bvecq chain.
+ * This looks up the folio, subpage and offset from a table generated whilst
+ * the bvecq is being assembled.
+ */
+static inline u8 bvecq_kunit_predict(struct kunit *test, size_t pos)
+{
+ size_t last_pos = 0;
+
+ for (int i = 0; i < bvecq_kunit_pagemap_nr; i++) {
+ if (pos < bvecq_kunit_pagemap[i].pos)
+ return bvecq_kunit_pattern(
+ bvecq_kunit_pagemap[i].folio,
+ bvecq_kunit_pagemap[i].subpage,
+ bvecq_kunit_pagemap[i].offset + (pos - last_pos));
+ last_pos = bvecq_kunit_pagemap[i].pos;
+ }
+
+ KUNIT_FAIL(test, "Overran pagemap pos=%zx\n", pos);
+ return 0;
+}
+
+/*
+ * Determine which page we're looking at, taking all the folios to be split
+ * into pages. This can be used to determine what iov_iter_npages() will say.
+ */
+static inline int bvecq_kunit_pos2page(struct kunit *test, size_t pos)
+{
+ for (int i = 0; i < bvecq_kunit_pagemap_nr; i++)
+ if (pos < bvecq_kunit_pagemap[i].pos)
+ return i;
+
+ if (pos > bvecq_kunit_pagemap[bvecq_kunit_pagemap_nr - 1].pos)
+ KUNIT_FAIL(test, "Overran pagemap pos=%zx\n", pos);
+ return bvecq_kunit_pagemap_nr;
+}
+
+/*
+ * Allocate a variety of folios of different orders and fill them with the
+ * pattern set for each folio.
+ */
+static void __init iov_bvecq_alloc_folios(struct kunit *test)
+{
+ struct folio *folio, **folios = bvecq_kunit_folios;
+ const int nr = ARRAY_SIZE(bvecq_kunit_folios);
+
+ bvecq_kunit_pagemap_nr = 0;
+ memset(bvecq_kunit_pagemap, 0, sizeof(bvecq_kunit_pagemap));
+ memset(bvecq_kunit_folios, 0, sizeof(bvecq_kunit_folios));
+
+ for (int i = 0; i < nr; i++) {
+ folio = folio_alloc(GFP_KERNEL, bvecq_kunit_orders[i]);
+ KUNIT_ASSERT_NOT_NULL(test, folio);
+ folios[i] = folio;
+
+ /* Fill each subpage with a different but predictable pattern. */
+ for (int j = 0; j < folio_nr_pages(folio); j++) {
+ u8 *p = kmap_local_folio(folio, PAGE_SIZE * j);
+
+ for (int k = 0; k < PAGE_SIZE; k++)
+ p[k] = bvecq_kunit_pattern(i, j, k);
+ kunmap_local(p);
+ }
+ }
+
+ kunit_add_action_or_reset(test, bvecq_kunit_release_folios, folios);
+}
+
+/*
+ * Follow the specified recipe to allocate a bvecq chain and stick the folios
+ * and empty gaps into it. Further, the offset into and length of each folio
+ * is adjusted from the partials list. Each ALLOC command will append a new
+ * bvecq to the chain and move to filling that.
+ */
+static struct bvecq *__init bvecq_kunit_load_chain(
+ struct kunit *test,
+ const struct bvecq_test_recipe *rec,
+ const struct bvecq_test_partials *parts,
+ size_t *size)
+{
+ struct bvecq *head = NULL, *bq = NULL, *p;
+ size_t pos = 0;
+ int folio_index = 0;
+
+ *size = 0;
+
+ for (; rec->cmd != BVECQ_KUNIT_STOP; rec++) {
+ if (!head)
+ KUNIT_ASSERT_EQ(test, rec->cmd, BVECQ_KUNIT_ALLOC);
+
+ switch (rec->cmd) {
+ case BVECQ_KUNIT_STOP:
+ break;
+
+ case BVECQ_KUNIT_ALLOC:
+ p = bvecq_alloc_one(rec->count, GFP_KERNEL, false);
+ KUNIT_ASSERT_NOT_NULL(test, p);
+
+ if (!head) {
+ head = p;
+ kunit_add_action_or_reset(test, bvecq_kunit_bvecq_put, head);
+ bq = p;
+ } else {
+ bvecq_append(bq, p);
+ bq = p;
+ }
+ break;
+
+ case BVECQ_KUNIT_LOAD:
+ KUNIT_ASSERT_LE(test, folio_index + rec->count,
+ ARRAY_SIZE(bvecq_kunit_orders));
+ KUNIT_ASSERT_LE(test, bq->nr_slots + rec->count, bq->max_slots);
+ for (int i = 0; i < rec->count; i++) {
+ struct folio *folio = bvecq_kunit_folios[folio_index];
+ size_t flen = PAGE_SIZE << bvecq_kunit_orders[folio_index];
+ size_t foff = 0;
+
+ KUNIT_ASSERT_LE(test,
+ parts[folio_index].add_start +
+ parts[folio_index].sub_end,
+ flen);
+
+ foff += parts[folio_index].add_start;
+ flen -= parts[folio_index].add_start;
+ flen -= parts[folio_index].sub_end;
+
+ bvec_set_page(&bq->bv[bq->nr_slots + i],
+ folio_page(folio, 0), flen, foff);
+ *size += flen;
+
+ for (int j = 0; j < folio_nr_pages(folio); j++) {
+ struct bvecq_test_pagemap *pm;
+ size_t part;
+
+ if (foff >= PAGE_SIZE) {
+ foff -= PAGE_SIZE;
+ continue;
+ }
+ part = min(flen, PAGE_SIZE - foff);
+ pos += part;
+ pm = &bvecq_kunit_pagemap[bvecq_kunit_pagemap_nr];
+ pm->pos = pos;
+ pm->folio = folio_index;
+ pm->subpage = j;
+ pm->offset = foff;
+ bvecq_kunit_pagemap_nr++;
+ flen -= part;
+ if (!flen)
+ break;
+ foff = 0;
+ }
+
+ folio_index++;
+ }
+ bvecq_filled_to(bq, bq->nr_slots + rec->count);
+ break;
+
+ case BVECQ_KUNIT_EMPTY:
+ KUNIT_ASSERT_LE(test, bq->nr_slots + rec->count, bq->max_slots);
+ for (int i = 0; i < rec->count; i++) {
+ bq->bv[bq->nr_slots + i].bv_page = ERR_PTR(-EIO);
+ bq->bv[bq->nr_slots + i].bv_offset = 0;
+ bq->bv[bq->nr_slots + i].bv_len = 0;
+ }
+ bvecq_filled_to(bq, bq->nr_slots + rec->count);
+ break;
+ }
+ }
+
+ return head;
+}
+
+/*
+ * Check the next few bytes of pattern from an iterator. Note that it is
+ * permitted for the iterator to have less data remaining than the number to
+ * check - in which case the copy is required to come up short.
+ */
+static void __init bvecq_kunit_check_next(struct kunit *test, struct iov_iter *iter,
+ size_t *pos, size_t *left, size_t nr)
+{
+ size_t part, n;
+ u8 buf[3], pred[3];
+
+ KUNIT_ASSERT_EQ(test, iov_iter_count(iter), *left);
+
+ part = min(*left, nr);
+ n = copy_from_iter(buf, nr, iter);
+ KUNIT_ASSERT_EQ(test, n, part);
+ *left -= n;
+
+ for (size_t i = 0; i < n; i++)
+ pred[i] = bvecq_kunit_predict(test, *pos + i);
+
+ for (size_t i = 0; i < n; i++) {
+ if (buf[i] != pred[i]) {
+ pr_notice(">>> pos=%05zx+%zx c=%zx buf=%*phN pred=%*phN\n",
+ *pos, i, iov_iter_count(iter) + n,
+ (int)n, buf, (int)n, pred);
+ }
+ KUNIT_ASSERT_EQ(test, buf[i], pred[i]);
+ }
+
+ *pos += n;
+}
+
+/*
+ * Test advancing and reverting over a bvecq chain.
+ *
+ * The loop starts at each 'byte position' in the chain, advancing a small
+ * amount, copying a few bytes of data, checking the fill pattern, reverting a
+ * bit, checking, reverting, checking and then reverting to a byte position one
+ * further on than the previous loop. Use small prime numbers for the advance
+ * and first revert so that we should test crossing the edges of the folios.
+ */
+static void __init bvecq_kunit_advance(struct kunit *test,
+ const struct bvecq_test_recipe *recipe,
+ const struct bvecq_test_partials *parts)
+{
+ struct iov_iter iter;
+ struct bvecq *bq, *p;
+ unsigned long bvec_align = 0;
+ size_t size, pos = 0, from;
+ int npages;
+
+ iov_bvecq_alloc_folios(test);
+ bq = bvecq_kunit_load_chain(test, recipe, parts, &size);
+
+ /* Compound up the alignments for ITER_BVEC on each individual bvecq
+ * span and compare that to the alignment for ITER_BVECQ. There is an
+ * issue in ITER_BVEC, however, in that it includes bv_offset from
+ * bio_vecs for which bv_len is 0, which ITER_BVECQ does not.
+ */
+ for (p = bq; p; p = p->next) {
+ size_t psize = 0;
+
+ for (int i = 0; i < p->nr_slots; i++)
+ psize += p->bv[i].bv_len;
+
+ if (!psize)
+ continue;
+ iov_iter_bvec(&iter, ITER_SOURCE, p->bv, p->nr_slots, psize);
+ bvec_align |= iov_iter_alignment(&iter);
+ }
+
+ iov_iter_bvec_queue(&iter, ITER_SOURCE, bq, 0, 0, size);
+
+ KUNIT_ASSERT_EQ(test, iov_iter_alignment(&iter), bvec_align);
+
+ npages = iov_iter_npages(&iter, INT_MAX);
+ from = iov_iter_count(&iter);
+ do {
+ size_t left = from, part;
+
+ KUNIT_ASSERT_EQ(test, iov_iter_npages(&iter, INT_MAX),
+ npages - bvecq_kunit_pos2page(test, pos));
+
+ /* 3 steps forward. */
+ part = min(left, 3);
+ iov_iter_advance(&iter, 3); /* May be short. */
+ pos += part;
+ left -= part;
+ bvecq_kunit_check_next(test, &iter, &pos, &left, 3);
+
+ /* 2 steps back */
+ part = umin(2, left);
+ iov_iter_revert(&iter, part);
+ pos -= part;
+ left += part;
+ bvecq_kunit_check_next(test, &iter, &pos, &left, 2);
+
+ /* 1 more step back */
+ part = umin(1, left);
+ iov_iter_revert(&iter, part);
+ pos -= part;
+ left += part;
+ bvecq_kunit_check_next(test, &iter, &pos, &left, 1);
+
+ KUNIT_ASSERT_EQ(test, left, iov_iter_count(&iter));
+
+ /* Move to one place on from the current loop. */
+ from--;
+ if (iov_iter_count(&iter) < from) {
+ part = from - iov_iter_count(&iter);
+ iov_iter_revert(&iter, part);
+ pos -= part;
+ }
+ KUNIT_ASSERT_EQ(test, from, iov_iter_count(&iter));
+ } while (from > 0);
+}
+
+/*
+ * Test jumping around in a bvecq chain using advance and revert.
+ *
+ * The loop starts at the start of the chain, then jumps forwards through the
+ * chain and then back again by large amounts, sampling the data each time.
+ */
+static void __init bvecq_kunit_jump(struct kunit *test,
+ const struct bvecq_test_recipe *recipe,
+ const struct bvecq_test_partials *parts)
+{
+ struct iov_iter iter;
+ struct bvecq *bq;
+ size_t size, pos = 0, step = 0x100;
+ bool dir = false;
+ int npages;
+
+ iov_bvecq_alloc_folios(test);
+ bq = bvecq_kunit_load_chain(test, recipe, parts, &size);
+
+ iov_iter_bvec_queue(&iter, ITER_SOURCE, bq, 0, 0, size);
+
+ npages = iov_iter_npages(&iter, INT_MAX);
+ while (step < size) {
+ size_t left = iov_iter_count(&iter), part;
+
+ KUNIT_ASSERT_EQ(test, iov_iter_npages(&iter, INT_MAX),
+ npages - bvecq_kunit_pos2page(test, pos));
+
+ if (!dir) {
+ /* Advance by step and then 3 more in check. */
+ part = min(left, step);
+ iov_iter_advance(&iter, step); /* May be short. */
+ pos += part;
+ left -= part;
+ bvecq_kunit_check_next(test, &iter, &pos, &left, 3);
+
+ KUNIT_ASSERT_EQ(test, left, iov_iter_count(&iter));
+
+ if (left == 0) {
+ /* Switch to reverting. */
+ dir = true;
+ step += 3;
+ }
+ } else {
+ /* Revert by step and then advance by 3 in check. */
+ part = min(size - left, step);
+ iov_iter_revert(&iter, part);
+ pos -= part;
+ left += part;
+ bvecq_kunit_check_next(test, &iter, &pos, &left, 3);
+
+ KUNIT_ASSERT_EQ(test, left, iov_iter_count(&iter));
+
+ if (pos <= 3) {
+ /* Switch to advancing. */
+ dir = false;
+ step *= 2;
+ }
+ }
+ }
+}
+
+/*
+ * Run the individual test combinations
+ */
+#define ADVANCE_TESTS_SET(CHECK, PARTIALS) \
+ TEST(CHECK, PARTIALS, 0) \
+ TEST(CHECK, PARTIALS, 1) \
+ TEST(CHECK, PARTIALS, 2) \
+ TEST(CHECK, PARTIALS, 3) \
+ TEST(CHECK, PARTIALS, 4) \
+ TEST(CHECK, PARTIALS, 5) \
+ TEST(CHECK, PARTIALS, 6)
+
+#define ADVANCE_TESTS(CHECK) \
+ ADVANCE_TESTS_SET(CHECK, full) \
+ ADVANCE_TESTS_SET(CHECK, scattered) \
+ ADVANCE_TESTS_SET(CHECK, edges)
+
+#define TEST(CHECK, PARTIALS, RECIPE) \
+static void __init bvecq_kunit_##CHECK##_##PARTIALS##_##RECIPE(struct kunit *test) \
+{ \
+ bvecq_kunit_##CHECK(test, \
+ bvecq_test_recipe_##RECIPE, \
+ bvecq_test_partials_##PARTIALS); \
+}
+
+/* Emit test stub functions. */
+ADVANCE_TESTS(advance);
+ADVANCE_TESTS(jump);
+#undef TEST
+
+#define TEST(CHECK, PARTIALS, RECIPE) \
+ KUNIT_CASE(bvecq_kunit_##CHECK##_##PARTIALS##_##RECIPE),
+
+static struct kunit_case __refdata bvecq_kunit_cases[] = {
+ ADVANCE_TESTS(advance)
+ ADVANCE_TESTS(jump)
+ {}
+};
+
+static struct kunit_suite bvecq_kunit_suite = {
+ .name = "bvecq",
+ .test_cases = bvecq_kunit_cases,
+};
+
+kunit_test_suites(&bvecq_kunit_suite);
next prev parent reply other threads:[~2026-11-07 16:09 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 16:08 [PATCH v16 00/15] netfs, iov_iter: Use a chain of bio_vec arrays instead of folio_queue David Howells
2026-11-07 16:08 ` [PATCH v16 01/15] Add a function to kmap one page of a multipage bio_vec David Howells
2026-11-07 16:08 ` [PATCH v16 02/15] iov_iter: Add a segmented queue of bio_vec[] David Howells
2026-11-07 16:08 ` [PATCH v16 03/15] netfs: Add some tools for managing bvecq chains David Howells
2026-11-07 16:08 ` [PATCH v16 04/15] iov_iter: Add an API document for the bvecq structure David Howells
2026-11-07 16:08 ` David Howells [this message]
2026-11-07 16:08 ` [PATCH v16 06/15] afs: Fix an inconsistency in the symlink code David Howells
2026-11-07 16:08 ` [PATCH v16 07/15] netfs: Explicitly pass tail padding to netfs_writeback_single() David Howells
2026-11-07 16:08 ` [PATCH v16 08/15] netfs: Change netfs_writeback_single() to not look inside the iterator David Howells
2026-11-07 16:08 ` [PATCH v16 09/15] afs: Use a bvecq to hold dir content rather than folioq David Howells
2026-11-07 16:08 ` [PATCH v16 10/15] cifs: Use a bvecq for buffering instead of a folioq David Howells
2026-11-07 16:08 ` [PATCH v16 11/15] smbdirect: Support ITER_BVECQ in smbdirect_map_sges_from_iter() David Howells
2026-11-07 16:08 ` [PATCH v16 12/15] netfs: Switch folioq to bvecq David Howells
2026-11-07 16:08 ` [PATCH v16 13/15] smbdirect: Remove support for ITER_FOLIOQ from smbdirect_map_sges_from_iter() David Howells
2026-11-07 16:08 ` [PATCH v16 14/15] iov_iter: Remove ITER_FOLIOQ David Howells
2026-11-07 16:08 ` [PATCH v16 15/15] netfs: Remove folio_queue David Howells
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261107160823.1465553-6-dhowells@redhat.com \
--to=dhowells@redhat.com \
--cc=asmadeus@codewreck.org \
--cc=axboe@kernel.dk \
--cc=ceph-devel@vger.kernel.org \
--cc=christian@brauner.io \
--cc=ericvh@kernel.org \
--cc=hch@infradead.org \
--cc=idryomov@gmail.com \
--cc=linkinjeon@kernel.org \
--cc=linux-afs@lists.infradead.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=marc.dionne@auristor.com \
--cc=metze@samba.org \
--cc=netfs@lists.linux.dev \
--cc=pc@manguebit.org \
--cc=v9fs@lists.linux.dev \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®