From owner-svn-src-head@freebsd.org Sat Oct 24 02:18:15 2015 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 67ABEA18363; Sat, 24 Oct 2015 02:18:15 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 2A0E31B7F; Sat, 24 Oct 2015 02:18:15 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id t9O2IEan008806; Sat, 24 Oct 2015 02:18:14 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id t9O2IEVr008805; Sat, 24 Oct 2015 02:18:14 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201510240218.t9O2IEVr008805@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Sat, 24 Oct 2015 02:18:14 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r289862 - head/sys/arm/arm X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 24 Oct 2015 02:18:15 -0000 Author: ian Date: Sat Oct 24 02:18:14 2015 New Revision: 289862 URL: https://svnweb.freebsd.org/changeset/base/289862 Log: Change the preallocation of a busdma segment mapping array from per-tag to per-map. The per-tag scheme is not safe, and a mutex can't be used to protect it because the mapping routines can't sleep. Code brought in from armv6 implementation. Modified: head/sys/arm/arm/busdma_machdep.c Modified: head/sys/arm/arm/busdma_machdep.c ============================================================================== --- head/sys/arm/arm/busdma_machdep.c Sat Oct 24 01:39:31 2015 (r289861) +++ head/sys/arm/arm/busdma_machdep.c Sat Oct 24 02:18:14 2015 (r289862) @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); #include #define MAX_BPAGES 64 +#define MAX_DMA_SEGMENTS 4096 #define BUS_DMA_COULD_BOUNCE BUS_DMA_BUS3 #define BUS_DMA_MIN_ALLOC_COMP BUS_DMA_BUS4 @@ -111,13 +112,6 @@ struct bus_dma_tag { */ struct arm32_dma_range *ranges; int _nranges; - /* - * Most tags need one or two segments, and can use the local tagsegs - * array. For tags with a larger limit, we'll allocate a bigger array - * on first use. - */ - bus_dma_segment_t *segments; - bus_dma_segment_t tagsegs[2]; }; struct bounce_page { @@ -175,20 +169,19 @@ struct bus_dmamap { bus_dmamap_callback_t *callback; void *callback_arg; int flags; -#define DMAMAP_COHERENT 0x8 -#define DMAMAP_CACHE_ALIGNED 0x10 +#define DMAMAP_COHERENT (1 << 0) +#define DMAMAP_DMAMEM_ALLOC (1 << 1) +#define DMAMAP_MBUF (1 << 2) +#define DMAMAP_CACHE_ALIGNED (1 << 3) STAILQ_ENTRY(bus_dmamap) links; + bus_dma_segment_t *segments; int sync_count; - struct sync_list *slist; + struct sync_list slist[]; }; static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist; static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist; -static struct mtx busdma_mtx; - -MTX_SYSINIT(busdma_mtx, &busdma_mtx, "busdma lock", MTX_DEF); - static void init_bounce_pages(void *dummy); static int alloc_bounce_zone(bus_dma_tag_t dmat); static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages); @@ -208,57 +201,16 @@ bus_dma_tag_t arm_root_dma_tag; * Begin block of code useful to transplant to other implementations. */ -static uma_zone_t dmamap_zone; /* Cache of struct bus_dmamap items */ - static busdma_bufalloc_t coherent_allocator; /* Cache of coherent buffers */ static busdma_bufalloc_t standard_allocator; /* Cache of standard buffers */ MALLOC_DEFINE(M_BUSDMA, "busdma", "busdma metadata"); MALLOC_DEFINE(M_BOUNCE, "bounce", "busdma bounce pages"); -/* - * This is the ctor function passed to uma_zcreate() for the pool of dma maps. - * It'll need platform-specific changes if this code is copied. - */ -static int -dmamap_ctor(void *mem, int size, void *arg, int flags) -{ - bus_dmamap_t map; - bus_dma_tag_t dmat; - - map = (bus_dmamap_t)mem; - dmat = (bus_dma_tag_t)arg; - - dmat->map_count++; - - map->dmat = dmat; - map->flags = 0; - STAILQ_INIT(&map->bpages); - - return (0); -} - -/* - * This is the dtor function passed to uma_zcreate() for the pool of dma maps. - * It may need platform-specific changes if this code is copied . - */ -static void -dmamap_dtor(void *mem, int size, void *arg) -{ - bus_dmamap_t map; - - map = (bus_dmamap_t)mem; - - map->dmat->map_count--; -} - static void busdma_init(void *dummy) { - /* Create a cache of maps for bus_dmamap_create(). */ - dmamap_zone = uma_zcreate("dma maps", sizeof(struct bus_dmamap), - dmamap_ctor, dmamap_dtor, NULL, NULL, UMA_ALIGN_PTR, 0); /* Create a cache of buffers in standard (cacheable) memory. */ standard_allocator = busdma_bufalloc_create("buffer", @@ -280,11 +232,11 @@ busdma_init(void *dummy) /* * This init historically used SI_SUB_VM, but now the init code requires - * malloc(9) using M_BUSDMA memory, which is set up later than SI_SUB_VM, by - * SI_SUB_KMEM and SI_ORDER_THIRD, so we'll go right after that by using - * SI_SUB_KMEM and SI_ORDER_FOURTH. + * malloc(9) using M_BUSDMA memory and the pcpu zones for counter(9), which get + * set up by SI_SUB_KMEM and SI_ORDER_LAST, so we'll go right after that by + * using SI_SUB_KMEM+1. */ -SYSINIT(busdma, SI_SUB_KMEM, SI_ORDER_FOURTH, busdma_init, NULL); +SYSINIT(busdma, SI_SUB_KMEM+1, SI_ORDER_FIRST, busdma_init, NULL); /* * End block of code useful to transplant to other implementations. @@ -406,8 +358,6 @@ dflt_lock(void *arg, bus_dma_lock_op_t o /* * Allocate a device specific dma_tag. */ -#define SEG_NB 1024 - int bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, @@ -451,21 +401,8 @@ bus_dma_tag_create(bus_dma_tag_t parent, newtag->lockfunc = dflt_lock; newtag->lockfuncarg = NULL; } - /* - * If all the segments we need fit into the local tagsegs array, set the - * pointer now. Otherwise NULL the pointer and an array of segments - * will be allocated later, on first use. We don't pre-allocate now - * because some tags exist just to pass contraints to children in the - * device hierarchy, and they tend to use BUS_SPACE_UNRESTRICTED and we - * sure don't want to try to allocate an array for that. - */ - if (newtag->nsegments <= nitems(newtag->tagsegs)) - newtag->segments = newtag->tagsegs; - else - newtag->segments = NULL; - /* - * Take into account any restrictions imposed by our parent tag - */ + + /* Take into account any restrictions imposed by our parent tag */ if (parent != NULL) { newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr); newtag->highaddr = MAX(parent->highaddr, newtag->highaddr); @@ -546,9 +483,6 @@ bus_dma_tag_destroy(bus_dma_tag_t dmat) parent = dmat->parent; atomic_subtract_int(&dmat->ref_count, 1); if (dmat->ref_count == 0) { - if (dmat->segments != NULL && - dmat->segments != dmat->tagsegs) - free(dmat->segments, M_BUSDMA); free(dmat, M_BUSDMA); /* * Last reference count, so @@ -565,7 +499,31 @@ bus_dma_tag_destroy(bus_dma_tag_t dmat) return (0); } -#include +static bus_dmamap_t +allocate_map(bus_dma_tag_t dmat, int mflags) +{ + int mapsize, segsize; + bus_dmamap_t map; + + /* + * Allocate the map. The map structure ends with an embedded + * variable-sized array of sync_list structures. Following that + * we allocate enough extra space to hold the array of bus_dma_segments. + */ + KASSERT(dmat->nsegments <= MAX_DMA_SEGMENTS, + ("cannot allocate %u dma segments (max is %u)", + dmat->nsegments, MAX_DMA_SEGMENTS)); + segsize = sizeof(struct bus_dma_segment) * dmat->nsegments; + mapsize = sizeof(*map) + sizeof(struct sync_list) * dmat->nsegments; + map = malloc(mapsize + segsize, M_BUSDMA, mflags | M_ZERO); + if (map == NULL) { + CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM); + return (NULL); + } + map->segments = (bus_dma_segment_t *)((uintptr_t)map + mapsize); + return (map); +} + /* * Allocate a handle for mapping from kva/uva/physical * address space into bus device space. @@ -573,40 +531,21 @@ bus_dma_tag_destroy(bus_dma_tag_t dmat) int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp) { - struct sync_list *slist; bus_dmamap_t map; int error = 0; - slist = malloc(sizeof(*slist) * dmat->nsegments, M_BUSDMA, M_NOWAIT); - if (slist == NULL) - return (ENOMEM); - - map = uma_zalloc_arg(dmamap_zone, dmat, M_NOWAIT); - *mapp = map; + *mapp = map = allocate_map(dmat, M_NOWAIT); if (map == NULL) { - free(slist, M_BUSDMA); + CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM); return (ENOMEM); } /* - * If the tag's segments haven't been allocated yet we need to do it - * now, because we can't sleep for resources at map load time. - */ - if (dmat->segments == NULL) { - dmat->segments = malloc(dmat->nsegments * - sizeof(*dmat->segments), M_BUSDMA, M_NOWAIT); - if (dmat->segments == NULL) { - free(slist, M_BUSDMA); - uma_zfree(dmamap_zone, map); - *mapp = NULL; - return (ENOMEM); - } - } - - /* - * Bouncing might be required if the driver asks for an active - * exclusion region, a data alignment that is stricter than 1, and/or - * an active address boundary. + * Bouncing might be required if the driver asks for an exclusion + * region, a data alignment that is stricter than 1, or DMA that begins + * or ends with a partial cacheline. Whether bouncing will actually + * happen can't be known until mapping time, but we need to pre-allocate + * resources now because we might not be allowed to at mapping time. */ if (dmat->flags & BUS_DMA_COULD_BOUNCE) { @@ -616,8 +555,7 @@ bus_dmamap_create(bus_dma_tag_t dmat, in if (dmat->bounce_zone == NULL) { if ((error = alloc_bounce_zone(dmat)) != 0) { - free(slist, M_BUSDMA); - uma_zfree(dmamap_zone, map); + free(map, M_BUSDMA); *mapp = NULL; return (error); } @@ -652,7 +590,6 @@ bus_dmamap_create(bus_dma_tag_t dmat, in bz->map_count++; } map->sync_count = 0; - map->slist = slist; CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", __func__, dmat, dmat->flags, error); @@ -672,12 +609,12 @@ bus_dmamap_destroy(bus_dma_tag_t dmat, b __func__, dmat, EBUSY); return (EBUSY); } - free(map->slist, M_BUSDMA); - uma_zfree(dmamap_zone, map); if (dmat->bounce_zone) dmat->bounce_zone->map_count--; + free(map, M_BUSDMA); + dmat->map_count--; CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat); - return (0); + return (0); } /* @@ -686,37 +623,31 @@ bus_dmamap_destroy(bus_dma_tag_t dmat, b * the allocated memory, and a pointer to an associated bus_dmamap. */ int -bus_dmamem_alloc(bus_dma_tag_t dmat, void **vaddrp, int flags, +bus_dmamem_alloc(bus_dma_tag_t dmat, void **vaddr, int flags, bus_dmamap_t *mapp) { - struct sync_list *slist; - void * vaddr; - struct busdma_bufzone *bufzone; busdma_bufalloc_t ba; + struct busdma_bufzone *bufzone; bus_dmamap_t map; - int mflags; vm_memattr_t memattr; + int mflags; if (flags & BUS_DMA_NOWAIT) mflags = M_NOWAIT; else mflags = M_WAITOK; - /* - * If the tag's segments haven't been allocated yet we need to do it - * now, because we can't sleep for resources at map load time. - */ - if (dmat->segments == NULL) - dmat->segments = malloc(dmat->nsegments * - sizeof(*dmat->segments), M_BUSDMA, mflags); + if (flags & BUS_DMA_ZERO) + mflags |= M_ZERO; - slist = malloc(sizeof(*slist) * dmat->nsegments, M_BUSDMA, M_NOWAIT); - if (slist == NULL) - return (ENOMEM); - map = uma_zalloc_arg(dmamap_zone, dmat, mflags); + *mapp = map = allocate_map(dmat, mflags); if (map == NULL) { - free(slist, M_BUSDMA); + CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", + __func__, dmat, dmat->flags, ENOMEM); return (ENOMEM); } + map->flags = DMAMAP_DMAMEM_ALLOC; + + /* Choose a busdma buffer allocator based on memory type flags. */ if (flags & BUS_DMA_COHERENT) { memattr = VM_MEMATTR_UNCACHEABLE; ba = coherent_allocator; @@ -725,11 +656,6 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, voi memattr = VM_MEMATTR_DEFAULT; ba = standard_allocator; } - /* All buffers we allocate are cache-aligned. */ - map->flags |= DMAMAP_CACHE_ALIGNED; - - if (flags & BUS_DMA_ZERO) - mflags |= M_ZERO; /* * Try to find a bufzone in the allocator that holds a cache of buffers @@ -753,28 +679,28 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, voi */ if (bufzone != NULL && dmat->alignment <= bufzone->size && !_bus_dma_can_bounce(dmat->lowaddr, dmat->highaddr)) { - vaddr = uma_zalloc(bufzone->umazone, mflags); + *vaddr = uma_zalloc(bufzone->umazone, mflags); } else if (dmat->nsegments >= btoc(dmat->maxsize) && dmat->alignment <= PAGE_SIZE && dmat->boundary == 0) { - vaddr = (void *)kmem_alloc_attr(kernel_arena, dmat->maxsize, + *vaddr = (void *)kmem_alloc_attr(kernel_arena, dmat->maxsize, mflags, 0, dmat->lowaddr, memattr); } else { - vaddr = (void *)kmem_alloc_contig(kernel_arena, dmat->maxsize, + *vaddr = (void *)kmem_alloc_contig(kernel_arena, dmat->maxsize, mflags, 0, dmat->lowaddr, dmat->alignment, dmat->boundary, memattr); } - if (vaddr == NULL) { - free(slist, M_BUSDMA); - uma_zfree(dmamap_zone, map); - map = NULL; - } else { - map->slist = slist; - map->sync_count = 0; + if (*vaddr == NULL) { + CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", + __func__, dmat, dmat->flags, ENOMEM); + free(map, M_BUSDMA); + *mapp = NULL; + return (ENOMEM); } - *vaddrp = vaddr; - *mapp = map; + dmat->map_count++; - return (vaddr == NULL ? ENOMEM : 0); + CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d", + __func__, dmat, dmat->flags, 0); + return (0); } /* @@ -792,9 +718,6 @@ bus_dmamem_free(bus_dma_tag_t dmat, void else ba = standard_allocator; - free(map->slist, M_BUSDMA); - uma_zfree(dmamap_zone, map); - bufzone = busdma_bufalloc_findzone(ba, dmat->maxsize); if (bufzone != NULL && dmat->alignment <= bufzone->size && @@ -802,6 +725,10 @@ bus_dmamem_free(bus_dma_tag_t dmat, void uma_zfree(bufzone->umazone, vaddr); else kmem_free(kernel_arena, (vm_offset_t)vaddr, dmat->maxsize); + + dmat->map_count--; + free(map, M_BUSDMA); + CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags); } static void @@ -962,7 +889,7 @@ _bus_dmamap_load_phys(bus_dma_tag_t dmat int error; if (segs == NULL) - segs = dmat->segments; + segs = map->segments; if ((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) { _bus_dmamap_count_phys(dmat, map, buf, buflen, flags); @@ -1046,7 +973,7 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dm int error = 0; if (segs == NULL) - segs = dmat->segments; + segs = map->segments; if ((flags & BUS_DMA_LOAD_MBUF) != 0) map->flags |= DMAMAP_CACHE_ALIGNED; @@ -1147,7 +1074,7 @@ _bus_dmamap_complete(bus_dma_tag_t dmat, { if (segs == NULL) - segs = dmat->segments; + segs = map->segments; return (segs); } @@ -1158,12 +1085,22 @@ void _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map) { struct bounce_page *bpage; + struct bounce_zone *bz; + + if ((bz = dmat->bounce_zone) != NULL) { + while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { + STAILQ_REMOVE_HEAD(&map->bpages, links); + free_bounce_page(dmat, bpage); + } - while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) { - STAILQ_REMOVE_HEAD(&map->bpages, links); - free_bounce_page(dmat, bpage); + bz = dmat->bounce_zone; + bz->free_bpages += map->pagesreserved; + bz->reserved_bpages -= map->pagesreserved; + map->pagesreserved = 0; + map->pagesneeded = 0; } map->sync_count = 0; + map->flags &= ~DMAMAP_MBUF; } static void