kernel -- MPIPE: Add a constructor argument and priv ptr.
[dragonfly.git] / sys / kern / kern_mpipe.c
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         construct(mpipe->array[n], priv);
94         ++mpipe->free_count;
95         ++mpipe->total_count;
96     }
97
98     lwkt_token_init(&mpipe->token, 1, "mpipe token");
99 }
100
101 /*
102  * Destroy a previously initialized mpipe.  This routine can also safely be
103  * called on an uninitialized mpipe structure if it was zero'd or mpipe_done()
104  * was previously called on it.
105  */
106 void
107 mpipe_done(malloc_pipe_t mpipe)
108 {
109     void *buf;
110     int n;
111
112     KKASSERT(mpipe->free_count == mpipe->total_count);  /* no outstanding mem */
113     for (n = mpipe->free_count - 1; n >= 0; --n) {
114         buf = mpipe->array[n];
115         mpipe->array[n] = NULL;
116         KKASSERT(buf != NULL);
117         if (mpipe->deconstruct)
118             mpipe->deconstruct(buf, mpipe->priv);
119         kfree(buf, mpipe->type);
120     }
121     mpipe->free_count = 0;
122     mpipe->total_count = 0;
123     if (mpipe->array) {
124         kfree(mpipe->array, M_MPIPEARY);
125         mpipe->array = NULL;
126     }
127
128     lwkt_token_uninit(&mpipe->token);
129 }
130
131 /*
132  * Allocate an entry, nominally non-blocking.  The allocation is guarenteed
133  * to return non-NULL up to the nominal count after which it may return NULL.
134  * Note that the implementation is defined to be allowed to block for short
135  * periods of time.  Use mpipe_alloc_waitok() to guarentee the allocation.
136  */
137 void *
138 mpipe_alloc_nowait(malloc_pipe_t mpipe)
139 {
140     void *buf;
141     int n;
142
143     lwkt_gettoken(&mpipe->token);
144     if ((n = mpipe->free_count) != 0) {
145         /*
146          * Use a free entry if it exists.
147          */
148         --n;
149         buf = mpipe->array[n];
150         mpipe->array[n] = NULL; /* sanity check, not absolutely needed */
151         mpipe->free_count = n;
152     } else if (mpipe->total_count >= mpipe->max_count) {
153         /*
154          * Return NULL if we have hit our limit
155          */
156         buf = NULL;
157     } else {
158         /*
159          * Otherwise try to malloc() non-blocking.
160          */
161         buf = kmalloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags);
162         if (buf) {
163             ++mpipe->total_count; 
164             mpipe->construct(buf, mpipe->priv);
165         }
166     }
167     lwkt_reltoken(&mpipe->token);
168     return(buf);
169 }
170
171 /*
172  * Allocate an entry, block until the allocation succeeds.  This may cause
173  * us to block waiting for a prior allocation to be freed.
174  */
175 void *
176 mpipe_alloc_waitok(malloc_pipe_t mpipe)
177 {
178     void *buf;
179     int n;
180     int mfailed;
181
182     lwkt_gettoken(&mpipe->token);
183     mfailed = 0;
184     for (;;) {
185         if ((n = mpipe->free_count) != 0) {
186             /*
187              * Use a free entry if it exists.
188              */
189             --n;
190             buf = mpipe->array[n];
191             mpipe->array[n] = NULL;
192             mpipe->free_count = n;
193             break;
194         }
195         if (mpipe->total_count >= mpipe->max_count || mfailed) {
196             /*
197              * Block if we have hit our limit
198              */
199             mpipe->pending = 1;
200             tsleep(mpipe, 0, "mpipe1", 0);
201             continue;
202         }
203         /*
204          * Otherwise try to malloc() non-blocking.  If that fails loop to
205          * recheck, and block instead of trying to malloc() again.
206          */
207         buf = kmalloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags);
208         if (buf) {
209             ++mpipe->total_count;
210             mpipe->construct(buf, mpipe->priv);
211             break;
212         }
213         mfailed = 1;
214     }
215     lwkt_reltoken(&mpipe->token);
216     return(buf);
217 }
218
219 /*
220  * Free an entry, unblock any waiters.  Allow NULL.
221  */
222 void
223 mpipe_free(malloc_pipe_t mpipe, void *buf)
224 {
225     int n;
226
227     if (buf == NULL)
228         return;
229
230     lwkt_gettoken(&mpipe->token);
231     if ((n = mpipe->free_count) < mpipe->ary_count) {
232         /*
233          * Free slot available in free array (LIFO)
234          */
235         mpipe->array[n] = buf;
236         ++mpipe->free_count;
237         if ((mpipe->mpflags & (MPF_CACHEDATA|MPF_NOZERO)) == 0) 
238             bzero(buf, mpipe->bytes);
239         lwkt_reltoken(&mpipe->token);
240
241         /*
242          * Wakeup anyone blocked in mpipe_alloc_*().
243          */
244         if (mpipe->pending) {
245             mpipe->pending = 0;
246             wakeup(mpipe);
247         }
248     } else {
249         /*
250          * All the free slots are full, free the buffer directly.
251          */
252         --mpipe->total_count;
253         KKASSERT(mpipe->total_count >= mpipe->free_count);
254         if (mpipe->deconstruct)
255             mpipe->deconstruct(buf, mpipe->priv);
256         lwkt_reltoken(&mpipe->token);
257         kfree(buf, mpipe->type);
258     }
259 }
260