xref: /minix3/minix/net/lwip/mempool.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
1*ef8d499eSDavid van Moolenbroek /* LWIP service - mempool.c - memory pool management and slab allocation */
2*ef8d499eSDavid van Moolenbroek /*
3*ef8d499eSDavid van Moolenbroek  * This module should be considered a replacement for lwIP's PBUF_POOL and
4*ef8d499eSDavid van Moolenbroek  * custom-pools functionality.  lwIP's PBUF_POOL system allows a PBUF_POOL type
5*ef8d499eSDavid van Moolenbroek  * allocation for a moderately large amount of memory, for example for a full-
6*ef8d499eSDavid van Moolenbroek  * sized packet, to be turned into a chain of "pbuf" buffers, each of a static
7*ef8d499eSDavid van Moolenbroek  * size.  Most of lwIP can deal with such pbuf chains, because many other types
8*ef8d499eSDavid van Moolenbroek  * of allocations also end up consisting of pbuf chains.  However, lwIP will
9*ef8d499eSDavid van Moolenbroek  * never use PBUF_POOL for its own memory allocations, and use PBUF_RAM
10*ef8d499eSDavid van Moolenbroek  * allocations instead.  Such PBUF_RAM allocations always return one single
11*ef8d499eSDavid van Moolenbroek  * pbuf with a contiguous memory area.  lwIP's custom pools support allows such
12*ef8d499eSDavid van Moolenbroek  * PBUF_RAM allocations to draw from user-defined pools of statically allocated
13*ef8d499eSDavid van Moolenbroek  * memory, as an alternative to turning such allocations into malloc() calls.
14*ef8d499eSDavid van Moolenbroek  *
15*ef8d499eSDavid van Moolenbroek  * However, lwIP itself does not offer a way to combine these two pool systems:
16*ef8d499eSDavid van Moolenbroek  * the PBUF_POOL buffer pool and the custom pools are completely separate.  We
17*ef8d499eSDavid van Moolenbroek  * want to be able to draw both kinds of memory from the same pool.  This is
18*ef8d499eSDavid van Moolenbroek  * the first reason that we are using our own memory pools.  The second is
19*ef8d499eSDavid van Moolenbroek  * something that lwIP could never offer anyway: we would like to provide a
20*ef8d499eSDavid van Moolenbroek  * certain amount of static/preallocated memory for those types of allocations,
21*ef8d499eSDavid van Moolenbroek  * but optionally also add a much larger amount of dynamic memory when needed.
22*ef8d499eSDavid van Moolenbroek  *
23*ef8d499eSDavid van Moolenbroek  * In order to make this module work, we do not use PBUF_POOL anywhere.
24*ef8d499eSDavid van Moolenbroek  * Instead, we use chained static-sized PBUF_RAM allocations for all types of
25*ef8d499eSDavid van Moolenbroek  * allocations that we manage ourselves--see pchain_alloc().  We tell lwIP to
26*ef8d499eSDavid van Moolenbroek  * use the functions in this module to do the malloc-type allocations for those
27*ef8d499eSDavid van Moolenbroek  * PBUF_RAM buffers.  As such, this module manages all PBUF_RAM allocations,
28*ef8d499eSDavid van Moolenbroek  * both from our own code and from lwIP.  Note that we do still use lwIP's own
29*ef8d499eSDavid van Moolenbroek  * pools for various lwIP structures.  We do want to keep the isolation
30*ef8d499eSDavid van Moolenbroek  * provided by the use of such pools, even though that means that we have to
31*ef8d499eSDavid van Moolenbroek  * provision some of those pools for the worst case, resulting in some memory
32*ef8d499eSDavid van Moolenbroek  * overhead that is unnecessary for the common case.
33*ef8d499eSDavid van Moolenbroek  *
34*ef8d499eSDavid van Moolenbroek  * With PBUF_RAM allocation redirection system in place, this module has to
35*ef8d499eSDavid van Moolenbroek  * manage the memory for those allocations.  It does this based on the
36*ef8d499eSDavid van Moolenbroek  * assertion that there are three main classes of PBUF_RAM allocation sizes:
37*ef8d499eSDavid van Moolenbroek  *
38*ef8d499eSDavid van Moolenbroek  * - "large" allocations: these are allocations for up to MEMPOOL_BUFSIZE bytes
39*ef8d499eSDavid van Moolenbroek  *   of PBUF_RAM data, where MEMPOOL_BUFSIZE is the allocation granularity that
40*ef8d499eSDavid van Moolenbroek  *   we have picked for the individual buffers in larger chains.  It is set to
41*ef8d499eSDavid van Moolenbroek  *   512 bytes right now, mainly to keep pbuf chains for full-sized ethernet
42*ef8d499eSDavid van Moolenbroek  *   packets short, which has many performance advantages.  Since the pbuf
43*ef8d499eSDavid van Moolenbroek  *   header itself also takes some space (16 bytes, right now), this results in
44*ef8d499eSDavid van Moolenbroek  *   allocations seen by mempool_malloc() of up to just over 512 bytes.
45*ef8d499eSDavid van Moolenbroek  * - "small" allocations: these are allocations mostly for packet headers, as
46*ef8d499eSDavid van Moolenbroek  *   needed by lwIP to prepend to (mainly TCP) packet data that we give to it.
47*ef8d499eSDavid van Moolenbroek  *   The size of these allocations varies, but most are 76 bytes (80 bytes if
48*ef8d499eSDavid van Moolenbroek  *   we ever add VLAN support), plus once again the pbuf header.
49*ef8d499eSDavid van Moolenbroek  * - "excessive" allocations: these are allocations larger than the maximum
50*ef8d499eSDavid van Moolenbroek  *   we have configured, effectively requesting contiguous memory of (possibly
51*ef8d499eSDavid van Moolenbroek  *   far) more than 512 bytes.  We do not make such allocations ourselves, as
52*ef8d499eSDavid van Moolenbroek  *   we only ever create pbuf chains.  Thus, any such allocations come from
53*ef8d499eSDavid van Moolenbroek  *   lwIP.  There are a few locations in lwIP that attempt to make those kinds
54*ef8d499eSDavid van Moolenbroek  *   of allocations, but we replace one important case in the lwIP code with
55*ef8d499eSDavid van Moolenbroek  *   a chained allocation, (currently) leaving only one case: allocation of
56*ef8d499eSDavid van Moolenbroek  *   ICMP ping reply packets.  In this module, we outright *deny* any excessive
57*ef8d499eSDavid van Moolenbroek  *   allocations.  Practically, that means that no replies are generated for
58*ef8d499eSDavid van Moolenbroek  *   requests exceeding around 460 bytes, which is in fact not bad, especially
59*ef8d499eSDavid van Moolenbroek  *   since we have multicast ICMP ping replying enabled.  If any new cases of
60*ef8d499eSDavid van Moolenbroek  *   excessive allocations are added to lwIP in the future, we will have to
61*ef8d499eSDavid van Moolenbroek  *   deal with those on a case-by-case basis, but for now this should be all.
62*ef8d499eSDavid van Moolenbroek  *
63*ef8d499eSDavid van Moolenbroek  * This module caters to the first two types of allocations.  For large buffer
64*ef8d499eSDavid van Moolenbroek  * allocations, it provides a standard slab allocator, with a hardcoded slab
65*ef8d499eSDavid van Moolenbroek  * size of MEMPOOL_LARGE_COUNT buffers with a 512-byte data area each.  One
66*ef8d499eSDavid van Moolenbroek  * slab is allocated at service start-up; additional slabs up to a configured
67*ef8d499eSDavid van Moolenbroek  * maximum are allocated on demand.  Once fallen out of use, all but one slabs
68*ef8d499eSDavid van Moolenbroek  * will be freed after a while, using a timer.  The current per-slab count of
69*ef8d499eSDavid van Moolenbroek  * 512 large buffers, combined with the buffer size of 512 plus the pbuf header
70*ef8d499eSDavid van Moolenbroek  * plus a bit of extra overhead, results in about 266 KB per slab.
71*ef8d499eSDavid van Moolenbroek  *
72*ef8d499eSDavid van Moolenbroek  * For small buffer allocations, there are two facilities.  First, there is a
73*ef8d499eSDavid van Moolenbroek  * static pool of small buffers.  This pool currently provides 256 small-sized
74*ef8d499eSDavid van Moolenbroek  * buffers, mainly in order to allow packet headers to be produced even in low-
75*ef8d499eSDavid van Moolenbroek  * memory conditions.  In addition, small buffers may be formed by allocating
76*ef8d499eSDavid van Moolenbroek  * and then splitting up one large buffer.  The module is currently configured
77*ef8d499eSDavid van Moolenbroek  * to split one large buffer into four small buffers, which yields a small
78*ef8d499eSDavid van Moolenbroek  * buffer size of just over 100 bytes--enough for the packet headers while
79*ef8d499eSDavid van Moolenbroek  * leaving little slack on either side.
80*ef8d499eSDavid van Moolenbroek  *
81*ef8d499eSDavid van Moolenbroek  * It is important to note that large and small buffer allocations are freed up
82*ef8d499eSDavid van Moolenbroek  * through the same function, with no information on the original allocation
83*ef8d499eSDavid van Moolenbroek  * size.  As a result, we have to distinguish between large and small buffers
84*ef8d499eSDavid van Moolenbroek  * using a unified system.  In particular, this module prepends each of its
85*ef8d499eSDavid van Moolenbroek  * allocations by a single pointer, which points to a header structure that is
86*ef8d499eSDavid van Moolenbroek  * at the very beginning of the slab that contains the allocated buffer.  That
87*ef8d499eSDavid van Moolenbroek  * header structure contains information about the type of slab (large or
88*ef8d499eSDavid van Moolenbroek  * small) as well as some accounting information used by both types.
89*ef8d499eSDavid van Moolenbroek  *
90*ef8d499eSDavid van Moolenbroek  * For large-buffer slabs, this header is part of a larger structure with for
91*ef8d499eSDavid van Moolenbroek  * example the slab's list of free buffers.  This larger structure is then
92*ef8d499eSDavid van Moolenbroek  * followed by the actual buffers in the slab.
93*ef8d499eSDavid van Moolenbroek  *
94*ef8d499eSDavid van Moolenbroek  * For small-buffer slabs, the header is followed directly by the actual small
95*ef8d499eSDavid van Moolenbroek  * buffers.  Thus, when a large buffer is split up into four small buffers, the
96*ef8d499eSDavid van Moolenbroek  * data area of that large buffer consists of a small-type slab header and four
97*ef8d499eSDavid van Moolenbroek  * small buffers.  The large buffer itself is simply considered in use, as
98*ef8d499eSDavid van Moolenbroek  * though it was allocated for regular data.  This nesting approach saves a lot
99*ef8d499eSDavid van Moolenbroek  * of memory for small allocations, at the cost of a bit more computation.
100*ef8d499eSDavid van Moolenbroek  *
101*ef8d499eSDavid van Moolenbroek  * It should be noted that all allocations should be (and are) pointer-aligned.
102*ef8d499eSDavid van Moolenbroek  * Normally lwIP would check for this, but we cannot tell lwIP the platform
103*ef8d499eSDavid van Moolenbroek  * pointer size without hardcoding that size.  This module performs proper
104*ef8d499eSDavid van Moolenbroek  * alignment of all buffers itself though, regardless of the pointer size.
105*ef8d499eSDavid van Moolenbroek  */
106*ef8d499eSDavid van Moolenbroek 
107*ef8d499eSDavid van Moolenbroek #include "lwip.h"
108*ef8d499eSDavid van Moolenbroek 
109*ef8d499eSDavid van Moolenbroek #include <sys/mman.h>
110*ef8d499eSDavid van Moolenbroek 
111*ef8d499eSDavid van Moolenbroek /* Alignment to pointer sizes. */
112*ef8d499eSDavid van Moolenbroek #define MEMPOOL_ALIGN_DOWN(s)	((s) & ~(sizeof(void *) - 1))
113*ef8d499eSDavid van Moolenbroek #define MEMPOOL_ALIGN_UP(s)	MEMPOOL_ALIGN_DOWN((s) + sizeof(void *) - 1)
114*ef8d499eSDavid van Moolenbroek 
115*ef8d499eSDavid van Moolenbroek /* Large buffers: per-slab count and data area size. */
116*ef8d499eSDavid van Moolenbroek #define MEMPOOL_LARGE_COUNT	512
117*ef8d499eSDavid van Moolenbroek #define MEMPOOL_LARGE_SIZE	\
118*ef8d499eSDavid van Moolenbroek     (MEMPOOL_ALIGN_UP(sizeof(struct pbuf)) + MEMPOOL_BUFSIZE)
119*ef8d499eSDavid van Moolenbroek 
120*ef8d499eSDavid van Moolenbroek /* Small buffers: per-slab count and data area size. */
121*ef8d499eSDavid van Moolenbroek #define MEMPOOL_SMALL_COUNT	4
122*ef8d499eSDavid van Moolenbroek #define MEMPOOL_SMALL_SIZE	\
123*ef8d499eSDavid van Moolenbroek     (MEMPOOL_ALIGN_DOWN(MEMPOOL_LARGE_SIZE / MEMPOOL_SMALL_COUNT) - \
124*ef8d499eSDavid van Moolenbroek      sizeof(struct mempool_header))
125*ef8d499eSDavid van Moolenbroek 
126*ef8d499eSDavid van Moolenbroek /* Memory pool slab header, part of both small and large slabs. */
127*ef8d499eSDavid van Moolenbroek struct mempool_header {
128*ef8d499eSDavid van Moolenbroek 	union {
129*ef8d499eSDavid van Moolenbroek 		struct {
130*ef8d499eSDavid van Moolenbroek 			uint8_t mhui_flags;
131*ef8d499eSDavid van Moolenbroek 			uint32_t mhui_inuse;
132*ef8d499eSDavid van Moolenbroek 		} mhu_info;
133*ef8d499eSDavid van Moolenbroek 		void *mhu_align;	/* force pointer alignment */
134*ef8d499eSDavid van Moolenbroek 	} mh_u;
135*ef8d499eSDavid van Moolenbroek };
136*ef8d499eSDavid van Moolenbroek #define mh_flags mh_u.mhu_info.mhui_flags
137*ef8d499eSDavid van Moolenbroek #define mh_inuse mh_u.mhu_info.mhui_inuse
138*ef8d499eSDavid van Moolenbroek 
139*ef8d499eSDavid van Moolenbroek /* Header flags. */
140*ef8d499eSDavid van Moolenbroek #define MHF_SMALL	0x01	/* slab is for small buffers, not large ones */
141*ef8d499eSDavid van Moolenbroek #define MHF_STATIC	0x02	/* small slab is statically allocated */
142*ef8d499eSDavid van Moolenbroek #define MHF_MARKED	0x04	/* large empty slab is up for deallocation */
143*ef8d499eSDavid van Moolenbroek 
144*ef8d499eSDavid van Moolenbroek /*
145*ef8d499eSDavid van Moolenbroek  * Large buffer.  When allocated, mlb_header points to the (header of) the
146*ef8d499eSDavid van Moolenbroek  * containing large slab, and mlb_data is returned for arbitrary use by the
147*ef8d499eSDavid van Moolenbroek  * user of the buffer.  When free, mlb_header is NULL and instead mlb_header2
148*ef8d499eSDavid van Moolenbroek  * points to the containing slab (allowing for double-free detection), and the
149*ef8d499eSDavid van Moolenbroek  * buffer is on the slab's free list by using mlb_next.
150*ef8d499eSDavid van Moolenbroek  */
151*ef8d499eSDavid van Moolenbroek struct mempool_large_buf {
152*ef8d499eSDavid van Moolenbroek 	struct mempool_header *mlb_header;
153*ef8d499eSDavid van Moolenbroek 	union {
154*ef8d499eSDavid van Moolenbroek 		struct {
155*ef8d499eSDavid van Moolenbroek 			struct mempool_header *mlbuf_header2;
156*ef8d499eSDavid van Moolenbroek 			LIST_ENTRY(mempool_large_buf) mlbuf_next;
157*ef8d499eSDavid van Moolenbroek 		} mlbu_free;
158*ef8d499eSDavid van Moolenbroek 		char mlbu_data[MEMPOOL_LARGE_SIZE];
159*ef8d499eSDavid van Moolenbroek 	} mlb_u;
160*ef8d499eSDavid van Moolenbroek };
161*ef8d499eSDavid van Moolenbroek #define mlb_header2 mlb_u.mlbu_free.mlbuf_header2
162*ef8d499eSDavid van Moolenbroek #define mlb_next mlb_u.mlbu_free.mlbuf_next
163*ef8d499eSDavid van Moolenbroek #define mlb_data mlb_u.mlbu_data
164*ef8d499eSDavid van Moolenbroek 
165*ef8d499eSDavid van Moolenbroek /* Small buffer.  Same idea, different size. */
166*ef8d499eSDavid van Moolenbroek struct mempool_small_buf {
167*ef8d499eSDavid van Moolenbroek 	struct mempool_header *msb_header;
168*ef8d499eSDavid van Moolenbroek 	union {
169*ef8d499eSDavid van Moolenbroek 		struct {
170*ef8d499eSDavid van Moolenbroek 			struct mempool_header *msbuf_header2;
171*ef8d499eSDavid van Moolenbroek 			TAILQ_ENTRY(mempool_small_buf) msbuf_next;
172*ef8d499eSDavid van Moolenbroek 		} msbu_free;
173*ef8d499eSDavid van Moolenbroek 		char msbu_data[MEMPOOL_SMALL_SIZE];
174*ef8d499eSDavid van Moolenbroek 	} msb_u;
175*ef8d499eSDavid van Moolenbroek };
176*ef8d499eSDavid van Moolenbroek #define msb_header2 msb_u.msbu_free.msbuf_header2
177*ef8d499eSDavid van Moolenbroek #define msb_next msb_u.msbu_free.msbuf_next
178*ef8d499eSDavid van Moolenbroek #define msb_data msb_u.msbu_data
179*ef8d499eSDavid van Moolenbroek 
180*ef8d499eSDavid van Moolenbroek /*
181*ef8d499eSDavid van Moolenbroek  * A large slab, including header, other per-slab fields, and large buffers.
182*ef8d499eSDavid van Moolenbroek  * Each of these structures is on exactly one of three slab lists, depending
183*ef8d499eSDavid van Moolenbroek  * on whether all its buffers are free (empty), some but not all of its buffers
184*ef8d499eSDavid van Moolenbroek  * are in use (partial), or all of its buffers are in use (full).  The mls_next
185*ef8d499eSDavid van Moolenbroek  * field is used for that list.  The mls_free field is the per-slab list of
186*ef8d499eSDavid van Moolenbroek  * free buffers.
187*ef8d499eSDavid van Moolenbroek  */
188*ef8d499eSDavid van Moolenbroek struct mempool_large_slab {
189*ef8d499eSDavid van Moolenbroek 	struct mempool_header mls_header;		/* MUST be first */
190*ef8d499eSDavid van Moolenbroek 	LIST_ENTRY(mempool_large_slab) mls_next;
191*ef8d499eSDavid van Moolenbroek 	LIST_HEAD(, mempool_large_buf) mls_free;
192*ef8d499eSDavid van Moolenbroek 	struct mempool_large_buf mls_buf[MEMPOOL_LARGE_COUNT];
193*ef8d499eSDavid van Moolenbroek };
194*ef8d499eSDavid van Moolenbroek 
195*ef8d499eSDavid van Moolenbroek /* The three slab lists for large slabs, as described above. */
196*ef8d499eSDavid van Moolenbroek static LIST_HEAD(, mempool_large_slab) mempool_empty_slabs;
197*ef8d499eSDavid van Moolenbroek static LIST_HEAD(, mempool_large_slab) mempool_partial_slabs;
198*ef8d499eSDavid van Moolenbroek static LIST_HEAD(, mempool_large_slab) mempool_full_slabs;
199*ef8d499eSDavid van Moolenbroek 
200*ef8d499eSDavid van Moolenbroek /*
201*ef8d499eSDavid van Moolenbroek  * A small slab, including header and small buffers.  We use unified free lists
202*ef8d499eSDavid van Moolenbroek  * for small buffers, and these small slabs are not part of any lists
203*ef8d499eSDavid van Moolenbroek  * themselves, so we need neither of the two fields from large slabs for that.
204*ef8d499eSDavid van Moolenbroek  */
205*ef8d499eSDavid van Moolenbroek struct mempool_small_slab {
206*ef8d499eSDavid van Moolenbroek 	struct mempool_header mss_header;		/* MUST be first */
207*ef8d499eSDavid van Moolenbroek 	struct mempool_small_buf mss_buf[MEMPOOL_SMALL_COUNT];
208*ef8d499eSDavid van Moolenbroek };
209*ef8d499eSDavid van Moolenbroek 
210*ef8d499eSDavid van Moolenbroek /*
211*ef8d499eSDavid van Moolenbroek  * The free lists for static small buffers (from the static pool, see below)
212*ef8d499eSDavid van Moolenbroek  * and dynamic small buffers (as obtained by splitting large buffers).
213*ef8d499eSDavid van Moolenbroek  */
214*ef8d499eSDavid van Moolenbroek static TAILQ_HEAD(, mempool_small_buf) mempool_small_static_freelist;
215*ef8d499eSDavid van Moolenbroek static TAILQ_HEAD(, mempool_small_buf) mempool_small_dynamic_freelist;
216*ef8d499eSDavid van Moolenbroek 
217*ef8d499eSDavid van Moolenbroek /*
218*ef8d499eSDavid van Moolenbroek  * A static pool of small buffers.  Small buffers are somewhat more important
219*ef8d499eSDavid van Moolenbroek  * than large buffers, because they are used for packet headers.  The purpose
220*ef8d499eSDavid van Moolenbroek  * of this static pool is to be able to make progress even if all large buffers
221*ef8d499eSDavid van Moolenbroek  * are allocated for data, typically in the case that the system is low on
222*ef8d499eSDavid van Moolenbroek  * memory.  Note that the number of static small buffers is the given number of
223*ef8d499eSDavid van Moolenbroek  * small slabs multiplied by MEMPOOL_SMALL_COUNT, hence the division.
224*ef8d499eSDavid van Moolenbroek  */
225*ef8d499eSDavid van Moolenbroek #define MEMPOOL_SMALL_SLABS	(256 / MEMPOOL_SMALL_COUNT)
226*ef8d499eSDavid van Moolenbroek 
227*ef8d499eSDavid van Moolenbroek static struct mempool_small_slab mempool_small_pool[MEMPOOL_SMALL_SLABS];
228*ef8d499eSDavid van Moolenbroek 
229*ef8d499eSDavid van Moolenbroek /*
230*ef8d499eSDavid van Moolenbroek  * The following setting (mempool_max_slabs) can be changed through sysctl(7).
231*ef8d499eSDavid van Moolenbroek  * As such it may be set by userland to a completely arbitrary value and must
232*ef8d499eSDavid van Moolenbroek  * be sanity-checked before any actual use.  The default is picked such that
233*ef8d499eSDavid van Moolenbroek  * all TCP sockets can fill up their send and receive queues: (TCP_SNDBUF_DEF +
234*ef8d499eSDavid van Moolenbroek  * TCP_RCVBUF_DEF) * NR_TCPSOCK / (MEMPOOL_BUFSIZE * MEMPOOL_LARGE_COUNT) =
235*ef8d499eSDavid van Moolenbroek  * (32768 + 32768) * 256 / (512 * 512) = 64.  We put in the resulting number
236*ef8d499eSDavid van Moolenbroek  * rather than the formula because not all those definitions are public.
237*ef8d499eSDavid van Moolenbroek  */
238*ef8d499eSDavid van Moolenbroek #define MEMPOOL_DEFAULT_MAX_SLABS	64	/* about 17 MB of memory */
239*ef8d499eSDavid van Moolenbroek 
240*ef8d499eSDavid van Moolenbroek static int mempool_max_slabs;	/* maximum number of large slabs */
241*ef8d499eSDavid van Moolenbroek static int mempool_nr_slabs;	/* current number of large slabs */
242*ef8d499eSDavid van Moolenbroek 
243*ef8d499eSDavid van Moolenbroek static int mempool_nr_large;	/* current number of large buffers */
244*ef8d499eSDavid van Moolenbroek static int mempool_used_large;	/* large buffers currently in use */
245*ef8d499eSDavid van Moolenbroek static int mempool_used_small;	/* small buffers currently in use */
246*ef8d499eSDavid van Moolenbroek 
247*ef8d499eSDavid van Moolenbroek /*
248*ef8d499eSDavid van Moolenbroek  * Number of clock ticks between timer invocations.  The timer is used to
249*ef8d499eSDavid van Moolenbroek  * deallocate unused slabs.
250*ef8d499eSDavid van Moolenbroek  */
251*ef8d499eSDavid van Moolenbroek #define MEMPOOL_TIMER_TICKS	(10 * sys_hz())
252*ef8d499eSDavid van Moolenbroek 
253*ef8d499eSDavid van Moolenbroek static minix_timer_t mempool_timer;
254*ef8d499eSDavid van Moolenbroek 
255*ef8d499eSDavid van Moolenbroek static int mempool_defer_alloc;		/* allocation failed, defer next try */
256*ef8d499eSDavid van Moolenbroek 
257*ef8d499eSDavid van Moolenbroek /* The CTL_MINIX MINIX_LWIP "mempool" subtree.  Dynamically numbered. */
258*ef8d499eSDavid van Moolenbroek static struct rmib_node minix_lwip_mempool_table[] = {
259*ef8d499eSDavid van Moolenbroek 	RMIB_INTPTR(RMIB_RW, &mempool_max_slabs, "slab_max",
260*ef8d499eSDavid van Moolenbroek 	    "Maximum number of memory slabs (configurable)"),
261*ef8d499eSDavid van Moolenbroek 	RMIB_INTPTR(RMIB_RO, &mempool_nr_slabs, "slab_num",
262*ef8d499eSDavid van Moolenbroek 	    "Current number of memory slabs"),
263*ef8d499eSDavid van Moolenbroek 	RMIB_INT(RMIB_RO, sizeof(struct mempool_large_slab), "slab_size",
264*ef8d499eSDavid van Moolenbroek 	    "Byte size of a single memory slab"),
265*ef8d499eSDavid van Moolenbroek 	RMIB_INT(RMIB_RO, MEMPOOL_LARGE_COUNT, "slab_bufs",
266*ef8d499eSDavid van Moolenbroek 	    "Number of large buffers per memory slab"),
267*ef8d499eSDavid van Moolenbroek 	RMIB_INTPTR(RMIB_RO, &mempool_nr_large, "large_num",
268*ef8d499eSDavid van Moolenbroek 	    "Current total number of large buffers"),
269*ef8d499eSDavid van Moolenbroek 	RMIB_INTPTR(RMIB_RO, &mempool_used_large, "large_used",
270*ef8d499eSDavid van Moolenbroek 	    "Current number of used large buffers"),
271*ef8d499eSDavid van Moolenbroek 	RMIB_INT(RMIB_RO, MEMPOOL_LARGE_SIZE, "large_size",
272*ef8d499eSDavid van Moolenbroek 	    "Byte size of a single large buffer"),
273*ef8d499eSDavid van Moolenbroek 	RMIB_INTPTR(RMIB_RO, &mempool_used_small, "small_used",
274*ef8d499eSDavid van Moolenbroek 	    "Current number of used small buffers"),
275*ef8d499eSDavid van Moolenbroek 	RMIB_INT(RMIB_RO, MEMPOOL_SMALL_SIZE, "small_size",
276*ef8d499eSDavid van Moolenbroek 	    "Byte size of a single small buffer"),
277*ef8d499eSDavid van Moolenbroek };
278*ef8d499eSDavid van Moolenbroek 
279*ef8d499eSDavid van Moolenbroek static struct rmib_node minix_lwip_mempool_node =
280*ef8d499eSDavid van Moolenbroek     RMIB_NODE(RMIB_RO, minix_lwip_mempool_table, "mempool",
281*ef8d499eSDavid van Moolenbroek 	"Memory pool settings");
282*ef8d499eSDavid van Moolenbroek 
283*ef8d499eSDavid van Moolenbroek /*
284*ef8d499eSDavid van Moolenbroek  * Initialize the given "slab" of small buffers.  The slab may either come from
285*ef8d499eSDavid van Moolenbroek  * the statically allocated pool ('is_static' is TRUE) or a single large buffer
286*ef8d499eSDavid van Moolenbroek  * that we aim to chop up into small buffers.
287*ef8d499eSDavid van Moolenbroek  */
288*ef8d499eSDavid van Moolenbroek static void
mempool_prepare_small(struct mempool_small_slab * mss,int is_static)289*ef8d499eSDavid van Moolenbroek mempool_prepare_small(struct mempool_small_slab * mss, int is_static)
290*ef8d499eSDavid van Moolenbroek {
291*ef8d499eSDavid van Moolenbroek 	struct mempool_small_buf *msb;
292*ef8d499eSDavid van Moolenbroek 	unsigned int count;
293*ef8d499eSDavid van Moolenbroek 
294*ef8d499eSDavid van Moolenbroek 	mss->mss_header.mh_flags = MHF_SMALL | ((is_static) ? MHF_STATIC : 0);
295*ef8d499eSDavid van Moolenbroek 	mss->mss_header.mh_inuse = 0;
296*ef8d499eSDavid van Moolenbroek 
297*ef8d499eSDavid van Moolenbroek 	msb = mss->mss_buf;
298*ef8d499eSDavid van Moolenbroek 
299*ef8d499eSDavid van Moolenbroek 	for (count = 0; count < MEMPOOL_SMALL_COUNT; count++, msb++) {
300*ef8d499eSDavid van Moolenbroek 		msb->msb_header = NULL;
301*ef8d499eSDavid van Moolenbroek 		msb->msb_header2 = &mss->mss_header;
302*ef8d499eSDavid van Moolenbroek 
303*ef8d499eSDavid van Moolenbroek 		if (is_static)
304*ef8d499eSDavid van Moolenbroek 			TAILQ_INSERT_HEAD(&mempool_small_static_freelist, msb,
305*ef8d499eSDavid van Moolenbroek 			    msb_next);
306*ef8d499eSDavid van Moolenbroek 		else
307*ef8d499eSDavid van Moolenbroek 			TAILQ_INSERT_HEAD(&mempool_small_dynamic_freelist, msb,
308*ef8d499eSDavid van Moolenbroek 			    msb_next);
309*ef8d499eSDavid van Moolenbroek 	}
310*ef8d499eSDavid van Moolenbroek }
311*ef8d499eSDavid van Moolenbroek 
312*ef8d499eSDavid van Moolenbroek /*
313*ef8d499eSDavid van Moolenbroek  * Allocate a new slab for large buffers, if allowed by policy and possible.
314*ef8d499eSDavid van Moolenbroek  */
315*ef8d499eSDavid van Moolenbroek static void
mempool_new_slab(void)316*ef8d499eSDavid van Moolenbroek mempool_new_slab(void)
317*ef8d499eSDavid van Moolenbroek {
318*ef8d499eSDavid van Moolenbroek 	struct mempool_large_slab *mls;
319*ef8d499eSDavid van Moolenbroek 	struct mempool_large_buf *mlb;
320*ef8d499eSDavid van Moolenbroek 	unsigned int count;
321*ef8d499eSDavid van Moolenbroek 
322*ef8d499eSDavid van Moolenbroek 	/*
323*ef8d499eSDavid van Moolenbroek 	 * See if allocating a new slab would result in overrunning the
324*ef8d499eSDavid van Moolenbroek 	 * configured maximum number of large buffers.  Round the maximum,
325*ef8d499eSDavid van Moolenbroek 	 * which is probably what the user intended.
326*ef8d499eSDavid van Moolenbroek 	 */
327*ef8d499eSDavid van Moolenbroek 	if (mempool_cur_buffers() + MEMPOOL_LARGE_COUNT / 2 >
328*ef8d499eSDavid van Moolenbroek 	    mempool_max_buffers()) {
329*ef8d499eSDavid van Moolenbroek 		assert(mempool_nr_slabs > 0);
330*ef8d499eSDavid van Moolenbroek 
331*ef8d499eSDavid van Moolenbroek 		return;
332*ef8d499eSDavid van Moolenbroek 	}
333*ef8d499eSDavid van Moolenbroek 
334*ef8d499eSDavid van Moolenbroek 	/*
335*ef8d499eSDavid van Moolenbroek 	 * If a previous allocation failed before during this timer interval,
336*ef8d499eSDavid van Moolenbroek 	 * do not try again now.
337*ef8d499eSDavid van Moolenbroek 	 */
338*ef8d499eSDavid van Moolenbroek 	if (mempool_defer_alloc)
339*ef8d499eSDavid van Moolenbroek 		return;
340*ef8d499eSDavid van Moolenbroek 
341*ef8d499eSDavid van Moolenbroek 	/*
342*ef8d499eSDavid van Moolenbroek 	 * Allocate the slab.  Preallocate the memory, or we might crash later
343*ef8d499eSDavid van Moolenbroek 	 * during low-memory conditions.  If allocation fails, simply do
344*ef8d499eSDavid van Moolenbroek 	 * nothing further.  The caller will check the free lists.
345*ef8d499eSDavid van Moolenbroek 	 */
346*ef8d499eSDavid van Moolenbroek 	mls = (struct mempool_large_slab *)mmap(NULL,
347*ef8d499eSDavid van Moolenbroek 	    sizeof(struct mempool_large_slab), PROT_READ | PROT_WRITE,
348*ef8d499eSDavid van Moolenbroek 	    MAP_ANON | MAP_PRIVATE | MAP_PREALLOC, -1, 0);
349*ef8d499eSDavid van Moolenbroek 
350*ef8d499eSDavid van Moolenbroek 	if (mls == MAP_FAILED) {
351*ef8d499eSDavid van Moolenbroek 		if (mempool_nr_slabs == 0)
352*ef8d499eSDavid van Moolenbroek 			panic("unable to allocate initial memory pool");
353*ef8d499eSDavid van Moolenbroek 
354*ef8d499eSDavid van Moolenbroek 		/*
355*ef8d499eSDavid van Moolenbroek 		 * Do not keep hammering VM with mmap requests when the system
356*ef8d499eSDavid van Moolenbroek 		 * is out of memory.  Try again after the next timer tick.
357*ef8d499eSDavid van Moolenbroek 		 */
358*ef8d499eSDavid van Moolenbroek 		mempool_defer_alloc = TRUE;
359*ef8d499eSDavid van Moolenbroek 
360*ef8d499eSDavid van Moolenbroek 		return;
361*ef8d499eSDavid van Moolenbroek 	}
362*ef8d499eSDavid van Moolenbroek 
363*ef8d499eSDavid van Moolenbroek 	/* Initialize the new slab. */
364*ef8d499eSDavid van Moolenbroek 	mls->mls_header.mh_flags = 0;
365*ef8d499eSDavid van Moolenbroek 	mls->mls_header.mh_inuse = 0;
366*ef8d499eSDavid van Moolenbroek 
367*ef8d499eSDavid van Moolenbroek 	mlb = mls->mls_buf;
368*ef8d499eSDavid van Moolenbroek 
369*ef8d499eSDavid van Moolenbroek 	LIST_INIT(&mls->mls_free);
370*ef8d499eSDavid van Moolenbroek 
371*ef8d499eSDavid van Moolenbroek 	for (count = 0; count < MEMPOOL_LARGE_COUNT; count++, mlb++) {
372*ef8d499eSDavid van Moolenbroek 		mlb->mlb_header = NULL;
373*ef8d499eSDavid van Moolenbroek 		mlb->mlb_header2 = &mls->mls_header;
374*ef8d499eSDavid van Moolenbroek 
375*ef8d499eSDavid van Moolenbroek 		LIST_INSERT_HEAD(&mls->mls_free, mlb, mlb_next);
376*ef8d499eSDavid van Moolenbroek 	}
377*ef8d499eSDavid van Moolenbroek 
378*ef8d499eSDavid van Moolenbroek 	LIST_INSERT_HEAD(&mempool_empty_slabs, mls, mls_next);
379*ef8d499eSDavid van Moolenbroek 
380*ef8d499eSDavid van Moolenbroek 	mempool_nr_slabs++;
381*ef8d499eSDavid van Moolenbroek 	mempool_nr_large += MEMPOOL_LARGE_COUNT;
382*ef8d499eSDavid van Moolenbroek }
383*ef8d499eSDavid van Moolenbroek 
384*ef8d499eSDavid van Moolenbroek /*
385*ef8d499eSDavid van Moolenbroek  * Deallocate a slab for large buffers, if allowed.
386*ef8d499eSDavid van Moolenbroek  */
387*ef8d499eSDavid van Moolenbroek static void
mempool_destroy_slab(struct mempool_large_slab * mls)388*ef8d499eSDavid van Moolenbroek mempool_destroy_slab(struct mempool_large_slab * mls)
389*ef8d499eSDavid van Moolenbroek {
390*ef8d499eSDavid van Moolenbroek 
391*ef8d499eSDavid van Moolenbroek 	assert(mempool_nr_slabs > 0);
392*ef8d499eSDavid van Moolenbroek 
393*ef8d499eSDavid van Moolenbroek 	assert(!(mls->mls_header.mh_flags & MHF_SMALL));
394*ef8d499eSDavid van Moolenbroek 	assert(mls->mls_header.mh_inuse == 0);
395*ef8d499eSDavid van Moolenbroek 
396*ef8d499eSDavid van Moolenbroek 	/* Never deallocate the last large slab. */
397*ef8d499eSDavid van Moolenbroek 	if (mempool_nr_slabs == 1)
398*ef8d499eSDavid van Moolenbroek 		return;
399*ef8d499eSDavid van Moolenbroek 
400*ef8d499eSDavid van Moolenbroek 	LIST_REMOVE(mls, mls_next);
401*ef8d499eSDavid van Moolenbroek 
402*ef8d499eSDavid van Moolenbroek 	if (munmap(mls, sizeof(*mls)) != 0)
403*ef8d499eSDavid van Moolenbroek 		panic("munmap failed: %d", -errno);
404*ef8d499eSDavid van Moolenbroek 
405*ef8d499eSDavid van Moolenbroek 	assert(mempool_nr_large > MEMPOOL_LARGE_COUNT);
406*ef8d499eSDavid van Moolenbroek 	mempool_nr_large -= MEMPOOL_LARGE_COUNT;
407*ef8d499eSDavid van Moolenbroek 	mempool_nr_slabs--;
408*ef8d499eSDavid van Moolenbroek }
409*ef8d499eSDavid van Moolenbroek 
410*ef8d499eSDavid van Moolenbroek /*
411*ef8d499eSDavid van Moolenbroek  * Regular timer.  Deallocate empty slabs already marked for deallocation, and
412*ef8d499eSDavid van Moolenbroek  * mark any other empty slabs for deallocation.
413*ef8d499eSDavid van Moolenbroek  */
414*ef8d499eSDavid van Moolenbroek static void
mempool_tick(int arg __unused)415*ef8d499eSDavid van Moolenbroek mempool_tick(int arg __unused)
416*ef8d499eSDavid van Moolenbroek {
417*ef8d499eSDavid van Moolenbroek 	struct mempool_large_slab *mls, *tmls;
418*ef8d499eSDavid van Moolenbroek 
419*ef8d499eSDavid van Moolenbroek 	/*
420*ef8d499eSDavid van Moolenbroek 	 * Go through all the empty slabs, destroying marked slabs and marking
421*ef8d499eSDavid van Moolenbroek 	 * unmarked slabs.
422*ef8d499eSDavid van Moolenbroek 	 */
423*ef8d499eSDavid van Moolenbroek 	LIST_FOREACH_SAFE(mls, &mempool_empty_slabs, mls_next, tmls) {
424*ef8d499eSDavid van Moolenbroek 		if (mls->mls_header.mh_flags & MHF_MARKED)
425*ef8d499eSDavid van Moolenbroek 			mempool_destroy_slab(mls);
426*ef8d499eSDavid van Moolenbroek 		else
427*ef8d499eSDavid van Moolenbroek 			mls->mls_header.mh_flags |= MHF_MARKED;
428*ef8d499eSDavid van Moolenbroek 	}
429*ef8d499eSDavid van Moolenbroek 
430*ef8d499eSDavid van Moolenbroek 	/*
431*ef8d499eSDavid van Moolenbroek 	 * If allocation failed during the last interval, allow a new attempt
432*ef8d499eSDavid van Moolenbroek 	 * during the next.
433*ef8d499eSDavid van Moolenbroek 	 */
434*ef8d499eSDavid van Moolenbroek 	mempool_defer_alloc = FALSE;
435*ef8d499eSDavid van Moolenbroek 
436*ef8d499eSDavid van Moolenbroek 	/* Set the next timer. */
437*ef8d499eSDavid van Moolenbroek 	set_timer(&mempool_timer, MEMPOOL_TIMER_TICKS, mempool_tick, 0);
438*ef8d499eSDavid van Moolenbroek }
439*ef8d499eSDavid van Moolenbroek 
440*ef8d499eSDavid van Moolenbroek /*
441*ef8d499eSDavid van Moolenbroek  * Initialize the memory pool module.
442*ef8d499eSDavid van Moolenbroek  */
443*ef8d499eSDavid van Moolenbroek void
mempool_init(void)444*ef8d499eSDavid van Moolenbroek mempool_init(void)
445*ef8d499eSDavid van Moolenbroek {
446*ef8d499eSDavid van Moolenbroek 	unsigned int slot;
447*ef8d499eSDavid van Moolenbroek 
448*ef8d499eSDavid van Moolenbroek 	/* These checks are for absolutely essential points. */
449*ef8d499eSDavid van Moolenbroek 	assert(sizeof(void *) == MEM_ALIGNMENT);
450*ef8d499eSDavid van Moolenbroek 	assert(sizeof(struct mempool_small_slab) <= MEMPOOL_LARGE_SIZE);
451*ef8d499eSDavid van Moolenbroek 	assert(offsetof(struct mempool_small_buf, msb_data) == sizeof(void *));
452*ef8d499eSDavid van Moolenbroek 	assert(offsetof(struct mempool_large_buf, mlb_data) == sizeof(void *));
453*ef8d499eSDavid van Moolenbroek 
454*ef8d499eSDavid van Moolenbroek 	/* Initialize module-local variables. */
455*ef8d499eSDavid van Moolenbroek 	LIST_INIT(&mempool_empty_slabs);
456*ef8d499eSDavid van Moolenbroek 	LIST_INIT(&mempool_partial_slabs);
457*ef8d499eSDavid van Moolenbroek 	LIST_INIT(&mempool_full_slabs);
458*ef8d499eSDavid van Moolenbroek 
459*ef8d499eSDavid van Moolenbroek 	TAILQ_INIT(&mempool_small_static_freelist);
460*ef8d499eSDavid van Moolenbroek 	TAILQ_INIT(&mempool_small_dynamic_freelist);
461*ef8d499eSDavid van Moolenbroek 
462*ef8d499eSDavid van Moolenbroek 	mempool_max_slabs = MEMPOOL_DEFAULT_MAX_SLABS;
463*ef8d499eSDavid van Moolenbroek 	mempool_nr_slabs = 0;
464*ef8d499eSDavid van Moolenbroek 
465*ef8d499eSDavid van Moolenbroek 	mempool_nr_large = 0;
466*ef8d499eSDavid van Moolenbroek 	mempool_used_large = 0;
467*ef8d499eSDavid van Moolenbroek 	mempool_used_small = 0;
468*ef8d499eSDavid van Moolenbroek 
469*ef8d499eSDavid van Moolenbroek 	mempool_defer_alloc = FALSE;
470*ef8d499eSDavid van Moolenbroek 
471*ef8d499eSDavid van Moolenbroek 	/* Initialize the static pool of small buffers. */
472*ef8d499eSDavid van Moolenbroek 	for (slot = 0; slot < __arraycount(mempool_small_pool); slot++)
473*ef8d499eSDavid van Moolenbroek 		mempool_prepare_small(&mempool_small_pool[slot],
474*ef8d499eSDavid van Moolenbroek 		    TRUE /*is_static*/);
475*ef8d499eSDavid van Moolenbroek 
476*ef8d499eSDavid van Moolenbroek 	/*
477*ef8d499eSDavid van Moolenbroek 	 * Allocate one large slab.  The service needs at least one large slab
478*ef8d499eSDavid van Moolenbroek 	 * for basic operation, and therefore will never deallocate the last.
479*ef8d499eSDavid van Moolenbroek 	 */
480*ef8d499eSDavid van Moolenbroek 	mempool_new_slab();
481*ef8d499eSDavid van Moolenbroek 
482*ef8d499eSDavid van Moolenbroek 	/* Set a regular low-frequency timer to deallocate unused slabs. */
483*ef8d499eSDavid van Moolenbroek 	set_timer(&mempool_timer, MEMPOOL_TIMER_TICKS, mempool_tick, 0);
484*ef8d499eSDavid van Moolenbroek 
485*ef8d499eSDavid van Moolenbroek 	/* Register the minix.lwip.mempool subtree. */
486*ef8d499eSDavid van Moolenbroek 	mibtree_register_lwip(&minix_lwip_mempool_node);
487*ef8d499eSDavid van Moolenbroek }
488*ef8d499eSDavid van Moolenbroek 
489*ef8d499eSDavid van Moolenbroek /*
490*ef8d499eSDavid van Moolenbroek  * Return the total number of large buffers currently in the system, regardless
491*ef8d499eSDavid van Moolenbroek  * of allocation status.
492*ef8d499eSDavid van Moolenbroek  */
493*ef8d499eSDavid van Moolenbroek unsigned int
mempool_cur_buffers(void)494*ef8d499eSDavid van Moolenbroek mempool_cur_buffers(void)
495*ef8d499eSDavid van Moolenbroek {
496*ef8d499eSDavid van Moolenbroek 
497*ef8d499eSDavid van Moolenbroek 	return mempool_nr_large;
498*ef8d499eSDavid van Moolenbroek }
499*ef8d499eSDavid van Moolenbroek 
500*ef8d499eSDavid van Moolenbroek /*
501*ef8d499eSDavid van Moolenbroek  * Return the maximum number of large buffers that the system has been allowed
502*ef8d499eSDavid van Moolenbroek  * to allocate.  Note that due to low-memory conditions, this maximum may not
503*ef8d499eSDavid van Moolenbroek  * be allocated in practice even when desired.
504*ef8d499eSDavid van Moolenbroek  */
505*ef8d499eSDavid van Moolenbroek unsigned int
mempool_max_buffers(void)506*ef8d499eSDavid van Moolenbroek mempool_max_buffers(void)
507*ef8d499eSDavid van Moolenbroek {
508*ef8d499eSDavid van Moolenbroek 
509*ef8d499eSDavid van Moolenbroek 	if (mempool_max_slabs <= 1)
510*ef8d499eSDavid van Moolenbroek 		return MEMPOOL_LARGE_COUNT;
511*ef8d499eSDavid van Moolenbroek 
512*ef8d499eSDavid van Moolenbroek 	if ((size_t)mempool_max_slabs >
513*ef8d499eSDavid van Moolenbroek 	    INT_MAX / sizeof(struct mempool_large_slab))
514*ef8d499eSDavid van Moolenbroek 		return INT_MAX / sizeof(struct mempool_large_slab);
515*ef8d499eSDavid van Moolenbroek 
516*ef8d499eSDavid van Moolenbroek 	return (size_t)mempool_max_slabs * MEMPOOL_LARGE_COUNT;
517*ef8d499eSDavid van Moolenbroek }
518*ef8d499eSDavid van Moolenbroek 
519*ef8d499eSDavid van Moolenbroek /*
520*ef8d499eSDavid van Moolenbroek  * Allocate a large buffer, either by taking one off a free list or by
521*ef8d499eSDavid van Moolenbroek  * allocating a new large slab.  On success, return a pointer to the data area
522*ef8d499eSDavid van Moolenbroek  * of the large buffer.  This data area is exactly MEMPOOL_LARGE_SIZE bytes in
523*ef8d499eSDavid van Moolenbroek  * size.  If no large buffer could be allocated, return NULL.
524*ef8d499eSDavid van Moolenbroek  */
525*ef8d499eSDavid van Moolenbroek static void *
mempool_alloc_large(void)526*ef8d499eSDavid van Moolenbroek mempool_alloc_large(void)
527*ef8d499eSDavid van Moolenbroek {
528*ef8d499eSDavid van Moolenbroek 	struct mempool_large_slab *mls;
529*ef8d499eSDavid van Moolenbroek 	struct mempool_large_buf *mlb;
530*ef8d499eSDavid van Moolenbroek 
531*ef8d499eSDavid van Moolenbroek 	/*
532*ef8d499eSDavid van Moolenbroek 	 * Find a large slab that has free large blocks.  As is standard for
533*ef8d499eSDavid van Moolenbroek 	 * slab allocation, favor partially used slabs over empty slabs for
534*ef8d499eSDavid van Moolenbroek 	 * eventual consolidation.  If both lists are empty, try allocating a
535*ef8d499eSDavid van Moolenbroek 	 * new slab.  If that fails, we are out of memory, and return NULL.
536*ef8d499eSDavid van Moolenbroek 	 */
537*ef8d499eSDavid van Moolenbroek 	if (!LIST_EMPTY(&mempool_partial_slabs))
538*ef8d499eSDavid van Moolenbroek 		mls = LIST_FIRST(&mempool_partial_slabs);
539*ef8d499eSDavid van Moolenbroek 	else {
540*ef8d499eSDavid van Moolenbroek 		if (LIST_EMPTY(&mempool_empty_slabs)) {
541*ef8d499eSDavid van Moolenbroek 			mempool_new_slab();
542*ef8d499eSDavid van Moolenbroek 
543*ef8d499eSDavid van Moolenbroek 			if (LIST_EMPTY(&mempool_empty_slabs))
544*ef8d499eSDavid van Moolenbroek 				return NULL; /* out of memory */
545*ef8d499eSDavid van Moolenbroek 		}
546*ef8d499eSDavid van Moolenbroek 
547*ef8d499eSDavid van Moolenbroek 		mls = LIST_FIRST(&mempool_empty_slabs);
548*ef8d499eSDavid van Moolenbroek 	}
549*ef8d499eSDavid van Moolenbroek 
550*ef8d499eSDavid van Moolenbroek 	/* Allocate a block from the slab that we picked. */
551*ef8d499eSDavid van Moolenbroek 	assert(mls != NULL);
552*ef8d499eSDavid van Moolenbroek 	assert(!LIST_EMPTY(&mls->mls_free));
553*ef8d499eSDavid van Moolenbroek 
554*ef8d499eSDavid van Moolenbroek 	mlb = LIST_FIRST(&mls->mls_free);
555*ef8d499eSDavid van Moolenbroek 	LIST_REMOVE(mlb, mlb_next);
556*ef8d499eSDavid van Moolenbroek 
557*ef8d499eSDavid van Moolenbroek 	assert(mlb->mlb_header == NULL);
558*ef8d499eSDavid van Moolenbroek 	assert(mlb->mlb_header2 == &mls->mls_header);
559*ef8d499eSDavid van Moolenbroek 
560*ef8d499eSDavid van Moolenbroek 	mlb->mlb_header = &mls->mls_header;
561*ef8d499eSDavid van Moolenbroek 
562*ef8d499eSDavid van Moolenbroek 	/*
563*ef8d499eSDavid van Moolenbroek 	 * Adjust accounting for the large slab, which may involve moving it
564*ef8d499eSDavid van Moolenbroek 	 * to another list.
565*ef8d499eSDavid van Moolenbroek 	 */
566*ef8d499eSDavid van Moolenbroek 	assert(mls->mls_header.mh_inuse < MEMPOOL_LARGE_COUNT);
567*ef8d499eSDavid van Moolenbroek 	mls->mls_header.mh_inuse++;
568*ef8d499eSDavid van Moolenbroek 
569*ef8d499eSDavid van Moolenbroek 	if (mls->mls_header.mh_inuse == MEMPOOL_LARGE_COUNT) {
570*ef8d499eSDavid van Moolenbroek 		LIST_REMOVE(mls, mls_next);
571*ef8d499eSDavid van Moolenbroek 
572*ef8d499eSDavid van Moolenbroek 		LIST_INSERT_HEAD(&mempool_full_slabs, mls, mls_next);
573*ef8d499eSDavid van Moolenbroek 	} else if (mls->mls_header.mh_inuse == 1) {
574*ef8d499eSDavid van Moolenbroek 		LIST_REMOVE(mls, mls_next);
575*ef8d499eSDavid van Moolenbroek 
576*ef8d499eSDavid van Moolenbroek 		LIST_INSERT_HEAD(&mempool_partial_slabs, mls, mls_next);
577*ef8d499eSDavid van Moolenbroek 	}
578*ef8d499eSDavid van Moolenbroek 
579*ef8d499eSDavid van Moolenbroek 	assert(mempool_used_large < mempool_nr_large);
580*ef8d499eSDavid van Moolenbroek 	mempool_used_large++;
581*ef8d499eSDavid van Moolenbroek 
582*ef8d499eSDavid van Moolenbroek 	/* Return the block's data area. */
583*ef8d499eSDavid van Moolenbroek 	return (void *)mlb->mlb_data;
584*ef8d499eSDavid van Moolenbroek }
585*ef8d499eSDavid van Moolenbroek 
586*ef8d499eSDavid van Moolenbroek /*
587*ef8d499eSDavid van Moolenbroek  * Allocate a small buffer, either by taking one off a free list or by
588*ef8d499eSDavid van Moolenbroek  * allocating a large buffer and splitting it up in new free small buffers.  On
589*ef8d499eSDavid van Moolenbroek  * success, return a pointer to the data area of the small buffer.  This data
590*ef8d499eSDavid van Moolenbroek  * area is exactly MEMPOOL_SMALL_SIZE bytes in size.  If no small buffer could
591*ef8d499eSDavid van Moolenbroek  * be allocated, return NULL.
592*ef8d499eSDavid van Moolenbroek  */
593*ef8d499eSDavid van Moolenbroek static void *
mempool_alloc_small(void)594*ef8d499eSDavid van Moolenbroek mempool_alloc_small(void)
595*ef8d499eSDavid van Moolenbroek {
596*ef8d499eSDavid van Moolenbroek 	struct mempool_small_slab *mss;
597*ef8d499eSDavid van Moolenbroek 	struct mempool_small_buf *msb;
598*ef8d499eSDavid van Moolenbroek 	struct mempool_header *mh;
599*ef8d499eSDavid van Moolenbroek 
600*ef8d499eSDavid van Moolenbroek 	/*
601*ef8d499eSDavid van Moolenbroek 	 * Find a free small block and take it off the free list.  Try the
602*ef8d499eSDavid van Moolenbroek 	 * static free list before the dynamic one, so that after a peak in
603*ef8d499eSDavid van Moolenbroek 	 * buffer usage we are likely to be able to free up the dynamic slabs
604*ef8d499eSDavid van Moolenbroek 	 * quickly.  If both lists are empty, try allocating a large block to
605*ef8d499eSDavid van Moolenbroek 	 * divvy up into small blocks.  If that fails, we are out of memory.
606*ef8d499eSDavid van Moolenbroek 	 */
607*ef8d499eSDavid van Moolenbroek 	if (!TAILQ_EMPTY(&mempool_small_static_freelist)) {
608*ef8d499eSDavid van Moolenbroek 		msb = TAILQ_FIRST(&mempool_small_static_freelist);
609*ef8d499eSDavid van Moolenbroek 
610*ef8d499eSDavid van Moolenbroek 		TAILQ_REMOVE(&mempool_small_static_freelist, msb, msb_next);
611*ef8d499eSDavid van Moolenbroek 	} else {
612*ef8d499eSDavid van Moolenbroek 		if (TAILQ_EMPTY(&mempool_small_dynamic_freelist)) {
613*ef8d499eSDavid van Moolenbroek 			mss =
614*ef8d499eSDavid van Moolenbroek 			    (struct mempool_small_slab *)mempool_alloc_large();
615*ef8d499eSDavid van Moolenbroek 
616*ef8d499eSDavid van Moolenbroek 			if (mss == NULL)
617*ef8d499eSDavid van Moolenbroek 				return NULL; /* out of memory */
618*ef8d499eSDavid van Moolenbroek 
619*ef8d499eSDavid van Moolenbroek 			/* Initialize the small slab, including its blocks. */
620*ef8d499eSDavid van Moolenbroek 			mempool_prepare_small(mss, FALSE /*is_static*/);
621*ef8d499eSDavid van Moolenbroek 		}
622*ef8d499eSDavid van Moolenbroek 
623*ef8d499eSDavid van Moolenbroek 		msb = TAILQ_FIRST(&mempool_small_dynamic_freelist);
624*ef8d499eSDavid van Moolenbroek 		assert(msb != NULL);
625*ef8d499eSDavid van Moolenbroek 
626*ef8d499eSDavid van Moolenbroek 		TAILQ_REMOVE(&mempool_small_dynamic_freelist, msb, msb_next);
627*ef8d499eSDavid van Moolenbroek 	}
628*ef8d499eSDavid van Moolenbroek 
629*ef8d499eSDavid van Moolenbroek 	/* Mark the small block as allocated, and return its data area. */
630*ef8d499eSDavid van Moolenbroek 	assert(msb != NULL);
631*ef8d499eSDavid van Moolenbroek 
632*ef8d499eSDavid van Moolenbroek 	assert(msb->msb_header == NULL);
633*ef8d499eSDavid van Moolenbroek 	assert(msb->msb_header2 != NULL);
634*ef8d499eSDavid van Moolenbroek 
635*ef8d499eSDavid van Moolenbroek 	mh = msb->msb_header2;
636*ef8d499eSDavid van Moolenbroek 	msb->msb_header = mh;
637*ef8d499eSDavid van Moolenbroek 
638*ef8d499eSDavid van Moolenbroek 	assert(mh->mh_inuse < MEMPOOL_SMALL_COUNT);
639*ef8d499eSDavid van Moolenbroek 	mh->mh_inuse++;
640*ef8d499eSDavid van Moolenbroek 
641*ef8d499eSDavid van Moolenbroek 	mempool_used_small++;
642*ef8d499eSDavid van Moolenbroek 
643*ef8d499eSDavid van Moolenbroek 	return (void *)msb->msb_data;
644*ef8d499eSDavid van Moolenbroek }
645*ef8d499eSDavid van Moolenbroek 
646*ef8d499eSDavid van Moolenbroek /*
647*ef8d499eSDavid van Moolenbroek  * Memory pool wrapper function for malloc() calls from lwIP.
648*ef8d499eSDavid van Moolenbroek  */
649*ef8d499eSDavid van Moolenbroek void *
mempool_malloc(size_t size)650*ef8d499eSDavid van Moolenbroek mempool_malloc(size_t size)
651*ef8d499eSDavid van Moolenbroek {
652*ef8d499eSDavid van Moolenbroek 
653*ef8d499eSDavid van Moolenbroek 	/*
654*ef8d499eSDavid van Moolenbroek 	 * It is currently expected that there will be allocation attempts for
655*ef8d499eSDavid van Moolenbroek 	 * sizes larger than our large size, in particular for ICMP ping
656*ef8d499eSDavid van Moolenbroek 	 * replies as described elsewhere.  As such, we cannot print any
657*ef8d499eSDavid van Moolenbroek 	 * warnings here.  For now, refusing these excessive allocations should
658*ef8d499eSDavid van Moolenbroek 	 * not be a problem in practice.
659*ef8d499eSDavid van Moolenbroek 	 */
660*ef8d499eSDavid van Moolenbroek 	if (size > MEMPOOL_LARGE_SIZE)
661*ef8d499eSDavid van Moolenbroek 		return NULL;
662*ef8d499eSDavid van Moolenbroek 
663*ef8d499eSDavid van Moolenbroek 	if (size <= MEMPOOL_SMALL_SIZE)
664*ef8d499eSDavid van Moolenbroek 		return mempool_alloc_small();
665*ef8d499eSDavid van Moolenbroek 	else
666*ef8d499eSDavid van Moolenbroek 		return mempool_alloc_large();
667*ef8d499eSDavid van Moolenbroek }
668*ef8d499eSDavid van Moolenbroek 
669*ef8d499eSDavid van Moolenbroek /*
670*ef8d499eSDavid van Moolenbroek  * Memory pool wrapper function for free() calls from lwIP.
671*ef8d499eSDavid van Moolenbroek  */
672*ef8d499eSDavid van Moolenbroek void
mempool_free(void * ptr)673*ef8d499eSDavid van Moolenbroek mempool_free(void * ptr)
674*ef8d499eSDavid van Moolenbroek {
675*ef8d499eSDavid van Moolenbroek 	struct mempool_large_slab *mls;
676*ef8d499eSDavid van Moolenbroek 	struct mempool_large_buf *mlb;
677*ef8d499eSDavid van Moolenbroek 	struct mempool_small_slab *mss;
678*ef8d499eSDavid van Moolenbroek 	struct mempool_small_buf *msb;
679*ef8d499eSDavid van Moolenbroek 	struct mempool_header *mh;
680*ef8d499eSDavid van Moolenbroek 	unsigned int count;
681*ef8d499eSDavid van Moolenbroek 
682*ef8d499eSDavid van Moolenbroek 	/*
683*ef8d499eSDavid van Moolenbroek 	 * Get a pointer to the slab header, which is right before the data
684*ef8d499eSDavid van Moolenbroek 	 * area for both large and small buffers.  This pointer is NULL if the
685*ef8d499eSDavid van Moolenbroek 	 * buffer is free, which would indicate that something is very wrong.
686*ef8d499eSDavid van Moolenbroek 	 */
687*ef8d499eSDavid van Moolenbroek 	ptr = (void *)((char *)ptr - sizeof(mh));
688*ef8d499eSDavid van Moolenbroek 
689*ef8d499eSDavid van Moolenbroek 	memcpy(&mh, ptr, sizeof(mh));
690*ef8d499eSDavid van Moolenbroek 
691*ef8d499eSDavid van Moolenbroek 	if (mh == NULL)
692*ef8d499eSDavid van Moolenbroek 		panic("mempool_free called on unallocated object!");
693*ef8d499eSDavid van Moolenbroek 
694*ef8d499eSDavid van Moolenbroek 	/*
695*ef8d499eSDavid van Moolenbroek 	 * If the slab header says that the slab is for small buffers, deal
696*ef8d499eSDavid van Moolenbroek 	 * with that case first.  If we free up the last small buffer of a
697*ef8d499eSDavid van Moolenbroek 	 * dynamically allocated small slab, we also free up the entire small
698*ef8d499eSDavid van Moolenbroek 	 * slab, which is in fact the data area of a large buffer.
699*ef8d499eSDavid van Moolenbroek 	 */
700*ef8d499eSDavid van Moolenbroek 	if (mh->mh_flags & MHF_SMALL) {
701*ef8d499eSDavid van Moolenbroek 		/*
702*ef8d499eSDavid van Moolenbroek 		 * Move the small buffer onto the appropriate small free list.
703*ef8d499eSDavid van Moolenbroek 		 */
704*ef8d499eSDavid van Moolenbroek 		msb = (struct mempool_small_buf *)ptr;
705*ef8d499eSDavid van Moolenbroek 
706*ef8d499eSDavid van Moolenbroek 		msb->msb_header2 = mh;
707*ef8d499eSDavid van Moolenbroek 		msb->msb_header = NULL;
708*ef8d499eSDavid van Moolenbroek 
709*ef8d499eSDavid van Moolenbroek 		/*
710*ef8d499eSDavid van Moolenbroek 		 * Simple heuristic, unless the buffer is static: favor reuse
711*ef8d499eSDavid van Moolenbroek 		 * of small buffers in containers that are already in use
712*ef8d499eSDavid van Moolenbroek 		 * for other small buffers as well, for consolidation.
713*ef8d499eSDavid van Moolenbroek 		 */
714*ef8d499eSDavid van Moolenbroek 		if (mh->mh_flags & MHF_STATIC)
715*ef8d499eSDavid van Moolenbroek 			TAILQ_INSERT_HEAD(&mempool_small_static_freelist, msb,
716*ef8d499eSDavid van Moolenbroek 			    msb_next);
717*ef8d499eSDavid van Moolenbroek 		else if (mh->mh_inuse > 1)
718*ef8d499eSDavid van Moolenbroek 			TAILQ_INSERT_HEAD(&mempool_small_dynamic_freelist, msb,
719*ef8d499eSDavid van Moolenbroek 			    msb_next);
720*ef8d499eSDavid van Moolenbroek 		else
721*ef8d499eSDavid van Moolenbroek 			TAILQ_INSERT_TAIL(&mempool_small_dynamic_freelist, msb,
722*ef8d499eSDavid van Moolenbroek 			    msb_next);
723*ef8d499eSDavid van Moolenbroek 
724*ef8d499eSDavid van Moolenbroek 		assert(mh->mh_inuse > 0);
725*ef8d499eSDavid van Moolenbroek 		mh->mh_inuse--;
726*ef8d499eSDavid van Moolenbroek 
727*ef8d499eSDavid van Moolenbroek 		assert(mempool_used_small > 0);
728*ef8d499eSDavid van Moolenbroek 		mempool_used_small--;
729*ef8d499eSDavid van Moolenbroek 
730*ef8d499eSDavid van Moolenbroek 		/*
731*ef8d499eSDavid van Moolenbroek 		 * If the small buffer is statically allocated, or it was not
732*ef8d499eSDavid van Moolenbroek 		 * the last allocated small buffer in its containing large
733*ef8d499eSDavid van Moolenbroek 		 * buffer, then we are done.
734*ef8d499eSDavid van Moolenbroek 		 */
735*ef8d499eSDavid van Moolenbroek 		if (mh->mh_inuse > 0 || (mh->mh_flags & MHF_STATIC))
736*ef8d499eSDavid van Moolenbroek 			return;
737*ef8d499eSDavid van Moolenbroek 
738*ef8d499eSDavid van Moolenbroek 		/*
739*ef8d499eSDavid van Moolenbroek 		 * Otherwise, free the containing large buffer as well.  First,
740*ef8d499eSDavid van Moolenbroek 		 * remove all its small buffers from the free list.
741*ef8d499eSDavid van Moolenbroek 		 */
742*ef8d499eSDavid van Moolenbroek 		mss = (struct mempool_small_slab *)mh;
743*ef8d499eSDavid van Moolenbroek 		msb = mss->mss_buf;
744*ef8d499eSDavid van Moolenbroek 
745*ef8d499eSDavid van Moolenbroek 		for (count = 0; count < MEMPOOL_SMALL_COUNT; count++, msb++) {
746*ef8d499eSDavid van Moolenbroek 			assert(msb->msb_header == NULL);
747*ef8d499eSDavid van Moolenbroek 			assert(msb->msb_header2 == mh);
748*ef8d499eSDavid van Moolenbroek 
749*ef8d499eSDavid van Moolenbroek 			TAILQ_REMOVE(&mempool_small_dynamic_freelist, msb,
750*ef8d499eSDavid van Moolenbroek 			    msb_next);
751*ef8d499eSDavid van Moolenbroek 		}
752*ef8d499eSDavid van Moolenbroek 
753*ef8d499eSDavid van Moolenbroek 		/* Then, fall through to the large-buffer free code. */
754*ef8d499eSDavid van Moolenbroek 		ptr = (void *)((char *)mh - sizeof(mh));
755*ef8d499eSDavid van Moolenbroek 
756*ef8d499eSDavid van Moolenbroek 		memcpy(&mh, ptr, sizeof(mh));
757*ef8d499eSDavid van Moolenbroek 
758*ef8d499eSDavid van Moolenbroek 		assert(mh != NULL);
759*ef8d499eSDavid van Moolenbroek 		assert(!(mh->mh_flags & MHF_SMALL));
760*ef8d499eSDavid van Moolenbroek 	}
761*ef8d499eSDavid van Moolenbroek 
762*ef8d499eSDavid van Moolenbroek 	/*
763*ef8d499eSDavid van Moolenbroek 	 * Move the large buffer onto the free list of the large slab to which
764*ef8d499eSDavid van Moolenbroek 	 * it belongs.
765*ef8d499eSDavid van Moolenbroek 	 */
766*ef8d499eSDavid van Moolenbroek 	mls = (struct mempool_large_slab *)mh;
767*ef8d499eSDavid van Moolenbroek 	mlb = (struct mempool_large_buf *)ptr;
768*ef8d499eSDavid van Moolenbroek 
769*ef8d499eSDavid van Moolenbroek 	mlb->mlb_header2 = &mls->mls_header;
770*ef8d499eSDavid van Moolenbroek 	mlb->mlb_header = NULL;
771*ef8d499eSDavid van Moolenbroek 
772*ef8d499eSDavid van Moolenbroek 	LIST_INSERT_HEAD(&mls->mls_free, mlb, mlb_next);
773*ef8d499eSDavid van Moolenbroek 
774*ef8d499eSDavid van Moolenbroek 	/*
775*ef8d499eSDavid van Moolenbroek 	 * Adjust accounting for the large slab, which may involve moving it
776*ef8d499eSDavid van Moolenbroek 	 * to another list.
777*ef8d499eSDavid van Moolenbroek 	 */
778*ef8d499eSDavid van Moolenbroek 	assert(mls->mls_header.mh_inuse > 0);
779*ef8d499eSDavid van Moolenbroek 	mls->mls_header.mh_inuse--;
780*ef8d499eSDavid van Moolenbroek 
781*ef8d499eSDavid van Moolenbroek 	if (mls->mls_header.mh_inuse == 0) {
782*ef8d499eSDavid van Moolenbroek 		LIST_REMOVE(mls, mls_next);
783*ef8d499eSDavid van Moolenbroek 
784*ef8d499eSDavid van Moolenbroek 		LIST_INSERT_HEAD(&mempool_empty_slabs, mls, mls_next);
785*ef8d499eSDavid van Moolenbroek 
786*ef8d499eSDavid van Moolenbroek 		mls->mls_header.mh_flags &= ~MHF_MARKED;
787*ef8d499eSDavid van Moolenbroek 	} else if (mls->mls_header.mh_inuse == MEMPOOL_LARGE_COUNT - 1) {
788*ef8d499eSDavid van Moolenbroek 		LIST_REMOVE(mls, mls_next);
789*ef8d499eSDavid van Moolenbroek 
790*ef8d499eSDavid van Moolenbroek 		LIST_INSERT_HEAD(&mempool_partial_slabs, mls, mls_next);
791*ef8d499eSDavid van Moolenbroek 	}
792*ef8d499eSDavid van Moolenbroek 
793*ef8d499eSDavid van Moolenbroek 	assert(mempool_used_large > 0);
794*ef8d499eSDavid van Moolenbroek 	mempool_used_large--;
795*ef8d499eSDavid van Moolenbroek }
796*ef8d499eSDavid van Moolenbroek 
797*ef8d499eSDavid van Moolenbroek /*
798*ef8d499eSDavid van Moolenbroek  * Memory pool wrapper function for calloc() calls from lwIP.
799*ef8d499eSDavid van Moolenbroek  */
800*ef8d499eSDavid van Moolenbroek void *
mempool_calloc(size_t num,size_t size)801*ef8d499eSDavid van Moolenbroek mempool_calloc(size_t num, size_t size)
802*ef8d499eSDavid van Moolenbroek {
803*ef8d499eSDavid van Moolenbroek 	void *ptr;
804*ef8d499eSDavid van Moolenbroek 	size_t total;
805*ef8d499eSDavid van Moolenbroek 
806*ef8d499eSDavid van Moolenbroek 	/*
807*ef8d499eSDavid van Moolenbroek 	 * Standard overflow check.  This can be improved, but it doesn't have
808*ef8d499eSDavid van Moolenbroek 	 * to be, because in practice lwIP never calls calloc() anyway.
809*ef8d499eSDavid van Moolenbroek 	 */
810*ef8d499eSDavid van Moolenbroek 	if (num > 0 && size > 0 && (size_t)-1 / size < num)
811*ef8d499eSDavid van Moolenbroek 		return NULL;
812*ef8d499eSDavid van Moolenbroek 
813*ef8d499eSDavid van Moolenbroek 	total = num * size;
814*ef8d499eSDavid van Moolenbroek 
815*ef8d499eSDavid van Moolenbroek 	if ((ptr = mempool_malloc(total)) == NULL)
816*ef8d499eSDavid van Moolenbroek 		return NULL;
817*ef8d499eSDavid van Moolenbroek 
818*ef8d499eSDavid van Moolenbroek 	memset(ptr, 0, total);
819*ef8d499eSDavid van Moolenbroek 
820*ef8d499eSDavid van Moolenbroek 	return ptr;
821*ef8d499eSDavid van Moolenbroek }
822