1244466Scognet/*- 2244466Scognet * Copyright (c) 2012 Ian Lepore 3244466Scognet * All rights reserved. 4244466Scognet * 5244466Scognet * Redistribution and use in source and binary forms, with or without 6244466Scognet * modification, are permitted provided that the following conditions 7244466Scognet * are met: 8244466Scognet * 1. Redistributions of source code must retain the above copyright 9244466Scognet * notice, this list of conditions and the following disclaimer. 10244466Scognet * 2. Redistributions in binary form must reproduce the above copyright 11244466Scognet * notice, this list of conditions and the following disclaimer in the 12244466Scognet * documentation and/or other materials provided with the distribution. 13244466Scognet * 14244466Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15244466Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16244466Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17244466Scognet * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18244466Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19244466Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20244466Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21244466Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22244466Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23244466Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24244466Scognet * SUCH DAMAGE. 25244466Scognet */ 26244466Scognet 27244466Scognet#include <sys/cdefs.h> 28244466Scognet__FBSDID("$FreeBSD$"); 29244466Scognet 30244466Scognet/* 31244466Scognet * Buffer allocation support routines for bus_dmamem_alloc implementations. 32244466Scognet */ 33244466Scognet 34244466Scognet#include <sys/param.h> 35244466Scognet#include <sys/systm.h> 36244466Scognet#include <sys/bus.h> 37244466Scognet#include <sys/busdma_bufalloc.h> 38244466Scognet#include <sys/malloc.h> 39244466Scognet 40244466Scognet#include <vm/vm.h> 41244466Scognet#include <vm/vm_extern.h> 42244466Scognet#include <vm/vm_kern.h> 43244466Scognet#include <vm/uma.h> 44244466Scognet 45244466Scognet/* 46244466Scognet * We manage buffer zones up to a page in size. Buffers larger than a page can 47244466Scognet * be managed by one of the kernel's page-oriented memory allocation routines as 48244466Scognet * efficiently as what we can do here. Also, a page is the largest size for 49244466Scognet * which we can g'tee contiguity when using uma, and contiguity is one of the 50244466Scognet * requirements we have to fulfill. 51244466Scognet */ 52244466Scognet#define MIN_ZONE_BUFSIZE 32 53244466Scognet#define MAX_ZONE_BUFSIZE PAGE_SIZE 54244466Scognet 55244466Scognet/* 56244466Scognet * The static array of 12 bufzones is big enough to handle all the zones for the 57244466Scognet * smallest supported allocation size of 32 through the largest supported page 58244466Scognet * size of 64K. If you up the biggest page size number, up the array size too. 59244466Scognet * Basically the size of the array needs to be log2(maxsize)-log2(minsize)+1, 60244466Scognet * but I don't know of an easy way to express that as a compile-time constant. 61244466Scognet */ 62244466Scognet#if PAGE_SIZE > 65536 63244466Scognet#error Unsupported page size 64244466Scognet#endif 65244466Scognet 66244466Scognetstruct busdma_bufalloc { 67244466Scognet bus_size_t min_size; 68244466Scognet size_t num_zones; 69244466Scognet struct busdma_bufzone buf_zones[12]; 70244466Scognet}; 71244466Scognet 72244466Scognetbusdma_bufalloc_t 73244466Scognetbusdma_bufalloc_create(const char *name, bus_size_t minimum_alignment, 74244466Scognet uma_alloc alloc_func, uma_free free_func, u_int32_t zcreate_flags) 75244466Scognet{ 76244466Scognet struct busdma_bufalloc *ba; 77244466Scognet struct busdma_bufzone *bz; 78244466Scognet int i; 79244466Scognet bus_size_t cursize; 80244466Scognet 81244466Scognet ba = malloc(sizeof(struct busdma_bufalloc), M_DEVBUF, 82244466Scognet M_ZERO | M_WAITOK); 83244466Scognet 84244466Scognet ba->min_size = MAX(MIN_ZONE_BUFSIZE, minimum_alignment); 85244466Scognet 86244466Scognet /* 87244466Scognet * Each uma zone is created with an alignment of size-1, meaning that 88244466Scognet * the alignment is equal to the size (I.E., 64 byte buffers are aligned 89244466Scognet * to 64 byte boundaries, etc). This allows for a fast efficient test 90244466Scognet * when deciding whether a pool buffer meets the constraints of a given 91244466Scognet * tag used for allocation: the buffer is usable if tag->alignment <= 92244466Scognet * bufzone->size. 93244466Scognet */ 94244466Scognet for (i = 0, bz = ba->buf_zones, cursize = ba->min_size; 95244466Scognet i < nitems(ba->buf_zones) && cursize <= MAX_ZONE_BUFSIZE; 96244466Scognet ++i, ++bz, cursize <<= 1) { 97289618Sian snprintf(bz->name, sizeof(bz->name), "dma %.10s %ju", 98289618Sian name, (uintmax_t)cursize); 99244466Scognet bz->size = cursize; 100244466Scognet bz->umazone = uma_zcreate(bz->name, bz->size, 101244466Scognet NULL, NULL, NULL, NULL, bz->size - 1, zcreate_flags); 102244466Scognet if (bz->umazone == NULL) { 103244466Scognet busdma_bufalloc_destroy(ba); 104244466Scognet return (NULL); 105244466Scognet } 106244466Scognet if (alloc_func != NULL) 107244466Scognet uma_zone_set_allocf(bz->umazone, alloc_func); 108244466Scognet if (free_func != NULL) 109244466Scognet uma_zone_set_freef(bz->umazone, free_func); 110244466Scognet ++ba->num_zones; 111244466Scognet } 112244466Scognet 113244466Scognet return (ba); 114244466Scognet} 115244466Scognet 116244466Scognetvoid 117244466Scognetbusdma_bufalloc_destroy(busdma_bufalloc_t ba) 118244466Scognet{ 119244466Scognet struct busdma_bufzone *bz; 120244466Scognet int i; 121244466Scognet 122244466Scognet if (ba == NULL) 123244466Scognet return; 124244466Scognet 125244466Scognet for (i = 0, bz = ba->buf_zones; i < ba->num_zones; ++i, ++bz) { 126244466Scognet uma_zdestroy(bz->umazone); 127244466Scognet } 128244466Scognet 129244466Scognet free(ba, M_DEVBUF); 130244466Scognet} 131244466Scognet 132244466Scognetstruct busdma_bufzone * 133244466Scognetbusdma_bufalloc_findzone(busdma_bufalloc_t ba, bus_size_t size) 134244466Scognet{ 135244466Scognet struct busdma_bufzone *bz; 136244466Scognet int i; 137244466Scognet 138244466Scognet if (size > MAX_ZONE_BUFSIZE) 139244466Scognet return (NULL); 140244466Scognet 141244466Scognet for (i = 0, bz = ba->buf_zones; i < ba->num_zones; ++i, ++bz) { 142244466Scognet if (bz->size >= size) 143244466Scognet return (bz); 144244466Scognet } 145244466Scognet 146244466Scognet panic("Didn't find a buffer zone of the right size"); 147244466Scognet} 148244466Scognet 149244466Scognetvoid * 150280957Srstonebusdma_bufalloc_alloc_uncacheable(uma_zone_t zone, vm_size_t size, 151280957Srstone uint8_t *pflag, int wait) 152244466Scognet{ 153244466Scognet#ifdef VM_MEMATTR_UNCACHEABLE 154244466Scognet 155254025Sjeff /* Inform UMA that this allocator uses kernel_arena/object. */ 156244466Scognet *pflag = UMA_SLAB_KERNEL; 157244466Scognet 158254025Sjeff return ((void *)kmem_alloc_attr(kernel_arena, size, wait, 0, 159244466Scognet BUS_SPACE_MAXADDR, VM_MEMATTR_UNCACHEABLE)); 160244466Scognet 161244466Scognet#else 162244466Scognet 163244466Scognet panic("VM_MEMATTR_UNCACHEABLE unavailable"); 164244466Scognet 165244466Scognet#endif /* VM_MEMATTR_UNCACHEABLE */ 166244466Scognet} 167244466Scognet 168244466Scognetvoid 169280957Srstonebusdma_bufalloc_free_uncacheable(void *item, vm_size_t size, uint8_t pflag) 170244466Scognet{ 171244466Scognet 172254025Sjeff kmem_free(kernel_arena, (vm_offset_t)item, size); 173244466Scognet} 174244466Scognet 175