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