Module Name: src Committed By: bouyer Date: Mon Mar 22 22:03:30 UTC 2010
Modified Files: src/sys/arch/x86/x86: bus_dma.c src/sys/arch/xen/x86: xen_bus_dma.c Log Message: bus_dmamem_alloc() may not get a boundary smaller than size, but it's perfectly valid for bus_dmamap_create() to do so (a contigous transfers will then split in multiple segment). Fix _xen_bus_dmamem_alloc_range() and _bus_dmamem_alloc_range() to allow a boundary limit smaller than size: - compute appropriate boundary for uvm_pglistalloc(), wich doesn't accept boundary < size - also take care of boundary when deciding to start a new segment. While there, remove useless boundary argument to _xen_alloc_contig(). Fix the boundary-related issue of PR port-amd64/42980 To generate a diff of this commit: cvs rdiff -u -r1.53 -r1.54 src/sys/arch/x86/x86/bus_dma.c cvs rdiff -u -r1.20 -r1.21 src/sys/arch/xen/x86/xen_bus_dma.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/x86/x86/bus_dma.c diff -u src/sys/arch/x86/x86/bus_dma.c:1.53 src/sys/arch/x86/x86/bus_dma.c:1.54 --- src/sys/arch/x86/x86/bus_dma.c:1.53 Fri Feb 26 19:25:07 2010 +++ src/sys/arch/x86/x86/bus_dma.c Mon Mar 22 22:03:30 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: bus_dma.c,v 1.53 2010/02/26 19:25:07 jym Exp $ */ +/* $NetBSD: bus_dma.c,v 1.54 2010/03/22 22:03:30 bouyer Exp $ */ /*- * Copyright (c) 1996, 1997, 1998, 2007 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.53 2010/02/26 19:25:07 jym Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.54 2010/03/22 22:03:30 bouyer Exp $"); /* * The following is included because _bus_dma_uiomove is derived from @@ -155,14 +155,28 @@ struct vm_page *m; struct pglist mlist; int curseg, error; + bus_size_t uboundary; /* Always round the size. */ size = round_page(size); + KASSERT(boundary >= PAGE_SIZE || boundary == 0); + /* * Allocate pages from the VM system. - */ - error = uvm_pglistalloc(size, low, high, alignment, boundary, + * We accept boundaries < size, splitting in multiple segments + * if needed. uvm_pglistalloc does not, so compute an appropriate + * boundary: next power of 2 >= size + */ + + if (boundary == 0) + uboundary = 0; + else { + uboundary = boundary; + while (uboundary < size) + uboundary = uboundary << 1; + } + error = uvm_pglistalloc(size, low, high, alignment, uboundary, &mlist, nsegs, (flags & BUS_DMA_NOWAIT) == 0); if (error) return (error); @@ -186,10 +200,13 @@ panic("_bus_dmamem_alloc_range"); } #endif - if (curaddr == (lastaddr + PAGE_SIZE)) + if (curaddr == (lastaddr + PAGE_SIZE) && + (lastaddr & boundary) == (curaddr & boundary)) { segs[curseg].ds_len += PAGE_SIZE; - else { + } else { curseg++; + if (curseg >= nsegs) + return EFBIG; segs[curseg].ds_addr = curaddr; segs[curseg].ds_len = PAGE_SIZE; } Index: src/sys/arch/xen/x86/xen_bus_dma.c diff -u src/sys/arch/xen/x86/xen_bus_dma.c:1.20 src/sys/arch/xen/x86/xen_bus_dma.c:1.21 --- src/sys/arch/xen/x86/xen_bus_dma.c:1.20 Tue Mar 9 23:12:06 2010 +++ src/sys/arch/xen/x86/xen_bus_dma.c Mon Mar 22 22:03:30 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: xen_bus_dma.c,v 1.20 2010/03/09 23:12:06 jym Exp $ */ +/* $NetBSD: xen_bus_dma.c,v 1.21 2010/03/22 22:03:30 bouyer Exp $ */ /* NetBSD bus_dma.c,v 1.21 2005/04/16 07:53:35 yamt Exp */ /*- @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: xen_bus_dma.c,v 1.20 2010/03/09 23:12:06 jym Exp $"); +__KERNEL_RCSID(0, "$NetBSD: xen_bus_dma.c,v 1.21 2010/03/22 22:03:30 bouyer Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -60,7 +60,7 @@ } static int -_xen_alloc_contig(bus_size_t size, bus_size_t alignment, bus_size_t boundary, +_xen_alloc_contig(bus_size_t size, bus_size_t alignment, struct pglist *mlistp, int flags, bus_addr_t low, bus_addr_t high) { int order, i; @@ -72,9 +72,9 @@ /* * When requesting a contigous memory region, the hypervisor will - * return a memory range aligned on size. This will automagically - * handle "boundary", but the only way to enforce alignment - * is to request a memory region of size max(alignment, size). + * return a memory range aligned on size. + * The only way to enforce alignment is to request a memory region + * of size max(alignment, size). */ order = max(get_order(size), get_order(alignment)); npages = (1 << order); @@ -206,21 +206,32 @@ struct pglist mlist; int curseg, error; int doingrealloc = 0; + bus_size_t uboundary; /* Always round the size. */ size = round_page(size); KASSERT((alignment & (alignment - 1)) == 0); KASSERT((boundary & (boundary - 1)) == 0); + KASSERT(boundary >= PAGE_SIZE || boundary == 0); + if (alignment < PAGE_SIZE) alignment = PAGE_SIZE; - if (boundary != 0 && boundary < size) - return (EINVAL); /* * Allocate pages from the VM system. + * We accept boundaries < size, splitting in multiple segments + * if needed. uvm_pglistalloc does not, so compute an appropriate + * boundary: next power of 2 >= size */ - error = uvm_pglistalloc(size, 0, avail_end, alignment, boundary, + if (boundary == 0) + uboundary = 0; + else { + uboundary = boundary; + while (uboundary < size) + uboundary = uboundary << 1; + } + error = uvm_pglistalloc(size, 0, avail_end, alignment, uboundary, &mlist, nsegs, (flags & BUS_DMA_NOWAIT) == 0); if (error) return (error); @@ -244,14 +255,18 @@ curaddr = _BUS_VM_PAGE_TO_BUS(m); if (curaddr < low || curaddr >= high) goto badaddr; - if (curaddr == (lastaddr + PAGE_SIZE)) { + if (curaddr == (lastaddr + PAGE_SIZE) && + (lastaddr & boundary) == (curaddr & boundary)) { segs[curseg].ds_len += PAGE_SIZE; - if ((lastaddr & boundary) != (curaddr & boundary)) - goto dorealloc; } else { curseg++; - if (curseg >= nsegs || (curaddr & (alignment - 1)) != 0) - goto dorealloc; + if (curseg >= nsegs || + (curaddr & (alignment - 1)) != 0) { + if (doingrealloc) + return EFBIG; + else + goto dorealloc; + } segs[curseg].ds_addr = curaddr; segs[curseg].ds_len = PAGE_SIZE; } @@ -293,7 +308,7 @@ segs[curseg].ds_len = 0; } error = _xen_alloc_contig(size, alignment, - boundary, &mlist, flags, low, high); + &mlist, flags, low, high); if (error) return error; goto again;