1*4a5d661aSToomas Soome /*
2*4a5d661aSToomas Soome * This module derived from code donated to the FreeBSD Project by
3*4a5d661aSToomas Soome * Matthew Dillon <dillon@backplane.com>
4*4a5d661aSToomas Soome *
5*4a5d661aSToomas Soome * Copyright (c) 1998 The FreeBSD Project
6*4a5d661aSToomas Soome * All rights reserved.
7*4a5d661aSToomas Soome *
8*4a5d661aSToomas Soome * Redistribution and use in source and binary forms, with or without
9*4a5d661aSToomas Soome * modification, are permitted provided that the following conditions
10*4a5d661aSToomas Soome * are met:
11*4a5d661aSToomas Soome * 1. Redistributions of source code must retain the above copyright
12*4a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer.
13*4a5d661aSToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
14*4a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer in the
15*4a5d661aSToomas Soome * documentation and/or other materials provided with the distribution.
16*4a5d661aSToomas Soome *
17*4a5d661aSToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18*4a5d661aSToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*4a5d661aSToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*4a5d661aSToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21*4a5d661aSToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*4a5d661aSToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*4a5d661aSToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*4a5d661aSToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*4a5d661aSToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*4a5d661aSToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*4a5d661aSToomas Soome * SUCH DAMAGE.
28*4a5d661aSToomas Soome */
29*4a5d661aSToomas Soome
30*4a5d661aSToomas Soome #include <sys/cdefs.h>
31*4a5d661aSToomas Soome __FBSDID("$FreeBSD$");
32*4a5d661aSToomas Soome
33*4a5d661aSToomas Soome /*
34*4a5d661aSToomas Soome * LIB/MEMORY/ZALLOC.C - self contained low-overhead memory pool/allocation
35*4a5d661aSToomas Soome * subsystem
36*4a5d661aSToomas Soome *
37*4a5d661aSToomas Soome * This subsystem implements memory pools and memory allocation
38*4a5d661aSToomas Soome * routines.
39*4a5d661aSToomas Soome *
40*4a5d661aSToomas Soome * Pools are managed via a linked list of 'free' areas. Allocating
41*4a5d661aSToomas Soome * memory creates holes in the freelist, freeing memory fills them.
42*4a5d661aSToomas Soome * Since the freelist consists only of free memory areas, it is possible
43*4a5d661aSToomas Soome * to allocate the entire pool without incuring any structural overhead.
44*4a5d661aSToomas Soome *
45*4a5d661aSToomas Soome * The system works best when allocating similarly-sized chunks of
46*4a5d661aSToomas Soome * memory. Care must be taken to avoid fragmentation when
47*4a5d661aSToomas Soome * allocating/deallocating dissimilar chunks.
48*4a5d661aSToomas Soome *
49*4a5d661aSToomas Soome * When a memory pool is first allocated, the entire pool is marked as
50*4a5d661aSToomas Soome * allocated. This is done mainly because we do not want to modify any
51*4a5d661aSToomas Soome * portion of a pool's data area until we are given permission. The
52*4a5d661aSToomas Soome * caller must explicitly deallocate portions of the pool to make them
53*4a5d661aSToomas Soome * available.
54*4a5d661aSToomas Soome *
55*4a5d661aSToomas Soome * z[n]xalloc() works like z[n]alloc() but the allocation is made from
56*4a5d661aSToomas Soome * within the specified address range. If the segment could not be
57*4a5d661aSToomas Soome * allocated, NULL is returned. WARNING! The address range will be
58*4a5d661aSToomas Soome * aligned to an 8 or 16 byte boundry depending on the cpu so if you
59*4a5d661aSToomas Soome * give an unaligned address range, unexpected results may occur.
60*4a5d661aSToomas Soome *
61*4a5d661aSToomas Soome * If a standard allocation fails, the reclaim function will be called
62*4a5d661aSToomas Soome * to recover some space. This usually causes other portions of the
63*4a5d661aSToomas Soome * same pool to be released. Memory allocations at this low level
64*4a5d661aSToomas Soome * should not block but you can do that too in your reclaim function
65*4a5d661aSToomas Soome * if you want. Reclaim does not function when z[n]xalloc() is used,
66*4a5d661aSToomas Soome * only for z[n]alloc().
67*4a5d661aSToomas Soome *
68*4a5d661aSToomas Soome * Allocation and frees of 0 bytes are valid operations.
69*4a5d661aSToomas Soome */
70*4a5d661aSToomas Soome
71*4a5d661aSToomas Soome #include "zalloc_defs.h"
72*4a5d661aSToomas Soome
73*4a5d661aSToomas Soome /*
74*4a5d661aSToomas Soome * Objects in the pool must be aligned to at least the size of struct MemNode.
75*4a5d661aSToomas Soome * They must also be aligned to MALLOCALIGN, which should normally be larger
76*4a5d661aSToomas Soome * than the struct, so assert that to be so at compile time.
77*4a5d661aSToomas Soome */
78*4a5d661aSToomas Soome typedef char assert_align[(sizeof(struct MemNode) <= MALLOCALIGN) ? 1 : -1];
79*4a5d661aSToomas Soome
80*4a5d661aSToomas Soome #define MEMNODE_SIZE_MASK MALLOCALIGN_MASK
81*4a5d661aSToomas Soome
82*4a5d661aSToomas Soome /*
83*4a5d661aSToomas Soome * znalloc() - allocate memory (without zeroing) from pool. Call reclaim
84*4a5d661aSToomas Soome * and retry if appropriate, return NULL if unable to allocate
85*4a5d661aSToomas Soome * memory.
86*4a5d661aSToomas Soome */
87*4a5d661aSToomas Soome
88*4a5d661aSToomas Soome void *
znalloc(MemPool * mp,uintptr_t bytes)89*4a5d661aSToomas Soome znalloc(MemPool *mp, uintptr_t bytes)
90*4a5d661aSToomas Soome {
91*4a5d661aSToomas Soome /*
92*4a5d661aSToomas Soome * align according to pool object size (can be 0). This is
93*4a5d661aSToomas Soome * inclusive of the MEMNODE_SIZE_MASK minimum alignment.
94*4a5d661aSToomas Soome *
95*4a5d661aSToomas Soome */
96*4a5d661aSToomas Soome bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
97*4a5d661aSToomas Soome
98*4a5d661aSToomas Soome if (bytes == 0)
99*4a5d661aSToomas Soome return((void *)-1);
100*4a5d661aSToomas Soome
101*4a5d661aSToomas Soome /*
102*4a5d661aSToomas Soome * locate freelist entry big enough to hold the object. If all objects
103*4a5d661aSToomas Soome * are the same size, this is a constant-time function.
104*4a5d661aSToomas Soome */
105*4a5d661aSToomas Soome
106*4a5d661aSToomas Soome if (bytes <= mp->mp_Size - mp->mp_Used) {
107*4a5d661aSToomas Soome MemNode **pmn;
108*4a5d661aSToomas Soome MemNode *mn;
109*4a5d661aSToomas Soome
110*4a5d661aSToomas Soome for (pmn = &mp->mp_First; (mn=*pmn) != NULL; pmn = &mn->mr_Next) {
111*4a5d661aSToomas Soome if (bytes > mn->mr_Bytes)
112*4a5d661aSToomas Soome continue;
113*4a5d661aSToomas Soome
114*4a5d661aSToomas Soome /*
115*4a5d661aSToomas Soome * Cut a chunk of memory out of the beginning of this
116*4a5d661aSToomas Soome * block and fixup the link appropriately.
117*4a5d661aSToomas Soome */
118*4a5d661aSToomas Soome
119*4a5d661aSToomas Soome {
120*4a5d661aSToomas Soome char *ptr = (char *)mn;
121*4a5d661aSToomas Soome
122*4a5d661aSToomas Soome if (mn->mr_Bytes == bytes) {
123*4a5d661aSToomas Soome *pmn = mn->mr_Next;
124*4a5d661aSToomas Soome } else {
125*4a5d661aSToomas Soome mn = (MemNode *)((char *)mn + bytes);
126*4a5d661aSToomas Soome mn->mr_Next = ((MemNode *)ptr)->mr_Next;
127*4a5d661aSToomas Soome mn->mr_Bytes = ((MemNode *)ptr)->mr_Bytes - bytes;
128*4a5d661aSToomas Soome *pmn = mn;
129*4a5d661aSToomas Soome }
130*4a5d661aSToomas Soome mp->mp_Used += bytes;
131*4a5d661aSToomas Soome return(ptr);
132*4a5d661aSToomas Soome }
133*4a5d661aSToomas Soome }
134*4a5d661aSToomas Soome }
135*4a5d661aSToomas Soome
136*4a5d661aSToomas Soome /*
137*4a5d661aSToomas Soome * Memory pool is full, return NULL.
138*4a5d661aSToomas Soome */
139*4a5d661aSToomas Soome
140*4a5d661aSToomas Soome return(NULL);
141*4a5d661aSToomas Soome }
142*4a5d661aSToomas Soome
143*4a5d661aSToomas Soome /*
144*4a5d661aSToomas Soome * zfree() - free previously allocated memory
145*4a5d661aSToomas Soome */
146*4a5d661aSToomas Soome
147*4a5d661aSToomas Soome void
zfree(MemPool * mp,void * ptr,uintptr_t bytes)148*4a5d661aSToomas Soome zfree(MemPool *mp, void *ptr, uintptr_t bytes)
149*4a5d661aSToomas Soome {
150*4a5d661aSToomas Soome /*
151*4a5d661aSToomas Soome * align according to pool object size (can be 0). This is
152*4a5d661aSToomas Soome * inclusive of the MEMNODE_SIZE_MASK minimum alignment.
153*4a5d661aSToomas Soome */
154*4a5d661aSToomas Soome bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK;
155*4a5d661aSToomas Soome
156*4a5d661aSToomas Soome if (bytes == 0)
157*4a5d661aSToomas Soome return;
158*4a5d661aSToomas Soome
159*4a5d661aSToomas Soome /*
160*4a5d661aSToomas Soome * panic if illegal pointer
161*4a5d661aSToomas Soome */
162*4a5d661aSToomas Soome
163*4a5d661aSToomas Soome if ((char *)ptr < (char *)mp->mp_Base ||
164*4a5d661aSToomas Soome (char *)ptr + bytes > (char *)mp->mp_End ||
165*4a5d661aSToomas Soome ((uintptr_t)ptr & MEMNODE_SIZE_MASK) != 0)
166*4a5d661aSToomas Soome panic("zfree(%p,%ju): wild pointer", ptr, (uintmax_t)bytes);
167*4a5d661aSToomas Soome
168*4a5d661aSToomas Soome /*
169*4a5d661aSToomas Soome * free the segment
170*4a5d661aSToomas Soome */
171*4a5d661aSToomas Soome
172*4a5d661aSToomas Soome {
173*4a5d661aSToomas Soome MemNode **pmn;
174*4a5d661aSToomas Soome MemNode *mn;
175*4a5d661aSToomas Soome
176*4a5d661aSToomas Soome mp->mp_Used -= bytes;
177*4a5d661aSToomas Soome
178*4a5d661aSToomas Soome for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) {
179*4a5d661aSToomas Soome /*
180*4a5d661aSToomas Soome * If area between last node and current node
181*4a5d661aSToomas Soome * - check range
182*4a5d661aSToomas Soome * - check merge with next area
183*4a5d661aSToomas Soome * - check merge with previous area
184*4a5d661aSToomas Soome */
185*4a5d661aSToomas Soome if ((char *)ptr <= (char *)mn) {
186*4a5d661aSToomas Soome /*
187*4a5d661aSToomas Soome * range check
188*4a5d661aSToomas Soome */
189*4a5d661aSToomas Soome if ((char *)ptr + bytes > (char *)mn) {
190*4a5d661aSToomas Soome panic("zfree(%p,%ju): corrupt memlist1", ptr,
191*4a5d661aSToomas Soome (uintmax_t)bytes);
192*4a5d661aSToomas Soome }
193*4a5d661aSToomas Soome
194*4a5d661aSToomas Soome /*
195*4a5d661aSToomas Soome * merge against next area or create independant area
196*4a5d661aSToomas Soome */
197*4a5d661aSToomas Soome
198*4a5d661aSToomas Soome if ((char *)ptr + bytes == (char *)mn) {
199*4a5d661aSToomas Soome ((MemNode *)ptr)->mr_Next = mn->mr_Next;
200*4a5d661aSToomas Soome ((MemNode *)ptr)->mr_Bytes= bytes + mn->mr_Bytes;
201*4a5d661aSToomas Soome } else {
202*4a5d661aSToomas Soome ((MemNode *)ptr)->mr_Next = mn;
203*4a5d661aSToomas Soome ((MemNode *)ptr)->mr_Bytes= bytes;
204*4a5d661aSToomas Soome }
205*4a5d661aSToomas Soome *pmn = mn = (MemNode *)ptr;
206*4a5d661aSToomas Soome
207*4a5d661aSToomas Soome /*
208*4a5d661aSToomas Soome * merge against previous area (if there is a previous
209*4a5d661aSToomas Soome * area).
210*4a5d661aSToomas Soome */
211*4a5d661aSToomas Soome
212*4a5d661aSToomas Soome if (pmn != &mp->mp_First) {
213*4a5d661aSToomas Soome if ((char*)pmn + ((MemNode*)pmn)->mr_Bytes == (char*)ptr) {
214*4a5d661aSToomas Soome ((MemNode *)pmn)->mr_Next = mn->mr_Next;
215*4a5d661aSToomas Soome ((MemNode *)pmn)->mr_Bytes += mn->mr_Bytes;
216*4a5d661aSToomas Soome mn = (MemNode *)pmn;
217*4a5d661aSToomas Soome }
218*4a5d661aSToomas Soome }
219*4a5d661aSToomas Soome return;
220*4a5d661aSToomas Soome /* NOT REACHED */
221*4a5d661aSToomas Soome }
222*4a5d661aSToomas Soome if ((char *)ptr < (char *)mn + mn->mr_Bytes) {
223*4a5d661aSToomas Soome panic("zfree(%p,%ju): corrupt memlist2", ptr,
224*4a5d661aSToomas Soome (uintmax_t)bytes);
225*4a5d661aSToomas Soome }
226*4a5d661aSToomas Soome }
227*4a5d661aSToomas Soome /*
228*4a5d661aSToomas Soome * We are beyond the last MemNode, append new MemNode. Merge against
229*4a5d661aSToomas Soome * previous area if possible.
230*4a5d661aSToomas Soome */
231*4a5d661aSToomas Soome if (pmn == &mp->mp_First ||
232*4a5d661aSToomas Soome (char *)pmn + ((MemNode *)pmn)->mr_Bytes != (char *)ptr
233*4a5d661aSToomas Soome ) {
234*4a5d661aSToomas Soome ((MemNode *)ptr)->mr_Next = NULL;
235*4a5d661aSToomas Soome ((MemNode *)ptr)->mr_Bytes = bytes;
236*4a5d661aSToomas Soome *pmn = (MemNode *)ptr;
237*4a5d661aSToomas Soome mn = (MemNode *)ptr;
238*4a5d661aSToomas Soome } else {
239*4a5d661aSToomas Soome ((MemNode *)pmn)->mr_Bytes += bytes;
240*4a5d661aSToomas Soome mn = (MemNode *)pmn;
241*4a5d661aSToomas Soome }
242*4a5d661aSToomas Soome }
243*4a5d661aSToomas Soome }
244*4a5d661aSToomas Soome
245*4a5d661aSToomas Soome /*
246*4a5d661aSToomas Soome * zextendPool() - extend memory pool to cover additional space.
247*4a5d661aSToomas Soome *
248*4a5d661aSToomas Soome * Note: the added memory starts out as allocated, you
249*4a5d661aSToomas Soome * must free it to make it available to the memory subsystem.
250*4a5d661aSToomas Soome *
251*4a5d661aSToomas Soome * Note: mp_Size may not reflect (mp_End - mp_Base) range
252*4a5d661aSToomas Soome * due to other parts of the system doing their own sbrk()
253*4a5d661aSToomas Soome * calls.
254*4a5d661aSToomas Soome */
255*4a5d661aSToomas Soome
256*4a5d661aSToomas Soome void
zextendPool(MemPool * mp,void * base,uintptr_t bytes)257*4a5d661aSToomas Soome zextendPool(MemPool *mp, void *base, uintptr_t bytes)
258*4a5d661aSToomas Soome {
259*4a5d661aSToomas Soome if (mp->mp_Size == 0) {
260*4a5d661aSToomas Soome mp->mp_Base = base;
261*4a5d661aSToomas Soome mp->mp_Used = bytes;
262*4a5d661aSToomas Soome mp->mp_End = (char *)base + bytes;
263*4a5d661aSToomas Soome mp->mp_Size = bytes;
264*4a5d661aSToomas Soome } else {
265*4a5d661aSToomas Soome void *pend = (char *)mp->mp_Base + mp->mp_Size;
266*4a5d661aSToomas Soome
267*4a5d661aSToomas Soome if (base < mp->mp_Base) {
268*4a5d661aSToomas Soome mp->mp_Size += (char *)mp->mp_Base - (char *)base;
269*4a5d661aSToomas Soome mp->mp_Used += (char *)mp->mp_Base - (char *)base;
270*4a5d661aSToomas Soome mp->mp_Base = base;
271*4a5d661aSToomas Soome }
272*4a5d661aSToomas Soome base = (char *)base + bytes;
273*4a5d661aSToomas Soome if (base > pend) {
274*4a5d661aSToomas Soome mp->mp_Size += (char *)base - (char *)pend;
275*4a5d661aSToomas Soome mp->mp_Used += (char *)base - (char *)pend;
276*4a5d661aSToomas Soome mp->mp_End = (char *)base;
277*4a5d661aSToomas Soome }
278*4a5d661aSToomas Soome }
279*4a5d661aSToomas Soome }
280*4a5d661aSToomas Soome
281*4a5d661aSToomas Soome #ifdef ZALLOCDEBUG
282*4a5d661aSToomas Soome
283*4a5d661aSToomas Soome void
zallocstats(MemPool * mp)284*4a5d661aSToomas Soome zallocstats(MemPool *mp)
285*4a5d661aSToomas Soome {
286*4a5d661aSToomas Soome int abytes = 0;
287*4a5d661aSToomas Soome int hbytes = 0;
288*4a5d661aSToomas Soome int fcount = 0;
289*4a5d661aSToomas Soome MemNode *mn;
290*4a5d661aSToomas Soome
291*4a5d661aSToomas Soome printf("%d bytes reserved", (int) mp->mp_Size);
292*4a5d661aSToomas Soome
293*4a5d661aSToomas Soome mn = mp->mp_First;
294*4a5d661aSToomas Soome
295*4a5d661aSToomas Soome if ((void *)mn != (void *)mp->mp_Base) {
296*4a5d661aSToomas Soome abytes += (char *)mn - (char *)mp->mp_Base;
297*4a5d661aSToomas Soome }
298*4a5d661aSToomas Soome
299*4a5d661aSToomas Soome while (mn) {
300*4a5d661aSToomas Soome if ((char *)mn + mn->mr_Bytes != mp->mp_End) {
301*4a5d661aSToomas Soome hbytes += mn->mr_Bytes;
302*4a5d661aSToomas Soome ++fcount;
303*4a5d661aSToomas Soome }
304*4a5d661aSToomas Soome if (mn->mr_Next)
305*4a5d661aSToomas Soome abytes += (char *)mn->mr_Next - ((char *)mn + mn->mr_Bytes);
306*4a5d661aSToomas Soome mn = mn->mr_Next;
307*4a5d661aSToomas Soome }
308*4a5d661aSToomas Soome printf(" %d bytes allocated\n%d fragments (%d bytes fragmented)\n",
309*4a5d661aSToomas Soome abytes,
310*4a5d661aSToomas Soome fcount,
311*4a5d661aSToomas Soome hbytes
312*4a5d661aSToomas Soome );
313*4a5d661aSToomas Soome }
314*4a5d661aSToomas Soome
315*4a5d661aSToomas Soome #endif
316*4a5d661aSToomas Soome
317