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