Skip to content

Commit

Permalink
Merge tag 'dma-mapping-5.7' of git://git.infradead.org/users/hch/dma-…
Browse files Browse the repository at this point in the history
…mapping

Pull dma-mapping updates from Christoph Hellwig:

 - fix an integer overflow in the coherent pool (Kevin Grandemange)

 - provide support for in-place uncached remapping and use that for
   openrisc

 - fix the arm coherent allocator to take the bus limit into account

* tag 'dma-mapping-5.7' of git://git.infradead.org/users/hch/dma-mapping:
  ARM/dma-mapping: merge __dma_supported into arm_dma_supported
  ARM/dma-mapping: take the bus limit into account in __dma_alloc
  ARM/dma-mapping: remove get_coherent_dma_mask
  openrisc: use the generic in-place uncached DMA allocator
  dma-direct: provide a arch_dma_clear_uncached hook
  dma-direct: make uncached_kernel_address more general
  dma-direct: consolidate the error handling in dma_direct_alloc_pages
  dma-direct: remove the cached_kernel_address hook
  dma-coherent: fix integer overflow in the reserved-memory dma allocation
  • Loading branch information
torvalds committed Apr 4, 2020
2 parents 1e396a5 + fd27a52 commit 6f43bae
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 166 deletions.
15 changes: 11 additions & 4 deletions arch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,18 @@ config ARCH_HAS_SET_DIRECT_MAP
bool

#
# Select if arch has an uncached kernel segment and provides the
# uncached_kernel_address / cached_kernel_address symbols to use it
# Select if the architecture provides the arch_dma_set_uncached symbol to
# either provide an uncached segement alias for a DMA allocation, or
# to remap the page tables in place.
#
config ARCH_HAS_UNCACHED_SEGMENT
select ARCH_HAS_DMA_PREP_COHERENT
config ARCH_HAS_DMA_SET_UNCACHED
bool

#
# Select if the architectures provides the arch_dma_clear_uncached symbol
# to undo an in-place page table remap for uncached access.
#
config ARCH_HAS_DMA_CLEAR_UNCACHED
bool

# Select if arch init_task must go in the __init_task_data section
Expand Down
2 changes: 0 additions & 2 deletions arch/arm/include/asm/dma-iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,5 @@ int arm_iommu_attach_device(struct device *dev,
struct dma_iommu_mapping *mapping);
void arm_iommu_detach_device(struct device *dev);

int arm_dma_supported(struct device *dev, u64 mask);

#endif /* __KERNEL__ */
#endif
76 changes: 18 additions & 58 deletions arch/arm/mm/dma-mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,23 @@ static void arm_dma_sync_single_for_device(struct device *dev,
__dma_page_cpu_to_dev(page, offset, size, dir);
}

/*
* Return whether the given device DMA address mask can be supported
* properly. For example, if your device can only drive the low 24-bits
* during bus mastering, then you would pass 0x00ffffff as the mask
* to this function.
*/
static int arm_dma_supported(struct device *dev, u64 mask)
{
unsigned long max_dma_pfn = min(max_pfn - 1, arm_dma_pfn_limit);

/*
* Translate the device's DMA mask to a PFN limit. This
* PFN number includes the page which we can DMA to.
*/
return dma_to_pfn(dev, mask) >= max_dma_pfn;
}

const struct dma_map_ops arm_dma_ops = {
.alloc = arm_dma_alloc,
.free = arm_dma_free,
Expand Down Expand Up @@ -219,49 +236,6 @@ const struct dma_map_ops arm_coherent_dma_ops = {
};
EXPORT_SYMBOL(arm_coherent_dma_ops);

static int __dma_supported(struct device *dev, u64 mask, bool warn)
{
unsigned long max_dma_pfn = min(max_pfn - 1, arm_dma_pfn_limit);

/*
* Translate the device's DMA mask to a PFN limit. This
* PFN number includes the page which we can DMA to.
*/
if (dma_to_pfn(dev, mask) < max_dma_pfn) {
if (warn)
dev_warn(dev, "Coherent DMA mask %#llx (pfn %#lx-%#lx) covers a smaller range of system memory than the DMA zone pfn 0x0-%#lx\n",
mask,
dma_to_pfn(dev, 0), dma_to_pfn(dev, mask) + 1,
max_dma_pfn + 1);
return 0;
}

return 1;
}

static u64 get_coherent_dma_mask(struct device *dev)
{
u64 mask = (u64)DMA_BIT_MASK(32);

if (dev) {
mask = dev->coherent_dma_mask;

/*
* Sanity check the DMA mask - it must be non-zero, and
* must be able to be satisfied by a DMA allocation.
*/
if (mask == 0) {
dev_warn(dev, "coherent DMA mask is unset\n");
return 0;
}

if (!__dma_supported(dev, mask, true))
return 0;
}

return mask;
}

static void __dma_clear_buffer(struct page *page, size_t size, int coherent_flag)
{
/*
Expand Down Expand Up @@ -688,7 +662,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
gfp_t gfp, pgprot_t prot, bool is_coherent,
unsigned long attrs, const void *caller)
{
u64 mask = get_coherent_dma_mask(dev);
u64 mask = min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
struct page *page = NULL;
void *addr;
bool allowblock, cma;
Expand All @@ -712,9 +686,6 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
}
#endif

if (!mask)
return NULL;

buf = kzalloc(sizeof(*buf),
gfp & ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM));
if (!buf)
Expand Down Expand Up @@ -1087,17 +1058,6 @@ void arm_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
dir);
}

/*
* Return whether the given device DMA address mask can be supported
* properly. For example, if your device can only drive the low 24-bits
* during bus mastering, then you would pass 0x00ffffff as the mask
* to this function.
*/
int arm_dma_supported(struct device *dev, u64 mask)
{
return __dma_supported(dev, mask, false);
}

static const struct dma_map_ops *arm_get_dma_map_ops(bool coherent)
{
/*
Expand Down
2 changes: 1 addition & 1 deletion arch/microblaze/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ config MICROBLAZE
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_HAS_SYNC_DMA_FOR_CPU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
select ARCH_HAS_UNCACHED_SEGMENT if !MMU
select ARCH_HAS_DMA_SET_UNCACHED if !MMU
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_WANT_IPC_PARSE_VERSION
select BUILDTIME_TABLE_SORT
Expand Down
9 changes: 1 addition & 8 deletions arch/microblaze/mm/consistent.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void arch_dma_prep_coherent(struct page *page, size_t size)
#define UNCACHED_SHADOW_MASK 0
#endif /* CONFIG_XILINX_UNCACHED_SHADOW */

void *uncached_kernel_address(void *ptr)
void *arch_dma_set_uncached(void *ptr, size_t size)
{
unsigned long addr = (unsigned long)ptr;

Expand All @@ -49,11 +49,4 @@ void *uncached_kernel_address(void *ptr)
pr_warn("ERROR: Your cache coherent area is CACHED!!!\n");
return (void *)addr;
}

void *cached_kernel_address(void *ptr)
{
unsigned long addr = (unsigned long)ptr;

return (void *)(addr & ~UNCACHED_SHADOW_MASK);
}
#endif /* CONFIG_MMU */
3 changes: 2 additions & 1 deletion arch/mips/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1192,8 +1192,9 @@ config DMA_NONCOHERENT
# significant advantages.
#
select ARCH_HAS_DMA_WRITE_COMBINE
select ARCH_HAS_DMA_PREP_COHERENT
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
select ARCH_HAS_UNCACHED_SEGMENT
select ARCH_HAS_DMA_SET_UNCACHED
select DMA_NONCOHERENT_MMAP
select DMA_NONCOHERENT_CACHE_SYNC
select NEED_DMA_MAP_STATE
Expand Down
7 changes: 1 addition & 6 deletions arch/mips/mm/dma-noncoherent.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,11 @@ void arch_dma_prep_coherent(struct page *page, size_t size)
dma_cache_wback_inv((unsigned long)page_address(page), size);
}

void *uncached_kernel_address(void *addr)
void *arch_dma_set_uncached(void *addr, size_t size)
{
return (void *)(__pa(addr) + UNCAC_BASE);
}

void *cached_kernel_address(void *addr)
{
return __va(addr) - UNCAC_BASE;
}

static inline void dma_sync_virt(void *addr, size_t size,
enum dma_data_direction dir)
{
Expand Down
3 changes: 2 additions & 1 deletion arch/nios2/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
config NIOS2
def_bool y
select ARCH_32BIT_OFF_T
select ARCH_HAS_DMA_PREP_COHERENT
select ARCH_HAS_SYNC_DMA_FOR_CPU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
select ARCH_HAS_UNCACHED_SEGMENT
select ARCH_HAS_DMA_SET_UNCACHED
select ARCH_NO_SWAP
select TIMER_OF
select GENERIC_ATOMIC64
Expand Down
12 changes: 1 addition & 11 deletions arch/nios2/mm/dma-mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,11 @@ void arch_dma_prep_coherent(struct page *page, size_t size)
flush_dcache_range(start, start + size);
}

void *uncached_kernel_address(void *ptr)
void *arch_dma_set_uncached(void *ptr, size_t size)
{
unsigned long addr = (unsigned long)ptr;

addr |= CONFIG_NIOS2_IO_REGION_BASE;

return (void *)ptr;
}

void *cached_kernel_address(void *ptr)
{
unsigned long addr = (unsigned long)ptr;

addr &= ~CONFIG_NIOS2_IO_REGION_BASE;
addr |= CONFIG_NIOS2_KERNEL_REGION_BASE;

return (void *)ptr;
}
2 changes: 2 additions & 0 deletions arch/openrisc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
config OPENRISC
def_bool y
select ARCH_32BIT_OFF_T
select ARCH_HAS_DMA_SET_UNCACHED
select ARCH_HAS_DMA_CLEAR_UNCACHED
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
select OF
select OF_EARLY_FLATTREE
Expand Down
55 changes: 10 additions & 45 deletions arch/openrisc/kernel/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
* Copyright (C) 2010-2011 Jonas Bonn <[email protected]>
*
* DMA mapping callbacks...
* As alloc_coherent is the only DMA callback being used currently, that's
* the only thing implemented properly. The rest need looking into...
*/

#include <linux/dma-noncoherent.h>
Expand Down Expand Up @@ -67,62 +65,29 @@ static const struct mm_walk_ops clear_nocache_walk_ops = {
.pte_entry = page_clear_nocache,
};

/*
* Alloc "coherent" memory, which for OpenRISC means simply uncached.
*
* This function effectively just calls __get_free_pages, sets the
* cache-inhibit bit on those pages, and makes sure that the pages are
* flushed out of the cache before they are used.
*
* If the NON_CONSISTENT attribute is set, then this function just
* returns "normal", cachable memory.
*
* There are additional flags WEAK_ORDERING and WRITE_COMBINE to take
* into consideration here, too. All current known implementations of
* the OR1K support only strongly ordered memory accesses, so that flag
* is being ignored for now; uncached but write-combined memory is a
* missing feature of the OR1K.
*/
void *
arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp, unsigned long attrs)
void *arch_dma_set_uncached(void *cpu_addr, size_t size)
{
unsigned long va;
void *page;

page = alloc_pages_exact(size, gfp | __GFP_ZERO);
if (!page)
return NULL;

/* This gives us the real physical address of the first page. */
*dma_handle = __pa(page);

va = (unsigned long)page;
unsigned long va = (unsigned long)cpu_addr;
int error;

/*
* We need to iterate through the pages, clearing the dcache for
* them and setting the cache-inhibit bit.
*/
if (walk_page_range(&init_mm, va, va + size, &set_nocache_walk_ops,
NULL)) {
free_pages_exact(page, size);
return NULL;
}

return (void *)va;
error = walk_page_range(&init_mm, va, va + size, &set_nocache_walk_ops,
NULL);
if (error)
return ERR_PTR(error);
return cpu_addr;
}

void
arch_dma_free(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, unsigned long attrs)
void arch_dma_clear_uncached(void *cpu_addr, size_t size)
{
unsigned long va = (unsigned long)vaddr;
unsigned long va = (unsigned long)cpu_addr;

/* walk_page_range shouldn't be able to fail here */
WARN_ON(walk_page_range(&init_mm, va, va + size,
&clear_nocache_walk_ops, NULL));

free_pages_exact(vaddr, size);
}

void arch_sync_dma_for_device(phys_addr_t addr, size_t size,
Expand Down
2 changes: 1 addition & 1 deletion arch/xtensa/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ config XTENSA
select ARCH_HAS_DMA_PREP_COHERENT if MMU
select ARCH_HAS_SYNC_DMA_FOR_CPU if MMU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE if MMU
select ARCH_HAS_UNCACHED_SEGMENT if MMU
select ARCH_HAS_DMA_SET_UNCACHED if MMU
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_USE_QUEUED_SPINLOCKS
select ARCH_WANT_FRAME_POINTERS
Expand Down
12 changes: 3 additions & 9 deletions arch/xtensa/kernel/pci-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,12 @@ void arch_dma_prep_coherent(struct page *page, size_t size)

/*
* Memory caching is platform-dependent in noMMU xtensa configurations.
* The following two functions should be implemented in platform code
* in order to enable coherent DMA memory operations when CONFIG_MMU is not
* enabled.
* This function should be implemented in platform code in order to enable
* coherent DMA memory operations when CONFIG_MMU is not enabled.
*/
#ifdef CONFIG_MMU
void *uncached_kernel_address(void *p)
void *arch_dma_set_uncached(void *p, size_t size)
{
return p + XCHAL_KSEG_BYPASS_VADDR - XCHAL_KSEG_CACHED_VADDR;
}

void *cached_kernel_address(void *p)
{
return p + XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
}
#endif /* CONFIG_MMU */
4 changes: 2 additions & 2 deletions include/linux/dma-noncoherent.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ static inline void arch_dma_prep_coherent(struct page *page, size_t size)
}
#endif /* CONFIG_ARCH_HAS_DMA_PREP_COHERENT */

void *uncached_kernel_address(void *addr);
void *cached_kernel_address(void *addr);
void *arch_dma_set_uncached(void *addr, size_t size);
void arch_dma_clear_uncached(void *addr, size_t size);

#endif /* _LINUX_DMA_NONCOHERENT_H */
Loading

0 comments on commit 6f43bae

Please sign in to comment.