lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <0c58625b-f26c-a6d0-876e-c20d10ed708e@huawei.com>
Date:   Mon, 15 Aug 2022 12:04:35 +0100
From:   John Garry <john.garry@...wei.com>
To:     <robin.murphy@....com>, <joro@...tes.org>, <will@...nel.org>
CC:     <iommu@...ts.linux.dev>, <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v2] iova: Re-order code to remove forward declarations

On 26/07/2022 16:26, John Garry wrote:

Hi Robin,

Any opinion on this patch?

Thanks,
John

> Now that the FQ code has been moved to dma-iommu.c and also the rcache-
> related structures have been brought into iova.c, let's re-order the code
> to remove all the forward declarations.
> 
> The general order will be as follows:
> - RB tree code
> - iova management
> - magazine helpers
> - rcache code and "fast" APIs
> - iova domain public APIs
> 
> Re-order prototypes in iova.h to follow the same general group ordering.
> 
> A couple of pre-existing checkpatch warnings are also remedied:
> 
> A suspect indentation is also corrected:
> WARNING: suspect code indent for conditional statements (16, 32)
>   #374: FILE: drivers/iommu/iova.c:194:
> +               } else if (overlap)
> +                               break;
> 
> WARNING: Block comments should align the * on each line
>   #1038: FILE: drivers/iommu/iova.c:787:
> + * fails too and the flush_rcache flag is set then the rcache will be flushed.
> +*/
> 
> No functional change intended.
> 
> Signed-off-by: John Garry <john.garry@...wei.com>
> ---
> Differences to v1:
> - tweak ordering a bit
> - rebase
> 
> diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
> index e44f565c5319..ddac9dcf97c6 100644
> --- a/drivers/iommu/iova.c
> +++ b/drivers/iommu/iova.c
> @@ -17,75 +17,45 @@
>   
>   #define IOVA_RANGE_CACHE_MAX_SIZE 6	/* log of max cached IOVA range size (in pages) */
>   
> -static bool iova_rcache_insert(struct iova_domain *iovad,
> -			       unsigned long pfn,
> -			       unsigned long size);
> -static unsigned long iova_rcache_get(struct iova_domain *iovad,
> -				     unsigned long size,
> -				     unsigned long limit_pfn);
> -static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
> -static void free_iova_rcaches(struct iova_domain *iovad);
> +/*
> + * Magazine caches for IOVA ranges.  For an introduction to magazines,
> + * see the USENIX 2001 paper "Magazines and Vmem: Extending the Slab
> + * Allocator to Many CPUs and Arbitrary Resources" by Bonwick and Adams.
> + * For simplicity, we use a static magazine size and don't implement the
> + * dynamic size tuning described in the paper.
> + */
>   
> -static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
> -{
> -	struct iova_domain *iovad;
> +/*
> + * As kmalloc's buffer size is fixed to power of 2, 127 is chosen to
> + * assure size of 'iova_magazine' to be 1024 bytes, so that no memory
> + * will be wasted.
> + */
> +#define IOVA_MAG_SIZE 127
> +#define MAX_GLOBAL_MAGS 32	/* magazines per bin */
>   
> -	iovad = hlist_entry_safe(node, struct iova_domain, cpuhp_dead);
> +struct iova_magazine {
> +	unsigned long size;
> +	unsigned long pfns[IOVA_MAG_SIZE];
> +};
>   
> -	free_cpu_cached_iovas(cpu, iovad);
> -	return 0;
> -}
> +struct iova_cpu_rcache {
> +	spinlock_t lock;
> +	struct iova_magazine *loaded;
> +	struct iova_magazine *prev;
> +};
>   
> -static void free_global_cached_iovas(struct iova_domain *iovad);
> +struct iova_rcache {
> +	spinlock_t lock;
> +	unsigned long depot_size;
> +	struct iova_magazine *depot[MAX_GLOBAL_MAGS];
> +	struct iova_cpu_rcache __percpu *cpu_rcaches;
> +};
>   
>   static struct iova *to_iova(struct rb_node *node)
>   {
>   	return rb_entry(node, struct iova, node);
>   }
>   
> -void
> -init_iova_domain(struct iova_domain *iovad, unsigned long granule,
> -	unsigned long start_pfn)
> -{
> -	/*
> -	 * IOVA granularity will normally be equal to the smallest
> -	 * supported IOMMU page size; both *must* be capable of
> -	 * representing individual CPU pages exactly.
> -	 */
> -	BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
> -
> -	spin_lock_init(&iovad->iova_rbtree_lock);
> -	iovad->rbroot = RB_ROOT;
> -	iovad->cached_node = &iovad->anchor.node;
> -	iovad->cached32_node = &iovad->anchor.node;
> -	iovad->granule = granule;
> -	iovad->start_pfn = start_pfn;
> -	iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad));
> -	iovad->max32_alloc_size = iovad->dma_32bit_pfn;
> -	iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
> -	rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
> -	rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
> -}
> -EXPORT_SYMBOL_GPL(init_iova_domain);
> -
> -static struct rb_node *
> -__get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn)
> -{
> -	if (limit_pfn <= iovad->dma_32bit_pfn)
> -		return iovad->cached32_node;
> -
> -	return iovad->cached_node;
> -}
> -
> -static void
> -__cached_rbnode_insert_update(struct iova_domain *iovad, struct iova *new)
> -{
> -	if (new->pfn_hi < iovad->dma_32bit_pfn)
> -		iovad->cached32_node = &new->node;
> -	else
> -		iovad->cached_node = &new->node;
> -}
> -
>   static void
>   __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
>   {
> @@ -105,43 +75,6 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
>   		iovad->cached_node = rb_next(&free->node);
>   }
>   
> -static struct rb_node *iova_find_limit(struct iova_domain *iovad, unsigned long limit_pfn)
> -{
> -	struct rb_node *node, *next;
> -	/*
> -	 * Ideally what we'd like to judge here is whether limit_pfn is close
> -	 * enough to the highest-allocated IOVA that starting the allocation
> -	 * walk from the anchor node will be quicker than this initial work to
> -	 * find an exact starting point (especially if that ends up being the
> -	 * anchor node anyway). This is an incredibly crude approximation which
> -	 * only really helps the most likely case, but is at least trivially easy.
> -	 */
> -	if (limit_pfn > iovad->dma_32bit_pfn)
> -		return &iovad->anchor.node;
> -
> -	node = iovad->rbroot.rb_node;
> -	while (to_iova(node)->pfn_hi < limit_pfn)
> -		node = node->rb_right;
> -
> -search_left:
> -	while (node->rb_left && to_iova(node->rb_left)->pfn_lo >= limit_pfn)
> -		node = node->rb_left;
> -
> -	if (!node->rb_left)
> -		return node;
> -
> -	next = node->rb_left;
> -	while (next->rb_right) {
> -		next = next->rb_right;
> -		if (to_iova(next)->pfn_lo >= limit_pfn) {
> -			node = next;
> -			goto search_left;
> -		}
> -	}
> -
> -	return node;
> -}
> -
>   /* Insert the iova into domain rbtree by holding writer lock */
>   static void
>   iova_insert_rbtree(struct rb_root *root, struct iova *iova,
> @@ -170,65 +103,15 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova,
>   	rb_insert_color(&iova->node, root);
>   }
>   
> -static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
> -		unsigned long size, unsigned long limit_pfn,
> -			struct iova *new, bool size_aligned)
> +static int
> +__is_range_overlap(struct rb_node *node,
> +	unsigned long pfn_lo, unsigned long pfn_hi)
>   {
> -	struct rb_node *curr, *prev;
> -	struct iova *curr_iova;
> -	unsigned long flags;
> -	unsigned long new_pfn, retry_pfn;
> -	unsigned long align_mask = ~0UL;
> -	unsigned long high_pfn = limit_pfn, low_pfn = iovad->start_pfn;
> -
> -	if (size_aligned)
> -		align_mask <<= fls_long(size - 1);
> -
> -	/* Walk the tree backwards */
> -	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
> -	if (limit_pfn <= iovad->dma_32bit_pfn &&
> -			size >= iovad->max32_alloc_size)
> -		goto iova32_full;
> -
> -	curr = __get_cached_rbnode(iovad, limit_pfn);
> -	curr_iova = to_iova(curr);
> -	retry_pfn = curr_iova->pfn_hi + 1;
> -
> -retry:
> -	do {
> -		high_pfn = min(high_pfn, curr_iova->pfn_lo);
> -		new_pfn = (high_pfn - size) & align_mask;
> -		prev = curr;
> -		curr = rb_prev(curr);
> -		curr_iova = to_iova(curr);
> -	} while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn);
> -
> -	if (high_pfn < size || new_pfn < low_pfn) {
> -		if (low_pfn == iovad->start_pfn && retry_pfn < limit_pfn) {
> -			high_pfn = limit_pfn;
> -			low_pfn = retry_pfn;
> -			curr = iova_find_limit(iovad, limit_pfn);
> -			curr_iova = to_iova(curr);
> -			goto retry;
> -		}
> -		iovad->max32_alloc_size = size;
> -		goto iova32_full;
> -	}
> -
> -	/* pfn_lo will point to size aligned address if size_aligned is set */
> -	new->pfn_lo = new_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, prev);
> -	__cached_rbnode_insert_update(iovad, new);
> +	struct iova *iova = to_iova(node);
>   
> -	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
> +	if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
> +		return 1;
>   	return 0;
> -
> -iova32_full:
> -	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
> -	return -ENOMEM;
>   }
>   
>   static struct kmem_cache *iova_cache;
> @@ -246,88 +129,87 @@ static void free_iova_mem(struct iova *iova)
>   		kmem_cache_free(iova_cache, iova);
>   }
>   
> -int iova_cache_get(void)
> +static inline struct iova *
> +alloc_and_init_iova(unsigned long pfn_lo, unsigned long pfn_hi)
>   {
> -	mutex_lock(&iova_cache_mutex);
> -	if (!iova_cache_users) {
> -		int ret;
> -
> -		ret = cpuhp_setup_state_multi(CPUHP_IOMMU_IOVA_DEAD, "iommu/iova:dead", NULL,
> -					iova_cpuhp_dead);
> -		if (ret) {
> -			mutex_unlock(&iova_cache_mutex);
> -			pr_err("Couldn't register cpuhp handler\n");
> -			return ret;
> -		}
> +	struct iova *iova;
>   
> -		iova_cache = kmem_cache_create(
> -			"iommu_iova", sizeof(struct iova), 0,
> -			SLAB_HWCACHE_ALIGN, NULL);
> -		if (!iova_cache) {
> -			cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
> -			mutex_unlock(&iova_cache_mutex);
> -			pr_err("Couldn't create iova cache\n");
> -			return -ENOMEM;
> -		}
> +	iova = alloc_iova_mem();
> +	if (iova) {
> +		iova->pfn_lo = pfn_lo;
> +		iova->pfn_hi = pfn_hi;
>   	}
>   
> -	iova_cache_users++;
> -	mutex_unlock(&iova_cache_mutex);
> +	return iova;
> +}
>   
> -	return 0;
> +static struct iova *
> +__insert_new_range(struct iova_domain *iovad,
> +	unsigned long pfn_lo, unsigned long pfn_hi)
> +{
> +	struct iova *iova;
> +
> +	iova = alloc_and_init_iova(pfn_lo, pfn_hi);
> +	if (iova)
> +		iova_insert_rbtree(&iovad->rbroot, iova, NULL);
> +
> +	return iova;
>   }
> -EXPORT_SYMBOL_GPL(iova_cache_get);
>   
> -void iova_cache_put(void)
> +static void
> +__adjust_overlap_range(struct iova *iova,
> +	unsigned long *pfn_lo, unsigned long *pfn_hi)
>   {
> -	mutex_lock(&iova_cache_mutex);
> -	if (WARN_ON(!iova_cache_users)) {
> -		mutex_unlock(&iova_cache_mutex);
> -		return;
> -	}
> -	iova_cache_users--;
> -	if (!iova_cache_users) {
> -		cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
> -		kmem_cache_destroy(iova_cache);
> -	}
> -	mutex_unlock(&iova_cache_mutex);
> +	if (*pfn_lo < iova->pfn_lo)
> +		iova->pfn_lo = *pfn_lo;
> +	if (*pfn_hi > iova->pfn_hi)
> +		*pfn_lo = iova->pfn_hi + 1;
>   }
> -EXPORT_SYMBOL_GPL(iova_cache_put);
>   
>   /**
> - * alloc_iova - allocates an iova
> - * @iovad: - iova domain in question
> - * @size: - size of page frames to allocate
> - * @limit_pfn: - max limit address
> - * @size_aligned: - set if size_aligned address range is required
> - * This function allocates an iova in the range iovad->start_pfn to limit_pfn,
> - * searching top-down from limit_pfn to iovad->start_pfn. If the size_aligned
> - * flag is set then the allocated address iova->pfn_lo will be naturally
> - * aligned on roundup_power_of_two(size).
> - */
> -struct iova *
> -alloc_iova(struct iova_domain *iovad, unsigned long size,
> -	unsigned long limit_pfn,
> -	bool size_aligned)
> + * reserve_iova - reserves an iova in the given range
> + * @iovad: - iova domain pointer
> + * @pfn_lo: - lower page frame address
> + * @pfn_hi:- higher pfn adderss
> + * This function allocates reserves the address range from pfn_lo to pfn_hi so
> + * that this address is not dished out as part of alloc_iova.
> + */
> +struct iova *
> +reserve_iova(struct iova_domain *iovad,
> +	unsigned long pfn_lo, unsigned long pfn_hi)
>   {
> -	struct iova *new_iova;
> -	int ret;
> +	struct rb_node *node;
> +	unsigned long flags;
> +	struct iova *iova;
> +	unsigned int overlap = 0;
>   
> -	new_iova = alloc_iova_mem();
> -	if (!new_iova)
> +	/* Don't allow nonsensical pfns */
> +	if (WARN_ON((pfn_hi | pfn_lo) > (ULLONG_MAX >> iova_shift(iovad))))
>   		return NULL;
>   
> -	ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
> -			new_iova, size_aligned);
> -
> -	if (ret) {
> -		free_iova_mem(new_iova);
> -		return NULL;
> +	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
> +	for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
> +		if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
> +			iova = to_iova(node);
> +			__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
> +			if ((pfn_lo >= iova->pfn_lo) &&
> +				(pfn_hi <= iova->pfn_hi))
> +				goto finish;
> +			overlap = 1;
> +		} else if (overlap)
> +			break;
>   	}
>   
> -	return new_iova;
> +	/* We are here either because this is the first reserver node
> +	 * or need to insert remaining non overlap addr range
> +	 */
> +	iova = __insert_new_range(iovad, pfn_lo, pfn_hi);
> +finish:
> +
> +	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
> +	return iova;
>   }
> -EXPORT_SYMBOL_GPL(alloc_iova);
> +EXPORT_SYMBOL_GPL(reserve_iova);
>   
>   static struct iova *
>   private_find_iova(struct iova_domain *iovad, unsigned long pfn)
> @@ -357,6 +239,122 @@ static void remove_iova(struct iova_domain *iovad, struct iova *iova)
>   	rb_erase(&iova->node, &iovad->rbroot);
>   }
>   
> +static struct rb_node *
> +__get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn)
> +{
> +	if (limit_pfn <= iovad->dma_32bit_pfn)
> +		return iovad->cached32_node;
> +
> +	return iovad->cached_node;
> +}
> +
> +static void
> +__cached_rbnode_insert_update(struct iova_domain *iovad, struct iova *new)
> +{
> +	if (new->pfn_hi < iovad->dma_32bit_pfn)
> +		iovad->cached32_node = &new->node;
> +	else
> +		iovad->cached_node = &new->node;
> +}
> +
> +static struct rb_node *iova_find_limit(struct iova_domain *iovad, unsigned long limit_pfn)
> +{
> +	struct rb_node *node, *next;
> +	/*
> +	 * Ideally what we'd like to judge here is whether limit_pfn is close
> +	 * enough to the highest-allocated IOVA that starting the allocation
> +	 * walk from the anchor node will be quicker than this initial work to
> +	 * find an exact starting point (especially if that ends up being the
> +	 * anchor node anyway). This is an incredibly crude approximation which
> +	 * only really helps the most likely case, but is at least trivially easy.
> +	 */
> +	if (limit_pfn > iovad->dma_32bit_pfn)
> +		return &iovad->anchor.node;
> +
> +	node = iovad->rbroot.rb_node;
> +	while (to_iova(node)->pfn_hi < limit_pfn)
> +		node = node->rb_right;
> +
> +search_left:
> +	while (node->rb_left && to_iova(node->rb_left)->pfn_lo >= limit_pfn)
> +		node = node->rb_left;
> +
> +	if (!node->rb_left)
> +		return node;
> +
> +	next = node->rb_left;
> +	while (next->rb_right) {
> +		next = next->rb_right;
> +		if (to_iova(next)->pfn_lo >= limit_pfn) {
> +			node = next;
> +			goto search_left;
> +		}
> +	}
> +
> +	return node;
> +}
> +
> +static int __alloc_and_insert_iova_range(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;
> +	unsigned long flags;
> +	unsigned long new_pfn, retry_pfn;
> +	unsigned long align_mask = ~0UL;
> +	unsigned long high_pfn = limit_pfn, low_pfn = iovad->start_pfn;
> +
> +	if (size_aligned)
> +		align_mask <<= fls_long(size - 1);
> +
> +	/* Walk the tree backwards */
> +	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
> +	if (limit_pfn <= iovad->dma_32bit_pfn &&
> +			size >= iovad->max32_alloc_size)
> +		goto iova32_full;
> +
> +	curr = __get_cached_rbnode(iovad, limit_pfn);
> +	curr_iova = to_iova(curr);
> +	retry_pfn = curr_iova->pfn_hi + 1;
> +
> +retry:
> +	do {
> +		high_pfn = min(high_pfn, curr_iova->pfn_lo);
> +		new_pfn = (high_pfn - size) & align_mask;
> +		prev = curr;
> +		curr = rb_prev(curr);
> +		curr_iova = to_iova(curr);
> +	} while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn);
> +
> +	if (high_pfn < size || new_pfn < low_pfn) {
> +		if (low_pfn == iovad->start_pfn && retry_pfn < limit_pfn) {
> +			high_pfn = limit_pfn;
> +			low_pfn = retry_pfn;
> +			curr = iova_find_limit(iovad, limit_pfn);
> +			curr_iova = to_iova(curr);
> +			goto retry;
> +		}
> +		iovad->max32_alloc_size = size;
> +		goto iova32_full;
> +	}
> +
> +	/* pfn_lo will point to size aligned address if size_aligned is set */
> +	new->pfn_lo = new_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, prev);
> +	__cached_rbnode_insert_update(iovad, new);
> +
> +	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
> +	return 0;
> +
> +iova32_full:
> +	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
> +	return -ENOMEM;
> +}
> +
>   /**
>    * find_iova - finds an iova for a given pfn
>    * @iovad: - iova domain in question.
> @@ -377,6 +375,41 @@ struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn)
>   }
>   EXPORT_SYMBOL_GPL(find_iova);
>   
> +/**
> + * alloc_iova - allocates an iova
> + * @iovad: - iova domain in question
> + * @size: - size of page frames to allocate
> + * @limit_pfn: - max limit address
> + * @size_aligned: - set if size_aligned address range is required
> + * This function allocates an iova in the range iovad->start_pfn to limit_pfn,
> + * searching top-down from limit_pfn to iovad->start_pfn. If the size_aligned
> + * flag is set then the allocated address iova->pfn_lo will be naturally
> + * aligned on roundup_power_of_two(size).
> + */
> +struct iova *
> +alloc_iova(struct iova_domain *iovad, unsigned long size,
> +	unsigned long limit_pfn,
> +	bool size_aligned)
> +{
> +	struct iova *new_iova;
> +	int ret;
> +
> +	new_iova = alloc_iova_mem();
> +	if (!new_iova)
> +		return NULL;
> +
> +	ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
> +			new_iova, size_aligned);
> +
> +	if (ret) {
> +		free_iova_mem(new_iova);
> +		return NULL;
> +	}
> +
> +	return new_iova;
> +}
> +EXPORT_SYMBOL_GPL(alloc_iova);
> +
>   /**
>    * __free_iova - frees the given iova
>    * @iovad: iova domain in question.
> @@ -420,234 +453,51 @@ free_iova(struct iova_domain *iovad, unsigned long pfn)
>   }
>   EXPORT_SYMBOL_GPL(free_iova);
>   
> -/**
> - * alloc_iova_fast - allocates an iova from rcache
> - * @iovad: - iova domain in question
> - * @size: - size of page frames to allocate
> - * @limit_pfn: - max limit address
> - * @flush_rcache: - set to flush rcache on regular allocation failure
> - * This function tries to satisfy an iova allocation from the rcache,
> - * and falls back to regular allocation on failure. If regular allocation
> - * fails too and the flush_rcache flag is set then the rcache will be flushed.
> -*/
> -unsigned long
> -alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
> -		unsigned long limit_pfn, bool flush_rcache)
> +static bool iova_magazine_full(struct iova_magazine *mag)
>   {
> -	unsigned long iova_pfn;
> -	struct iova *new_iova;
> -
> -	/*
> -	 * Freeing non-power-of-two-sized allocations back into the IOVA caches
> -	 * will come back to bite us badly, so we have to waste a bit of space
> -	 * rounding up anything cacheable to make sure that can't happen. The
> -	 * order of the unadjusted size will still match upon freeing.
> -	 */
> -	if (size < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
> -		size = roundup_pow_of_two(size);
> -
> -	iova_pfn = iova_rcache_get(iovad, size, limit_pfn + 1);
> -	if (iova_pfn)
> -		return iova_pfn;
> -
> -retry:
> -	new_iova = alloc_iova(iovad, size, limit_pfn, true);
> -	if (!new_iova) {
> -		unsigned int cpu;
> +	return (mag && mag->size == IOVA_MAG_SIZE);
> +}
>   
> -		if (!flush_rcache)
> -			return 0;
> +static void iova_magazine_push(struct iova_magazine *mag, unsigned long pfn)
> +{
> +	BUG_ON(iova_magazine_full(mag));
>   
> -		/* Try replenishing IOVAs by flushing rcache. */
> -		flush_rcache = false;
> -		for_each_online_cpu(cpu)
> -			free_cpu_cached_iovas(cpu, iovad);
> -		free_global_cached_iovas(iovad);
> -		goto retry;
> -	}
> +	mag->pfns[mag->size++] = pfn;
> +}
>   
> -	return new_iova->pfn_lo;
> +static struct iova_magazine *iova_magazine_alloc(gfp_t flags)
> +{
> +	return kzalloc(sizeof(struct iova_magazine), flags);
>   }
> -EXPORT_SYMBOL_GPL(alloc_iova_fast);
>   
> -/**
> - * free_iova_fast - free iova pfn range into rcache
> - * @iovad: - iova domain in question.
> - * @pfn: - pfn that is allocated previously
> - * @size: - # of pages in range
> - * This functions frees an iova range by trying to put it into the rcache,
> - * falling back to regular iova deallocation via free_iova() if this fails.
> - */
> -void
> -free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
> -{
> -	if (iova_rcache_insert(iovad, pfn, size))
> -		return;
> -
> -	free_iova(iovad, pfn);
> -}
> -EXPORT_SYMBOL_GPL(free_iova_fast);
> -
> -static void iova_domain_free_rcaches(struct iova_domain *iovad)
> -{
> -	cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
> -					    &iovad->cpuhp_dead);
> -	free_iova_rcaches(iovad);
> -}
> -
> -/**
> - * put_iova_domain - destroys the iova domain
> - * @iovad: - iova domain in question.
> - * All the iova's in that domain are destroyed.
> - */
> -void put_iova_domain(struct iova_domain *iovad)
> -{
> -	struct iova *iova, *tmp;
> -
> -	if (iovad->rcaches)
> -		iova_domain_free_rcaches(iovad);
> -
> -	rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node)
> -		free_iova_mem(iova);
> -}
> -EXPORT_SYMBOL_GPL(put_iova_domain);
> -
> -static int
> -__is_range_overlap(struct rb_node *node,
> -	unsigned long pfn_lo, unsigned long pfn_hi)
> -{
> -	struct iova *iova = to_iova(node);
> -
> -	if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
> -		return 1;
> -	return 0;
> -}
> -
> -static inline struct iova *
> -alloc_and_init_iova(unsigned long pfn_lo, unsigned long pfn_hi)
> -{
> -	struct iova *iova;
> -
> -	iova = alloc_iova_mem();
> -	if (iova) {
> -		iova->pfn_lo = pfn_lo;
> -		iova->pfn_hi = pfn_hi;
> -	}
> -
> -	return iova;
> -}
> -
> -static struct iova *
> -__insert_new_range(struct iova_domain *iovad,
> -	unsigned long pfn_lo, unsigned long pfn_hi)
> +static void iova_magazine_free(struct iova_magazine *mag)
>   {
> -	struct iova *iova;
> -
> -	iova = alloc_and_init_iova(pfn_lo, pfn_hi);
> -	if (iova)
> -		iova_insert_rbtree(&iovad->rbroot, iova, NULL);
> -
> -	return iova;
> +	kfree(mag);
>   }
>   
> -static void
> -__adjust_overlap_range(struct iova *iova,
> -	unsigned long *pfn_lo, unsigned long *pfn_hi)
> +static bool iova_magazine_empty(struct iova_magazine *mag)
>   {
> -	if (*pfn_lo < iova->pfn_lo)
> -		iova->pfn_lo = *pfn_lo;
> -	if (*pfn_hi > iova->pfn_hi)
> -		*pfn_lo = iova->pfn_hi + 1;
> +	return (!mag || mag->size == 0);
>   }
>   
> -/**
> - * reserve_iova - reserves an iova in the given range
> - * @iovad: - iova domain pointer
> - * @pfn_lo: - lower page frame address
> - * @pfn_hi:- higher pfn adderss
> - * This function allocates reserves the address range from pfn_lo to pfn_hi so
> - * that this address is not dished out as part of alloc_iova.
> - */
> -struct iova *
> -reserve_iova(struct iova_domain *iovad,
> -	unsigned long pfn_lo, unsigned long pfn_hi)
> +static unsigned long iova_magazine_pop(struct iova_magazine *mag,
> +				       unsigned long limit_pfn)
>   {
> -	struct rb_node *node;
> -	unsigned long flags;
> -	struct iova *iova;
> -	unsigned int overlap = 0;
> -
> -	/* Don't allow nonsensical pfns */
> -	if (WARN_ON((pfn_hi | pfn_lo) > (ULLONG_MAX >> iova_shift(iovad))))
> -		return NULL;
> -
> -	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
> -	for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
> -		if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
> -			iova = to_iova(node);
> -			__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
> -			if ((pfn_lo >= iova->pfn_lo) &&
> -				(pfn_hi <= iova->pfn_hi))
> -				goto finish;
> -			overlap = 1;
> -
> -		} else if (overlap)
> -				break;
> -	}
> -
> -	/* We are here either because this is the first reserver node
> -	 * or need to insert remaining non overlap addr range
> -	 */
> -	iova = __insert_new_range(iovad, pfn_lo, pfn_hi);
> -finish:
> -
> -	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
> -	return iova;
> -}
> -EXPORT_SYMBOL_GPL(reserve_iova);
> -
> -/*
> - * Magazine caches for IOVA ranges.  For an introduction to magazines,
> - * see the USENIX 2001 paper "Magazines and Vmem: Extending the Slab
> - * Allocator to Many CPUs and Arbitrary Resources" by Bonwick and Adams.
> - * For simplicity, we use a static magazine size and don't implement the
> - * dynamic size tuning described in the paper.
> - */
> -
> -/*
> - * As kmalloc's buffer size is fixed to power of 2, 127 is chosen to
> - * assure size of 'iova_magazine' to be 1024 bytes, so that no memory
> - * will be wasted.
> - */
> -#define IOVA_MAG_SIZE 127
> -#define MAX_GLOBAL_MAGS 32	/* magazines per bin */
> -
> -struct iova_magazine {
> -	unsigned long size;
> -	unsigned long pfns[IOVA_MAG_SIZE];
> -};
> +	int i;
> +	unsigned long pfn;
>   
> -struct iova_cpu_rcache {
> -	spinlock_t lock;
> -	struct iova_magazine *loaded;
> -	struct iova_magazine *prev;
> -};
> +	BUG_ON(iova_magazine_empty(mag));
>   
> -struct iova_rcache {
> -	spinlock_t lock;
> -	unsigned long depot_size;
> -	struct iova_magazine *depot[MAX_GLOBAL_MAGS];
> -	struct iova_cpu_rcache __percpu *cpu_rcaches;
> -};
> +	/* Only fall back to the rbtree if we have no suitable pfns at all */
> +	for (i = mag->size - 1; mag->pfns[i] > limit_pfn; i--)
> +		if (i == 0)
> +			return 0;
>   
> -static struct iova_magazine *iova_magazine_alloc(gfp_t flags)
> -{
> -	return kzalloc(sizeof(struct iova_magazine), flags);
> -}
> +	/* Swap it to pop it */
> +	pfn = mag->pfns[i];
> +	mag->pfns[i] = mag->pfns[--mag->size];
>   
> -static void iova_magazine_free(struct iova_magazine *mag)
> -{
> -	kfree(mag);
> +	return pfn;
>   }
>   
>   static void
> @@ -676,91 +526,91 @@ iova_magazine_free_pfns(struct iova_magazine *mag, struct iova_domain *iovad)
>   	mag->size = 0;
>   }
>   
> -static bool iova_magazine_full(struct iova_magazine *mag)
> -{
> -	return (mag && mag->size == IOVA_MAG_SIZE);
> -}
> -
> -static bool iova_magazine_empty(struct iova_magazine *mag)
> -{
> -	return (!mag || mag->size == 0);
> -}
> -
> -static unsigned long iova_magazine_pop(struct iova_magazine *mag,
> -				       unsigned long limit_pfn)
> +/*
> + * free all the IOVA ranges cached by a cpu (used when cpu is unplugged)
> + */
> +static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad)
>   {
> +	struct iova_cpu_rcache *cpu_rcache;
> +	struct iova_rcache *rcache;
> +	unsigned long flags;
>   	int i;
> -	unsigned long pfn;
> -
> -	BUG_ON(iova_magazine_empty(mag));
> -
> -	/* Only fall back to the rbtree if we have no suitable pfns at all */
> -	for (i = mag->size - 1; mag->pfns[i] > limit_pfn; i--)
> -		if (i == 0)
> -			return 0;
> -
> -	/* Swap it to pop it */
> -	pfn = mag->pfns[i];
> -	mag->pfns[i] = mag->pfns[--mag->size];
>   
> -	return pfn;
> +	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
> +		rcache = &iovad->rcaches[i];
> +		cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
> +		spin_lock_irqsave(&cpu_rcache->lock, flags);
> +		iova_magazine_free_pfns(cpu_rcache->loaded, iovad);
> +		iova_magazine_free_pfns(cpu_rcache->prev, iovad);
> +		spin_unlock_irqrestore(&cpu_rcache->lock, flags);
> +	}
>   }
>   
> -static void iova_magazine_push(struct iova_magazine *mag, unsigned long pfn)
> +static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
>   {
> -	BUG_ON(iova_magazine_full(mag));
> +	struct iova_domain *iovad;
>   
> -	mag->pfns[mag->size++] = pfn;
> +	iovad = hlist_entry_safe(node, struct iova_domain, cpuhp_dead);
> +
> +	free_cpu_cached_iovas(cpu, iovad);
> +	return 0;
>   }
>   
> -int iova_domain_init_rcaches(struct iova_domain *iovad)
> +/*
> + * free all the IOVA ranges of global cache
> + */
> +static void free_global_cached_iovas(struct iova_domain *iovad)
>   {
> -	unsigned int cpu;
> -	int i, ret;
> -
> -	iovad->rcaches = kcalloc(IOVA_RANGE_CACHE_MAX_SIZE,
> -				 sizeof(struct iova_rcache),
> -				 GFP_KERNEL);
> -	if (!iovad->rcaches)
> -		return -ENOMEM;
> +	struct iova_rcache *rcache;
> +	unsigned long flags;
> +	int i, j;
>   
>   	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
> -		struct iova_cpu_rcache *cpu_rcache;
> -		struct iova_rcache *rcache;
> -
>   		rcache = &iovad->rcaches[i];
> -		spin_lock_init(&rcache->lock);
> -		rcache->depot_size = 0;
> -		rcache->cpu_rcaches = __alloc_percpu(sizeof(*cpu_rcache),
> -						     cache_line_size());
> -		if (!rcache->cpu_rcaches) {
> -			ret = -ENOMEM;
> -			goto out_err;
> +		spin_lock_irqsave(&rcache->lock, flags);
> +		for (j = 0; j < rcache->depot_size; ++j) {
> +			iova_magazine_free_pfns(rcache->depot[j], iovad);
> +			iova_magazine_free(rcache->depot[j]);
>   		}
> -		for_each_possible_cpu(cpu) {
> -			cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
> +		rcache->depot_size = 0;
> +		spin_unlock_irqrestore(&rcache->lock, flags);
> +	}
> +}
>   
> -			spin_lock_init(&cpu_rcache->lock);
> -			cpu_rcache->loaded = iova_magazine_alloc(GFP_KERNEL);
> -			cpu_rcache->prev = iova_magazine_alloc(GFP_KERNEL);
> -			if (!cpu_rcache->loaded || !cpu_rcache->prev) {
> -				ret = -ENOMEM;
> -				goto out_err;
> -			}
> +/*
> + * free rcache data structures.
> + */
> +static void free_iova_rcaches(struct iova_domain *iovad)
> +{
> +	struct iova_rcache *rcache;
> +	struct iova_cpu_rcache *cpu_rcache;
> +	unsigned int cpu;
> +	int i, j;
> +
> +	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
> +		rcache = &iovad->rcaches[i];
> +		if (!rcache->cpu_rcaches)
> +			break;
> +		for_each_possible_cpu(cpu) {
> +			cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
> +			iova_magazine_free(cpu_rcache->loaded);
> +			iova_magazine_free(cpu_rcache->prev);
>   		}
> +		free_percpu(rcache->cpu_rcaches);
> +		for (j = 0; j < rcache->depot_size; ++j)
> +			iova_magazine_free(rcache->depot[j]);
>   	}
>   
> -	ret = cpuhp_state_add_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
> -					       &iovad->cpuhp_dead);
> -	if (ret)
> -		goto out_err;
> -	return 0;
> +	kfree(iovad->rcaches);
> +	iovad->rcaches = NULL;
> +}
>   
> -out_err:
> +static void iova_domain_free_rcaches(struct iova_domain *iovad)
> +{
> +	cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
> +					    &iovad->cpuhp_dead);
>   	free_iova_rcaches(iovad);
> -	return ret;
>   }
> -EXPORT_SYMBOL_GPL(iova_domain_init_rcaches);
>   
>   /*
>    * Try inserting IOVA range starting with 'iova_pfn' into 'rcache', and
> @@ -883,73 +733,212 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad,
>   	return __iova_rcache_get(&iovad->rcaches[log_size], limit_pfn - size);
>   }
>   
> -/*
> - * free rcache data structures.
> +/**
> + * alloc_iova_fast - allocates an iova from rcache
> + * @iovad: - iova domain in question
> + * @size: - size of page frames to allocate
> + * @limit_pfn: - max limit address
> + * @flush_rcache: - set to flush rcache on regular allocation failure
> + * This function tries to satisfy an iova allocation from the rcache,
> + * and falls back to regular allocation on failure. If regular allocation
> + * fails too and the flush_rcache flag is set then the rcache will be flushed.
>    */
> -static void free_iova_rcaches(struct iova_domain *iovad)
> +unsigned long
> +alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
> +		unsigned long limit_pfn, bool flush_rcache)
> +{
> +	unsigned long iova_pfn;
> +	struct iova *new_iova;
> +
> +	/*
> +	 * Freeing non-power-of-two-sized allocations back into the IOVA caches
> +	 * will come back to bite us badly, so we have to waste a bit of space
> +	 * rounding up anything cacheable to make sure that can't happen. The
> +	 * order of the unadjusted size will still match upon freeing.
> +	 */
> +	if (size < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
> +		size = roundup_pow_of_two(size);
> +
> +	iova_pfn = iova_rcache_get(iovad, size, limit_pfn + 1);
> +	if (iova_pfn)
> +		return iova_pfn;
> +
> +retry:
> +	new_iova = alloc_iova(iovad, size, limit_pfn, true);
> +	if (!new_iova) {
> +		unsigned int cpu;
> +
> +		if (!flush_rcache)
> +			return 0;
> +
> +		/* Try replenishing IOVAs by flushing rcache. */
> +		flush_rcache = false;
> +		for_each_online_cpu(cpu)
> +			free_cpu_cached_iovas(cpu, iovad);
> +		free_global_cached_iovas(iovad);
> +		goto retry;
> +	}
> +
> +	return new_iova->pfn_lo;
> +}
> +EXPORT_SYMBOL_GPL(alloc_iova_fast);
> +
> +/**
> + * free_iova_fast - free iova pfn range into rcache
> + * @iovad: - iova domain in question.
> + * @pfn: - pfn that is allocated previously
> + * @size: - # of pages in range
> + * This functions frees an iova range by trying to put it into the rcache,
> + * falling back to regular iova deallocation via free_iova() if this fails.
> + */
> +void
> +free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
> +{
> +	if (iova_rcache_insert(iovad, pfn, size))
> +		return;
> +
> +	free_iova(iovad, pfn);
> +}
> +EXPORT_SYMBOL_GPL(free_iova_fast);
> +
> +int iova_domain_init_rcaches(struct iova_domain *iovad)
>   {
> -	struct iova_rcache *rcache;
> -	struct iova_cpu_rcache *cpu_rcache;
>   	unsigned int cpu;
> -	int i, j;
> +	int i, ret;
> +
> +	iovad->rcaches = kcalloc(IOVA_RANGE_CACHE_MAX_SIZE,
> +				 sizeof(struct iova_rcache),
> +				 GFP_KERNEL);
> +	if (!iovad->rcaches)
> +		return -ENOMEM;
>   
>   	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
> +		struct iova_cpu_rcache *cpu_rcache;
> +		struct iova_rcache *rcache;
> +
>   		rcache = &iovad->rcaches[i];
> -		if (!rcache->cpu_rcaches)
> -			break;
> +		spin_lock_init(&rcache->lock);
> +		rcache->depot_size = 0;
> +		rcache->cpu_rcaches = __alloc_percpu(sizeof(*cpu_rcache),
> +						     cache_line_size());
> +		if (!rcache->cpu_rcaches) {
> +			ret = -ENOMEM;
> +			goto out_err;
> +		}
>   		for_each_possible_cpu(cpu) {
>   			cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
> -			iova_magazine_free(cpu_rcache->loaded);
> -			iova_magazine_free(cpu_rcache->prev);
> +
> +			spin_lock_init(&cpu_rcache->lock);
> +			cpu_rcache->loaded = iova_magazine_alloc(GFP_KERNEL);
> +			cpu_rcache->prev = iova_magazine_alloc(GFP_KERNEL);
> +			if (!cpu_rcache->loaded || !cpu_rcache->prev) {
> +				ret = -ENOMEM;
> +				goto out_err;
> +			}
>   		}
> -		free_percpu(rcache->cpu_rcaches);
> -		for (j = 0; j < rcache->depot_size; ++j)
> -			iova_magazine_free(rcache->depot[j]);
>   	}
>   
> -	kfree(iovad->rcaches);
> -	iovad->rcaches = NULL;
> +	ret = cpuhp_state_add_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
> +					       &iovad->cpuhp_dead);
> +	if (ret)
> +		goto out_err;
> +	return 0;
> +
> +out_err:
> +	free_iova_rcaches(iovad);
> +	return ret;
>   }
> +EXPORT_SYMBOL_GPL(iova_domain_init_rcaches);
>   
> -/*
> - * free all the IOVA ranges cached by a cpu (used when cpu is unplugged)
> - */
> -static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad)
> +void
> +init_iova_domain(struct iova_domain *iovad, unsigned long granule,
> +	unsigned long start_pfn)
>   {
> -	struct iova_cpu_rcache *cpu_rcache;
> -	struct iova_rcache *rcache;
> -	unsigned long flags;
> -	int i;
> +	/*
> +	 * IOVA granularity will normally be equal to the smallest
> +	 * supported IOMMU page size; both *must* be capable of
> +	 * representing individual CPU pages exactly.
> +	 */
> +	BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
>   
> -	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
> -		rcache = &iovad->rcaches[i];
> -		cpu_rcache = per_cpu_ptr(rcache->cpu_rcaches, cpu);
> -		spin_lock_irqsave(&cpu_rcache->lock, flags);
> -		iova_magazine_free_pfns(cpu_rcache->loaded, iovad);
> -		iova_magazine_free_pfns(cpu_rcache->prev, iovad);
> -		spin_unlock_irqrestore(&cpu_rcache->lock, flags);
> -	}
> +	spin_lock_init(&iovad->iova_rbtree_lock);
> +	iovad->rbroot = RB_ROOT;
> +	iovad->cached_node = &iovad->anchor.node;
> +	iovad->cached32_node = &iovad->anchor.node;
> +	iovad->granule = granule;
> +	iovad->start_pfn = start_pfn;
> +	iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad));
> +	iovad->max32_alloc_size = iovad->dma_32bit_pfn;
> +	iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
> +	rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
> +	rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
>   }
> +EXPORT_SYMBOL_GPL(init_iova_domain);
>   
> -/*
> - * free all the IOVA ranges of global cache
> +/**
> + * put_iova_domain - destroys the iova domain
> + * @iovad: - iova domain in question.
> + * All the iova's in that domain are destroyed.
>    */
> -static void free_global_cached_iovas(struct iova_domain *iovad)
> +void put_iova_domain(struct iova_domain *iovad)
>   {
> -	struct iova_rcache *rcache;
> -	unsigned long flags;
> -	int i, j;
> +	struct iova *iova, *tmp;
>   
> -	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
> -		rcache = &iovad->rcaches[i];
> -		spin_lock_irqsave(&rcache->lock, flags);
> -		for (j = 0; j < rcache->depot_size; ++j) {
> -			iova_magazine_free_pfns(rcache->depot[j], iovad);
> -			iova_magazine_free(rcache->depot[j]);
> +	if (iovad->rcaches)
> +		iova_domain_free_rcaches(iovad);
> +
> +	rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node)
> +		free_iova_mem(iova);
> +}
> +EXPORT_SYMBOL_GPL(put_iova_domain);
> +
> +int iova_cache_get(void)
> +{
> +	mutex_lock(&iova_cache_mutex);
> +	if (!iova_cache_users) {
> +		int ret;
> +
> +		ret = cpuhp_setup_state_multi(CPUHP_IOMMU_IOVA_DEAD, "iommu/iova:dead", NULL,
> +					iova_cpuhp_dead);
> +		if (ret) {
> +			mutex_unlock(&iova_cache_mutex);
> +			pr_err("Couldn't register cpuhp handler\n");
> +			return ret;
>   		}
> -		rcache->depot_size = 0;
> -		spin_unlock_irqrestore(&rcache->lock, flags);
> +
> +		iova_cache = kmem_cache_create(
> +			"iommu_iova", sizeof(struct iova), 0,
> +			SLAB_HWCACHE_ALIGN, NULL);
> +		if (!iova_cache) {
> +			cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
> +			mutex_unlock(&iova_cache_mutex);
> +			pr_err("Couldn't create iova cache\n");
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	iova_cache_users++;
> +	mutex_unlock(&iova_cache_mutex);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iova_cache_get);
> +
> +void iova_cache_put(void)
> +{
> +	mutex_lock(&iova_cache_mutex);
> +	if (WARN_ON(!iova_cache_users)) {
> +		mutex_unlock(&iova_cache_mutex);
> +		return;
>   	}
> +	iova_cache_users--;
> +	if (!iova_cache_users) {
> +		cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
> +		kmem_cache_destroy(iova_cache);
> +	}
> +	mutex_unlock(&iova_cache_mutex);
>   }
> +EXPORT_SYMBOL_GPL(iova_cache_put);
> +
>   MODULE_AUTHOR("Anil S Keshavamurthy <anil.s.keshavamurthy@...el.com>");
>   MODULE_LICENSE("GPL");
> diff --git a/include/linux/iova.h b/include/linux/iova.h
> index 320a70e40233..a167fb22c899 100644
> --- a/include/linux/iova.h
> +++ b/include/linux/iova.h
> @@ -76,35 +76,26 @@ static inline unsigned long iova_pfn(struct iova_domain *iovad, dma_addr_t iova)
>   }
>   
>   #if IS_ENABLED(CONFIG_IOMMU_IOVA)
> -int iova_cache_get(void);
> -void iova_cache_put(void);
> -
>   void free_iova(struct iova_domain *iovad, unsigned long pfn);
>   void __free_iova(struct iova_domain *iovad, struct iova *iova);
>   struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
>   	unsigned long limit_pfn,
>   	bool size_aligned);
> +struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
> +	unsigned long pfn_hi);
> +struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
>   void free_iova_fast(struct iova_domain *iovad, unsigned long pfn,
>   		    unsigned long size);
>   unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
>   			      unsigned long limit_pfn, bool flush_rcache);
> -struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
> -	unsigned long pfn_hi);
> +int iova_domain_init_rcaches(struct iova_domain *iovad);
>   void init_iova_domain(struct iova_domain *iovad, unsigned long granule,
>   	unsigned long start_pfn);
> -int iova_domain_init_rcaches(struct iova_domain *iovad);
> -struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
>   void put_iova_domain(struct iova_domain *iovad);
> -#else
> -static inline int iova_cache_get(void)
> -{
> -	return -ENOTSUPP;
> -}
> -
> -static inline void iova_cache_put(void)
> -{
> -}
> +int iova_cache_get(void);
> +void iova_cache_put(void);
>   
> +#else
>   static inline void free_iova(struct iova_domain *iovad, unsigned long pfn)
>   {
>   }
> @@ -121,6 +112,19 @@ static inline struct iova *alloc_iova(struct iova_domain *iovad,
>   	return NULL;
>   }
>   
> +static inline struct iova *reserve_iova(struct iova_domain *iovad,
> +					unsigned long pfn_lo,
> +					unsigned long pfn_hi)
> +{
> +	return NULL;
> +}
> +
> +static inline struct iova *find_iova(struct iova_domain *iovad,
> +				     unsigned long pfn)
> +{
> +	return NULL;
> +}
> +
>   static inline void free_iova_fast(struct iova_domain *iovad,
>   				  unsigned long pfn,
>   				  unsigned long size)
> @@ -135,12 +139,6 @@ static inline unsigned long alloc_iova_fast(struct iova_domain *iovad,
>   	return 0;
>   }
>   
> -static inline struct iova *reserve_iova(struct iova_domain *iovad,
> -					unsigned long pfn_lo,
> -					unsigned long pfn_hi)
> -{
> -	return NULL;
> -}
>   
>   static inline void init_iova_domain(struct iova_domain *iovad,
>   				    unsigned long granule,
> @@ -148,13 +146,17 @@ static inline void init_iova_domain(struct iova_domain *iovad,
>   {
>   }
>   
> -static inline struct iova *find_iova(struct iova_domain *iovad,
> -				     unsigned long pfn)
> +
> +static inline void put_iova_domain(struct iova_domain *iovad)
>   {
> -	return NULL;
>   }
>   
> -static inline void put_iova_domain(struct iova_domain *iovad)
> +static inline int iova_cache_get(void)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline void iova_cache_put(void)
>   {
>   }
>   

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ