xref: /dflybsd-src/lib/libc/stdlib/nmalloc.c (revision 8b07b5e82a38bb7f04797b88aca14d3ccb08532c)
182949828SMatthew Dillon /*
282949828SMatthew Dillon  * NMALLOC.C	- New Malloc (ported from kernel slab allocator)
382949828SMatthew Dillon  *
40bb7d8c8SVenkatesh Srinivas  * Copyright (c) 2003,2004,2009,2010 The DragonFly Project. All rights reserved.
582949828SMatthew Dillon  *
682949828SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
70bb7d8c8SVenkatesh Srinivas  * by Matthew Dillon <dillon@backplane.com> and by
80bb7d8c8SVenkatesh Srinivas  * Venkatesh Srinivas <me@endeavour.zapto.org>.
982949828SMatthew Dillon  *
1082949828SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
1182949828SMatthew Dillon  * modification, are permitted provided that the following conditions
1282949828SMatthew Dillon  * are met:
1382949828SMatthew Dillon  *
1482949828SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1582949828SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1682949828SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1782949828SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
1882949828SMatthew Dillon  *    the documentation and/or other materials provided with the
1982949828SMatthew Dillon  *    distribution.
2082949828SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
2182949828SMatthew Dillon  *    contributors may be used to endorse or promote products derived
2282949828SMatthew Dillon  *    from this software without specific, prior written permission.
2382949828SMatthew Dillon  *
2482949828SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2582949828SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2682949828SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2782949828SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2882949828SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2982949828SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
3082949828SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3182949828SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3282949828SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3382949828SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3482949828SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3582949828SMatthew Dillon  * SUCH DAMAGE.
360bb7d8c8SVenkatesh Srinivas  *
370bb7d8c8SVenkatesh Srinivas  * $Id: nmalloc.c,v 1.37 2010/07/23 08:20:35 vsrinivas Exp $
3882949828SMatthew Dillon  */
3982949828SMatthew Dillon /*
4082949828SMatthew Dillon  * This module implements a slab allocator drop-in replacement for the
4182949828SMatthew Dillon  * libc malloc().
4282949828SMatthew Dillon  *
4382949828SMatthew Dillon  * A slab allocator reserves a ZONE for each chunk size, then lays the
4482949828SMatthew Dillon  * chunks out in an array within the zone.  Allocation and deallocation
450bb7d8c8SVenkatesh Srinivas  * is nearly instantaneous, and overhead losses are limited to a fixed
4682949828SMatthew Dillon  * worst-case amount.
4782949828SMatthew Dillon  *
4882949828SMatthew Dillon  * The slab allocator does not have to pre-initialize the list of
4982949828SMatthew Dillon  * free chunks for each zone, and the underlying VM will not be
5082949828SMatthew Dillon  * touched at all beyond the zone header until an actual allocation
5182949828SMatthew Dillon  * needs it.
5282949828SMatthew Dillon  *
5382949828SMatthew Dillon  * Slab management and locking is done on a per-zone basis.
5482949828SMatthew Dillon  *
5582949828SMatthew Dillon  *	Alloc Size	Chunking        Number of zones
5682949828SMatthew Dillon  *	0-127		8		16
5782949828SMatthew Dillon  *	128-255		16		8
5882949828SMatthew Dillon  *	256-511		32		8
5982949828SMatthew Dillon  *	512-1023	64		8
6082949828SMatthew Dillon  *	1024-2047	128		8
6182949828SMatthew Dillon  *	2048-4095	256		8
6282949828SMatthew Dillon  *	4096-8191	512		8
6382949828SMatthew Dillon  *	8192-16383	1024		8
6482949828SMatthew Dillon  *	16384-32767	2048		8
6582949828SMatthew Dillon  *
6682949828SMatthew Dillon  *	Allocations >= ZoneLimit (16K) go directly to mmap and a hash table
6782949828SMatthew Dillon  *	is used to locate for free.  One and Two-page allocations use the
6882949828SMatthew Dillon  *	zone mechanic to avoid excessive mmap()/munmap() calls.
6982949828SMatthew Dillon  *
7082949828SMatthew Dillon  *			   API FEATURES AND SIDE EFFECTS
7182949828SMatthew Dillon  *
7282949828SMatthew Dillon  *    + power-of-2 sized allocations up to a page will be power-of-2 aligned.
7382949828SMatthew Dillon  *	Above that power-of-2 sized allocations are page-aligned.  Non
7482949828SMatthew Dillon  *	power-of-2 sized allocations are aligned the same as the chunk
7582949828SMatthew Dillon  *	size for their zone.
7682949828SMatthew Dillon  *    + malloc(0) returns a special non-NULL value
7782949828SMatthew Dillon  *    + ability to allocate arbitrarily large chunks of memory
7882949828SMatthew Dillon  *    + realloc will reuse the passed pointer if possible, within the
7982949828SMatthew Dillon  *	limitations of the zone chunking.
800bb7d8c8SVenkatesh Srinivas  *
810bb7d8c8SVenkatesh Srinivas  * Multithreaded enhancements for small allocations introduced August 2010.
820bb7d8c8SVenkatesh Srinivas  * These are in the spirit of 'libumem'. See:
830bb7d8c8SVenkatesh Srinivas  *	Bonwick, J.; Adams, J. (2001). "Magazines and Vmem: Extending the
840bb7d8c8SVenkatesh Srinivas  *	slab allocator to many CPUs and arbitrary resources". In Proc. 2001
850bb7d8c8SVenkatesh Srinivas  *	USENIX Technical Conference. USENIX Association.
860bb7d8c8SVenkatesh Srinivas  *
8707a8ffeaSMatthew Dillon  * Oversized allocations employ the BIGCACHE mechanic whereby large
8807a8ffeaSMatthew Dillon  * allocations may be handed significantly larger buffers, allowing them
8907a8ffeaSMatthew Dillon  * to avoid mmap/munmap operations even through significant realloc()s.
9007a8ffeaSMatthew Dillon  * The excess space is only trimmed if too many large allocations have been
9107a8ffeaSMatthew Dillon  * given this treatment.
9207a8ffeaSMatthew Dillon  *
930bb7d8c8SVenkatesh Srinivas  * TUNING
940bb7d8c8SVenkatesh Srinivas  *
950bb7d8c8SVenkatesh Srinivas  * The value of the environment variable MALLOC_OPTIONS is a character string
960bb7d8c8SVenkatesh Srinivas  * containing various flags to tune nmalloc.
970bb7d8c8SVenkatesh Srinivas  *
980bb7d8c8SVenkatesh Srinivas  * 'U'   / ['u']	Generate / do not generate utrace entries for ktrace(1)
990bb7d8c8SVenkatesh Srinivas  *			This will generate utrace events for all malloc,
1000bb7d8c8SVenkatesh Srinivas  *			realloc, and free calls. There are tools (mtrplay) to
1010bb7d8c8SVenkatesh Srinivas  *			replay and allocation pattern or to graph heap structure
1020bb7d8c8SVenkatesh Srinivas  *			(mtrgraph) which can interpret these logs.
1030bb7d8c8SVenkatesh Srinivas  * 'Z'   / ['z']	Zero out / do not zero all allocations.
1040bb7d8c8SVenkatesh Srinivas  *			Each new byte of memory allocated by malloc, realloc, or
1050bb7d8c8SVenkatesh Srinivas  *			reallocf will be initialized to 0. This is intended for
1060bb7d8c8SVenkatesh Srinivas  *			debugging and will affect performance negatively.
1070bb7d8c8SVenkatesh Srinivas  * 'H'	/  ['h']	Pass a hint to the kernel about pages unused by the
1080bb7d8c8SVenkatesh Srinivas  *			allocation functions.
10982949828SMatthew Dillon  */
11082949828SMatthew Dillon 
1110bb7d8c8SVenkatesh Srinivas /* cc -shared -fPIC -g -O -I/usr/src/lib/libc/include -o nmalloc.so nmalloc.c */
1120bb7d8c8SVenkatesh Srinivas 
113d19ab22dSSascha Wildner #include "namespace.h"
11482949828SMatthew Dillon #include <sys/param.h>
11582949828SMatthew Dillon #include <sys/types.h>
11682949828SMatthew Dillon #include <sys/mman.h>
1170bb7d8c8SVenkatesh Srinivas #include <sys/queue.h>
1180bb7d8c8SVenkatesh Srinivas #include <sys/ktrace.h>
11982949828SMatthew Dillon #include <stdio.h>
1200bb7d8c8SVenkatesh Srinivas #include <stdint.h>
12182949828SMatthew Dillon #include <stdlib.h>
12282949828SMatthew Dillon #include <stdarg.h>
12382949828SMatthew Dillon #include <stddef.h>
12482949828SMatthew Dillon #include <unistd.h>
12582949828SMatthew Dillon #include <string.h>
12682949828SMatthew Dillon #include <fcntl.h>
12782949828SMatthew Dillon #include <errno.h>
1280bb7d8c8SVenkatesh Srinivas #include <pthread.h>
12907a8ffeaSMatthew Dillon #include <machine/atomic.h>
13082949828SMatthew Dillon #include "un-namespace.h"
13182949828SMatthew Dillon 
132d19ab22dSSascha Wildner #include "libc_private.h"
133d19ab22dSSascha Wildner #include "spinlock.h"
13407a8ffeaSMatthew Dillon 
135a32e3ba6SSascha Wildner void __free(void *);
136a32e3ba6SSascha Wildner void *__malloc(size_t);
137a32e3ba6SSascha Wildner void *__calloc(size_t, size_t);
138a32e3ba6SSascha Wildner void *__realloc(void *, size_t);
139a32e3ba6SSascha Wildner void *__aligned_alloc(size_t, size_t);
140a32e3ba6SSascha Wildner int __posix_memalign(void **, size_t, size_t);
141a32e3ba6SSascha Wildner 
14282949828SMatthew Dillon /*
14382949828SMatthew Dillon  * Linked list of large allocations
14482949828SMatthew Dillon  */
14582949828SMatthew Dillon typedef struct bigalloc {
14682949828SMatthew Dillon 	struct bigalloc *next;	/* hash link */
14782949828SMatthew Dillon 	void	*base;		/* base pointer */
14807a8ffeaSMatthew Dillon 	u_long	active;		/* bytes active */
14982949828SMatthew Dillon 	u_long	bytes;		/* bytes allocated */
15082949828SMatthew Dillon } *bigalloc_t;
15182949828SMatthew Dillon 
15282949828SMatthew Dillon /*
15382949828SMatthew Dillon  * Note that any allocations which are exact multiples of PAGE_SIZE, or
15482949828SMatthew Dillon  * which are >= ZALLOC_ZONE_LIMIT, will fall through to the kmem subsystem.
15582949828SMatthew Dillon  */
15682949828SMatthew Dillon #define ZALLOC_ZONE_LIMIT	(16 * 1024)	/* max slab-managed alloc */
15782949828SMatthew Dillon #define ZALLOC_MIN_ZONE_SIZE	(32 * 1024)	/* minimum zone size */
15882949828SMatthew Dillon #define ZALLOC_MAX_ZONE_SIZE	(128 * 1024)	/* maximum zone size */
15982949828SMatthew Dillon #define ZALLOC_ZONE_SIZE	(64 * 1024)
16082949828SMatthew Dillon #define ZALLOC_SLAB_MAGIC	0x736c6162	/* magic sanity */
16182949828SMatthew Dillon #define ZALLOC_SLAB_SLIDE	20		/* L1-cache skip */
16282949828SMatthew Dillon 
16382949828SMatthew Dillon #if ZALLOC_ZONE_LIMIT == 16384
16482949828SMatthew Dillon #define NZONES			72
16582949828SMatthew Dillon #elif ZALLOC_ZONE_LIMIT == 32768
16682949828SMatthew Dillon #define NZONES			80
16782949828SMatthew Dillon #else
16882949828SMatthew Dillon #error "I couldn't figure out NZONES"
16982949828SMatthew Dillon #endif
17082949828SMatthew Dillon 
17182949828SMatthew Dillon /*
17282949828SMatthew Dillon  * Chunk structure for free elements
17382949828SMatthew Dillon  */
17482949828SMatthew Dillon typedef struct slchunk {
17582949828SMatthew Dillon 	struct slchunk *c_Next;
17682949828SMatthew Dillon } *slchunk_t;
17782949828SMatthew Dillon 
17882949828SMatthew Dillon /*
17982949828SMatthew Dillon  * The IN-BAND zone header is placed at the beginning of each zone.
18082949828SMatthew Dillon  */
18182949828SMatthew Dillon struct slglobaldata;
18282949828SMatthew Dillon 
18382949828SMatthew Dillon typedef struct slzone {
1840bb7d8c8SVenkatesh Srinivas 	int32_t		z_Magic;	/* magic number for sanity check */
18582949828SMatthew Dillon 	int		z_NFree;	/* total free chunks / ualloc space */
18682949828SMatthew Dillon 	struct slzone *z_Next;		/* ZoneAry[] link if z_NFree non-zero */
18782949828SMatthew Dillon 	int		z_NMax;		/* maximum free chunks */
18882949828SMatthew Dillon 	char		*z_BasePtr;	/* pointer to start of chunk array */
18982949828SMatthew Dillon 	int		z_UIndex;	/* current initial allocation index */
19082949828SMatthew Dillon 	int		z_UEndIndex;	/* last (first) allocation index */
19182949828SMatthew Dillon 	int		z_ChunkSize;	/* chunk size for validation */
19282949828SMatthew Dillon 	int		z_FirstFreePg;	/* chunk list on a page-by-page basis */
19382949828SMatthew Dillon 	int		z_ZoneIndex;
19482949828SMatthew Dillon 	int		z_Flags;
19582949828SMatthew Dillon 	struct slchunk *z_PageAry[ZALLOC_ZONE_SIZE / PAGE_SIZE];
19682949828SMatthew Dillon } *slzone_t;
19782949828SMatthew Dillon 
19882949828SMatthew Dillon typedef struct slglobaldata {
19982949828SMatthew Dillon 	spinlock_t	Spinlock;
20082949828SMatthew Dillon 	slzone_t	ZoneAry[NZONES];/* linked list of zones NFree > 0 */
20182949828SMatthew Dillon 	int		JunkIndex;
20282949828SMatthew Dillon } *slglobaldata_t;
20382949828SMatthew Dillon 
20482949828SMatthew Dillon #define SLZF_UNOTZEROD		0x0001
20582949828SMatthew Dillon 
2060bb7d8c8SVenkatesh Srinivas #define FASTSLABREALLOC		0x02
2070bb7d8c8SVenkatesh Srinivas 
20882949828SMatthew Dillon /*
20982949828SMatthew Dillon  * Misc constants.  Note that allocations that are exact multiples of
21082949828SMatthew Dillon  * PAGE_SIZE, or exceed the zone limit, fall through to the kmem module.
21182949828SMatthew Dillon  * IN_SAME_PAGE_MASK is used to sanity-check the per-page free lists.
21282949828SMatthew Dillon  */
21382949828SMatthew Dillon #define MIN_CHUNK_SIZE		8		/* in bytes */
21482949828SMatthew Dillon #define MIN_CHUNK_MASK		(MIN_CHUNK_SIZE - 1)
21582949828SMatthew Dillon #define IN_SAME_PAGE_MASK	(~(intptr_t)PAGE_MASK | MIN_CHUNK_MASK)
21682949828SMatthew Dillon 
21782949828SMatthew Dillon /*
2188ff099aeSMatthew Dillon  * WARNING: A limited number of spinlocks are available, BIGXSIZE should
2198ff099aeSMatthew Dillon  *	    not be larger then 64.
22082949828SMatthew Dillon  */
22182949828SMatthew Dillon #define BIGHSHIFT	10			/* bigalloc hash table */
22282949828SMatthew Dillon #define BIGHSIZE	(1 << BIGHSHIFT)
22382949828SMatthew Dillon #define BIGHMASK	(BIGHSIZE - 1)
22482949828SMatthew Dillon #define BIGXSIZE	(BIGHSIZE / 16)		/* bigalloc lock table */
22582949828SMatthew Dillon #define BIGXMASK	(BIGXSIZE - 1)
22682949828SMatthew Dillon 
22707a8ffeaSMatthew Dillon /*
22807a8ffeaSMatthew Dillon  * BIGCACHE caches oversized allocations.  Note that a linear search is
22907a8ffeaSMatthew Dillon  * performed, so do not make the cache too large.
23007a8ffeaSMatthew Dillon  *
23107a8ffeaSMatthew Dillon  * BIGCACHE will garbage-collect excess space when the excess exceeds the
23207a8ffeaSMatthew Dillon  * specified value.  A relatively large number should be used here because
23307a8ffeaSMatthew Dillon  * garbage collection is expensive.
23407a8ffeaSMatthew Dillon  */
23507a8ffeaSMatthew Dillon #define BIGCACHE	16
23607a8ffeaSMatthew Dillon #define BIGCACHE_MASK	(BIGCACHE - 1)
23707a8ffeaSMatthew Dillon #define BIGCACHE_LIMIT	(1024 * 1024)		/* size limit */
23807a8ffeaSMatthew Dillon #define BIGCACHE_EXCESS	(16 * 1024 * 1024)	/* garbage collect */
23907a8ffeaSMatthew Dillon 
240*8b07b5e8SMatthew Dillon #define CACHE_CHUNKS	32
241*8b07b5e8SMatthew Dillon 
24282949828SMatthew Dillon #define SAFLAG_ZERO	0x0001
24382949828SMatthew Dillon #define SAFLAG_PASSIVE	0x0002
244*8b07b5e8SMatthew Dillon #define SAFLAG_MAGS	0x0004
24582949828SMatthew Dillon 
24682949828SMatthew Dillon /*
24782949828SMatthew Dillon  * Thread control
24882949828SMatthew Dillon  */
24982949828SMatthew Dillon 
25082949828SMatthew Dillon #define arysize(ary)	(sizeof(ary)/sizeof((ary)[0]))
25182949828SMatthew Dillon 
252721505deSMatthew Dillon /*
253721505deSMatthew Dillon  * The assertion macros try to pretty-print assertion failures
254721505deSMatthew Dillon  * which can be caused by corruption.  If a lock is held, we
255721505deSMatthew Dillon  * provide a macro that attempts to release it before asserting
256721505deSMatthew Dillon  * in order to prevent (e.g.) a reentrant SIGABRT calling malloc
257721505deSMatthew Dillon  * and deadlocking, resulting in the program freezing up.
258721505deSMatthew Dillon  */
259721505deSMatthew Dillon #define MASSERT(exp)				\
260721505deSMatthew Dillon 	do { if (__predict_false(!(exp)))	\
26182949828SMatthew Dillon 	    _mpanic("assertion: %s in %s",	\
26282949828SMatthew Dillon 		    #exp, __func__);		\
26382949828SMatthew Dillon 	} while (0)
26482949828SMatthew Dillon 
265721505deSMatthew Dillon #define MASSERT_WTHUNLK(exp, unlk)		\
266721505deSMatthew Dillon 	do { if (__predict_false(!(exp))) {	\
267721505deSMatthew Dillon 	    unlk;				\
268721505deSMatthew Dillon 	    _mpanic("assertion: %s in %s",	\
269721505deSMatthew Dillon 		    #exp, __func__);		\
270721505deSMatthew Dillon 	  }					\
271721505deSMatthew Dillon 	} while (0)
272721505deSMatthew Dillon 
27382949828SMatthew Dillon /*
2740bb7d8c8SVenkatesh Srinivas  * Magazines
2750bb7d8c8SVenkatesh Srinivas  */
2760bb7d8c8SVenkatesh Srinivas 
2770bb7d8c8SVenkatesh Srinivas #define M_MAX_ROUNDS	64
2780bb7d8c8SVenkatesh Srinivas #define M_ZONE_ROUNDS	64
2790bb7d8c8SVenkatesh Srinivas #define M_LOW_ROUNDS	32
2800bb7d8c8SVenkatesh Srinivas #define M_INIT_ROUNDS	8
2810bb7d8c8SVenkatesh Srinivas #define M_BURST_FACTOR  8
2820bb7d8c8SVenkatesh Srinivas #define M_BURST_NSCALE	2
2830bb7d8c8SVenkatesh Srinivas 
2840bb7d8c8SVenkatesh Srinivas #define M_BURST		0x0001
2850bb7d8c8SVenkatesh Srinivas #define M_BURST_EARLY	0x0002
2860bb7d8c8SVenkatesh Srinivas 
2870bb7d8c8SVenkatesh Srinivas struct magazine {
2880bb7d8c8SVenkatesh Srinivas 	SLIST_ENTRY(magazine) nextmagazine;
2890bb7d8c8SVenkatesh Srinivas 
2900bb7d8c8SVenkatesh Srinivas 	int		flags;
2910bb7d8c8SVenkatesh Srinivas 	int		capacity;	/* Max rounds in this magazine */
2920bb7d8c8SVenkatesh Srinivas 	int		rounds;		/* Current number of free rounds */
2930bb7d8c8SVenkatesh Srinivas 	int		burst_factor;	/* Number of blocks to prefill with */
2940bb7d8c8SVenkatesh Srinivas 	int		low_factor;	/* Free till low_factor from full mag */
2950bb7d8c8SVenkatesh Srinivas 	void		*objects[M_MAX_ROUNDS];
2960bb7d8c8SVenkatesh Srinivas };
2970bb7d8c8SVenkatesh Srinivas 
2980bb7d8c8SVenkatesh Srinivas SLIST_HEAD(magazinelist, magazine);
2990bb7d8c8SVenkatesh Srinivas 
3000bb7d8c8SVenkatesh Srinivas static spinlock_t zone_mag_lock;
301e2caf0e7SMatthew Dillon static spinlock_t depot_spinlock;
3020bb7d8c8SVenkatesh Srinivas static struct magazine zone_magazine = {
3030bb7d8c8SVenkatesh Srinivas 	.flags = M_BURST | M_BURST_EARLY,
3040bb7d8c8SVenkatesh Srinivas 	.capacity = M_ZONE_ROUNDS,
3050bb7d8c8SVenkatesh Srinivas 	.rounds = 0,
3060bb7d8c8SVenkatesh Srinivas 	.burst_factor = M_BURST_FACTOR,
3070bb7d8c8SVenkatesh Srinivas 	.low_factor = M_LOW_ROUNDS
3080bb7d8c8SVenkatesh Srinivas };
3090bb7d8c8SVenkatesh Srinivas 
3100bb7d8c8SVenkatesh Srinivas #define MAGAZINE_FULL(mp)	(mp->rounds == mp->capacity)
3110bb7d8c8SVenkatesh Srinivas #define MAGAZINE_NOTFULL(mp)	(mp->rounds < mp->capacity)
3120bb7d8c8SVenkatesh Srinivas #define MAGAZINE_EMPTY(mp)	(mp->rounds == 0)
3130bb7d8c8SVenkatesh Srinivas #define MAGAZINE_NOTEMPTY(mp)	(mp->rounds != 0)
3140bb7d8c8SVenkatesh Srinivas 
31507a8ffeaSMatthew Dillon /*
31607a8ffeaSMatthew Dillon  * Each thread will have a pair of magazines per size-class (NZONES)
3170bb7d8c8SVenkatesh Srinivas  * The loaded magazine will support immediate allocations, the previous
31807a8ffeaSMatthew Dillon  * magazine will either be full or empty and can be swapped at need
31907a8ffeaSMatthew Dillon  */
3200bb7d8c8SVenkatesh Srinivas typedef struct magazine_pair {
3210bb7d8c8SVenkatesh Srinivas 	struct magazine	*loaded;
3220bb7d8c8SVenkatesh Srinivas 	struct magazine	*prev;
3230bb7d8c8SVenkatesh Srinivas } magazine_pair;
3240bb7d8c8SVenkatesh Srinivas 
3250bb7d8c8SVenkatesh Srinivas /* A depot is a collection of magazines for a single zone. */
3260bb7d8c8SVenkatesh Srinivas typedef struct magazine_depot {
3270bb7d8c8SVenkatesh Srinivas 	struct magazinelist full;
3280bb7d8c8SVenkatesh Srinivas 	struct magazinelist empty;
329ebe0d361SMatthew Dillon 	spinlock_t	lock;
3300bb7d8c8SVenkatesh Srinivas } magazine_depot;
3310bb7d8c8SVenkatesh Srinivas 
3320bb7d8c8SVenkatesh Srinivas typedef struct thr_mags {
3330bb7d8c8SVenkatesh Srinivas 	magazine_pair	mags[NZONES];
334e58e48b4SMatthew Dillon 	struct magazine	*newmag;
3350bb7d8c8SVenkatesh Srinivas 	int		init;
3360bb7d8c8SVenkatesh Srinivas } thr_mags;
3370bb7d8c8SVenkatesh Srinivas 
3380bb7d8c8SVenkatesh Srinivas static __thread thr_mags thread_mags TLS_ATTRIBUTE;
3390bb7d8c8SVenkatesh Srinivas static pthread_key_t thread_mags_key;
3400bb7d8c8SVenkatesh Srinivas static pthread_once_t thread_mags_once = PTHREAD_ONCE_INIT;
3410bb7d8c8SVenkatesh Srinivas static magazine_depot depots[NZONES];
3420bb7d8c8SVenkatesh Srinivas 
3430bb7d8c8SVenkatesh Srinivas /*
34482949828SMatthew Dillon  * Fixed globals (not per-cpu)
34582949828SMatthew Dillon  */
34682949828SMatthew Dillon static const int ZoneSize = ZALLOC_ZONE_SIZE;
34782949828SMatthew Dillon static const int ZoneLimit = ZALLOC_ZONE_LIMIT;
34882949828SMatthew Dillon static const int ZonePageCount = ZALLOC_ZONE_SIZE / PAGE_SIZE;
34982949828SMatthew Dillon static const int ZoneMask = ZALLOC_ZONE_SIZE - 1;
35082949828SMatthew Dillon 
3510bb7d8c8SVenkatesh Srinivas static int opt_madvise = 0;
3520bb7d8c8SVenkatesh Srinivas static int opt_utrace = 0;
3530bb7d8c8SVenkatesh Srinivas static int g_malloc_flags = 0;
3540bb7d8c8SVenkatesh Srinivas static struct slglobaldata SLGlobalData;
35582949828SMatthew Dillon static bigalloc_t bigalloc_array[BIGHSIZE];
35682949828SMatthew Dillon static spinlock_t bigspin_array[BIGXSIZE];
35707a8ffeaSMatthew Dillon static volatile void *bigcache_array[BIGCACHE];		/* atomic swap */
35807a8ffeaSMatthew Dillon static volatile size_t bigcache_size_array[BIGCACHE];	/* SMP races ok */
35907a8ffeaSMatthew Dillon static volatile int bigcache_index;			/* SMP races ok */
36082949828SMatthew Dillon static int malloc_panic;
36107a8ffeaSMatthew Dillon static size_t excess_alloc;				/* excess big allocs */
36282949828SMatthew Dillon 
36382949828SMatthew Dillon static void *_slaballoc(size_t size, int flags);
36482949828SMatthew Dillon static void *_slabrealloc(void *ptr, size_t size);
3650bb7d8c8SVenkatesh Srinivas static void _slabfree(void *ptr, int, bigalloc_t *);
366d3a54aeeSzrj static int _slabmemalign(void **memptr, size_t alignment, size_t size);
36782949828SMatthew Dillon static void *_vmem_alloc(size_t bytes, size_t align, int flags);
36882949828SMatthew Dillon static void _vmem_free(void *ptr, size_t bytes);
3690bb7d8c8SVenkatesh Srinivas static void *magazine_alloc(struct magazine *, int *);
3700bb7d8c8SVenkatesh Srinivas static int magazine_free(struct magazine *, void *);
371*8b07b5e8SMatthew Dillon static void *mtmagazine_alloc(int zi, int flags);
3720bb7d8c8SVenkatesh Srinivas static int mtmagazine_free(int zi, void *);
3730bb7d8c8SVenkatesh Srinivas static void mtmagazine_init(void);
3740bb7d8c8SVenkatesh Srinivas static void mtmagazine_destructor(void *);
3750bb7d8c8SVenkatesh Srinivas static slzone_t zone_alloc(int flags);
3760bb7d8c8SVenkatesh Srinivas static void zone_free(void *z);
377b58f1e66SSascha Wildner static void _mpanic(const char *ctl, ...) __printflike(1, 2);
378450f08dbSSascha Wildner static void malloc_init(void) __constructor(101);
37982949828SMatthew Dillon 
3800bb7d8c8SVenkatesh Srinivas struct nmalloc_utrace {
3810bb7d8c8SVenkatesh Srinivas 	void *p;
3820bb7d8c8SVenkatesh Srinivas 	size_t s;
3830bb7d8c8SVenkatesh Srinivas 	void *r;
3840bb7d8c8SVenkatesh Srinivas };
3850bb7d8c8SVenkatesh Srinivas 
3860bb7d8c8SVenkatesh Srinivas #define UTRACE(a, b, c)						\
3870bb7d8c8SVenkatesh Srinivas 	if (opt_utrace) {					\
3880bb7d8c8SVenkatesh Srinivas 		struct nmalloc_utrace ut = {			\
3890bb7d8c8SVenkatesh Srinivas 			.p = (a),				\
3900bb7d8c8SVenkatesh Srinivas 			.s = (b),				\
3910bb7d8c8SVenkatesh Srinivas 			.r = (c)				\
3920bb7d8c8SVenkatesh Srinivas 		};						\
3930bb7d8c8SVenkatesh Srinivas 		utrace(&ut, sizeof(ut));			\
3940bb7d8c8SVenkatesh Srinivas 	}
3950bb7d8c8SVenkatesh Srinivas 
3960bb7d8c8SVenkatesh Srinivas static void
3970bb7d8c8SVenkatesh Srinivas malloc_init(void)
3980bb7d8c8SVenkatesh Srinivas {
3990bb7d8c8SVenkatesh Srinivas 	const char *p = NULL;
4000bb7d8c8SVenkatesh Srinivas 
4010bb7d8c8SVenkatesh Srinivas 	if (issetugid() == 0)
4020bb7d8c8SVenkatesh Srinivas 		p = getenv("MALLOC_OPTIONS");
4030bb7d8c8SVenkatesh Srinivas 
4040bb7d8c8SVenkatesh Srinivas 	for (; p != NULL && *p != '\0'; p++) {
4050bb7d8c8SVenkatesh Srinivas 		switch(*p) {
4060bb7d8c8SVenkatesh Srinivas 		case 'u':	opt_utrace = 0; break;
4070bb7d8c8SVenkatesh Srinivas 		case 'U':	opt_utrace = 1; break;
4080bb7d8c8SVenkatesh Srinivas 		case 'h':	opt_madvise = 0; break;
4090bb7d8c8SVenkatesh Srinivas 		case 'H':	opt_madvise = 1; break;
4100bb7d8c8SVenkatesh Srinivas 		case 'z':	g_malloc_flags = 0; break;
4110bb7d8c8SVenkatesh Srinivas 		case 'Z':	g_malloc_flags = SAFLAG_ZERO; break;
4120bb7d8c8SVenkatesh Srinivas 		default:
4130bb7d8c8SVenkatesh Srinivas 			break;
4140bb7d8c8SVenkatesh Srinivas 		}
4150bb7d8c8SVenkatesh Srinivas 	}
4160bb7d8c8SVenkatesh Srinivas 
4170bb7d8c8SVenkatesh Srinivas 	UTRACE((void *) -1, 0, NULL);
4180bb7d8c8SVenkatesh Srinivas }
4190bb7d8c8SVenkatesh Srinivas 
42082949828SMatthew Dillon /*
4216c4de62cSMatthew Dillon  * We have to install a handler for nmalloc thread teardowns when
4226c4de62cSMatthew Dillon  * the thread is created.  We cannot delay this because destructors in
4236c4de62cSMatthew Dillon  * sophisticated userland programs can call malloc() for the first time
4246c4de62cSMatthew Dillon  * during their thread exit.
4256c4de62cSMatthew Dillon  *
4266c4de62cSMatthew Dillon  * This routine is called directly from pthreads.
4276c4de62cSMatthew Dillon  */
4286c4de62cSMatthew Dillon void
4296c4de62cSMatthew Dillon _nmalloc_thr_init(void)
4306c4de62cSMatthew Dillon {
4316c4de62cSMatthew Dillon 	thr_mags *tp;
4326c4de62cSMatthew Dillon 
4336c4de62cSMatthew Dillon 	/*
4346c4de62cSMatthew Dillon 	 * Disallow mtmagazine operations until the mtmagazine is
4356c4de62cSMatthew Dillon 	 * initialized.
4366c4de62cSMatthew Dillon 	 */
4376c4de62cSMatthew Dillon 	tp = &thread_mags;
4386c4de62cSMatthew Dillon 	tp->init = -1;
4396c4de62cSMatthew Dillon 
440d19ab22dSSascha Wildner 	_pthread_once(&thread_mags_once, mtmagazine_init);
441d19ab22dSSascha Wildner 	_pthread_setspecific(thread_mags_key, tp);
4426c4de62cSMatthew Dillon 	tp->init = 1;
4436c4de62cSMatthew Dillon }
4446c4de62cSMatthew Dillon 
445e2caf0e7SMatthew Dillon void
446e2caf0e7SMatthew Dillon _nmalloc_thr_prepfork(void)
447e2caf0e7SMatthew Dillon {
448e2caf0e7SMatthew Dillon 	if (__isthreaded) {
449e2caf0e7SMatthew Dillon 		_SPINLOCK(&zone_mag_lock);
450e2caf0e7SMatthew Dillon 		_SPINLOCK(&depot_spinlock);
451e2caf0e7SMatthew Dillon 	}
452e2caf0e7SMatthew Dillon }
453e2caf0e7SMatthew Dillon 
454e2caf0e7SMatthew Dillon void
455e2caf0e7SMatthew Dillon _nmalloc_thr_parentfork(void)
456e2caf0e7SMatthew Dillon {
457e2caf0e7SMatthew Dillon 	if (__isthreaded) {
458e2caf0e7SMatthew Dillon 		_SPINUNLOCK(&depot_spinlock);
459e2caf0e7SMatthew Dillon 		_SPINUNLOCK(&zone_mag_lock);
460e2caf0e7SMatthew Dillon 	}
461e2caf0e7SMatthew Dillon }
462e2caf0e7SMatthew Dillon 
463e2caf0e7SMatthew Dillon void
464e2caf0e7SMatthew Dillon _nmalloc_thr_childfork(void)
465e2caf0e7SMatthew Dillon {
466e2caf0e7SMatthew Dillon 	if (__isthreaded) {
467e2caf0e7SMatthew Dillon 		_SPINUNLOCK(&depot_spinlock);
468e2caf0e7SMatthew Dillon 		_SPINUNLOCK(&zone_mag_lock);
469e2caf0e7SMatthew Dillon 	}
470e2caf0e7SMatthew Dillon }
471e2caf0e7SMatthew Dillon 
4726c4de62cSMatthew Dillon /*
473721505deSMatthew Dillon  * Handle signal reentrancy safely whether we are threaded or not.
474721505deSMatthew Dillon  * This improves the stability for mono and will probably improve
475721505deSMatthew Dillon  * stability for other high-level languages which are becoming increasingly
476721505deSMatthew Dillon  * sophisticated.
477721505deSMatthew Dillon  *
478721505deSMatthew Dillon  * The sigblockall()/sigunblockall() implementation uses a counter on
479721505deSMatthew Dillon  * a per-thread shared user/kernel page, avoids system calls, and is thus
480721505deSMatthew Dillon  *  very fast.
481721505deSMatthew Dillon  */
482721505deSMatthew Dillon static __inline void
483721505deSMatthew Dillon nmalloc_sigblockall(void)
484721505deSMatthew Dillon {
485721505deSMatthew Dillon 	sigblockall();
486721505deSMatthew Dillon }
487721505deSMatthew Dillon 
488721505deSMatthew Dillon static __inline void
489721505deSMatthew Dillon nmalloc_sigunblockall(void)
490721505deSMatthew Dillon {
491721505deSMatthew Dillon 	sigunblockall();
492721505deSMatthew Dillon }
493721505deSMatthew Dillon 
494721505deSMatthew Dillon /*
49582949828SMatthew Dillon  * Thread locks.
49682949828SMatthew Dillon  */
49782949828SMatthew Dillon static __inline void
49882949828SMatthew Dillon slgd_lock(slglobaldata_t slgd)
49982949828SMatthew Dillon {
50082949828SMatthew Dillon 	if (__isthreaded)
50182949828SMatthew Dillon 		_SPINLOCK(&slgd->Spinlock);
502721505deSMatthew Dillon 	else
503721505deSMatthew Dillon 		sigblockall();
50482949828SMatthew Dillon }
50582949828SMatthew Dillon 
50682949828SMatthew Dillon static __inline void
50782949828SMatthew Dillon slgd_unlock(slglobaldata_t slgd)
50882949828SMatthew Dillon {
50982949828SMatthew Dillon 	if (__isthreaded)
51082949828SMatthew Dillon 		_SPINUNLOCK(&slgd->Spinlock);
511721505deSMatthew Dillon 	else
512721505deSMatthew Dillon 		sigunblockall();
51382949828SMatthew Dillon }
51482949828SMatthew Dillon 
5150bb7d8c8SVenkatesh Srinivas static __inline void
51684ebaf33SSascha Wildner depot_lock(magazine_depot *dp __unused)
5170bb7d8c8SVenkatesh Srinivas {
5180bb7d8c8SVenkatesh Srinivas 	if (__isthreaded)
519e2caf0e7SMatthew Dillon 		_SPINLOCK(&depot_spinlock);
520721505deSMatthew Dillon 	else
521721505deSMatthew Dillon 		sigblockall();
522e2caf0e7SMatthew Dillon #if 0
523e2caf0e7SMatthew Dillon 	if (__isthreaded)
524ebe0d361SMatthew Dillon 		_SPINLOCK(&dp->lock);
525e2caf0e7SMatthew Dillon #endif
5260bb7d8c8SVenkatesh Srinivas }
5270bb7d8c8SVenkatesh Srinivas 
5280bb7d8c8SVenkatesh Srinivas static __inline void
52984ebaf33SSascha Wildner depot_unlock(magazine_depot *dp __unused)
5300bb7d8c8SVenkatesh Srinivas {
5310bb7d8c8SVenkatesh Srinivas 	if (__isthreaded)
532e2caf0e7SMatthew Dillon 		_SPINUNLOCK(&depot_spinlock);
533721505deSMatthew Dillon 	else
534721505deSMatthew Dillon 		sigunblockall();
535e2caf0e7SMatthew Dillon #if 0
536e2caf0e7SMatthew Dillon 	if (__isthreaded)
537ebe0d361SMatthew Dillon 		_SPINUNLOCK(&dp->lock);
538e2caf0e7SMatthew Dillon #endif
5390bb7d8c8SVenkatesh Srinivas }
5400bb7d8c8SVenkatesh Srinivas 
5410bb7d8c8SVenkatesh Srinivas static __inline void
5420bb7d8c8SVenkatesh Srinivas zone_magazine_lock(void)
5430bb7d8c8SVenkatesh Srinivas {
5440bb7d8c8SVenkatesh Srinivas 	if (__isthreaded)
5450bb7d8c8SVenkatesh Srinivas 		_SPINLOCK(&zone_mag_lock);
546721505deSMatthew Dillon 	else
547721505deSMatthew Dillon 		sigblockall();
5480bb7d8c8SVenkatesh Srinivas }
5490bb7d8c8SVenkatesh Srinivas 
5500bb7d8c8SVenkatesh Srinivas static __inline void
5510bb7d8c8SVenkatesh Srinivas zone_magazine_unlock(void)
5520bb7d8c8SVenkatesh Srinivas {
5530bb7d8c8SVenkatesh Srinivas 	if (__isthreaded)
5540bb7d8c8SVenkatesh Srinivas 		_SPINUNLOCK(&zone_mag_lock);
555721505deSMatthew Dillon 	else
556721505deSMatthew Dillon 		sigunblockall();
5570bb7d8c8SVenkatesh Srinivas }
5580bb7d8c8SVenkatesh Srinivas 
5590bb7d8c8SVenkatesh Srinivas static __inline void
5600bb7d8c8SVenkatesh Srinivas swap_mags(magazine_pair *mp)
5610bb7d8c8SVenkatesh Srinivas {
5620bb7d8c8SVenkatesh Srinivas 	struct magazine *tmp;
5630bb7d8c8SVenkatesh Srinivas 	tmp = mp->loaded;
5640bb7d8c8SVenkatesh Srinivas 	mp->loaded = mp->prev;
5650bb7d8c8SVenkatesh Srinivas 	mp->prev = tmp;
5660bb7d8c8SVenkatesh Srinivas }
5670bb7d8c8SVenkatesh Srinivas 
56882949828SMatthew Dillon /*
56982949828SMatthew Dillon  * bigalloc hashing and locking support.
57082949828SMatthew Dillon  *
57182949828SMatthew Dillon  * Return an unmasked hash code for the passed pointer.
57282949828SMatthew Dillon  */
57382949828SMatthew Dillon static __inline int
57482949828SMatthew Dillon _bigalloc_hash(void *ptr)
57582949828SMatthew Dillon {
57682949828SMatthew Dillon 	int hv;
57782949828SMatthew Dillon 
5789a768e12SMatthew Dillon 	hv = ((int)(intptr_t)ptr >> PAGE_SHIFT) ^
5799a768e12SMatthew Dillon 	      ((int)(intptr_t)ptr >> (PAGE_SHIFT + BIGHSHIFT));
58082949828SMatthew Dillon 
58182949828SMatthew Dillon 	return(hv);
58282949828SMatthew Dillon }
58382949828SMatthew Dillon 
58482949828SMatthew Dillon /*
58582949828SMatthew Dillon  * Lock the hash chain and return a pointer to its base for the specified
58682949828SMatthew Dillon  * address.
58782949828SMatthew Dillon  */
58882949828SMatthew Dillon static __inline bigalloc_t *
58982949828SMatthew Dillon bigalloc_lock(void *ptr)
59082949828SMatthew Dillon {
59182949828SMatthew Dillon 	int hv = _bigalloc_hash(ptr);
59282949828SMatthew Dillon 	bigalloc_t *bigp;
59382949828SMatthew Dillon 
59482949828SMatthew Dillon 	bigp = &bigalloc_array[hv & BIGHMASK];
59582949828SMatthew Dillon 	if (__isthreaded)
59682949828SMatthew Dillon 		_SPINLOCK(&bigspin_array[hv & BIGXMASK]);
59782949828SMatthew Dillon 	return(bigp);
59882949828SMatthew Dillon }
59982949828SMatthew Dillon 
60082949828SMatthew Dillon /*
60182949828SMatthew Dillon  * Lock the hash chain and return a pointer to its base for the specified
60282949828SMatthew Dillon  * address.
60382949828SMatthew Dillon  *
60482949828SMatthew Dillon  * BUT, if the hash chain is empty, just return NULL and do not bother
60582949828SMatthew Dillon  * to lock anything.
60682949828SMatthew Dillon  */
60782949828SMatthew Dillon static __inline bigalloc_t *
60882949828SMatthew Dillon bigalloc_check_and_lock(void *ptr)
60982949828SMatthew Dillon {
61082949828SMatthew Dillon 	int hv = _bigalloc_hash(ptr);
61182949828SMatthew Dillon 	bigalloc_t *bigp;
61282949828SMatthew Dillon 
61382949828SMatthew Dillon 	bigp = &bigalloc_array[hv & BIGHMASK];
61482949828SMatthew Dillon 	if (*bigp == NULL)
61582949828SMatthew Dillon 		return(NULL);
61682949828SMatthew Dillon 	if (__isthreaded) {
61782949828SMatthew Dillon 		_SPINLOCK(&bigspin_array[hv & BIGXMASK]);
61882949828SMatthew Dillon 	}
61982949828SMatthew Dillon 	return(bigp);
62082949828SMatthew Dillon }
62182949828SMatthew Dillon 
62282949828SMatthew Dillon static __inline void
62382949828SMatthew Dillon bigalloc_unlock(void *ptr)
62482949828SMatthew Dillon {
62582949828SMatthew Dillon 	int hv;
62682949828SMatthew Dillon 
62782949828SMatthew Dillon 	if (__isthreaded) {
62882949828SMatthew Dillon 		hv = _bigalloc_hash(ptr);
62982949828SMatthew Dillon 		_SPINUNLOCK(&bigspin_array[hv & BIGXMASK]);
63082949828SMatthew Dillon 	}
63182949828SMatthew Dillon }
63282949828SMatthew Dillon 
63382949828SMatthew Dillon /*
63407a8ffeaSMatthew Dillon  * Find a bigcache entry that might work for the allocation.  SMP races are
63507a8ffeaSMatthew Dillon  * ok here except for the swap (that is, it is ok if bigcache_size_array[i]
63607a8ffeaSMatthew Dillon  * is wrong or if a NULL or too-small big is returned).
63707a8ffeaSMatthew Dillon  *
63807a8ffeaSMatthew Dillon  * Generally speaking it is ok to find a large entry even if the bytes
63907a8ffeaSMatthew Dillon  * requested are relatively small (but still oversized), because we really
64007a8ffeaSMatthew Dillon  * don't know *what* the application is going to do with the buffer.
64107a8ffeaSMatthew Dillon  */
64207a8ffeaSMatthew Dillon static __inline
64307a8ffeaSMatthew Dillon bigalloc_t
64407a8ffeaSMatthew Dillon bigcache_find_alloc(size_t bytes)
64507a8ffeaSMatthew Dillon {
64607a8ffeaSMatthew Dillon 	bigalloc_t big = NULL;
64707a8ffeaSMatthew Dillon 	size_t test;
64807a8ffeaSMatthew Dillon 	int i;
64907a8ffeaSMatthew Dillon 
65007a8ffeaSMatthew Dillon 	for (i = 0; i < BIGCACHE; ++i) {
65107a8ffeaSMatthew Dillon 		test = bigcache_size_array[i];
65207a8ffeaSMatthew Dillon 		if (bytes <= test) {
65307a8ffeaSMatthew Dillon 			bigcache_size_array[i] = 0;
65407a8ffeaSMatthew Dillon 			big = atomic_swap_ptr(&bigcache_array[i], NULL);
65507a8ffeaSMatthew Dillon 			break;
65607a8ffeaSMatthew Dillon 		}
65707a8ffeaSMatthew Dillon 	}
65807a8ffeaSMatthew Dillon 	return big;
65907a8ffeaSMatthew Dillon }
66007a8ffeaSMatthew Dillon 
66107a8ffeaSMatthew Dillon /*
66207a8ffeaSMatthew Dillon  * Free a bigcache entry, possibly returning one that the caller really must
66307a8ffeaSMatthew Dillon  * free.  This is used to cache recent oversized memory blocks.  Only
66407a8ffeaSMatthew Dillon  * big blocks smaller than BIGCACHE_LIMIT will be cached this way, so try
66507a8ffeaSMatthew Dillon  * to collect the biggest ones we can that are under the limit.
66607a8ffeaSMatthew Dillon  */
66707a8ffeaSMatthew Dillon static __inline
66807a8ffeaSMatthew Dillon bigalloc_t
66907a8ffeaSMatthew Dillon bigcache_find_free(bigalloc_t big)
67007a8ffeaSMatthew Dillon {
67107a8ffeaSMatthew Dillon 	int i;
67207a8ffeaSMatthew Dillon 	int j;
67307a8ffeaSMatthew Dillon 	int b;
67407a8ffeaSMatthew Dillon 
67507a8ffeaSMatthew Dillon 	b = ++bigcache_index;
67607a8ffeaSMatthew Dillon 	for (i = 0; i < BIGCACHE; ++i) {
67707a8ffeaSMatthew Dillon 		j = (b + i) & BIGCACHE_MASK;
67807a8ffeaSMatthew Dillon 		if (bigcache_size_array[j] < big->bytes) {
67907a8ffeaSMatthew Dillon 			bigcache_size_array[j] = big->bytes;
68007a8ffeaSMatthew Dillon 			big = atomic_swap_ptr(&bigcache_array[j], big);
68107a8ffeaSMatthew Dillon 			break;
68207a8ffeaSMatthew Dillon 		}
68307a8ffeaSMatthew Dillon 	}
68407a8ffeaSMatthew Dillon 	return big;
68507a8ffeaSMatthew Dillon }
68607a8ffeaSMatthew Dillon 
68707a8ffeaSMatthew Dillon static __inline
68807a8ffeaSMatthew Dillon void
68907a8ffeaSMatthew Dillon handle_excess_big(void)
69007a8ffeaSMatthew Dillon {
69107a8ffeaSMatthew Dillon 	int i;
69207a8ffeaSMatthew Dillon 	bigalloc_t big;
69307a8ffeaSMatthew Dillon 	bigalloc_t *bigp;
69407a8ffeaSMatthew Dillon 
69507a8ffeaSMatthew Dillon 	if (excess_alloc <= BIGCACHE_EXCESS)
69607a8ffeaSMatthew Dillon 		return;
69707a8ffeaSMatthew Dillon 
69807a8ffeaSMatthew Dillon 	for (i = 0; i < BIGHSIZE; ++i) {
69907a8ffeaSMatthew Dillon 		bigp = &bigalloc_array[i];
70007a8ffeaSMatthew Dillon 		if (*bigp == NULL)
70107a8ffeaSMatthew Dillon 			continue;
70207a8ffeaSMatthew Dillon 		if (__isthreaded)
70307a8ffeaSMatthew Dillon 			_SPINLOCK(&bigspin_array[i & BIGXMASK]);
70407a8ffeaSMatthew Dillon 		for (big = *bigp; big; big = big->next) {
70507a8ffeaSMatthew Dillon 			if (big->active < big->bytes) {
706721505deSMatthew Dillon 				MASSERT_WTHUNLK((big->active & PAGE_MASK) == 0,
707721505deSMatthew Dillon 				    _SPINUNLOCK(&bigspin_array[i & BIGXMASK]));
708721505deSMatthew Dillon 				MASSERT_WTHUNLK((big->bytes & PAGE_MASK) == 0,
709721505deSMatthew Dillon 				    _SPINUNLOCK(&bigspin_array[i & BIGXMASK]));
71007a8ffeaSMatthew Dillon 				munmap((char *)big->base + big->active,
71107a8ffeaSMatthew Dillon 				       big->bytes - big->active);
71207a8ffeaSMatthew Dillon 				atomic_add_long(&excess_alloc,
71307a8ffeaSMatthew Dillon 						big->active - big->bytes);
71407a8ffeaSMatthew Dillon 				big->bytes = big->active;
71507a8ffeaSMatthew Dillon 			}
71607a8ffeaSMatthew Dillon 		}
71707a8ffeaSMatthew Dillon 		if (__isthreaded)
71807a8ffeaSMatthew Dillon 			_SPINUNLOCK(&bigspin_array[i & BIGXMASK]);
71907a8ffeaSMatthew Dillon 	}
72007a8ffeaSMatthew Dillon }
72107a8ffeaSMatthew Dillon 
72207a8ffeaSMatthew Dillon /*
72382949828SMatthew Dillon  * Calculate the zone index for the allocation request size and set the
72482949828SMatthew Dillon  * allocation request size to that particular zone's chunk size.
72582949828SMatthew Dillon  */
72682949828SMatthew Dillon static __inline int
72782949828SMatthew Dillon zoneindex(size_t *bytes, size_t *chunking)
72882949828SMatthew Dillon {
72982949828SMatthew Dillon 	size_t n = (unsigned int)*bytes;	/* unsigned for shift opt */
7303f81f453SMatthew Dillon 
7313f81f453SMatthew Dillon 	/*
7323f81f453SMatthew Dillon 	 * This used to be 8-byte chunks and 16 zones for n < 128.
7333f81f453SMatthew Dillon 	 * However some instructions may require 16-byte alignment
7343f81f453SMatthew Dillon 	 * (aka SIMD) and programs might not request an aligned size
7353f81f453SMatthew Dillon 	 * (aka GCC-7), so change this as follows:
7363f81f453SMatthew Dillon 	 *
7373f81f453SMatthew Dillon 	 * 0-15 bytes	8-byte alignment in two zones	(0-1)
7383f81f453SMatthew Dillon 	 * 16-127 bytes	16-byte alignment in four zones	(3-10)
7393f81f453SMatthew Dillon 	 * zone index 2 and 11-15 are currently unused.
7403f81f453SMatthew Dillon 	 */
7413f81f453SMatthew Dillon 	if (n < 16) {
74282949828SMatthew Dillon 		*bytes = n = (n + 7) & ~7;
74382949828SMatthew Dillon 		*chunking = 8;
7443f81f453SMatthew Dillon 		return(n / 8 - 1);		/* 8 byte chunks, 2 zones */
7453f81f453SMatthew Dillon 		/* zones 0,1, zone 2 is unused */
7463f81f453SMatthew Dillon 	}
7473f81f453SMatthew Dillon 	if (n < 128) {
7483f81f453SMatthew Dillon 		*bytes = n = (n + 15) & ~15;
7493f81f453SMatthew Dillon 		*chunking = 16;
7503f81f453SMatthew Dillon 		return(n / 16 + 2);		/* 16 byte chunks, 8 zones */
7513f81f453SMatthew Dillon 		/* zones 3-10, zones 11-15 unused */
75282949828SMatthew Dillon 	}
75382949828SMatthew Dillon 	if (n < 256) {
75482949828SMatthew Dillon 		*bytes = n = (n + 15) & ~15;
75582949828SMatthew Dillon 		*chunking = 16;
75682949828SMatthew Dillon 		return(n / 16 + 7);
75782949828SMatthew Dillon 	}
75882949828SMatthew Dillon 	if (n < 8192) {
75982949828SMatthew Dillon 		if (n < 512) {
76082949828SMatthew Dillon 			*bytes = n = (n + 31) & ~31;
76182949828SMatthew Dillon 			*chunking = 32;
76282949828SMatthew Dillon 			return(n / 32 + 15);
76382949828SMatthew Dillon 		}
76482949828SMatthew Dillon 		if (n < 1024) {
76582949828SMatthew Dillon 			*bytes = n = (n + 63) & ~63;
76682949828SMatthew Dillon 			*chunking = 64;
76782949828SMatthew Dillon 			return(n / 64 + 23);
76882949828SMatthew Dillon 		}
76982949828SMatthew Dillon 		if (n < 2048) {
77082949828SMatthew Dillon 			*bytes = n = (n + 127) & ~127;
77182949828SMatthew Dillon 			*chunking = 128;
77282949828SMatthew Dillon 			return(n / 128 + 31);
77382949828SMatthew Dillon 		}
77482949828SMatthew Dillon 		if (n < 4096) {
77582949828SMatthew Dillon 			*bytes = n = (n + 255) & ~255;
77682949828SMatthew Dillon 			*chunking = 256;
77782949828SMatthew Dillon 			return(n / 256 + 39);
77882949828SMatthew Dillon 		}
77982949828SMatthew Dillon 		*bytes = n = (n + 511) & ~511;
78082949828SMatthew Dillon 		*chunking = 512;
78182949828SMatthew Dillon 		return(n / 512 + 47);
78282949828SMatthew Dillon 	}
78382949828SMatthew Dillon #if ZALLOC_ZONE_LIMIT > 8192
78482949828SMatthew Dillon 	if (n < 16384) {
78582949828SMatthew Dillon 		*bytes = n = (n + 1023) & ~1023;
78682949828SMatthew Dillon 		*chunking = 1024;
78782949828SMatthew Dillon 		return(n / 1024 + 55);
78882949828SMatthew Dillon 	}
78982949828SMatthew Dillon #endif
79082949828SMatthew Dillon #if ZALLOC_ZONE_LIMIT > 16384
79182949828SMatthew Dillon 	if (n < 32768) {
79282949828SMatthew Dillon 		*bytes = n = (n + 2047) & ~2047;
79382949828SMatthew Dillon 		*chunking = 2048;
79482949828SMatthew Dillon 		return(n / 2048 + 63);
79582949828SMatthew Dillon 	}
79682949828SMatthew Dillon #endif
7970a227237SSascha Wildner 	_mpanic("Unexpected byte count %zu", n);
79882949828SMatthew Dillon 	return(0);
79982949828SMatthew Dillon }
80082949828SMatthew Dillon 
80182949828SMatthew Dillon /*
80282949828SMatthew Dillon  * malloc() - call internal slab allocator
80382949828SMatthew Dillon  */
80482949828SMatthew Dillon void *
80569baab3bSImre Vadász __malloc(size_t size)
80682949828SMatthew Dillon {
80711e45f67SMatthew Dillon 	void *ptr;
80811e45f67SMatthew Dillon 
809721505deSMatthew Dillon 	nmalloc_sigblockall();
81011e45f67SMatthew Dillon 	ptr = _slaballoc(size, 0);
81111e45f67SMatthew Dillon 	if (ptr == NULL)
81211e45f67SMatthew Dillon 		errno = ENOMEM;
8130bb7d8c8SVenkatesh Srinivas 	else
8140bb7d8c8SVenkatesh Srinivas 		UTRACE(0, size, ptr);
815721505deSMatthew Dillon 	nmalloc_sigunblockall();
816721505deSMatthew Dillon 
81711e45f67SMatthew Dillon 	return(ptr);
81882949828SMatthew Dillon }
81982949828SMatthew Dillon 
8202d114219SJoris Giovannangeli #define MUL_NO_OVERFLOW	(1UL << (sizeof(size_t) * 4))
8212d114219SJoris Giovannangeli 
82282949828SMatthew Dillon /*
82382949828SMatthew Dillon  * calloc() - call internal slab allocator
82482949828SMatthew Dillon  */
82582949828SMatthew Dillon void *
82669baab3bSImre Vadász __calloc(size_t number, size_t size)
82782949828SMatthew Dillon {
82811e45f67SMatthew Dillon 	void *ptr;
82911e45f67SMatthew Dillon 
83032af0e61SJoris Giovannangeli 	if ((number >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
83132af0e61SJoris Giovannangeli 	     number > 0 && SIZE_MAX / number < size) {
8322d114219SJoris Giovannangeli 		errno = ENOMEM;
8332d114219SJoris Giovannangeli 		return(NULL);
8342d114219SJoris Giovannangeli 	}
8352d114219SJoris Giovannangeli 
836721505deSMatthew Dillon 	nmalloc_sigblockall();
83711e45f67SMatthew Dillon 	ptr = _slaballoc(number * size, SAFLAG_ZERO);
83811e45f67SMatthew Dillon 	if (ptr == NULL)
83911e45f67SMatthew Dillon 		errno = ENOMEM;
8400bb7d8c8SVenkatesh Srinivas 	else
8410bb7d8c8SVenkatesh Srinivas 		UTRACE(0, number * size, ptr);
842721505deSMatthew Dillon 	nmalloc_sigunblockall();
843721505deSMatthew Dillon 
84411e45f67SMatthew Dillon 	return(ptr);
84582949828SMatthew Dillon }
84682949828SMatthew Dillon 
84782949828SMatthew Dillon /*
84882949828SMatthew Dillon  * realloc() (SLAB ALLOCATOR)
84982949828SMatthew Dillon  *
85082949828SMatthew Dillon  * We do not attempt to optimize this routine beyond reusing the same
85182949828SMatthew Dillon  * pointer if the new size fits within the chunking of the old pointer's
85282949828SMatthew Dillon  * zone.
85382949828SMatthew Dillon  */
85482949828SMatthew Dillon void *
85569baab3bSImre Vadász __realloc(void *ptr, size_t size)
85682949828SMatthew Dillon {
8570bb7d8c8SVenkatesh Srinivas 	void *ret;
858721505deSMatthew Dillon 
859721505deSMatthew Dillon 	nmalloc_sigblockall();
8600bb7d8c8SVenkatesh Srinivas 	ret = _slabrealloc(ptr, size);
8610bb7d8c8SVenkatesh Srinivas 	if (ret == NULL)
86211e45f67SMatthew Dillon 		errno = ENOMEM;
8630bb7d8c8SVenkatesh Srinivas 	else
8640bb7d8c8SVenkatesh Srinivas 		UTRACE(ptr, size, ret);
865721505deSMatthew Dillon 	nmalloc_sigunblockall();
866721505deSMatthew Dillon 
8670bb7d8c8SVenkatesh Srinivas 	return(ret);
86882949828SMatthew Dillon }
86982949828SMatthew Dillon 
87011e45f67SMatthew Dillon /*
871d3a54aeeSzrj  * aligned_alloc()
872d3a54aeeSzrj  *
873d3a54aeeSzrj  * Allocate (size) bytes with a alignment of (alignment).
874d3a54aeeSzrj  */
875d3a54aeeSzrj void *
876d3a54aeeSzrj __aligned_alloc(size_t alignment, size_t size)
877d3a54aeeSzrj {
878d3a54aeeSzrj 	void *ptr;
879d3a54aeeSzrj 	int rc;
880d3a54aeeSzrj 
881721505deSMatthew Dillon 	nmalloc_sigblockall();
882d3a54aeeSzrj 	ptr = NULL;
883d3a54aeeSzrj 	rc = _slabmemalign(&ptr, alignment, size);
884d3a54aeeSzrj 	if (rc)
885d3a54aeeSzrj 		errno = rc;
886721505deSMatthew Dillon 	nmalloc_sigunblockall();
887d3a54aeeSzrj 
888d3a54aeeSzrj 	return (ptr);
889d3a54aeeSzrj }
890d3a54aeeSzrj 
891d3a54aeeSzrj /*
89211e45f67SMatthew Dillon  * posix_memalign()
89311e45f67SMatthew Dillon  *
89411e45f67SMatthew Dillon  * Allocate (size) bytes with a alignment of (alignment), where (alignment)
89511e45f67SMatthew Dillon  * is a power of 2 >= sizeof(void *).
896d3a54aeeSzrj  */
897d3a54aeeSzrj int
898d3a54aeeSzrj __posix_memalign(void **memptr, size_t alignment, size_t size)
899d3a54aeeSzrj {
900d3a54aeeSzrj 	int rc;
901d3a54aeeSzrj 
902d3a54aeeSzrj 	/*
903d3a54aeeSzrj 	 * OpenGroup spec issue 6 check
904d3a54aeeSzrj 	 */
905d3a54aeeSzrj 	if (alignment < sizeof(void *)) {
906d3a54aeeSzrj 		*memptr = NULL;
907d3a54aeeSzrj 		return(EINVAL);
908d3a54aeeSzrj 	}
909d3a54aeeSzrj 
910721505deSMatthew Dillon 	nmalloc_sigblockall();
911d3a54aeeSzrj 	rc = _slabmemalign(memptr, alignment, size);
912721505deSMatthew Dillon 	nmalloc_sigunblockall();
913d3a54aeeSzrj 
914d3a54aeeSzrj 	return (rc);
915d3a54aeeSzrj }
916d3a54aeeSzrj 
917d3a54aeeSzrj /*
91811e45f67SMatthew Dillon  * The slab allocator will allocate on power-of-2 boundaries up to
91911e45f67SMatthew Dillon  * at least PAGE_SIZE.  We use the zoneindex mechanic to find a
92011e45f67SMatthew Dillon  * zone matching the requirements, and _vmem_alloc() otherwise.
92111e45f67SMatthew Dillon  */
922d3a54aeeSzrj static int
923d3a54aeeSzrj _slabmemalign(void **memptr, size_t alignment, size_t size)
92411e45f67SMatthew Dillon {
92511e45f67SMatthew Dillon 	bigalloc_t *bigp;
92611e45f67SMatthew Dillon 	bigalloc_t big;
9276c23d8e0SJordan Gordeev 	size_t chunking;
928cf515c3aSJohn Marino 	int zi __unused;
92911e45f67SMatthew Dillon 
930d3a54aeeSzrj 	if (alignment < 1) {
931d3a54aeeSzrj 		*memptr = NULL;
932d3a54aeeSzrj 		return(EINVAL);
933d3a54aeeSzrj 	}
934d3a54aeeSzrj 
93511e45f67SMatthew Dillon 	/*
93611e45f67SMatthew Dillon 	 * OpenGroup spec issue 6 checks
93711e45f67SMatthew Dillon 	 */
93811e45f67SMatthew Dillon 	if ((alignment | (alignment - 1)) + 1 != (alignment << 1)) {
93911e45f67SMatthew Dillon 		*memptr = NULL;
94011e45f67SMatthew Dillon 		return(EINVAL);
94111e45f67SMatthew Dillon 	}
94211e45f67SMatthew Dillon 
94311e45f67SMatthew Dillon 	/*
9448ff099aeSMatthew Dillon 	 * Our zone mechanism guarantees same-sized alignment for any
9458ff099aeSMatthew Dillon 	 * power-of-2 allocation.  If size is a power-of-2 and reasonable
9468ff099aeSMatthew Dillon 	 * we can just call _slaballoc() and be done.  We round size up
9478ff099aeSMatthew Dillon 	 * to the nearest alignment boundary to improve our odds of
9488ff099aeSMatthew Dillon 	 * it becoming a power-of-2 if it wasn't before.
94911e45f67SMatthew Dillon 	 */
9508ff099aeSMatthew Dillon 	if (size <= alignment)
95111e45f67SMatthew Dillon 		size = alignment;
9528ff099aeSMatthew Dillon 	else
9538ff099aeSMatthew Dillon 		size = (size + alignment - 1) & ~(size_t)(alignment - 1);
954e9586122Szrj 
955e9586122Szrj 	/*
95672732463SMatthew Dillon 	 * If we have overflowed above when rounding to the nearest alignment
957e9586122Szrj 	 * boundary, just return ENOMEM, size should be == N * sizeof(void *).
95872732463SMatthew Dillon 	 *
95972732463SMatthew Dillon 	 * Power-of-2 allocations up to 8KB will be aligned to the allocation
96072732463SMatthew Dillon 	 * size and _slaballoc() can simply be used.  Please see line 1082
96172732463SMatthew Dillon 	 * for this special case: 'Align the storage in the zone based on
96272732463SMatthew Dillon 	 * the chunking' has a special case for powers of 2.
963e9586122Szrj 	 */
964e9586122Szrj 	if (size == 0)
965e9586122Szrj 		return(ENOMEM);
966e9586122Szrj 
96772732463SMatthew Dillon 	if (size <= PAGE_SIZE*2 && (size | (size - 1)) + 1 == (size << 1)) {
9688ff099aeSMatthew Dillon 		*memptr = _slaballoc(size, 0);
9698ff099aeSMatthew Dillon 		return(*memptr ? 0 : ENOMEM);
9708ff099aeSMatthew Dillon 	}
9718ff099aeSMatthew Dillon 
9728ff099aeSMatthew Dillon 	/*
9738ff099aeSMatthew Dillon 	 * Otherwise locate a zone with a chunking that matches
9748ff099aeSMatthew Dillon 	 * the requested alignment, within reason.   Consider two cases:
9758ff099aeSMatthew Dillon 	 *
9768ff099aeSMatthew Dillon 	 * (1) A 1K allocation on a 32-byte alignment.  The first zoneindex
9778ff099aeSMatthew Dillon 	 *     we find will be the best fit because the chunking will be
9788ff099aeSMatthew Dillon 	 *     greater or equal to the alignment.
9798ff099aeSMatthew Dillon 	 *
9808ff099aeSMatthew Dillon 	 * (2) A 513 allocation on a 256-byte alignment.  In this case
9818ff099aeSMatthew Dillon 	 *     the first zoneindex we find will be for 576 byte allocations
9828ff099aeSMatthew Dillon 	 *     with a chunking of 64, which is not sufficient.  To fix this
9838ff099aeSMatthew Dillon 	 *     we simply find the nearest power-of-2 >= size and use the
9848ff099aeSMatthew Dillon 	 *     same side-effect of _slaballoc() which guarantees
9858ff099aeSMatthew Dillon 	 *     same-alignment on a power-of-2 allocation.
9868ff099aeSMatthew Dillon 	 */
9878ff099aeSMatthew Dillon 	if (size < PAGE_SIZE) {
98811e45f67SMatthew Dillon 		zi = zoneindex(&size, &chunking);
98911e45f67SMatthew Dillon 		if (chunking >= alignment) {
99011e45f67SMatthew Dillon 			*memptr = _slaballoc(size, 0);
99111e45f67SMatthew Dillon 			return(*memptr ? 0 : ENOMEM);
99211e45f67SMatthew Dillon 		}
9938ff099aeSMatthew Dillon 		if (size >= 1024)
9948ff099aeSMatthew Dillon 			alignment = 1024;
9958ff099aeSMatthew Dillon 		if (size >= 16384)
9968ff099aeSMatthew Dillon 			alignment = 16384;
9978ff099aeSMatthew Dillon 		while (alignment < size)
9988ff099aeSMatthew Dillon 			alignment <<= 1;
9998ff099aeSMatthew Dillon 		*memptr = _slaballoc(alignment, 0);
10008ff099aeSMatthew Dillon 		return(*memptr ? 0 : ENOMEM);
100111e45f67SMatthew Dillon 	}
100211e45f67SMatthew Dillon 
100311e45f67SMatthew Dillon 	/*
100411e45f67SMatthew Dillon 	 * If the slab allocator cannot handle it use vmem_alloc().
100511e45f67SMatthew Dillon 	 *
100611e45f67SMatthew Dillon 	 * Alignment must be adjusted up to at least PAGE_SIZE in this case.
100711e45f67SMatthew Dillon 	 */
100811e45f67SMatthew Dillon 	if (alignment < PAGE_SIZE)
100911e45f67SMatthew Dillon 		alignment = PAGE_SIZE;
101011e45f67SMatthew Dillon 	if (size < alignment)
101111e45f67SMatthew Dillon 		size = alignment;
101211e45f67SMatthew Dillon 	size = (size + PAGE_MASK) & ~(size_t)PAGE_MASK;
1013c8a21d03SImre Vadász 	if (alignment == PAGE_SIZE && size <= BIGCACHE_LIMIT) {
1014c8a21d03SImre Vadász 		big = bigcache_find_alloc(size);
1015c8a21d03SImre Vadász 		if (big && big->bytes < size) {
1016c8a21d03SImre Vadász 			_slabfree(big->base, FASTSLABREALLOC, &big);
1017c8a21d03SImre Vadász 			big = NULL;
1018c8a21d03SImre Vadász 		}
1019c8a21d03SImre Vadász 		if (big) {
1020c8a21d03SImre Vadász 			*memptr = big->base;
1021c8a21d03SImre Vadász 			big->active = size;
1022c8a21d03SImre Vadász 			if (big->active < big->bytes) {
1023c8a21d03SImre Vadász 				atomic_add_long(&excess_alloc,
1024c8a21d03SImre Vadász 						big->bytes - big->active);
1025c8a21d03SImre Vadász 			}
1026c8a21d03SImre Vadász 			bigp = bigalloc_lock(*memptr);
1027c8a21d03SImre Vadász 			big->next = *bigp;
1028c8a21d03SImre Vadász 			*bigp = big;
1029c8a21d03SImre Vadász 			bigalloc_unlock(*memptr);
1030c8a21d03SImre Vadász 			handle_excess_big();
1031c8a21d03SImre Vadász 			return(0);
1032c8a21d03SImre Vadász 		}
1033c8a21d03SImre Vadász 	}
103411e45f67SMatthew Dillon 	*memptr = _vmem_alloc(size, alignment, 0);
103511e45f67SMatthew Dillon 	if (*memptr == NULL)
103611e45f67SMatthew Dillon 		return(ENOMEM);
103711e45f67SMatthew Dillon 
103811e45f67SMatthew Dillon 	big = _slaballoc(sizeof(struct bigalloc), 0);
103911e45f67SMatthew Dillon 	if (big == NULL) {
104011e45f67SMatthew Dillon 		_vmem_free(*memptr, size);
104111e45f67SMatthew Dillon 		*memptr = NULL;
104211e45f67SMatthew Dillon 		return(ENOMEM);
104311e45f67SMatthew Dillon 	}
104411e45f67SMatthew Dillon 	bigp = bigalloc_lock(*memptr);
104511e45f67SMatthew Dillon 	big->base = *memptr;
104607a8ffeaSMatthew Dillon 	big->active = size;
104707a8ffeaSMatthew Dillon 	big->bytes = size;		/* no excess */
104811e45f67SMatthew Dillon 	big->next = *bigp;
104911e45f67SMatthew Dillon 	*bigp = big;
105011e45f67SMatthew Dillon 	bigalloc_unlock(*memptr);
105111e45f67SMatthew Dillon 
105211e45f67SMatthew Dillon 	return(0);
105311e45f67SMatthew Dillon }
105411e45f67SMatthew Dillon 
105511e45f67SMatthew Dillon /*
105611e45f67SMatthew Dillon  * free() (SLAB ALLOCATOR) - do the obvious
105711e45f67SMatthew Dillon  */
105882949828SMatthew Dillon void
105969baab3bSImre Vadász __free(void *ptr)
106082949828SMatthew Dillon {
10610bb7d8c8SVenkatesh Srinivas 	UTRACE(ptr, 0, 0);
1062721505deSMatthew Dillon 	nmalloc_sigblockall();
10630bb7d8c8SVenkatesh Srinivas 	_slabfree(ptr, 0, NULL);
1064721505deSMatthew Dillon 	nmalloc_sigunblockall();
106582949828SMatthew Dillon }
106682949828SMatthew Dillon 
106782949828SMatthew Dillon /*
106882949828SMatthew Dillon  * _slaballoc()	(SLAB ALLOCATOR)
106982949828SMatthew Dillon  *
107082949828SMatthew Dillon  *	Allocate memory via the slab allocator.  If the request is too large,
107182949828SMatthew Dillon  *	or if it page-aligned beyond a certain size, we fall back to the
107282949828SMatthew Dillon  *	KMEM subsystem
107382949828SMatthew Dillon  */
107482949828SMatthew Dillon static void *
107582949828SMatthew Dillon _slaballoc(size_t size, int flags)
107682949828SMatthew Dillon {
107782949828SMatthew Dillon 	slzone_t z;
107882949828SMatthew Dillon 	slchunk_t chunk;
107982949828SMatthew Dillon 	slglobaldata_t slgd;
10806c23d8e0SJordan Gordeev 	size_t chunking;
1081*8b07b5e8SMatthew Dillon 	thr_mags *tp;
1082*8b07b5e8SMatthew Dillon 	struct magazine *mp;
1083*8b07b5e8SMatthew Dillon 	int count;
108482949828SMatthew Dillon 	int zi;
108582949828SMatthew Dillon 	int off;
10860bb7d8c8SVenkatesh Srinivas 	void *obj;
10870bb7d8c8SVenkatesh Srinivas 
108882949828SMatthew Dillon 	/*
108982949828SMatthew Dillon 	 * Handle the degenerate size == 0 case.  Yes, this does happen.
109082949828SMatthew Dillon 	 * Return a special pointer.  This is to maintain compatibility with
109182949828SMatthew Dillon 	 * the original malloc implementation.  Certain devices, such as the
109282949828SMatthew Dillon 	 * adaptec driver, not only allocate 0 bytes, they check for NULL and
109382949828SMatthew Dillon 	 * also realloc() later on.  Joy.
109482949828SMatthew Dillon 	 */
109582949828SMatthew Dillon 	if (size == 0)
1096e2caf0e7SMatthew Dillon 		size = 1;
109782949828SMatthew Dillon 
10980bb7d8c8SVenkatesh Srinivas 	/* Capture global flags */
10990bb7d8c8SVenkatesh Srinivas 	flags |= g_malloc_flags;
11000bb7d8c8SVenkatesh Srinivas 
110182949828SMatthew Dillon 	/*
110282949828SMatthew Dillon 	 * Handle large allocations directly.  There should not be very many
110382949828SMatthew Dillon 	 * of these so performance is not a big issue.
110482949828SMatthew Dillon 	 *
110582949828SMatthew Dillon 	 * The backend allocator is pretty nasty on a SMP system.   Use the
110682949828SMatthew Dillon 	 * slab allocator for one and two page-sized chunks even though we
110782949828SMatthew Dillon 	 * lose some efficiency.
110872732463SMatthew Dillon 	 *
110972732463SMatthew Dillon 	 * NOTE: Please see posix_memalign around line 864, which assumes
111072732463SMatthew Dillon 	 *	 that power-of-2 allocations of PAGE_SIZE and PAGE_SIZE*2
111172732463SMatthew Dillon 	 *	 can use _slaballoc() and be aligned to the same.  The
111272732463SMatthew Dillon 	 *	 zone cache can be used for this case, bigalloc does not
111372732463SMatthew Dillon 	 *	 have to be used.
111482949828SMatthew Dillon 	 */
111582949828SMatthew Dillon 	if (size >= ZoneLimit ||
111682949828SMatthew Dillon 	    ((size & PAGE_MASK) == 0 && size > PAGE_SIZE*2)) {
111782949828SMatthew Dillon 		bigalloc_t big;
111882949828SMatthew Dillon 		bigalloc_t *bigp;
111982949828SMatthew Dillon 
11208120f5e2SMatthew Dillon 		/*
11218120f5e2SMatthew Dillon 		 * Page-align and cache-color in case of virtually indexed
11228120f5e2SMatthew Dillon 		 * physically tagged L1 caches (aka SandyBridge).  No sweat
11238120f5e2SMatthew Dillon 		 * otherwise, so just do it.
112407a8ffeaSMatthew Dillon 		 *
112507a8ffeaSMatthew Dillon 		 * (don't count as excess).
11268120f5e2SMatthew Dillon 		 */
112782949828SMatthew Dillon 		size = (size + PAGE_MASK) & ~(size_t)PAGE_MASK;
1128d0bc7769Szrj 
1129d0bc7769Szrj 		/*
113072732463SMatthew Dillon 		 * If we have overflowed above when rounding to the page
1131d0bc7769Szrj 		 * boundary, something has passed us (size_t)[-PAGE_MASK..-1]
1132d0bc7769Szrj 		 * so just return NULL, size at this point should be >= 0.
1133d0bc7769Szrj 		*/
1134d0bc7769Szrj 		if (size == 0)
1135d0bc7769Szrj 			return (NULL);
1136d0bc7769Szrj 
113707a8ffeaSMatthew Dillon 		if ((size & (PAGE_SIZE * 2 - 1)) == 0)
113807a8ffeaSMatthew Dillon 			size += PAGE_SIZE;
11398120f5e2SMatthew Dillon 
114007a8ffeaSMatthew Dillon 		/*
114107a8ffeaSMatthew Dillon 		 * Try to reuse a cached big block to avoid mmap'ing.  If it
114207a8ffeaSMatthew Dillon 		 * turns out not to fit our requirements we throw it away
114307a8ffeaSMatthew Dillon 		 * and allocate normally.
114407a8ffeaSMatthew Dillon 		 */
114507a8ffeaSMatthew Dillon 		big = NULL;
114607a8ffeaSMatthew Dillon 		if (size <= BIGCACHE_LIMIT) {
114707a8ffeaSMatthew Dillon 			big = bigcache_find_alloc(size);
114807a8ffeaSMatthew Dillon 			if (big && big->bytes < size) {
114907a8ffeaSMatthew Dillon 				_slabfree(big->base, FASTSLABREALLOC, &big);
115007a8ffeaSMatthew Dillon 				big = NULL;
115107a8ffeaSMatthew Dillon 			}
115207a8ffeaSMatthew Dillon 		}
115307a8ffeaSMatthew Dillon 		if (big) {
115407a8ffeaSMatthew Dillon 			chunk = big->base;
115507a8ffeaSMatthew Dillon 			if (flags & SAFLAG_ZERO)
115607a8ffeaSMatthew Dillon 				bzero(chunk, size);
115707a8ffeaSMatthew Dillon 		} else {
115882949828SMatthew Dillon 			chunk = _vmem_alloc(size, PAGE_SIZE, flags);
115982949828SMatthew Dillon 			if (chunk == NULL)
116082949828SMatthew Dillon 				return(NULL);
116182949828SMatthew Dillon 
116282949828SMatthew Dillon 			big = _slaballoc(sizeof(struct bigalloc), 0);
116311e45f67SMatthew Dillon 			if (big == NULL) {
116411e45f67SMatthew Dillon 				_vmem_free(chunk, size);
116511e45f67SMatthew Dillon 				return(NULL);
116611e45f67SMatthew Dillon 			}
116782949828SMatthew Dillon 			big->base = chunk;
116882949828SMatthew Dillon 			big->bytes = size;
116907a8ffeaSMatthew Dillon 		}
117007a8ffeaSMatthew Dillon 		big->active = size;
117107a8ffeaSMatthew Dillon 
117207a8ffeaSMatthew Dillon 		bigp = bigalloc_lock(chunk);
117307a8ffeaSMatthew Dillon 		if (big->active < big->bytes) {
117407a8ffeaSMatthew Dillon 			atomic_add_long(&excess_alloc,
117507a8ffeaSMatthew Dillon 					big->bytes - big->active);
117607a8ffeaSMatthew Dillon 		}
117782949828SMatthew Dillon 		big->next = *bigp;
117882949828SMatthew Dillon 		*bigp = big;
117982949828SMatthew Dillon 		bigalloc_unlock(chunk);
118007a8ffeaSMatthew Dillon 		handle_excess_big();
118182949828SMatthew Dillon 
118282949828SMatthew Dillon 		return(chunk);
118382949828SMatthew Dillon 	}
118482949828SMatthew Dillon 
11850bb7d8c8SVenkatesh Srinivas 	/* Compute allocation zone; zoneindex will panic on excessive sizes */
11860bb7d8c8SVenkatesh Srinivas 	zi = zoneindex(&size, &chunking);
11870bb7d8c8SVenkatesh Srinivas 	MASSERT(zi < NZONES);
11880bb7d8c8SVenkatesh Srinivas 
1189*8b07b5e8SMatthew Dillon 	obj = mtmagazine_alloc(zi, flags);
11900bb7d8c8SVenkatesh Srinivas 	if (obj != NULL) {
11910bb7d8c8SVenkatesh Srinivas 		if (flags & SAFLAG_ZERO)
11920bb7d8c8SVenkatesh Srinivas 			bzero(obj, size);
11930bb7d8c8SVenkatesh Srinivas 		return (obj);
119482949828SMatthew Dillon 	}
119582949828SMatthew Dillon 
119682949828SMatthew Dillon 	/*
1197*8b07b5e8SMatthew Dillon 	 * Attempt to allocate out of an existing global zone.  If all zones
1198*8b07b5e8SMatthew Dillon 	 * are exhausted pull one off the free list or allocate a new one.
119982949828SMatthew Dillon 	 */
1200*8b07b5e8SMatthew Dillon 	slgd = &SLGlobalData;
1201*8b07b5e8SMatthew Dillon 
1202*8b07b5e8SMatthew Dillon again:
1203*8b07b5e8SMatthew Dillon 	if (slgd->ZoneAry[zi] == NULL) {
12040bb7d8c8SVenkatesh Srinivas 		z = zone_alloc(flags);
120582949828SMatthew Dillon 		if (z == NULL)
120682949828SMatthew Dillon 			goto fail;
120782949828SMatthew Dillon 
120882949828SMatthew Dillon 		/*
120982949828SMatthew Dillon 		 * How big is the base structure?
121082949828SMatthew Dillon 		 */
121182949828SMatthew Dillon 		off = sizeof(struct slzone);
121282949828SMatthew Dillon 
121382949828SMatthew Dillon 		/*
121482949828SMatthew Dillon 		 * Align the storage in the zone based on the chunking.
121582949828SMatthew Dillon 		 *
12160bb7d8c8SVenkatesh Srinivas 		 * Guarantee power-of-2 alignment for power-of-2-sized
121782949828SMatthew Dillon 		 * chunks.  Otherwise align based on the chunking size
121882949828SMatthew Dillon 		 * (typically 8 or 16 bytes for small allocations).
121982949828SMatthew Dillon 		 *
122082949828SMatthew Dillon 		 * NOTE: Allocations >= ZoneLimit are governed by the
122182949828SMatthew Dillon 		 * bigalloc code and typically only guarantee page-alignment.
122282949828SMatthew Dillon 		 *
122382949828SMatthew Dillon 		 * Set initial conditions for UIndex near the zone header
122482949828SMatthew Dillon 		 * to reduce unecessary page faults, vs semi-randomization
122582949828SMatthew Dillon 		 * to improve L1 cache saturation.
122672732463SMatthew Dillon 		 *
122772732463SMatthew Dillon 		 * NOTE: Please see posix_memalign around line 864-ish, which
122872732463SMatthew Dillon 		 *	 assumes that power-of-2 allocations of PAGE_SIZE
122972732463SMatthew Dillon 		 *	 and PAGE_SIZE*2 can use _slaballoc() and be aligned
123072732463SMatthew Dillon 		 *	 to the same.  The zone cache can be used for this
123172732463SMatthew Dillon 		 *	 case, bigalloc does not have to be used.
123272732463SMatthew Dillon 		 *
123372732463SMatthew Dillon 		 *	 ALL power-of-2 requests that fall through to this
123472732463SMatthew Dillon 		 *	 code use this rule (conditionals above limit this
123572732463SMatthew Dillon 		 *	 to <= PAGE_SIZE*2.
123682949828SMatthew Dillon 		 */
123782949828SMatthew Dillon 		if ((size | (size - 1)) + 1 == (size << 1))
1238965b839fSSascha Wildner 			off = roundup2(off, size);
123982949828SMatthew Dillon 		else
1240965b839fSSascha Wildner 			off = roundup2(off, chunking);
124182949828SMatthew Dillon 		z->z_Magic = ZALLOC_SLAB_MAGIC;
124282949828SMatthew Dillon 		z->z_ZoneIndex = zi;
124382949828SMatthew Dillon 		z->z_NMax = (ZoneSize - off) / size;
124482949828SMatthew Dillon 		z->z_NFree = z->z_NMax;
124582949828SMatthew Dillon 		z->z_BasePtr = (char *)z + off;
124682949828SMatthew Dillon 		z->z_UIndex = z->z_UEndIndex = 0;
124782949828SMatthew Dillon 		z->z_ChunkSize = size;
124882949828SMatthew Dillon 		z->z_FirstFreePg = ZonePageCount;
124982949828SMatthew Dillon 		if ((z->z_Flags & SLZF_UNOTZEROD) == 0) {
125082949828SMatthew Dillon 			flags &= ~SAFLAG_ZERO;	/* already zero'd */
125182949828SMatthew Dillon 			flags |= SAFLAG_PASSIVE;
125282949828SMatthew Dillon 		}
125382949828SMatthew Dillon 
125482949828SMatthew Dillon 		/*
125582949828SMatthew Dillon 		 * Slide the base index for initial allocations out of the
125682949828SMatthew Dillon 		 * next zone we create so we do not over-weight the lower
125782949828SMatthew Dillon 		 * part of the cpu memory caches.
125882949828SMatthew Dillon 		 */
1259*8b07b5e8SMatthew Dillon 		slgd_lock(slgd);
1260*8b07b5e8SMatthew Dillon 		z->z_Next = slgd->ZoneAry[zi];
1261*8b07b5e8SMatthew Dillon 		slgd->ZoneAry[zi] = z;
126282949828SMatthew Dillon 		slgd->JunkIndex = (slgd->JunkIndex + ZALLOC_SLAB_SLIDE)
126382949828SMatthew Dillon 					& (ZALLOC_MAX_ZONE_SIZE - 1);
1264*8b07b5e8SMatthew Dillon 	} else {
1265*8b07b5e8SMatthew Dillon 		slgd_lock(slgd);
1266*8b07b5e8SMatthew Dillon 		z = slgd->ZoneAry[zi];
1267*8b07b5e8SMatthew Dillon 		if (z == NULL) {
1268*8b07b5e8SMatthew Dillon 			slgd_unlock(slgd);
1269*8b07b5e8SMatthew Dillon 			goto again;
1270*8b07b5e8SMatthew Dillon 		}
127182949828SMatthew Dillon 	}
127282949828SMatthew Dillon 
127382949828SMatthew Dillon 	/*
127482949828SMatthew Dillon 	 * Ok, we have a zone from which at least one chunk is available.
127582949828SMatthew Dillon 	 */
1276721505deSMatthew Dillon 	MASSERT_WTHUNLK(z->z_NFree > 0, slgd_unlock(slgd));
127782949828SMatthew Dillon 
1278*8b07b5e8SMatthew Dillon 	/*
1279*8b07b5e8SMatthew Dillon 	 * Try to cache <count> chunks, up to CACHE_CHUNKS (32 typ)
1280*8b07b5e8SMatthew Dillon 	 * to avoid unnecessary global lock contention.
1281*8b07b5e8SMatthew Dillon 	 */
1282*8b07b5e8SMatthew Dillon 	tp = &thread_mags;
1283*8b07b5e8SMatthew Dillon 	mp = tp->mags[zi].loaded;
1284*8b07b5e8SMatthew Dillon 	count = 0;
1285*8b07b5e8SMatthew Dillon 	if (mp && tp->init >= 0) {
1286*8b07b5e8SMatthew Dillon 		count = mp->capacity - mp->rounds;
1287*8b07b5e8SMatthew Dillon 		if (count >= z->z_NFree)
1288*8b07b5e8SMatthew Dillon 			count = z->z_NFree - 1;
1289*8b07b5e8SMatthew Dillon 		if (count > CACHE_CHUNKS)
1290*8b07b5e8SMatthew Dillon 			count = CACHE_CHUNKS;
129182949828SMatthew Dillon 	}
129282949828SMatthew Dillon 
129382949828SMatthew Dillon 	/*
129482949828SMatthew Dillon 	 * Locate a chunk in a free page.  This attempts to localize
129582949828SMatthew Dillon 	 * reallocations into earlier pages without us having to sort
129682949828SMatthew Dillon 	 * the chunk list.  A chunk may still overlap a page boundary.
129782949828SMatthew Dillon 	 */
129882949828SMatthew Dillon 	while (z->z_FirstFreePg < ZonePageCount) {
129982949828SMatthew Dillon 		if ((chunk = z->z_PageAry[z->z_FirstFreePg]) != NULL) {
1300721505deSMatthew Dillon 			if (((uintptr_t)chunk & ZoneMask) == 0) {
1301721505deSMatthew Dillon 				slgd_unlock(slgd);
1302721505deSMatthew Dillon 				_mpanic("assertion: corrupt malloc zone");
1303721505deSMatthew Dillon 			}
130482949828SMatthew Dillon 			z->z_PageAry[z->z_FirstFreePg] = chunk->c_Next;
1305*8b07b5e8SMatthew Dillon 			--z->z_NFree;
1306*8b07b5e8SMatthew Dillon 
1307*8b07b5e8SMatthew Dillon 			if (count == 0)
130882949828SMatthew Dillon 				goto done;
1309*8b07b5e8SMatthew Dillon 			mp->objects[mp->rounds++] = chunk;
1310*8b07b5e8SMatthew Dillon 			--count;
1311*8b07b5e8SMatthew Dillon 			continue;
131282949828SMatthew Dillon 		}
131382949828SMatthew Dillon 		++z->z_FirstFreePg;
131482949828SMatthew Dillon 	}
131582949828SMatthew Dillon 
131682949828SMatthew Dillon 	/*
131782949828SMatthew Dillon 	 * No chunks are available but NFree said we had some memory,
131882949828SMatthew Dillon 	 * so it must be available in the never-before-used-memory
131982949828SMatthew Dillon 	 * area governed by UIndex.  The consequences are very
132082949828SMatthew Dillon 	 * serious if our zone got corrupted so we use an explicit
132182949828SMatthew Dillon 	 * panic rather then a KASSERT.
132282949828SMatthew Dillon 	 */
1323*8b07b5e8SMatthew Dillon 	for (;;) {
132482949828SMatthew Dillon 		chunk = (slchunk_t)(z->z_BasePtr + z->z_UIndex * size);
1325*8b07b5e8SMatthew Dillon 		--z->z_NFree;
132682949828SMatthew Dillon 		if (++z->z_UIndex == z->z_NMax)
132782949828SMatthew Dillon 			z->z_UIndex = 0;
132882949828SMatthew Dillon 		if (z->z_UIndex == z->z_UEndIndex) {
1329*8b07b5e8SMatthew Dillon 			if (z->z_NFree != 0) {
1330*8b07b5e8SMatthew Dillon 				slgd_unlock(slgd);
133182949828SMatthew Dillon 				_mpanic("slaballoc: corrupted zone");
133282949828SMatthew Dillon 			}
1333*8b07b5e8SMatthew Dillon 		}
1334*8b07b5e8SMatthew Dillon 		if (count == 0)
1335*8b07b5e8SMatthew Dillon 			break;
1336*8b07b5e8SMatthew Dillon 		mp->objects[mp->rounds++] = chunk;
1337*8b07b5e8SMatthew Dillon 		--count;
1338*8b07b5e8SMatthew Dillon 	}
133982949828SMatthew Dillon 
134082949828SMatthew Dillon 	if ((z->z_Flags & SLZF_UNOTZEROD) == 0) {
134182949828SMatthew Dillon 		flags &= ~SAFLAG_ZERO;
134282949828SMatthew Dillon 		flags |= SAFLAG_PASSIVE;
134382949828SMatthew Dillon 	}
134482949828SMatthew Dillon 
134582949828SMatthew Dillon done:
1346*8b07b5e8SMatthew Dillon 	/*
1347*8b07b5e8SMatthew Dillon 	 * Remove us from the ZoneAry[] when we become empty
1348*8b07b5e8SMatthew Dillon 	 */
1349*8b07b5e8SMatthew Dillon 	if (z->z_NFree == 0) {
1350*8b07b5e8SMatthew Dillon 		slgd->ZoneAry[zi] = z->z_Next;
1351*8b07b5e8SMatthew Dillon 		z->z_Next = NULL;
1352*8b07b5e8SMatthew Dillon 	}
135382949828SMatthew Dillon 	slgd_unlock(slgd);
13546aa0e649SSascha Wildner 	if (flags & SAFLAG_ZERO)
135582949828SMatthew Dillon 		bzero(chunk, size);
1356*8b07b5e8SMatthew Dillon 
135782949828SMatthew Dillon 	return(chunk);
135882949828SMatthew Dillon fail:
135982949828SMatthew Dillon 	return(NULL);
136082949828SMatthew Dillon }
136182949828SMatthew Dillon 
136282949828SMatthew Dillon /*
136382949828SMatthew Dillon  * Reallocate memory within the chunk
136482949828SMatthew Dillon  */
136582949828SMatthew Dillon static void *
136682949828SMatthew Dillon _slabrealloc(void *ptr, size_t size)
136782949828SMatthew Dillon {
136882949828SMatthew Dillon 	bigalloc_t *bigp;
136982949828SMatthew Dillon 	void *nptr;
137082949828SMatthew Dillon 	slzone_t z;
137182949828SMatthew Dillon 	size_t chunking;
137282949828SMatthew Dillon 
1373e2caf0e7SMatthew Dillon 	if (ptr == NULL) {
137482949828SMatthew Dillon 		return(_slaballoc(size, 0));
1375ebe0d361SMatthew Dillon 	}
137682949828SMatthew Dillon 
1377e2caf0e7SMatthew Dillon 	if (size == 0)
1378e2caf0e7SMatthew Dillon 		size = 1;
137982949828SMatthew Dillon 
138082949828SMatthew Dillon 	/*
13810bb7d8c8SVenkatesh Srinivas 	 * Handle oversized allocations.
138282949828SMatthew Dillon 	 */
138382949828SMatthew Dillon 	if ((bigp = bigalloc_check_and_lock(ptr)) != NULL) {
138482949828SMatthew Dillon 		bigalloc_t big;
138582949828SMatthew Dillon 		size_t bigbytes;
138682949828SMatthew Dillon 
138782949828SMatthew Dillon 		while ((big = *bigp) != NULL) {
138882949828SMatthew Dillon 			if (big->base == ptr) {
138982949828SMatthew Dillon 				size = (size + PAGE_MASK) & ~(size_t)PAGE_MASK;
139082949828SMatthew Dillon 				bigbytes = big->bytes;
139197f56c04SMatthew Dillon 
139297f56c04SMatthew Dillon 				/*
139397f56c04SMatthew Dillon 				 * If it already fits determine if it makes
139497f56c04SMatthew Dillon 				 * sense to shrink/reallocate.  Try to optimize
139597f56c04SMatthew Dillon 				 * programs which stupidly make incremental
139697f56c04SMatthew Dillon 				 * reallocations larger or smaller by scaling
139797f56c04SMatthew Dillon 				 * the allocation.  Also deal with potential
139897f56c04SMatthew Dillon 				 * coloring.
139997f56c04SMatthew Dillon 				 */
140007a8ffeaSMatthew Dillon 				if (size >= (bigbytes >> 1) &&
140107a8ffeaSMatthew Dillon 				    size <= bigbytes) {
140207a8ffeaSMatthew Dillon 					if (big->active != size) {
140307a8ffeaSMatthew Dillon 						atomic_add_long(&excess_alloc,
140407a8ffeaSMatthew Dillon 								big->active -
140507a8ffeaSMatthew Dillon 								size);
140607a8ffeaSMatthew Dillon 					}
140707a8ffeaSMatthew Dillon 					big->active = size;
140882949828SMatthew Dillon 					bigalloc_unlock(ptr);
140982949828SMatthew Dillon 					return(ptr);
14100bb7d8c8SVenkatesh Srinivas 				}
141197f56c04SMatthew Dillon 
141297f56c04SMatthew Dillon 				/*
141307a8ffeaSMatthew Dillon 				 * For large reallocations, allocate more space
141497f56c04SMatthew Dillon 				 * than we need to try to avoid excessive
141597f56c04SMatthew Dillon 				 * reallocations later on.
141697f56c04SMatthew Dillon 				 */
141707a8ffeaSMatthew Dillon 				chunking = size + (size >> 3);
141807a8ffeaSMatthew Dillon 				chunking = (chunking + PAGE_MASK) &
141997f56c04SMatthew Dillon 					   ~(size_t)PAGE_MASK;
142007a8ffeaSMatthew Dillon 
142107a8ffeaSMatthew Dillon 				/*
142207a8ffeaSMatthew Dillon 				 * Try to allocate adjacently in case the
142307a8ffeaSMatthew Dillon 				 * program is idiotically realloc()ing a
142407a8ffeaSMatthew Dillon 				 * huge memory block just slightly bigger.
142507a8ffeaSMatthew Dillon 				 * (llvm's llc tends to do this a lot).
142607a8ffeaSMatthew Dillon 				 *
142707a8ffeaSMatthew Dillon 				 * (MAP_TRYFIXED forces mmap to fail if there
142807a8ffeaSMatthew Dillon 				 *  is already something at the address).
142907a8ffeaSMatthew Dillon 				 */
143007a8ffeaSMatthew Dillon 				if (chunking > bigbytes) {
143107a8ffeaSMatthew Dillon 					char *addr;
1432e00a0047Szrj 					int errno_save = errno;
143307a8ffeaSMatthew Dillon 
143407a8ffeaSMatthew Dillon 					addr = mmap((char *)ptr + bigbytes,
143507a8ffeaSMatthew Dillon 						    chunking - bigbytes,
143607a8ffeaSMatthew Dillon 						    PROT_READ|PROT_WRITE,
143707a8ffeaSMatthew Dillon 						    MAP_PRIVATE|MAP_ANON|
143807a8ffeaSMatthew Dillon 						    MAP_TRYFIXED,
143907a8ffeaSMatthew Dillon 						    -1, 0);
1440e00a0047Szrj 					errno = errno_save;
144107a8ffeaSMatthew Dillon 					if (addr == (char *)ptr + bigbytes) {
144207a8ffeaSMatthew Dillon 						atomic_add_long(&excess_alloc,
144307a8ffeaSMatthew Dillon 								big->active -
144407a8ffeaSMatthew Dillon 								big->bytes +
144507a8ffeaSMatthew Dillon 								chunking -
144607a8ffeaSMatthew Dillon 								size);
144707a8ffeaSMatthew Dillon 						big->bytes = chunking;
144807a8ffeaSMatthew Dillon 						big->active = size;
144907a8ffeaSMatthew Dillon 						bigalloc_unlock(ptr);
145007a8ffeaSMatthew Dillon 
145107a8ffeaSMatthew Dillon 						return(ptr);
145207a8ffeaSMatthew Dillon 					}
1453721505deSMatthew Dillon 					MASSERT_WTHUNLK(
1454721505deSMatthew Dillon 						(void *)addr == MAP_FAILED,
1455721505deSMatthew Dillon 						bigalloc_unlock(ptr));
145697f56c04SMatthew Dillon 				}
145797f56c04SMatthew Dillon 
145807a8ffeaSMatthew Dillon 				/*
145907a8ffeaSMatthew Dillon 				 * Failed, unlink big and allocate fresh.
146007a8ffeaSMatthew Dillon 				 * (note that we have to leave (big) intact
146107a8ffeaSMatthew Dillon 				 * in case the slaballoc fails).
146207a8ffeaSMatthew Dillon 				 */
14630bb7d8c8SVenkatesh Srinivas 				*bigp = big->next;
14640bb7d8c8SVenkatesh Srinivas 				bigalloc_unlock(ptr);
14650bb7d8c8SVenkatesh Srinivas 				if ((nptr = _slaballoc(size, 0)) == NULL) {
14660bb7d8c8SVenkatesh Srinivas 					/* Relink block */
14670bb7d8c8SVenkatesh Srinivas 					bigp = bigalloc_lock(ptr);
14680bb7d8c8SVenkatesh Srinivas 					big->next = *bigp;
14690bb7d8c8SVenkatesh Srinivas 					*bigp = big;
14700bb7d8c8SVenkatesh Srinivas 					bigalloc_unlock(ptr);
147182949828SMatthew Dillon 					return(NULL);
14720bb7d8c8SVenkatesh Srinivas 				}
147382949828SMatthew Dillon 				if (size > bigbytes)
147482949828SMatthew Dillon 					size = bigbytes;
147582949828SMatthew Dillon 				bcopy(ptr, nptr, size);
147607a8ffeaSMatthew Dillon 				atomic_add_long(&excess_alloc, big->active -
147707a8ffeaSMatthew Dillon 							       big->bytes);
14780bb7d8c8SVenkatesh Srinivas 				_slabfree(ptr, FASTSLABREALLOC, &big);
147907a8ffeaSMatthew Dillon 
148082949828SMatthew Dillon 				return(nptr);
148182949828SMatthew Dillon 			}
148282949828SMatthew Dillon 			bigp = &big->next;
148382949828SMatthew Dillon 		}
148482949828SMatthew Dillon 		bigalloc_unlock(ptr);
148507a8ffeaSMatthew Dillon 		handle_excess_big();
148682949828SMatthew Dillon 	}
148782949828SMatthew Dillon 
148882949828SMatthew Dillon 	/*
148982949828SMatthew Dillon 	 * Get the original allocation's zone.  If the new request winds
149082949828SMatthew Dillon 	 * up using the same chunk size we do not have to do anything.
149182949828SMatthew Dillon 	 *
149282949828SMatthew Dillon 	 * NOTE: We don't have to lock the globaldata here, the fields we
149382949828SMatthew Dillon 	 * access here will not change at least as long as we have control
149482949828SMatthew Dillon 	 * over the allocation.
149582949828SMatthew Dillon 	 */
149682949828SMatthew Dillon 	z = (slzone_t)((uintptr_t)ptr & ~(uintptr_t)ZoneMask);
149782949828SMatthew Dillon 	MASSERT(z->z_Magic == ZALLOC_SLAB_MAGIC);
149882949828SMatthew Dillon 
149982949828SMatthew Dillon 	/*
150082949828SMatthew Dillon 	 * Use zoneindex() to chunk-align the new size, as long as the
150182949828SMatthew Dillon 	 * new size is not too large.
150282949828SMatthew Dillon 	 */
150382949828SMatthew Dillon 	if (size < ZoneLimit) {
150482949828SMatthew Dillon 		zoneindex(&size, &chunking);
1505ebe0d361SMatthew Dillon 		if (z->z_ChunkSize == size) {
150682949828SMatthew Dillon 			return(ptr);
150782949828SMatthew Dillon 		}
1508ebe0d361SMatthew Dillon 	}
150982949828SMatthew Dillon 
151082949828SMatthew Dillon 	/*
151182949828SMatthew Dillon 	 * Allocate memory for the new request size and copy as appropriate.
151282949828SMatthew Dillon 	 */
151382949828SMatthew Dillon 	if ((nptr = _slaballoc(size, 0)) != NULL) {
151482949828SMatthew Dillon 		if (size > z->z_ChunkSize)
151582949828SMatthew Dillon 			size = z->z_ChunkSize;
151682949828SMatthew Dillon 		bcopy(ptr, nptr, size);
15170bb7d8c8SVenkatesh Srinivas 		_slabfree(ptr, 0, NULL);
151882949828SMatthew Dillon 	}
151982949828SMatthew Dillon 
152082949828SMatthew Dillon 	return(nptr);
152182949828SMatthew Dillon }
152282949828SMatthew Dillon 
152382949828SMatthew Dillon /*
152482949828SMatthew Dillon  * free (SLAB ALLOCATOR)
152582949828SMatthew Dillon  *
152682949828SMatthew Dillon  * Free a memory block previously allocated by malloc.  Note that we do not
152782949828SMatthew Dillon  * attempt to uplodate ks_loosememuse as MP races could prevent us from
152882949828SMatthew Dillon  * checking memory limits in malloc.
152982949828SMatthew Dillon  *
15300bb7d8c8SVenkatesh Srinivas  * flags:
15314cd64cfeSMatthew Dillon  *	FASTSLABREALLOC		Fast call from realloc, *rbigp already
15324cd64cfeSMatthew Dillon  *				unlinked.
15334cd64cfeSMatthew Dillon  *
153482949828SMatthew Dillon  * MPSAFE
153582949828SMatthew Dillon  */
153682949828SMatthew Dillon static void
15370bb7d8c8SVenkatesh Srinivas _slabfree(void *ptr, int flags, bigalloc_t *rbigp)
153882949828SMatthew Dillon {
153982949828SMatthew Dillon 	slzone_t z;
154082949828SMatthew Dillon 	slchunk_t chunk;
154182949828SMatthew Dillon 	bigalloc_t big;
154282949828SMatthew Dillon 	bigalloc_t *bigp;
154382949828SMatthew Dillon 	slglobaldata_t slgd;
154482949828SMatthew Dillon 	size_t size;
15450bb7d8c8SVenkatesh Srinivas 	int zi;
154682949828SMatthew Dillon 	int pgno;
154782949828SMatthew Dillon 
15480bb7d8c8SVenkatesh Srinivas 	/* Fast realloc path for big allocations */
15490bb7d8c8SVenkatesh Srinivas 	if (flags & FASTSLABREALLOC) {
15500bb7d8c8SVenkatesh Srinivas 		big = *rbigp;
15510bb7d8c8SVenkatesh Srinivas 		goto fastslabrealloc;
15520bb7d8c8SVenkatesh Srinivas 	}
15530bb7d8c8SVenkatesh Srinivas 
155482949828SMatthew Dillon 	/*
155582949828SMatthew Dillon 	 * Handle NULL frees and special 0-byte allocations
155682949828SMatthew Dillon 	 */
155782949828SMatthew Dillon 	if (ptr == NULL)
155882949828SMatthew Dillon 		return;
155982949828SMatthew Dillon 
156082949828SMatthew Dillon 	/*
156182949828SMatthew Dillon 	 * Handle oversized allocations.
156282949828SMatthew Dillon 	 */
156382949828SMatthew Dillon 	if ((bigp = bigalloc_check_and_lock(ptr)) != NULL) {
156482949828SMatthew Dillon 		while ((big = *bigp) != NULL) {
156582949828SMatthew Dillon 			if (big->base == ptr) {
156682949828SMatthew Dillon 				*bigp = big->next;
156707a8ffeaSMatthew Dillon 				atomic_add_long(&excess_alloc, big->active -
156807a8ffeaSMatthew Dillon 							       big->bytes);
156982949828SMatthew Dillon 				bigalloc_unlock(ptr);
157007a8ffeaSMatthew Dillon 
157107a8ffeaSMatthew Dillon 				/*
157207a8ffeaSMatthew Dillon 				 * Try to stash the block we are freeing,
157307a8ffeaSMatthew Dillon 				 * potentially receiving another block in
157407a8ffeaSMatthew Dillon 				 * return which must be freed.
157507a8ffeaSMatthew Dillon 				 */
15760bb7d8c8SVenkatesh Srinivas fastslabrealloc:
157707a8ffeaSMatthew Dillon 				if (big->bytes <= BIGCACHE_LIMIT) {
157807a8ffeaSMatthew Dillon 					big = bigcache_find_free(big);
157907a8ffeaSMatthew Dillon 					if (big == NULL)
158007a8ffeaSMatthew Dillon 						return;
158107a8ffeaSMatthew Dillon 				}
158207a8ffeaSMatthew Dillon 				ptr = big->base;	/* reload */
158382949828SMatthew Dillon 				size = big->bytes;
15840bb7d8c8SVenkatesh Srinivas 				_slabfree(big, 0, NULL);
158582949828SMatthew Dillon 				_vmem_free(ptr, size);
158682949828SMatthew Dillon 				return;
158782949828SMatthew Dillon 			}
158882949828SMatthew Dillon 			bigp = &big->next;
158982949828SMatthew Dillon 		}
159082949828SMatthew Dillon 		bigalloc_unlock(ptr);
159107a8ffeaSMatthew Dillon 		handle_excess_big();
159282949828SMatthew Dillon 	}
159382949828SMatthew Dillon 
159482949828SMatthew Dillon 	/*
159582949828SMatthew Dillon 	 * Zone case.  Figure out the zone based on the fact that it is
159682949828SMatthew Dillon 	 * ZoneSize aligned.
159782949828SMatthew Dillon 	 */
159882949828SMatthew Dillon 	z = (slzone_t)((uintptr_t)ptr & ~(uintptr_t)ZoneMask);
159982949828SMatthew Dillon 	MASSERT(z->z_Magic == ZALLOC_SLAB_MAGIC);
160082949828SMatthew Dillon 
16010bb7d8c8SVenkatesh Srinivas 	size = z->z_ChunkSize;
16020bb7d8c8SVenkatesh Srinivas 	zi = z->z_ZoneIndex;
16030bb7d8c8SVenkatesh Srinivas 
16040bb7d8c8SVenkatesh Srinivas 	if (g_malloc_flags & SAFLAG_ZERO)
16050bb7d8c8SVenkatesh Srinivas 		bzero(ptr, size);
16060bb7d8c8SVenkatesh Srinivas 
16076c4de62cSMatthew Dillon 	if (mtmagazine_free(zi, ptr) == 0)
16080bb7d8c8SVenkatesh Srinivas 		return;
16090bb7d8c8SVenkatesh Srinivas 
161082949828SMatthew Dillon 	pgno = ((char *)ptr - (char *)z) >> PAGE_SHIFT;
161182949828SMatthew Dillon 	chunk = ptr;
161282949828SMatthew Dillon 
161382949828SMatthew Dillon 	/*
161482949828SMatthew Dillon 	 * Add this free non-zero'd chunk to a linked list for reuse, adjust
161582949828SMatthew Dillon 	 * z_FirstFreePg.
161682949828SMatthew Dillon 	 */
1617*8b07b5e8SMatthew Dillon 	slgd = &SLGlobalData;
1618*8b07b5e8SMatthew Dillon 	slgd_lock(slgd);
1619*8b07b5e8SMatthew Dillon 
162082949828SMatthew Dillon 	chunk->c_Next = z->z_PageAry[pgno];
162182949828SMatthew Dillon 	z->z_PageAry[pgno] = chunk;
162282949828SMatthew Dillon 	if (z->z_FirstFreePg > pgno)
162382949828SMatthew Dillon 		z->z_FirstFreePg = pgno;
162482949828SMatthew Dillon 
162582949828SMatthew Dillon 	/*
162682949828SMatthew Dillon 	 * Bump the number of free chunks.  If it becomes non-zero the zone
162782949828SMatthew Dillon 	 * must be added back onto the appropriate list.
162882949828SMatthew Dillon 	 */
162982949828SMatthew Dillon 	if (z->z_NFree++ == 0) {
163082949828SMatthew Dillon 		z->z_Next = slgd->ZoneAry[z->z_ZoneIndex];
163182949828SMatthew Dillon 		slgd->ZoneAry[z->z_ZoneIndex] = z;
163282949828SMatthew Dillon 	}
163382949828SMatthew Dillon 
163482949828SMatthew Dillon 	/*
1635*8b07b5e8SMatthew Dillon 	 * If the zone becomes totally free we get rid of it.
163682949828SMatthew Dillon 	 */
163782949828SMatthew Dillon 	if (z->z_NFree == z->z_NMax) {
163882949828SMatthew Dillon 		slzone_t *pz;
163982949828SMatthew Dillon 
164082949828SMatthew Dillon 		pz = &slgd->ZoneAry[z->z_ZoneIndex];
164182949828SMatthew Dillon 		while (z != *pz)
164282949828SMatthew Dillon 			pz = &(*pz)->z_Next;
164382949828SMatthew Dillon 		*pz = z->z_Next;
164482949828SMatthew Dillon 		z->z_Magic = -1;
16450bb7d8c8SVenkatesh Srinivas 		z->z_Next = NULL;
164682949828SMatthew Dillon 		slgd_unlock(slgd);
1647*8b07b5e8SMatthew Dillon 		zone_free(z);
1648*8b07b5e8SMatthew Dillon 	} else {
1649*8b07b5e8SMatthew Dillon 		slgd_unlock(slgd);
1650*8b07b5e8SMatthew Dillon 	}
165182949828SMatthew Dillon }
165282949828SMatthew Dillon 
16534cd64cfeSMatthew Dillon /*
16544cd64cfeSMatthew Dillon  * Allocate and return a magazine.  NULL is returned and *burst is adjusted
16554cd64cfeSMatthew Dillon  * if the magazine is empty.
16564cd64cfeSMatthew Dillon  */
16570bb7d8c8SVenkatesh Srinivas static __inline void *
16580bb7d8c8SVenkatesh Srinivas magazine_alloc(struct magazine *mp, int *burst)
16590bb7d8c8SVenkatesh Srinivas {
16604cd64cfeSMatthew Dillon 	void *obj;
16610bb7d8c8SVenkatesh Srinivas 
1662e58e48b4SMatthew Dillon 	if (mp == NULL)
1663e58e48b4SMatthew Dillon 		return(NULL);
16644cd64cfeSMatthew Dillon 	if (MAGAZINE_NOTEMPTY(mp)) {
16650bb7d8c8SVenkatesh Srinivas 		obj = mp->objects[--mp->rounds];
16664cd64cfeSMatthew Dillon 		return(obj);
16670bb7d8c8SVenkatesh Srinivas 	}
16680bb7d8c8SVenkatesh Srinivas 
16694cd64cfeSMatthew Dillon 	/*
16704cd64cfeSMatthew Dillon 	 * Return burst factor to caller along with NULL
16714cd64cfeSMatthew Dillon 	 */
16720bb7d8c8SVenkatesh Srinivas 	if ((mp->flags & M_BURST) && (burst != NULL)) {
16730bb7d8c8SVenkatesh Srinivas 		*burst = mp->burst_factor;
16740bb7d8c8SVenkatesh Srinivas 	}
16750bb7d8c8SVenkatesh Srinivas 	/* Reduce burst factor by NSCALE; if it hits 1, disable BURST */
16760bb7d8c8SVenkatesh Srinivas 	if ((mp->flags & M_BURST) && (mp->flags & M_BURST_EARLY) &&
16770bb7d8c8SVenkatesh Srinivas 	    (burst != NULL)) {
16780bb7d8c8SVenkatesh Srinivas 		mp->burst_factor -= M_BURST_NSCALE;
16790bb7d8c8SVenkatesh Srinivas 		if (mp->burst_factor <= 1) {
16800bb7d8c8SVenkatesh Srinivas 			mp->burst_factor = 1;
16810bb7d8c8SVenkatesh Srinivas 			mp->flags &= ~(M_BURST);
16820bb7d8c8SVenkatesh Srinivas 			mp->flags &= ~(M_BURST_EARLY);
16830bb7d8c8SVenkatesh Srinivas 		}
16840bb7d8c8SVenkatesh Srinivas 	}
16854cd64cfeSMatthew Dillon 	return (NULL);
16860bb7d8c8SVenkatesh Srinivas }
16870bb7d8c8SVenkatesh Srinivas 
16880bb7d8c8SVenkatesh Srinivas static __inline int
16890bb7d8c8SVenkatesh Srinivas magazine_free(struct magazine *mp, void *p)
16900bb7d8c8SVenkatesh Srinivas {
16910bb7d8c8SVenkatesh Srinivas 	if (mp != NULL && MAGAZINE_NOTFULL(mp)) {
16920bb7d8c8SVenkatesh Srinivas 		mp->objects[mp->rounds++] = p;
16930bb7d8c8SVenkatesh Srinivas 		return 0;
16940bb7d8c8SVenkatesh Srinivas 	}
16950bb7d8c8SVenkatesh Srinivas 
16960bb7d8c8SVenkatesh Srinivas 	return -1;
16970bb7d8c8SVenkatesh Srinivas }
16980bb7d8c8SVenkatesh Srinivas 
16990bb7d8c8SVenkatesh Srinivas static void *
1700*8b07b5e8SMatthew Dillon mtmagazine_alloc(int zi, int flags)
17010bb7d8c8SVenkatesh Srinivas {
17020bb7d8c8SVenkatesh Srinivas 	thr_mags *tp;
17030bb7d8c8SVenkatesh Srinivas 	struct magazine *mp, *emptymag;
17040bb7d8c8SVenkatesh Srinivas 	magazine_depot *d;
1705e58e48b4SMatthew Dillon 	void *obj;
17060bb7d8c8SVenkatesh Srinivas 
17076c4de62cSMatthew Dillon 	/*
17086c4de62cSMatthew Dillon 	 * Do not try to access per-thread magazines while the mtmagazine
17096c4de62cSMatthew Dillon 	 * is being initialized or destroyed.
17106c4de62cSMatthew Dillon 	 */
17110bb7d8c8SVenkatesh Srinivas 	tp = &thread_mags;
17126c4de62cSMatthew Dillon 	if (tp->init < 0)
17136c4de62cSMatthew Dillon 		return(NULL);
17140bb7d8c8SVenkatesh Srinivas 
17156c4de62cSMatthew Dillon 	/*
17166c4de62cSMatthew Dillon 	 * Primary per-thread allocation loop
17176c4de62cSMatthew Dillon 	 */
1718721505deSMatthew Dillon 	nmalloc_sigblockall();
17197b033ca7SVenkatesh Srinivas 	for (;;) {
1720e58e48b4SMatthew Dillon 		/*
1721*8b07b5e8SMatthew Dillon 		 * Make sure we have a magazine available for use.
1722*8b07b5e8SMatthew Dillon 		 */
1723*8b07b5e8SMatthew Dillon 		if (tp->newmag == NULL && (flags & SAFLAG_MAGS) == 0) {
1724*8b07b5e8SMatthew Dillon 			mp = _slaballoc(sizeof(struct magazine),
1725*8b07b5e8SMatthew Dillon 					SAFLAG_ZERO | SAFLAG_MAGS);
1726*8b07b5e8SMatthew Dillon 			if (mp == NULL) {
1727*8b07b5e8SMatthew Dillon 				obj = NULL;
1728*8b07b5e8SMatthew Dillon 				break;
1729*8b07b5e8SMatthew Dillon 			}
1730*8b07b5e8SMatthew Dillon 			if (tp->newmag) {
1731*8b07b5e8SMatthew Dillon 				_slabfree(mp, 0, NULL);
1732*8b07b5e8SMatthew Dillon 			} else {
1733*8b07b5e8SMatthew Dillon 				tp->newmag = mp;
1734*8b07b5e8SMatthew Dillon 			}
1735*8b07b5e8SMatthew Dillon 		}
1736*8b07b5e8SMatthew Dillon 
1737*8b07b5e8SMatthew Dillon 		/*
1738e58e48b4SMatthew Dillon 		 * If the loaded magazine has rounds, allocate and return
1739e58e48b4SMatthew Dillon 		 */
1740e58e48b4SMatthew Dillon 		mp = tp->mags[zi].loaded;
17410bb7d8c8SVenkatesh Srinivas 		obj = magazine_alloc(mp, NULL);
1742e58e48b4SMatthew Dillon 		if (obj)
17430bb7d8c8SVenkatesh Srinivas 			break;
17440bb7d8c8SVenkatesh Srinivas 
1745e58e48b4SMatthew Dillon 		/*
1746*8b07b5e8SMatthew Dillon 		 * The prev magazine can only be completely empty or completely
1747*8b07b5e8SMatthew Dillon 		 * full.  If it is full, swap it with the loaded magazine
1748*8b07b5e8SMatthew Dillon 		 * and retry.
1749e58e48b4SMatthew Dillon 		 */
1750e58e48b4SMatthew Dillon 		mp = tp->mags[zi].prev;
1751e58e48b4SMatthew Dillon 		if (mp && MAGAZINE_FULL(mp)) {
1752e58e48b4SMatthew Dillon 			MASSERT(mp->rounds != 0);
1753443d9a50SMatthew Dillon 			swap_mags(&tp->mags[zi]);	/* prev now empty */
17540bb7d8c8SVenkatesh Srinivas 			continue;
17550bb7d8c8SVenkatesh Srinivas 		}
17560bb7d8c8SVenkatesh Srinivas 
17574cd64cfeSMatthew Dillon 		/*
1758*8b07b5e8SMatthew Dillon 		 * If the depot has no loaded magazines ensure that tp->loaded
1759*8b07b5e8SMatthew Dillon 		 * is not NULL and return NULL.  This will allow _slaballoc()
1760*8b07b5e8SMatthew Dillon 		 * to cache referals to SLGlobalData in a magazine.
17614cd64cfeSMatthew Dillon 		 */
17620bb7d8c8SVenkatesh Srinivas 		d = &depots[zi];
1763*8b07b5e8SMatthew Dillon 		if (SLIST_EMPTY(&d->full)) {	/* UNLOCKED TEST IS SAFE */
1764*8b07b5e8SMatthew Dillon 			mp = tp->mags[zi].loaded;
1765*8b07b5e8SMatthew Dillon 			if (mp == NULL && tp->newmag) {
1766*8b07b5e8SMatthew Dillon 				mp = tp->newmag;
1767*8b07b5e8SMatthew Dillon 				tp->newmag = NULL;
1768*8b07b5e8SMatthew Dillon 				mp->capacity = M_MAX_ROUNDS;
1769*8b07b5e8SMatthew Dillon 				mp->rounds = 0;
1770*8b07b5e8SMatthew Dillon 				mp->flags = 0;
1771e58e48b4SMatthew Dillon 				tp->mags[zi].loaded = mp;
1772*8b07b5e8SMatthew Dillon 			}
1773*8b07b5e8SMatthew Dillon 			break;
1774*8b07b5e8SMatthew Dillon 		}
1775*8b07b5e8SMatthew Dillon 
1776*8b07b5e8SMatthew Dillon 		/*
1777*8b07b5e8SMatthew Dillon 		 * Cycle: depot(loaded) -> loaded -> prev -> depot(empty)
1778*8b07b5e8SMatthew Dillon 		 *
1779*8b07b5e8SMatthew Dillon 		 * If we race and the depot has no full magazines, retry.
1780*8b07b5e8SMatthew Dillon 		 */
1781*8b07b5e8SMatthew Dillon 		depot_lock(d);
1782*8b07b5e8SMatthew Dillon 		mp = SLIST_FIRST(&d->full);
1783e58e48b4SMatthew Dillon 		if (mp) {
17840bb7d8c8SVenkatesh Srinivas 			SLIST_REMOVE_HEAD(&d->full, nextmagazine);
1785*8b07b5e8SMatthew Dillon 			emptymag = tp->mags[zi].prev;
1786*8b07b5e8SMatthew Dillon 			if (emptymag) {
1787*8b07b5e8SMatthew Dillon 				SLIST_INSERT_HEAD(&d->empty, emptymag,
1788*8b07b5e8SMatthew Dillon 						  nextmagazine);
1789*8b07b5e8SMatthew Dillon 			}
1790*8b07b5e8SMatthew Dillon 			tp->mags[zi].prev = tp->mags[zi].loaded;
1791*8b07b5e8SMatthew Dillon 			tp->mags[zi].loaded = mp;
1792721505deSMatthew Dillon 			MASSERT(MAGAZINE_NOTEMPTY(mp));
17930bb7d8c8SVenkatesh Srinivas 		}
17944cd64cfeSMatthew Dillon 		depot_unlock(d);
1795*8b07b5e8SMatthew Dillon 		continue;
17967b033ca7SVenkatesh Srinivas 	}
1797721505deSMatthew Dillon 	nmalloc_sigunblockall();
17980bb7d8c8SVenkatesh Srinivas 
17990bb7d8c8SVenkatesh Srinivas 	return (obj);
18000bb7d8c8SVenkatesh Srinivas }
18010bb7d8c8SVenkatesh Srinivas 
18020bb7d8c8SVenkatesh Srinivas static int
18030bb7d8c8SVenkatesh Srinivas mtmagazine_free(int zi, void *ptr)
18040bb7d8c8SVenkatesh Srinivas {
18050bb7d8c8SVenkatesh Srinivas 	thr_mags *tp;
1806e58e48b4SMatthew Dillon 	struct magazine *mp, *loadedmag;
18070bb7d8c8SVenkatesh Srinivas 	magazine_depot *d;
18080bb7d8c8SVenkatesh Srinivas 	int rc = -1;
18090bb7d8c8SVenkatesh Srinivas 
18106c4de62cSMatthew Dillon 	/*
18116c4de62cSMatthew Dillon 	 * Do not try to access per-thread magazines while the mtmagazine
18126c4de62cSMatthew Dillon 	 * is being initialized or destroyed.
18136c4de62cSMatthew Dillon 	 */
18140bb7d8c8SVenkatesh Srinivas 	tp = &thread_mags;
18156c4de62cSMatthew Dillon 	if (tp->init < 0)
18166c4de62cSMatthew Dillon 		return(-1);
18170bb7d8c8SVenkatesh Srinivas 
18186c4de62cSMatthew Dillon 	/*
18196c4de62cSMatthew Dillon 	 * Primary per-thread freeing loop
18206c4de62cSMatthew Dillon 	 */
1821721505deSMatthew Dillon 	nmalloc_sigblockall();
18227b033ca7SVenkatesh Srinivas 	for (;;) {
1823e58e48b4SMatthew Dillon 		/*
1824443d9a50SMatthew Dillon 		 * Make sure a new magazine is available in case we have
1825443d9a50SMatthew Dillon 		 * to use it.  Staging the newmag allows us to avoid
1826443d9a50SMatthew Dillon 		 * some locking/reentrancy complexity.
1827443d9a50SMatthew Dillon 		 *
1828443d9a50SMatthew Dillon 		 * Temporarily disable the per-thread caches for this
1829443d9a50SMatthew Dillon 		 * allocation to avoid reentrancy and/or to avoid a
1830443d9a50SMatthew Dillon 		 * stack overflow if the [zi] happens to be the same that
1831443d9a50SMatthew Dillon 		 * would be used to allocate the new magazine.
1832443d9a50SMatthew Dillon 		 */
1833443d9a50SMatthew Dillon 		if (tp->newmag == NULL) {
1834443d9a50SMatthew Dillon 			tp->newmag = _slaballoc(sizeof(struct magazine),
1835443d9a50SMatthew Dillon 						SAFLAG_ZERO);
1836443d9a50SMatthew Dillon 			if (tp->newmag == NULL) {
1837443d9a50SMatthew Dillon 				rc = -1;
1838443d9a50SMatthew Dillon 				break;
1839443d9a50SMatthew Dillon 			}
1840443d9a50SMatthew Dillon 		}
1841443d9a50SMatthew Dillon 
1842443d9a50SMatthew Dillon 		/*
1843e58e48b4SMatthew Dillon 		 * If the loaded magazine has space, free directly to it
1844e58e48b4SMatthew Dillon 		 */
1845e58e48b4SMatthew Dillon 		rc = magazine_free(tp->mags[zi].loaded, ptr);
1846e58e48b4SMatthew Dillon 		if (rc == 0)
18470bb7d8c8SVenkatesh Srinivas 			break;
18480bb7d8c8SVenkatesh Srinivas 
1849e58e48b4SMatthew Dillon 		/*
1850*8b07b5e8SMatthew Dillon 		 * The prev magazine can only be completely empty or completely
1851*8b07b5e8SMatthew Dillon 		 * full.  If it is empty, swap it with the loaded magazine
1852*8b07b5e8SMatthew Dillon 		 * and retry.
1853e58e48b4SMatthew Dillon 		 */
1854e58e48b4SMatthew Dillon 		mp = tp->mags[zi].prev;
1855e58e48b4SMatthew Dillon 		if (mp && MAGAZINE_EMPTY(mp)) {
1856e58e48b4SMatthew Dillon 			MASSERT(mp->rounds == 0);
1857443d9a50SMatthew Dillon 			swap_mags(&tp->mags[zi]);	/* prev now full */
18580bb7d8c8SVenkatesh Srinivas 			continue;
18590bb7d8c8SVenkatesh Srinivas 		}
18600bb7d8c8SVenkatesh Srinivas 
1861e58e48b4SMatthew Dillon 		/*
1862e58e48b4SMatthew Dillon 		 * Try to get an empty magazine from the depot.  Cycle
1863e58e48b4SMatthew Dillon 		 * through depot(empty)->loaded->prev->depot(full).
1864e58e48b4SMatthew Dillon 		 * Retry if an empty magazine was available from the depot.
18654cd64cfeSMatthew Dillon 		 */
1866e58e48b4SMatthew Dillon 		d = &depots[zi];
18674cd64cfeSMatthew Dillon 		depot_lock(d);
1868e58e48b4SMatthew Dillon 
1869e58e48b4SMatthew Dillon 		if ((loadedmag = tp->mags[zi].prev) != NULL)
1870e58e48b4SMatthew Dillon 			SLIST_INSERT_HEAD(&d->full, loadedmag, nextmagazine);
1871e58e48b4SMatthew Dillon 		tp->mags[zi].prev = tp->mags[zi].loaded;
1872e58e48b4SMatthew Dillon 		mp = SLIST_FIRST(&d->empty);
1873e58e48b4SMatthew Dillon 		if (mp) {
1874e58e48b4SMatthew Dillon 			tp->mags[zi].loaded = mp;
1875e58e48b4SMatthew Dillon 			SLIST_REMOVE_HEAD(&d->empty, nextmagazine);
1876721505deSMatthew Dillon 			depot_unlock(d);
1877e58e48b4SMatthew Dillon 			MASSERT(MAGAZINE_NOTFULL(mp));
1878e58e48b4SMatthew Dillon 		} else {
1879e58e48b4SMatthew Dillon 			mp = tp->newmag;
1880e58e48b4SMatthew Dillon 			tp->newmag = NULL;
1881e58e48b4SMatthew Dillon 			mp->capacity = M_MAX_ROUNDS;
1882e58e48b4SMatthew Dillon 			mp->rounds = 0;
1883e58e48b4SMatthew Dillon 			mp->flags = 0;
1884e58e48b4SMatthew Dillon 			tp->mags[zi].loaded = mp;
1885e58e48b4SMatthew Dillon 			depot_unlock(d);
18867b033ca7SVenkatesh Srinivas 		}
1887721505deSMatthew Dillon 	}
1888721505deSMatthew Dillon 	nmalloc_sigunblockall();
18890bb7d8c8SVenkatesh Srinivas 
18900bb7d8c8SVenkatesh Srinivas 	return rc;
18910bb7d8c8SVenkatesh Srinivas }
18920bb7d8c8SVenkatesh Srinivas 
18930bb7d8c8SVenkatesh Srinivas static void
18946c4de62cSMatthew Dillon mtmagazine_init(void)
18956c4de62cSMatthew Dillon {
18966c4de62cSMatthew Dillon 	int error;
18976c4de62cSMatthew Dillon 
1898d19ab22dSSascha Wildner 	error = _pthread_key_create(&thread_mags_key, mtmagazine_destructor);
18996c4de62cSMatthew Dillon 	if (error)
19000bb7d8c8SVenkatesh Srinivas 		abort();
19010bb7d8c8SVenkatesh Srinivas }
19020bb7d8c8SVenkatesh Srinivas 
19036c4de62cSMatthew Dillon /*
19046c4de62cSMatthew Dillon  * This function is only used by the thread exit destructor
19056c4de62cSMatthew Dillon  */
19060bb7d8c8SVenkatesh Srinivas static void
19070bb7d8c8SVenkatesh Srinivas mtmagazine_drain(struct magazine *mp)
19080bb7d8c8SVenkatesh Srinivas {
19090bb7d8c8SVenkatesh Srinivas 	void *obj;
19100bb7d8c8SVenkatesh Srinivas 
19110bb7d8c8SVenkatesh Srinivas 	while (MAGAZINE_NOTEMPTY(mp)) {
19120bb7d8c8SVenkatesh Srinivas 		obj = magazine_alloc(mp, NULL);
19136c4de62cSMatthew Dillon 		_slabfree(obj, 0, NULL);
19140bb7d8c8SVenkatesh Srinivas 	}
19150bb7d8c8SVenkatesh Srinivas }
19160bb7d8c8SVenkatesh Srinivas 
19170bb7d8c8SVenkatesh Srinivas /*
19180bb7d8c8SVenkatesh Srinivas  * mtmagazine_destructor()
19190bb7d8c8SVenkatesh Srinivas  *
19200bb7d8c8SVenkatesh Srinivas  * When a thread exits, we reclaim all its resources; all its magazines are
19210bb7d8c8SVenkatesh Srinivas  * drained and the structures are freed.
19226c4de62cSMatthew Dillon  *
19236c4de62cSMatthew Dillon  * WARNING!  The destructor can be called multiple times if the larger user
19246c4de62cSMatthew Dillon  *	     program has its own destructors which run after ours which
19256c4de62cSMatthew Dillon  *	     allocate or free memory.
19260bb7d8c8SVenkatesh Srinivas  */
19270bb7d8c8SVenkatesh Srinivas static void
19280bb7d8c8SVenkatesh Srinivas mtmagazine_destructor(void *thrp)
19290bb7d8c8SVenkatesh Srinivas {
19300bb7d8c8SVenkatesh Srinivas 	thr_mags *tp = thrp;
19310bb7d8c8SVenkatesh Srinivas 	struct magazine *mp;
19320bb7d8c8SVenkatesh Srinivas 	int i;
19330bb7d8c8SVenkatesh Srinivas 
1934*8b07b5e8SMatthew Dillon 	if (__isexiting)
1935*8b07b5e8SMatthew Dillon 		return;
1936*8b07b5e8SMatthew Dillon 
19376c4de62cSMatthew Dillon 	/*
19386c4de62cSMatthew Dillon 	 * Prevent further use of mtmagazines while we are destructing
19396c4de62cSMatthew Dillon 	 * them, as well as for any destructors which are run after us
19406c4de62cSMatthew Dillon 	 * prior to the thread actually being destroyed.
19416c4de62cSMatthew Dillon 	 */
19426c4de62cSMatthew Dillon 	tp->init = -1;
19436c4de62cSMatthew Dillon 
19440bb7d8c8SVenkatesh Srinivas 	for (i = 0; i < NZONES; i++) {
19450bb7d8c8SVenkatesh Srinivas 		mp = tp->mags[i].loaded;
19466c4de62cSMatthew Dillon 		tp->mags[i].loaded = NULL;
1947e58e48b4SMatthew Dillon 		if (mp) {
1948e58e48b4SMatthew Dillon 			if (MAGAZINE_NOTEMPTY(mp))
19490bb7d8c8SVenkatesh Srinivas 				mtmagazine_drain(mp);
19506c4de62cSMatthew Dillon 			_slabfree(mp, 0, NULL);
1951e58e48b4SMatthew Dillon 		}
19520bb7d8c8SVenkatesh Srinivas 
19530bb7d8c8SVenkatesh Srinivas 		mp = tp->mags[i].prev;
19546c4de62cSMatthew Dillon 		tp->mags[i].prev = NULL;
1955e58e48b4SMatthew Dillon 		if (mp) {
1956e58e48b4SMatthew Dillon 			if (MAGAZINE_NOTEMPTY(mp))
19570bb7d8c8SVenkatesh Srinivas 				mtmagazine_drain(mp);
19586c4de62cSMatthew Dillon 			_slabfree(mp, 0, NULL);
19590bb7d8c8SVenkatesh Srinivas 		}
19600bb7d8c8SVenkatesh Srinivas 	}
19610bb7d8c8SVenkatesh Srinivas 
1962e58e48b4SMatthew Dillon 	if (tp->newmag) {
1963e58e48b4SMatthew Dillon 		mp = tp->newmag;
1964e58e48b4SMatthew Dillon 		tp->newmag = NULL;
1965e58e48b4SMatthew Dillon 		_slabfree(mp, 0, NULL);
1966e58e48b4SMatthew Dillon 	}
1967e58e48b4SMatthew Dillon }
1968e58e48b4SMatthew Dillon 
19690bb7d8c8SVenkatesh Srinivas /*
19700bb7d8c8SVenkatesh Srinivas  * zone_alloc()
19710bb7d8c8SVenkatesh Srinivas  *
19720bb7d8c8SVenkatesh Srinivas  * Attempt to allocate a zone from the zone magazine; the zone magazine has
19730bb7d8c8SVenkatesh Srinivas  * M_BURST_EARLY enabled, so honor the burst request from the magazine.
19740bb7d8c8SVenkatesh Srinivas  */
19750bb7d8c8SVenkatesh Srinivas static slzone_t
19760bb7d8c8SVenkatesh Srinivas zone_alloc(int flags)
19770bb7d8c8SVenkatesh Srinivas {
19780bb7d8c8SVenkatesh Srinivas 	int burst = 1;
19790bb7d8c8SVenkatesh Srinivas 	int i, j;
19800bb7d8c8SVenkatesh Srinivas 	slzone_t z;
19810bb7d8c8SVenkatesh Srinivas 
19820bb7d8c8SVenkatesh Srinivas 	zone_magazine_lock();
19830bb7d8c8SVenkatesh Srinivas 
19840bb7d8c8SVenkatesh Srinivas 	z = magazine_alloc(&zone_magazine, &burst);
19854cd64cfeSMatthew Dillon 	if (z == NULL && burst == 1) {
19860bb7d8c8SVenkatesh Srinivas 		zone_magazine_unlock();
19870bb7d8c8SVenkatesh Srinivas 		z = _vmem_alloc(ZoneSize * burst, ZoneSize, flags);
19884cd64cfeSMatthew Dillon 	} else if (z == NULL) {
19894cd64cfeSMatthew Dillon 		z = _vmem_alloc(ZoneSize * burst, ZoneSize, flags);
19904cd64cfeSMatthew Dillon 		if (z) {
19910bb7d8c8SVenkatesh Srinivas 			for (i = 1; i < burst; i++) {
19920bb7d8c8SVenkatesh Srinivas 				j = magazine_free(&zone_magazine,
19930bb7d8c8SVenkatesh Srinivas 						  (char *) z + (ZoneSize * i));
1994721505deSMatthew Dillon 				MASSERT_WTHUNLK(j == 0, zone_magazine_unlock());
19950bb7d8c8SVenkatesh Srinivas 			}
19964cd64cfeSMatthew Dillon 		}
19970bb7d8c8SVenkatesh Srinivas 		zone_magazine_unlock();
19980bb7d8c8SVenkatesh Srinivas 	} else {
19990bb7d8c8SVenkatesh Srinivas 		z->z_Flags |= SLZF_UNOTZEROD;
20000bb7d8c8SVenkatesh Srinivas 		zone_magazine_unlock();
20010bb7d8c8SVenkatesh Srinivas 	}
2002*8b07b5e8SMatthew Dillon 
20030bb7d8c8SVenkatesh Srinivas 	return z;
20040bb7d8c8SVenkatesh Srinivas }
20050bb7d8c8SVenkatesh Srinivas 
20060bb7d8c8SVenkatesh Srinivas /*
2007*8b07b5e8SMatthew Dillon  * Free a zone.
20080bb7d8c8SVenkatesh Srinivas  */
20090bb7d8c8SVenkatesh Srinivas static void
20100bb7d8c8SVenkatesh Srinivas zone_free(void *z)
20110bb7d8c8SVenkatesh Srinivas {
20120bb7d8c8SVenkatesh Srinivas 	void *excess[M_ZONE_ROUNDS - M_LOW_ROUNDS] = {};
20130bb7d8c8SVenkatesh Srinivas 	int i, j;
20140bb7d8c8SVenkatesh Srinivas 
20150bb7d8c8SVenkatesh Srinivas 	zone_magazine_lock();
20160bb7d8c8SVenkatesh Srinivas 
20170bb7d8c8SVenkatesh Srinivas 	bzero(z, sizeof(struct slzone));
20180bb7d8c8SVenkatesh Srinivas 
20190bb7d8c8SVenkatesh Srinivas 	if (opt_madvise)
20200bb7d8c8SVenkatesh Srinivas 		madvise(z, ZoneSize, MADV_FREE);
20210bb7d8c8SVenkatesh Srinivas 
20220bb7d8c8SVenkatesh Srinivas 	i = magazine_free(&zone_magazine, z);
20230bb7d8c8SVenkatesh Srinivas 
20244cd64cfeSMatthew Dillon 	/*
20254cd64cfeSMatthew Dillon 	 * If we failed to free, collect excess magazines; release the zone
20260bb7d8c8SVenkatesh Srinivas 	 * magazine lock, and then free to the system via _vmem_free. Re-enable
20274cd64cfeSMatthew Dillon 	 * BURST mode for the magazine.
20284cd64cfeSMatthew Dillon 	 */
20290bb7d8c8SVenkatesh Srinivas 	if (i == -1) {
20300bb7d8c8SVenkatesh Srinivas 		j = zone_magazine.rounds - zone_magazine.low_factor;
20310bb7d8c8SVenkatesh Srinivas 		for (i = 0; i < j; i++) {
20320bb7d8c8SVenkatesh Srinivas 			excess[i] = magazine_alloc(&zone_magazine, NULL);
2033721505deSMatthew Dillon 			MASSERT_WTHUNLK(excess[i] !=  NULL,
2034721505deSMatthew Dillon 					zone_magazine_unlock());
20350bb7d8c8SVenkatesh Srinivas 		}
20360bb7d8c8SVenkatesh Srinivas 
20370bb7d8c8SVenkatesh Srinivas 		zone_magazine_unlock();
20380bb7d8c8SVenkatesh Srinivas 
20390bb7d8c8SVenkatesh Srinivas 		for (i = 0; i < j; i++)
20400bb7d8c8SVenkatesh Srinivas 			_vmem_free(excess[i], ZoneSize);
20410bb7d8c8SVenkatesh Srinivas 
20420bb7d8c8SVenkatesh Srinivas 		_vmem_free(z, ZoneSize);
20430bb7d8c8SVenkatesh Srinivas 	} else {
20440bb7d8c8SVenkatesh Srinivas 		zone_magazine_unlock();
20450bb7d8c8SVenkatesh Srinivas 	}
20460bb7d8c8SVenkatesh Srinivas }
20470bb7d8c8SVenkatesh Srinivas 
204882949828SMatthew Dillon /*
204982949828SMatthew Dillon  * _vmem_alloc()
205082949828SMatthew Dillon  *
205182949828SMatthew Dillon  *	Directly map memory in PAGE_SIZE'd chunks with the specified
205282949828SMatthew Dillon  *	alignment.
205382949828SMatthew Dillon  *
205482949828SMatthew Dillon  *	Alignment must be a multiple of PAGE_SIZE.
205511e45f67SMatthew Dillon  *
205611e45f67SMatthew Dillon  *	Size must be >= alignment.
205782949828SMatthew Dillon  */
205882949828SMatthew Dillon static void *
205982949828SMatthew Dillon _vmem_alloc(size_t size, size_t align, int flags)
206082949828SMatthew Dillon {
2061721505deSMatthew Dillon 	static char *addr_hint;
2062721505deSMatthew Dillon 	static int reset_hint = 16;
206382949828SMatthew Dillon 	char *addr;
206482949828SMatthew Dillon 	char *save;
2065721505deSMatthew Dillon 
2066721505deSMatthew Dillon 	if (--reset_hint <= 0) {
2067721505deSMatthew Dillon 		addr_hint = NULL;
2068721505deSMatthew Dillon 		reset_hint = 16;
2069721505deSMatthew Dillon 	}
207082949828SMatthew Dillon 
207182949828SMatthew Dillon 	/*
207282949828SMatthew Dillon 	 * Map anonymous private memory.
207382949828SMatthew Dillon 	 */
2074721505deSMatthew Dillon 	save = mmap(addr_hint, size, PROT_READ|PROT_WRITE,
207582949828SMatthew Dillon 		    MAP_PRIVATE|MAP_ANON, -1, 0);
2076721505deSMatthew Dillon 	if (save == MAP_FAILED)
2077721505deSMatthew Dillon 		goto worst_case;
2078721505deSMatthew Dillon 	if (((uintptr_t)save & (align - 1)) == 0)
2079721505deSMatthew Dillon 		return((void *)save);
208082949828SMatthew Dillon 
2081721505deSMatthew Dillon 	addr_hint = (char *)(((size_t)save + (align - 1)) & ~(align - 1));
2082721505deSMatthew Dillon 	munmap(save, size);
208311e45f67SMatthew Dillon 
2084721505deSMatthew Dillon 	save = mmap(addr_hint, size, PROT_READ|PROT_WRITE,
2085721505deSMatthew Dillon 		    MAP_PRIVATE|MAP_ANON, -1, 0);
2086721505deSMatthew Dillon 	if (save == MAP_FAILED)
2087721505deSMatthew Dillon 		goto worst_case;
2088721505deSMatthew Dillon 	if (((size_t)save & (align - 1)) == 0)
2089721505deSMatthew Dillon 		return((void *)save);
2090721505deSMatthew Dillon 	munmap(save, size);
209111e45f67SMatthew Dillon 
2092721505deSMatthew Dillon worst_case:
2093721505deSMatthew Dillon 	save = mmap(NULL, size + align, PROT_READ|PROT_WRITE,
2094721505deSMatthew Dillon 		    MAP_PRIVATE|MAP_ANON, -1, 0);
2095721505deSMatthew Dillon 	if (save == MAP_FAILED)
2096721505deSMatthew Dillon 		return NULL;
2097721505deSMatthew Dillon 
2098721505deSMatthew Dillon 	addr = (char *)(((size_t)save + (align - 1)) & ~(align - 1));
2099721505deSMatthew Dillon 	if (save != addr)
2100721505deSMatthew Dillon 		munmap(save, addr - save);
2101721505deSMatthew Dillon 	if (addr + size != save + size + align)
2102721505deSMatthew Dillon 		munmap(addr + size, save + align - addr);
2103721505deSMatthew Dillon 
2104721505deSMatthew Dillon 	addr_hint = addr + size;
2105721505deSMatthew Dillon 
210682949828SMatthew Dillon 	return ((void *)addr);
210782949828SMatthew Dillon }
210882949828SMatthew Dillon 
210982949828SMatthew Dillon /*
211082949828SMatthew Dillon  * _vmem_free()
211182949828SMatthew Dillon  *
211282949828SMatthew Dillon  *	Free a chunk of memory allocated with _vmem_alloc()
211382949828SMatthew Dillon  */
211482949828SMatthew Dillon static void
2115b435182dSSimon Schubert _vmem_free(void *ptr, size_t size)
211682949828SMatthew Dillon {
211782949828SMatthew Dillon 	munmap(ptr, size);
211882949828SMatthew Dillon }
211982949828SMatthew Dillon 
212082949828SMatthew Dillon /*
212182949828SMatthew Dillon  * Panic on fatal conditions
212282949828SMatthew Dillon  */
212382949828SMatthew Dillon static void
212482949828SMatthew Dillon _mpanic(const char *ctl, ...)
212582949828SMatthew Dillon {
212682949828SMatthew Dillon 	va_list va;
212782949828SMatthew Dillon 
212882949828SMatthew Dillon 	if (malloc_panic == 0) {
212982949828SMatthew Dillon 		malloc_panic = 1;
213082949828SMatthew Dillon 		va_start(va, ctl);
213182949828SMatthew Dillon 		vfprintf(stderr, ctl, va);
213282949828SMatthew Dillon 		fprintf(stderr, "\n");
213382949828SMatthew Dillon 		fflush(stderr);
213482949828SMatthew Dillon 		va_end(va);
213582949828SMatthew Dillon 	}
213682949828SMatthew Dillon 	abort();
213782949828SMatthew Dillon }
213869baab3bSImre Vadász 
2139d3a54aeeSzrj __weak_reference(__aligned_alloc, aligned_alloc);
214069baab3bSImre Vadász __weak_reference(__malloc, malloc);
214169baab3bSImre Vadász __weak_reference(__calloc, calloc);
214269baab3bSImre Vadász __weak_reference(__posix_memalign, posix_memalign);
214369baab3bSImre Vadász __weak_reference(__realloc, realloc);
214469baab3bSImre Vadász __weak_reference(__free, free);
2145