From: Caleb Sander Mateos <csander@purestorage.com>
To: Jens Axboe <axboe@kernel.dk>, Keith Busch <kbusch@kernel.org>,
Christoph Hellwig <hch@lst.de>, Sagi Grimberg <sagi@grimberg.me>
Cc: io-uring@vger.kernel.org, linux-nvme@lists.infradead.org,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
Caleb Sander Mateos <csander@purestorage.com>
Subject: [PATCH 3/6] blk-integrity: pass iov_iter to blk_rq_integrity_map_user()
Date: Wed, 9 Sep 2026 16:28:33 -0600 [thread overview]
Message-ID: <20260909222836.2475352-4-csander@purestorage.com> (raw)
In-Reply-To: <20260909222836.2475352-1-csander@purestorage.com>
To allow blk_rq_integrity_map_user() to accept an io_uring registered
buffer (represented as a ITER_BVEC iov_iter), take the buffer as an
iov_iter argument. Move the conversion from a user address-length pair
into an iov_iter to the caller.
Since a non-ITER_UBUF iov_iter could be passed in the future,
iov_iter_extract_pages() in bio_integrity_map_user() won't necessarily
pin the pages. Condition the page unpins on iov_iter_extract_will_pin().
Add a BIP_PAGE_PINNED bit to bip_flags to track if the pages are pinned.
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
---
block/bio-integrity.c | 43 +++++++++++++++++++++++------------
block/blk-integrity.c | 7 ++----
drivers/nvme/host/ioctl.c | 6 ++++-
include/linux/bio-integrity.h | 1 +
include/linux/blk-integrity.h | 6 ++---
5 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index 8ba8bfa41f8a..3a5554d3ea2f 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -187,11 +187,12 @@ static void bio_integrity_uncopy_user(struct bio_integrity_payload *bip)
iov_iter_bvec(&orig_iter, ITER_DEST, orig_bvecs, orig_nr_vecs, bytes);
ret = copy_to_iter(bvec_virt(bounce_bvec), bytes, &orig_iter);
WARN_ON_ONCE(ret != bytes);
- bio_integrity_unpin_bvec(orig_bvecs, orig_nr_vecs);
+ if (bip->bip_flags & BIP_PAGE_PINNED)
+ bio_integrity_unpin_bvec(orig_bvecs, orig_nr_vecs);
}
/**
* bio_integrity_unmap_user - Unmap user integrity payload
* @bio: bio containing bip to be unmapped
@@ -207,11 +208,12 @@ void bio_integrity_unmap_user(struct bio *bio)
bio_integrity_uncopy_user(bip);
kfree(bvec_virt(bip->bip_vec));
return;
}
- bio_integrity_unpin_bvec(bip->bip_vec, bip->bip_max_vcnt);
+ if (bip->bip_flags & BIP_PAGE_PINNED)
+ bio_integrity_unpin_bvec(bip->bip_vec, bip->bip_max_vcnt);
}
/**
* bio_integrity_add_page - Attach integrity metadata
* @bio: bio to update
@@ -257,11 +259,11 @@ int bio_integrity_add_page(struct bio *bio, struct page *page,
return len;
}
EXPORT_SYMBOL(bio_integrity_add_page);
static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec,
- int nr_vecs, unsigned int len)
+ int nr_vecs, unsigned int len, bool pinned)
{
bool write = op_is_write(bio_op(bio));
struct bio_integrity_payload *bip;
struct iov_iter iter;
void *buf;
@@ -292,14 +294,18 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec,
if (IS_ERR(bip)) {
ret = PTR_ERR(bip);
goto free_buf;
}
- if (write)
- bio_integrity_unpin_bvec(bvec, nr_vecs);
- else
+ if (write) {
+ if (pinned)
+ bio_integrity_unpin_bvec(bvec, nr_vecs);
+ } else {
memcpy(&bip->bip_vec[1], bvec, nr_vecs * sizeof(*bvec));
+ if (pinned)
+ bip->bip_flags |= BIP_PAGE_PINNED;
+ }
ret = bio_integrity_add_page(bio, virt_to_page(buf), len,
offset_in_page(buf));
WARN_ON_ONCE(ret != len);
bip->bip_flags |= BIP_COPY_USER;
@@ -308,27 +314,29 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec,
kfree(buf);
return ret;
}
static int bio_integrity_init_user(struct bio *bio, struct bio_vec *bvec,
- int nr_vecs, unsigned int len)
+ int nr_vecs, unsigned int len, bool pinned)
{
struct bio_integrity_payload *bip;
bip = bio_integrity_alloc(bio, GFP_KERNEL, nr_vecs);
if (IS_ERR(bip))
return PTR_ERR(bip);
memcpy(bip->bip_vec, bvec, nr_vecs * sizeof(*bvec));
bip->bip_iter.bi_size = len;
bip->bip_vcnt = nr_vecs;
+ if (pinned)
+ bip->bip_flags |= BIP_PAGE_PINNED;
return 0;
}
static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages,
int nr_vecs, ssize_t bytes, ssize_t offset,
- bool *is_p2p)
+ bool *is_p2p, bool pinned)
{
unsigned int nr_bvecs = 0;
int i, j;
for (i = 0; i < nr_vecs; i = j) {
@@ -340,11 +348,12 @@ static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages,
size_t next = min_t(size_t, PAGE_SIZE, bytes);
if (page_folio(pages[j]) != folio ||
pages[j] != pages[j - 1] + 1)
break;
- unpin_user_page(pages[j]);
+ if (pinned)
+ unpin_user_page(pages[j]);
size += next;
bytes -= next;
}
if (is_pci_p2pdma_page(pages[i]))
@@ -361,10 +370,11 @@ static unsigned int bvec_from_pages(struct bio_vec *bvec, struct page **pages,
int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
{
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
struct page *stack_pages[UIO_FASTIOV], **pages = stack_pages;
struct bio_vec stack_vec[UIO_FASTIOV], *bvec = stack_vec;
+ bool pinned = iov_iter_extract_will_pin(iter);
iov_iter_extraction_t extraction_flags = 0;
size_t offset, bytes = iter->count;
bool copy, is_p2p = false;
unsigned int nr_bvecs;
int ret, nr_vecs;
@@ -397,12 +407,12 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
/*
* Handle partial pinning. This can happen when pin_user_pages_fast()
* returns fewer pages than requested.
*/
- if (user_backed_iter(iter) && unlikely(ret != bytes)) {
- if (ret > 0) {
+ if (unlikely(ret != bytes)) {
+ if (pinned && ret > 0) {
int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE);
int i;
for (i = 0; i < npinned; i++)
unpin_user_page(pages[i]);
@@ -412,31 +422,34 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter)
ret = -EFAULT;
goto free_bvec;
}
nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset,
- &is_p2p);
+ &is_p2p, pinned);
if (pages != stack_pages)
kvfree(pages);
if (nr_bvecs > queue_max_integrity_segments(q))
copy = true;
if (is_p2p)
bio->bi_opf |= REQ_NOMERGE;
if (copy)
- ret = bio_integrity_copy_user(bio, bvec, nr_bvecs, bytes);
+ ret = bio_integrity_copy_user(bio, bvec, nr_bvecs, bytes,
+ pinned);
else
- ret = bio_integrity_init_user(bio, bvec, nr_bvecs, bytes);
+ ret = bio_integrity_init_user(bio, bvec, nr_bvecs, bytes,
+ pinned);
if (ret)
goto release_pages;
if (bvec != stack_vec)
kfree(bvec);
return 0;
release_pages:
- bio_integrity_unpin_bvec(bvec, nr_bvecs);
+ if (pinned)
+ bio_integrity_unpin_bvec(bvec, nr_bvecs);
free_bvec:
if (bvec != stack_vec)
kfree(bvec);
return ret;
}
diff --git a/block/blk-integrity.c b/block/blk-integrity.c
index 964eebbee14d..6977bfd96927 100644
--- a/block/blk-integrity.c
+++ b/block/blk-integrity.c
@@ -118,18 +118,15 @@ int blk_get_meta_cap(struct block_device *bdev, unsigned int cmd,
out:
return copy_struct_to_user(argp, usize, &meta_cap, sizeof(meta_cap),
NULL);
}
-int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf,
- ssize_t bytes)
+int blk_rq_integrity_map_user(struct request *rq, struct iov_iter *iter)
{
int ret;
- struct iov_iter iter;
- iov_iter_ubuf(&iter, rq_data_dir(rq), ubuf, bytes);
- ret = bio_integrity_map_user(rq->bio, &iter);
+ ret = bio_integrity_map_user(rq->bio, iter);
if (ret)
return ret;
rq->nr_integrity_segments = blk_rq_count_integrity_sg(rq->q, rq->bio);
rq->cmd_flags |= REQ_INTEGRITY;
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index 748a4cbf7e90..e69c49c334a0 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -175,11 +175,15 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
0, rq_data_dir(req));
if (ret)
return ret;
if (has_metadata) {
- ret = blk_rq_integrity_map_user(req, meta_buffer, meta_len);
+ struct iov_iter meta_iter;
+
+ iov_iter_ubuf(&meta_iter, rq_data_dir(req), meta_buffer,
+ meta_len);
+ ret = blk_rq_integrity_map_user(req, &meta_iter);
if (ret)
goto out_unmap;
}
return ret;
diff --git a/include/linux/bio-integrity.h b/include/linux/bio-integrity.h
index 0ea2a8bf7efb..740acb8c1077 100644
--- a/include/linux/bio-integrity.h
+++ b/include/linux/bio-integrity.h
@@ -12,10 +12,11 @@ enum bip_flags {
BIP_COPY_USER = 1 << 4, /* Kernel bounce buffer in use */
BIP_CHECK_GUARD = 1 << 5, /* guard check */
BIP_CHECK_REFTAG = 1 << 6, /* reftag check */
BIP_CHECK_APPTAG = 1 << 7, /* apptag check */
+ BIP_PAGE_PINNED = 1 << 14, /* pages need to be unpinned */
BIP_MEMPOOL = 1 << 15, /* buffer backed by mempool */
};
/* flags that require generate/verify action. */
#define BIP_CHECK_FLAGS (BIP_CHECK_GUARD | BIP_CHECK_REFTAG | BIP_CHECK_APPTAG)
diff --git a/include/linux/blk-integrity.h b/include/linux/blk-integrity.h
index b1b530613c34..8b655ac165e8 100644
--- a/include/linux/blk-integrity.h
+++ b/include/linux/blk-integrity.h
@@ -28,12 +28,11 @@ static inline bool queue_limits_stack_integrity_bdev(struct queue_limits *t,
#ifdef CONFIG_BLK_DEV_INTEGRITY
int blk_rq_map_integrity_sg(struct request *, struct scatterlist *);
int blk_rq_count_integrity_sg(struct request_queue *, struct bio *);
-int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf,
- ssize_t bytes);
+int blk_rq_integrity_map_user(struct request *rq, struct iov_iter *iter);
int blk_get_meta_cap(struct block_device *bdev, unsigned int cmd,
struct logical_block_metadata_cap __user *argp);
bool blk_rq_integrity_dma_map_iter_start(struct request *req,
struct device *dma_dev, struct dma_iova_state *state,
struct blk_dma_iter *iter);
@@ -116,12 +115,11 @@ static inline int blk_rq_map_integrity_sg(struct request *q,
struct scatterlist *s)
{
return 0;
}
static inline int blk_rq_integrity_map_user(struct request *rq,
- void __user *ubuf,
- ssize_t bytes)
+ struct iov_iter *iter)
{
return -EINVAL;
}
static inline bool blk_rq_integrity_dma_map_iter_start(struct request *req,
struct device *dma_dev, struct dma_iova_state *state,
--
2.55.0
next prev parent reply other threads:[~2026-09-09 22:29 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-09 22:28 [PATCH 0/6] io_uring/nvme: support fixed buffer for metadata Caleb Sander Mateos
2026-09-09 22:28 ` [PATCH 1/6] bio-integrity: remove dead bio_integrity_copy_user() error path Caleb Sander Mateos
2026-09-09 22:28 ` [PATCH 2/6] nvme/ioctl: remove struct nvme_uring_data Caleb Sander Mateos
2026-09-09 22:28 ` Caleb Sander Mateos [this message]
2026-09-09 22:28 ` [PATCH 4/6] nvme/ioctl: pass iov_iter to nvme_map_user_request() Caleb Sander Mateos
2026-09-09 22:28 ` [PATCH 5/6] io_uring/cmd: support fixed buffer for metadata Caleb Sander Mateos
2026-09-09 22:28 ` [PATCH 6/6] nvme/ioctl: " Caleb Sander Mateos
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260909222836.2475352-4-csander@purestorage.com \
--to=csander@purestorage.com \
--cc=axboe@kernel.dk \
--cc=hch@lst.de \
--cc=io-uring@vger.kernel.org \
--cc=kbusch@kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nvme@lists.infradead.org \
--cc=sagi@grimberg.me \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox