From nobody Thu Dec 18 20:15:46 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8BE0CEE801A for ; Fri, 8 Sep 2023 16:04:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244813AbjIHQEb (ORCPT ); Fri, 8 Sep 2023 12:04:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44720 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244761AbjIHQE2 (ORCPT ); Fri, 8 Sep 2023 12:04:28 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ED96C1FC7 for ; Fri, 8 Sep 2023 09:03:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1694189014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8QArWqYjany3/3Kdac4mEmfhyAbXGy4THe9mhs3GeTM=; b=e0iuiqJHw9PE4t8tqKJ5M6bEPoHYRO4uuDQt3psuYciimwBeuUgjHVgXDZK96q7StvCuTh rZ3EEpFv840C7RdzaEEl8qkoKIt81oc7WGMauK5hTMddVbwyzEhQRDxQiliPbKi3jFM4MR w20S/YZ1LYyCkJxyJW3hrOhMK89AvT4= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-230-ChoFSJu6N1CkSRVz_hnIRQ-1; Fri, 08 Sep 2023 12:03:32 -0400 X-MC-Unique: ChoFSJu6N1CkSRVz_hnIRQ-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id C7392101CA84; Fri, 8 Sep 2023 16:03:31 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.148]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0C70F2026D4B; Fri, 8 Sep 2023 16:03:29 +0000 (UTC) From: David Howells To: Jens Axboe , Al Viro , Christoph Hellwig Cc: David Howells , Matthew Wilcox , Christian Brauner , David Hildenbrand , John Hubbard , Jeff Layton , Linus Torvalds , linux-fsdevel@vger.kernel.org, linux-block@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/3] iov_iter: Fix iov_iter_extract_pages() Date: Fri, 8 Sep 2023 17:03:20 +0100 Message-ID: <20230908160322.1714302-2-dhowells@redhat.com> In-Reply-To: <20230908160322.1714302-1-dhowells@redhat.com> References: <20230908160322.1714302-1-dhowells@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.1 on 10.11.54.4 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 cc: Christoph Hellwig cc: Christian Brauner cc: Jens Axboe cc: Al Viro cc: David Hildenbrand cc: John Hubbard cc: linux-mm@kvack.org cc: linux-block@vger.kernel.org cc: linux-fsdevel@vger.kernel.org Reviewed-by: Christoph Hellwig --- 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 i= ov_iter *i, size_t *offset0) { struct page **p, *page; - size_t skip =3D i->iov_offset, offset; + size_t skip =3D i->iov_offset, offset, size; int k; =20 for (;;) { if (i->nr_segs =3D=3D 0) return 0; - maxsize =3D min(maxsize, i->bvec->bv_len - skip); - if (maxsize) + size =3D min(maxsize, i->bvec->bv_len - skip); + if (size) break; i->iov_offset =3D 0; i->nr_segs--; @@ -1674,16 +1674,16 @@ static ssize_t iov_iter_extract_bvec_pages(struct i= ov_iter *i, offset =3D skip % PAGE_SIZE; *offset0 =3D offset; =20 - maxpages =3D want_pages_array(pages, maxsize, offset, maxpages); + maxpages =3D want_pages_array(pages, size, offset, maxpages); if (!maxpages) return -ENOMEM; p =3D *pages; for (k =3D 0; k < maxpages; k++) p[k] =3D page + k; =20 - maxsize =3D min_t(size_t, maxsize, maxpages * PAGE_SIZE - offset); - iov_iter_advance(i, maxsize); - return maxsize; + size =3D min_t(size_t, size, maxpages * PAGE_SIZE - offset); + iov_iter_advance(i, size); + return size; } =20 /* @@ -1698,14 +1698,14 @@ static ssize_t iov_iter_extract_kvec_pages(struct i= ov_iter *i, { struct page **p, *page; const void *kaddr; - size_t skip =3D i->iov_offset, offset, len; + size_t skip =3D i->iov_offset, offset, len, size; int k; =20 for (;;) { if (i->nr_segs =3D=3D 0) return 0; - maxsize =3D min(maxsize, i->kvec->iov_len - skip); - if (maxsize) + size =3D min(maxsize, i->kvec->iov_len - skip); + if (size) break; i->iov_offset =3D 0; i->nr_segs--; @@ -1717,13 +1717,13 @@ static ssize_t iov_iter_extract_kvec_pages(struct i= ov_iter *i, offset =3D (unsigned long)kaddr & ~PAGE_MASK; *offset0 =3D offset; =20 - maxpages =3D want_pages_array(pages, maxsize, offset, maxpages); + maxpages =3D want_pages_array(pages, size, offset, maxpages); if (!maxpages) return -ENOMEM; p =3D *pages; =20 kaddr -=3D offset; - len =3D offset + maxsize; + len =3D offset + size; for (k =3D 0; k < maxpages; k++) { size_t seg =3D min_t(size_t, len, PAGE_SIZE); =20 @@ -1737,9 +1737,9 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov= _iter *i, kaddr +=3D PAGE_SIZE; } =20 - maxsize =3D min_t(size_t, maxsize, maxpages * PAGE_SIZE - offset); - iov_iter_advance(i, maxsize); - return maxsize; + size =3D min_t(size_t, size, maxpages * PAGE_SIZE - offset); + iov_iter_advance(i, size); + return size; } =20 /* From nobody Thu Dec 18 20:15:46 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 980DCEE801C for ; Fri, 8 Sep 2023 16:04:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244848AbjIHQEf (ORCPT ); Fri, 8 Sep 2023 12:04:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44796 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244806AbjIHQEc (ORCPT ); Fri, 8 Sep 2023 12:04:32 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8BE201FEE for ; Fri, 8 Sep 2023 09:03:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1694189020; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5fxSxbVLUpbcc4PTXWiXVoOmdxGdublXXMjuvnbuZd0=; b=UhIg+43dfZT/ef34K251jWen0j3MucQoqPZ2o00NBevMTVWUKoTjbfGYA+MimptuQUgVav PSt1lv6MVleoRQAQCsqwwXVPmkkNH6B4JbdB/gUjML7iv8vORdXOk8oa0MZtv9KO0xaj8J HDDusWRNOqtrYqDl4gSlrM8XnakpoHU= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-222-xhhcDnLGMYCZ_-APKhYyfA-1; Fri, 08 Sep 2023 12:03:35 -0400 X-MC-Unique: xhhcDnLGMYCZ_-APKhYyfA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6EB9F281CC20; Fri, 8 Sep 2023 16:03:34 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.148]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6FCFD140E963; Fri, 8 Sep 2023 16:03:32 +0000 (UTC) From: David Howells To: Jens Axboe , Al Viro , Christoph Hellwig Cc: David Howells , Matthew Wilcox , Christian Brauner , David Hildenbrand , John Hubbard , Jeff Layton , Linus Torvalds , linux-fsdevel@vger.kernel.org, linux-block@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/3] iov_iter: Kunit tests for copying to/from an iterator Date: Fri, 8 Sep 2023 17:03:21 +0100 Message-ID: <20230908160322.1714302-3-dhowells@redhat.com> In-Reply-To: <20230908160322.1714302-1-dhowells@redhat.com> References: <20230908160322.1714302-1-dhowells@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 cc: Christoph Hellwig cc: Christian Brauner cc: Jens Axboe cc: Al Viro cc: David Hildenbrand cc: John Hubbard 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 =20 If unsure, say N. =20 +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) +=3D test_bitops.o CFLAGS_test_bitops.o +=3D -Werror obj-$(CONFIG_CPUMASK_KUNIT_TEST) +=3D cpumask_kunit.o obj-$(CONFIG_TEST_SYSCTL) +=3D test_sysctl.o +obj-$(CONFIG_TEST_IOV_ITER) +=3D kunit_iov_iter.o obj-$(CONFIG_HASH_KUNIT_TEST) +=3D test_hash.o obj-$(CONFIG_TEST_IDA) +=3D test_ida.o obj-$(CONFIG_TEST_UBSAN) +=3D 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 +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("iov_iter testing"); +MODULE_AUTHOR("David Howells "); +MODULE_LICENSE("GPL"); + +struct kvec_test_range { + int from, to; +}; + +static const struct kvec_test_range kvec_test_ranges[] =3D { + { 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 =3D kunit_kcalloc(test, npages, sizeof(struct page *), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); + *ppages =3D pages; + + got =3D alloc_pages_bulk_array(GFP_KERNEL, npages, pages); + if (got !=3D npages) { + release_pages(pages, got); + KUNIT_ASSERT_EQ(test, got, npages); + } + + buffer =3D 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 =3D 0; + int i; + + for (i =3D 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 =3D buffer + pr->from; + kvec[i].iov_len =3D pr->to - pr->from; + size +=3D 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 =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + scratch =3D iov_kunit_create_buffer(test, &spages, npages); + for (i =3D 0; i < bufsize; i++) + scratch[i] =3D pattern(i); + + buffer =3D 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 =3D iter.count; + + copied =3D 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 =3D 0; + memset(scratch, 0, bufsize); + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) + for (i =3D pr->from; i < pr->to; i++) + scratch[i] =3D pattern(patt++); + + /* Compare the images */ + for (i =3D 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=3D%x", i); + if (buffer[i] !=3D 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 =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + buffer =3D iov_kunit_create_buffer(test, &bpages, npages); + for (i =3D 0; i < bufsize; i++) + buffer[i] =3D pattern(i); + + scratch =3D 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 =3D min(iter.count, bufsize); + + copied =3D 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 =3D 0; + memset(buffer, 0, bufsize); + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) { + for (j =3D pr->from; j < pr->to; j++) { + buffer[i++] =3D pattern(j); + if (i >=3D bufsize) + goto stop; + } + } +stop: + + /* Compare the images */ + for (i =3D 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=3D%x", i); + if (scratch[i] !=3D buffer[i]) + return; + } + + KUNIT_SUCCEED(); +} + +struct bvec_test_range { + int page, from, to; +}; + +static const struct bvec_test_range bvec_test_ranges[] =3D { + { 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 =3D NULL, *page; + size_t size =3D 0; + int i; + + for (i =3D 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 =3D pages[pr->page]; + if (pr->from =3D=3D 0 && pr->from !=3D pr->to && page =3D=3D can_merge) { + i--; + bvec[i].bv_len +=3D pr->to; + } else { + bvec_set_page(&bvec[i], page, pr->to - pr->from, pr->from); + } + + size +=3D pr->to - pr->from; + if ((pr->to & ~PAGE_MASK) =3D=3D 0) + can_merge =3D page + pr->to / PAGE_SIZE; + else + can_merge =3D 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 =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + scratch =3D iov_kunit_create_buffer(test, &spages, npages); + for (i =3D 0; i < bufsize; i++) + scratch[i] =3D pattern(i); + + buffer =3D 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 =3D iter.count; + + copied =3D 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 =3D 0; + patt =3D 0; + memset(scratch, 0, bufsize); + for (pr =3D bvec_test_ranges; pr->from >=3D 0; pr++, b++) { + u8 *p =3D scratch + pr->page * PAGE_SIZE; + + for (i =3D pr->from; i < pr->to; i++) + p[i] =3D pattern(patt++); + } + + /* Compare the images */ + for (i =3D 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=3D%x", i); + if (buffer[i] !=3D 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 =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + buffer =3D iov_kunit_create_buffer(test, &bpages, npages); + for (i =3D 0; i < bufsize; i++) + buffer[i] =3D pattern(i); + + scratch =3D 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 =3D iter.count; + + copied =3D 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 =3D 0; + memset(buffer, 0, bufsize); + for (pr =3D bvec_test_ranges; pr->from >=3D 0; pr++) { + size_t patt =3D pr->page * PAGE_SIZE; + + for (j =3D pr->from; j < pr->to; j++) { + buffer[i++] =3D pattern(patt + j); + if (i >=3D bufsize) + goto stop; + } + } +stop: + + /* Compare the images */ + for (i =3D 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=3D%x", i); + if (scratch[i] !=3D buffer[i]) + return; + } + + KUNIT_SUCCEED(); +} + +static void iov_kunit_destroy_xarray(void *data) +{ + struct xarray *xarray =3D 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 =3D 0; + int i; + + for (i =3D 0; i < npages; i++) { + void *x =3D xa_store(xarray, i, pages[i], GFP_KERNEL); + + KUNIT_ASSERT_FALSE(test, xa_is_err(x)); + size +=3D PAGE_SIZE; + } + iov_iter_xarray(iter, dir, xarray, 0, size); +} + +static struct xarray *iov_kunit_create_xarray(struct kunit *test) +{ + struct xarray *xarray; + + xarray =3D 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 =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + xarray =3D iov_kunit_create_xarray(test); + + scratch =3D iov_kunit_create_buffer(test, &spages, npages); + for (i =3D 0; i < bufsize; i++) + scratch[i] =3D pattern(i); + + buffer =3D iov_kunit_create_buffer(test, &bpages, npages); + memset(buffer, 0, bufsize); + + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); + + i =3D 0; + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) { + size =3D pr->to - pr->from; + KUNIT_ASSERT_LE(test, pr->to, bufsize); + + iov_iter_xarray(&iter, READ, xarray, pr->from, size); + copied =3D 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 +=3D size; + } + + /* Build the expected image in the scratch buffer. */ + patt =3D 0; + memset(scratch, 0, bufsize); + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) + for (i =3D pr->from; i < pr->to; i++) + scratch[i] =3D pattern(patt++); + + /* Compare the images */ + for (i =3D 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=3D%x", i); + if (buffer[i] !=3D 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 =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + xarray =3D iov_kunit_create_xarray(test); + + buffer =3D iov_kunit_create_buffer(test, &bpages, npages); + for (i =3D 0; i < bufsize; i++) + buffer[i] =3D pattern(i); + + scratch =3D iov_kunit_create_buffer(test, &spages, npages); + memset(scratch, 0, bufsize); + + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); + + i =3D 0; + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) { + size =3D pr->to - pr->from; + KUNIT_ASSERT_LE(test, pr->to, bufsize); + + iov_iter_xarray(&iter, WRITE, xarray, pr->from, size); + copied =3D 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 +=3D size; + } + + /* Build the expected image in the main buffer. */ + i =3D 0; + memset(buffer, 0, bufsize); + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) { + for (j =3D pr->from; j < pr->to; j++) { + buffer[i++] =3D pattern(j); + if (i >=3D bufsize) + goto stop; + } + } +stop: + + /* Compare the images */ + for (i =3D 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=3D%x", i); + if (scratch[i] !=3D buffer[i]) + return; + } + + KUNIT_SUCCEED(); +} + +static struct kunit_case __refdata iov_kunit_cases[] =3D { + 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 =3D { + .name =3D "iov_iter", + .test_cases =3D iov_kunit_cases, +}; + +kunit_test_suites(&iov_kunit_suite); From nobody Thu Dec 18 20:15:46 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6415FEE8019 for ; Fri, 8 Sep 2023 16:04:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244870AbjIHQEh (ORCPT ); Fri, 8 Sep 2023 12:04:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244819AbjIHQEe (ORCPT ); Fri, 8 Sep 2023 12:04:34 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 29D281FF0 for ; Fri, 8 Sep 2023 09:03:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1694189021; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=c5qKGDJOH0h32jOCU9/toIJ7m/LhliQCTlgTlpbMOlk=; b=W6xRGmayA4FT+2V13upy+5Y33uUoKvs5sdVL/SQl/70ZQB5Or/tR1qfmHj6sKvZShMfJ2j B0O5OeCbO3nJCoJzrCrYfaeCe/mlLslhNCn1b8uDDIfC/D4oIjnJmsWaf37KeAKphzOQHS 33KNG9ZgfJFbILXMh+pDPtNQ36txTa0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-614-qVvRZSWPMUOFjFvB_AMgfg-1; Fri, 08 Sep 2023 12:03:38 -0400 X-MC-Unique: qVvRZSWPMUOFjFvB_AMgfg-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id F2BAC101A529; Fri, 8 Sep 2023 16:03:36 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.148]) by smtp.corp.redhat.com (Postfix) with ESMTP id 25E5FC03295; Fri, 8 Sep 2023 16:03:35 +0000 (UTC) From: David Howells To: Jens Axboe , Al Viro , Christoph Hellwig Cc: David Howells , Matthew Wilcox , Christian Brauner , David Hildenbrand , John Hubbard , Jeff Layton , Linus Torvalds , linux-fsdevel@vger.kernel.org, linux-block@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/3] iov_iter: Kunit tests for page extraction Date: Fri, 8 Sep 2023 17:03:22 +0100 Message-ID: <20230908160322.1714302-4-dhowells@redhat.com> In-Reply-To: <20230908160322.1714302-1-dhowells@redhat.com> References: <20230908160322.1714302-1-dhowells@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 cc: Christoph Hellwig cc: Christian Brauner cc: Jens Axboe cc: Al Viro cc: David Hildenbrand cc: John Hubbard 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(); } =20 +/* + * 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 =3D pagelist; + struct kvec kvec[8]; + u8 *buffer; + ssize_t len; + size_t bufsize, size =3D 0, npages; + int i, from; + + bufsize =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + buffer =3D iov_kunit_create_buffer(test, &bpages, npages); + + iov_kunit_load_kvec(test, &iter, READ, kvec, ARRAY_SIZE(kvec), + buffer, bufsize, kvec_test_ranges); + size =3D iter.count; + + pr =3D kvec_test_ranges; + from =3D pr->from; + do { + size_t offset0 =3D LONG_MAX; + + for (i =3D 0; i < ARRAY_SIZE(pagelist); i++) + pagelist[i] =3D (void *)(unsigned long)0xaa55aa55aa55aa55ULL; + + len =3D 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 -=3D len; + + if (len =3D=3D 0) + break; + + for (i =3D 0; i < ARRAY_SIZE(pagelist); i++) { + struct page *p; + ssize_t part =3D min_t(ssize_t, len, PAGE_SIZE - offset0); + int ix; + + KUNIT_ASSERT_GE(test, part, 0); + while (from =3D=3D pr->to) { + pr++; + from =3D pr->from; + if (from < 0) + goto stop; + } + ix =3D from / PAGE_SIZE; + KUNIT_ASSERT_LT(test, ix, npages); + p =3D bpages[ix]; + KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p); + KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE); + from +=3D part; + len -=3D part; + KUNIT_ASSERT_GE(test, len, 0); + if (len =3D=3D 0) + break; + offset0 =3D 0; + } + + if (test->status =3D=3D 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 =3D pagelist; + struct bio_vec bvec[8]; + ssize_t len; + size_t bufsize, size =3D 0, npages; + int i, from; + + bufsize =3D 0x100000; + npages =3D 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 =3D iter.count; + + pr =3D bvec_test_ranges; + from =3D pr->from; + do { + size_t offset0 =3D LONG_MAX; + + for (i =3D 0; i < ARRAY_SIZE(pagelist); i++) + pagelist[i] =3D (void *)(unsigned long)0xaa55aa55aa55aa55ULL; + + len =3D 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 -=3D len; + + if (len =3D=3D 0) + break; + + for (i =3D 0; i < ARRAY_SIZE(pagelist); i++) { + struct page *p; + ssize_t part =3D min_t(ssize_t, len, PAGE_SIZE - offset0); + int ix; + + KUNIT_ASSERT_GE(test, part, 0); + while (from =3D=3D pr->to) { + pr++; + from =3D pr->from; + if (from < 0) + goto stop; + } + ix =3D pr->page + from / PAGE_SIZE; + KUNIT_ASSERT_LT(test, ix, npages); + p =3D bpages[ix]; + KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p); + KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE); + from +=3D part; + len -=3D part; + KUNIT_ASSERT_GE(test, len, 0); + if (len =3D=3D 0) + break; + offset0 =3D 0; + } + + if (test->status =3D=3D 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 =3D pagelist; + ssize_t len; + size_t bufsize, size =3D 0, npages; + int i, from; + + bufsize =3D 0x100000; + npages =3D bufsize / PAGE_SIZE; + + xarray =3D iov_kunit_create_xarray(test); + + iov_kunit_create_buffer(test, &bpages, npages); + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); + + for (pr =3D kvec_test_ranges; pr->from >=3D 0; pr++) { + from =3D pr->from; + size =3D pr->to - from; + KUNIT_ASSERT_LE(test, pr->to, bufsize); + + iov_iter_xarray(&iter, WRITE, xarray, from, size); + + do { + size_t offset0 =3D LONG_MAX; + + for (i =3D 0; i < ARRAY_SIZE(pagelist); i++) + pagelist[i] =3D (void *)(unsigned long)0xaa55aa55aa55aa55ULL; + + len =3D 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 =3D=3D 0) + break; + size -=3D len; + KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0); + KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE); + + for (i =3D 0; i < ARRAY_SIZE(pagelist); i++) { + struct page *p; + ssize_t part =3D min_t(ssize_t, len, PAGE_SIZE - offset0); + int ix; + + KUNIT_ASSERT_GE(test, part, 0); + ix =3D from / PAGE_SIZE; + KUNIT_ASSERT_LT(test, ix, npages); + p =3D bpages[ix]; + KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p); + KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE); + from +=3D part; + len -=3D part; + KUNIT_ASSERT_GE(test, len, 0); + if (len =3D=3D 0) + break; + offset0 =3D 0; + } + + if (test->status =3D=3D 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[] =3D { 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[] = =3D { 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), {} };