diff options
Diffstat (limited to 'arch/x86/kernel/pci-dma.c')
| -rw-r--r-- | arch/x86/kernel/pci-dma.c | 20 | 
1 files changed, 14 insertions, 6 deletions
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 0c37f16b695..dc00a1331ac 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -378,6 +378,7 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,  	struct page *page;  	unsigned long dma_mask = 0;  	dma_addr_t bus; +	int noretry = 0;  	/* ignore region specifiers */  	gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32); @@ -385,30 +386,37 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,  	if (dma_alloc_from_coherent_mem(dev, size, dma_handle, &memory))  		return memory; -	if (!dev) +	if (!dev) {  		dev = &fallback_dev; +		gfp |= GFP_DMA; +	}  	dma_mask = dev->coherent_dma_mask;  	if (dma_mask == 0) -		dma_mask = DMA_32BIT_MASK; +		dma_mask = (gfp & GFP_DMA) ? DMA_24BIT_MASK : DMA_32BIT_MASK;  	/* Device not DMA able */  	if (dev->dma_mask == NULL)  		return NULL; -	/* Don't invoke OOM killer */ -	gfp |= __GFP_NORETRY; +	/* Don't invoke OOM killer or retry in lower 16MB DMA zone */ +	if (gfp & __GFP_DMA) +		noretry = 1;  #ifdef CONFIG_X86_64  	/* Why <=? Even when the mask is smaller than 4GB it is often  	   larger than 16MB and in this case we have a chance of  	   finding fitting memory in the next higher zone first. If  	   not retry with true GFP_DMA. -AK */ -	if (dma_mask <= DMA_32BIT_MASK) +	if (dma_mask <= DMA_32BIT_MASK && !(gfp & GFP_DMA)) {  		gfp |= GFP_DMA32; +		if (dma_mask < DMA_32BIT_MASK) +			noretry = 1; +	}  #endif   again: -	page = dma_alloc_pages(dev, gfp, get_order(size)); +	page = dma_alloc_pages(dev, +		noretry ? gfp | __GFP_NORETRY : gfp, get_order(size));  	if (page == NULL)  		return NULL;  |