1 /* 2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/kern/kern_mpipe.c,v 1.8 2004/07/16 05:51:10 dillon Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/slaballoc.h> 41 #include <sys/mbuf.h> 42 #include <sys/vmmeter.h> 43 #include <sys/lock.h> 44 #include <sys/thread.h> 45 #include <sys/globaldata.h> 46 #include <sys/mpipe.h> 47 48 #include <sys/thread2.h> 49 50 #define arysize(ary) (sizeof(ary)/sizeof((ary)[0])) 51 52 static MALLOC_DEFINE(M_MPIPEARY, "MPipe Array", "Auxillary MPIPE structure"); 53 54 /* 55 * Initialize a malloc pipeline for the specified malloc type and allocation 56 * size. Create an array to cache up to nom_count buffers and preallocate 57 * them. 58 */ 59 void 60 mpipe_init(malloc_pipe_t mpipe, malloc_type_t type, int bytes, 61 int nnom, int nmax, 62 int mpflags, void (*deconstruct)(struct malloc_pipe *, void *)) 63 { 64 int n; 65 66 if (nnom < 1) 67 nnom = 1; 68 if (nmax < 0) 69 nmax = 0x7FFF0000; /* some very large number */ 70 if (nmax < nnom) 71 nmax = nnom; 72 bzero(mpipe, sizeof(struct malloc_pipe)); 73 mpipe->type = type; 74 mpipe->bytes = bytes; 75 mpipe->mpflags = mpflags; 76 mpipe->deconstruct = deconstruct; 77 if ((mpflags & MPF_NOZERO) == 0) 78 mpipe->mflags |= M_ZERO; 79 if (mpflags & MPF_INT) 80 mpipe->mflags |= M_USE_RESERVE | M_USE_INTERRUPT_RESERVE; 81 mpipe->ary_count = nnom; 82 mpipe->max_count = nmax; 83 mpipe->array = malloc(nnom * sizeof(mpipe->array[0]), M_MPIPEARY, 84 M_WAITOK | M_ZERO); 85 86 while (mpipe->free_count < nnom) { 87 n = mpipe->free_count; 88 mpipe->array[n] = malloc(bytes, mpipe->type, M_WAITOK | mpipe->mflags); 89 ++mpipe->free_count; 90 ++mpipe->total_count; 91 } 92 } 93 94 /* 95 * Destroy a previously initialized mpipe. This routine can also safely be 96 * called on an uninitialized mpipe structure if it was zero'd or mpipe_done() 97 * was previously called on it. 98 */ 99 void 100 mpipe_done(malloc_pipe_t mpipe) 101 { 102 void *buf; 103 int n; 104 105 KKASSERT(mpipe->free_count == mpipe->total_count); /* no outstanding mem */ 106 for (n = mpipe->free_count - 1; n >= 0; --n) { 107 buf = mpipe->array[n]; 108 mpipe->array[n] = NULL; 109 KKASSERT(buf != NULL); 110 if (mpipe->deconstruct) 111 mpipe->deconstruct(mpipe, buf); 112 free(buf, mpipe->type); 113 } 114 mpipe->free_count = 0; 115 mpipe->total_count = 0; 116 if (mpipe->array) { 117 free(mpipe->array, M_MPIPEARY); 118 mpipe->array = NULL; 119 } 120 } 121 122 /* 123 * Allocate an entry, nominally non-blocking. The allocation is guarenteed 124 * to return non-NULL up to the nominal count after which it may return NULL. 125 * Note that the implementation is defined to be allowed to block for short 126 * periods of time. Use mpipe_alloc_waitok() to guarentee the allocation. 127 */ 128 void * 129 mpipe_alloc_nowait(malloc_pipe_t mpipe) 130 { 131 void *buf; 132 int n; 133 134 crit_enter(); 135 if ((n = mpipe->free_count) != 0) { 136 /* 137 * Use a free entry if it exists. 138 */ 139 --n; 140 buf = mpipe->array[n]; 141 mpipe->array[n] = NULL; /* sanity check, not absolutely needed */ 142 mpipe->free_count = n; 143 } else if (mpipe->total_count >= mpipe->max_count) { 144 /* 145 * Return NULL if we have hit our limit 146 */ 147 buf = NULL; 148 } else { 149 /* 150 * Otherwise try to malloc() non-blocking. 151 */ 152 buf = malloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags); 153 if (buf) 154 ++mpipe->total_count; 155 } 156 crit_exit(); 157 return(buf); 158 } 159 160 /* 161 * Allocate an entry, block until the allocation succeeds. This may cause 162 * us to block waiting for a prior allocation to be freed. 163 */ 164 void * 165 mpipe_alloc_waitok(malloc_pipe_t mpipe) 166 { 167 void *buf; 168 int n; 169 int mfailed; 170 171 crit_enter(); 172 mfailed = 0; 173 for (;;) { 174 if ((n = mpipe->free_count) != 0) { 175 /* 176 * Use a free entry if it exists. 177 */ 178 --n; 179 buf = mpipe->array[n]; 180 mpipe->array[n] = NULL; 181 mpipe->free_count = n; 182 break; 183 } 184 if (mpipe->total_count >= mpipe->max_count || mfailed) { 185 /* 186 * Block if we have hit our limit 187 */ 188 mpipe->pending = 1; 189 tsleep(mpipe, 0, "mpipe1", 0); 190 continue; 191 } 192 /* 193 * Otherwise try to malloc() non-blocking. If that fails loop to 194 * recheck, and block instead of trying to malloc() again. 195 */ 196 buf = malloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags); 197 if (buf) { 198 ++mpipe->total_count; 199 break; 200 } 201 mfailed = 1; 202 } 203 crit_exit(); 204 return(buf); 205 } 206 207 /* 208 * Free an entry, unblock any waiters. Allow NULL. 209 */ 210 void 211 mpipe_free(malloc_pipe_t mpipe, void *buf) 212 { 213 int n; 214 215 if (buf == NULL) 216 return; 217 218 crit_enter(); 219 if ((n = mpipe->free_count) < mpipe->ary_count) { 220 /* 221 * Free slot available in free array (LIFO) 222 */ 223 mpipe->array[n] = buf; 224 ++mpipe->free_count; 225 if ((mpipe->mpflags & (MPF_CACHEDATA|MPF_NOZERO)) == 0) 226 bzero(buf, mpipe->bytes); 227 crit_exit(); 228 229 /* 230 * Wakeup anyone blocked in mpipe_alloc_*(). 231 */ 232 if (mpipe->pending) { 233 mpipe->pending = 0; 234 wakeup(mpipe); 235 } 236 } else { 237 /* 238 * All the free slots are full, free the buffer directly. 239 */ 240 --mpipe->total_count; 241 KKASSERT(mpipe->total_count >= mpipe->free_count); 242 if (mpipe->deconstruct) 243 mpipe->deconstruct(mpipe, buf); 244 crit_exit(); 245 free(buf, mpipe->type); 246 } 247 } 248 249