dma-direct: provide mmap and get_sgtable method overrides
authorChristoph Hellwig <hch@lst.de>
Tue, 29 Oct 2019 10:01:37 +0000 (11:01 +0100)
committerChristoph Hellwig <hch@lst.de>
Mon, 11 Nov 2019 09:52:15 +0000 (10:52 +0100)
For dma-direct we know that the DMA address is an encoding of the
physical address that we can trivially decode.  Use that fact to
provide implementations that do not need the arch_dma_coherent_to_pfn
architecture hook.  Note that we still can only support mmap of
non-coherent memory only if the architecture provides a way to set an
uncached bit in the page tables.  This must be true for architectures
that use the generic remap helpers, but other architectures can also
manually select it.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Max Filippov <jcmvbkbc@gmail.com>
16 files changed:
arch/arc/Kconfig
arch/arm/Kconfig
arch/arm/mm/dma-mapping.c
arch/arm64/Kconfig
arch/ia64/Kconfig
arch/ia64/kernel/dma-mapping.c
arch/microblaze/Kconfig
arch/mips/Kconfig
arch/mips/mm/dma-noncoherent.c
arch/powerpc/platforms/Kconfig.cputype
include/linux/dma-direct.h
include/linux/dma-noncoherent.h
kernel/dma/Kconfig
kernel/dma/direct.c
kernel/dma/mapping.c
kernel/dma/remap.c

index 8383155c8c824f486c2cadd7e46178ca627bfe5a..4d7b671c8ff4aca60392c8913e9f882a4a4ded86 100644 (file)
@@ -6,7 +6,6 @@
 config ARC
        def_bool y
        select ARC_TIMERS
-       select ARCH_HAS_DMA_COHERENT_TO_PFN
        select ARCH_HAS_DMA_PREP_COHERENT
        select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_SETUP_DMA_OPS
index 8a50efb559f35a2c75f6fa379f34e4580e6c5a8d..80e795aacd3a734367bf84f417f3f206680cc5da 100644 (file)
@@ -7,7 +7,6 @@ config ARM
        select ARCH_HAS_BINFMT_FLAT
        select ARCH_HAS_DEBUG_VIRTUAL if MMU
        select ARCH_HAS_DEVMEM_IS_ALLOWED
-       select ARCH_HAS_DMA_COHERENT_TO_PFN if SWIOTLB
        select ARCH_HAS_DMA_WRITE_COMBINE if !ARM_DMA_MEM_BUFFERABLE
        select ARCH_HAS_ELF_RANDOMIZE
        select ARCH_HAS_FORTIFY_SOURCE
index 7d042d5c43e32f117e3825e39d4c06ecd00b24a3..f3cbeba7f9cb55bcfec58d5cc3534bdf8b556eca 100644 (file)
@@ -2346,12 +2346,6 @@ void arch_sync_dma_for_cpu(struct device *dev, phys_addr_t paddr,
                              size, dir);
 }
 
-long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr,
-               dma_addr_t dma_addr)
-{
-       return dma_to_pfn(dev, dma_addr);
-}
-
 void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
                gfp_t gfp, unsigned long attrs)
 {
index 3f047afb982c8d17ad9e9c41e04b35d1fc02cffe..57606307fe3422bcaebb7cf987c591f83d9a9bf8 100644 (file)
@@ -12,7 +12,6 @@ config ARM64
        select ARCH_CLOCKSOURCE_DATA
        select ARCH_HAS_DEBUG_VIRTUAL
        select ARCH_HAS_DEVMEM_IS_ALLOWED
-       select ARCH_HAS_DMA_COHERENT_TO_PFN
        select ARCH_HAS_DMA_PREP_COHERENT
        select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
        select ARCH_HAS_FAST_MULTIPLIER
index 16714477eef429847cf5a57da5180f83b0167d44..bab7cd878464d7f0f78badcb61ecb3934ea152db 100644 (file)
@@ -33,7 +33,7 @@ config IA64
        select HAVE_ARCH_TRACEHOOK
        select HAVE_MEMBLOCK_NODE_MAP
        select HAVE_VIRT_CPU_ACCOUNTING
-       select ARCH_HAS_DMA_COHERENT_TO_PFN
+       select DMA_NONCOHERENT_MMAP
        select ARCH_HAS_SYNC_DMA_FOR_CPU
        select VIRT_TO_BUS
        select GENERIC_IRQ_PROBE
index 4a3262795890443625712445b0a797d21b35f438..09ef9ce9988d1fa218864d8e0c1193c75a38ba72 100644 (file)
@@ -19,9 +19,3 @@ void arch_dma_free(struct device *dev, size_t size, void *cpu_addr,
 {
        dma_direct_free_pages(dev, size, cpu_addr, dma_addr, attrs);
 }
-
-long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr,
-               dma_addr_t dma_addr)
-{
-       return page_to_pfn(virt_to_page(cpu_addr));
-}
index c9c4be822456b4320800571e11dba5023ab67bf1..261c26df1c9ff19df521ea23ec3fed93048a4ffa 100644 (file)
@@ -4,7 +4,6 @@ config MICROBLAZE
        select ARCH_32BIT_OFF_T
        select ARCH_NO_SWAP
        select ARCH_HAS_BINFMT_FLAT if !MMU
-       select ARCH_HAS_DMA_COHERENT_TO_PFN if MMU
        select ARCH_HAS_DMA_PREP_COHERENT
        select ARCH_HAS_GCOV_PROFILE_ALL
        select ARCH_HAS_SYNC_DMA_FOR_CPU
index a0bd9bdb5f8302ae19e52a0eb7ad1641eca2f5d1..248d39b8a1608c9cf5dd137a524b064026defd1d 100644 (file)
@@ -1134,9 +1134,9 @@ config DMA_NONCOHERENT
        select ARCH_HAS_DMA_WRITE_COMBINE
        select ARCH_HAS_SYNC_DMA_FOR_DEVICE
        select ARCH_HAS_UNCACHED_SEGMENT
-       select NEED_DMA_MAP_STATE
-       select ARCH_HAS_DMA_COHERENT_TO_PFN
+       select DMA_NONCOHERENT_MMAP
        select DMA_NONCOHERENT_CACHE_SYNC
+       select NEED_DMA_MAP_STATE
 
 config SYS_HAS_EARLY_PRINTK
        bool
index 1d4d57dd9acf8ccff46780daa3107a16b1aad120..fcf6d3eaac6678b6abd3ef9b769dd81fcc8092c8 100644 (file)
@@ -59,12 +59,6 @@ void *cached_kernel_address(void *addr)
        return __va(addr) - UNCAC_BASE;
 }
 
-long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr,
-               dma_addr_t dma_addr)
-{
-       return page_to_pfn(virt_to_page(cached_kernel_address(cpu_addr)));
-}
-
 static inline void dma_sync_virt(void *addr, size_t size,
                enum dma_data_direction dir)
 {
index 12543e53fa96a507236ccad32f978ed20661bfa4..303752f97c19dc8689b9cda97ec6fd8adb35aafd 100644 (file)
@@ -459,7 +459,6 @@ config NOT_COHERENT_CACHE
        bool
        depends on 4xx || PPC_8xx || E200 || PPC_MPC512x || \
                GAMECUBE_COMMON || AMIGAONE
-       select ARCH_HAS_DMA_COHERENT_TO_PFN
        select ARCH_HAS_DMA_PREP_COHERENT
        select ARCH_HAS_SYNC_DMA_FOR_DEVICE
        select ARCH_HAS_SYNC_DMA_FOR_CPU
index ff3d5edc44b9fd6dd44918892eed5e9d9f7d3140..bcd953fb1f5a2eb26a77ffb58a78ca744902d350 100644 (file)
@@ -68,5 +68,12 @@ void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
                dma_addr_t dma_addr, unsigned long attrs);
 struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
                gfp_t gfp, unsigned long attrs);
+int dma_direct_get_sgtable(struct device *dev, struct sg_table *sgt,
+               void *cpu_addr, dma_addr_t dma_addr, size_t size,
+               unsigned long attrs);
+bool dma_direct_can_mmap(struct device *dev);
+int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
+               void *cpu_addr, dma_addr_t dma_addr, size_t size,
+               unsigned long attrs);
 int dma_direct_supported(struct device *dev, u64 mask);
 #endif /* _LINUX_DMA_DIRECT_H */
index dd3de6d88fc0814670f0f3396ec02e4be78da409..e30fca1f1b129b22076ba156d0365c6c0276dbd7 100644 (file)
@@ -41,8 +41,6 @@ void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
                gfp_t gfp, unsigned long attrs);
 void arch_dma_free(struct device *dev, size_t size, void *cpu_addr,
                dma_addr_t dma_addr, unsigned long attrs);
-long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr,
-               dma_addr_t dma_addr);
 
 #ifdef CONFIG_MMU
 /*
index 73c5c2b8e82459c043fecaec865d45e084db47ac..4c103a24e380e400c2c36d3a9ed4c4a2e22ae049 100644 (file)
@@ -51,9 +51,6 @@ config ARCH_HAS_SYNC_DMA_FOR_CPU_ALL
 config ARCH_HAS_DMA_PREP_COHERENT
        bool
 
-config ARCH_HAS_DMA_COHERENT_TO_PFN
-       bool
-
 config ARCH_HAS_FORCE_DMA_UNENCRYPTED
        bool
 
@@ -68,9 +65,18 @@ config SWIOTLB
        bool
        select NEED_DMA_MAP_STATE
 
+#
+# Should be selected if we can mmap non-coherent mappings to userspace.
+# The only thing that is really required is a way to set an uncached bit
+# in the pagetables
+#
+config DMA_NONCOHERENT_MMAP
+       bool
+
 config DMA_REMAP
        depends on MMU
        select GENERIC_ALLOCATOR
+       select DMA_NONCOHERENT_MMAP
        bool
 
 config DMA_DIRECT_REMAP
index 724c282dd943c09ebc896f146d8d600814a1cd86..58beaa9ddd27bf5891046837fa817570de05324f 100644 (file)
@@ -43,6 +43,12 @@ static inline dma_addr_t phys_to_dma_direct(struct device *dev,
        return phys_to_dma(dev, phys);
 }
 
+static inline struct page *dma_direct_to_page(struct device *dev,
+               dma_addr_t dma_addr)
+{
+       return pfn_to_page(PHYS_PFN(dma_to_phys(dev, dma_addr)));
+}
+
 u64 dma_direct_get_required_mask(struct device *dev)
 {
        u64 max_dma = phys_to_dma_direct(dev, (max_pfn - 1) << PAGE_SHIFT);
@@ -379,6 +385,59 @@ dma_addr_t dma_direct_map_resource(struct device *dev, phys_addr_t paddr,
 }
 EXPORT_SYMBOL(dma_direct_map_resource);
 
+int dma_direct_get_sgtable(struct device *dev, struct sg_table *sgt,
+               void *cpu_addr, dma_addr_t dma_addr, size_t size,
+               unsigned long attrs)
+{
+       struct page *page = dma_direct_to_page(dev, dma_addr);
+       int ret;
+
+       ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+       if (!ret)
+               sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+       return ret;
+}
+
+#ifdef CONFIG_MMU
+bool dma_direct_can_mmap(struct device *dev)
+{
+       return dev_is_dma_coherent(dev) ||
+               IS_ENABLED(CONFIG_DMA_NONCOHERENT_MMAP);
+}
+
+int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
+               void *cpu_addr, dma_addr_t dma_addr, size_t size,
+               unsigned long attrs)
+{
+       unsigned long user_count = vma_pages(vma);
+       unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+       unsigned long pfn = PHYS_PFN(dma_to_phys(dev, dma_addr));
+       int ret = -ENXIO;
+
+       vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
+
+       if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
+               return ret;
+
+       if (vma->vm_pgoff >= count || user_count > count - vma->vm_pgoff)
+               return -ENXIO;
+       return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
+                       user_count << PAGE_SHIFT, vma->vm_page_prot);
+}
+#else /* CONFIG_MMU */
+bool dma_direct_can_mmap(struct device *dev)
+{
+       return false;
+}
+
+int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
+               void *cpu_addr, dma_addr_t dma_addr, size_t size,
+               unsigned long attrs)
+{
+       return -ENXIO;
+}
+#endif /* CONFIG_MMU */
+
 /*
  * Because 32-bit DMA masks are so common we expect every architecture to be
  * able to satisfy them - either by not supporting more physical memory, or by
index d9334f31a5afb08b9f0413e7df18d35f703f5a09..12ff766ec1fa35f20b1bff80c85593fe8342271d 100644 (file)
@@ -112,24 +112,9 @@ int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
                 void *cpu_addr, dma_addr_t dma_addr, size_t size,
                 unsigned long attrs)
 {
-       struct page *page;
+       struct page *page = virt_to_page(cpu_addr);
        int ret;
 
-       if (!dev_is_dma_coherent(dev)) {
-               unsigned long pfn;
-
-               if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_COHERENT_TO_PFN))
-                       return -ENXIO;
-
-               /* If the PFN is not valid, we do not have a struct page */
-               pfn = arch_dma_coherent_to_pfn(dev, cpu_addr, dma_addr);
-               if (!pfn_valid(pfn))
-                       return -ENXIO;
-               page = pfn_to_page(pfn);
-       } else {
-               page = virt_to_page(cpu_addr);
-       }
-
        ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
        if (!ret)
                sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
@@ -154,7 +139,7 @@ int dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt,
        const struct dma_map_ops *ops = get_dma_ops(dev);
 
        if (dma_is_direct(ops))
-               return dma_common_get_sgtable(dev, sgt, cpu_addr, dma_addr,
+               return dma_direct_get_sgtable(dev, sgt, cpu_addr, dma_addr,
                                size, attrs);
        if (!ops->get_sgtable)
                return -ENXIO;
@@ -192,7 +177,6 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
        unsigned long user_count = vma_pages(vma);
        unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
        unsigned long off = vma->vm_pgoff;
-       unsigned long pfn;
        int ret = -ENXIO;
 
        vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
@@ -203,19 +187,8 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
        if (off >= count || user_count > count - off)
                return -ENXIO;
 
-       if (!dev_is_dma_coherent(dev)) {
-               if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_COHERENT_TO_PFN))
-                       return -ENXIO;
-
-               /* If the PFN is not valid, we do not have a struct page */
-               pfn = arch_dma_coherent_to_pfn(dev, cpu_addr, dma_addr);
-               if (!pfn_valid(pfn))
-                       return -ENXIO;
-       } else {
-               pfn = page_to_pfn(virt_to_page(cpu_addr));
-       }
-
-       return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
+       return remap_pfn_range(vma, vma->vm_start,
+                       page_to_pfn(virt_to_page(cpu_addr)) + vma->vm_pgoff,
                        user_count << PAGE_SHIFT, vma->vm_page_prot);
 #else
        return -ENXIO;
@@ -233,12 +206,8 @@ bool dma_can_mmap(struct device *dev)
 {
        const struct dma_map_ops *ops = get_dma_ops(dev);
 
-       if (dma_is_direct(ops)) {
-               return IS_ENABLED(CONFIG_MMU) &&
-                      (dev_is_dma_coherent(dev) ||
-                       IS_ENABLED(CONFIG_ARCH_HAS_DMA_COHERENT_TO_PFN));
-       }
-
+       if (dma_is_direct(ops))
+               return dma_direct_can_mmap(dev);
        return ops->mmap != NULL;
 }
 EXPORT_SYMBOL_GPL(dma_can_mmap);
@@ -263,7 +232,7 @@ int dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
        const struct dma_map_ops *ops = get_dma_ops(dev);
 
        if (dma_is_direct(ops))
-               return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size,
+               return dma_direct_mmap(dev, vma, cpu_addr, dma_addr, size,
                                attrs);
        if (!ops->mmap)
                return -ENXIO;
index 90d5ce77c189c4a8f9a51991b7eb57355092cccc..3c49499ee6b08f9aa2c3328de73692ec03415b5a 100644 (file)
@@ -259,10 +259,4 @@ void arch_dma_free(struct device *dev, size_t size, void *vaddr,
                dma_free_contiguous(dev, page, size);
        }
 }
-
-long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr,
-               dma_addr_t dma_addr)
-{
-       return __phys_to_pfn(dma_to_phys(dev, dma_addr));
-}
 #endif /* CONFIG_DMA_DIRECT_REMAP */