Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+288 -9
+18 -1
tools/testing/selftests/vfio/lib/include/vfio_util.h
··· 4 4 5 5 #include <fcntl.h> 6 6 #include <string.h> 7 - #include <linux/vfio.h> 7 + 8 + #include <uapi/linux/types.h> 9 + #include <linux/iommufd.h> 8 10 #include <linux/list.h> 9 11 #include <linux/pci_regs.h> 12 + #include <linux/vfio.h> 10 13 11 14 #include "../../../kselftest.h" 12 15 ··· 188 185 struct vfio_pci_driver driver; 189 186 }; 190 187 188 + struct iova_allocator { 189 + struct iommu_iova_range *ranges; 190 + u32 nranges; 191 + u32 range_idx; 192 + u64 range_offset; 193 + }; 194 + 191 195 /* 192 196 * Return the BDF string of the device that the test should use. 193 197 * ··· 215 205 struct vfio_pci_device *vfio_pci_device_init(const char *bdf, const char *iommu_mode); 216 206 void vfio_pci_device_cleanup(struct vfio_pci_device *device); 217 207 void vfio_pci_device_reset(struct vfio_pci_device *device); 208 + 209 + struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device, 210 + u32 *nranges); 211 + 212 + struct iova_allocator *iova_allocator_init(struct vfio_pci_device *device); 213 + void iova_allocator_cleanup(struct iova_allocator *allocator); 214 + iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size); 218 215 219 216 int __vfio_pci_dma_map(struct vfio_pci_device *device, 220 217 struct vfio_dma_region *region);
+245 -1
tools/testing/selftests/vfio/lib/vfio_pci_device.c
··· 12 12 #include <sys/mman.h> 13 13 14 14 #include <uapi/linux/types.h> 15 + #include <linux/iommufd.h> 15 16 #include <linux/limits.h> 16 17 #include <linux/mman.h> 18 + #include <linux/overflow.h> 17 19 #include <linux/types.h> 18 20 #include <linux/vfio.h> 19 - #include <linux/iommufd.h> 20 21 21 22 #include "../../../kselftest.h" 22 23 #include <vfio_util.h> ··· 29 28 int __ret = ioctl((_fd), (_op), (__arg)); \ 30 29 VFIO_ASSERT_EQ(__ret, 0, "ioctl(%s, %s, %s) returned %d\n", #_fd, #_op, #_arg, __ret); \ 31 30 } while (0) 31 + 32 + static struct vfio_info_cap_header *next_cap_hdr(void *buf, u32 bufsz, 33 + u32 *cap_offset) 34 + { 35 + struct vfio_info_cap_header *hdr; 36 + 37 + if (!*cap_offset) 38 + return NULL; 39 + 40 + VFIO_ASSERT_LT(*cap_offset, bufsz); 41 + VFIO_ASSERT_GE(bufsz - *cap_offset, sizeof(*hdr)); 42 + 43 + hdr = (struct vfio_info_cap_header *)((u8 *)buf + *cap_offset); 44 + *cap_offset = hdr->next; 45 + 46 + return hdr; 47 + } 48 + 49 + static struct vfio_info_cap_header *vfio_iommu_info_cap_hdr(struct vfio_iommu_type1_info *info, 50 + u16 cap_id) 51 + { 52 + struct vfio_info_cap_header *hdr; 53 + u32 cap_offset = info->cap_offset; 54 + u32 max_depth; 55 + u32 depth = 0; 56 + 57 + if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) 58 + return NULL; 59 + 60 + if (cap_offset) 61 + VFIO_ASSERT_GE(cap_offset, sizeof(*info)); 62 + 63 + max_depth = (info->argsz - sizeof(*info)) / sizeof(*hdr); 64 + 65 + while ((hdr = next_cap_hdr(info, info->argsz, &cap_offset))) { 66 + depth++; 67 + VFIO_ASSERT_LE(depth, max_depth, "Capability chain contains a cycle\n"); 68 + 69 + if (hdr->id == cap_id) 70 + return hdr; 71 + } 72 + 73 + return NULL; 74 + } 75 + 76 + /* Return buffer including capability chain, if present. Free with free() */ 77 + static struct vfio_iommu_type1_info *vfio_iommu_get_info(struct vfio_pci_device *device) 78 + { 79 + struct vfio_iommu_type1_info *info; 80 + 81 + info = malloc(sizeof(*info)); 82 + VFIO_ASSERT_NOT_NULL(info); 83 + 84 + *info = (struct vfio_iommu_type1_info) { 85 + .argsz = sizeof(*info), 86 + }; 87 + 88 + ioctl_assert(device->container_fd, VFIO_IOMMU_GET_INFO, info); 89 + VFIO_ASSERT_GE(info->argsz, sizeof(*info)); 90 + 91 + info = realloc(info, info->argsz); 92 + VFIO_ASSERT_NOT_NULL(info); 93 + 94 + ioctl_assert(device->container_fd, VFIO_IOMMU_GET_INFO, info); 95 + VFIO_ASSERT_GE(info->argsz, sizeof(*info)); 96 + 97 + return info; 98 + } 99 + 100 + /* 101 + * Return iova ranges for the device's container. Normalize vfio_iommu_type1 to 102 + * report iommufd's iommu_iova_range. Free with free(). 103 + */ 104 + static struct iommu_iova_range *vfio_iommu_iova_ranges(struct vfio_pci_device *device, 105 + u32 *nranges) 106 + { 107 + struct vfio_iommu_type1_info_cap_iova_range *cap_range; 108 + struct vfio_iommu_type1_info *info; 109 + struct vfio_info_cap_header *hdr; 110 + struct iommu_iova_range *ranges = NULL; 111 + 112 + info = vfio_iommu_get_info(device); 113 + hdr = vfio_iommu_info_cap_hdr(info, VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE); 114 + VFIO_ASSERT_NOT_NULL(hdr); 115 + 116 + cap_range = container_of(hdr, struct vfio_iommu_type1_info_cap_iova_range, header); 117 + VFIO_ASSERT_GT(cap_range->nr_iovas, 0); 118 + 119 + ranges = calloc(cap_range->nr_iovas, sizeof(*ranges)); 120 + VFIO_ASSERT_NOT_NULL(ranges); 121 + 122 + for (u32 i = 0; i < cap_range->nr_iovas; i++) { 123 + ranges[i] = (struct iommu_iova_range){ 124 + .start = cap_range->iova_ranges[i].start, 125 + .last = cap_range->iova_ranges[i].end, 126 + }; 127 + } 128 + 129 + *nranges = cap_range->nr_iovas; 130 + 131 + free(info); 132 + return ranges; 133 + } 134 + 135 + /* Return iova ranges of the device's IOAS. Free with free() */ 136 + static struct iommu_iova_range *iommufd_iova_ranges(struct vfio_pci_device *device, 137 + u32 *nranges) 138 + { 139 + struct iommu_iova_range *ranges; 140 + int ret; 141 + 142 + struct iommu_ioas_iova_ranges query = { 143 + .size = sizeof(query), 144 + .ioas_id = device->ioas_id, 145 + }; 146 + 147 + ret = ioctl(device->iommufd, IOMMU_IOAS_IOVA_RANGES, &query); 148 + VFIO_ASSERT_EQ(ret, -1); 149 + VFIO_ASSERT_EQ(errno, EMSGSIZE); 150 + VFIO_ASSERT_GT(query.num_iovas, 0); 151 + 152 + ranges = calloc(query.num_iovas, sizeof(*ranges)); 153 + VFIO_ASSERT_NOT_NULL(ranges); 154 + 155 + query.allowed_iovas = (uintptr_t)ranges; 156 + 157 + ioctl_assert(device->iommufd, IOMMU_IOAS_IOVA_RANGES, &query); 158 + *nranges = query.num_iovas; 159 + 160 + return ranges; 161 + } 162 + 163 + static int iova_range_comp(const void *a, const void *b) 164 + { 165 + const struct iommu_iova_range *ra = a, *rb = b; 166 + 167 + if (ra->start < rb->start) 168 + return -1; 169 + 170 + if (ra->start > rb->start) 171 + return 1; 172 + 173 + return 0; 174 + } 175 + 176 + /* Return sorted IOVA ranges of the device. Free with free(). */ 177 + struct iommu_iova_range *vfio_pci_iova_ranges(struct vfio_pci_device *device, 178 + u32 *nranges) 179 + { 180 + struct iommu_iova_range *ranges; 181 + 182 + if (device->iommufd) 183 + ranges = iommufd_iova_ranges(device, nranges); 184 + else 185 + ranges = vfio_iommu_iova_ranges(device, nranges); 186 + 187 + if (!ranges) 188 + return NULL; 189 + 190 + VFIO_ASSERT_GT(*nranges, 0); 191 + 192 + /* Sort and check that ranges are sane and non-overlapping */ 193 + qsort(ranges, *nranges, sizeof(*ranges), iova_range_comp); 194 + VFIO_ASSERT_LT(ranges[0].start, ranges[0].last); 195 + 196 + for (u32 i = 1; i < *nranges; i++) { 197 + VFIO_ASSERT_LT(ranges[i].start, ranges[i].last); 198 + VFIO_ASSERT_LT(ranges[i - 1].last, ranges[i].start); 199 + } 200 + 201 + return ranges; 202 + } 203 + 204 + struct iova_allocator *iova_allocator_init(struct vfio_pci_device *device) 205 + { 206 + struct iova_allocator *allocator; 207 + struct iommu_iova_range *ranges; 208 + u32 nranges; 209 + 210 + ranges = vfio_pci_iova_ranges(device, &nranges); 211 + VFIO_ASSERT_NOT_NULL(ranges); 212 + 213 + allocator = malloc(sizeof(*allocator)); 214 + VFIO_ASSERT_NOT_NULL(allocator); 215 + 216 + *allocator = (struct iova_allocator){ 217 + .ranges = ranges, 218 + .nranges = nranges, 219 + .range_idx = 0, 220 + .range_offset = 0, 221 + }; 222 + 223 + return allocator; 224 + } 225 + 226 + void iova_allocator_cleanup(struct iova_allocator *allocator) 227 + { 228 + free(allocator->ranges); 229 + free(allocator); 230 + } 231 + 232 + iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size) 233 + { 234 + VFIO_ASSERT_GT(size, 0, "Invalid size arg, zero\n"); 235 + VFIO_ASSERT_EQ(size & (size - 1), 0, "Invalid size arg, non-power-of-2\n"); 236 + 237 + for (;;) { 238 + struct iommu_iova_range *range; 239 + iova_t iova, last; 240 + 241 + VFIO_ASSERT_LT(allocator->range_idx, allocator->nranges, 242 + "IOVA allocator out of space\n"); 243 + 244 + range = &allocator->ranges[allocator->range_idx]; 245 + iova = range->start + allocator->range_offset; 246 + 247 + /* Check for sufficient space at the current offset */ 248 + if (check_add_overflow(iova, size - 1, &last) || 249 + last > range->last) 250 + goto next_range; 251 + 252 + /* Align iova to size */ 253 + iova = last & ~(size - 1); 254 + 255 + /* Check for sufficient space at the aligned iova */ 256 + if (check_add_overflow(iova, size - 1, &last) || 257 + last > range->last) 258 + goto next_range; 259 + 260 + if (last == range->last) { 261 + allocator->range_idx++; 262 + allocator->range_offset = 0; 263 + } else { 264 + allocator->range_offset = last - range->start + 1; 265 + } 266 + 267 + return iova; 268 + 269 + next_range: 270 + allocator->range_idx++; 271 + allocator->range_offset = 0; 272 + } 273 + } 32 274 33 275 iova_t __to_iova(struct vfio_pci_device *device, void *vaddr) 34 276 {
+17 -3
tools/testing/selftests/vfio/vfio_dma_mapping_test.c
··· 3 3 #include <sys/mman.h> 4 4 #include <unistd.h> 5 5 6 + #include <uapi/linux/types.h> 7 + #include <linux/iommufd.h> 6 8 #include <linux/limits.h> 7 9 #include <linux/mman.h> 8 10 #include <linux/sizes.h> ··· 95 93 96 94 FIXTURE(vfio_dma_mapping_test) { 97 95 struct vfio_pci_device *device; 96 + struct iova_allocator *iova_allocator; 98 97 }; 99 98 100 99 FIXTURE_VARIANT(vfio_dma_mapping_test) { ··· 120 117 FIXTURE_SETUP(vfio_dma_mapping_test) 121 118 { 122 119 self->device = vfio_pci_device_init(device_bdf, variant->iommu_mode); 120 + self->iova_allocator = iova_allocator_init(self->device); 123 121 } 124 122 125 123 FIXTURE_TEARDOWN(vfio_dma_mapping_test) 126 124 { 125 + iova_allocator_cleanup(self->iova_allocator); 127 126 vfio_pci_device_cleanup(self->device); 128 127 } 129 128 ··· 147 142 else 148 143 ASSERT_NE(region.vaddr, MAP_FAILED); 149 144 150 - region.iova = (u64)region.vaddr; 145 + region.iova = iova_allocator_alloc(self->iova_allocator, size); 151 146 region.size = size; 152 147 153 148 vfio_pci_dma_map(self->device, &region); ··· 224 219 FIXTURE_SETUP(vfio_dma_map_limit_test) 225 220 { 226 221 struct vfio_dma_region *region = &self->region; 222 + struct iommu_iova_range *ranges; 227 223 u64 region_size = getpagesize(); 224 + iova_t last_iova; 225 + u32 nranges; 228 226 229 227 /* 230 228 * Over-allocate mmap by double the size to provide enough backing vaddr ··· 240 232 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 241 233 ASSERT_NE(region->vaddr, MAP_FAILED); 242 234 243 - /* One page prior to the end of address space */ 244 - region->iova = ~(iova_t)0 & ~(region_size - 1); 235 + ranges = vfio_pci_iova_ranges(self->device, &nranges); 236 + VFIO_ASSERT_NOT_NULL(ranges); 237 + last_iova = ranges[nranges - 1].last; 238 + free(ranges); 239 + 240 + /* One page prior to the last iova */ 241 + region->iova = last_iova & ~(region_size - 1); 245 242 region->size = region_size; 246 243 } 247 244 ··· 289 276 struct vfio_dma_region *region = &self->region; 290 277 int rc; 291 278 279 + region->iova = ~(iova_t)0 & ~(region->size - 1); 292 280 region->size = self->mmap_size; 293 281 294 282 rc = __vfio_pci_dma_map(self->device, region);
+8 -4
tools/testing/selftests/vfio/vfio_pci_driver_test.c
··· 19 19 } while (0) 20 20 21 21 static void region_setup(struct vfio_pci_device *device, 22 + struct iova_allocator *iova_allocator, 22 23 struct vfio_dma_region *region, u64 size) 23 24 { 24 25 const int flags = MAP_SHARED | MAP_ANONYMOUS; ··· 30 29 VFIO_ASSERT_NE(vaddr, MAP_FAILED); 31 30 32 31 region->vaddr = vaddr; 33 - region->iova = (u64)vaddr; 32 + region->iova = iova_allocator_alloc(iova_allocator, size); 34 33 region->size = size; 35 34 36 35 vfio_pci_dma_map(device, region); ··· 45 44 46 45 FIXTURE(vfio_pci_driver_test) { 47 46 struct vfio_pci_device *device; 47 + struct iova_allocator *iova_allocator; 48 48 struct vfio_dma_region memcpy_region; 49 49 void *vaddr; 50 50 int msi_fd; ··· 74 72 struct vfio_pci_driver *driver; 75 73 76 74 self->device = vfio_pci_device_init(device_bdf, variant->iommu_mode); 75 + self->iova_allocator = iova_allocator_init(self->device); 77 76 78 77 driver = &self->device->driver; 79 78 80 - region_setup(self->device, &self->memcpy_region, SZ_1G); 81 - region_setup(self->device, &driver->region, SZ_2M); 79 + region_setup(self->device, self->iova_allocator, &self->memcpy_region, SZ_1G); 80 + region_setup(self->device, self->iova_allocator, &driver->region, SZ_2M); 82 81 83 82 /* Any IOVA that doesn't overlap memcpy_region and driver->region. */ 84 - self->unmapped_iova = 8UL * SZ_1G; 83 + self->unmapped_iova = iova_allocator_alloc(self->iova_allocator, SZ_1G); 85 84 86 85 vfio_pci_driver_init(self->device); 87 86 self->msi_fd = self->device->msi_eventfds[driver->msi]; ··· 111 108 region_teardown(self->device, &self->memcpy_region); 112 109 region_teardown(self->device, &driver->region); 113 110 111 + iova_allocator_cleanup(self->iova_allocator); 114 112 vfio_pci_device_cleanup(self->device); 115 113 } 116 114