Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2216364/?format=api
{ "id": 2216364, "url": "http://patchwork.ozlabs.org/api/patches/2216364/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/20260326104544.509518-13-dhowells@redhat.com/", "project": { "id": 12, "url": "http://patchwork.ozlabs.org/api/projects/12/?format=api", "name": "Linux CIFS Client", "link_name": "linux-cifs-client", "list_id": "linux-cifs.vger.kernel.org", "list_email": "linux-cifs@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260326104544.509518-13-dhowells@redhat.com>", "list_archive_url": null, "date": "2026-03-26T10:45:27", "name": "[12/26] iov_iter: Add a segmented queue of bio_vec[]", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "53473dd609923ce1f3411ed622524adeb0a9224c", "submitter": { "id": 59, "url": "http://patchwork.ozlabs.org/api/people/59/?format=api", "name": "David Howells", "email": "dhowells@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/20260326104544.509518-13-dhowells@redhat.com/mbox/", "series": [ { "id": 497565, "url": "http://patchwork.ozlabs.org/api/series/497565/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/list/?series=497565", "date": "2026-03-26T10:45:15", "name": "netfs: Keep track of folios in a segmented bio_vec[] chain", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/497565/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2216364/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2216364/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-cifs+bounces-10535-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-cifs@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=CPiFdP5J;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c0a:e001:db::12fc:5321; helo=sea.lore.kernel.org;\n envelope-from=linux-cifs+bounces-10535-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com\n header.b=\"CPiFdP5J\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=170.10.129.124", "smtp.subspace.kernel.org;\n dmarc=pass (p=quarantine dis=none) header.from=redhat.com", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=redhat.com" ], "Received": [ "from sea.lore.kernel.org (sea.lore.kernel.org\n [IPv6:2600:3c0a:e001:db::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fhLf625JMz1yGD\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 26 Mar 2026 22:10:06 +1100 (AEDT)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 104663185506\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 26 Mar 2026 10:51:07 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 5F0E43DDDC4;\n\tThu, 26 Mar 2026 10:48:03 +0000 (UTC)", "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id E110638AC6A\n\tfor <linux-cifs@vger.kernel.org>; Thu, 26 Mar 2026 10:48:00 +0000 (UTC)", "from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-489-zh8E2yOlOx6RfvhMNxM4VQ-1; Thu,\n 26 Mar 2026 06:47:55 -0400", "from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 1A08C1800464;\n\tThu, 26 Mar 2026 10:47:52 +0000 (UTC)", "from warthog.procyon.org.com (unknown [10.44.33.121])\n\tby mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id CCC0E19560B1;\n\tThu, 26 Mar 2026 10:47:44 +0000 (UTC)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774522083; cv=none;\n b=ieiFhc1mXjy4sBQ8KvWuBj9aa+QMFoob6LquCe8b5EqpGu+JO4wPYWF5fOGzBcXuhkro0mqN8AtiJerSBprWUdAR3CFqqGtIzoZ5grP6yvNsW4gt947x6zmbacO+NR3fHQOb3n2OyB9wagn3a6zJ/QJjiogyZ+5u/QyPTE61NOE=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774522083; c=relaxed/simple;\n\tbh=sDpdOIVwyiqdQdI2+efkY+hWuJKO5Eo2Q6hjaMSiLCg=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=fur1gEsjhiHbv/wRfWcew46Hb/3zO2ocxWqA9lVeDFMiVrCxk8xfOH2zTde4bchf8dedd7H50MiA2qi4NTV+1TjXe1SY0EPcZMirU9Y0LQfNymtZBFyoTGWHgKWBuPMF7BqOd/NUlP0NpLS4BoBJ3EbljYsYobnfb1dp47pXp2Y=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=quarantine dis=none) header.from=redhat.com;\n spf=pass smtp.mailfrom=redhat.com;\n dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com\n header.b=CPiFdP5J; arc=none smtp.client-ip=170.10.129.124", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1774522080;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\t to:to:cc:cc:mime-version:mime-version:\n\t content-transfer-encoding:content-transfer-encoding:\n\t in-reply-to:in-reply-to:references:references;\n\tbh=DxL+yW7+B7jQcX5wo/zZyXW1S1PpGyKQ1dUYF5xN7ks=;\n\tb=CPiFdP5J+i8sKf0jtvKr/TuFx7z1KJtaI33B6C3pTM50jP7PQ5deoRl6kBdcNAOQ/cU9ZY\n\t0B9zS/T42WgkG7RU0hRPex4UGKTVm9hiHFUuTa8vyWeCDUVnQyN9hSe7yOdW+RHdeARFOT\n\tGSnsInAfdmHFuSGfoLp+hzv0qG+nddQ=", "X-MC-Unique": "zh8E2yOlOx6RfvhMNxM4VQ-1", "X-Mimecast-MFC-AGG-ID": "zh8E2yOlOx6RfvhMNxM4VQ_1774522072", "From": "David Howells <dhowells@redhat.com>", "To": "Christian Brauner <christian@brauner.io>,\n\tMatthew Wilcox <willy@infradead.org>,\n\tChristoph Hellwig <hch@infradead.org>", "Cc": "David Howells <dhowells@redhat.com>,\n\tPaulo Alcantara <pc@manguebit.com>,\n\tJens Axboe <axboe@kernel.dk>,\n\tLeon Romanovsky <leon@kernel.org>,\n\tSteve French <sfrench@samba.org>,\n\tChenXiaoSong <chenxiaosong@chenxiaosong.com>,\n\tMarc Dionne <marc.dionne@auristor.com>,\n\tEric Van Hensbergen <ericvh@kernel.org>,\n\tDominique Martinet <asmadeus@codewreck.org>,\n\tIlya Dryomov <idryomov@gmail.com>,\n\tTrond Myklebust <trondmy@kernel.org>,\n\tnetfs@lists.linux.dev,\n\tlinux-afs@lists.infradead.org,\n\tlinux-cifs@vger.kernel.org,\n\tlinux-nfs@vger.kernel.org,\n\tceph-devel@vger.kernel.org,\n\tv9fs@lists.linux.dev,\n\tlinux-erofs@lists.ozlabs.org,\n\tlinux-fsdevel@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org,\n\tPaulo Alcantara <pc@manguebit.org>,\n\tlinux-block@vger.kernel.org", "Subject": "[PATCH 12/26] iov_iter: Add a segmented queue of bio_vec[]", "Date": "Thu, 26 Mar 2026 10:45:27 +0000", "Message-ID": "<20260326104544.509518-13-dhowells@redhat.com>", "In-Reply-To": "<20260326104544.509518-1-dhowells@redhat.com>", "References": "<20260326104544.509518-1-dhowells@redhat.com>", "Precedence": "bulk", "X-Mailing-List": "linux-cifs@vger.kernel.org", "List-Id": "<linux-cifs.vger.kernel.org>", "List-Subscribe": "<mailto:linux-cifs+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-cifs+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.12" }, "content": "Add the concept of a segmented queue of bio_vec[] arrays. This allows an\nindefinite quantity of elements to be handled and allows things like\nnetwork filesystems and crypto drivers to glue bits on the ends without\nhaving to reallocate the array.\n\nThe bvecq struct that defines each segment also carries capacity/usage\ninformation along with flags indicating whether the constituent memory\nregions need freeing or unpinning and the file position of the first\nelement in a segment. The bvecq structs are refcounted to allow a queue to\nbe extracted in batches and split between a number of subrequests.\n\nThe bvecq can have the bio_vec[] it manages allocated in with it, but this\nis not required. A flag is provided for if this is the case as comparing\n->bv to ->__bv is not sufficient to detect this case.\n\nAdd an iterator type ITER_BVECQ for it. This is intended to replace\nITER_FOLIOQ (and ITER_XARRAY).\n\nNote that the prev pointer is only really needed for iov_iter_revert() and\ncould be dispensed with if struct iov_iter contained the head information\nas well as the current point.\n\nSigned-off-by: David Howells <dhowells@redhat.com>\ncc: Paulo Alcantara <pc@manguebit.org>\ncc: Matthew Wilcox <willy@infradead.org>\ncc: Christoph Hellwig <hch@infradead.org>\ncc: Jens Axboe <axboe@kernel.dk>\ncc: linux-block@vger.kernel.org\ncc: netfs@lists.linux.dev\ncc: linux-fsdevel@vger.kernel.org\n---\n include/linux/bvecq.h | 46 ++++++\n include/linux/iov_iter.h | 63 +++++++-\n include/linux/uio.h | 11 ++\n lib/iov_iter.c | 288 ++++++++++++++++++++++++++++++++++++-\n lib/scatterlist.c | 66 +++++++++\n lib/tests/kunit_iov_iter.c | 180 +++++++++++++++++++++++\n 6 files changed, 649 insertions(+), 5 deletions(-)\n create mode 100644 include/linux/bvecq.h", "diff": "diff --git a/include/linux/bvecq.h b/include/linux/bvecq.h\nnew file mode 100644\nindex 000000000000..462125af1cc7\n--- /dev/null\n+++ b/include/linux/bvecq.h\n@@ -0,0 +1,46 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+/* Implementation of a segmented queue of bio_vec[].\n+ *\n+ * Copyright (C) 2026 Red Hat, Inc. All Rights Reserved.\n+ * Written by David Howells (dhowells@redhat.com)\n+ */\n+\n+#ifndef _LINUX_BVECQ_H\n+#define _LINUX_BVECQ_H\n+\n+#include <linux/bvec.h>\n+\n+/*\n+ * Segmented bio_vec queue.\n+ *\n+ * These can be linked together to form messages of indefinite length and\n+ * iterated over with an ITER_BVECQ iterator. The list is non-circular; next\n+ * and prev are NULL at the ends.\n+ *\n+ * The bv pointer points to the segment array; this may be __bv if allocated\n+ * together. The caller is responsible for determining whether or not this is\n+ * the case as the array pointed to by bv may be follow on directly from the\n+ * bvecq by accident of allocation (ie. ->bv == ->__bv is *not* sufficient to\n+ * determine this).\n+ *\n+ * The file position and discontiguity flag allow non-contiguous data sets to\n+ * be chained together, but still teased apart without the need to convert the\n+ * info in the bio_vec back into a folio pointer.\n+ */\n+struct bvecq {\n+\tstruct bvecq\t*next;\t\t/* Next bvec in the list or NULL */\n+\tstruct bvecq\t*prev;\t\t/* Prev bvec in the list or NULL */\n+\tunsigned long long fpos;\t/* File position */\n+\trefcount_t\tref;\n+\tu32\t\tpriv;\t\t/* Private data */\n+\tu16\t\tnr_segs;\t/* Number of elements in bv[] used */\n+\tu16\t\tmax_segs;\t/* Number of elements allocated in bv[] */\n+\tbool\t\tinline_bv:1;\t/* T if __bv[] is being used */\n+\tbool\t\tfree:1;\t\t/* T if the pages need freeing */\n+\tbool\t\tunpin:1;\t/* T if the pages need unpinning, not freeing */\n+\tbool\t\tdiscontig:1;\t/* T if not contiguous with previous bvecq */\n+\tstruct bio_vec\t*bv;\t\t/* Pointer to array of page fragments */\n+\tstruct bio_vec\t__bv[];\t\t/* Default array (if ->inline_bv) */\n+};\n+\n+#endif /* _LINUX_BVECQ_H */\ndiff --git a/include/linux/iov_iter.h b/include/linux/iov_iter.h\nindex f9a17fbbd398..999607ece481 100644\n--- a/include/linux/iov_iter.h\n+++ b/include/linux/iov_iter.h\n@@ -9,7 +9,7 @@\n #define _LINUX_IOV_ITER_H\n \n #include <linux/uio.h>\n-#include <linux/bvec.h>\n+#include <linux/bvecq.h>\n #include <linux/folio_queue.h>\n \n typedef size_t (*iov_step_f)(void *iter_base, size_t progress, size_t len,\n@@ -141,6 +141,59 @@ size_t iterate_bvec(struct iov_iter *iter, size_t len, void *priv, void *priv2,\n \treturn progress;\n }\n \n+/*\n+ * Handle ITER_BVECQ.\n+ */\n+static __always_inline\n+size_t iterate_bvecq(struct iov_iter *iter, size_t len, void *priv, void *priv2,\n+\t\t iov_step_f step)\n+{\n+\tconst struct bvecq *bq = iter->bvecq;\n+\tunsigned int slot = iter->bvecq_slot;\n+\tsize_t progress = 0, skip = iter->iov_offset;\n+\n+\tif (slot == bq->nr_segs) {\n+\t\t/* The iterator may have been extended. */\n+\t\tbq = bq->next;\n+\t\tslot = 0;\n+\t}\n+\n+\tdo {\n+\t\tconst struct bio_vec *bvec = &bq->bv[slot];\n+\t\tstruct page *page = bvec->bv_page + (bvec->bv_offset + skip) / PAGE_SIZE;\n+\t\tsize_t part, remain, consumed;\n+\t\tsize_t poff = (bvec->bv_offset + skip) % PAGE_SIZE;\n+\t\tvoid *base;\n+\n+\t\tpart = umin(umin(bvec->bv_len - skip, PAGE_SIZE - poff), len);\n+\t\tbase = kmap_local_page(page) + poff;\n+\t\tremain = step(base, progress, part, priv, priv2);\n+\t\tkunmap_local(base);\n+\t\tconsumed = part - remain;\n+\t\tlen -= consumed;\n+\t\tprogress += consumed;\n+\t\tskip += consumed;\n+\t\tif (skip >= bvec->bv_len) {\n+\t\t\tskip = 0;\n+\t\t\tslot++;\n+\t\t\tif (slot >= bq->nr_segs) {\n+\t\t\t\tif (!bq->next)\n+\t\t\t\t\tbreak;\n+\t\t\t\tbq = bq->next;\n+\t\t\t\tslot = 0;\n+\t\t\t}\n+\t\t}\n+\t\tif (remain)\n+\t\t\tbreak;\n+\t} while (len);\n+\n+\titer->bvecq_slot = slot;\n+\titer->bvecq = bq;\n+\titer->iov_offset = skip;\n+\titer->count -= progress;\n+\treturn progress;\n+}\n+\n /*\n * Handle ITER_FOLIOQ.\n */\n@@ -306,6 +359,8 @@ size_t iterate_and_advance2(struct iov_iter *iter, size_t len, void *priv,\n \t\treturn iterate_bvec(iter, len, priv, priv2, step);\n \tif (iov_iter_is_kvec(iter))\n \t\treturn iterate_kvec(iter, len, priv, priv2, step);\n+\tif (iov_iter_is_bvecq(iter))\n+\t\treturn iterate_bvecq(iter, len, priv, priv2, step);\n \tif (iov_iter_is_folioq(iter))\n \t\treturn iterate_folioq(iter, len, priv, priv2, step);\n \tif (iov_iter_is_xarray(iter))\n@@ -342,8 +397,8 @@ size_t iterate_and_advance(struct iov_iter *iter, size_t len, void *priv,\n * buffer is presented in segments, which for kernel iteration are broken up by\n * physical pages and mapped, with the mapped address being presented.\n *\n- * [!] Note This will only handle BVEC, KVEC, FOLIOQ, XARRAY and DISCARD-type\n- * iterators; it will not handle UBUF or IOVEC-type iterators.\n+ * [!] Note This will only handle BVEC, KVEC, BVECQ, FOLIOQ, XARRAY and\n+ * DISCARD-type iterators; it will not handle UBUF or IOVEC-type iterators.\n *\n * A step functions, @step, must be provided, one for handling mapped kernel\n * addresses and the other is given user addresses which have the potential to\n@@ -370,6 +425,8 @@ size_t iterate_and_advance_kernel(struct iov_iter *iter, size_t len, void *priv,\n \t\treturn iterate_bvec(iter, len, priv, priv2, step);\n \tif (iov_iter_is_kvec(iter))\n \t\treturn iterate_kvec(iter, len, priv, priv2, step);\n+\tif (iov_iter_is_bvecq(iter))\n+\t\treturn iterate_bvecq(iter, len, priv, priv2, step);\n \tif (iov_iter_is_folioq(iter))\n \t\treturn iterate_folioq(iter, len, priv, priv2, step);\n \tif (iov_iter_is_xarray(iter))\ndiff --git a/include/linux/uio.h b/include/linux/uio.h\nindex a9bc5b3067e3..aa50d348dfcc 100644\n--- a/include/linux/uio.h\n+++ b/include/linux/uio.h\n@@ -27,6 +27,7 @@ enum iter_type {\n \tITER_BVEC,\n \tITER_KVEC,\n \tITER_FOLIOQ,\n+\tITER_BVECQ,\n \tITER_XARRAY,\n \tITER_DISCARD,\n };\n@@ -69,6 +70,7 @@ struct iov_iter {\n \t\t\t\tconst struct kvec *kvec;\n \t\t\t\tconst struct bio_vec *bvec;\n \t\t\t\tconst struct folio_queue *folioq;\n+\t\t\t\tconst struct bvecq *bvecq;\n \t\t\t\tstruct xarray *xarray;\n \t\t\t\tvoid __user *ubuf;\n \t\t\t};\n@@ -78,6 +80,7 @@ struct iov_iter {\n \tunion {\n \t\tunsigned long nr_segs;\n \t\tu8 folioq_slot;\n+\t\tu16 bvecq_slot;\n \t\tloff_t xarray_start;\n \t};\n };\n@@ -150,6 +153,11 @@ static inline bool iov_iter_is_folioq(const struct iov_iter *i)\n \treturn iov_iter_type(i) == ITER_FOLIOQ;\n }\n \n+static inline bool iov_iter_is_bvecq(const struct iov_iter *i)\n+{\n+\treturn iov_iter_type(i) == ITER_BVECQ;\n+}\n+\n static inline bool iov_iter_is_xarray(const struct iov_iter *i)\n {\n \treturn iov_iter_type(i) == ITER_XARRAY;\n@@ -298,6 +306,9 @@ void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count);\n void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction,\n \t\t\t const struct folio_queue *folioq,\n \t\t\t unsigned int first_slot, unsigned int offset, size_t count);\n+void iov_iter_bvec_queue(struct iov_iter *i, unsigned int direction,\n+\t\t\t const struct bvecq *bvecq,\n+\t\t\t unsigned int first_slot, unsigned int offset, size_t count);\n void iov_iter_xarray(struct iov_iter *i, unsigned int direction, struct xarray *xarray,\n \t\t loff_t start, size_t count);\n ssize_t iov_iter_get_pages2(struct iov_iter *i, struct page **pages,\ndiff --git a/lib/iov_iter.c b/lib/iov_iter.c\nindex 0a63c7fba313..df8d037894b1 100644\n--- a/lib/iov_iter.c\n+++ b/lib/iov_iter.c\n@@ -571,6 +571,39 @@ static void iov_iter_folioq_advance(struct iov_iter *i, size_t size)\n \ti->folioq = folioq;\n }\n \n+static void iov_iter_bvecq_advance(struct iov_iter *i, size_t by)\n+{\n+\tconst struct bvecq *bq = i->bvecq;\n+\tunsigned int slot = i->bvecq_slot;\n+\n+\tif (!i->count)\n+\t\treturn;\n+\ti->count -= by;\n+\n+\tif (slot >= bq->nr_segs) {\n+\t\tbq = bq->next;\n+\t\tslot = 0;\n+\t}\n+\n+\tby += i->iov_offset; /* From beginning of current segment. */\n+\tdo {\n+\t\tsize_t len = bq->bv[slot].bv_len;\n+\n+\t\tif (likely(by < len))\n+\t\t\tbreak;\n+\t\tby -= len;\n+\t\tslot++;\n+\t\tif (slot >= bq->nr_segs && bq->next) {\n+\t\t\tbq = bq->next;\n+\t\t\tslot = 0;\n+\t\t}\n+\t} while (by);\n+\n+\ti->iov_offset = by;\n+\ti->bvecq_slot = slot;\n+\ti->bvecq = bq;\n+}\n+\n void iov_iter_advance(struct iov_iter *i, size_t size)\n {\n \tif (unlikely(i->count < size))\n@@ -585,6 +618,8 @@ void iov_iter_advance(struct iov_iter *i, size_t size)\n \t\tiov_iter_bvec_advance(i, size);\n \t} else if (iov_iter_is_folioq(i)) {\n \t\tiov_iter_folioq_advance(i, size);\n+\t} else if (iov_iter_is_bvecq(i)) {\n+\t\tiov_iter_bvecq_advance(i, size);\n \t} else if (iov_iter_is_discard(i)) {\n \t\ti->count -= size;\n \t}\n@@ -617,6 +652,32 @@ static void iov_iter_folioq_revert(struct iov_iter *i, size_t unroll)\n \ti->folioq = folioq;\n }\n \n+static void iov_iter_bvecq_revert(struct iov_iter *i, size_t unroll)\n+{\n+\tconst struct bvecq *bq = i->bvecq;\n+\tunsigned int slot = i->bvecq_slot;\n+\n+\tfor (;;) {\n+\t\tsize_t len;\n+\n+\t\tif (slot == 0) {\n+\t\t\tbq = bq->prev;\n+\t\t\tslot = bq->nr_segs;\n+\t\t}\n+\t\tslot--;\n+\n+\t\tlen = bq->bv[slot].bv_len;\n+\t\tif (unroll <= len) {\n+\t\t\ti->iov_offset = len - unroll;\n+\t\t\tbreak;\n+\t\t}\n+\t\tunroll -= len;\n+\t}\n+\n+\ti->bvecq_slot = slot;\n+\ti->bvecq = bq;\n+}\n+\n void iov_iter_revert(struct iov_iter *i, size_t unroll)\n {\n \tif (!unroll)\n@@ -651,6 +712,9 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll)\n \t} else if (iov_iter_is_folioq(i)) {\n \t\ti->iov_offset = 0;\n \t\tiov_iter_folioq_revert(i, unroll);\n+\t} else if (iov_iter_is_bvecq(i)) {\n+\t\ti->iov_offset = 0;\n+\t\tiov_iter_bvecq_revert(i, unroll);\n \t} else { /* same logics for iovec and kvec */\n \t\tconst struct iovec *iov = iter_iov(i);\n \t\twhile (1) {\n@@ -678,9 +742,12 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i)\n \t\tif (iov_iter_is_bvec(i))\n \t\t\treturn min(i->count, i->bvec->bv_len - i->iov_offset);\n \t}\n+\tif (!i->count)\n+\t\treturn 0;\n \tif (unlikely(iov_iter_is_folioq(i)))\n-\t\treturn !i->count ? 0 :\n-\t\t\tumin(folioq_folio_size(i->folioq, i->folioq_slot), i->count);\n+\t\treturn umin(folioq_folio_size(i->folioq, i->folioq_slot), i->count);\n+\tif (unlikely(iov_iter_is_bvecq(i)))\n+\t\treturn min(i->count, i->bvecq->bv[i->bvecq_slot].bv_len - i->iov_offset);\n \treturn i->count;\n }\n EXPORT_SYMBOL(iov_iter_single_seg_count);\n@@ -747,6 +814,35 @@ void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction,\n }\n EXPORT_SYMBOL(iov_iter_folio_queue);\n \n+/**\n+ * iov_iter_bvec_queue - Initialise an I/O iterator to use a segmented bvec queue\n+ * @i: The iterator to initialise.\n+ * @direction: The direction of the transfer.\n+ * @bvecq: The starting point in the bvec queue.\n+ * @first_slot: The first slot in the bvec queue to use\n+ * @offset: The offset into the bvec in the first slot to start at\n+ * @count: The size of the I/O buffer in bytes.\n+ *\n+ * Set up an I/O iterator to either draw data out of the buffers attached to an\n+ * inode or to inject data into those buffers. The pages *must* be prevented\n+ * from evaporation, either by the caller.\n+ */\n+void iov_iter_bvec_queue(struct iov_iter *i, unsigned int direction,\n+\t\t\t const struct bvecq *bvecq, unsigned int first_slot,\n+\t\t\t unsigned int offset, size_t count)\n+{\n+\tWARN_ON(direction & ~(READ | WRITE));\n+\t*i = (struct iov_iter) {\n+\t\t.iter_type\t= ITER_BVECQ,\n+\t\t.data_source\t= direction,\n+\t\t.bvecq\t\t= bvecq,\n+\t\t.bvecq_slot\t= first_slot,\n+\t\t.count\t\t= count,\n+\t\t.iov_offset\t= offset,\n+\t};\n+}\n+EXPORT_SYMBOL(iov_iter_bvec_queue);\n+\n /**\n * iov_iter_xarray - Initialise an I/O iterator to use the pages in an xarray\n * @i: The iterator to initialise.\n@@ -839,6 +935,37 @@ static unsigned long iov_iter_alignment_bvec(const struct iov_iter *i)\n \treturn res;\n }\n \n+static unsigned long iov_iter_alignment_bvecq(const struct iov_iter *iter)\n+{\n+\tconst struct bvecq *bq;\n+\tunsigned long res = 0;\n+\tunsigned int slot = iter->bvecq_slot;\n+\tsize_t skip = iter->iov_offset;\n+\tsize_t size = iter->count;\n+\n+\tif (!size)\n+\t\treturn res;\n+\n+\tfor (bq = iter->bvecq; bq; bq = bq->next) {\n+\t\tfor (; slot < bq->nr_segs; slot++) {\n+\t\t\tconst struct bio_vec *bvec = &bq->bv[slot];\n+\t\t\tsize_t part = umin(bvec->bv_len - skip, size);\n+\n+\t\t\tres |= bvec->bv_offset + skip;\n+\t\t\tres |= part;\n+\n+\t\t\tsize -= part;\n+\t\t\tif (size == 0)\n+\t\t\t\treturn res;\n+\t\t\tskip = 0;\n+\t\t}\n+\n+\t\tslot = 0;\n+\t}\n+\n+\treturn res;\n+}\n+\n unsigned long iov_iter_alignment(const struct iov_iter *i)\n {\n \tif (likely(iter_is_ubuf(i))) {\n@@ -858,6 +985,8 @@ unsigned long iov_iter_alignment(const struct iov_iter *i)\n \t/* With both xarray and folioq types, we're dealing with whole folios. */\n \tif (iov_iter_is_folioq(i))\n \t\treturn i->iov_offset | i->count;\n+\tif (iov_iter_is_bvecq(i))\n+\t\treturn iov_iter_alignment_bvecq(i);\n \tif (iov_iter_is_xarray(i))\n \t\treturn (i->xarray_start + i->iov_offset) | i->count;\n \n@@ -1124,6 +1253,7 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,\n \t\treturn iter_folioq_get_pages(i, pages, maxsize, maxpages, start);\n \tif (iov_iter_is_xarray(i))\n \t\treturn iter_xarray_get_pages(i, pages, maxsize, maxpages, start);\n+\tWARN_ON_ONCE(iov_iter_is_bvecq(i));\n \treturn -EFAULT;\n }\n \n@@ -1192,6 +1322,36 @@ static int bvec_npages(const struct iov_iter *i, int maxpages)\n \treturn npages;\n }\n \n+static size_t iov_npages_bvecq(const struct iov_iter *iter, size_t maxpages)\n+{\n+\tconst struct bvecq *bq;\n+\tunsigned int slot = iter->bvecq_slot;\n+\tsize_t npages = 0;\n+\tsize_t skip = iter->iov_offset;\n+\tsize_t size = iter->count;\n+\n+\tfor (bq = iter->bvecq; bq; bq = bq->next) {\n+\t\tfor (; slot < bq->nr_segs; slot++) {\n+\t\t\tconst struct bio_vec *bvec = &bq->bv[slot];\n+\t\t\tsize_t offs = (bvec->bv_offset + skip) % PAGE_SIZE;\n+\t\t\tsize_t part = umin(bvec->bv_len - skip, size);\n+\n+\t\t\tnpages += DIV_ROUND_UP(offs + part, PAGE_SIZE);\n+\t\t\tif (npages >= maxpages)\n+\t\t\t\tgoto out;\n+\n+\t\t\tsize -= part;\n+\t\t\tif (!size)\n+\t\t\t\tgoto out;\n+\t\t\tskip = 0;\n+\t\t}\n+\n+\t\tslot = 0;\n+\t}\n+out:\n+\treturn umin(npages, maxpages);\n+}\n+\n int iov_iter_npages(const struct iov_iter *i, int maxpages)\n {\n \tif (unlikely(!i->count))\n@@ -1211,6 +1371,8 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)\n \t\tint npages = DIV_ROUND_UP(offset + i->count, PAGE_SIZE);\n \t\treturn min(npages, maxpages);\n \t}\n+\tif (iov_iter_is_bvecq(i))\n+\t\treturn iov_npages_bvecq(i, maxpages);\n \tif (iov_iter_is_xarray(i)) {\n \t\tunsigned offset = (i->xarray_start + i->iov_offset) % PAGE_SIZE;\n \t\tint npages = DIV_ROUND_UP(offset + i->count, PAGE_SIZE);\n@@ -1554,6 +1716,124 @@ static ssize_t iov_iter_extract_folioq_pages(struct iov_iter *i,\n \treturn extracted;\n }\n \n+/*\n+ * Extract a list of virtually contiguous pages from an ITER_BVECQ iterator.\n+ * This does not get references on the pages, nor does it get a pin on them.\n+ */\n+static ssize_t iov_iter_extract_bvecq_pages(struct iov_iter *iter,\n+\t\t\t\t\t struct page ***pages, size_t maxsize,\n+\t\t\t\t\t unsigned int maxpages,\n+\t\t\t\t\t iov_iter_extraction_t extraction_flags,\n+\t\t\t\t\t size_t *offset0)\n+{\n+\tconst struct bvecq *bvecq = iter->bvecq;\n+\tstruct page **p;\n+\tunsigned int seg = iter->bvecq_slot, count = 0, nr = 0;\n+\tsize_t extracted = 0, offset = iter->iov_offset;\n+\n+\tif (seg >= bvecq->nr_segs) {\n+\t\tbvecq = bvecq->next;\n+\t\tif (WARN_ON_ONCE(!bvecq))\n+\t\t\treturn 0;\n+\t\tseg = 0;\n+\t}\n+\n+\t/* First, we count the run of virtually contiguous pages. */\n+\tdo {\n+\t\tconst struct bio_vec *bv = &bvecq->bv[seg];\n+\t\tsize_t boff = bv->bv_offset, blen = bv->bv_len;\n+\n+\t\tif (!bv->bv_page)\n+\t\t\tblen = 0;\n+\t\tif (extracted > 0 && boff % PAGE_SIZE)\n+\t\t\tbreak;\n+\n+\t\tif (offset < blen) {\n+\t\t\tsize_t part = umin(maxsize - extracted, blen - offset);\n+\t\t\tsize_t poff = (boff + offset) % PAGE_SIZE;\n+\t\t\tsize_t pcount = DIV_ROUND_UP(poff + blen, PAGE_SIZE);\n+\n+\t\t\toffset\t += part;\n+\t\t\textracted += part;\n+\t\t\tcount\t += pcount;\n+\t\t\tif ((boff + blen) % PAGE_SIZE)\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (offset >= blen) {\n+\t\t\toffset = 0;\n+\t\t\tseg++;\n+\t\t\tif (seg >= bvecq->nr_segs) {\n+\t\t\t\tif (!bvecq->next) {\n+\t\t\t\t\tWARN_ON_ONCE(extracted < iter->count);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tbvecq = bvecq->next;\n+\t\t\t\tseg = 0;\n+\t\t\t}\n+\t\t}\n+\t} while (count < maxpages && extracted < maxsize);\n+\n+\tmaxpages = umin(maxpages, count);\n+\n+\tif (!*pages) {\n+\t\t*pages = kvmalloc_array(maxpages, sizeof(struct page *), GFP_KERNEL);\n+\t\tif (!*pages)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\tp = *pages;\n+\n+\t/* Now transcribe the page pointers. */\n+\textracted = 0;\n+\tbvecq = iter->bvecq;\n+\toffset = iter->iov_offset;\n+\tseg = iter->bvecq_slot;\n+\n+\tdo {\n+\t\tconst struct bio_vec *bv = &bvecq->bv[seg];\n+\t\tsize_t boff = bv->bv_offset, blen = bv->bv_len;\n+\n+\t\tif (!bv->bv_page)\n+\t\t\tblen = 0;\n+\n+\t\tif (offset < blen) {\n+\t\t\tsize_t part = umin(maxsize - extracted, blen - offset);\n+\t\t\tsize_t poff = (boff + offset) % PAGE_SIZE;\n+\t\t\tsize_t pix = (boff + offset) / PAGE_SIZE;\n+\n+\t\t\tif (poff + part > PAGE_SIZE)\n+\t\t\t\tpart = PAGE_SIZE - poff;\n+\n+\t\t\tif (!extracted)\n+\t\t\t\t*offset0 = poff;\n+\n+\t\t\tp[nr++] = bv->bv_page + pix;\n+\t\t\toffset += part;\n+\t\t\textracted += part;\n+\t\t}\n+\n+\t\tif (offset >= blen) {\n+\t\t\toffset = 0;\n+\t\t\tseg++;\n+\t\t\tif (seg >= bvecq->nr_segs) {\n+\t\t\t\tif (!bvecq->next) {\n+\t\t\t\t\tWARN_ON_ONCE(extracted < iter->count);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tbvecq = bvecq->next;\n+\t\t\t\tseg = 0;\n+\t\t\t}\n+\t\t}\n+\t} while (nr < maxpages && extracted < maxsize);\n+\n+\titer->bvecq = bvecq;\n+\titer->bvecq_slot = seg;\n+\titer->iov_offset = offset;\n+\titer->count -= extracted;\n+\treturn extracted;\n+}\n+\n /*\n * Extract a list of contiguous pages from an ITER_XARRAY iterator. This does not\n * get references on the pages, nor does it get a pin on them.\n@@ -1838,6 +2118,10 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i,\n \t\treturn iov_iter_extract_folioq_pages(i, pages, maxsize,\n \t\t\t\t\t\t maxpages, extraction_flags,\n \t\t\t\t\t\t offset0);\n+\tif (iov_iter_is_bvecq(i))\n+\t\treturn iov_iter_extract_bvecq_pages(i, pages, maxsize,\n+\t\t\t\t\t\t maxpages, extraction_flags,\n+\t\t\t\t\t\t offset0);\n \tif (iov_iter_is_xarray(i))\n \t\treturn iov_iter_extract_xarray_pages(i, pages, maxsize,\n \t\t\t\t\t\t maxpages, extraction_flags,\ndiff --git a/lib/scatterlist.c b/lib/scatterlist.c\nindex d773720d11bf..03e3883a1a2d 100644\n--- a/lib/scatterlist.c\n+++ b/lib/scatterlist.c\n@@ -10,6 +10,7 @@\n #include <linux/highmem.h>\n #include <linux/kmemleak.h>\n #include <linux/bvec.h>\n+#include <linux/bvecq.h>\n #include <linux/uio.h>\n #include <linux/folio_queue.h>\n \n@@ -1328,6 +1329,68 @@ static ssize_t extract_folioq_to_sg(struct iov_iter *iter,\n \treturn ret;\n }\n \n+/*\n+ * Extract up to sg_max folios from an BVECQ-type iterator and add them to\n+ * the scatterlist. The pages are not pinned.\n+ */\n+static ssize_t extract_bvecq_to_sg(struct iov_iter *iter,\n+\t\t\t\t ssize_t maxsize,\n+\t\t\t\t struct sg_table *sgtable,\n+\t\t\t\t unsigned int sg_max,\n+\t\t\t\t iov_iter_extraction_t extraction_flags)\n+{\n+\tconst struct bvecq *bvecq = iter->bvecq;\n+\tstruct scatterlist *sg = sgtable->sgl + sgtable->nents;\n+\tunsigned int seg = iter->bvecq_slot;\n+\tssize_t ret = 0;\n+\tsize_t offset = iter->iov_offset;\n+\n+\tif (seg >= bvecq->nr_segs) {\n+\t\tbvecq = bvecq->next;\n+\t\tif (WARN_ON_ONCE(!bvecq))\n+\t\t\treturn 0;\n+\t\tseg = 0;\n+\t}\n+\n+\tdo {\n+\t\tconst struct bio_vec *bv = &bvecq->bv[seg];\n+\t\tsize_t blen = bv->bv_len;\n+\n+\t\tif (!bv->bv_page)\n+\t\t\tblen = 0;\n+\n+\t\tif (offset < blen) {\n+\t\t\tsize_t part = umin(maxsize - ret, blen - offset);\n+\n+\t\t\tsg_set_page(sg, bv->bv_page, part, bv->bv_offset + offset);\n+\t\t\tsgtable->nents++;\n+\t\t\tsg++;\n+\t\t\tsg_max--;\n+\t\t\toffset += part;\n+\t\t\tret += part;\n+\t\t}\n+\n+\t\tif (offset >= blen) {\n+\t\t\toffset = 0;\n+\t\t\tseg++;\n+\t\t\tif (seg >= bvecq->nr_segs) {\n+\t\t\t\tif (!bvecq->next) {\n+\t\t\t\t\tWARN_ON_ONCE(ret < iter->count);\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tbvecq = bvecq->next;\n+\t\t\t\tseg = 0;\n+\t\t\t}\n+\t\t}\n+\t} while (sg_max > 0 && ret < maxsize);\n+\n+\titer->bvecq = bvecq;\n+\titer->bvecq_slot = seg;\n+\titer->iov_offset = offset;\n+\titer->count -= ret;\n+\treturn ret;\n+}\n+\n /*\n * Extract up to sg_max folios from an XARRAY-type iterator and add them to\n * the scatterlist. The pages are not pinned.\n@@ -1426,6 +1489,9 @@ ssize_t extract_iter_to_sg(struct iov_iter *iter, size_t maxsize,\n \tcase ITER_FOLIOQ:\n \t\treturn extract_folioq_to_sg(iter, maxsize, sgtable, sg_max,\n \t\t\t\t\t extraction_flags);\n+\tcase ITER_BVECQ:\n+\t\treturn extract_bvecq_to_sg(iter, maxsize, sgtable, sg_max,\n+\t\t\t\t\t extraction_flags);\n \tcase ITER_XARRAY:\n \t\treturn extract_xarray_to_sg(iter, maxsize, sgtable, sg_max,\n \t\t\t\t\t extraction_flags);\ndiff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c\nindex bb847e5010eb..5bc941f64343 100644\n--- a/lib/tests/kunit_iov_iter.c\n+++ b/lib/tests/kunit_iov_iter.c\n@@ -12,6 +12,7 @@\n #include <linux/mm.h>\n #include <linux/uio.h>\n #include <linux/bvec.h>\n+#include <linux/bvecq.h>\n #include <linux/folio_queue.h>\n #include <kunit/test.h>\n \n@@ -536,6 +537,183 @@ static void __init iov_kunit_copy_from_folioq(struct kunit *test)\n \tKUNIT_SUCCEED(test);\n }\n \n+static void iov_kunit_destroy_bvecq(void *data)\n+{\n+\tstruct bvecq *bq, *next;\n+\n+\tfor (bq = data; bq; bq = next) {\n+\t\tnext = bq->next;\n+\t\tfor (int i = 0; i < bq->nr_segs; i++)\n+\t\t\tif (bq->bv[i].bv_page)\n+\t\t\t\tput_page(bq->bv[i].bv_page);\n+\t\tkfree(bq);\n+\t}\n+}\n+\n+static struct bvecq *iov_kunit_alloc_bvecq(struct kunit *test, unsigned int max_segs)\n+{\n+\tstruct bvecq *bq;\n+\n+\tbq = kzalloc(struct_size(bq, __bv, max_segs), GFP_KERNEL);\n+\tKUNIT_ASSERT_NOT_ERR_OR_NULL(test, bq);\n+\tbq->max_segs = max_segs;\n+\treturn bq;\n+}\n+\n+static struct bvecq *iov_kunit_create_bvecq(struct kunit *test, unsigned int max_segs)\n+{\n+\tstruct bvecq *bq;\n+\n+\tbq = iov_kunit_alloc_bvecq(test, max_segs);\n+\tkunit_add_action_or_reset(test, iov_kunit_destroy_bvecq, bq);\n+\treturn bq;\n+}\n+\n+static void __init iov_kunit_load_bvecq(struct kunit *test,\n+\t\t\t\t\tstruct iov_iter *iter, int dir,\n+\t\t\t\t\tstruct bvecq *bq_head,\n+\t\t\t\t\tstruct page **pages, size_t npages)\n+{\n+\tstruct bvecq *bq = bq_head;\n+\tsize_t size = 0;\n+\n+\tfor (int i = 0; i < npages; i++) {\n+\t\tif (bq->nr_segs >= bq->max_segs) {\n+\t\t\tbq->next = iov_kunit_alloc_bvecq(test, 8);\n+\t\t\tbq->next->prev = bq;\n+\t\t\tbq = bq->next;\n+\t\t}\n+\t\tbvec_set_page(&bq->bv[bq->nr_segs], pages[i], PAGE_SIZE, 0);\n+\t\tbq->nr_segs++;\n+\t\tsize += PAGE_SIZE;\n+\t}\n+\tiov_iter_bvec_queue(iter, dir, bq_head, 0, 0, size);\n+}\n+\n+/*\n+ * Test copying to a ITER_BVECQ-type iterator.\n+ */\n+static void __init iov_kunit_copy_to_bvecq(struct kunit *test)\n+{\n+\tconst struct kvec_test_range *pr;\n+\tstruct iov_iter iter;\n+\tstruct bvecq *bq;\n+\tstruct page **spages, **bpages;\n+\tu8 *scratch, *buffer;\n+\tsize_t bufsize, npages, size, copied;\n+\tint i, patt;\n+\n+\tbufsize = 0x100000;\n+\tnpages = bufsize / PAGE_SIZE;\n+\n+\tbq = iov_kunit_create_bvecq(test, 8);\n+\n+\tscratch = iov_kunit_create_buffer(test, &spages, npages);\n+\tfor (i = 0; i < bufsize; i++)\n+\t\tscratch[i] = pattern(i);\n+\n+\tbuffer = iov_kunit_create_buffer(test, &bpages, npages);\n+\tmemset(buffer, 0, bufsize);\n+\n+\tiov_kunit_load_bvecq(test, &iter, READ, bq, bpages, npages);\n+\n+\ti = 0;\n+\tfor (pr = kvec_test_ranges; pr->from >= 0; pr++) {\n+\t\tsize = pr->to - pr->from;\n+\t\tKUNIT_ASSERT_LE(test, pr->to, bufsize);\n+\n+\t\tiov_iter_bvec_queue(&iter, READ, bq, 0, 0, pr->to);\n+\t\tiov_iter_advance(&iter, pr->from);\n+\t\tcopied = copy_to_iter(scratch + i, size, &iter);\n+\n+\t\tKUNIT_EXPECT_EQ(test, copied, size);\n+\t\tKUNIT_EXPECT_EQ(test, iter.count, 0);\n+\t\ti += size;\n+\t\tif (test->status == KUNIT_FAILURE)\n+\t\t\tgoto stop;\n+\t}\n+\n+\t/* Build the expected image in the scratch buffer. */\n+\tpatt = 0;\n+\tmemset(scratch, 0, bufsize);\n+\tfor (pr = kvec_test_ranges; pr->from >= 0; pr++)\n+\t\tfor (i = pr->from; i < pr->to; i++)\n+\t\t\tscratch[i] = pattern(patt++);\n+\n+\t/* Compare the images */\n+\tfor (i = 0; i < bufsize; i++) {\n+\t\tKUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], \"at i=%x\", i);\n+\t\tif (buffer[i] != scratch[i])\n+\t\t\treturn;\n+\t}\n+\n+stop:\n+\tKUNIT_SUCCEED(test);\n+}\n+\n+/*\n+ * Test copying from a ITER_BVECQ-type iterator.\n+ */\n+static void __init iov_kunit_copy_from_bvecq(struct kunit *test)\n+{\n+\tconst struct kvec_test_range *pr;\n+\tstruct iov_iter iter;\n+\tstruct bvecq *bq;\n+\tstruct page **spages, **bpages;\n+\tu8 *scratch, *buffer;\n+\tsize_t bufsize, npages, size, copied;\n+\tint i, j;\n+\n+\tbufsize = 0x100000;\n+\tnpages = bufsize / PAGE_SIZE;\n+\n+\tbq = iov_kunit_create_bvecq(test, 8);\n+\n+\tbuffer = iov_kunit_create_buffer(test, &bpages, npages);\n+\tfor (i = 0; i < bufsize; i++)\n+\t\tbuffer[i] = pattern(i);\n+\n+\tscratch = iov_kunit_create_buffer(test, &spages, npages);\n+\tmemset(scratch, 0, bufsize);\n+\n+\tiov_kunit_load_bvecq(test, &iter, READ, bq, bpages, npages);\n+\n+\ti = 0;\n+\tfor (pr = kvec_test_ranges; pr->from >= 0; pr++) {\n+\t\tsize = pr->to - pr->from;\n+\t\tKUNIT_ASSERT_LE(test, pr->to, bufsize);\n+\n+\t\tiov_iter_bvec_queue(&iter, WRITE, bq, 0, 0, pr->to);\n+\t\tiov_iter_advance(&iter, pr->from);\n+\t\tcopied = copy_from_iter(scratch + i, size, &iter);\n+\n+\t\tKUNIT_EXPECT_EQ(test, copied, size);\n+\t\tKUNIT_EXPECT_EQ(test, iter.count, 0);\n+\t\ti += size;\n+\t}\n+\n+\t/* Build the expected image in the main buffer. */\n+\ti = 0;\n+\tmemset(buffer, 0, bufsize);\n+\tfor (pr = kvec_test_ranges; pr->from >= 0; pr++) {\n+\t\tfor (j = pr->from; j < pr->to; j++) {\n+\t\t\tbuffer[i++] = pattern(j);\n+\t\t\tif (i >= bufsize)\n+\t\t\t\tgoto stop;\n+\t\t}\n+\t}\n+stop:\n+\n+\t/* Compare the images */\n+\tfor (i = 0; i < bufsize; i++) {\n+\t\tKUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], \"at i=%x\", i);\n+\t\tif (scratch[i] != buffer[i])\n+\t\t\treturn;\n+\t}\n+\n+\tKUNIT_SUCCEED(test);\n+}\n+\n static void iov_kunit_destroy_xarray(void *data)\n {\n \tstruct xarray *xarray = data;\n@@ -1016,6 +1194,8 @@ static struct kunit_case __refdata iov_kunit_cases[] = {\n \tKUNIT_CASE(iov_kunit_copy_from_bvec),\n \tKUNIT_CASE(iov_kunit_copy_to_folioq),\n \tKUNIT_CASE(iov_kunit_copy_from_folioq),\n+\tKUNIT_CASE(iov_kunit_copy_to_bvecq),\n+\tKUNIT_CASE(iov_kunit_copy_from_bvecq),\n \tKUNIT_CASE(iov_kunit_copy_to_xarray),\n \tKUNIT_CASE(iov_kunit_copy_from_xarray),\n \tKUNIT_CASE(iov_kunit_extract_pages_kvec),\n", "prefixes": [ "12/26" ] }