This way allocations like the NVMe HMB don't consume iomap space

Signed-off-by: Christoph Hellwig <h...@lst.de>
---

Note: compile tested only, I stumbled over this when researching dma api
quirks for HMB support.

 arch/arc/mm/dma.c | 42 +++++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c
index 2a07e6ecafbd..d8999ac88879 100644
--- a/arch/arc/mm/dma.c
+++ b/arch/arc/mm/dma.c
@@ -28,13 +28,22 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
        struct page *page;
        phys_addr_t paddr;
        void *kvaddr;
-       int need_coh = 1, need_kvaddr = 0;
+       bool need_cache_sync = true, need_kvaddr = false;
 
        page = alloc_pages(gfp, order);
        if (!page)
                return NULL;
 
        /*
+        * No-kernel mapping memoery doesn't need a kernel virtual address.
+        * But we do the initial cache flush to make sure we don't write back
+        * to from a previous mapping into the now device owned memory.
+        */
+       if (attrs & DMA_ATTR_NO_KERNEL_MAPPING) {
+               need_cache_sync = true;
+               need_kvaddr = false;
+
+       /*
         * IOC relies on all data (even coherent DMA data) being in cache
         * Thus allocate normal cached memory
         *
@@ -45,17 +54,19 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
         *   -For coherent data, Read/Write to buffers terminate early in cache
         *   (vs. always going to memory - thus are faster)
         */
-       if ((is_isa_arcv2() && ioc_enable) ||
-           (attrs & DMA_ATTR_NON_CONSISTENT))
-               need_coh = 0;
+       } else if ((is_isa_arcv2() && ioc_enable) ||
+                  (attrs & DMA_ATTR_NON_CONSISTENT)) {
+               need_cache_sync = false;
+               need_kvaddr = true;
 
        /*
         * - A coherent buffer needs MMU mapping to enforce non-cachability
         * - A highmem page needs a virtual handle (hence MMU mapping)
         *   independent of cachability
         */
-       if (PageHighMem(page) || need_coh)
-               need_kvaddr = 1;
+       } else if (PageHighMem(page)) {
+               need_kvaddr = true;
+       }
 
        /* This is linear addr (0x8000_0000 based) */
        paddr = page_to_phys(page);
@@ -83,7 +94,7 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
         * Currently flush_cache_vmap nukes the L1 cache completely which
         * will be optimized as a separate commit
         */
-       if (need_coh)
+       if (need_cache_sync)
                dma_cache_wback_inv(paddr, size);
 
        return kvaddr;
@@ -93,14 +104,19 @@ static void arc_dma_free(struct device *dev, size_t size, 
void *vaddr,
                dma_addr_t dma_handle, unsigned long attrs)
 {
        phys_addr_t paddr = plat_dma_to_phys(dev, dma_handle);
-       struct page *page = virt_to_page(paddr);
-       int is_non_coh = 1;
 
-       is_non_coh = (attrs & DMA_ATTR_NON_CONSISTENT) ||
-                       (is_isa_arcv2() && ioc_enable);
+       if (!(attrs & DMA_ATTR_NO_KERNEL_MAPPING)) {
+               struct page *page = virt_to_page(paddr);
+               bool need_iounmap = true;
+
+               if (!PageHighMem(page) &&
+                   ((is_isa_arcv2() && ioc_enable) ||
+                    (attrs & DMA_ATTR_NON_CONSISTENT)))
+                       need_iounmap = false;
 
-       if (PageHighMem(page) || !is_non_coh)
-               iounmap((void __force __iomem *)vaddr);
+               if (need_iounmap)
+                       iounmap((void __force __iomem *)vaddr);
+       }
 
        __free_pages(page, get_order(size));
 }
-- 
2.11.0

Reply via email to