mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
ARM: another fix for the DMA mapping checks
Peter reports that OMAP audio broke with the recent fix for these checks, caused by OMAP audio using a 64-bit DMA mask. We should allow 64-bit DMA masks even with 32-bit dma_addr_t if we can be sure the amount of RAM we have won't allow the 32-bit dma_addr_t to overflow. Unfortunately, the checks to detect overflow were not correct. Tested-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
11d4bb1bd0
commit
9f28cde0bc
1 changed files with 40 additions and 51 deletions
|
@ -158,13 +158,49 @@ 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;
|
||||
|
||||
/*
|
||||
* If the mask allows for more memory than we can address,
|
||||
* and we actually have that much memory, then we must
|
||||
* indicate that DMA to this device is not supported.
|
||||
*/
|
||||
if (sizeof(mask) != sizeof(dma_addr_t) &&
|
||||
mask > (dma_addr_t)~0 &&
|
||||
dma_to_pfn(dev, ~0) < max_pfn) {
|
||||
if (warn) {
|
||||
dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n",
|
||||
mask);
|
||||
dev_warn(dev, "Driver did not use or check the return value from dma_set_coherent_mask()?\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
max_dma_pfn = min(max_pfn, 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) {
|
||||
unsigned long max_dma_pfn;
|
||||
|
||||
mask = dev->coherent_dma_mask;
|
||||
|
||||
/*
|
||||
|
@ -176,34 +212,8 @@ static u64 get_coherent_dma_mask(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
max_dma_pfn = min(max_pfn, arm_dma_pfn_limit);
|
||||
|
||||
/*
|
||||
* If the mask allows for more memory than we can address,
|
||||
* and we actually have that much memory, then fail the
|
||||
* allocation.
|
||||
*/
|
||||
if (sizeof(mask) != sizeof(dma_addr_t) &&
|
||||
mask > (dma_addr_t)~0 &&
|
||||
dma_to_pfn(dev, ~0) > max_dma_pfn) {
|
||||
dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n",
|
||||
mask);
|
||||
dev_warn(dev, "Driver did not use or check the return value from dma_set_coherent_mask()?\n");
|
||||
if (!__dma_supported(dev, mask, true))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now check that the mask, when translated to a PFN,
|
||||
* fits within the allowable addresses which we can
|
||||
* allocate.
|
||||
*/
|
||||
if (dma_to_pfn(dev, mask) < max_dma_pfn) {
|
||||
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,
|
||||
arm_dma_pfn_limit + 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
|
@ -1032,28 +1042,7 @@ void arm_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
|||
*/
|
||||
int dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
unsigned long limit;
|
||||
|
||||
/*
|
||||
* If the mask allows for more memory than we can address,
|
||||
* and we actually have that much memory, then we must
|
||||
* indicate that DMA to this device is not supported.
|
||||
*/
|
||||
if (sizeof(mask) != sizeof(dma_addr_t) &&
|
||||
mask > (dma_addr_t)~0 &&
|
||||
dma_to_pfn(dev, ~0) > arm_dma_pfn_limit)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Translate the device's DMA mask to a PFN limit. This
|
||||
* PFN number includes the page which we can DMA to.
|
||||
*/
|
||||
limit = dma_to_pfn(dev, mask);
|
||||
|
||||
if (limit < arm_dma_pfn_limit)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return __dma_supported(dev, mask, false);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_supported);
|
||||
|
||||
|
|
Loading…
Reference in a new issue