Commit cfedb3d5 authored by Nicolin Chen's avatar Nicolin Chen Committed by Alex Williamson

vfio/ccw: Only pass in contiguous pages

This driver is the only caller of vfio_pin/unpin_pages that might pass
in a non-contiguous PFN list, but in many cases it has a contiguous PFN
list to process. So letting VFIO API handle a non-contiguous PFN list
is actually counterproductive.

Add a pair of simple loops to pass in contiguous PFNs only, to have an
efficient implementation in VFIO.
Reviewed-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Reviewed-by: default avatarEric Farman <farman@linux.ibm.com>
Tested-by: default avatarEric Farman <farman@linux.ibm.com>
Signed-off-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Link: https://lore.kernel.org/r/20220723020256.30081-5-nicolinc@nvidia.comSigned-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent 10e19d49
...@@ -90,6 +90,38 @@ static int pfn_array_alloc(struct pfn_array *pa, u64 iova, unsigned int len) ...@@ -90,6 +90,38 @@ static int pfn_array_alloc(struct pfn_array *pa, u64 iova, unsigned int len)
return 0; return 0;
} }
/*
* pfn_array_unpin() - Unpin user pages in memory
* @pa: pfn_array on which to perform the operation
* @vdev: the vfio device to perform the operation
* @pa_nr: number of user pages to unpin
*
* Only unpin if any pages were pinned to begin with, i.e. pa_nr > 0,
* otherwise only clear pa->pa_nr
*/
static void pfn_array_unpin(struct pfn_array *pa,
struct vfio_device *vdev, int pa_nr)
{
int unpinned = 0, npage = 1;
while (unpinned < pa_nr) {
unsigned long *first = &pa->pa_iova_pfn[unpinned];
unsigned long *last = &first[npage];
if (unpinned + npage < pa_nr &&
*first + npage == *last) {
npage++;
continue;
}
vfio_unpin_pages(vdev, first, npage);
unpinned += npage;
npage = 1;
}
pa->pa_nr = 0;
}
/* /*
* pfn_array_pin() - Pin user pages in memory * pfn_array_pin() - Pin user pages in memory
* @pa: pfn_array on which to perform the operation * @pa: pfn_array on which to perform the operation
...@@ -101,34 +133,44 @@ static int pfn_array_alloc(struct pfn_array *pa, u64 iova, unsigned int len) ...@@ -101,34 +133,44 @@ static int pfn_array_alloc(struct pfn_array *pa, u64 iova, unsigned int len)
*/ */
static int pfn_array_pin(struct pfn_array *pa, struct vfio_device *vdev) static int pfn_array_pin(struct pfn_array *pa, struct vfio_device *vdev)
{ {
int pinned = 0, npage = 1;
int ret = 0; int ret = 0;
ret = vfio_pin_pages(vdev, pa->pa_iova_pfn, pa->pa_nr, while (pinned < pa->pa_nr) {
IOMMU_READ | IOMMU_WRITE, pa->pa_pfn); unsigned long *first = &pa->pa_iova_pfn[pinned];
unsigned long *last = &first[npage];
if (ret < 0) { if (pinned + npage < pa->pa_nr &&
goto err_out; *first + npage == *last) {
} else if (ret > 0 && ret != pa->pa_nr) { npage++;
vfio_unpin_pages(vdev, pa->pa_iova_pfn, ret); continue;
ret = -EINVAL; }
goto err_out;
ret = vfio_pin_pages(vdev, first, npage,
IOMMU_READ | IOMMU_WRITE,
&pa->pa_pfn[pinned]);
if (ret < 0) {
goto err_out;
} else if (ret > 0 && ret != npage) {
pinned += ret;
ret = -EINVAL;
goto err_out;
}
pinned += npage;
npage = 1;
} }
return ret; return ret;
err_out: err_out:
pa->pa_nr = 0; pfn_array_unpin(pa, vdev, pinned);
return ret; return ret;
} }
/* Unpin the pages before releasing the memory. */ /* Unpin the pages before releasing the memory. */
static void pfn_array_unpin_free(struct pfn_array *pa, struct vfio_device *vdev) static void pfn_array_unpin_free(struct pfn_array *pa, struct vfio_device *vdev)
{ {
/* Only unpin if any pages were pinned to begin with */ pfn_array_unpin(pa, vdev, pa->pa_nr);
if (pa->pa_nr)
vfio_unpin_pages(vdev, pa->pa_iova_pfn, pa->pa_nr);
pa->pa_nr = 0;
kfree(pa->pa_iova_pfn); kfree(pa->pa_iova_pfn);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment