From mboxrd@z Thu Jan 1 00:00:00 1970 From: Matt Domsch Subject: [PATCH 2.4.30-pre3] x86_64: pci_alloc_consistent() match 2.6 implementation Date: Fri, 18 Mar 2005 15:23:44 -0600 Message-ID: <20050318212344.GC26112@lists.us.dell.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Received: from ausc60ps301.us.dell.com ([143.166.148.206]:57905 "EHLO ausc60ps301.us.dell.com") by vger.kernel.org with ESMTP id S261368AbVCRVXr (ORCPT ); Fri, 18 Mar 2005 16:23:47 -0500 Content-Disposition: inline Sender: linux-scsi-owner@vger.kernel.org List-Id: linux-scsi@vger.kernel.org To: ak@suse.de, linux-kernel@vger.kernel.org Cc: linux-scsi@vger.kernel.org For review and comment. On x86_64 systems with no IOMMU and with >4GB RAM (in fact, whenever there are any pages mapped above 4GB), pci_alloc_consistent() falls back to using ZONE_DMA for all allocations, even if the device's dma_mask could have supported using memory from other zones. Problems can be seen when other ZONE_DMA users (SWIOTLB, scsi_malloc()) consume all of ZONE_DMA, leaving none left for pci_alloc_consistent() use. Patch below makes pci_alloc_consistent() for the nommu case (EM64T processors) match the 2.6 implementation of dma_alloc_coherent(), with the exception that this continues to use GFP_ATOMIC. Signed-off-by: Matt Domsch Thanks, Matt -- Matt Domsch Software Architect Dell Linux Solutions linux.dell.com & www.dell.com/linux Linux on Dell mailing lists @ http://lists.us.dell.com --- linux-2.4/arch/x86_64/kernel/pci-nommu.c Fri Feb 25 13:01:44 2005 +++ linux-2.4/arch/x86_64/kernel/pci-nommu.c Fri Feb 25 06:56:55 2005 @@ -13,18 +13,28 @@ void *pci_alloc_consistent(struct pci_de dma_addr_t *dma_handle) { void *ret; + u64 mask; + int order = get_order(size); int gfp = GFP_ATOMIC; - - if (hwdev == NULL || - end_pfn > (hwdev->dma_mask>>PAGE_SHIFT) || /* XXX */ - (u32)hwdev->dma_mask < 0xffffffff) - gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); - if (ret != NULL) { - memset(ret, 0, size); + if (hwdev) + mask = hwdev->dma_mask; + else + mask = 0xffffffffULL; + + for (;;) { + ret = (void *)__get_free_pages(gfp, order); + if (ret == NULL) + return NULL; *dma_handle = virt_to_bus(ret); + if ((*dma_handle & ~mask) == 0) + break; + free_pages((unsigned long)ret, order); + if (gfp & GFP_DMA) + return NULL; + gfp |= GFP_DMA; } + memset(ret, 0, size); return ret; }