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