]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/iommu/dma-iommu.c
iommu/dma: Plumb in the per-CPU IOVA caches
[mirror_ubuntu-zesty-kernel.git] / drivers / iommu / dma-iommu.c
index 2db0d641cf4505b565656d99dbb29309247cd964..dce6b17f2c9e84b0e11a10e8968a0de4a0a7e0c4 100644 (file)
@@ -37,15 +37,41 @@ struct iommu_dma_msi_page {
        phys_addr_t             phys;
 };
 
+enum iommu_dma_cookie_type {
+       IOMMU_DMA_IOVA_COOKIE,
+       IOMMU_DMA_MSI_COOKIE,
+};
+
 struct iommu_dma_cookie {
-       struct iova_domain      iovad;
-       struct list_head        msi_page_list;
-       spinlock_t              msi_lock;
+       enum iommu_dma_cookie_type      type;
+       union {
+               /* Full allocator for IOMMU_DMA_IOVA_COOKIE */
+               struct iova_domain      iovad;
+               /* Trivial linear page allocator for IOMMU_DMA_MSI_COOKIE */
+               dma_addr_t              msi_iova;
+       };
+       struct list_head                msi_page_list;
+       spinlock_t                      msi_lock;
 };
 
-static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
+static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie)
+{
+       if (cookie->type == IOMMU_DMA_IOVA_COOKIE)
+               return cookie->iovad.granule;
+       return PAGE_SIZE;
+}
+
+static struct iommu_dma_cookie *cookie_alloc(enum iommu_dma_cookie_type type)
 {
-       return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad;
+       struct iommu_dma_cookie *cookie;
+
+       cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+       if (cookie) {
+               spin_lock_init(&cookie->msi_lock);
+               INIT_LIST_HEAD(&cookie->msi_page_list);
+               cookie->type = type;
+       }
+       return cookie;
 }
 
 int iommu_dma_init(void)
@@ -61,26 +87,54 @@ int iommu_dma_init(void)
  * callback when domain->type == IOMMU_DOMAIN_DMA.
  */
 int iommu_get_dma_cookie(struct iommu_domain *domain)
+{
+       if (domain->iova_cookie)
+               return -EEXIST;
+
+       domain->iova_cookie = cookie_alloc(IOMMU_DMA_IOVA_COOKIE);
+       if (!domain->iova_cookie)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL(iommu_get_dma_cookie);
+
+/**
+ * iommu_get_msi_cookie - Acquire just MSI remapping resources
+ * @domain: IOMMU domain to prepare
+ * @base: Start address of IOVA region for MSI mappings
+ *
+ * Users who manage their own IOVA allocation and do not want DMA API support,
+ * but would still like to take advantage of automatic MSI remapping, can use
+ * this to initialise their own domain appropriately. Users should reserve a
+ * contiguous IOVA region, starting at @base, large enough to accommodate the
+ * number of PAGE_SIZE mappings necessary to cover every MSI doorbell address
+ * used by the devices attached to @domain.
+ */
+int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
 {
        struct iommu_dma_cookie *cookie;
 
+       if (domain->type != IOMMU_DOMAIN_UNMANAGED)
+               return -EINVAL;
+
        if (domain->iova_cookie)
                return -EEXIST;
 
-       cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
+       cookie = cookie_alloc(IOMMU_DMA_MSI_COOKIE);
        if (!cookie)
                return -ENOMEM;
 
-       spin_lock_init(&cookie->msi_lock);
-       INIT_LIST_HEAD(&cookie->msi_page_list);
+       cookie->msi_iova = base;
        domain->iova_cookie = cookie;
        return 0;
 }
-EXPORT_SYMBOL(iommu_get_dma_cookie);
+EXPORT_SYMBOL(iommu_get_msi_cookie);
 
 /**
  * iommu_put_dma_cookie - Release a domain's DMA mapping resources
- * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
+ * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie() or
+ *          iommu_get_msi_cookie()
  *
  * IOMMU drivers should normally call this from their domain_free callback.
  */
@@ -92,7 +146,7 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
        if (!cookie)
                return;
 
-       if (cookie->iovad.granule)
+       if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule)
                put_iova_domain(&cookie->iovad);
 
        list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
@@ -137,11 +191,13 @@ static void iova_reserve_pci_windows(struct pci_dev *dev,
 int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
                u64 size, struct device *dev)
 {
-       struct iova_domain *iovad = cookie_iovad(domain);
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
        unsigned long order, base_pfn, end_pfn;
+       bool pci = dev && dev_is_pci(dev);
 
-       if (!iovad)
-               return -ENODEV;
+       if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
+               return -EINVAL;
 
        /* Use the smallest supported page size for IOVA granularity */
        order = __ffs(domain->pgsize_bitmap);
@@ -161,19 +217,31 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
                end_pfn = min_t(unsigned long, end_pfn,
                                domain->geometry.aperture_end >> order);
        }
+       /*
+        * PCI devices may have larger DMA masks, but still prefer allocating
+        * within a 32-bit mask to avoid DAC addressing. Such limitations don't
+        * apply to the typical platform device, so for those we may as well
+        * leave the cache limit at the top of their range to save an rb_last()
+        * traversal on every allocation.
+        */
+       if (pci)
+               end_pfn &= DMA_BIT_MASK(32) >> order;
 
-       /* All we can safely do with an existing domain is enlarge it */
+       /* start_pfn is always nonzero for an already-initialised domain */
        if (iovad->start_pfn) {
                if (1UL << order != iovad->granule ||
-                   base_pfn != iovad->start_pfn ||
-                   end_pfn < iovad->dma_32bit_pfn) {
+                   base_pfn != iovad->start_pfn) {
                        pr_warn("Incompatible range for DMA domain\n");
                        return -EFAULT;
                }
-               iovad->dma_32bit_pfn = end_pfn;
+               /*
+                * If we have devices with different DMA masks, move the free
+                * area cache limit down for the benefit of the smaller one.
+                */
+               iovad->dma_32bit_pfn = min(end_pfn, iovad->dma_32bit_pfn);
        } else {
                init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
-               if (dev && dev_is_pci(dev))
+               if (pci)
                        iova_reserve_pci_windows(to_pci_dev(dev), iovad);
        }
        return 0;
@@ -203,39 +271,67 @@ int dma_direction_to_prot(enum dma_data_direction dir, bool coherent)
        }
 }
 
-static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
-               dma_addr_t dma_limit)
+static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain,
+               size_t size, dma_addr_t dma_limit, struct device *dev)
 {
-       struct iova_domain *iovad = cookie_iovad(domain);
-       unsigned long shift = iova_shift(iovad);
-       unsigned long length = iova_align(iovad, size) >> shift;
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
+       unsigned long shift, iova_len, iova = 0;
 
-       if (domain->geometry.force_aperture)
-               dma_limit = min(dma_limit, domain->geometry.aperture_end);
+       if (cookie->type == IOMMU_DMA_MSI_COOKIE) {
+               cookie->msi_iova += size;
+               return cookie->msi_iova - size;
+       }
+
+       shift = iova_shift(iovad);
+       iova_len = size >> shift;
        /*
-        * Enforce size-alignment to be safe - there could perhaps be an
-        * attribute to control this per-device, or at least per-domain...
+        * 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.
         */
-       return alloc_iova(iovad, length, dma_limit >> shift, true);
+       if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
+               iova_len = roundup_pow_of_two(iova_len);
+
+       if (domain->geometry.force_aperture)
+               dma_limit = min(dma_limit, domain->geometry.aperture_end);
+
+       /* Try to get PCI devices a SAC address */
+       if (dma_limit > DMA_BIT_MASK(32) && dev_is_pci(dev))
+               iova = alloc_iova_fast(iovad, iova_len, DMA_BIT_MASK(32) >> shift);
+
+       if (!iova)
+               iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift);
+
+       return (dma_addr_t)iova << shift;
 }
 
-/* The IOVA allocator knows what we mapped, so just unmap whatever that was */
-static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr)
+static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie,
+               dma_addr_t iova, size_t size)
 {
-       struct iova_domain *iovad = cookie_iovad(domain);
+       struct iova_domain *iovad = &cookie->iovad;
        unsigned long shift = iova_shift(iovad);
-       unsigned long pfn = dma_addr >> shift;
-       struct iova *iova = find_iova(iovad, pfn);
-       size_t size;
 
-       if (WARN_ON(!iova))
-               return;
+       /* The MSI case is only ever cleaning up its most recent allocation */
+       if (cookie->type == IOMMU_DMA_MSI_COOKIE)
+               cookie->msi_iova -= size;
+       else
+               free_iova_fast(iovad, iova >> shift, size >> shift);
+}
+
+static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr,
+               size_t size)
+{
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
+       size_t iova_off = iova_offset(iovad, dma_addr);
+
+       dma_addr -= iova_off;
+       size = iova_align(iovad, size + iova_off);
 
-       size = iova_size(iova) << shift;
-       size -= iommu_unmap(domain, pfn << shift, size);
-       /* ...and if we can't, then something is horribly, horribly wrong */
-       WARN_ON(size > 0);
-       __free_iova(iovad, iova);
+       WARN_ON(iommu_unmap(domain, dma_addr, size) != size);
+       iommu_dma_free_iova(cookie, dma_addr, size);
 }
 
 static void __iommu_dma_free_pages(struct page **pages, int count)
@@ -317,7 +413,7 @@ static struct page **__iommu_dma_alloc_pages(unsigned int count,
 void iommu_dma_free(struct device *dev, struct page **pages, size_t size,
                dma_addr_t *handle)
 {
-       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), *handle);
+       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), *handle, size);
        __iommu_dma_free_pages(pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
        *handle = DMA_ERROR_CODE;
 }
@@ -345,11 +441,11 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
                void (*flush_page)(struct device *, const void *, phys_addr_t))
 {
        struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-       struct iova_domain *iovad = cookie_iovad(domain);
-       struct iova *iova;
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
        struct page **pages;
        struct sg_table sgt;
-       dma_addr_t dma_addr;
+       dma_addr_t iova;
        unsigned int count, min_size, alloc_sizes = domain->pgsize_bitmap;
 
        *handle = DMA_ERROR_CODE;
@@ -369,11 +465,11 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
        if (!pages)
                return NULL;
 
-       iova = __alloc_iova(domain, size, dev->coherent_dma_mask);
+       size = iova_align(iovad, size);
+       iova = iommu_dma_alloc_iova(domain, size, dev->coherent_dma_mask, dev);
        if (!iova)
                goto out_free_pages;
 
-       size = iova_align(iovad, size);
        if (sg_alloc_table_from_pages(&sgt, pages, count, 0, size, GFP_KERNEL))
                goto out_free_iova;
 
@@ -389,19 +485,18 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
                sg_miter_stop(&miter);
        }
 
-       dma_addr = iova_dma_addr(iovad, iova);
-       if (iommu_map_sg(domain, dma_addr, sgt.sgl, sgt.orig_nents, prot)
+       if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, prot)
                        < size)
                goto out_free_sg;
 
-       *handle = dma_addr;
+       *handle = iova;
        sg_free_table(&sgt);
        return pages;
 
 out_free_sg:
        sg_free_table(&sgt);
 out_free_iova:
-       __free_iova(iovad, iova);
+       iommu_dma_free_iova(cookie, iova, size);
 out_free_pages:
        __iommu_dma_free_pages(pages, count);
        return NULL;
@@ -435,22 +530,22 @@ int iommu_dma_mmap(struct page **pages, size_t size, struct vm_area_struct *vma)
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
                size_t size, int prot)
 {
-       dma_addr_t dma_addr;
        struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-       struct iova_domain *iovad = cookie_iovad(domain);
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
        size_t iova_off = iova_offset(iovad, phys);
-       size_t len = iova_align(iovad, size + iova_off);
-       struct iova *iova = __alloc_iova(domain, len, dma_get_mask(dev));
+       dma_addr_t iova;
 
+       size = iova_align(iovad, size + iova_off);
+       iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
        if (!iova)
                return DMA_ERROR_CODE;
 
-       dma_addr = iova_dma_addr(iovad, iova);
-       if (iommu_map(domain, dma_addr, phys - iova_off, len, prot)) {
-               __free_iova(iovad, iova);
+       if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
+               iommu_dma_free_iova(cookie, iova, size);
                return DMA_ERROR_CODE;
        }
-       return dma_addr + iova_off;
+       return iova + iova_off;
 }
 
 dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
@@ -462,7 +557,7 @@ dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
 void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
                enum dma_data_direction dir, unsigned long attrs)
 {
-       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle);
+       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle, size);
 }
 
 /*
@@ -551,10 +646,10 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
                int nents, int prot)
 {
        struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-       struct iova_domain *iovad = cookie_iovad(domain);
-       struct iova *iova;
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_domain *iovad = &cookie->iovad;
        struct scatterlist *s, *prev = NULL;
-       dma_addr_t dma_addr;
+       dma_addr_t iova;
        size_t iova_len = 0;
        unsigned long mask = dma_get_seg_boundary(dev);
        int i;
@@ -598,7 +693,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
                prev = s;
        }
 
-       iova = __alloc_iova(domain, iova_len, dma_get_mask(dev));
+       iova = iommu_dma_alloc_iova(domain, iova_len, dma_get_mask(dev), dev);
        if (!iova)
                goto out_restore_sg;
 
@@ -606,14 +701,13 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
         * We'll leave any physical concatenation to the IOMMU driver's
         * implementation - it knows better than we do.
         */
-       dma_addr = iova_dma_addr(iovad, iova);
-       if (iommu_map_sg(domain, dma_addr, sg, nents, prot) < iova_len)
+       if (iommu_map_sg(domain, iova, sg, nents, prot) < iova_len)
                goto out_free_iova;
 
-       return __finalise_sg(dev, sg, nents, dma_addr);
+       return __finalise_sg(dev, sg, nents, iova);
 
 out_free_iova:
-       __free_iova(iovad, iova);
+       iommu_dma_free_iova(cookie, iova, iova_len);
 out_restore_sg:
        __invalidate_sg(sg, nents);
        return 0;
@@ -622,11 +716,21 @@ out_restore_sg:
 void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
                enum dma_data_direction dir, unsigned long attrs)
 {
+       dma_addr_t start, end;
+       struct scatterlist *tmp;
+       int i;
        /*
         * The scatterlist segments are mapped into a single
         * contiguous IOVA allocation, so this is incredibly easy.
         */
-       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), sg_dma_address(sg));
+       start = sg_dma_address(sg);
+       for_each_sg(sg_next(sg), tmp, nents - 1, i) {
+               if (sg_dma_len(tmp) == 0)
+                       break;
+               sg = tmp;
+       }
+       end = sg_dma_address(sg) + sg_dma_len(sg);
+       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), start, end - start);
 }
 
 dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
@@ -639,7 +743,7 @@ dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
 void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
                size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle);
+       __iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle, size);
 }
 
 int iommu_dma_supported(struct device *dev, u64 mask)
@@ -662,11 +766,11 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
 {
        struct iommu_dma_cookie *cookie = domain->iova_cookie;
        struct iommu_dma_msi_page *msi_page;
-       struct iova_domain *iovad = &cookie->iovad;
-       struct iova *iova;
+       dma_addr_t iova;
        int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+       size_t size = cookie_msi_granule(cookie);
 
-       msi_addr &= ~(phys_addr_t)iova_mask(iovad);
+       msi_addr &= ~(phys_addr_t)(size - 1);
        list_for_each_entry(msi_page, &cookie->msi_page_list, list)
                if (msi_page->phys == msi_addr)
                        return msi_page;
@@ -675,21 +779,16 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
        if (!msi_page)
                return NULL;
 
-       iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
-       if (!iova)
+       iova = __iommu_dma_map(dev, msi_addr, size, prot);
+       if (iommu_dma_mapping_error(dev, iova))
                goto out_free_page;
 
-       msi_page->phys = msi_addr;
-       msi_page->iova = iova_dma_addr(iovad, iova);
-       if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
-               goto out_free_iova;
-
        INIT_LIST_HEAD(&msi_page->list);
+       msi_page->phys = msi_addr;
+       msi_page->iova = iova;
        list_add(&msi_page->list, &cookie->msi_page_list);
        return msi_page;
 
-out_free_iova:
-       __free_iova(iovad, iova);
 out_free_page:
        kfree(msi_page);
        return NULL;
@@ -730,7 +829,7 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
                msg->data = ~0U;
        } else {
                msg->address_hi = upper_32_bits(msi_page->iova);
-               msg->address_lo &= iova_mask(&cookie->iovad);
+               msg->address_lo &= cookie_msi_granule(cookie) - 1;
                msg->address_lo += lower_32_bits(msi_page->iova);
        }
 }