From: Karthikeyan KS <karthiproffesional@gmail.com>
To: "Michael S. Tsirkin" <mst@redhat.com>,
Jason Wang <jasowangio@gmail.com>,
Mike Christie <michael.christie@oracle.com>
Cc: "Paolo Bonzini" <pbonzini@redhat.com>,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Eugenio Pérez" <eperezma@redhat.com>,
"John Hubbard" <jhubbard@nvidia.com>,
virtualization@lists.linux.dev, kvm@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2] vhost-scsi: use FOLL_PIN for DMA page pinning
Date: Tue, 3 Nov 2026 16:59:17 +0000 [thread overview]
Message-ID: <20261103165917.148720-1-karthiproffesional@gmail.com> (raw)
vhost_scsi_map_to_sgl() takes references on guest buffer pages with
iov_iter_get_pages2() (FOLL_GET) and then performs DMA to/from them.
Per Documentation/core-api/pin_user_pages.rst (Case 1: Direct IO),
pages that are the target of DMA must be pinned with FOLL_PIN instead.
Only FOLL_PIN pins are visible to folio_maybe_dma_pinned(), which fork()
uses to copy rather than COW-share anonymous pages under DMA, and only
FOLL_PIN guarantees a pinned anonymous page is exclusive to the process,
so a later write fault cannot replace it while DMA is in flight.
Switch to iov_iter_extract_pages(), which pins user-backed iterators,
and release the pages with unpin_user_page(). Kernel-backed iterators
take neither a reference nor a pin, so track with
iov_iter_extract_will_pin() whether an unpin is needed.
Cc: Jason Wang <jasowangio@gmail.com>
Cc: Mike Christie <michael.christie@oracle.com>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: virtualization@lists.linux.dev
Signed-off-by: Karthikeyan KS <karthiproffesional@gmail.com>
---
v1 -> v2:
- Rebased on vhost next. Sashiko reported that the revert loop in
vhost_scsi_map_to_sgl() leaks a page when the misaligned-I/O check
trips on the first page with a non-zero offset. That bug is older
than this patch (the loop leaked a put_page() reference) and is fixed
in vhost next by Jia Jia's 058fc59326af ("vhost-scsi: fix page
reference leak in misaligned I/O fallback"). On top of it, this patch
only calls unpin_user_page() instead of put_page() in that loop.
- Reworded the commit message to describe FOLL_GET and FOLL_PIN more
accurately.
Testing: in QEMU (fileio backend), a 1 MiB write at page offset 3840 is
257 pages, more than BIO_MAX_VECS, so the misaligned-I/O check trips on
the first page and dmesg shows the "vhost-scsi detected misaligned IO"
warning. On v1 (before 058fc59326af) the nr_foll_pin_acquired and
nr_foll_pin_released counters end at 257 and 256 for each such command;
with v2 on vhost next they balance. Plain (256 pages) and T10-PI (260
pages) writes balance too.
v1: https://lore.kernel.org/20261007163716.707102-1-karthiproffesional@gmail.com
RESEND: https://lore.kernel.org/20261020182640.2048506-1-karthiproffesional@gmail.com
review: https://lore.kernel.org/sashiko-outbox-180555@kernel.org
leak fix: https://lore.kernel.org/20261023104035.6686-1-physicalmtea@gmail.com
drivers/vhost/scsi.c | 50 +++++++++++++++++++++++++++++++-------------
1 file changed, 35 insertions(+), 15 deletions(-)
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index e44044449cef..8beeff06a7a7 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -115,6 +115,8 @@ struct vhost_scsi_cmd {
u32 tvc_sgl_count;
u32 tvc_prot_sgl_count;
u32 copied_iov:1;
+ u32 tvc_need_unpin:1;
+ u32 tvc_prot_need_unpin:1;
const void *read_iov;
struct iov_iter *read_iter;
struct scatterlist *sgl;
@@ -463,8 +465,13 @@ static void vhost_scsi_release_cmd_res(struct se_cmd *se_cmd)
if (tv_cmd->copied_iov)
__free_page(page);
- else
- put_page(page);
+ else if (tv_cmd->tvc_need_unpin)
+ unpin_user_page(page);
+ /*
+ * Else: iov_iter_extract_pages() took neither a ref
+ * nor a pin on this page (ITER_KVEC/BVEC/etc), so
+ * there is nothing to release here.
+ */
}
kfree(tv_cmd->read_iter);
kfree(tv_cmd->read_iov);
@@ -473,8 +480,8 @@ static void vhost_scsi_release_cmd_res(struct se_cmd *se_cmd)
if (tv_cmd->tvc_prot_sgl_count) {
for_each_sgtable_sg(&tv_cmd->prot_table, sg, i) {
page = sg_page(sg);
- if (page)
- put_page(page);
+ if (page && tv_cmd->tvc_prot_need_unpin)
+ unpin_user_page(page);
}
sg_free_table_chained(&tv_cmd->prot_table, vs->inline_sg_cnt);
}
@@ -820,7 +827,8 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, u64 scsi_tag)
static void vhost_scsi_revert_map_iov_to_sgl(struct iov_iter *iter,
struct scatterlist *curr,
- struct scatterlist *end)
+ struct scatterlist *end,
+ bool need_unpin)
{
size_t revert_bytes = 0;
struct page *page;
@@ -829,7 +837,8 @@ static void vhost_scsi_revert_map_iov_to_sgl(struct iov_iter *iter,
page = sg_page(curr);
if (page) {
- put_page(page);
+ if (need_unpin)
+ unpin_user_page(page);
revert_bytes += curr->length;
}
/* Clear so we can re-use it for the copy path */
@@ -859,9 +868,9 @@ vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd,
size_t offset;
unsigned int n, npages = 0;
- bytes = iov_iter_get_pages2(iter, pages, LONG_MAX,
- VHOST_SCSI_PREALLOC_UPAGES, &offset);
- /* No pages were pinned */
+ bytes = iov_iter_extract_pages(iter, &pages, LONG_MAX,
+ VHOST_SCSI_PREALLOC_UPAGES, 0, &offset);
+ /* No pages were extracted */
if (bytes <= 0)
return bytes < 0 ? bytes : -EFAULT;
@@ -884,7 +893,7 @@ vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd,
cmd->tvc_sgl_count > BIO_MAX_VECS) {
WARN_ONCE(true,
"vhost-scsi detected misaligned IO. Performance may be degraded.");
- goto revert_iter_get_pages;
+ goto revert_iter_extract_pages;
}
sg_set_page(sg, pages[npages++], n, offset);
@@ -896,8 +905,10 @@ vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd,
*sgl = sg;
return npages;
-revert_iter_get_pages:
- vhost_scsi_revert_map_iov_to_sgl(iter, *sgl, sg);
+revert_iter_extract_pages:
+ vhost_scsi_revert_map_iov_to_sgl(iter, *sgl, sg,
+ is_prot ? cmd->tvc_prot_need_unpin :
+ cmd->tvc_need_unpin);
iov_iter_revert(iter, bytes);
/*
@@ -907,7 +918,9 @@ vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd,
while (bytes) {
n = min_t(unsigned int, PAGE_SIZE - offset, bytes);
- put_page(pages[npages++]);
+ if (is_prot ? cmd->tvc_prot_need_unpin : cmd->tvc_need_unpin)
+ unpin_user_page(pages[npages]);
+ npages++;
bytes -= n;
offset = 0;
}
@@ -983,6 +996,7 @@ vhost_scsi_copy_iov_to_sgl(struct vhost_scsi_cmd *cmd, struct iov_iter *iter,
}
cmd->copied_iov = 1;
+ cmd->tvc_need_unpin = 0;
return 0;
err:
@@ -1007,11 +1021,17 @@ vhost_scsi_map_iov_to_sgl(struct vhost_scsi_cmd *cmd, struct iov_iter *iter,
struct scatterlist *sg = sg_table->sgl;
int ret;
+ if (is_prot)
+ cmd->tvc_prot_need_unpin = iov_iter_extract_will_pin(iter);
+ else
+ cmd->tvc_need_unpin = iov_iter_extract_will_pin(iter);
+
while (iov_iter_count(iter)) {
ret = vhost_scsi_map_to_sgl(cmd, iter, sg_table, &sg, is_prot);
if (ret < 0) {
vhost_scsi_revert_map_iov_to_sgl(iter, sg_table->sgl,
- sg);
+ sg, is_prot ? cmd->tvc_prot_need_unpin :
+ cmd->tvc_need_unpin);
return ret;
}
}
@@ -1449,7 +1469,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
/*
* If T10_PI header + payload is present, setup prot_iter values
* and recalculate data_iter for vhost_scsi_mapal() mapping to
- * host scatterlists via get_user_pages_fast().
+ * host scatterlists via iov_iter_extract_pages().
*/
if (t10_pi) {
if (v_req_pi.pi_bytesout) {
base-commit: 1a577f9402c1780cc1e663f48d5dd54c02051b09
--
2.43.0
reply other threads:[~2026-11-03 16:59 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20261103165917.148720-1-karthiproffesional@gmail.com \
--to=karthiproffesional@gmail.com \
--cc=eperezma@redhat.com \
--cc=jasowangio@gmail.com \
--cc=jhubbard@nvidia.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michael.christie@oracle.com \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=pbonzini@redhat.com \
--cc=stefanha@redhat.com \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®