fix "generic dma" implementation bugs
Subsystems (2)
PATCH 1/1 fix "generic dma" implementation bugs
This should fix some arch-specific problems with the "generic DMA" API implementations of dma_supported() and dma_set_mask():
- dma_set_mask():
* Removes pointless clones that were used on arm, i386, ia64, mips, and parisc
* Also removed PCI-specific "asm-generic" version
* Replaces them with a truly generic implementation that doesn't needlessly BUG() with non-PCI devices
* Adds an ARCH_HAS_* hook to cope with sparc64-only restrictions (for ali5451 pci audio hardware); sparc64 retains a BUG() for non-pci hardware.
- dma_supported():
* Removes broken versions from arm, i386, mips; the broken versions never actually compared the two masks they were specified to compare:
+ arm only verified there was a device mask
+ i386/mips didn't even do that much
* Also removed PCI-specific "asm-generic" version
* Replaces them with a generic implementation that
+ doesn't needlessly BUG() with non-PCI devices
+ actually compares the two masks (bit-wise)
* Adds an ARCH_HAS_* hook to handle ia64 and parisc, which already have their own fancy implementations
Sanity tested on x86/pc and ARM/PXA.
It's unfortunate that the arch-specific BUG() behavior only started getting reported recently (test7?); and particularly that the previous dma_supported() was so broken even on i386. In consequence, it's not quite clear what bugs are being hidden by the bogus masking.
- Dave
include/linux/dma-mapping.h +31 -0
@@ -12,6 +12,37 @@#include <asm/dma-mapping.h> +#ifndef ARCH_HAS_DMA_SUPPORTED +/* +* Returns true iff the device can support those DMA address bits on this +* system. For example: to see if it can do DMA with any 64 bit address +* you could pass ~(u64)0 as the mask; to test for 24 bit bus mastering +* address support, pass 0x00ffffff as the mask. +*/ +static inline int +dma_supported (struct device *dev,u64 mask) +{ +return dev && dev->dma_mask && (mask & ~*dev->dma_mask) ==0 ; +} +#endif + +#ifndef ARCH_HAS_DMA_SET_MASK +/* +* Changes a driver's DMA mask; this can sometimes have side-effects. +* Only the lowest level of the driver stack may use this call; +* virtualized devices (higher in the stack) must never use this, +* since they don't directly control the hardware or DMA. +*/ +static inline int +dma_set_mask (struct device *dev,u64 mask) +{ +if (!dma_supported (dev, mask)) +return -EIO ; + *dev->dma_mask = mask; +return 0 ; +} +#endif +#endif
include/asm-arm/dma-mapping.h +0 -21
@@ -39,27 +39,6 @@#define dmadev_is_sa1111 (dev) (0)#endif -/* -* 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 inline int dma_supported (struct device *dev,u64 mask) -{ -return dev->dma_mask && *dev->dma_mask !=0 ; -} - -static inline int dma_set_mask (struct device *dev,u64 dma_mask) -{ -if (!dev->dma_mask || !dma_supported (dev, dma_mask)) -return -EIO ; - - *dev->dma_mask = dma_mask; - -return 0 ; -} -static inline int dma_get_cache_alignment (void ) {return 32 ;
include/asm-generic/dma-mapping.h +0 -16
@@ -13,22 +13,6 @@/* need struct page definitions */ #include <linux/mm.h> -static inline int -dma_supported (struct device *dev,u64 mask) -{ -BUG_ON (dev->bus != &pci_bus_type); - -return pci_dma_supported (to_pci_dev (dev), mask); -} - -static inline int -dma_set_mask (struct device *dev,u64 dma_mask) -{ -BUG_ON (dev->bus != &pci_bus_type); - -return pci_set_dma_mask (to_pci_dev (dev), dma_mask); -} -static inline void *dma_alloc_coherent (struct device *dev,size_t size,dma_addr_t *dma_handle,int flag)
include/asm-i386/dma-mapping.h +0 -25
@@ -93,31 +93,6 @@ }static inline int -dma_supported (struct device *dev,u64 mask) -{ -/* -* we fall back to GFP_DMA when the mask isn't all 1s, -* so we can't guarantee allocations that must be -* within a tighter range than GFP_DMA.. -*/ -if (mask <0x00ffffff ) -return 0 ; - -return 1 ; -} - -static inline int -dma_set_mask (struct device *dev,u64 mask) -{ -if (!dev->dma_mask || !dma_supported (dev, mask)) -return -EIO ; - - *dev->dma_mask = mask; - -return 0 ; -} - -static inline int dma_get_cache_alignment (void ) {/* no easy way to get cache size on all x86, so return the
include/asm-ia64/dma-mapping.h +1 -9
@@ -31,15 +31,7 @@ dma_sync_single(dev, dma_handle, size, dir)#define dma_supported platform_dma_supported - -static inline int -dma_set_mask (struct device *dev,u64 mask) -{ -if (!dev->dma_mask || !dma_supported (dev, mask)) -return -EIO ; - *dev->dma_mask = mask; -return 0 ; -} +#define ARCH_HAS_DMA_SUPPORTED static inline int dma_get_cache_alignment (void )
include/asm-mips/dma-mapping.h +0 -25
@@ -181,31 +181,6 @@#endif /* CONFIG_MAPPED_DMA_IO */ static inline int -dma_supported (struct device *dev,u64 mask) -{ -/* -* we fall back to GFP_DMA when the mask isn't all 1s, -* so we can't guarantee allocations that must be -* within a tighter range than GFP_DMA.. -*/ -if (mask <0x00ffffff ) -return 0 ; - -return 1 ; -} - -static inline int -dma_set_mask (struct device *dev,u64 mask) -{ -if (!dev->dma_mask || !dma_supported (dev, mask)) -return -EIO ; - - *dev->dma_mask = mask; - -return 0 ; -} - -static inline int dma_get_cache_alignment (void ) {/* XXX Largest on any MIPS */
include/asm-parisc/dma-mapping.h +1 -11
@@ -142,21 +142,11 @@ hppa_dma_ops->dma_sync_sg (dev, sg, nelems, direction); } +#define ARCH_HAS_DMA_SUPPORTED static inline int dma_supported (struct device *dev,u64 mask) {return hppa_dma_ops->dma_supported (dev, mask); -} - -static inline int -dma_set_mask (struct device *dev,u64 mask) -{ -if (!dev->dma_mask || !dma_supported (dev, mask)) -return -EIO ; - - *dev->dma_mask = mask; - -return 0 ; } static inline int
include/asm-sparc64/dma-mapping.h +10 -0
@@ -2,4 +2,14 @@#ifdef CONFIG_PCI #include <asm-generic/dma-mapping.h> + +#define ARCH_HAS_DMA_SET_MASK +static inline int +dma_set_mask (struct device *dev,u64 dma_mask) +{ +BUG_ON (dev->bus != &pci_bus_type); + +return pci_set_dma_mask (to_pci_dev (dev), dma_mask); +} +#endif