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