]> www.infradead.org Git - users/hch/block.git/commitdiff
dma-mapping: provide an interface to allocate IOVA
authorLeon Romanovsky <leonro@nvidia.com>
Mon, 29 Jan 2024 11:28:54 +0000 (13:28 +0200)
committerLeon Romanovsky <leon@kernel.org>
Thu, 3 Oct 2024 16:05:52 +0000 (19:05 +0300)
Existing .map_page() callback provides two things at the same time:
allocates IOVA and links DMA pages. That combination works great for
most of the callers who use it in control paths, but less effective
in fast paths.

These advanced callers already manage their data in some sort of
database and can perform IOVA allocation in advance, leaving range
linkage operation to be in fast path.

Provide an interface to allocate/deallocate IOVA and next patch
link/unlink DMA ranges to that specific IOVA.

Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
include/linux/dma-mapping.h
kernel/dma/mapping.c

index f391c3a6d4a4624ced003c7259d3fe5012de961e..2442bd8b9c4ce0c09413bd75d33f7e7f3b79adb2 100644 (file)
@@ -118,6 +118,10 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
        return 0;
 }
 
+int dma_alloc_iova_unaligned(struct dma_iova_state *state, phys_addr_t phys,
+                            size_t size);
+void dma_free_iova(struct dma_iova_state *state);
+
 dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
                size_t offset, size_t size, enum dma_data_direction dir,
                unsigned long attrs);
@@ -167,6 +171,14 @@ void dma_vunmap_noncontiguous(struct device *dev, void *vaddr);
 int dma_mmap_noncontiguous(struct device *dev, struct vm_area_struct *vma,
                size_t size, struct sg_table *sgt);
 #else /* CONFIG_HAS_DMA */
+static inline int dma_alloc_iova_unaligned(struct dma_iova_state *state,
+                                          phys_addr_t phys, size_t size)
+{
+       return -EOPNOTSUPP;
+}
+static inline void dma_free_iova(struct dma_iova_state *state)
+{
+}
 static inline dma_addr_t dma_map_page_attrs(struct device *dev,
                struct page *page, size_t offset, size_t size,
                enum dma_data_direction dir, unsigned long attrs)
@@ -373,6 +385,10 @@ static inline bool dma_need_sync(struct device *dev, dma_addr_t dma_addr)
        return false;
 }
 #endif /* !CONFIG_HAS_DMA || !CONFIG_DMA_NEED_SYNC */
+static inline int dma_alloc_iova(struct dma_iova_state *state, size_t size)
+{
+       return dma_alloc_iova_unaligned(state, 0, size);
+}
 
 struct page *dma_alloc_pages(struct device *dev, size_t size,
                dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp);
index 864a1121bf086d0d7e19dec90f9f70b10d03ceb9..b7a15431f203e0145babc5922aa9bc00b4dd8e96 100644 (file)
@@ -962,3 +962,38 @@ unsigned long dma_get_merge_boundary(struct device *dev)
        return ops->get_merge_boundary(dev);
 }
 EXPORT_SYMBOL_GPL(dma_get_merge_boundary);
+
+/**
+ * dma_alloc_iova_unaligned - Allocate an IOVA space
+ * @state: IOVA state
+ * @phys: physical address
+ * @size: IOVA size
+ *
+ * Allocate an IOVA space for the given IOVA state and size. The IOVA space
+ * is allocated to the worst case when whole range is going to be used.
+ */
+int dma_alloc_iova_unaligned(struct dma_iova_state *state, phys_addr_t phys,
+                            size_t size)
+{
+       if (!use_dma_iommu(state->dev))
+               return 0;
+
+       WARN_ON_ONCE(!size);
+       return iommu_dma_alloc_iova(state, phys, size);
+}
+EXPORT_SYMBOL_GPL(dma_alloc_iova_unaligned);
+
+/**
+ * dma_free_iova - Free an IOVA space
+ * @state: IOVA state
+ *
+ * Free an IOVA space for the given IOVA attributes.
+ */
+void dma_free_iova(struct dma_iova_state *state)
+{
+       if (!use_dma_iommu(state->dev))
+               return;
+
+       iommu_dma_free_iova(state);
+}
+EXPORT_SYMBOL_GPL(dma_free_iova);