diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index dc7c6662a2c5..fe7b2c3d87cc 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -11,6 +11,36 @@ config IOASID config IOMMU_API bool +if IOMMU_IOVA + +config IOMMU_LIMIT_IOVA_ALIGNMENT + bool "Limit IOVA alignment" + help + When the IOVA framework applies IOVA alignment it aligns all + IOVAs to the smallest PAGE_SIZE order which is greater than or + equal to the requested IOVA size. This works fine for sizes up + to several MiB, but for larger sizes it results in address + space wastage and fragmentation. For example drivers with a 4 + GiB IOVA space might run out of IOVA space when allocating + buffers great than 64 MiB. + + Enable this option to impose a limit on the alignment of IOVAs. + + If unsure, say N. + +config IOMMU_IOVA_ALIGNMENT + int "Maximum PAGE_SIZE order of alignment for IOVAs" + depends on IOMMU_LIMIT_IOVA_ALIGNMENT + range 4 9 + default 9 + help + With this parameter you can specify the maximum PAGE_SIZE order for + IOVAs. Larger IOVAs will be aligned only to this specified order. + The order is expressed a power of two multiplied by the PAGE_SIZE. + + If unsure, leave the default value "9". +endif + menuconfig IOMMU_SUPPORT bool "IOMMU Hardware Support" depends on MMU diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 313d29477dc5..5844d4d4661b 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -178,6 +178,24 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova, rb_insert_color(&iova->node, root); } +#ifdef CONFIG_IOMMU_LIMIT_IOVA_ALIGNMENT +static unsigned long limit_align_shift(struct iova_domain *iovad, + unsigned long shift) +{ + unsigned long max_align_shift; + + max_align_shift = CONFIG_IOMMU_IOVA_ALIGNMENT + PAGE_SHIFT + - iova_shift(iovad); + return min_t(unsigned long, max_align_shift, shift); +} +#else +static unsigned long limit_align_shift(struct iova_domain *iovad, + unsigned long shift) +{ + return shift; +} +#endif + static int __alloc_and_insert_iova_range(struct iova_domain *iovad, unsigned long size, unsigned long limit_pfn, struct iova *new, bool size_aligned) @@ -189,7 +207,7 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, unsigned long align_mask = ~0UL; if (size_aligned) - align_mask <<= fls_long(size - 1); + align_mask <<= limit_align_shift(iovad, fls_long(size - 1)); /* Walk the tree backwards */ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); @@ -242,7 +260,7 @@ static int __alloc_and_insert_iova_best_fit(struct iova_domain *iovad, unsigned long gap, candidate_gap = ~0UL; if (size_aligned) - align_mask <<= limit_align(iovad, fls_long(size - 1)); + align_mask <<= limit_align_shift(iovad, fls_long(size - 1)); /* Walk the tree backwards */ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);