Skip to content

Commit cfedb3d

Browse files
nicolincawilliam
authored andcommitted
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: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Eric Farman <farman@linux.ibm.com> Tested-by: Eric Farman <farman@linux.ibm.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Link: https://lore.kernel.org/r/20220723020256.30081-5-nicolinc@nvidia.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
1 parent 10e19d4 commit cfedb3d

File tree

1 file changed

+56
-14
lines changed

1 file changed

+56
-14
lines changed

drivers/s390/cio/vfio_ccw_cp.c

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,38 @@ static int pfn_array_alloc(struct pfn_array *pa, u64 iova, unsigned int len)
9090
return 0;
9191
}
9292

93+
/*
94+
* pfn_array_unpin() - Unpin user pages in memory
95+
* @pa: pfn_array on which to perform the operation
96+
* @vdev: the vfio device to perform the operation
97+
* @pa_nr: number of user pages to unpin
98+
*
99+
* Only unpin if any pages were pinned to begin with, i.e. pa_nr > 0,
100+
* otherwise only clear pa->pa_nr
101+
*/
102+
static void pfn_array_unpin(struct pfn_array *pa,
103+
struct vfio_device *vdev, int pa_nr)
104+
{
105+
int unpinned = 0, npage = 1;
106+
107+
while (unpinned < pa_nr) {
108+
unsigned long *first = &pa->pa_iova_pfn[unpinned];
109+
unsigned long *last = &first[npage];
110+
111+
if (unpinned + npage < pa_nr &&
112+
*first + npage == *last) {
113+
npage++;
114+
continue;
115+
}
116+
117+
vfio_unpin_pages(vdev, first, npage);
118+
unpinned += npage;
119+
npage = 1;
120+
}
121+
122+
pa->pa_nr = 0;
123+
}
124+
93125
/*
94126
* pfn_array_pin() - Pin user pages in memory
95127
* @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)
101133
*/
102134
static int pfn_array_pin(struct pfn_array *pa, struct vfio_device *vdev)
103135
{
136+
int pinned = 0, npage = 1;
104137
int ret = 0;
105138

106-
ret = vfio_pin_pages(vdev, pa->pa_iova_pfn, pa->pa_nr,
107-
IOMMU_READ | IOMMU_WRITE, pa->pa_pfn);
139+
while (pinned < pa->pa_nr) {
140+
unsigned long *first = &pa->pa_iova_pfn[pinned];
141+
unsigned long *last = &first[npage];
108142

109-
if (ret < 0) {
110-
goto err_out;
111-
} else if (ret > 0 && ret != pa->pa_nr) {
112-
vfio_unpin_pages(vdev, pa->pa_iova_pfn, ret);
113-
ret = -EINVAL;
114-
goto err_out;
143+
if (pinned + npage < pa->pa_nr &&
144+
*first + npage == *last) {
145+
npage++;
146+
continue;
147+
}
148+
149+
ret = vfio_pin_pages(vdev, first, npage,
150+
IOMMU_READ | IOMMU_WRITE,
151+
&pa->pa_pfn[pinned]);
152+
if (ret < 0) {
153+
goto err_out;
154+
} else if (ret > 0 && ret != npage) {
155+
pinned += ret;
156+
ret = -EINVAL;
157+
goto err_out;
158+
}
159+
pinned += npage;
160+
npage = 1;
115161
}
116162

117163
return ret;
118164

119165
err_out:
120-
pa->pa_nr = 0;
121-
166+
pfn_array_unpin(pa, vdev, pinned);
122167
return ret;
123168
}
124169

125170
/* Unpin the pages before releasing the memory. */
126171
static void pfn_array_unpin_free(struct pfn_array *pa, struct vfio_device *vdev)
127172
{
128-
/* Only unpin if any pages were pinned to begin with */
129-
if (pa->pa_nr)
130-
vfio_unpin_pages(vdev, pa->pa_iova_pfn, pa->pa_nr);
131-
pa->pa_nr = 0;
173+
pfn_array_unpin(pa, vdev, pa->pa_nr);
132174
kfree(pa->pa_iova_pfn);
133175
}
134176

0 commit comments

Comments
 (0)