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