]> www.infradead.org Git - users/hch/misc.git/commitdiff
iommu: generalize the batched sync after map interface
authorChristoph Hellwig <hch@lst.de>
Mon, 5 May 2025 07:01:40 +0000 (10:01 +0300)
committerMarek Szyprowski <m.szyprowski@samsung.com>
Tue, 6 May 2025 06:36:53 +0000 (08:36 +0200)
For the upcoming IOVA-based DMA API we want to batch the
ops->iotlb_sync_map() call after mapping multiple IOVAs from
dma-iommu without having a scatterlist. Improve the API.

Add a wrapper for the map_sync as iommu_sync_map() so that callers
don't need to poke into the methods directly.

Formalize __iommu_map() into iommu_map_nosync() which requires the
caller to call iommu_sync_map() after all maps are completed.

Refactor the existing sanity checks from all the different layers
into iommu_map_nosync().

Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Will Deacon <will@kernel.org>
Tested-by: Jens Axboe <axboe@kernel.dk>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
drivers/iommu/iommu.c
include/linux/iommu.h

index c8033ca6637771a3f3f1497775b7993921b5bf97..3dc47f62d9ff1ed12633a19423cf203114ce26d6 100644 (file)
@@ -2440,8 +2440,8 @@ out_set_count:
        return pgsize;
 }
 
-static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
-                      phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova,
+               phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
        const struct iommu_domain_ops *ops = domain->ops;
        unsigned long orig_iova = iova;
@@ -2450,12 +2450,19 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
        phys_addr_t orig_paddr = paddr;
        int ret = 0;
 
+       might_sleep_if(gfpflags_allow_blocking(gfp));
+
        if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING)))
                return -EINVAL;
 
        if (WARN_ON(!ops->map_pages || domain->pgsize_bitmap == 0UL))
                return -ENODEV;
 
+       /* Discourage passing strange GFP flags */
+       if (WARN_ON_ONCE(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 |
+                               __GFP_HIGHMEM)))
+               return -EINVAL;
+
        /* find out the minimum page size supported */
        min_pagesz = 1 << __ffs(domain->pgsize_bitmap);
 
@@ -2503,31 +2510,27 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
        return ret;
 }
 
-int iommu_map(struct iommu_domain *domain, unsigned long iova,
-             phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+int iommu_sync_map(struct iommu_domain *domain, unsigned long iova, size_t size)
 {
        const struct iommu_domain_ops *ops = domain->ops;
-       int ret;
-
-       might_sleep_if(gfpflags_allow_blocking(gfp));
 
-       /* Discourage passing strange GFP flags */
-       if (WARN_ON_ONCE(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 |
-                               __GFP_HIGHMEM)))
-               return -EINVAL;
+       if (!ops->iotlb_sync_map)
+               return 0;
+       return ops->iotlb_sync_map(domain, iova, size);
+}
 
-       ret = __iommu_map(domain, iova, paddr, size, prot, gfp);
-       if (ret == 0 && ops->iotlb_sync_map) {
-               ret = ops->iotlb_sync_map(domain, iova, size);
-               if (ret)
-                       goto out_err;
-       }
+int iommu_map(struct iommu_domain *domain, unsigned long iova,
+             phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+{
+       int ret;
 
-       return ret;
+       ret = iommu_map_nosync(domain, iova, paddr, size, prot, gfp);
+       if (ret)
+               return ret;
 
-out_err:
-       /* undo mappings already done */
-       iommu_unmap(domain, iova, size);
+       ret = iommu_sync_map(domain, iova, size);
+       if (ret)
+               iommu_unmap(domain, iova, size);
 
        return ret;
 }
@@ -2627,26 +2630,17 @@ ssize_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
                     struct scatterlist *sg, unsigned int nents, int prot,
                     gfp_t gfp)
 {
-       const struct iommu_domain_ops *ops = domain->ops;
        size_t len = 0, mapped = 0;
        phys_addr_t start;
        unsigned int i = 0;
        int ret;
 
-       might_sleep_if(gfpflags_allow_blocking(gfp));
-
-       /* Discourage passing strange GFP flags */
-       if (WARN_ON_ONCE(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 |
-                               __GFP_HIGHMEM)))
-               return -EINVAL;
-
        while (i <= nents) {
                phys_addr_t s_phys = sg_phys(sg);
 
                if (len && s_phys != start + len) {
-                       ret = __iommu_map(domain, iova + mapped, start,
+                       ret = iommu_map_nosync(domain, iova + mapped, start,
                                        len, prot, gfp);
-
                        if (ret)
                                goto out_err;
 
@@ -2669,11 +2663,10 @@ next:
                        sg = sg_next(sg);
        }
 
-       if (ops->iotlb_sync_map) {
-               ret = ops->iotlb_sync_map(domain, iova, mapped);
-               if (ret)
-                       goto out_err;
-       }
+       ret = iommu_sync_map(domain, iova, mapped);
+       if (ret)
+               goto out_err;
+
        return mapped;
 
 out_err:
index ccce8a751e2a55512fbf50437fcd3b07087323cd..ce472af8e9c3996f37467e36f5c8452499b5a5a7 100644 (file)
@@ -872,6 +872,10 @@ extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
 extern struct iommu_domain *iommu_get_dma_domain(struct device *dev);
 extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
                     phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
+int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova,
+               phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
+int iommu_sync_map(struct iommu_domain *domain, unsigned long iova,
+               size_t size);
 extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
                          size_t size);
 extern size_t iommu_unmap_fast(struct iommu_domain *domain,