Reviewed-by: Ben Serebrin <sereb...@google.com>

Looks good.

On Mon, Dec 28, 2015 at 8:17 AM, Adam Morrison <m...@cs.technion.ac.il> wrote:
> From: Omer Peleg <o...@cs.technion.ac.il>
>
> Make intel-iommu map/unmap/invalidate work with IOVA pfns instead of
> pointers to "struct iova". This avoids using the iova struct from the IOVA
> red-black tree and the resulting explicit find_iova() on unmap.
>
> This patch will allow us to cache IOVAs in the next patch, in order to
> avoid rbtree operations for the majority of map/unmap operations.
>
> Note: In eliminating the find_iova() operation, we have also eliminated
> the sanity check previously done in the unmap flow. Arguably, this was
> overhead that is better avoided in production code, but it could be
> brought back as a debug option for driver development.
>
> Signed-off-by: Omer Peleg <o...@cs.technion.ac.il>
> [m...@cs.technion.ac.il: rebased and reworded the commit message]
> Signed-off-by: Adam Morrison <m...@cs.technion.ac.il>
> ---
>  drivers/iommu/intel-iommu.c | 73 
> ++++++++++++++++++++++-----------------------
>  drivers/iommu/iova.c        |  8 ++---
>  include/linux/iova.h        |  2 +-
>  3 files changed, 40 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 6c37bbc..c2de0e7 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -455,7 +455,7 @@ static LIST_HEAD(dmar_rmrr_units);
>  static void flush_unmaps_timeout(unsigned long data);
>
>  struct deferred_flush_entry {
> -       struct iova *iova;
> +       unsigned long iova_pfn;
>         unsigned long nrpages;
>         struct dmar_domain *domain;
>         struct page *freelist;
> @@ -3264,11 +3264,11 @@ error:
>  }
>
>  /* This takes a number of _MM_ pages, not VTD pages */
> -static struct iova *intel_alloc_iova(struct device *dev,
> +static unsigned long intel_alloc_iova(struct device *dev,
>                                      struct dmar_domain *domain,
>                                      unsigned long nrpages, uint64_t dma_mask)
>  {
> -       struct iova *iova = NULL;
> +       unsigned long iova_pfn;
>
>         /* Restrict dma_mask to the width that the iommu can handle */
>         dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask);
> @@ -3281,19 +3281,19 @@ static struct iova *intel_alloc_iova(struct device 
> *dev,
>                  * DMA_BIT_MASK(32) and if that fails then try allocating
>                  * from higher range
>                  */
> -               iova = alloc_iova(&domain->iovad, nrpages,
> -                                 IOVA_PFN(DMA_BIT_MASK(32)), 1);
> -               if (iova)
> -                       return iova;
> +               iova_pfn = alloc_iova(&domain->iovad, nrpages,
> +                                     IOVA_PFN(DMA_BIT_MASK(32)), 1);
> +               if (iova_pfn)
> +                       return iova_pfn;
>         }
> -       iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1);
> -       if (unlikely(!iova)) {
> +       iova_pfn = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1);
> +       if (unlikely(!iova_pfn)) {
>                 pr_err("Allocating %ld-page iova for %s failed",
>                        nrpages, dev_name(dev));
> -               return NULL;
> +               return 0;
>         }
>
> -       return iova;
> +       return iova_pfn;
>  }
>
>  static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
> @@ -3391,7 +3391,7 @@ static dma_addr_t __intel_map_single(struct device 
> *dev, phys_addr_t paddr,
>  {
>         struct dmar_domain *domain;
>         phys_addr_t start_paddr;
> -       struct iova *iova;
> +       unsigned long iova_pfn;
>         int prot = 0;
>         int ret;
>         struct intel_iommu *iommu;
> @@ -3409,8 +3409,8 @@ static dma_addr_t __intel_map_single(struct device 
> *dev, phys_addr_t paddr,
>         iommu = domain_get_iommu(domain);
>         size = aligned_nrpages(paddr, size);
>
> -       iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), dma_mask);
> -       if (!iova)
> +       iova_pfn = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size), 
> dma_mask);
> +       if (!iova_pfn)
>                 goto error;
>
>         /*
> @@ -3428,7 +3428,7 @@ static dma_addr_t __intel_map_single(struct device 
> *dev, phys_addr_t paddr,
>          * might have two guest_addr mapping to the same host paddr, but this
>          * is not a big problem
>          */
> -       ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova->pfn_lo),
> +       ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova_pfn),
>                                  mm_to_dma_pfn(paddr_pfn), size, prot);
>         if (ret)
>                 goto error;
> @@ -3436,18 +3436,18 @@ static dma_addr_t __intel_map_single(struct device 
> *dev, phys_addr_t paddr,
>         /* it's a non-present to present mapping. Only flush if caching mode 
> */
>         if (cap_caching_mode(iommu->cap))
>                 iommu_flush_iotlb_psi(iommu, domain,
> -                                     mm_to_dma_pfn(iova->pfn_lo),
> +                                     mm_to_dma_pfn(iova_pfn),
>                                       size, 0, 1);
>         else
>                 iommu_flush_write_buffer(iommu);
>
> -       start_paddr = (phys_addr_t)iova->pfn_lo << PAGE_SHIFT;
> +       start_paddr = (phys_addr_t)iova_pfn << PAGE_SHIFT;
>         start_paddr += paddr & ~PAGE_MASK;
>         return start_paddr;
>
>  error:
> -       if (iova)
> -               __free_iova(&domain->iovad, iova);
> +       if (iova_pfn)
> +               free_iova(&domain->iovad, iova_pfn);
>         pr_err("Device %s request: %zx@%llx dir %d --- failed\n",
>                 dev_name(dev), size, (unsigned long long)paddr, dir);
>         return 0;
> @@ -3487,7 +3487,7 @@ static void flush_unmaps(struct deferred_flush_data 
> *flush_data)
>                         unsigned long mask;
>                         struct deferred_flush_entry *entry =
>                                                 &flush_table->entries[j];
> -                       struct iova *iova = entry->iova;
> +                       unsigned long iova_pfn = entry->iova_pfn;
>                         unsigned long nrpages = entry->nrpages;
>                         struct dmar_domain *domain = entry->domain;
>                         struct page *freelist = entry->freelist;
> @@ -3495,14 +3495,14 @@ static void flush_unmaps(struct deferred_flush_data 
> *flush_data)
>                         /* On real hardware multiple invalidations are 
> expensive */
>                         if (cap_caching_mode(iommu->cap))
>                                 iommu_flush_iotlb_psi(iommu, domain,
> -                                       mm_to_dma_pfn(iova->pfn_lo),
> +                                       mm_to_dma_pfn(iova_pfn),
>                                         nrpages, !freelist, 0);
>                         else {
>                                 mask = ilog2(nrpages);
>                                 iommu_flush_dev_iotlb(domain,
> -                                               (uint64_t)iova->pfn_lo << 
> PAGE_SHIFT, mask);
> +                                               (uint64_t)iova_pfn << 
> PAGE_SHIFT, mask);
>                         }
> -                       __free_iova(&domain->iovad, iova);
> +                       free_iova(&domain->iovad, iova_pfn);
>                         if (freelist)
>                                 dma_free_pagelist(freelist);
>                 }
> @@ -3522,7 +3522,7 @@ static void flush_unmaps_timeout(unsigned long cpuid)
>         spin_unlock_irqrestore(&flush_data->lock, flags);
>  }
>
> -static void add_unmap(struct dmar_domain *dom, struct iova *iova,
> +static void add_unmap(struct dmar_domain *dom, unsigned long iova_pfn,
>                       unsigned long nrpages, struct page *freelist)
>  {
>         unsigned long flags;
> @@ -3547,7 +3547,7 @@ static void add_unmap(struct dmar_domain *dom, struct 
> iova *iova,
>
>         entry = &flush_data->tables[iommu_id].entries[entry_id];
>         entry->domain = dom;
> -       entry->iova = iova;
> +       entry->iova_pfn = iova_pfn;
>         entry->nrpages = nrpages;
>         entry->freelist = freelist;
>
> @@ -3566,7 +3566,7 @@ static void intel_unmap(struct device *dev, dma_addr_t 
> dev_addr, size_t size)
>         struct dmar_domain *domain;
>         unsigned long start_pfn, last_pfn;
>         unsigned long nrpages;
> -       struct iova *iova;
> +       unsigned long iova_pfn;
>         struct intel_iommu *iommu;
>         struct page *freelist;
>
> @@ -3578,13 +3578,10 @@ static void intel_unmap(struct device *dev, 
> dma_addr_t dev_addr, size_t size)
>
>         iommu = domain_get_iommu(domain);
>
> -       iova = find_iova(&domain->iovad, IOVA_PFN(dev_addr));
> -       if (WARN_ONCE(!iova, "Driver unmaps unmatched page at PFN %llx\n",
> -                     (unsigned long long)dev_addr))
> -               return;
> +       iova_pfn = IOVA_PFN(dev_addr);
>
>         nrpages = aligned_nrpages(dev_addr, size);
> -       start_pfn = mm_to_dma_pfn(iova->pfn_lo);
> +       start_pfn = mm_to_dma_pfn(iova_pfn);
>         last_pfn = start_pfn + nrpages - 1;
>
>         pr_debug("Device %s unmapping: pfn %lx-%lx\n",
> @@ -3596,10 +3593,10 @@ static void intel_unmap(struct device *dev, 
> dma_addr_t dev_addr, size_t size)
>                 iommu_flush_iotlb_psi(iommu, domain, start_pfn,
>                                       nrpages, !freelist, 0);
>                 /* free iova */
> -               __free_iova(&domain->iovad, iova);
> +               free_iova(&domain->iovad, iova_pfn);
>                 dma_free_pagelist(freelist);
>         } else {
> -               add_unmap(domain, iova, nrpages, freelist);
> +               add_unmap(domain, iova_pfn, nrpages, freelist);
>                 /*
>                  * queue up the release of the unmap to save the 1/6th of the
>                  * cpu used up by the iotlb flush operation...
> @@ -3712,7 +3709,7 @@ static int intel_map_sg(struct device *dev, struct 
> scatterlist *sglist, int nele
>         struct dmar_domain *domain;
>         size_t size = 0;
>         int prot = 0;
> -       struct iova *iova = NULL;
> +       unsigned long iova_pfn;
>         int ret;
>         struct scatterlist *sg;
>         unsigned long start_vpfn;
> @@ -3731,9 +3728,9 @@ static int intel_map_sg(struct device *dev, struct 
> scatterlist *sglist, int nele
>         for_each_sg(sglist, sg, nelems, i)
>                 size += aligned_nrpages(sg->offset, sg->length);
>
> -       iova = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size),
> +       iova_pfn = intel_alloc_iova(dev, domain, dma_to_mm_pfn(size),
>                                 *dev->dma_mask);
> -       if (!iova) {
> +       if (!iova_pfn) {
>                 sglist->dma_length = 0;
>                 return 0;
>         }
> @@ -3748,13 +3745,13 @@ static int intel_map_sg(struct device *dev, struct 
> scatterlist *sglist, int nele
>         if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)
>                 prot |= DMA_PTE_WRITE;
>
> -       start_vpfn = mm_to_dma_pfn(iova->pfn_lo);
> +       start_vpfn = mm_to_dma_pfn(iova_pfn);
>
>         ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot);
>         if (unlikely(ret)) {
>                 dma_pte_free_pagetable(domain, start_vpfn,
>                                        start_vpfn + size - 1);
> -               __free_iova(&domain->iovad, iova);
> +               free_iova(&domain->iovad, iova_pfn);
>                 return 0;
>         }
>
> diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
> index fa0adef..9009ce6 100644
> --- a/drivers/iommu/iova.c
> +++ b/drivers/iommu/iova.c
> @@ -267,7 +267,7 @@ EXPORT_SYMBOL_GPL(iova_cache_put);
>   * flag is set then the allocated address iova->pfn_lo will be naturally
>   * aligned on roundup_power_of_two(size).
>   */
> -struct iova *
> +unsigned long
>  alloc_iova(struct iova_domain *iovad, unsigned long size,
>         unsigned long limit_pfn,
>         bool size_aligned)
> @@ -277,17 +277,17 @@ alloc_iova(struct iova_domain *iovad, unsigned long 
> size,
>
>         new_iova = alloc_iova_mem();
>         if (!new_iova)
> -               return NULL;
> +               return 0;
>
>         ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn,
>                         new_iova, size_aligned);
>
>         if (ret) {
>                 free_iova_mem(new_iova);
> -               return NULL;
> +               return 0;
>         }
>
> -       return new_iova;
> +       return new_iova->pfn_lo;
>  }
>  EXPORT_SYMBOL_GPL(alloc_iova);
>
> diff --git a/include/linux/iova.h b/include/linux/iova.h
> index 92f7177..efecee0 100644
> --- a/include/linux/iova.h
> +++ b/include/linux/iova.h
> @@ -75,7 +75,7 @@ struct iova *alloc_iova_mem(void);
>  void free_iova_mem(struct iova *iova);
>  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 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,
> --
> 1.9.1
>
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to