xref: /dragonfly/sys/kern/kern_mpipe.c (revision b40e316c)
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