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(sizeof(*allocator)); 33 VFIO_ASSERT_NOT_NULL(allocator); 34 35 *allocator = (struct iova_allocator){ 36 .ranges = ranges, 37 .nranges = nranges, 38 .range_idx = 0, 39 .range_offset = 0, 40 }; 41 42 return allocator; 43 } 44 45 void iova_allocator_cleanup(struct iova_allocator *allocator) 46 { 47 free(allocator->ranges); 48 free(allocator); 49 } 50 51 iova_t iova_allocator_alloc(struct iova_allocator *allocator, size_t size) 52 { 53 VFIO_ASSERT_GT(size, 0, "Invalid size arg, zero\n"); 54 VFIO_ASSERT_EQ(size & (size - 1), 0, "Invalid size arg, non-power-of-2\n"); 55 56 for (;;) { 57 struct iommu_iova_range *range; 58 iova_t iova, last; 59 60 VFIO_ASSERT_LT(allocator->range_idx, allocator->nranges, 61 "IOVA allocator out of space\n"); 62 63 range = &allocator->ranges[allocator->range_idx]; 64 iova = range->start + allocator->range_offset; 65 66 /* Check for sufficient space at the current offset */ 67 if (check_add_overflow(iova, size - 1, &last) || 68 last > range->last) 69 goto next_range; 70 71 /* Align iova to size */ 72 iova = last & ~(size - 1); 73 74 /* Check for sufficient space at the aligned iova */ 75 if (check_add_overflow(iova, size - 1, &last) || 76 last > range->last) 77 goto next_range; 78 79 if (last == range->last) { 80 allocator->range_idx++; 81 allocator->range_offset = 0; 82 } else { 83 allocator->range_offset = last - range->start + 1; 84 } 85 86 return iova; 87 88 next_range: 89 allocator->range_idx++; 90 allocator->range_offset = 0; 91 } 92 } 93 94