1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <dirent.h> 3 #include <fcntl.h> 4 #include <libgen.h> 5 #include <stdint.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 10 #include <sys/eventfd.h> 11 #include <sys/ioctl.h> 12 #include <sys/mman.h> 13 14 #include <uapi/linux/types.h> 15 #include <linux/iommufd.h> 16 #include <linux/limits.h> 17 #include <linux/mman.h> 18 #include <linux/overflow.h> 19 #include <linux/types.h> 20 #include <linux/vfio.h> 21 22 #include <libvfio.h> 23 24 struct iova_allocator *iova_allocator_init(struct iommu *iommu) 25 { 26 struct iova_allocator *allocator; 27 struct iommu_iova_range *ranges; 28 u32 nranges; 29 30 ranges = iommu_iova_ranges(iommu, &nranges); 31 VFIO_ASSERT_NOT_NULL(ranges); 32 33 allocator = malloc(sizeof(*allocator)); 34 VFIO_ASSERT_NOT_NULL(allocator); 35 36 *allocator = (struct iova_allocator){ 37 .ranges = ranges, 38 .nranges = nranges, 39 .range_idx = 0, 40 .range_offset = 0, 41 }; 42 43 return allocator; 44 } 45 46 void iova_allocator_cleanup(struct iova_allocator *allocator) 47 { 48 free(allocator->ranges); 49 free(allocator); 50 } 51 52 iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size) 53 { 54 VFIO_ASSERT_GT(size, 0, "Invalid size arg, zero\n"); 55 VFIO_ASSERT_EQ(size & (size - 1), 0, "Invalid size arg, non-power-of-2\n"); 56 57 for (;;) { 58 struct iommu_iova_range *range; 59 iova_t iova, last; 60 61 VFIO_ASSERT_LT(allocator->range_idx, allocator->nranges, 62 "IOVA allocator out of space\n"); 63 64 range = &allocator->ranges[allocator->range_idx]; 65 iova = range->start + allocator->range_offset; 66 67 /* Check for sufficient space at the current offset */ 68 if (check_add_overflow(iova, size - 1, &last) || 69 last > range->last) 70 goto next_range; 71 72 /* Align iova to size */ 73 iova = last & ~(size - 1); 74 75 /* Check for sufficient space at the aligned iova */ 76 if (check_add_overflow(iova, size - 1, &last) || 77 last > range->last) 78 goto next_range; 79 80 if (last == range->last) { 81 allocator->range_idx++; 82 allocator->range_offset = 0; 83 } else { 84 allocator->range_offset = last - range->start + 1; 85 } 86 87 return iova; 88 89 next_range: 90 allocator->range_idx++; 91 allocator->range_offset = 0; 92 } 93 } 94 95