FROMLIST: iommu/iova: Add a best-fit algorithm
Using the best-fit algorithm, instead of the first-fit algorithm, may reduce fragmentation when allocating IOVAs. Bug: 149544392 Link: https://lore.kernel.org/lkml/7239ddd532e94a4371289f3be23c66a3@codeaurora.org/ Change-Id: Icfbac0cc7be972a092915335508cbc73c47471cf Signed-off-by: Patrick Daly <pdaly@codeaurora.org> Signed-off-by: Isaac J. Manjarres <isaacm@codeaurora.org> Signed-off-by: Chris Goldsworthy <cgoldswo@codeaurora.org>
This commit is contained in:

committed by
Chris Goldsworthy

parent
0d5de782bb
commit
c640e76a4e
@@ -396,6 +396,24 @@ int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(iommu_dma_reserve_iova);
|
EXPORT_SYMBOL(iommu_dma_reserve_iova);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Should be called prior to using dma-apis.
|
||||||
|
*/
|
||||||
|
int iommu_dma_enable_best_fit_algo(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iommu_domain *domain;
|
||||||
|
struct iova_domain *iovad;
|
||||||
|
|
||||||
|
domain = iommu_get_domain_for_dev(dev);
|
||||||
|
if (!domain || !domain->iova_cookie)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
iovad = &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad;
|
||||||
|
iovad->best_fit = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(iommu_dma_enable_best_fit_algo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
|
* dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
|
||||||
* page flags.
|
* page flags.
|
||||||
|
@@ -50,6 +50,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
|
|||||||
iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
|
iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
|
||||||
rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
|
rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
|
||||||
rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
|
rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
|
||||||
|
iovad->best_fit = false;
|
||||||
init_iova_rcaches(iovad);
|
init_iova_rcaches(iovad);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(init_iova_domain);
|
EXPORT_SYMBOL_GPL(init_iova_domain);
|
||||||
@@ -227,6 +228,70 @@ iova32_full:
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __alloc_and_insert_iova_best_fit(struct iova_domain *iovad,
|
||||||
|
unsigned long size,
|
||||||
|
unsigned long limit_pfn,
|
||||||
|
struct iova *new, bool size_aligned)
|
||||||
|
{
|
||||||
|
struct rb_node *curr, *prev;
|
||||||
|
struct iova *curr_iova, *prev_iova;
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned long align_mask = ~0UL;
|
||||||
|
struct rb_node *candidate_rb_parent;
|
||||||
|
unsigned long new_pfn, candidate_pfn = ~0UL;
|
||||||
|
unsigned long gap, candidate_gap = ~0UL;
|
||||||
|
|
||||||
|
if (size_aligned)
|
||||||
|
align_mask <<= limit_align(iovad, fls_long(size - 1));
|
||||||
|
|
||||||
|
/* Walk the tree backwards */
|
||||||
|
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
||||||
|
curr = &iovad->anchor.node;
|
||||||
|
prev = rb_prev(curr);
|
||||||
|
for (; prev; curr = prev, prev = rb_prev(curr)) {
|
||||||
|
curr_iova = rb_entry(curr, struct iova, node);
|
||||||
|
prev_iova = rb_entry(prev, struct iova, node);
|
||||||
|
|
||||||
|
limit_pfn = min(limit_pfn, curr_iova->pfn_lo);
|
||||||
|
new_pfn = (limit_pfn - size) & align_mask;
|
||||||
|
gap = curr_iova->pfn_lo - prev_iova->pfn_hi - 1;
|
||||||
|
if ((limit_pfn >= size) && (new_pfn > prev_iova->pfn_hi)
|
||||||
|
&& (gap < candidate_gap)) {
|
||||||
|
candidate_gap = gap;
|
||||||
|
candidate_pfn = new_pfn;
|
||||||
|
candidate_rb_parent = curr;
|
||||||
|
if (gap == size)
|
||||||
|
goto insert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curr_iova = rb_entry(curr, struct iova, node);
|
||||||
|
limit_pfn = min(limit_pfn, curr_iova->pfn_lo);
|
||||||
|
new_pfn = (limit_pfn - size) & align_mask;
|
||||||
|
gap = curr_iova->pfn_lo - iovad->start_pfn;
|
||||||
|
if (limit_pfn >= size && new_pfn >= iovad->start_pfn &&
|
||||||
|
gap < candidate_gap) {
|
||||||
|
candidate_gap = gap;
|
||||||
|
candidate_pfn = new_pfn;
|
||||||
|
candidate_rb_parent = curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert:
|
||||||
|
if (candidate_pfn == ~0UL) {
|
||||||
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pfn_lo will point to size aligned address if size_aligned is set */
|
||||||
|
new->pfn_lo = candidate_pfn;
|
||||||
|
new->pfn_hi = new->pfn_lo + size - 1;
|
||||||
|
|
||||||
|
/* If we have 'prev', it's a valid place to start the insertion. */
|
||||||
|
iova_insert_rbtree(&iovad->rbroot, new, candidate_rb_parent);
|
||||||
|
spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct kmem_cache *iova_cache;
|
static struct kmem_cache *iova_cache;
|
||||||
static unsigned int iova_cache_users;
|
static unsigned int iova_cache_users;
|
||||||
static DEFINE_MUTEX(iova_cache_mutex);
|
static DEFINE_MUTEX(iova_cache_mutex);
|
||||||
@@ -302,8 +367,13 @@ alloc_iova(struct iova_domain *iovad, unsigned long size,
|
|||||||
if (!new_iova)
|
if (!new_iova)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
|
if (iovad->best_fit) {
|
||||||
new_iova, size_aligned);
|
ret = __alloc_and_insert_iova_best_fit(iovad, size,
|
||||||
|
limit_pfn + 1, new_iova, size_aligned);
|
||||||
|
} else {
|
||||||
|
ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
|
||||||
|
new_iova, size_aligned);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
free_iova_mem(new_iova);
|
free_iova_mem(new_iova);
|
||||||
|
@@ -40,6 +40,8 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
|
|||||||
int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
|
int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
|
||||||
u64 size);
|
u64 size);
|
||||||
|
|
||||||
|
int iommu_dma_enable_best_fit_algo(struct device *dev);
|
||||||
|
|
||||||
#else /* CONFIG_IOMMU_DMA */
|
#else /* CONFIG_IOMMU_DMA */
|
||||||
|
|
||||||
struct iommu_domain;
|
struct iommu_domain;
|
||||||
@@ -87,5 +89,10 @@ static inline int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int iommu_dma_enable_best_fit_algo(struct device *dev)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_IOMMU_DMA */
|
#endif /* CONFIG_IOMMU_DMA */
|
||||||
#endif /* __DMA_IOMMU_H */
|
#endif /* __DMA_IOMMU_H */
|
||||||
|
@@ -95,6 +95,7 @@ struct iova_domain {
|
|||||||
flush-queues */
|
flush-queues */
|
||||||
atomic_t fq_timer_on; /* 1 when timer is active, 0
|
atomic_t fq_timer_on; /* 1 when timer is active, 0
|
||||||
when not */
|
when not */
|
||||||
|
bool best_fit;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline unsigned long iova_size(struct iova *iova)
|
static inline unsigned long iova_size(struct iova *iova)
|
||||||
|
Reference in New Issue
Block a user