FROMLIST: iommu/iova: Support limiting IOVA alignment

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.

We support use cases that requires large buffers (> 64 MB in
size) to be allocated and mapped in their stage 1 page tables.
However, with this alignment scheme we find ourselves running
out of IOVA space for 32 bit devices, so we are proposing this
config, along the similar vein as CONFIG_CMA_ALIGNMENT for CMA
allocations.

Add CONFIG_IOMMU_LIMIT_IOVA_ALIGNMENT to limit the alignment of
IOVAs to some desired PAGE_SIZE order, specified by
CONFIG_IOMMU_IOVA_ALIGNMENT. This helps reduce the impact of
fragmentation caused by the current IOVA alignment scheme, and
gives better IOVA space utilization.

Bug: 148141615
Link: https://lore.kernel.org/lkml/alpine.DEB.2.10.2002141223510.27047@lmark-linux.qualcomm.com/
Change-Id: I511ac685d5855e1b9feb5025e025ebbebee7f40d
Signed-off-by: Liam Mark <lmark@codeaurora.org>
Signed-off-by: Chris Goldsworthy <cgoldswo@codeaurora.org>
This commit is contained in:
Liam Mark 2020-02-26 14:23:41 -08:00 committed by Chris Goldsworthy
parent c640e76a4e
commit ee98c0848c
2 changed files with 50 additions and 2 deletions

View File

@ -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

View File

@ -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);