Skip to content

Commit 6da43bb

Browse files
committed
Merge tag 'vfio-v6.18-rc6' of https://github.com/awilliam/linux-vfio
Pull VFIO seftest fixes from Alex Williamson: - Fix vfio selftests to remove the expectation that the IOMMU supports a 64-bit IOVA space. These manifest both in the original set of tests introduced this development cycle in identity mapping the IOVA to buffer virtual address space, as well as the more recent boundary testing. Implement facilities for collecting the valid IOVA ranges from the backend, implement a simple IOVA allocator, and use the information for determining extents (Alex Mastro) * tag 'vfio-v6.18-rc6' of https://github.com/awilliam/linux-vfio: vfio: selftests: replace iova=vaddr with allocated iovas vfio: selftests: add iova allocator vfio: selftests: fix map limit tests to use last available iova vfio: selftests: add iova range query helpers
2 parents 01814e1 + d323ad7 commit 6da43bb

4 files changed

Lines changed: 288 additions & 9 deletions

File tree

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
#include <fcntl.h>
66
#include <string.h>
7-
#include <linux/vfio.h>
7+
8+
#include <uapi/linux/types.h>
9+
#include <linux/iommufd.h>
810
#include <linux/list.h>
911
#include <linux/pci_regs.h>
12+
#include <linux/vfio.h>
1013

1114
#include "../../../kselftest.h"
1215

@@ -185,6 +188,13 @@ struct vfio_pci_device {
185188
struct vfio_pci_driver driver;
186189
};
187190

191+
struct iova_allocator {
192+
struct iommu_iova_range *ranges;
193+
u32 nranges;
194+
u32 range_idx;
195+
u64 range_offset;
196+
};
197+
188198
/*
189199
* Return the BDF string of the device that the test should use.
190200
*
@@ -206,6 +216,13 @@ struct vfio_pci_device *vfio_pci_device_init(const char *bdf, const char *iommu_
206216
void vfio_pci_device_cleanup(struct vfio_pci_device *device);
207217
void vfio_pci_device_reset(struct vfio_pci_device *device);
208218

219+
struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device,
220+
u32 *nranges);
221+
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+
209226
int __vfio_pci_dma_map(struct vfio_pci_device *device,
210227
struct vfio_dma_region *region);
211228
int __vfio_pci_dma_unmap(struct vfio_pci_device *device,

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

Lines changed: 245 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>
@@ -29,6 +30,249 @@
2930
VFIO_ASSERT_EQ(__ret, 0, "ioctl(%s, %s, %s) returned %d\n", #_fd, #_op, #_arg, __ret); \
3031
} while (0)
3132

33+
static struct vfio_info_cap_header *next_cap_hdr(void *buf, u32 bufsz,
34+
u32 *cap_offset)
35+
{
36+
struct vfio_info_cap_header *hdr;
37+
38+
if (!*cap_offset)
39+
return NULL;
40+
41+
VFIO_ASSERT_LT(*cap_offset, bufsz);
42+
VFIO_ASSERT_GE(bufsz - *cap_offset, sizeof(*hdr));
43+
44+
hdr = (struct vfio_info_cap_header *)((u8 *)buf + *cap_offset);
45+
*cap_offset = hdr->next;
46+
47+
return hdr;
48+
}
49+
50+
static struct vfio_info_cap_header *vfio_iommu_info_cap_hdr(struct vfio_iommu_type1_info *info,
51+
u16 cap_id)
52+
{
53+
struct vfio_info_cap_header *hdr;
54+
u32 cap_offset = info->cap_offset;
55+
u32 max_depth;
56+
u32 depth = 0;
57+
58+
if (!(info->flags & VFIO_IOMMU_INFO_CAPS))
59+
return NULL;
60+
61+
if (cap_offset)
62+
VFIO_ASSERT_GE(cap_offset, sizeof(*info));
63+
64+
max_depth = (info->argsz - sizeof(*info)) / sizeof(*hdr);
65+
66+
while ((hdr = next_cap_hdr(info, info->argsz, &cap_offset))) {
67+
depth++;
68+
VFIO_ASSERT_LE(depth, max_depth, "Capability chain contains a cycle\n");
69+
70+
if (hdr->id == cap_id)
71+
return hdr;
72+
}
73+
74+
return NULL;
75+
}
76+
77+
/* Return buffer including capability chain, if present. Free with free() */
78+
static struct vfio_iommu_type1_info *vfio_iommu_get_info(struct vfio_pci_device *device)
79+
{
80+
struct vfio_iommu_type1_info *info;
81+
82+
info = malloc(sizeof(*info));
83+
VFIO_ASSERT_NOT_NULL(info);
84+
85+
*info = (struct vfio_iommu_type1_info) {
86+
.argsz = sizeof(*info),
87+
};
88+
89+
ioctl_assert(device->container_fd, VFIO_IOMMU_GET_INFO, info);
90+
VFIO_ASSERT_GE(info->argsz, sizeof(*info));
91+
92+
info = realloc(info, info->argsz);
93+
VFIO_ASSERT_NOT_NULL(info);
94+
95+
ioctl_assert(device->container_fd, VFIO_IOMMU_GET_INFO, info);
96+
VFIO_ASSERT_GE(info->argsz, sizeof(*info));
97+
98+
return info;
99+
}
100+
101+
/*
102+
* Return iova ranges for the device's container. Normalize vfio_iommu_type1 to
103+
* report iommufd's iommu_iova_range. Free with free().
104+
*/
105+
static struct iommu_iova_range *vfio_iommu_iova_ranges(struct vfio_pci_device *device,
106+
u32 *nranges)
107+
{
108+
struct vfio_iommu_type1_info_cap_iova_range *cap_range;
109+
struct vfio_iommu_type1_info *info;
110+
struct vfio_info_cap_header *hdr;
111+
struct iommu_iova_range *ranges = NULL;
112+
113+
info = vfio_iommu_get_info(device);
114+
hdr = vfio_iommu_info_cap_hdr(info, VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE);
115+
VFIO_ASSERT_NOT_NULL(hdr);
116+
117+
cap_range = container_of(hdr, struct vfio_iommu_type1_info_cap_iova_range, header);
118+
VFIO_ASSERT_GT(cap_range->nr_iovas, 0);
119+
120+
ranges = calloc(cap_range->nr_iovas, sizeof(*ranges));
121+
VFIO_ASSERT_NOT_NULL(ranges);
122+
123+
for (u32 i = 0; i < cap_range->nr_iovas; i++) {
124+
ranges[i] = (struct iommu_iova_range){
125+
.start = cap_range->iova_ranges[i].start,
126+
.last = cap_range->iova_ranges[i].end,
127+
};
128+
}
129+
130+
*nranges = cap_range->nr_iovas;
131+
132+
free(info);
133+
return ranges;
134+
}
135+
136+
/* Return iova ranges of the device's IOAS. Free with free() */
137+
static struct iommu_iova_range *iommufd_iova_ranges(struct vfio_pci_device *device,
138+
u32 *nranges)
139+
{
140+
struct iommu_iova_range *ranges;
141+
int ret;
142+
143+
struct iommu_ioas_iova_ranges query = {
144+
.size = sizeof(query),
145+
.ioas_id = device->ioas_id,
146+
};
147+
148+
ret = ioctl(device->iommufd, IOMMU_IOAS_IOVA_RANGES, &query);
149+
VFIO_ASSERT_EQ(ret, -1);
150+
VFIO_ASSERT_EQ(errno, EMSGSIZE);
151+
VFIO_ASSERT_GT(query.num_iovas, 0);
152+
153+
ranges = calloc(query.num_iovas, sizeof(*ranges));
154+
VFIO_ASSERT_NOT_NULL(ranges);
155+
156+
query.allowed_iovas = (uintptr_t)ranges;
157+
158+
ioctl_assert(device->iommufd, IOMMU_IOAS_IOVA_RANGES, &query);
159+
*nranges = query.num_iovas;
160+
161+
return ranges;
162+
}
163+
164+
static int iova_range_comp(const void *a, const void *b)
165+
{
166+
const struct iommu_iova_range *ra = a, *rb = b;
167+
168+
if (ra->start < rb->start)
169+
return -1;
170+
171+
if (ra->start > rb->start)
172+
return 1;
173+
174+
return 0;
175+
}
176+
177+
/* Return sorted IOVA ranges of the device. Free with free(). */
178+
struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device,
179+
u32 *nranges)
180+
{
181+
struct iommu_iova_range *ranges;
182+
183+
if (device->iommufd)
184+
ranges = iommufd_iova_ranges(device, nranges);
185+
else
186+
ranges = vfio_iommu_iova_ranges(device, nranges);
187+
188+
if (!ranges)
189+
return NULL;
190+
191+
VFIO_ASSERT_GT(*nranges, 0);
192+
193+
/* Sort and check that ranges are sane and non-overlapping */
194+
qsort(ranges, *nranges, sizeof(*ranges), iova_range_comp);
195+
VFIO_ASSERT_LT(ranges[0].start, ranges[0].last);
196+
197+
for (u32 i = 1; i < *nranges; i++) {
198+
VFIO_ASSERT_LT(ranges[i].start, ranges[i].last);
199+
VFIO_ASSERT_LT(ranges[i - 1].last, ranges[i].start);
200+
}
201+
202+
return ranges;
203+
}
204+
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+
32276
iova_t __to_iova(struct vfio_pci_device *device, void *vaddr)
33277
{
34278
struct vfio_dma_region *region;

tools/testing/selftests/vfio/vfio_dma_mapping_test.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <sys/mman.h>
44
#include <unistd.h>
55

6+
#include <uapi/linux/types.h>
7+
#include <linux/iommufd.h>
68
#include <linux/limits.h>
79
#include <linux/mman.h>
810
#include <linux/sizes.h>
@@ -93,6 +95,7 @@ static int iommu_mapping_get(const char *bdf, u64 iova,
9395

9496
FIXTURE(vfio_dma_mapping_test) {
9597
struct vfio_pci_device *device;
98+
struct iova_allocator *iova_allocator;
9699
};
97100

98101
FIXTURE_VARIANT(vfio_dma_mapping_test) {
@@ -117,10 +120,12 @@ FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES(anonymous_hugetlb_1gb, SZ_1G, MAP_HUGETLB |
117120
FIXTURE_SETUP(vfio_dma_mapping_test)
118121
{
119122
self->device = vfio_pci_device_init(device_bdf, variant->iommu_mode);
123+
self->iova_allocator = iova_allocator_init(self->device);
120124
}
121125

122126
FIXTURE_TEARDOWN(vfio_dma_mapping_test)
123127
{
128+
iova_allocator_cleanup(self->iova_allocator);
124129
vfio_pci_device_cleanup(self->device);
125130
}
126131

@@ -142,7 +147,7 @@ TEST_F(vfio_dma_mapping_test, dma_map_unmap)
142147
else
143148
ASSERT_NE(region.vaddr, MAP_FAILED);
144149

145-
region.iova = (u64)region.vaddr;
150+
region.iova = iova_allocator_alloc(self->iova_allocator, size);
146151
region.size = size;
147152

148153
vfio_pci_dma_map(self->device, &region);
@@ -219,7 +224,10 @@ FIXTURE_VARIANT_ADD_ALL_IOMMU_MODES();
219224
FIXTURE_SETUP(vfio_dma_map_limit_test)
220225
{
221226
struct vfio_dma_region *region = &self->region;
227+
struct iommu_iova_range *ranges;
222228
u64 region_size = getpagesize();
229+
iova_t last_iova;
230+
u32 nranges;
223231

224232
/*
225233
* Over-allocate mmap by double the size to provide enough backing vaddr
@@ -232,8 +240,13 @@ FIXTURE_SETUP(vfio_dma_map_limit_test)
232240
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
233241
ASSERT_NE(region->vaddr, MAP_FAILED);
234242

235-
/* One page prior to the end of address space */
236-
region->iova = ~(iova_t)0 & ~(region_size - 1);
243+
ranges = vfio_pci_iova_ranges(self->device, &nranges);
244+
VFIO_ASSERT_NOT_NULL(ranges);
245+
last_iova = ranges[nranges - 1].last;
246+
free(ranges);
247+
248+
/* One page prior to the last iova */
249+
region->iova = last_iova & ~(region_size - 1);
237250
region->size = region_size;
238251
}
239252

@@ -276,6 +289,7 @@ TEST_F(vfio_dma_map_limit_test, overflow)
276289
struct vfio_dma_region *region = &self->region;
277290
int rc;
278291

292+
region->iova = ~(iova_t)0 & ~(region->size - 1);
279293
region->size = self->mmap_size;
280294

281295
rc = __vfio_pci_dma_map(self->device, region);

0 commit comments

Comments
 (0)