Skip to content

Commit ce0e3c4

Browse files
Alex Mastroawilliam
authored andcommitted
vfio: selftests: add iova allocator
Add struct iova_allocator, which gives tests a convenient way to generate legally-accessible IOVAs to map. This allocator traverses the sorted available IOVA ranges linearly, requires power-of-two size allocations, and does not support freeing iova allocations. The assumption is that tests are not IOVA space-bounded, and will not need to recycle IOVAs. This is based on Alex Williamson's patch series for adding an IOVA allocator [1]. [1] https://lore.kernel.org/all/[email protected]/ Reviewed-by: David Matlack <[email protected]> Tested-by: David Matlack <[email protected]> Signed-off-by: Alex Mastro <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alex Williamson <[email protected]>
1 parent a77fa0b commit ce0e3c4

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

tools/testing/selftests/vfio/lib/include/vfio_util.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ struct vfio_pci_device {
188188
struct vfio_pci_driver driver;
189189
};
190190

191+
struct iova_allocator {
192+
struct iommu_iova_range *ranges;
193+
u32 nranges;
194+
u32 range_idx;
195+
u64 range_offset;
196+
};
197+
191198
/*
192199
* Return the BDF string of the device that the test should use.
193200
*
@@ -212,6 +219,10 @@ void vfio_pci_device_reset(struct vfio_pci_device *device);
212219
struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device,
213220
u32 *nranges);
214221

222+
struct iova_allocator *iova_allocator_init(struct vfio_pci_device *device);
223+
void iova_allocator_cleanup(struct iova_allocator *allocator);
224+
iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size);
225+
215226
int __vfio_pci_dma_map(struct vfio_pci_device *device,
216227
struct vfio_dma_region *region);
217228
int __vfio_pci_dma_unmap(struct vfio_pci_device *device,

tools/testing/selftests/vfio/lib/vfio_pci_device.c

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
#include <sys/mman.h>
1313

1414
#include <uapi/linux/types.h>
15+
#include <linux/iommufd.h>
1516
#include <linux/limits.h>
1617
#include <linux/mman.h>
18+
#include <linux/overflow.h>
1719
#include <linux/types.h>
1820
#include <linux/vfio.h>
19-
#include <linux/iommufd.h>
2021

2122
#include "../../../kselftest.h"
2223
#include <vfio_util.h>
@@ -201,6 +202,77 @@ struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device,
201202
return ranges;
202203
}
203204

205+
struct iova_allocator *iova_allocator_init(struct vfio_pci_device *device)
206+
{
207+
struct iova_allocator *allocator;
208+
struct iommu_iova_range *ranges;
209+
u32 nranges;
210+
211+
ranges = vfio_pci_iova_ranges(device, &nranges);
212+
VFIO_ASSERT_NOT_NULL(ranges);
213+
214+
allocator = malloc(sizeof(*allocator));
215+
VFIO_ASSERT_NOT_NULL(allocator);
216+
217+
*allocator = (struct iova_allocator){
218+
.ranges = ranges,
219+
.nranges = nranges,
220+
.range_idx = 0,
221+
.range_offset = 0,
222+
};
223+
224+
return allocator;
225+
}
226+
227+
void iova_allocator_cleanup(struct iova_allocator *allocator)
228+
{
229+
free(allocator->ranges);
230+
free(allocator);
231+
}
232+
233+
iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size)
234+
{
235+
VFIO_ASSERT_GT(size, 0, "Invalid size arg, zero\n");
236+
VFIO_ASSERT_EQ(size & (size - 1), 0, "Invalid size arg, non-power-of-2\n");
237+
238+
for (;;) {
239+
struct iommu_iova_range *range;
240+
iova_t iova, last;
241+
242+
VFIO_ASSERT_LT(allocator->range_idx, allocator->nranges,
243+
"IOVA allocator out of space\n");
244+
245+
range = &allocator->ranges[allocator->range_idx];
246+
iova = range->start + allocator->range_offset;
247+
248+
/* Check for sufficient space at the current offset */
249+
if (check_add_overflow(iova, size - 1, &last) ||
250+
last > range->last)
251+
goto next_range;
252+
253+
/* Align iova to size */
254+
iova = last & ~(size - 1);
255+
256+
/* Check for sufficient space at the aligned iova */
257+
if (check_add_overflow(iova, size - 1, &last) ||
258+
last > range->last)
259+
goto next_range;
260+
261+
if (last == range->last) {
262+
allocator->range_idx++;
263+
allocator->range_offset = 0;
264+
} else {
265+
allocator->range_offset = last - range->start + 1;
266+
}
267+
268+
return iova;
269+
270+
next_range:
271+
allocator->range_idx++;
272+
allocator->range_offset = 0;
273+
}
274+
}
275+
204276
iova_t __to_iova(struct vfio_pci_device *device, void *vaddr)
205277
{
206278
struct vfio_dma_region *region;

0 commit comments

Comments
 (0)