Adding Robin.

Robin, can you please have a look?

On Wed, Sep 20, 2017 at 11:28:19AM -0400, David Woods wrote:
> When allocating pages with alloc_iova() where limit_pfn > dma_32bit_pfn
> __alloc_and_insert_iova_range does a linear traversal of the tree to
> find a free block.  In the worst case it makes the alloc O(n) for each
> page, where n is the number of pages allocated so far.  The worst case
> turns out to be common for drivers that allocate lots of pages at start
> up and never free them.
> 
> There is an optimization for allocating 32-bit addresses where it
> starts the search at the point where the previous allocated page was
> inserted.  This is sensible, since otherwise it would have to always
> search through all the 64-bit pages first.
> 
> To improve this situation, extend the optimization to also keep track
> of the point were the previous 64-bit page was inserted.  With this
> change, the search for an available slot can normally be done in
> constant time and the whole alloc_iova only costs O(log n) due to the
> rb tree insert.
> 
> Reviewed-by: Chris Metcalf <[email protected]>
> Signed-off-by: David Woods <[email protected]>
> ---
>  drivers/iommu/iova.c | 82 
> +++++++++++++++++++++++++++++++++++-----------------
>  include/linux/iova.h |  1 +
>  2 files changed, 56 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
> index 33edfa7..3c82694 100644
> --- a/drivers/iommu/iova.c
> +++ b/drivers/iommu/iova.c
> @@ -108,15 +108,26 @@ int init_iova_flush_queue(struct iova_domain *iovad,
>  EXPORT_SYMBOL_GPL(init_iova_flush_queue);
>  
>  static struct rb_node *
> -__get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
> +__get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn)
>  {
> -     if ((*limit_pfn > iovad->dma_32bit_pfn) ||
> -             (iovad->cached32_node == NULL))
> +     if (limit_pfn <= iovad->dma_32bit_pfn)
> +             return iovad->cached32_node;
> +     else
> +             return iovad->cached64_node;
> +}
> +
> +static struct rb_node *
> +__get_cached_rbnode_and_limit(struct iova_domain *iovad,
> +                           unsigned long *limit_pfn)
> +{
> +     struct rb_node *cached_node = __get_cached_rbnode(iovad, *limit_pfn);
> +
> +     if (cached_node == NULL) {
>               return rb_last(&iovad->rbroot);
> -     else {
> -             struct rb_node *prev_node = rb_prev(iovad->cached32_node);
> +     } else {
> +             struct rb_node *prev_node = rb_prev(cached_node);
>               struct iova *curr_iova =
> -                     rb_entry(iovad->cached32_node, struct iova, node);
> +                     rb_entry(cached_node, struct iova, node);
>               *limit_pfn = curr_iova->pfn_lo;
>               return prev_node;
>       }
> @@ -124,33 +135,50 @@ __get_cached_rbnode(struct iova_domain *iovad, unsigned 
> long *limit_pfn)
>  
>  static void
>  __cached_rbnode_insert_update(struct iova_domain *iovad,
> -     unsigned long limit_pfn, struct iova *new)
> +     unsigned long limit_pfn, struct rb_node *node)
>  {
> -     if (limit_pfn != iovad->dma_32bit_pfn)
> -             return;
> -     iovad->cached32_node = &new->node;
> +     if (limit_pfn == iovad->dma_32bit_pfn)
> +             iovad->cached32_node = node;
> +     else if (limit_pfn > iovad->dma_32bit_pfn)
> +             iovad->cached64_node = node;
>  }
>  
>  static void
>  __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free)
>  {
>       struct iova *cached_iova;
> -     struct rb_node *curr;
> -
> -     if (!iovad->cached32_node)
> -             return;
> -     curr = iovad->cached32_node;
> -     cached_iova = rb_entry(curr, struct iova, node);
> -
> -     if (free->pfn_lo >= cached_iova->pfn_lo) {
> -             struct rb_node *node = rb_next(&free->node);
> -             struct iova *iova = rb_entry(node, struct iova, node);
> +     struct rb_node *cached_node;
>  
> -             /* only cache if it's below 32bit pfn */
> -             if (node && iova->pfn_lo < iovad->dma_32bit_pfn)
> -                     iovad->cached32_node = node;
> -             else
> -                     iovad->cached32_node = NULL;
> +     if (free->pfn_lo <= iovad->dma_32bit_pfn) {
> +             if (unlikely(iovad->cached64_node == &free->node))
> +                     iovad->cached64_node = NULL;
> +             cached_node = iovad->cached32_node;
> +             if (!cached_node)
> +                     return;
> +             cached_iova = rb_entry(cached_node, struct iova, node);
> +             if (free->pfn_lo >= cached_iova->pfn_lo) {
> +                     struct rb_node *next_node = rb_next(&free->node);
> +
> +                     if (next_node &&
> +                         rb_entry(next_node, struct iova, node)->pfn_lo <=
> +                         iovad->dma_32bit_pfn)
> +                             iovad->cached32_node = next_node;
> +                     else
> +                             iovad->cached32_node = NULL;
> +             }
> +     } else {
> +             cached_node = iovad->cached64_node;
> +             if (!cached_node)
> +                     return;
> +             cached_iova = container_of(cached_node, struct iova, node);
> +             if (free->pfn_lo >= cached_iova->pfn_lo) {
> +                     struct rb_node *next_node = rb_next(&free->node);
> +
> +                     if (next_node)
> +                             iovad->cached64_node = next_node;
> +                     else
> +                             iovad->cached64_node = NULL;
> +             }
>       }
>  }
>  
> @@ -204,7 +232,7 @@ static int __alloc_and_insert_iova_range(struct 
> iova_domain *iovad,
>       /* Walk the tree backwards */
>       spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
>       saved_pfn = limit_pfn;
> -     curr = __get_cached_rbnode(iovad, &limit_pfn);
> +     curr = __get_cached_rbnode_and_limit(iovad, &limit_pfn);
>       prev = curr;
>       while (curr) {
>               struct iova *curr_iova = rb_entry(curr, struct iova, node);
> @@ -238,7 +266,7 @@ static int __alloc_and_insert_iova_range(struct 
> iova_domain *iovad,
>  
>       /* 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, saved_pfn, new);
> +     __cached_rbnode_insert_update(iovad, saved_pfn, &new->node);
>  
>       spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
>  
> diff --git a/include/linux/iova.h b/include/linux/iova.h
> index d179b9b..ea89695 100644
> --- a/include/linux/iova.h
> +++ b/include/linux/iova.h
> @@ -71,6 +71,7 @@ struct iova_domain {
>       spinlock_t      iova_rbtree_lock; /* Lock to protect update of rbtree */
>       struct rb_root  rbroot;         /* iova domain rbtree root */
>       struct rb_node  *cached32_node; /* Save last alloced node */
> +     struct rb_node  *cached64_node; /* Save last alloced node */
>       unsigned long   granule;        /* pfn granularity for this domain */
>       unsigned long   start_pfn;      /* Lower limit for this domain */
>       unsigned long   dma_32bit_pfn;
> -- 
> 2.1.2

Reply via email to