]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
arm64: dma-mapping: map sg lists into the SMMU as virtually contiguous
authorMitchel Humpherys <mitchelh@codeaurora.org>
Tue, 28 Oct 2014 20:45:02 +0000 (13:45 -0700)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Fri, 13 Apr 2018 14:00:14 +0000 (16:00 +0200)
In arm_iommu_map_sg, currently we map each individual link in the given
scatterlist into the SMMU individually such that they may or may not be
virtually contiguous.  However, in most (all?) of our use cases we
actually want the entire sg list mapped into the SMMU as a single
contiguous range.  Use iommu_map_range to accomplish this.

Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
[Forward Ported this from msm3.14]
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
[added iommu_map_range in generic dma code]
Signed-off-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
arch/arm64/mm/dma-mapping.c

index 625a6754e181c0cb7a8b2b4c5e57cf0c56e10885..68e9911168ea0e4d85666540ce37ff76d3f134c8 100644 (file)
@@ -1563,6 +1563,39 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
        return __iommu_map_sg(dev, sg, nents, dir, attrs, true);
 }
 
+static int iommu_map_range(struct iommu_domain *domain, unsigned int iova,
+                   struct scatterlist *sg, unsigned int len, int opt)
+{
+       s32 ret = 0;
+       u32 offset = 0;
+       u32 start_iova = iova;
+
+       BUG_ON(iova & (~PAGE_MASK));
+
+       while (offset < len) {
+               phys_addr_t phys = page_to_phys(sg_page(sg));
+               u32 page_len = PAGE_ALIGN(sg->offset + sg->length);
+
+               ret = iommu_map(domain, iova, phys, page_len, opt);
+               if (ret)
+                       goto fail;
+
+               iova += page_len;
+               offset += page_len;
+               if (offset < len)
+                       sg = sg_next(sg);
+       }
+
+       goto out;
+
+fail:
+       /* undo mappings already done in case of error */
+       iommu_unmap(domain, start_iova, offset);
+out:
+
+       return ret;
+}
+
 /**
  * arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA
  * @dev: valid struct device pointer
@@ -1578,7 +1611,29 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
 int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
                int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
 {
-       return __iommu_map_sg(dev, sg, nents, dir, attrs, false);
+       struct scatterlist *s;
+       int ret, i;
+       struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+       unsigned int iova, total_length = 0, current_offset = 0;
+       int prot = __dma_direction_to_prot(dir);
+
+       for_each_sg(sg, s, nents, i)
+               total_length += s->length;
+
+       iova = __alloc_iova(mapping, total_length);
+       ret = iommu_map_range(mapping->domain, iova, sg, total_length, prot);
+       if (ret) {
+               __free_iova(mapping, iova, total_length);
+               return 0;
+       }
+
+       for_each_sg(sg, s, nents, i) {
+               s->dma_address = iova + current_offset;
+               s->dma_length = total_length - current_offset;
+               current_offset += s->length;
+       }
+
+       return nents;
 }
 
 static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
@@ -1628,7 +1683,15 @@ void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
 void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
                        enum dma_data_direction dir, struct dma_attrs *attrs)
 {
-       __iommu_unmap_sg(dev, sg, nents, dir, attrs, false);
+       struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+       unsigned int total_length = sg_dma_len(sg);
+       unsigned int iova = sg_dma_address(sg);
+
+       total_length = PAGE_ALIGN((iova & ~PAGE_MASK) + total_length);
+       iova &= PAGE_MASK;
+
+       iommu_unmap(mapping->domain, iova, total_length);
+       __free_iova(mapping, iova, total_length);
 }
 
 /**