Good catch! Thanks for the fix, it looks good to me.

On 10/31/2026 10:45 AM, Tal Zussman wrote:
When pin_user_pages() or vhost_vdpa_map() fails, vhost_vdpa_pa_map()
is supposed to unpin the pages pinned after the last successfully
mapped chunk, i.e. the range [map_pfn, last_pfn]. The unpin is guarded
by nchunks, but nchunks is reset to 0 every time a chunk is mapped from
inside the current page list, so a failure after any such boundary
leaves that pending range pinned forever. With physically scattered
memory nearly every page list contains a boundary, so the leak happens
on practically every failed IOTLB update.

The pending range is non-empty exactly when a page has been pinned,
which last_pfn already tells us, so drop nchunks and test last_pfn.

Fixes: ad89653f79f1 ("vhost-vdpa: fix page pinning leakage in error path 
(rework)")
Cc: [email protected]
Acked-by: Si-Wei Liu <[email protected]>

Signed-off-by: Tal Zussman <[email protected]>
---
  drivers/vhost/vdpa.c | 8 ++------
  1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ff7d90719171..d9af728942b3 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -1151,7 +1151,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
        unsigned long list_size = PAGE_SIZE / sizeof(struct page *);
        unsigned int gup_flags = FOLL_LONGTERM;
        unsigned long npages, cur_base, map_pfn, last_pfn = 0;
-       unsigned long lock_limit, sz2pin, nchunks, i;
+       unsigned long lock_limit, sz2pin, i;
        unsigned long page_offset;
        u64 start = iova;
        long pinned;
@@ -1187,7 +1187,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
cur_base = uaddr & PAGE_MASK;
        iova &= PAGE_MASK;
-       nchunks = 0;
while (npages) {
                sz2pin = min_t(unsigned long, npages, list_size);
@@ -1202,7 +1201,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
                        }
                        goto out;
                }
-               nchunks++;
if (!last_pfn)
                        map_pfn = page_to_pfn(page_list[0]);
@@ -1233,7 +1231,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
map_pfn = this_pfn;
                                iova += csize;
-                               nchunks = 0;
                        }
last_pfn = this_pfn;
@@ -1248,7 +1245,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
                             PFN_PHYS(map_pfn), perm, NULL);
  out:
        if (ret) {
-               if (nchunks) {
+               if (last_pfn) {
                        unsigned long pfn;
/*
@@ -1260,7 +1257,6 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
                         * the corresponding unpinning will be handled by
                         * vdpa_unmap().
                         */
-                       WARN_ON(!last_pfn);
                        for (pfn = map_pfn; pfn <= last_pfn; pfn++)
                                unpin_user_page(pfn_to_page(pfn));
                }

--
2.39.5


Reply via email to