From: Samuel Crossley <samuelcrossley@gmail.com>
To: Alex Williamson <alex@shazbot.org>
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
Samuel Crossley <samuelcrossley@gmail.com>,
Sam Crossley <samuelcrossley@gmail.com>
Subject: [PATCH] vfio/type1: conditional rescheduling while unpinning
Date: Thu, 23 Jul 2026 08:20:00 -0700 [thread overview]
Message-ID: <20260723-vfio-v1-1-3b59579916c6@gmail.com> (raw)
Tearing down a large device-passthrough DMA mapping can unpin tens to
hundreds of millions of pages in a single VFIO_IOMMU_UNMAP_DMA.
vfio_unpin_pages_remote() walks the whole contiguous range in one
uninterrupted pass with no reschedule point:
- has_rsvd (reserved / device-memory mappings, e.g. GPU HBM/BAR mapped
for peer DMA): a per-page put_pfn() loop, each doing a
pfn_valid()/is_invalid_reserved_pfn() section lookup;
- otherwise: the batched unpin_user_page_range_dirty_lock() folio walk.
The existing cond_resched() calls in the unmap path only run between
regions/chunks, so they cannot break up one giant call.
Observed on a GPU-passthrough host: unmapping a 128 GiB device-memory
region (~33.6M reserved 4K pages, has_rsvd) held a CPU 22s in the per-page
loop and tripped the soft-lockup watchdog, panicking the host:
watchdog: BUG: soft lockup - CPU#74 stuck for 22s! [qemu-system-x86]
put_pfn / is_invalid_reserved_pfn / pfn_valid
vfio_unpin_pages_remote / vfio_sync_unpin / vfio_unmap_unpin
vfio_remove_dma / vfio_iommu_type1_ioctl (VFIO_IOMMU_UNMAP_DMA)
The v6.18 batching series (d10872050ffe, d14de5b92578) optimized only the
non-reserved folio path, so it does not help this has_rsvd loop. Bound the
work per iteration and cond_resched() between chunks, mirroring the
pin-side fix in commit b1779e4f209c ("vfio/type1: conditional
rescheduling while pinning").
cond_resched() is safe on this path: the only lock held across
vfio_unpin_pages_remote() is iommu->lock, a mutex, so sleeping is allowed,
and it is taken in vfio_dma_do_unmap() and held continuously through
vfio_remove_dma() without being dropped on this path. No spinlock,
preempt-disabled, IRQ-disabled or RCU read-side section is held anywhere in
the type1 unmap path. The loop's callees (put_pfn(),
unpin_user_page_range_dirty_lock()) drop any transient folio reference and
folio lock before returning, so only the mutex is held at the reschedule
point. The path is already demonstrably sleepable: the same unmap chain
already calls cond_resched() at the region/chunk level, and the
non-reserved dirty path already takes folio_lock().
Signed-off-by: Samuel Crossley <samuelcrossley@gmail.com>
---
Signed-off-by: Sam Crossley <samuelcrossley@gmail.com>
---
drivers/vfio/vfio_iommu_type1.c | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index c8151ba54de3..ce7f6051f799 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -814,22 +814,42 @@ static inline void put_valid_unreserved_pfns(unsigned long start_pfn,
prot & IOMMU_WRITE);
}
+/* Pages to unpin per cond_resched() when tearing down a large mapping. */
+#define VFIO_UNPIN_RESCHED_PAGES (16UL * 1024) /* 64MB @ 4K pages */
+
static long vfio_unpin_pages_remote(struct vfio_dma *dma, dma_addr_t iova,
unsigned long pfn, unsigned long npage,
bool do_accounting)
{
long unlocked = 0, locked = vpfn_pages(dma, iova, npage);
+ unsigned long remaining = npage;
- if (dma->has_rsvd) {
- unsigned long i;
+ /*
+ * A single unmap of a very large device-passthrough mapping can unpin
+ * hundreds of millions of pages here. Bound the work per iteration and
+ * cond_resched() so one VFIO_IOMMU_UNMAP_DMA cannot hold a CPU past the
+ * soft-lockup watchdog. Mirrors the pin-side reschedule in commit
+ * edeca59cb88d2 ("vfio/type1: conditional rescheduling while pinning").
+ */
+ while (remaining) {
+ unsigned long batch = min(remaining, VFIO_UNPIN_RESCHED_PAGES);
- for (i = 0; i < npage; i++)
- if (put_pfn(pfn++, dma->prot))
- unlocked++;
- } else {
- put_valid_unreserved_pfns(pfn, npage, dma->prot);
- unlocked = npage;
+ if (dma->has_rsvd) {
+ unsigned long i;
+
+ for (i = 0; i < batch; i++)
+ if (put_pfn(pfn++, dma->prot))
+ unlocked++;
+ } else {
+ put_valid_unreserved_pfns(pfn, batch, dma->prot);
+ unlocked += batch;
+ pfn += batch;
+ }
+
+ remaining -= batch;
+ cond_resched();
}
+
if (do_accounting)
vfio_lock_acct(dma, locked - unlocked, true);
---
base-commit: dc77acfeb979dded39b247b60fef0399536bfa77
change-id: 20260723-vfio-decd86bf3cf6
Best regards,
--
Sam Crossley <samuelcrossley@gmail.com>
next reply other threads:[~2026-07-23 15:20 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-23 15:20 Samuel Crossley [this message]
2026-08-03 21:47 ` [PATCH] vfio/type1: conditional rescheduling while unpinning Alex Williamson
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=20260723-vfio-v1-1-3b59579916c6@gmail.com \
--to=samuelcrossley@gmail.com \
--cc=alex@shazbot.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/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