Skip to content

Commit eb996ee

Browse files
committed
vfio/type1: Use vfio_batch for vaddr_get_pfns()
Passing the vfio_batch to vaddr_get_pfns() allows for greater distinction between page backed pfns and pfnmaps. In the case of page backed pfns, vfio_batch.size is set to a positive value matching the number of pages filled in vfio_batch.pages. For a pfnmap, vfio_batch.size remains zero as vfio_batch.pages are not used. In both cases the return value continues to indicate the number of pfns and the provided pfn arg is set to the initial pfn value. This allows us to shortcut the pfnmap case, which is detected by the zero vfio_batch.size. pfnmaps do not contribute to locked memory accounting, therefore we can update counters and continue directly, which also enables a future where vaddr_get_pfns() can return a value greater than one for consecutive pfnmaps. NB. Now that we're not guessing whether the initial pfn is page backed or pfnmap, we no longer need to special case the put_pfn() and batch size reset. It's safe for vfio_batch_unpin() to handle this case. Reviewed-by: Peter Xu <[email protected]> Reviewed-by: Mitchell Augustin <[email protected]> Tested-by: Mitchell Augustin <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alex Williamson <[email protected]>
1 parent 7a701e9 commit eb996ee

File tree

1 file changed

+35
-28
lines changed

1 file changed

+35
-28
lines changed

drivers/vfio/vfio_iommu_type1.c

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -555,12 +555,16 @@ static int follow_fault_pfn(struct vm_area_struct *vma, struct mm_struct *mm,
555555

556556
/*
557557
* Returns the positive number of pfns successfully obtained or a negative
558-
* error code.
558+
* error code. The initial pfn is stored in the pfn arg. For page-backed
559+
* pfns, the provided batch is also updated to indicate the filled pages and
560+
* initial offset. For VM_PFNMAP pfns, only the returned number of pfns and
561+
* returned initial pfn are provided; subsequent pfns are contiguous.
559562
*/
560563
static int vaddr_get_pfns(struct mm_struct *mm, unsigned long vaddr,
561564
long npages, int prot, unsigned long *pfn,
562-
struct page **pages)
565+
struct vfio_batch *batch)
563566
{
567+
long pin_pages = min_t(long, npages, batch->capacity);
564568
struct vm_area_struct *vma;
565569
unsigned int flags = 0;
566570
int ret;
@@ -569,10 +573,12 @@ static int vaddr_get_pfns(struct mm_struct *mm, unsigned long vaddr,
569573
flags |= FOLL_WRITE;
570574

571575
mmap_read_lock(mm);
572-
ret = pin_user_pages_remote(mm, vaddr, npages, flags | FOLL_LONGTERM,
573-
pages, NULL);
576+
ret = pin_user_pages_remote(mm, vaddr, pin_pages, flags | FOLL_LONGTERM,
577+
batch->pages, NULL);
574578
if (ret > 0) {
575-
*pfn = page_to_pfn(pages[0]);
579+
*pfn = page_to_pfn(batch->pages[0]);
580+
batch->size = ret;
581+
batch->offset = 0;
576582
goto done;
577583
} else if (!ret) {
578584
ret = -EFAULT;
@@ -628,32 +634,42 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
628634
*pfn_base = 0;
629635
}
630636

637+
if (unlikely(disable_hugepages))
638+
npage = 1;
639+
631640
while (npage) {
632641
if (!batch->size) {
633642
/* Empty batch, so refill it. */
634-
long req_pages = min_t(long, npage, batch->capacity);
635-
636-
ret = vaddr_get_pfns(mm, vaddr, req_pages, dma->prot,
637-
&pfn, batch->pages);
643+
ret = vaddr_get_pfns(mm, vaddr, npage, dma->prot,
644+
&pfn, batch);
638645
if (ret < 0)
639646
goto unpin_out;
640647

641-
batch->size = ret;
642-
batch->offset = 0;
643-
644648
if (!*pfn_base) {
645649
*pfn_base = pfn;
646650
rsvd = is_invalid_reserved_pfn(*pfn_base);
647651
}
652+
653+
/* Handle pfnmap */
654+
if (!batch->size) {
655+
if (pfn != *pfn_base + pinned || !rsvd)
656+
goto out;
657+
658+
pinned += ret;
659+
npage -= ret;
660+
vaddr += (PAGE_SIZE * ret);
661+
iova += (PAGE_SIZE * ret);
662+
continue;
663+
}
648664
}
649665

650666
/*
651-
* pfn is preset for the first iteration of this inner loop and
652-
* updated at the end to handle a VM_PFNMAP pfn. In that case,
653-
* batch->pages isn't valid (there's no struct page), so allow
654-
* batch->pages to be touched only when there's more than one
655-
* pfn to check, which guarantees the pfns are from a
656-
* !VM_PFNMAP vma.
667+
* pfn is preset for the first iteration of this inner loop
668+
* due to the fact that vaddr_get_pfns() needs to provide the
669+
* initial pfn for pfnmaps. Therefore to reduce redundancy,
670+
* the next pfn is fetched at the end of the loop.
671+
* A PageReserved() page could still qualify as page backed
672+
* and rsvd here, and therefore continues to use the batch.
657673
*/
658674
while (true) {
659675
if (pfn != *pfn_base + pinned ||
@@ -688,21 +704,12 @@ static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
688704

689705
pfn = page_to_pfn(batch->pages[batch->offset]);
690706
}
691-
692-
if (unlikely(disable_hugepages))
693-
break;
694707
}
695708

696709
out:
697710
ret = vfio_lock_acct(dma, lock_acct, false);
698711

699712
unpin_out:
700-
if (batch->size == 1 && !batch->offset) {
701-
/* May be a VM_PFNMAP pfn, which the batch can't remember. */
702-
put_pfn(pfn, dma->prot);
703-
batch->size = 0;
704-
}
705-
706713
if (ret < 0) {
707714
if (pinned && !rsvd) {
708715
for (pfn = *pfn_base ; pinned ; pfn++, pinned--)
@@ -750,7 +757,7 @@ static int vfio_pin_page_external(struct vfio_dma *dma, unsigned long vaddr,
750757

751758
vfio_batch_init_single(&batch);
752759

753-
ret = vaddr_get_pfns(mm, vaddr, 1, dma->prot, pfn_base, batch.pages);
760+
ret = vaddr_get_pfns(mm, vaddr, 1, dma->prot, pfn_base, &batch);
754761
if (ret != 1)
755762
goto out;
756763

0 commit comments

Comments
 (0)