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