linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] iov_iter: Add kunit tests and fix iov_iter_extract_pages()
@ 2023-09-08 16:03 David Howells
  2023-09-08 16:03 ` [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages() David Howells
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: David Howells @ 2023-09-08 16:03 UTC (permalink / raw)
  To: Jens Axboe, Al Viro, Christoph Hellwig
  Cc: David Howells, Matthew Wilcox, Christian Brauner,
	David Hildenbrand, John Hubbard, Jeff Layton, Linus Torvalds,
	linux-fsdevel, linux-block, linux-kselftest, linux-mm,
	linux-kernel

Hi Jens, Christoph, Al,

Here are two patches to add some kunit tests for the iov_iter stuff and a
patch to fix a couple of bugs found by these tests.

It's by no means comprehensive, but it does at least test the basic
copy_to_iter(), copy_from_iter() and iov_iter_extract_pages() for
ITER_KVEC, ITER_BVEC and ITER_XARRAY.

I've left ITER_UBUF and ITER_IOVEC untested for now as they require
userspace VM interaction and I'm not sure if that's possible under kunit
tests.  I've also left ITER_DISCARD for the moment as that does nothing and
can't be extracted.

The kunit tests should also perhaps be using folios rather than pages, but
for the moment I'm using pages because I'm using vmap() and also
iov_iter_extract_pages() doesn't yet have a folio equivalent.

I've pushed the patches here also:

	https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/log/?h=iov-fixes

David

David Howells (3):
  iov_iter: Fix iov_iter_extract_pages()
  iov_iter: Kunit tests for copying to/from an iterator
  iov_iter: Kunit tests for page extraction

 lib/Kconfig.debug    |  11 +
 lib/Makefile         |   1 +
 lib/iov_iter.c       |  30 +-
 lib/kunit_iov_iter.c | 777 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 804 insertions(+), 15 deletions(-)
 create mode 100644 lib/kunit_iov_iter.c


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages()
  2023-09-08 16:03 [PATCH 0/3] iov_iter: Add kunit tests and fix iov_iter_extract_pages() David Howells
@ 2023-09-08 16:03 ` David Howells
  2023-09-09 11:27   ` Christoph Hellwig
  2023-09-08 16:03 ` [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator David Howells
  2023-09-08 16:03 ` [PATCH 3/3] iov_iter: Kunit tests for page extraction David Howells
  2 siblings, 1 reply; 6+ messages in thread
From: David Howells @ 2023-09-08 16:03 UTC (permalink / raw)
  To: Jens Axboe, Al Viro, Christoph Hellwig
  Cc: David Howells, Matthew Wilcox, Christian Brauner,
	David Hildenbrand, John Hubbard, Jeff Layton, Linus Torvalds,
	linux-fsdevel, linux-block, linux-kselftest, linux-mm,
	linux-kernel

iov_iter_extract_pages() doesn't correctly handle skipping over initial
zero-length entries in ITER_KVEC and ITER_BVEC-type iterators.  The problem is
that it accidentally reduces maxsize to 0 when it skipping and thus runs to
the end of the array and returns 0.

Fix this by sticking the calculated size-to-copy in a new variable rather
than back in maxsize.

Fixes: 7d58fe731028 ("iov_iter: Add a function to extract a page list from an iterator")
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Christoph Hellwig <hch@lst.de>
cc: Christian Brauner <brauner@kernel.org>
cc: Jens Axboe <axboe@kernel.dk>
cc: Al Viro <viro@zeniv.linux.org.uk>
cc: David Hildenbrand <david@redhat.com>
cc: John Hubbard <jhubbard@nvidia.com>
cc: linux-mm@kvack.org
cc: linux-block@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
---
 lib/iov_iter.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index b31597b0ca20..27234a820eeb 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1654,14 +1654,14 @@ static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i,
 					   size_t *offset0)
 {
 	struct page **p, *page;
-	size_t skip = i->iov_offset, offset;
+	size_t skip = i->iov_offset, offset, size;
 	int k;
 
 	for (;;) {
 		if (i->nr_segs == 0)
 			return 0;
-		maxsize = min(maxsize, i->bvec->bv_len - skip);
-		if (maxsize)
+		size = min(maxsize, i->bvec->bv_len - skip);
+		if (size)
 			break;
 		i->iov_offset = 0;
 		i->nr_segs--;
@@ -1674,16 +1674,16 @@ static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i,
 	offset = skip % PAGE_SIZE;
 	*offset0 = offset;
 
-	maxpages = want_pages_array(pages, maxsize, offset, maxpages);
+	maxpages = want_pages_array(pages, size, offset, maxpages);
 	if (!maxpages)
 		return -ENOMEM;
 	p = *pages;
 	for (k = 0; k < maxpages; k++)
 		p[k] = page + k;
 
-	maxsize = min_t(size_t, maxsize, maxpages * PAGE_SIZE - offset);
-	iov_iter_advance(i, maxsize);
-	return maxsize;
+	size = min_t(size_t, size, maxpages * PAGE_SIZE - offset);
+	iov_iter_advance(i, size);
+	return size;
 }
 
 /*
@@ -1698,14 +1698,14 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov_iter *i,
 {
 	struct page **p, *page;
 	const void *kaddr;
-	size_t skip = i->iov_offset, offset, len;
+	size_t skip = i->iov_offset, offset, len, size;
 	int k;
 
 	for (;;) {
 		if (i->nr_segs == 0)
 			return 0;
-		maxsize = min(maxsize, i->kvec->iov_len - skip);
-		if (maxsize)
+		size = min(maxsize, i->kvec->iov_len - skip);
+		if (size)
 			break;
 		i->iov_offset = 0;
 		i->nr_segs--;
@@ -1717,13 +1717,13 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov_iter *i,
 	offset = (unsigned long)kaddr & ~PAGE_MASK;
 	*offset0 = offset;
 
-	maxpages = want_pages_array(pages, maxsize, offset, maxpages);
+	maxpages = want_pages_array(pages, size, offset, maxpages);
 	if (!maxpages)
 		return -ENOMEM;
 	p = *pages;
 
 	kaddr -= offset;
-	len = offset + maxsize;
+	len = offset + size;
 	for (k = 0; k < maxpages; k++) {
 		size_t seg = min_t(size_t, len, PAGE_SIZE);
 
@@ -1737,9 +1737,9 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov_iter *i,
 		kaddr += PAGE_SIZE;
 	}
 
-	maxsize = min_t(size_t, maxsize, maxpages * PAGE_SIZE - offset);
-	iov_iter_advance(i, maxsize);
-	return maxsize;
+	size = min_t(size_t, size, maxpages * PAGE_SIZE - offset);
+	iov_iter_advance(i, size);
+	return size;
 }
 
 /*


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator
  2023-09-08 16:03 [PATCH 0/3] iov_iter: Add kunit tests and fix iov_iter_extract_pages() David Howells
  2023-09-08 16:03 ` [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages() David Howells
@ 2023-09-08 16:03 ` David Howells
  2023-09-09 11:30   ` Christoph Hellwig
  2023-09-08 16:03 ` [PATCH 3/3] iov_iter: Kunit tests for page extraction David Howells
  2 siblings, 1 reply; 6+ messages in thread
From: David Howells @ 2023-09-08 16:03 UTC (permalink / raw)
  To: Jens Axboe, Al Viro, Christoph Hellwig
  Cc: David Howells, Matthew Wilcox, Christian Brauner,
	David Hildenbrand, John Hubbard, Jeff Layton, Linus Torvalds,
	linux-fsdevel, linux-block, linux-kselftest, linux-mm,
	linux-kernel

Add some kunit tests for page extraction for ITER_BVEC, ITER_KVEC and
ITER_XARRAY type iterators.  ITER_UBUF and ITER_IOVEC aren't dealt with as
they require userspace VM interaction.  ITER_DISCARD isn't dealt with
either as that does nothing.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Christoph Hellwig <hch@lst.de>
cc: Christian Brauner <brauner@kernel.org>
cc: Jens Axboe <axboe@kernel.dk>
cc: Al Viro <viro@zeniv.linux.org.uk>
cc: David Hildenbrand <david@redhat.com>
cc: John Hubbard <jhubbard@nvidia.com>
cc: linux-mm@kvack.org
cc: linux-block@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
---
 lib/Kconfig.debug    |  11 +
 lib/Makefile         |   1 +
 lib/kunit_iov_iter.c | 537 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 549 insertions(+)
 create mode 100644 lib/kunit_iov_iter.c

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 319cfbeb0738..fa307f93fa2e 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2237,6 +2237,17 @@ config TEST_DIV64
 
 	  If unsure, say N.
 
+config TEST_IOV_ITER
+	tristate "Test iov_iter operation" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  Enable this to turn on testing of the operation of the I/O iterator
+	  (iov_iter). 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/Makefile b/lib/Makefile
index 2e08397f6210..740109b6e2c8 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_TEST_BITOPS) += test_bitops.o
 CFLAGS_test_bitops.o += -Werror
 obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o
 obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o
+obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o
 obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
 obj-$(CONFIG_TEST_IDA) += test_ida.o
 obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
diff --git a/lib/kunit_iov_iter.c b/lib/kunit_iov_iter.c
new file mode 100644
index 000000000000..25a910187d17
--- /dev/null
+++ b/lib/kunit_iov_iter.c
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* I/O iterator tests.  This can only test kernel-backed iterator types.
+ *
+ * Copyright (C) 2023 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/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/uio.h>
+#include <linux/bvec.h>
+#include <kunit/test.h>
+
+MODULE_DESCRIPTION("iov_iter testing");
+MODULE_AUTHOR("David Howells <dhowells@redhat.com>");
+MODULE_LICENSE("GPL");
+
+struct kvec_test_range {
+	int	from, to;
+};
+
+static const struct kvec_test_range kvec_test_ranges[] = {
+	{ 0x00002, 0x00002 },
+	{ 0x00027, 0x03000 },
+	{ 0x05193, 0x18794 },
+	{ 0x20000, 0x20000 },
+	{ 0x20000, 0x24000 },
+	{ 0x24000, 0x27001 },
+	{ 0x29000, 0xffffb },
+	{ 0xffffd, 0xffffe },
+	{ -1 }
+};
+
+static inline u8 pattern(unsigned long x)
+{
+	return x & 0xff;
+}
+
+static void iov_kunit_unmap(void *data)
+{
+	vunmap(data);
+}
+
+static void *__init iov_kunit_create_buffer(struct kunit *test,
+					    struct page ***ppages,
+					    size_t npages)
+{
+	struct page **pages;
+	unsigned long got;
+	void *buffer;
+
+	pages = kunit_kcalloc(test, npages, sizeof(struct page *), GFP_KERNEL);
+        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages);
+	*ppages = pages;
+
+	got = alloc_pages_bulk_array(GFP_KERNEL, npages, pages);
+	if (got != npages) {
+		release_pages(pages, got);
+		KUNIT_ASSERT_EQ(test, got, npages);
+	}
+
+	buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL);
+        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);
+
+	kunit_add_action_or_reset(test, iov_kunit_unmap, buffer);
+	return buffer;
+}
+
+static void __init iov_kunit_load_kvec(struct kunit *test,
+				       struct iov_iter *iter, int dir,
+				       struct kvec *kvec, unsigned int kvmax,
+				       void *buffer, size_t bufsize,
+				       const struct kvec_test_range *pr)
+{
+	size_t size = 0;
+	int i;
+
+	for (i = 0; i < kvmax; i++, pr++) {
+		if (pr->from < 0)
+			break;
+		KUNIT_ASSERT_GE(test, pr->to, pr->from);
+		KUNIT_ASSERT_LE(test, pr->to, bufsize);
+		kvec[i].iov_base = buffer + pr->from;
+		kvec[i].iov_len = pr->to - pr->from;
+		size += pr->to - pr->from;
+	}
+	KUNIT_ASSERT_LE(test, size, bufsize);
+
+	iov_iter_kvec(iter, dir, kvec, i, size);
+}
+
+/*
+ * Test copying to a ITER_KVEC-type iterator.
+ */
+static void __init iov_kunit_copy_to_kvec(struct kunit *test)
+{
+	const struct kvec_test_range *pr;
+	struct iov_iter iter;
+	struct page **spages, **bpages;
+	struct kvec kvec[8];
+	u8 *scratch, *buffer;
+	size_t bufsize, npages, size, copied;
+	int i, patt;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	scratch = iov_kunit_create_buffer(test, &spages, npages);
+	for (i = 0; i < bufsize; i++)
+		scratch[i] = pattern(i);
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+	memset(buffer, 0, bufsize);
+
+	iov_kunit_load_kvec(test, &iter, READ, kvec, ARRAY_SIZE(kvec),
+			    buffer, bufsize, kvec_test_ranges);
+	size = iter.count;
+
+	copied = copy_to_iter(scratch, size, &iter);
+
+	KUNIT_EXPECT_EQ(test, copied, size);
+	KUNIT_EXPECT_EQ(test, iter.count, 0);
+	KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
+
+	/* Build the expected image in the scratch buffer. */
+	patt = 0;
+	memset(scratch, 0, bufsize);
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++)
+		for (i = pr->from; i < pr->to; i++)
+			scratch[i] = pattern(patt++);
+
+	/* Compare the images */
+	for (i = 0; i < bufsize; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i);
+		if (buffer[i] != scratch[i])
+			return;
+	}
+
+	KUNIT_SUCCEED();
+}
+
+/*
+ * Test copying from a ITER_KVEC-type iterator.
+ */
+static void __init iov_kunit_copy_from_kvec(struct kunit *test)
+{
+	const struct kvec_test_range *pr;
+	struct iov_iter iter;
+	struct page **spages, **bpages;
+	struct kvec kvec[8];
+	u8 *scratch, *buffer;
+	size_t bufsize, npages, size, copied;
+	int i, j;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+	for (i = 0; i < bufsize; i++)
+		buffer[i] = pattern(i);
+
+	scratch = iov_kunit_create_buffer(test, &spages, npages);
+	memset(scratch, 0, bufsize);
+
+	iov_kunit_load_kvec(test, &iter, WRITE, kvec, ARRAY_SIZE(kvec),
+			    buffer, bufsize, kvec_test_ranges);
+	size = min(iter.count, bufsize);
+
+	copied = copy_from_iter(scratch, size, &iter);
+
+	KUNIT_EXPECT_EQ(test, copied, size);
+	KUNIT_EXPECT_EQ(test, iter.count, 0);
+	KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
+
+	/* Build the expected image in the main buffer. */
+	i = 0;
+	memset(buffer, 0, bufsize);
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+		for (j = pr->from; j < pr->to; j++) {
+			buffer[i++] = pattern(j);
+			if (i >= bufsize)
+				goto stop;
+		}
+	}
+stop:
+
+	/* Compare the images */
+	for (i = 0; i < bufsize; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i);
+		if (scratch[i] != buffer[i])
+			return;
+	}
+
+	KUNIT_SUCCEED();
+}
+
+struct bvec_test_range {
+	int	page, from, to;
+};
+
+static const struct bvec_test_range bvec_test_ranges[] = {
+	{ 0, 0x0002, 0x0002 },
+	{ 1, 0x0027, 0x0893 },
+	{ 2, 0x0193, 0x0794 },
+	{ 3, 0x0000, 0x1000 },
+	{ 4, 0x0000, 0x1000 },
+	{ 5, 0x0000, 0x1000 },
+	{ 6, 0x0000, 0x0ffb },
+	{ 6, 0x0ffd, 0x0ffe },
+	{ -1, -1, -1 }
+};
+
+static void __init iov_kunit_load_bvec(struct kunit *test,
+				       struct iov_iter *iter, int dir,
+				       struct bio_vec *bvec, unsigned int bvmax,
+				       struct page **pages, size_t npages,
+				       size_t bufsize,
+				       const struct bvec_test_range *pr)
+{
+	struct page *can_merge = NULL, *page;
+	size_t size = 0;
+	int i;
+
+	for (i = 0; i < bvmax; i++, pr++) {
+		if (pr->from < 0)
+			break;
+		KUNIT_ASSERT_LT(test, pr->page, npages);
+		KUNIT_ASSERT_LT(test, pr->page * PAGE_SIZE, bufsize);
+		KUNIT_ASSERT_GE(test, pr->from, 0);
+		KUNIT_ASSERT_GE(test, pr->to, pr->from);
+		KUNIT_ASSERT_LE(test, pr->to, PAGE_SIZE);
+
+		page = pages[pr->page];
+		if (pr->from == 0 && pr->from != pr->to && page == can_merge) {
+			i--;
+			bvec[i].bv_len += pr->to;
+		} else {
+			bvec_set_page(&bvec[i], page, pr->to - pr->from, pr->from);
+		}
+
+		size += pr->to - pr->from;
+		if ((pr->to & ~PAGE_MASK) == 0)
+			can_merge = page + pr->to / PAGE_SIZE;
+		else
+			can_merge = NULL;
+	}
+
+	iov_iter_bvec(iter, dir, bvec, i, size);
+}
+
+/*
+ * Test copying to a ITER_BVEC-type iterator.
+ */
+static void __init iov_kunit_copy_to_bvec(struct kunit *test)
+{
+	const struct bvec_test_range *pr;
+	struct iov_iter iter;
+	struct bio_vec bvec[8];
+	struct page **spages, **bpages;
+	u8 *scratch, *buffer;
+	size_t bufsize, npages, size, copied;
+	int i, b, patt;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	scratch = iov_kunit_create_buffer(test, &spages, npages);
+	for (i = 0; i < bufsize; i++)
+		scratch[i] = pattern(i);
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+	memset(buffer, 0, bufsize);
+
+	iov_kunit_load_bvec(test, &iter, READ, bvec, ARRAY_SIZE(bvec),
+			    bpages, npages, bufsize, bvec_test_ranges);
+	size = iter.count;
+
+	copied = copy_to_iter(scratch, size, &iter);
+
+	KUNIT_EXPECT_EQ(test, copied, size);
+	KUNIT_EXPECT_EQ(test, iter.count, 0);
+	KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
+
+	/* Build the expected image in the scratch buffer. */
+	b = 0;
+	patt = 0;
+	memset(scratch, 0, bufsize);
+	for (pr = bvec_test_ranges; pr->from >= 0; pr++, b++) {
+		u8 *p = scratch + pr->page * PAGE_SIZE;
+
+		for (i = pr->from; i < pr->to; i++)
+			p[i] = pattern(patt++);
+	}
+
+	/* Compare the images */
+	for (i = 0; i < bufsize; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i);
+		if (buffer[i] != scratch[i])
+			return;
+	}
+
+	KUNIT_SUCCEED();
+}
+
+/*
+ * Test copying from a ITER_BVEC-type iterator.
+ */
+static void __init iov_kunit_copy_from_bvec(struct kunit *test)
+{
+	const struct bvec_test_range *pr;
+	struct iov_iter iter;
+	struct bio_vec bvec[8];
+	struct page **spages, **bpages;
+	u8 *scratch, *buffer;
+	size_t bufsize, npages, size, copied;
+	int i, j;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+	for (i = 0; i < bufsize; i++)
+		buffer[i] = pattern(i);
+
+	scratch = iov_kunit_create_buffer(test, &spages, npages);
+	memset(scratch, 0, bufsize);
+
+	iov_kunit_load_bvec(test, &iter, WRITE, bvec, ARRAY_SIZE(bvec),
+			    bpages, npages, bufsize, bvec_test_ranges);
+	size = iter.count;
+
+	copied = copy_from_iter(scratch, size, &iter);
+
+	KUNIT_EXPECT_EQ(test, copied, size);
+	KUNIT_EXPECT_EQ(test, iter.count, 0);
+	KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
+
+	/* Build the expected image in the main buffer. */
+	i = 0;
+	memset(buffer, 0, bufsize);
+	for (pr = bvec_test_ranges; pr->from >= 0; pr++) {
+		size_t patt = pr->page * PAGE_SIZE;
+
+		for (j = pr->from; j < pr->to; j++) {
+			buffer[i++] = pattern(patt + j);
+			if (i >= bufsize)
+				goto stop;
+		}
+	}
+stop:
+
+	/* Compare the images */
+	for (i = 0; i < bufsize; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i);
+		if (scratch[i] != buffer[i])
+			return;
+	}
+
+	KUNIT_SUCCEED();
+}
+
+static void iov_kunit_destroy_xarray(void *data)
+{
+	struct xarray *xarray = data;
+
+	xa_destroy(xarray);
+	kfree(xarray);
+}
+
+static void __init iov_kunit_load_xarray(struct kunit *test,
+					 struct iov_iter *iter, int dir,
+					 struct xarray *xarray,
+					 struct page **pages, size_t npages)
+{
+	size_t size = 0;
+	int i;
+
+	for (i = 0; i < npages; i++) {
+		void *x = xa_store(xarray, i, pages[i], GFP_KERNEL);
+
+		KUNIT_ASSERT_FALSE(test, xa_is_err(x));
+		size += PAGE_SIZE;
+	}
+	iov_iter_xarray(iter, dir, xarray, 0, size);
+}
+
+static struct xarray *iov_kunit_create_xarray(struct kunit *test)
+{
+	struct xarray *xarray;
+
+	xarray = kzalloc(sizeof(struct xarray), GFP_KERNEL);
+	xa_init(xarray);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xarray);
+	kunit_add_action_or_reset(test, iov_kunit_destroy_xarray, xarray);
+	return xarray;
+}
+
+/*
+ * Test copying to a ITER_XARRAY-type iterator.
+ */
+static void __init iov_kunit_copy_to_xarray(struct kunit *test)
+{
+	const struct kvec_test_range *pr;
+	struct iov_iter iter;
+	struct xarray *xarray;
+	struct page **spages, **bpages;
+	u8 *scratch, *buffer;
+	size_t bufsize, npages, size, copied;
+	int i, patt;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	xarray = iov_kunit_create_xarray(test);
+
+	scratch = iov_kunit_create_buffer(test, &spages, npages);
+	for (i = 0; i < bufsize; i++)
+		scratch[i] = pattern(i);
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+	memset(buffer, 0, bufsize);
+
+	iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages);
+
+	i = 0;
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+		size = pr->to - pr->from;
+		KUNIT_ASSERT_LE(test, pr->to, bufsize);
+
+		iov_iter_xarray(&iter, READ, xarray, pr->from, size);
+		copied = copy_to_iter(scratch + i, size, &iter);
+
+		KUNIT_EXPECT_EQ(test, copied, size);
+		KUNIT_EXPECT_EQ(test, iter.count, 0);
+		KUNIT_EXPECT_EQ(test, iter.iov_offset, size);
+		i += size;
+	}
+
+	/* Build the expected image in the scratch buffer. */
+	patt = 0;
+	memset(scratch, 0, bufsize);
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++)
+		for (i = pr->from; i < pr->to; i++)
+			scratch[i] = pattern(patt++);
+
+	/* Compare the images */
+	for (i = 0; i < bufsize; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i);
+		if (buffer[i] != scratch[i])
+			return;
+	}
+
+	KUNIT_SUCCEED();
+}
+
+/*
+ * Test copying from a ITER_XARRAY-type iterator.
+ */
+static void __init iov_kunit_copy_from_xarray(struct kunit *test)
+{
+	const struct kvec_test_range *pr;
+	struct iov_iter iter;
+	struct xarray *xarray;
+	struct page **spages, **bpages;
+	u8 *scratch, *buffer;
+	size_t bufsize, npages, size, copied;
+	int i, j;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	xarray = iov_kunit_create_xarray(test);
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+	for (i = 0; i < bufsize; i++)
+		buffer[i] = pattern(i);
+
+	scratch = iov_kunit_create_buffer(test, &spages, npages);
+	memset(scratch, 0, bufsize);
+
+	iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages);
+
+	i = 0;
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+		size = pr->to - pr->from;
+		KUNIT_ASSERT_LE(test, pr->to, bufsize);
+
+		iov_iter_xarray(&iter, WRITE, xarray, pr->from, size);
+		copied = copy_from_iter(scratch + i, size, &iter);
+
+		KUNIT_EXPECT_EQ(test, copied, size);
+		KUNIT_EXPECT_EQ(test, iter.count, 0);
+		KUNIT_EXPECT_EQ(test, iter.iov_offset, size);
+		i += size;
+	}
+
+	/* Build the expected image in the main buffer. */
+	i = 0;
+	memset(buffer, 0, bufsize);
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+		for (j = pr->from; j < pr->to; j++) {
+			buffer[i++] = pattern(j);
+			if (i >= bufsize)
+				goto stop;
+		}
+	}
+stop:
+
+	/* Compare the images */
+	for (i = 0; i < bufsize; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i);
+		if (scratch[i] != buffer[i])
+			return;
+	}
+
+	KUNIT_SUCCEED();
+}
+
+static struct kunit_case __refdata iov_kunit_cases[] = {
+	KUNIT_CASE(iov_kunit_copy_to_kvec),
+	KUNIT_CASE(iov_kunit_copy_from_kvec),
+	KUNIT_CASE(iov_kunit_copy_to_bvec),
+	KUNIT_CASE(iov_kunit_copy_from_bvec),
+	KUNIT_CASE(iov_kunit_copy_to_xarray),
+	KUNIT_CASE(iov_kunit_copy_from_xarray),
+	{}
+};
+
+static struct kunit_suite iov_kunit_suite = {
+	.name = "iov_iter",
+	.test_cases = iov_kunit_cases,
+};
+
+kunit_test_suites(&iov_kunit_suite);


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] iov_iter: Kunit tests for page extraction
  2023-09-08 16:03 [PATCH 0/3] iov_iter: Add kunit tests and fix iov_iter_extract_pages() David Howells
  2023-09-08 16:03 ` [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages() David Howells
  2023-09-08 16:03 ` [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator David Howells
@ 2023-09-08 16:03 ` David Howells
  2 siblings, 0 replies; 6+ messages in thread
From: David Howells @ 2023-09-08 16:03 UTC (permalink / raw)
  To: Jens Axboe, Al Viro, Christoph Hellwig
  Cc: David Howells, Matthew Wilcox, Christian Brauner,
	David Hildenbrand, John Hubbard, Jeff Layton, Linus Torvalds,
	linux-fsdevel, linux-block, linux-kselftest, linux-mm,
	linux-kernel

Add some kunit tests for page extraction for ITER_BVEC, ITER_KVEC and
ITER_XARRAY type iterators.  ITER_UBUF and ITER_IOVEC aren't dealt with as
they require userspace VM interaction.  ITER_DISCARD isn't dealt with
either as that can't be extracted.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Christoph Hellwig <hch@lst.de>
cc: Christian Brauner <brauner@kernel.org>
cc: Jens Axboe <axboe@kernel.dk>
cc: Al Viro <viro@zeniv.linux.org.uk>
cc: David Hildenbrand <david@redhat.com>
cc: John Hubbard <jhubbard@nvidia.com>
cc: linux-mm@kvack.org
cc: linux-block@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
---
 lib/kunit_iov_iter.c | 240 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 240 insertions(+)

diff --git a/lib/kunit_iov_iter.c b/lib/kunit_iov_iter.c
index 25a910187d17..859b67c4d697 100644
--- a/lib/kunit_iov_iter.c
+++ b/lib/kunit_iov_iter.c
@@ -519,6 +519,243 @@ static void __init iov_kunit_copy_from_xarray(struct kunit *test)
 	KUNIT_SUCCEED();
 }
 
+/*
+ * Test the extraction of ITER_KVEC-type iterators.
+ */
+static void __init iov_kunit_extract_pages_kvec(struct kunit *test)
+{
+	const struct kvec_test_range *pr;
+	struct iov_iter iter;
+	struct page **bpages, *pagelist[8], **pages = pagelist;
+	struct kvec kvec[8];
+	u8 *buffer;
+	ssize_t len;
+	size_t bufsize, size = 0, npages;
+	int i, from;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	buffer = iov_kunit_create_buffer(test, &bpages, npages);
+
+	iov_kunit_load_kvec(test, &iter, READ, kvec, ARRAY_SIZE(kvec),
+			    buffer, bufsize, kvec_test_ranges);
+	size = iter.count;
+
+	pr = kvec_test_ranges;
+	from = pr->from;
+	do {
+		size_t offset0 = LONG_MAX;
+
+		for (i = 0; i < ARRAY_SIZE(pagelist); i++)
+			pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL;
+
+		len = iov_iter_extract_pages(&iter, &pages, 100 * 1024,
+					     ARRAY_SIZE(pagelist), 0, &offset0);
+		KUNIT_EXPECT_GE(test, len, 0);
+		if (len < 0)
+			break;
+		KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0);
+		KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE);
+		KUNIT_EXPECT_LE(test, len, size);
+		KUNIT_EXPECT_EQ(test, iter.count, size - len);
+		size -= len;
+
+		if (len == 0)
+			break;
+
+		for (i = 0; i < ARRAY_SIZE(pagelist); i++) {
+			struct page *p;
+			ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0);
+			int ix;
+
+			KUNIT_ASSERT_GE(test, part, 0);
+			while (from == pr->to) {
+				pr++;
+				from = pr->from;
+				if (from < 0)
+					goto stop;
+			}
+			ix = from / PAGE_SIZE;
+			KUNIT_ASSERT_LT(test, ix, npages);
+			p = bpages[ix];
+			KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p);
+			KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE);
+			from += part;
+			len -= part;
+			KUNIT_ASSERT_GE(test, len, 0);
+			if (len == 0)
+				break;
+			offset0 = 0;
+		}
+
+		if (test->status == KUNIT_FAILURE)
+			break;
+	} while (iov_iter_count(&iter) > 0);
+
+stop:
+	KUNIT_EXPECT_EQ(test, size, 0);
+	KUNIT_EXPECT_EQ(test, iter.count, 0);
+	KUNIT_SUCCEED();
+}
+
+/*
+ * Test the extraction of ITER_BVEC-type iterators.
+ */
+static void __init iov_kunit_extract_pages_bvec(struct kunit *test)
+{
+	const struct bvec_test_range *pr;
+	struct iov_iter iter;
+	struct page **bpages, *pagelist[8], **pages = pagelist;
+	struct bio_vec bvec[8];
+	ssize_t len;
+	size_t bufsize, size = 0, npages;
+	int i, from;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	iov_kunit_create_buffer(test, &bpages, npages);
+	iov_kunit_load_bvec(test, &iter, READ, bvec, ARRAY_SIZE(bvec),
+			    bpages, npages, bufsize, bvec_test_ranges);
+	size = iter.count;
+
+	pr = bvec_test_ranges;
+	from = pr->from;
+	do {
+		size_t offset0 = LONG_MAX;
+
+		for (i = 0; i < ARRAY_SIZE(pagelist); i++)
+			pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL;
+
+		len = iov_iter_extract_pages(&iter, &pages, 100 * 1024,
+					     ARRAY_SIZE(pagelist), 0, &offset0);
+		KUNIT_EXPECT_GE(test, len, 0);
+		if (len < 0)
+			break;
+		KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0);
+		KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE);
+		KUNIT_EXPECT_LE(test, len, size);
+		KUNIT_EXPECT_EQ(test, iter.count, size - len);
+		size -= len;
+
+		if (len == 0)
+			break;
+
+		for (i = 0; i < ARRAY_SIZE(pagelist); i++) {
+			struct page *p;
+			ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0);
+			int ix;
+
+			KUNIT_ASSERT_GE(test, part, 0);
+			while (from == pr->to) {
+				pr++;
+				from = pr->from;
+				if (from < 0)
+					goto stop;
+			}
+			ix = pr->page + from / PAGE_SIZE;
+			KUNIT_ASSERT_LT(test, ix, npages);
+			p = bpages[ix];
+			KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p);
+			KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE);
+			from += part;
+			len -= part;
+			KUNIT_ASSERT_GE(test, len, 0);
+			if (len == 0)
+				break;
+			offset0 = 0;
+		}
+
+		if (test->status == KUNIT_FAILURE)
+			break;
+	} while (iov_iter_count(&iter) > 0);
+
+stop:
+	KUNIT_EXPECT_EQ(test, size, 0);
+	KUNIT_EXPECT_EQ(test, iter.count, 0);
+	KUNIT_SUCCEED();
+}
+
+/*
+ * Test the extraction of ITER_XARRAY-type iterators.
+ */
+static void __init iov_kunit_extract_pages_xarray(struct kunit *test)
+{
+	const struct kvec_test_range *pr;
+	struct iov_iter iter;
+	struct xarray *xarray;
+	struct page **bpages, *pagelist[8], **pages = pagelist;
+	ssize_t len;
+	size_t bufsize, size = 0, npages;
+	int i, from;
+
+	bufsize = 0x100000;
+	npages = bufsize / PAGE_SIZE;
+
+	xarray = iov_kunit_create_xarray(test);
+
+	iov_kunit_create_buffer(test, &bpages, npages);
+	iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages);
+
+	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+		from = pr->from;
+		size = pr->to - from;
+		KUNIT_ASSERT_LE(test, pr->to, bufsize);
+
+		iov_iter_xarray(&iter, WRITE, xarray, from, size);
+
+		do {
+			size_t offset0 = LONG_MAX;
+
+			for (i = 0; i < ARRAY_SIZE(pagelist); i++)
+				pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL;
+
+			len = iov_iter_extract_pages(&iter, &pages, 100 * 1024,
+						     ARRAY_SIZE(pagelist), 0, &offset0);
+			KUNIT_EXPECT_GE(test, len, 0);
+			if (len < 0)
+				break;
+			KUNIT_EXPECT_LE(test, len, size);
+			KUNIT_EXPECT_EQ(test, iter.count, size - len);
+			if (len == 0)
+				break;
+			size -= len;
+			KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0);
+			KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE);
+
+			for (i = 0; i < ARRAY_SIZE(pagelist); i++) {
+				struct page *p;
+				ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0);
+				int ix;
+
+				KUNIT_ASSERT_GE(test, part, 0);
+				ix = from / PAGE_SIZE;
+				KUNIT_ASSERT_LT(test, ix, npages);
+				p = bpages[ix];
+				KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p);
+				KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE);
+				from += part;
+				len -= part;
+				KUNIT_ASSERT_GE(test, len, 0);
+				if (len == 0)
+					break;
+				offset0 = 0;
+			}
+
+			if (test->status == KUNIT_FAILURE)
+				goto stop;
+		} while (iov_iter_count(&iter) > 0);
+
+		KUNIT_EXPECT_EQ(test, size, 0);
+		KUNIT_EXPECT_EQ(test, iter.count, 0);
+		KUNIT_EXPECT_EQ(test, iter.iov_offset, pr->to - pr->from);
+	}
+
+stop:
+	KUNIT_SUCCEED();
+}
+
 static struct kunit_case __refdata iov_kunit_cases[] = {
 	KUNIT_CASE(iov_kunit_copy_to_kvec),
 	KUNIT_CASE(iov_kunit_copy_from_kvec),
@@ -526,6 +763,9 @@ static struct kunit_case __refdata iov_kunit_cases[] = {
 	KUNIT_CASE(iov_kunit_copy_from_bvec),
 	KUNIT_CASE(iov_kunit_copy_to_xarray),
 	KUNIT_CASE(iov_kunit_copy_from_xarray),
+	KUNIT_CASE(iov_kunit_extract_pages_kvec),
+	KUNIT_CASE(iov_kunit_extract_pages_bvec),
+	KUNIT_CASE(iov_kunit_extract_pages_xarray),
 	{}
 };
 


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages()
  2023-09-08 16:03 ` [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages() David Howells
@ 2023-09-09 11:27   ` Christoph Hellwig
  0 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2023-09-09 11:27 UTC (permalink / raw)
  To: David Howells
  Cc: Jens Axboe, Al Viro, Christoph Hellwig, Matthew Wilcox,
	Christian Brauner, David Hildenbrand, John Hubbard, Jeff Layton,
	Linus Torvalds, linux-fsdevel, linux-block, linux-kselftest,
	linux-mm, linux-kernel

On Fri, Sep 08, 2023 at 05:03:20PM +0100, David Howells wrote:
> iov_iter_extract_pages() doesn't correctly handle skipping over initial
> zero-length entries in ITER_KVEC and ITER_BVEC-type iterators.

Maybe put a little of that into the subject.  Fix $foo only makes sense
when it is completely broken and not for a corner case.

i.e.

iov_iter: skip over leading empty vecs in iov_iter_extract_pages

The change itself looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator
  2023-09-08 16:03 ` [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator David Howells
@ 2023-09-09 11:30   ` Christoph Hellwig
  0 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2023-09-09 11:30 UTC (permalink / raw)
  To: David Howells
  Cc: Jens Axboe, Al Viro, Christoph Hellwig, Matthew Wilcox,
	Christian Brauner, David Hildenbrand, John Hubbard, Jeff Layton,
	Linus Torvalds, linux-fsdevel, linux-block, linux-kselftest,
	linux-mm, linux-kernel

> +/* I/O iterator tests.  This can only test kernel-backed iterator types.

kernel comments start with a:

/*

and nothing else on the line.  (for brevity I'm not going to repeat
the comment for the rest of this series)

> +static const struct kvec_test_range kvec_test_ranges[] = {
> +	{ 0x00002, 0x00002 },
> +	{ 0x00027, 0x03000 },
> +	{ 0x05193, 0x18794 },
> +	{ 0x20000, 0x20000 },
> +	{ 0x20000, 0x24000 },
> +	{ 0x24000, 0x27001 },
> +	{ 0x29000, 0xffffb },
> +	{ 0xffffd, 0xffffe },

How were these values picked?  Should there be a comment explaining them?

> +	buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL);
> +        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);

The KUNIT_ASSERT_NOT_ERR_OR_NULL seems misindented.

> + */
> +static void __init iov_kunit_copy_to_bvec(struct kunit *test)
> +{
> +	const struct bvec_test_range *pr;
> +	struct iov_iter iter;
> +	struct bio_vec bvec[8];
> +	struct page **spages, **bpages;
> +	u8 *scratch, *buffer;
> +	size_t bufsize, npages, size, copied;
> +	int i, b, patt;
> +
> +	bufsize = 0x100000;
> +	npages = bufsize / PAGE_SIZE;
> +
> +	scratch = iov_kunit_create_buffer(test, &spages, npages);
> +	for (i = 0; i < bufsize; i++)
> +		scratch[i] = pattern(i);
> +
> +	buffer = iov_kunit_create_buffer(test, &bpages, npages);
> +	memset(buffer, 0, bufsize);
> +
> +	iov_kunit_load_bvec(test, &iter, READ, bvec, ARRAY_SIZE(bvec),
> +			    bpages, npages, bufsize, bvec_test_ranges);
> +	size = iter.count;
> +
> +	copied = copy_to_iter(scratch, size, &iter);
> +
> +	KUNIT_EXPECT_EQ(test, copied, size);
> +	KUNIT_EXPECT_EQ(test, iter.count, 0);
> +	KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
> +
> +	/* Build the expected image in the scratch buffer. */
> +	b = 0;
> +	patt = 0;
> +	memset(scratch, 0, bufsize);
> +	for (pr = bvec_test_ranges; pr->from >= 0; pr++, b++) {
> +		u8 *p = scratch + pr->page * PAGE_SIZE;
> +
> +		for (i = pr->from; i < pr->to; i++)
> +			p[i] = pattern(patt++);
> +	}
> +
> +	/* Compare the images */
> +	for (i = 0; i < bufsize; i++) {
> +		KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i);
> +		if (buffer[i] != scratch[i])
> +			return;
> +	}
> +
> +	KUNIT_SUCCEED();
> +}
> +
> +/*
> + * Test copying from a ITER_BVEC-type iterator.
> + */
> +static void __init iov_kunit_copy_from_bvec(struct kunit *test)
> +{
> +	const struct bvec_test_range *pr;
> +	struct iov_iter iter;
> +	struct bio_vec bvec[8];
> +	struct page **spages, **bpages;
> +	u8 *scratch, *buffer;
> +	size_t bufsize, npages, size, copied;
> +	int i, j;
> +
> +	bufsize = 0x100000;
> +	npages = bufsize / PAGE_SIZE;
> +
> +	buffer = iov_kunit_create_buffer(test, &bpages, npages);
> +	for (i = 0; i < bufsize; i++)
> +		buffer[i] = pattern(i);
> +
> +	scratch = iov_kunit_create_buffer(test, &spages, npages);
> +	memset(scratch, 0, bufsize);
> +
> +	iov_kunit_load_bvec(test, &iter, WRITE, bvec, ARRAY_SIZE(bvec),
> +			    bpages, npages, bufsize, bvec_test_ranges);
> +	size = iter.count;
> +
> +	copied = copy_from_iter(scratch, size, &iter);
> +
> +	KUNIT_EXPECT_EQ(test, copied, size);
> +	KUNIT_EXPECT_EQ(test, iter.count, 0);
> +	KUNIT_EXPECT_EQ(test, iter.nr_segs, 0);
> +
> +	/* Build the expected image in the main buffer. */
> +	i = 0;
> +	memset(buffer, 0, bufsize);
> +	for (pr = bvec_test_ranges; pr->from >= 0; pr++) {
> +		size_t patt = pr->page * PAGE_SIZE;
> +
> +		for (j = pr->from; j < pr->to; j++) {
> +			buffer[i++] = pattern(patt + j);
> +			if (i >= bufsize)
> +				goto stop;
> +		}
> +	}
> +stop:
> +
> +	/* Compare the images */
> +	for (i = 0; i < bufsize; i++) {
> +		KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i);
> +		if (scratch[i] != buffer[i])
> +			return;
> +	}
> +
> +	KUNIT_SUCCEED();
> +}
> +
> +static void iov_kunit_destroy_xarray(void *data)
> +{
> +	struct xarray *xarray = data;
> +
> +	xa_destroy(xarray);
> +	kfree(xarray);
> +}
> +
> +static void __init iov_kunit_load_xarray(struct kunit *test,
> +					 struct iov_iter *iter, int dir,
> +					 struct xarray *xarray,
> +					 struct page **pages, size_t npages)
> +{
> +	size_t size = 0;
> +	int i;
> +
> +	for (i = 0; i < npages; i++) {
> +		void *x = xa_store(xarray, i, pages[i], GFP_KERNEL);
> +
> +		KUNIT_ASSERT_FALSE(test, xa_is_err(x));
> +		size += PAGE_SIZE;
> +	}
> +	iov_iter_xarray(iter, dir, xarray, 0, size);
> +}
> +
> +static struct xarray *iov_kunit_create_xarray(struct kunit *test)
> +{
> +	struct xarray *xarray;
> +
> +	xarray = kzalloc(sizeof(struct xarray), GFP_KERNEL);
> +	xa_init(xarray);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xarray);
> +	kunit_add_action_or_reset(test, iov_kunit_destroy_xarray, xarray);
> +	return xarray;
> +}
> +
> +/*
> + * Test copying to a ITER_XARRAY-type iterator.
> + */
> +static void __init iov_kunit_copy_to_xarray(struct kunit *test)
> +{
> +	const struct kvec_test_range *pr;
> +	struct iov_iter iter;
> +	struct xarray *xarray;
> +	struct page **spages, **bpages;
> +	u8 *scratch, *buffer;
> +	size_t bufsize, npages, size, copied;
> +	int i, patt;
> +
> +	bufsize = 0x100000;
> +	npages = bufsize / PAGE_SIZE;
> +
> +	xarray = iov_kunit_create_xarray(test);
> +
> +	scratch = iov_kunit_create_buffer(test, &spages, npages);
> +	for (i = 0; i < bufsize; i++)
> +		scratch[i] = pattern(i);
> +
> +	buffer = iov_kunit_create_buffer(test, &bpages, npages);
> +	memset(buffer, 0, bufsize);
> +
> +	iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages);
> +
> +	i = 0;
> +	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
> +		size = pr->to - pr->from;
> +		KUNIT_ASSERT_LE(test, pr->to, bufsize);
> +
> +		iov_iter_xarray(&iter, READ, xarray, pr->from, size);
> +		copied = copy_to_iter(scratch + i, size, &iter);
> +
> +		KUNIT_EXPECT_EQ(test, copied, size);
> +		KUNIT_EXPECT_EQ(test, iter.count, 0);
> +		KUNIT_EXPECT_EQ(test, iter.iov_offset, size);
> +		i += size;
> +	}
> +
> +	/* Build the expected image in the scratch buffer. */
> +	patt = 0;
> +	memset(scratch, 0, bufsize);
> +	for (pr = kvec_test_ranges; pr->from >= 0; pr++)
> +		for (i = pr->from; i < pr->to; i++)
> +			scratch[i] = pattern(patt++);
> +
> +	/* Compare the images */
> +	for (i = 0; i < bufsize; i++) {
> +		KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i);
> +		if (buffer[i] != scratch[i])
> +			return;
> +	}
> +
> +	KUNIT_SUCCEED();
> +}
> +
> +/*
> + * Test copying from a ITER_XARRAY-type iterator.
> + */
> +static void __init iov_kunit_copy_from_xarray(struct kunit *test)
> +{
> +	const struct kvec_test_range *pr;
> +	struct iov_iter iter;
> +	struct xarray *xarray;
> +	struct page **spages, **bpages;
> +	u8 *scratch, *buffer;
> +	size_t bufsize, npages, size, copied;
> +	int i, j;
> +
> +	bufsize = 0x100000;
> +	npages = bufsize / PAGE_SIZE;
> +
> +	xarray = iov_kunit_create_xarray(test);
> +
> +	buffer = iov_kunit_create_buffer(test, &bpages, npages);
> +	for (i = 0; i < bufsize; i++)
> +		buffer[i] = pattern(i);
> +
> +	scratch = iov_kunit_create_buffer(test, &spages, npages);
> +	memset(scratch, 0, bufsize);
> +
> +	iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages);
> +
> +	i = 0;
> +	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
> +		size = pr->to - pr->from;
> +		KUNIT_ASSERT_LE(test, pr->to, bufsize);
> +
> +		iov_iter_xarray(&iter, WRITE, xarray, pr->from, size);
> +		copied = copy_from_iter(scratch + i, size, &iter);
> +
> +		KUNIT_EXPECT_EQ(test, copied, size);
> +		KUNIT_EXPECT_EQ(test, iter.count, 0);
> +		KUNIT_EXPECT_EQ(test, iter.iov_offset, size);
> +		i += size;
> +	}
> +
> +	/* Build the expected image in the main buffer. */
> +	i = 0;
> +	memset(buffer, 0, bufsize);
> +	for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
> +		for (j = pr->from; j < pr->to; j++) {
> +			buffer[i++] = pattern(j);
> +			if (i >= bufsize)
> +				goto stop;
> +		}
> +	}
> +stop:
> +
> +	/* Compare the images */
> +	for (i = 0; i < bufsize; i++) {
> +		KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i);
> +		if (scratch[i] != buffer[i])
> +			return;
> +	}
> +
> +	KUNIT_SUCCEED();
> +}
> +
> +static struct kunit_case __refdata iov_kunit_cases[] = {
> +	KUNIT_CASE(iov_kunit_copy_to_kvec),
> +	KUNIT_CASE(iov_kunit_copy_from_kvec),
> +	KUNIT_CASE(iov_kunit_copy_to_bvec),
> +	KUNIT_CASE(iov_kunit_copy_from_bvec),
> +	KUNIT_CASE(iov_kunit_copy_to_xarray),
> +	KUNIT_CASE(iov_kunit_copy_from_xarray),
> +	{}
> +};
> +
> +static struct kunit_suite iov_kunit_suite = {
> +	.name = "iov_iter",
> +	.test_cases = iov_kunit_cases,
> +};
> +
> +kunit_test_suites(&iov_kunit_suite);
---end quoted text---

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-09-09 11:30 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-09-08 16:03 [PATCH 0/3] iov_iter: Add kunit tests and fix iov_iter_extract_pages() David Howells
2023-09-08 16:03 ` [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages() David Howells
2023-09-09 11:27   ` Christoph Hellwig
2023-09-08 16:03 ` [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator David Howells
2023-09-09 11:30   ` Christoph Hellwig
2023-09-08 16:03 ` [PATCH 3/3] iov_iter: Kunit tests for page extraction David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).