xref: /minix3/lib/libc/stdlib/jemalloc.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: jemalloc.c,v 1.38 2015/07/26 17:21:55 martin Exp $	*/
22fe8fb19SBen Gras 
32fe8fb19SBen Gras /*-
42fe8fb19SBen Gras  * Copyright (C) 2006,2007 Jason Evans <jasone@FreeBSD.org>.
52fe8fb19SBen Gras  * All rights reserved.
62fe8fb19SBen Gras  *
72fe8fb19SBen Gras  * Redistribution and use in source and binary forms, with or without
82fe8fb19SBen Gras  * modification, are permitted provided that the following conditions
92fe8fb19SBen Gras  * are met:
102fe8fb19SBen Gras  * 1. Redistributions of source code must retain the above copyright
112fe8fb19SBen Gras  *    notice(s), this list of conditions and the following disclaimer as
122fe8fb19SBen Gras  *    the first lines of this file unmodified other than the possible
132fe8fb19SBen Gras  *    addition of one or more copyright notices.
142fe8fb19SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
152fe8fb19SBen Gras  *    notice(s), this list of conditions and the following disclaimer in
162fe8fb19SBen Gras  *    the documentation and/or other materials provided with the
172fe8fb19SBen Gras  *    distribution.
182fe8fb19SBen Gras  *
192fe8fb19SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
202fe8fb19SBen Gras  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
212fe8fb19SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
222fe8fb19SBen Gras  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
232fe8fb19SBen Gras  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
242fe8fb19SBen Gras  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
252fe8fb19SBen Gras  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
262fe8fb19SBen Gras  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
272fe8fb19SBen Gras  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
282fe8fb19SBen Gras  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
292fe8fb19SBen Gras  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
302fe8fb19SBen Gras  *
312fe8fb19SBen Gras  *******************************************************************************
322fe8fb19SBen Gras  *
332fe8fb19SBen Gras  * This allocator implementation is designed to provide scalable performance
342fe8fb19SBen Gras  * for multi-threaded programs on multi-processor systems.  The following
352fe8fb19SBen Gras  * features are included for this purpose:
362fe8fb19SBen Gras  *
372fe8fb19SBen Gras  *   + Multiple arenas are used if there are multiple CPUs, which reduces lock
382fe8fb19SBen Gras  *     contention and cache sloshing.
392fe8fb19SBen Gras  *
402fe8fb19SBen Gras  *   + Cache line sharing between arenas is avoided for internal data
412fe8fb19SBen Gras  *     structures.
422fe8fb19SBen Gras  *
432fe8fb19SBen Gras  *   + Memory is managed in chunks and runs (chunks can be split into runs),
442fe8fb19SBen Gras  *     rather than as individual pages.  This provides a constant-time
452fe8fb19SBen Gras  *     mechanism for associating allocations with particular arenas.
462fe8fb19SBen Gras  *
472fe8fb19SBen Gras  * Allocation requests are rounded up to the nearest size class, and no record
482fe8fb19SBen Gras  * of the original request size is maintained.  Allocations are broken into
492fe8fb19SBen Gras  * categories according to size class.  Assuming runtime defaults, 4 kB pages
502fe8fb19SBen Gras  * and a 16 byte quantum, the size classes in each category are as follows:
512fe8fb19SBen Gras  *
522fe8fb19SBen Gras  *   |=====================================|
532fe8fb19SBen Gras  *   | Category | Subcategory    |    Size |
542fe8fb19SBen Gras  *   |=====================================|
552fe8fb19SBen Gras  *   | Small    | Tiny           |       2 |
562fe8fb19SBen Gras  *   |          |                |       4 |
572fe8fb19SBen Gras  *   |          |                |       8 |
582fe8fb19SBen Gras  *   |          |----------------+---------|
592fe8fb19SBen Gras  *   |          | Quantum-spaced |      16 |
602fe8fb19SBen Gras  *   |          |                |      32 |
612fe8fb19SBen Gras  *   |          |                |      48 |
622fe8fb19SBen Gras  *   |          |                |     ... |
632fe8fb19SBen Gras  *   |          |                |     480 |
642fe8fb19SBen Gras  *   |          |                |     496 |
652fe8fb19SBen Gras  *   |          |                |     512 |
662fe8fb19SBen Gras  *   |          |----------------+---------|
672fe8fb19SBen Gras  *   |          | Sub-page       |    1 kB |
682fe8fb19SBen Gras  *   |          |                |    2 kB |
692fe8fb19SBen Gras  *   |=====================================|
702fe8fb19SBen Gras  *   | Large                     |    4 kB |
712fe8fb19SBen Gras  *   |                           |    8 kB |
722fe8fb19SBen Gras  *   |                           |   12 kB |
732fe8fb19SBen Gras  *   |                           |     ... |
742fe8fb19SBen Gras  *   |                           | 1012 kB |
752fe8fb19SBen Gras  *   |                           | 1016 kB |
762fe8fb19SBen Gras  *   |                           | 1020 kB |
772fe8fb19SBen Gras  *   |=====================================|
782fe8fb19SBen Gras  *   | Huge                      |    1 MB |
792fe8fb19SBen Gras  *   |                           |    2 MB |
802fe8fb19SBen Gras  *   |                           |    3 MB |
812fe8fb19SBen Gras  *   |                           |     ... |
822fe8fb19SBen Gras  *   |=====================================|
832fe8fb19SBen Gras  *
842fe8fb19SBen Gras  * A different mechanism is used for each category:
852fe8fb19SBen Gras  *
862fe8fb19SBen Gras  *   Small : Each size class is segregated into its own set of runs.  Each run
872fe8fb19SBen Gras  *           maintains a bitmap of which regions are free/allocated.
882fe8fb19SBen Gras  *
892fe8fb19SBen Gras  *   Large : Each allocation is backed by a dedicated run.  Metadata are stored
902fe8fb19SBen Gras  *           in the associated arena chunk header maps.
912fe8fb19SBen Gras  *
922fe8fb19SBen Gras  *   Huge : Each allocation is backed by a dedicated contiguous set of chunks.
932fe8fb19SBen Gras  *          Metadata are stored in a separate red-black tree.
942fe8fb19SBen Gras  *
952fe8fb19SBen Gras  *******************************************************************************
962fe8fb19SBen Gras  */
972fe8fb19SBen Gras 
982fe8fb19SBen Gras /* LINTLIBRARY */
992fe8fb19SBen Gras 
100*0a6a1f1dSLionel Sambuc #if defined(__NetBSD__) || defined(__minix)
1012fe8fb19SBen Gras #  define xutrace(a, b)		utrace("malloc", (a), (b))
1022fe8fb19SBen Gras #  define __DECONST(x, y)	((x)__UNCONST(y))
1032fe8fb19SBen Gras #  define NO_TLS
1042fe8fb19SBen Gras #else
1052fe8fb19SBen Gras #  define xutrace(a, b)		utrace((a), (b))
1062fe8fb19SBen Gras #endif	/* __NetBSD__ */
1072fe8fb19SBen Gras 
1082fe8fb19SBen Gras /*
1092fe8fb19SBen Gras  * MALLOC_PRODUCTION disables assertions and statistics gathering.  It also
1102fe8fb19SBen Gras  * defaults the A and J runtime options to off.  These settings are appropriate
1112fe8fb19SBen Gras  * for production systems.
1122fe8fb19SBen Gras  */
1132fe8fb19SBen Gras #define MALLOC_PRODUCTION
1142fe8fb19SBen Gras 
1152fe8fb19SBen Gras #ifndef MALLOC_PRODUCTION
1162fe8fb19SBen Gras #  define MALLOC_DEBUG
1172fe8fb19SBen Gras #endif
1182fe8fb19SBen Gras 
1192fe8fb19SBen Gras #include <sys/cdefs.h>
1202fe8fb19SBen Gras /* __FBSDID("$FreeBSD: src/lib/libc/stdlib/malloc.c,v 1.147 2007/06/15 22:00:16 jasone Exp $"); */
121*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: jemalloc.c,v 1.38 2015/07/26 17:21:55 martin Exp $");
1222fe8fb19SBen Gras 
1232fe8fb19SBen Gras #ifdef __FreeBSD__
1242fe8fb19SBen Gras #include "libc_private.h"
1252fe8fb19SBen Gras #ifdef MALLOC_DEBUG
1262fe8fb19SBen Gras #  define _LOCK_DEBUG
1272fe8fb19SBen Gras #endif
1282fe8fb19SBen Gras #include "spinlock.h"
1292fe8fb19SBen Gras #endif
1302fe8fb19SBen Gras #include "namespace.h"
1312fe8fb19SBen Gras #include <sys/mman.h>
1322fe8fb19SBen Gras #include <sys/param.h>
1332fe8fb19SBen Gras #ifdef __FreeBSD__
1342fe8fb19SBen Gras #include <sys/stddef.h>
1352fe8fb19SBen Gras #endif
1362fe8fb19SBen Gras #include <sys/time.h>
1372fe8fb19SBen Gras #include <sys/types.h>
1382fe8fb19SBen Gras #include <sys/sysctl.h>
1392fe8fb19SBen Gras #include <sys/tree.h>
1402fe8fb19SBen Gras #include <sys/uio.h>
1412fe8fb19SBen Gras #include <sys/ktrace.h> /* Must come after several other sys/ includes. */
1422fe8fb19SBen Gras 
1432fe8fb19SBen Gras #ifdef __FreeBSD__
1442fe8fb19SBen Gras #include <machine/atomic.h>
1452fe8fb19SBen Gras #include <machine/cpufunc.h>
1462fe8fb19SBen Gras #endif
1472fe8fb19SBen Gras #include <machine/vmparam.h>
1482fe8fb19SBen Gras 
1492fe8fb19SBen Gras #include <errno.h>
1502fe8fb19SBen Gras #include <limits.h>
1512fe8fb19SBen Gras #include <pthread.h>
1522fe8fb19SBen Gras #include <sched.h>
1532fe8fb19SBen Gras #include <stdarg.h>
1542fe8fb19SBen Gras #include <stdbool.h>
1552fe8fb19SBen Gras #include <stdio.h>
1562fe8fb19SBen Gras #include <stdint.h>
1572fe8fb19SBen Gras #include <stdlib.h>
1582fe8fb19SBen Gras #include <string.h>
1592fe8fb19SBen Gras #include <strings.h>
1602fe8fb19SBen Gras #include <unistd.h>
1612fe8fb19SBen Gras 
1622fe8fb19SBen Gras #ifdef __NetBSD__
1632fe8fb19SBen Gras #  include <reentrant.h>
1642fe8fb19SBen Gras #  include "extern.h"
1652fe8fb19SBen Gras 
1662fe8fb19SBen Gras #define STRERROR_R(a, b, c)	__strerror_r(a, b, c);
1672fe8fb19SBen Gras /*
1682fe8fb19SBen Gras  * A non localized version of strerror, that avoids bringing in
1692fe8fb19SBen Gras  * stdio and the locale code. All the malloc messages are in English
1702fe8fb19SBen Gras  * so why bother?
1712fe8fb19SBen Gras  */
1722fe8fb19SBen Gras static int
__strerror_r(int e,char * s,size_t l)1732fe8fb19SBen Gras __strerror_r(int e, char *s, size_t l)
1742fe8fb19SBen Gras {
1752fe8fb19SBen Gras 	int rval;
1762fe8fb19SBen Gras 	size_t slen;
1772fe8fb19SBen Gras 
1782fe8fb19SBen Gras 	if (e >= 0 && e < sys_nerr) {
1792fe8fb19SBen Gras 		slen = strlcpy(s, sys_errlist[e], l);
1802fe8fb19SBen Gras 		rval = 0;
1812fe8fb19SBen Gras 	} else {
1822fe8fb19SBen Gras 		slen = snprintf_ss(s, l, "Unknown error %u", e);
1832fe8fb19SBen Gras 		rval = EINVAL;
1842fe8fb19SBen Gras 	}
1852fe8fb19SBen Gras 	return slen >= l ? ERANGE : rval;
1862fe8fb19SBen Gras }
1872fe8fb19SBen Gras #endif
1882fe8fb19SBen Gras 
1892fe8fb19SBen Gras #ifdef __FreeBSD__
1902fe8fb19SBen Gras #define STRERROR_R(a, b, c)	strerror_r(a, b, c);
1912fe8fb19SBen Gras #include "un-namespace.h"
1922fe8fb19SBen Gras #endif
1932fe8fb19SBen Gras 
1942fe8fb19SBen Gras /* MALLOC_STATS enables statistics calculation. */
1952fe8fb19SBen Gras #ifndef MALLOC_PRODUCTION
1962fe8fb19SBen Gras #  define MALLOC_STATS
1972fe8fb19SBen Gras #endif
1982fe8fb19SBen Gras 
1992fe8fb19SBen Gras #ifdef MALLOC_DEBUG
2002fe8fb19SBen Gras #  ifdef NDEBUG
2012fe8fb19SBen Gras #    undef NDEBUG
2022fe8fb19SBen Gras #  endif
2032fe8fb19SBen Gras #else
2042fe8fb19SBen Gras #  ifndef NDEBUG
2052fe8fb19SBen Gras #    define NDEBUG
2062fe8fb19SBen Gras #  endif
2072fe8fb19SBen Gras #endif
2082fe8fb19SBen Gras #include <assert.h>
2092fe8fb19SBen Gras 
2102fe8fb19SBen Gras #ifdef MALLOC_DEBUG
2112fe8fb19SBen Gras    /* Disable inlining to make debugging easier. */
2122fe8fb19SBen Gras #  define inline
2132fe8fb19SBen Gras #endif
2142fe8fb19SBen Gras 
2152fe8fb19SBen Gras /* Size of stack-allocated buffer passed to strerror_r(). */
2162fe8fb19SBen Gras #define	STRERROR_BUF		64
2172fe8fb19SBen Gras 
2182fe8fb19SBen Gras /* Minimum alignment of allocations is 2^QUANTUM_2POW_MIN bytes. */
219*0a6a1f1dSLionel Sambuc 
220*0a6a1f1dSLionel Sambuc /*
221*0a6a1f1dSLionel Sambuc  * If you touch the TINY_MIN_2POW definition for any architecture, please
222*0a6a1f1dSLionel Sambuc  * make sure to adjust the corresponding definition for JEMALLOC_TINY_MIN_2POW
223*0a6a1f1dSLionel Sambuc  * in the gcc 4.8 tree in dist/gcc/tree-ssa-ccp.c and verify that a native
224*0a6a1f1dSLionel Sambuc  * gcc is still buildable!
225*0a6a1f1dSLionel Sambuc  */
226*0a6a1f1dSLionel Sambuc 
2272fe8fb19SBen Gras #ifdef __i386__
2282fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2292fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2302fe8fb19SBen Gras #  define USE_BRK
2312fe8fb19SBen Gras #endif
2322fe8fb19SBen Gras #ifdef __ia64__
2332fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2342fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	3
2352fe8fb19SBen Gras #endif
236*0a6a1f1dSLionel Sambuc #ifdef __aarch64__
237*0a6a1f1dSLionel Sambuc #  define QUANTUM_2POW_MIN	4
238*0a6a1f1dSLionel Sambuc #  define SIZEOF_PTR_2POW	3
239*0a6a1f1dSLionel Sambuc #  define NO_TLS
240*0a6a1f1dSLionel Sambuc #endif
2412fe8fb19SBen Gras #ifdef __alpha__
2422fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2432fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	3
244*0a6a1f1dSLionel Sambuc #  define TINY_MIN_2POW		3
2452fe8fb19SBen Gras #  define NO_TLS
2462fe8fb19SBen Gras #endif
2472fe8fb19SBen Gras #ifdef __sparc64__
2482fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2492fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	3
250*0a6a1f1dSLionel Sambuc #  define TINY_MIN_2POW		3
2512fe8fb19SBen Gras #  define NO_TLS
2522fe8fb19SBen Gras #endif
2532fe8fb19SBen Gras #ifdef __amd64__
2542fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2552fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	3
256*0a6a1f1dSLionel Sambuc #  define TINY_MIN_2POW		3
2572fe8fb19SBen Gras #endif
2582fe8fb19SBen Gras #ifdef __arm__
2592fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	3
2602fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2612fe8fb19SBen Gras #  define USE_BRK
262*0a6a1f1dSLionel Sambuc #  ifdef __ARM_EABI__
263*0a6a1f1dSLionel Sambuc #    define TINY_MIN_2POW	3
264*0a6a1f1dSLionel Sambuc #  endif
2652fe8fb19SBen Gras #  define NO_TLS
2662fe8fb19SBen Gras #endif
2672fe8fb19SBen Gras #ifdef __powerpc__
2682fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2692fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2702fe8fb19SBen Gras #  define USE_BRK
271*0a6a1f1dSLionel Sambuc #  define TINY_MIN_2POW		3
2722fe8fb19SBen Gras #endif
2732fe8fb19SBen Gras #if defined(__sparc__) && !defined(__sparc64__)
2742fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2752fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2762fe8fb19SBen Gras #  define USE_BRK
2772fe8fb19SBen Gras #endif
278*0a6a1f1dSLionel Sambuc #ifdef __or1k__
279*0a6a1f1dSLionel Sambuc #  define QUANTUM_2POW_MIN	4
280*0a6a1f1dSLionel Sambuc #  define SIZEOF_PTR_2POW	2
281*0a6a1f1dSLionel Sambuc #  define USE_BRK
282*0a6a1f1dSLionel Sambuc #endif
2832fe8fb19SBen Gras #ifdef __vax__
2842fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2852fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2862fe8fb19SBen Gras #  define USE_BRK
2872fe8fb19SBen Gras #endif
2882fe8fb19SBen Gras #ifdef __sh__
2892fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2902fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2912fe8fb19SBen Gras #  define USE_BRK
2922fe8fb19SBen Gras #endif
2932fe8fb19SBen Gras #ifdef __m68k__
2942fe8fb19SBen Gras #  define QUANTUM_2POW_MIN	4
2952fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
2962fe8fb19SBen Gras #  define USE_BRK
2972fe8fb19SBen Gras #endif
298*0a6a1f1dSLionel Sambuc #if defined(__mips__) || defined(__riscv__)
299*0a6a1f1dSLionel Sambuc # ifdef _LP64
300*0a6a1f1dSLionel Sambuc #  define SIZEOF_PTR_2POW	3
301*0a6a1f1dSLionel Sambuc #  define TINY_MIN_2POW		3
302*0a6a1f1dSLionel Sambuc # else
3032fe8fb19SBen Gras #  define SIZEOF_PTR_2POW	2
304*0a6a1f1dSLionel Sambuc # endif
305*0a6a1f1dSLionel Sambuc # define QUANTUM_2POW_MIN	4
3062fe8fb19SBen Gras # define USE_BRK
3072fe8fb19SBen Gras #endif
3082fe8fb19SBen Gras #ifdef __hppa__
3092fe8fb19SBen Gras #  define QUANTUM_2POW_MIN     4
3102fe8fb19SBen Gras #  define SIZEOF_PTR_2POW      2
3112fe8fb19SBen Gras #  define USE_BRK
3122fe8fb19SBen Gras #endif
3132fe8fb19SBen Gras 
3142fe8fb19SBen Gras #define	SIZEOF_PTR		(1 << SIZEOF_PTR_2POW)
3152fe8fb19SBen Gras 
3162fe8fb19SBen Gras /* sizeof(int) == (1 << SIZEOF_INT_2POW). */
3172fe8fb19SBen Gras #ifndef SIZEOF_INT_2POW
3182fe8fb19SBen Gras #  define SIZEOF_INT_2POW	2
3192fe8fb19SBen Gras #endif
3202fe8fb19SBen Gras 
3212fe8fb19SBen Gras /*
3222fe8fb19SBen Gras  * Size and alignment of memory chunks that are allocated by the OS's virtual
3232fe8fb19SBen Gras  * memory system.
3242fe8fb19SBen Gras  */
3252fe8fb19SBen Gras #define	CHUNK_2POW_DEFAULT	20
3262fe8fb19SBen Gras 
3272fe8fb19SBen Gras /*
3282fe8fb19SBen Gras  * Maximum size of L1 cache line.  This is used to avoid cache line aliasing,
3292fe8fb19SBen Gras  * so over-estimates are okay (up to a point), but under-estimates will
3302fe8fb19SBen Gras  * negatively affect performance.
3312fe8fb19SBen Gras  */
3322fe8fb19SBen Gras #define	CACHELINE_2POW		6
3332fe8fb19SBen Gras #define	CACHELINE		((size_t)(1 << CACHELINE_2POW))
3342fe8fb19SBen Gras 
3352fe8fb19SBen Gras /* Smallest size class to support. */
336*0a6a1f1dSLionel Sambuc #ifndef TINY_MIN_2POW
337*0a6a1f1dSLionel Sambuc #define	TINY_MIN_2POW		2
338*0a6a1f1dSLionel Sambuc #endif
3392fe8fb19SBen Gras 
3402fe8fb19SBen Gras /*
3412fe8fb19SBen Gras  * Maximum size class that is a multiple of the quantum, but not (necessarily)
3422fe8fb19SBen Gras  * a power of 2.  Above this size, allocations are rounded up to the nearest
3432fe8fb19SBen Gras  * power of 2.
3442fe8fb19SBen Gras  */
3452fe8fb19SBen Gras #define	SMALL_MAX_2POW_DEFAULT	9
3462fe8fb19SBen Gras #define	SMALL_MAX_DEFAULT	(1 << SMALL_MAX_2POW_DEFAULT)
3472fe8fb19SBen Gras 
3482fe8fb19SBen Gras /*
349f14fb602SLionel Sambuc  * RUN_MAX_OVRHD indicates maximum desired run header overhead.  Runs are sized
350f14fb602SLionel Sambuc  * as small as possible such that this setting is still honored, without
351f14fb602SLionel Sambuc  * violating other constraints.  The goal is to make runs as small as possible
352f14fb602SLionel Sambuc  * without exceeding a per run external fragmentation threshold.
3532fe8fb19SBen Gras  *
354f14fb602SLionel Sambuc  * We use binary fixed point math for overhead computations, where the binary
355f14fb602SLionel Sambuc  * point is implicitly RUN_BFP bits to the left.
3562fe8fb19SBen Gras  *
357f14fb602SLionel Sambuc  * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be
358f14fb602SLionel Sambuc  * honored for some/all object sizes, since there is one bit of header overhead
359f14fb602SLionel Sambuc  * per object (plus a constant).  This constraint is relaxed (ignored) for runs
360f14fb602SLionel Sambuc  * that are so small that the per-region overhead is greater than:
361f14fb602SLionel Sambuc  *
362f14fb602SLionel Sambuc  *   (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP))
3632fe8fb19SBen Gras  */
364f14fb602SLionel Sambuc #define RUN_BFP			12
365f14fb602SLionel Sambuc /*                              \/   Implicit binary fixed point. */
366f14fb602SLionel Sambuc #define RUN_MAX_OVRHD		0x0000003dU
367f14fb602SLionel Sambuc #define RUN_MAX_OVRHD_RELAX	0x00001800U
3682fe8fb19SBen Gras 
3692fe8fb19SBen Gras /* Put a cap on small object run size.  This overrides RUN_MAX_OVRHD. */
3702fe8fb19SBen Gras #define RUN_MAX_SMALL_2POW	15
3712fe8fb19SBen Gras #define RUN_MAX_SMALL		(1 << RUN_MAX_SMALL_2POW)
3722fe8fb19SBen Gras 
3732fe8fb19SBen Gras /******************************************************************************/
3742fe8fb19SBen Gras 
3752fe8fb19SBen Gras #ifdef __FreeBSD__
3762fe8fb19SBen Gras /*
3772fe8fb19SBen Gras  * Mutexes based on spinlocks.  We can't use normal pthread mutexes, because
3782fe8fb19SBen Gras  * they require malloc()ed memory.
3792fe8fb19SBen Gras  */
3802fe8fb19SBen Gras typedef struct {
3812fe8fb19SBen Gras 	spinlock_t	lock;
3822fe8fb19SBen Gras } malloc_mutex_t;
3832fe8fb19SBen Gras 
3842fe8fb19SBen Gras /* Set to true once the allocator has been initialized. */
3852fe8fb19SBen Gras static bool malloc_initialized = false;
3862fe8fb19SBen Gras 
3872fe8fb19SBen Gras /* Used to avoid initialization races. */
3882fe8fb19SBen Gras static malloc_mutex_t init_lock = {_SPINLOCK_INITIALIZER};
3892fe8fb19SBen Gras #else
3902fe8fb19SBen Gras #define	malloc_mutex_t	mutex_t
3912fe8fb19SBen Gras 
3922fe8fb19SBen Gras /* Set to true once the allocator has been initialized. */
3932fe8fb19SBen Gras static bool malloc_initialized = false;
3942fe8fb19SBen Gras 
395*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
3962fe8fb19SBen Gras /* Used to avoid initialization races. */
3972fe8fb19SBen Gras static mutex_t init_lock = MUTEX_INITIALIZER;
3982fe8fb19SBen Gras #endif
399*0a6a1f1dSLionel Sambuc #endif
4002fe8fb19SBen Gras 
4012fe8fb19SBen Gras /******************************************************************************/
4022fe8fb19SBen Gras /*
4032fe8fb19SBen Gras  * Statistics data structures.
4042fe8fb19SBen Gras  */
4052fe8fb19SBen Gras 
4062fe8fb19SBen Gras #ifdef MALLOC_STATS
4072fe8fb19SBen Gras 
4082fe8fb19SBen Gras typedef struct malloc_bin_stats_s malloc_bin_stats_t;
4092fe8fb19SBen Gras struct malloc_bin_stats_s {
4102fe8fb19SBen Gras 	/*
4112fe8fb19SBen Gras 	 * Number of allocation requests that corresponded to the size of this
4122fe8fb19SBen Gras 	 * bin.
4132fe8fb19SBen Gras 	 */
4142fe8fb19SBen Gras 	uint64_t	nrequests;
4152fe8fb19SBen Gras 
4162fe8fb19SBen Gras 	/* Total number of runs created for this bin's size class. */
4172fe8fb19SBen Gras 	uint64_t	nruns;
4182fe8fb19SBen Gras 
4192fe8fb19SBen Gras 	/*
4202fe8fb19SBen Gras 	 * Total number of runs reused by extracting them from the runs tree for
4212fe8fb19SBen Gras 	 * this bin's size class.
4222fe8fb19SBen Gras 	 */
4232fe8fb19SBen Gras 	uint64_t	reruns;
4242fe8fb19SBen Gras 
4252fe8fb19SBen Gras 	/* High-water mark for this bin. */
4262fe8fb19SBen Gras 	unsigned long	highruns;
4272fe8fb19SBen Gras 
4282fe8fb19SBen Gras 	/* Current number of runs in this bin. */
4292fe8fb19SBen Gras 	unsigned long	curruns;
4302fe8fb19SBen Gras };
4312fe8fb19SBen Gras 
4322fe8fb19SBen Gras typedef struct arena_stats_s arena_stats_t;
4332fe8fb19SBen Gras struct arena_stats_s {
4342fe8fb19SBen Gras 	/* Number of bytes currently mapped. */
4352fe8fb19SBen Gras 	size_t		mapped;
4362fe8fb19SBen Gras 
4372fe8fb19SBen Gras 	/* Per-size-category statistics. */
4382fe8fb19SBen Gras 	size_t		allocated_small;
4392fe8fb19SBen Gras 	uint64_t	nmalloc_small;
4402fe8fb19SBen Gras 	uint64_t	ndalloc_small;
4412fe8fb19SBen Gras 
4422fe8fb19SBen Gras 	size_t		allocated_large;
4432fe8fb19SBen Gras 	uint64_t	nmalloc_large;
4442fe8fb19SBen Gras 	uint64_t	ndalloc_large;
4452fe8fb19SBen Gras };
4462fe8fb19SBen Gras 
4472fe8fb19SBen Gras typedef struct chunk_stats_s chunk_stats_t;
4482fe8fb19SBen Gras struct chunk_stats_s {
4492fe8fb19SBen Gras 	/* Number of chunks that were allocated. */
4502fe8fb19SBen Gras 	uint64_t	nchunks;
4512fe8fb19SBen Gras 
4522fe8fb19SBen Gras 	/* High-water mark for number of chunks allocated. */
4532fe8fb19SBen Gras 	unsigned long	highchunks;
4542fe8fb19SBen Gras 
4552fe8fb19SBen Gras 	/*
4562fe8fb19SBen Gras 	 * Current number of chunks allocated.  This value isn't maintained for
4572fe8fb19SBen Gras 	 * any other purpose, so keep track of it in order to be able to set
4582fe8fb19SBen Gras 	 * highchunks.
4592fe8fb19SBen Gras 	 */
4602fe8fb19SBen Gras 	unsigned long	curchunks;
4612fe8fb19SBen Gras };
4622fe8fb19SBen Gras 
4632fe8fb19SBen Gras #endif /* #ifdef MALLOC_STATS */
4642fe8fb19SBen Gras 
4652fe8fb19SBen Gras /******************************************************************************/
4662fe8fb19SBen Gras /*
4672fe8fb19SBen Gras  * Chunk data structures.
4682fe8fb19SBen Gras  */
4692fe8fb19SBen Gras 
4702fe8fb19SBen Gras /* Tree of chunks. */
4712fe8fb19SBen Gras typedef struct chunk_node_s chunk_node_t;
4722fe8fb19SBen Gras struct chunk_node_s {
4732fe8fb19SBen Gras 	/* Linkage for the chunk tree. */
4742fe8fb19SBen Gras 	RB_ENTRY(chunk_node_s) link;
4752fe8fb19SBen Gras 
4762fe8fb19SBen Gras 	/*
4772fe8fb19SBen Gras 	 * Pointer to the chunk that this tree node is responsible for.  In some
4782fe8fb19SBen Gras 	 * (but certainly not all) cases, this data structure is placed at the
4792fe8fb19SBen Gras 	 * beginning of the corresponding chunk, so this field may point to this
4802fe8fb19SBen Gras 	 * node.
4812fe8fb19SBen Gras 	 */
4822fe8fb19SBen Gras 	void	*chunk;
4832fe8fb19SBen Gras 
4842fe8fb19SBen Gras 	/* Total chunk size. */
4852fe8fb19SBen Gras 	size_t	size;
4862fe8fb19SBen Gras };
4872fe8fb19SBen Gras typedef struct chunk_tree_s chunk_tree_t;
4882fe8fb19SBen Gras RB_HEAD(chunk_tree_s, chunk_node_s);
4892fe8fb19SBen Gras 
4902fe8fb19SBen Gras /******************************************************************************/
4912fe8fb19SBen Gras /*
4922fe8fb19SBen Gras  * Arena data structures.
4932fe8fb19SBen Gras  */
4942fe8fb19SBen Gras 
4952fe8fb19SBen Gras typedef struct arena_s arena_t;
4962fe8fb19SBen Gras typedef struct arena_bin_s arena_bin_t;
4972fe8fb19SBen Gras 
4982fe8fb19SBen Gras typedef struct arena_chunk_map_s arena_chunk_map_t;
4992fe8fb19SBen Gras struct arena_chunk_map_s {
5002fe8fb19SBen Gras 	/* Number of pages in run. */
5012fe8fb19SBen Gras 	uint32_t	npages;
5022fe8fb19SBen Gras 	/*
5032fe8fb19SBen Gras 	 * Position within run.  For a free run, this is POS_FREE for the first
5042fe8fb19SBen Gras 	 * and last pages.  The POS_FREE special value makes it possible to
5052fe8fb19SBen Gras 	 * quickly coalesce free runs.
5062fe8fb19SBen Gras 	 *
5072fe8fb19SBen Gras 	 * This is the limiting factor for chunksize; there can be at most 2^31
5082fe8fb19SBen Gras 	 * pages in a run.
5092fe8fb19SBen Gras 	 */
5102fe8fb19SBen Gras #define POS_FREE ((uint32_t)0xffffffffU)
5112fe8fb19SBen Gras 	uint32_t	pos;
5122fe8fb19SBen Gras };
5132fe8fb19SBen Gras 
5142fe8fb19SBen Gras /* Arena chunk header. */
5152fe8fb19SBen Gras typedef struct arena_chunk_s arena_chunk_t;
5162fe8fb19SBen Gras struct arena_chunk_s {
5172fe8fb19SBen Gras 	/* Arena that owns the chunk. */
5182fe8fb19SBen Gras 	arena_t *arena;
5192fe8fb19SBen Gras 
5202fe8fb19SBen Gras 	/* Linkage for the arena's chunk tree. */
5212fe8fb19SBen Gras 	RB_ENTRY(arena_chunk_s) link;
5222fe8fb19SBen Gras 
5232fe8fb19SBen Gras 	/*
5242fe8fb19SBen Gras 	 * Number of pages in use.  This is maintained in order to make
5252fe8fb19SBen Gras 	 * detection of empty chunks fast.
5262fe8fb19SBen Gras 	 */
5272fe8fb19SBen Gras 	uint32_t pages_used;
5282fe8fb19SBen Gras 
5292fe8fb19SBen Gras 	/*
5302fe8fb19SBen Gras 	 * Every time a free run larger than this value is created/coalesced,
5312fe8fb19SBen Gras 	 * this value is increased.  The only way that the value decreases is if
5322fe8fb19SBen Gras 	 * arena_run_alloc() fails to find a free run as large as advertised by
5332fe8fb19SBen Gras 	 * this value.
5342fe8fb19SBen Gras 	 */
5352fe8fb19SBen Gras 	uint32_t max_frun_npages;
5362fe8fb19SBen Gras 
5372fe8fb19SBen Gras 	/*
5382fe8fb19SBen Gras 	 * Every time a free run that starts at an earlier page than this value
5392fe8fb19SBen Gras 	 * is created/coalesced, this value is decreased.  It is reset in a
5402fe8fb19SBen Gras 	 * similar fashion to max_frun_npages.
5412fe8fb19SBen Gras 	 */
5422fe8fb19SBen Gras 	uint32_t min_frun_ind;
5432fe8fb19SBen Gras 
5442fe8fb19SBen Gras 	/*
5452fe8fb19SBen Gras 	 * Map of pages within chunk that keeps track of free/large/small.  For
5462fe8fb19SBen Gras 	 * free runs, only the map entries for the first and last pages are
5472fe8fb19SBen Gras 	 * kept up to date, so that free runs can be quickly coalesced.
5482fe8fb19SBen Gras 	 */
5492fe8fb19SBen Gras 	arena_chunk_map_t map[1]; /* Dynamically sized. */
5502fe8fb19SBen Gras };
5512fe8fb19SBen Gras typedef struct arena_chunk_tree_s arena_chunk_tree_t;
5522fe8fb19SBen Gras RB_HEAD(arena_chunk_tree_s, arena_chunk_s);
5532fe8fb19SBen Gras 
5542fe8fb19SBen Gras typedef struct arena_run_s arena_run_t;
5552fe8fb19SBen Gras struct arena_run_s {
5562fe8fb19SBen Gras 	/* Linkage for run trees. */
5572fe8fb19SBen Gras 	RB_ENTRY(arena_run_s) link;
5582fe8fb19SBen Gras 
5592fe8fb19SBen Gras #ifdef MALLOC_DEBUG
5602fe8fb19SBen Gras 	uint32_t	magic;
5612fe8fb19SBen Gras #  define ARENA_RUN_MAGIC 0x384adf93
5622fe8fb19SBen Gras #endif
5632fe8fb19SBen Gras 
5642fe8fb19SBen Gras 	/* Bin this run is associated with. */
5652fe8fb19SBen Gras 	arena_bin_t	*bin;
5662fe8fb19SBen Gras 
5672fe8fb19SBen Gras 	/* Index of first element that might have a free region. */
5682fe8fb19SBen Gras 	unsigned	regs_minelm;
5692fe8fb19SBen Gras 
5702fe8fb19SBen Gras 	/* Number of free regions in run. */
5712fe8fb19SBen Gras 	unsigned	nfree;
5722fe8fb19SBen Gras 
5732fe8fb19SBen Gras 	/* Bitmask of in-use regions (0: in use, 1: free). */
5742fe8fb19SBen Gras 	unsigned	regs_mask[1]; /* Dynamically sized. */
5752fe8fb19SBen Gras };
5762fe8fb19SBen Gras typedef struct arena_run_tree_s arena_run_tree_t;
5772fe8fb19SBen Gras RB_HEAD(arena_run_tree_s, arena_run_s);
5782fe8fb19SBen Gras 
5792fe8fb19SBen Gras struct arena_bin_s {
5802fe8fb19SBen Gras 	/*
5812fe8fb19SBen Gras 	 * Current run being used to service allocations of this bin's size
5822fe8fb19SBen Gras 	 * class.
5832fe8fb19SBen Gras 	 */
5842fe8fb19SBen Gras 	arena_run_t	*runcur;
5852fe8fb19SBen Gras 
5862fe8fb19SBen Gras 	/*
5872fe8fb19SBen Gras 	 * Tree of non-full runs.  This tree is used when looking for an
5882fe8fb19SBen Gras 	 * existing run when runcur is no longer usable.  We choose the
5892fe8fb19SBen Gras 	 * non-full run that is lowest in memory; this policy tends to keep
5902fe8fb19SBen Gras 	 * objects packed well, and it can also help reduce the number of
5912fe8fb19SBen Gras 	 * almost-empty chunks.
5922fe8fb19SBen Gras 	 */
5932fe8fb19SBen Gras 	arena_run_tree_t runs;
5942fe8fb19SBen Gras 
5952fe8fb19SBen Gras 	/* Size of regions in a run for this bin's size class. */
5962fe8fb19SBen Gras 	size_t		reg_size;
5972fe8fb19SBen Gras 
5982fe8fb19SBen Gras 	/* Total size of a run for this bin's size class. */
5992fe8fb19SBen Gras 	size_t		run_size;
6002fe8fb19SBen Gras 
6012fe8fb19SBen Gras 	/* Total number of regions in a run for this bin's size class. */
6022fe8fb19SBen Gras 	uint32_t	nregs;
6032fe8fb19SBen Gras 
6042fe8fb19SBen Gras 	/* Number of elements in a run's regs_mask for this bin's size class. */
6052fe8fb19SBen Gras 	uint32_t	regs_mask_nelms;
6062fe8fb19SBen Gras 
6072fe8fb19SBen Gras 	/* Offset of first region in a run for this bin's size class. */
6082fe8fb19SBen Gras 	uint32_t	reg0_offset;
6092fe8fb19SBen Gras 
6102fe8fb19SBen Gras #ifdef MALLOC_STATS
6112fe8fb19SBen Gras 	/* Bin statistics. */
6122fe8fb19SBen Gras 	malloc_bin_stats_t stats;
6132fe8fb19SBen Gras #endif
6142fe8fb19SBen Gras };
6152fe8fb19SBen Gras 
6162fe8fb19SBen Gras struct arena_s {
6172fe8fb19SBen Gras #ifdef MALLOC_DEBUG
6182fe8fb19SBen Gras 	uint32_t		magic;
6192fe8fb19SBen Gras #  define ARENA_MAGIC 0x947d3d24
6202fe8fb19SBen Gras #endif
6212fe8fb19SBen Gras 
6222fe8fb19SBen Gras 	/* All operations on this arena require that mtx be locked. */
6232fe8fb19SBen Gras 	malloc_mutex_t		mtx;
6242fe8fb19SBen Gras 
6252fe8fb19SBen Gras #ifdef MALLOC_STATS
6262fe8fb19SBen Gras 	arena_stats_t		stats;
6272fe8fb19SBen Gras #endif
6282fe8fb19SBen Gras 
6292fe8fb19SBen Gras 	/*
6302fe8fb19SBen Gras 	 * Tree of chunks this arena manages.
6312fe8fb19SBen Gras 	 */
6322fe8fb19SBen Gras 	arena_chunk_tree_t	chunks;
6332fe8fb19SBen Gras 
6342fe8fb19SBen Gras 	/*
6352fe8fb19SBen Gras 	 * In order to avoid rapid chunk allocation/deallocation when an arena
6362fe8fb19SBen Gras 	 * oscillates right on the cusp of needing a new chunk, cache the most
6372fe8fb19SBen Gras 	 * recently freed chunk.  This caching is disabled by opt_hint.
6382fe8fb19SBen Gras 	 *
6392fe8fb19SBen Gras 	 * There is one spare chunk per arena, rather than one spare total, in
6402fe8fb19SBen Gras 	 * order to avoid interactions between multiple threads that could make
6412fe8fb19SBen Gras 	 * a single spare inadequate.
6422fe8fb19SBen Gras 	 */
6432fe8fb19SBen Gras 	arena_chunk_t *spare;
6442fe8fb19SBen Gras 
6452fe8fb19SBen Gras 	/*
6462fe8fb19SBen Gras 	 * bins is used to store rings of free regions of the following sizes,
6472fe8fb19SBen Gras 	 * assuming a 16-byte quantum, 4kB pagesize, and default MALLOC_OPTIONS.
6482fe8fb19SBen Gras 	 *
6492fe8fb19SBen Gras 	 *   bins[i] | size |
6502fe8fb19SBen Gras 	 *   --------+------+
6512fe8fb19SBen Gras 	 *        0  |    2 |
6522fe8fb19SBen Gras 	 *        1  |    4 |
6532fe8fb19SBen Gras 	 *        2  |    8 |
6542fe8fb19SBen Gras 	 *   --------+------+
6552fe8fb19SBen Gras 	 *        3  |   16 |
6562fe8fb19SBen Gras 	 *        4  |   32 |
6572fe8fb19SBen Gras 	 *        5  |   48 |
6582fe8fb19SBen Gras 	 *        6  |   64 |
6592fe8fb19SBen Gras 	 *           :      :
6602fe8fb19SBen Gras 	 *           :      :
6612fe8fb19SBen Gras 	 *       33  |  496 |
6622fe8fb19SBen Gras 	 *       34  |  512 |
6632fe8fb19SBen Gras 	 *   --------+------+
6642fe8fb19SBen Gras 	 *       35  | 1024 |
6652fe8fb19SBen Gras 	 *       36  | 2048 |
6662fe8fb19SBen Gras 	 *   --------+------+
6672fe8fb19SBen Gras 	 */
6682fe8fb19SBen Gras 	arena_bin_t		bins[1]; /* Dynamically sized. */
6692fe8fb19SBen Gras };
6702fe8fb19SBen Gras 
6712fe8fb19SBen Gras /******************************************************************************/
6722fe8fb19SBen Gras /*
6732fe8fb19SBen Gras  * Data.
6742fe8fb19SBen Gras  */
6752fe8fb19SBen Gras 
6762fe8fb19SBen Gras /* Number of CPUs. */
6772fe8fb19SBen Gras static unsigned		ncpus;
6782fe8fb19SBen Gras 
6792fe8fb19SBen Gras /* VM page size. */
6802fe8fb19SBen Gras static size_t		pagesize;
6812fe8fb19SBen Gras static size_t		pagesize_mask;
6822fe8fb19SBen Gras static int		pagesize_2pow;
6832fe8fb19SBen Gras 
6842fe8fb19SBen Gras /* Various bin-related settings. */
6852fe8fb19SBen Gras static size_t		bin_maxclass; /* Max size class for bins. */
6862fe8fb19SBen Gras static unsigned		ntbins; /* Number of (2^n)-spaced tiny bins. */
6872fe8fb19SBen Gras static unsigned		nqbins; /* Number of quantum-spaced bins. */
6882fe8fb19SBen Gras static unsigned		nsbins; /* Number of (2^n)-spaced sub-page bins. */
6892fe8fb19SBen Gras static size_t		small_min;
6902fe8fb19SBen Gras static size_t		small_max;
6912fe8fb19SBen Gras 
6922fe8fb19SBen Gras /* Various quantum-related settings. */
6932fe8fb19SBen Gras static size_t		quantum;
6942fe8fb19SBen Gras static size_t		quantum_mask; /* (quantum - 1). */
6952fe8fb19SBen Gras 
6962fe8fb19SBen Gras /* Various chunk-related settings. */
6972fe8fb19SBen Gras static size_t		chunksize;
6982fe8fb19SBen Gras static size_t		chunksize_mask; /* (chunksize - 1). */
6992fe8fb19SBen Gras static int		chunksize_2pow;
7002fe8fb19SBen Gras static unsigned		chunk_npages;
7012fe8fb19SBen Gras static unsigned		arena_chunk_header_npages;
7022fe8fb19SBen Gras static size_t		arena_maxclass; /* Max size class for arenas. */
7032fe8fb19SBen Gras 
7042fe8fb19SBen Gras /********/
7052fe8fb19SBen Gras /*
7062fe8fb19SBen Gras  * Chunks.
7072fe8fb19SBen Gras  */
7082fe8fb19SBen Gras 
709*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
7102fe8fb19SBen Gras /* Protects chunk-related data structures. */
7112fe8fb19SBen Gras static malloc_mutex_t	chunks_mtx;
712*0a6a1f1dSLionel Sambuc #endif
7132fe8fb19SBen Gras 
7142fe8fb19SBen Gras /* Tree of chunks that are stand-alone huge allocations. */
7152fe8fb19SBen Gras static chunk_tree_t	huge;
7162fe8fb19SBen Gras 
7172fe8fb19SBen Gras #ifdef USE_BRK
7182fe8fb19SBen Gras /*
7192fe8fb19SBen Gras  * Try to use brk for chunk-size allocations, due to address space constraints.
7202fe8fb19SBen Gras  */
7212fe8fb19SBen Gras /*
7222fe8fb19SBen Gras  * Protects sbrk() calls.  This must be separate from chunks_mtx, since
7232fe8fb19SBen Gras  * base_pages_alloc() also uses sbrk(), but cannot lock chunks_mtx (doing so
7242fe8fb19SBen Gras  * could cause recursive lock acquisition).
7252fe8fb19SBen Gras  */
7262fe8fb19SBen Gras static malloc_mutex_t	brk_mtx;
7272fe8fb19SBen Gras /* Result of first sbrk(0) call. */
7282fe8fb19SBen Gras static void		*brk_base;
7292fe8fb19SBen Gras /* Current end of brk, or ((void *)-1) if brk is exhausted. */
7302fe8fb19SBen Gras static void		*brk_prev;
7312fe8fb19SBen Gras /* Current upper limit on brk addresses. */
7322fe8fb19SBen Gras static void		*brk_max;
7332fe8fb19SBen Gras #endif
7342fe8fb19SBen Gras 
7352fe8fb19SBen Gras #ifdef MALLOC_STATS
7362fe8fb19SBen Gras /* Huge allocation statistics. */
7372fe8fb19SBen Gras static uint64_t		huge_nmalloc;
7382fe8fb19SBen Gras static uint64_t		huge_ndalloc;
7392fe8fb19SBen Gras static uint64_t		huge_nralloc;
7402fe8fb19SBen Gras static size_t		huge_allocated;
7412fe8fb19SBen Gras #endif
7422fe8fb19SBen Gras 
7432fe8fb19SBen Gras /*
7442fe8fb19SBen Gras  * Tree of chunks that were previously allocated.  This is used when allocating
7452fe8fb19SBen Gras  * chunks, in an attempt to re-use address space.
7462fe8fb19SBen Gras  */
7472fe8fb19SBen Gras static chunk_tree_t	old_chunks;
7482fe8fb19SBen Gras 
7492fe8fb19SBen Gras /****************************/
7502fe8fb19SBen Gras /*
7512fe8fb19SBen Gras  * base (internal allocation).
7522fe8fb19SBen Gras  */
7532fe8fb19SBen Gras 
7542fe8fb19SBen Gras /*
7552fe8fb19SBen Gras  * Current pages that are being used for internal memory allocations.  These
7562fe8fb19SBen Gras  * pages are carved up in cacheline-size quanta, so that there is no chance of
7572fe8fb19SBen Gras  * false cache line sharing.
7582fe8fb19SBen Gras  */
7592fe8fb19SBen Gras static void		*base_pages;
7602fe8fb19SBen Gras static void		*base_next_addr;
7612fe8fb19SBen Gras static void		*base_past_addr; /* Addr immediately past base_pages. */
7622fe8fb19SBen Gras static chunk_node_t	*base_chunk_nodes; /* LIFO cache of chunk nodes. */
763*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
7642fe8fb19SBen Gras static malloc_mutex_t	base_mtx;
765*0a6a1f1dSLionel Sambuc #endif
7662fe8fb19SBen Gras #ifdef MALLOC_STATS
7672fe8fb19SBen Gras static size_t		base_mapped;
7682fe8fb19SBen Gras #endif
7692fe8fb19SBen Gras 
7702fe8fb19SBen Gras /********/
7712fe8fb19SBen Gras /*
7722fe8fb19SBen Gras  * Arenas.
7732fe8fb19SBen Gras  */
7742fe8fb19SBen Gras 
7752fe8fb19SBen Gras /*
7762fe8fb19SBen Gras  * Arenas that are used to service external requests.  Not all elements of the
7772fe8fb19SBen Gras  * arenas array are necessarily used; arenas are created lazily as needed.
7782fe8fb19SBen Gras  */
7792fe8fb19SBen Gras static arena_t		**arenas;
7802fe8fb19SBen Gras static unsigned		narenas;
7812fe8fb19SBen Gras static unsigned		next_arena;
782*0a6a1f1dSLionel Sambuc #ifdef _REENTRANT
7832fe8fb19SBen Gras static malloc_mutex_t	arenas_mtx; /* Protects arenas initialization. */
784*0a6a1f1dSLionel Sambuc #endif
7852fe8fb19SBen Gras 
7862fe8fb19SBen Gras /*
7872fe8fb19SBen Gras  * Map of pthread_self() --> arenas[???], used for selecting an arena to use
7882fe8fb19SBen Gras  * for allocations.
7892fe8fb19SBen Gras  */
790*0a6a1f1dSLionel Sambuc #ifndef NO_TLS
791*0a6a1f1dSLionel Sambuc static __thread arena_t	**arenas_map;
792*0a6a1f1dSLionel Sambuc #else
793*0a6a1f1dSLionel Sambuc static arena_t	**arenas_map;
794*0a6a1f1dSLionel Sambuc #endif
795*0a6a1f1dSLionel Sambuc 
796*0a6a1f1dSLionel Sambuc #if !defined(NO_TLS) || !defined(_REENTRANT)
7972fe8fb19SBen Gras # define	get_arenas_map()	(arenas_map)
7982fe8fb19SBen Gras # define	set_arenas_map(x)	(arenas_map = x)
7992fe8fb19SBen Gras #else
800*0a6a1f1dSLionel Sambuc 
801*0a6a1f1dSLionel Sambuc static thread_key_t arenas_map_key = -1;
802*0a6a1f1dSLionel Sambuc 
803*0a6a1f1dSLionel Sambuc static inline arena_t **
get_arenas_map(void)804*0a6a1f1dSLionel Sambuc get_arenas_map(void)
805*0a6a1f1dSLionel Sambuc {
806*0a6a1f1dSLionel Sambuc 	if (!__isthreaded)
807*0a6a1f1dSLionel Sambuc 		return arenas_map;
808*0a6a1f1dSLionel Sambuc 
809*0a6a1f1dSLionel Sambuc 	if (arenas_map_key == -1) {
810*0a6a1f1dSLionel Sambuc 		(void)thr_keycreate(&arenas_map_key, NULL);
811*0a6a1f1dSLionel Sambuc 		if (arenas_map != NULL) {
812*0a6a1f1dSLionel Sambuc 			thr_setspecific(arenas_map_key, arenas_map);
813*0a6a1f1dSLionel Sambuc 			arenas_map = NULL;
814*0a6a1f1dSLionel Sambuc 		}
815*0a6a1f1dSLionel Sambuc 	}
816*0a6a1f1dSLionel Sambuc 
817*0a6a1f1dSLionel Sambuc 	return thr_getspecific(arenas_map_key);
818*0a6a1f1dSLionel Sambuc }
819*0a6a1f1dSLionel Sambuc 
820*0a6a1f1dSLionel Sambuc static __inline void
set_arenas_map(arena_t ** a)821*0a6a1f1dSLionel Sambuc set_arenas_map(arena_t **a)
822*0a6a1f1dSLionel Sambuc {
823*0a6a1f1dSLionel Sambuc 	if (!__isthreaded) {
824*0a6a1f1dSLionel Sambuc 		arenas_map = a;
825*0a6a1f1dSLionel Sambuc 		return;
826*0a6a1f1dSLionel Sambuc 	}
827*0a6a1f1dSLionel Sambuc 
828*0a6a1f1dSLionel Sambuc 	if (arenas_map_key == -1) {
829*0a6a1f1dSLionel Sambuc 		(void)thr_keycreate(&arenas_map_key, NULL);
830*0a6a1f1dSLionel Sambuc 		if (arenas_map != NULL) {
831*0a6a1f1dSLionel Sambuc 			_DIAGASSERT(arenas_map == a);
832*0a6a1f1dSLionel Sambuc 			arenas_map = NULL;
833*0a6a1f1dSLionel Sambuc 		}
834*0a6a1f1dSLionel Sambuc 	}
835*0a6a1f1dSLionel Sambuc 
836*0a6a1f1dSLionel Sambuc 	thr_setspecific(arenas_map_key, a);
837*0a6a1f1dSLionel Sambuc }
8382fe8fb19SBen Gras #endif
8392fe8fb19SBen Gras 
8402fe8fb19SBen Gras #ifdef MALLOC_STATS
8412fe8fb19SBen Gras /* Chunk statistics. */
8422fe8fb19SBen Gras static chunk_stats_t	stats_chunks;
8432fe8fb19SBen Gras #endif
8442fe8fb19SBen Gras 
8452fe8fb19SBen Gras /*******************************/
8462fe8fb19SBen Gras /*
8472fe8fb19SBen Gras  * Runtime configuration options.
8482fe8fb19SBen Gras  */
8492fe8fb19SBen Gras const char	*_malloc_options;
8502fe8fb19SBen Gras 
8512fe8fb19SBen Gras #ifndef MALLOC_PRODUCTION
8522fe8fb19SBen Gras static bool	opt_abort = true;
8532fe8fb19SBen Gras static bool	opt_junk = true;
8542fe8fb19SBen Gras #else
8552fe8fb19SBen Gras static bool	opt_abort = false;
8562fe8fb19SBen Gras static bool	opt_junk = false;
8572fe8fb19SBen Gras #endif
8582fe8fb19SBen Gras static bool	opt_hint = false;
8592fe8fb19SBen Gras static bool	opt_print_stats = false;
8602fe8fb19SBen Gras static int	opt_quantum_2pow = QUANTUM_2POW_MIN;
8612fe8fb19SBen Gras static int	opt_small_max_2pow = SMALL_MAX_2POW_DEFAULT;
8622fe8fb19SBen Gras static int	opt_chunk_2pow = CHUNK_2POW_DEFAULT;
8632fe8fb19SBen Gras static bool	opt_utrace = false;
8642fe8fb19SBen Gras static bool	opt_sysv = false;
8652fe8fb19SBen Gras static bool	opt_xmalloc = false;
8662fe8fb19SBen Gras static bool	opt_zero = false;
8672fe8fb19SBen Gras static int32_t	opt_narenas_lshift = 0;
8682fe8fb19SBen Gras 
8692fe8fb19SBen Gras typedef struct {
8702fe8fb19SBen Gras 	void	*p;
8712fe8fb19SBen Gras 	size_t	s;
8722fe8fb19SBen Gras 	void	*r;
8732fe8fb19SBen Gras } malloc_utrace_t;
8742fe8fb19SBen Gras 
8752fe8fb19SBen Gras #define	UTRACE(a, b, c)							\
8762fe8fb19SBen Gras 	if (opt_utrace) {						\
8772fe8fb19SBen Gras 		malloc_utrace_t ut;					\
8782fe8fb19SBen Gras 		ut.p = a;						\
8792fe8fb19SBen Gras 		ut.s = b;						\
8802fe8fb19SBen Gras 		ut.r = c;						\
8812fe8fb19SBen Gras 		xutrace(&ut, sizeof(ut));				\
8822fe8fb19SBen Gras 	}
8832fe8fb19SBen Gras 
8842fe8fb19SBen Gras /******************************************************************************/
8852fe8fb19SBen Gras /*
8862fe8fb19SBen Gras  * Begin function prototypes for non-inline static functions.
8872fe8fb19SBen Gras  */
8882fe8fb19SBen Gras 
8892fe8fb19SBen Gras static void	wrtmessage(const char *p1, const char *p2, const char *p3,
8902fe8fb19SBen Gras 		const char *p4);
8912fe8fb19SBen Gras #ifdef MALLOC_STATS
8922fe8fb19SBen Gras static void	malloc_printf(const char *format, ...);
8932fe8fb19SBen Gras #endif
894f14fb602SLionel Sambuc static char	*size_t2s(size_t x, char *s);
8952fe8fb19SBen Gras static bool	base_pages_alloc(size_t minsize);
8962fe8fb19SBen Gras static void	*base_alloc(size_t size);
8972fe8fb19SBen Gras static chunk_node_t *base_chunk_node_alloc(void);
8982fe8fb19SBen Gras static void	base_chunk_node_dealloc(chunk_node_t *node);
8992fe8fb19SBen Gras #ifdef MALLOC_STATS
9002fe8fb19SBen Gras static void	stats_print(arena_t *arena);
9012fe8fb19SBen Gras #endif
9022fe8fb19SBen Gras static void	*pages_map(void *addr, size_t size);
9032fe8fb19SBen Gras static void	*pages_map_align(void *addr, size_t size, int align);
9042fe8fb19SBen Gras static void	pages_unmap(void *addr, size_t size);
9052fe8fb19SBen Gras static void	*chunk_alloc(size_t size);
9062fe8fb19SBen Gras static void	chunk_dealloc(void *chunk, size_t size);
9072fe8fb19SBen Gras static void	arena_run_split(arena_t *arena, arena_run_t *run, size_t size);
9082fe8fb19SBen Gras static arena_chunk_t *arena_chunk_alloc(arena_t *arena);
9092fe8fb19SBen Gras static void	arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk);
9102fe8fb19SBen Gras static arena_run_t *arena_run_alloc(arena_t *arena, size_t size);
9112fe8fb19SBen Gras static void	arena_run_dalloc(arena_t *arena, arena_run_t *run, size_t size);
9122fe8fb19SBen Gras static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin);
9132fe8fb19SBen Gras static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin);
9142fe8fb19SBen Gras static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size);
9152fe8fb19SBen Gras static void	*arena_malloc(arena_t *arena, size_t size);
9162fe8fb19SBen Gras static void	*arena_palloc(arena_t *arena, size_t alignment, size_t size,
9172fe8fb19SBen Gras     size_t alloc_size);
9182fe8fb19SBen Gras static size_t	arena_salloc(const void *ptr);
9192fe8fb19SBen Gras static void	*arena_ralloc(void *ptr, size_t size, size_t oldsize);
9202fe8fb19SBen Gras static void	arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr);
9212fe8fb19SBen Gras static bool	arena_new(arena_t *arena);
9222fe8fb19SBen Gras static arena_t	*arenas_extend(unsigned ind);
9232fe8fb19SBen Gras static void	*huge_malloc(size_t size);
9242fe8fb19SBen Gras static void	*huge_palloc(size_t alignment, size_t size);
9252fe8fb19SBen Gras static void	*huge_ralloc(void *ptr, size_t size, size_t oldsize);
9262fe8fb19SBen Gras static void	huge_dalloc(void *ptr);
9272fe8fb19SBen Gras static void	*imalloc(size_t size);
9282fe8fb19SBen Gras static void	*ipalloc(size_t alignment, size_t size);
9292fe8fb19SBen Gras static void	*icalloc(size_t size);
9302fe8fb19SBen Gras static size_t	isalloc(const void *ptr);
9312fe8fb19SBen Gras static void	*iralloc(void *ptr, size_t size);
9322fe8fb19SBen Gras static void	idalloc(void *ptr);
9332fe8fb19SBen Gras static void	malloc_print_stats(void);
9342fe8fb19SBen Gras static bool	malloc_init_hard(void);
9352fe8fb19SBen Gras 
9362fe8fb19SBen Gras /*
9372fe8fb19SBen Gras  * End function prototypes.
9382fe8fb19SBen Gras  */
9392fe8fb19SBen Gras /******************************************************************************/
9402fe8fb19SBen Gras /*
9412fe8fb19SBen Gras  * Begin mutex.
9422fe8fb19SBen Gras  */
9432fe8fb19SBen Gras 
9442fe8fb19SBen Gras #ifdef __NetBSD__
9452fe8fb19SBen Gras #define	malloc_mutex_init(m)	mutex_init(m, NULL)
9462fe8fb19SBen Gras #define	malloc_mutex_lock(m)	mutex_lock(m)
9472fe8fb19SBen Gras #define	malloc_mutex_unlock(m)	mutex_unlock(m)
9482fe8fb19SBen Gras #else	/* __NetBSD__ */
9492fe8fb19SBen Gras static inline void
malloc_mutex_init(malloc_mutex_t * a_mutex)9502fe8fb19SBen Gras malloc_mutex_init(malloc_mutex_t *a_mutex)
9512fe8fb19SBen Gras {
9522fe8fb19SBen Gras 	static const spinlock_t lock = _SPINLOCK_INITIALIZER;
9532fe8fb19SBen Gras 
9542fe8fb19SBen Gras 	a_mutex->lock = lock;
9552fe8fb19SBen Gras }
9562fe8fb19SBen Gras 
9572fe8fb19SBen Gras static inline void
malloc_mutex_lock(malloc_mutex_t * a_mutex)9582fe8fb19SBen Gras malloc_mutex_lock(malloc_mutex_t *a_mutex)
9592fe8fb19SBen Gras {
9602fe8fb19SBen Gras 
9612fe8fb19SBen Gras 	if (__isthreaded)
9622fe8fb19SBen Gras 		_SPINLOCK(&a_mutex->lock);
9632fe8fb19SBen Gras }
9642fe8fb19SBen Gras 
9652fe8fb19SBen Gras static inline void
malloc_mutex_unlock(malloc_mutex_t * a_mutex)9662fe8fb19SBen Gras malloc_mutex_unlock(malloc_mutex_t *a_mutex)
9672fe8fb19SBen Gras {
9682fe8fb19SBen Gras 
9692fe8fb19SBen Gras 	if (__isthreaded)
9702fe8fb19SBen Gras 		_SPINUNLOCK(&a_mutex->lock);
9712fe8fb19SBen Gras }
9722fe8fb19SBen Gras #endif	/* __NetBSD__ */
9732fe8fb19SBen Gras 
9742fe8fb19SBen Gras /*
9752fe8fb19SBen Gras  * End mutex.
9762fe8fb19SBen Gras  */
9772fe8fb19SBen Gras /******************************************************************************/
9782fe8fb19SBen Gras /*
9792fe8fb19SBen Gras  * Begin Utility functions/macros.
9802fe8fb19SBen Gras  */
9812fe8fb19SBen Gras 
9822fe8fb19SBen Gras /* Return the chunk address for allocation address a. */
9832fe8fb19SBen Gras #define	CHUNK_ADDR2BASE(a)						\
9842fe8fb19SBen Gras 	((void *)((uintptr_t)(a) & ~chunksize_mask))
9852fe8fb19SBen Gras 
9862fe8fb19SBen Gras /* Return the chunk offset of address a. */
9872fe8fb19SBen Gras #define	CHUNK_ADDR2OFFSET(a)						\
9882fe8fb19SBen Gras 	((size_t)((uintptr_t)(a) & chunksize_mask))
9892fe8fb19SBen Gras 
9902fe8fb19SBen Gras /* Return the smallest chunk multiple that is >= s. */
9912fe8fb19SBen Gras #define	CHUNK_CEILING(s)						\
9922fe8fb19SBen Gras 	(((s) + chunksize_mask) & ~chunksize_mask)
9932fe8fb19SBen Gras 
9942fe8fb19SBen Gras /* Return the smallest cacheline multiple that is >= s. */
9952fe8fb19SBen Gras #define	CACHELINE_CEILING(s)						\
9962fe8fb19SBen Gras 	(((s) + (CACHELINE - 1)) & ~(CACHELINE - 1))
9972fe8fb19SBen Gras 
9982fe8fb19SBen Gras /* Return the smallest quantum multiple that is >= a. */
9992fe8fb19SBen Gras #define	QUANTUM_CEILING(a)						\
10002fe8fb19SBen Gras 	(((a) + quantum_mask) & ~quantum_mask)
10012fe8fb19SBen Gras 
10022fe8fb19SBen Gras /* Return the smallest pagesize multiple that is >= s. */
10032fe8fb19SBen Gras #define	PAGE_CEILING(s)							\
10042fe8fb19SBen Gras 	(((s) + pagesize_mask) & ~pagesize_mask)
10052fe8fb19SBen Gras 
10062fe8fb19SBen Gras /* Compute the smallest power of 2 that is >= x. */
10072fe8fb19SBen Gras static inline size_t
pow2_ceil(size_t x)10082fe8fb19SBen Gras pow2_ceil(size_t x)
10092fe8fb19SBen Gras {
10102fe8fb19SBen Gras 
10112fe8fb19SBen Gras 	x--;
10122fe8fb19SBen Gras 	x |= x >> 1;
10132fe8fb19SBen Gras 	x |= x >> 2;
10142fe8fb19SBen Gras 	x |= x >> 4;
10152fe8fb19SBen Gras 	x |= x >> 8;
10162fe8fb19SBen Gras 	x |= x >> 16;
10172fe8fb19SBen Gras #if (SIZEOF_PTR == 8)
10182fe8fb19SBen Gras 	x |= x >> 32;
10192fe8fb19SBen Gras #endif
10202fe8fb19SBen Gras 	x++;
10212fe8fb19SBen Gras 	return (x);
10222fe8fb19SBen Gras }
10232fe8fb19SBen Gras 
10242fe8fb19SBen Gras static void
wrtmessage(const char * p1,const char * p2,const char * p3,const char * p4)10252fe8fb19SBen Gras wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4)
10262fe8fb19SBen Gras {
10272fe8fb19SBen Gras 
10282fe8fb19SBen Gras 	write(STDERR_FILENO, p1, strlen(p1));
10292fe8fb19SBen Gras 	write(STDERR_FILENO, p2, strlen(p2));
10302fe8fb19SBen Gras 	write(STDERR_FILENO, p3, strlen(p3));
10312fe8fb19SBen Gras 	write(STDERR_FILENO, p4, strlen(p4));
10322fe8fb19SBen Gras }
10332fe8fb19SBen Gras 
10342fe8fb19SBen Gras void	(*_malloc_message)(const char *p1, const char *p2, const char *p3,
10352fe8fb19SBen Gras 	    const char *p4) = wrtmessage;
10362fe8fb19SBen Gras 
10372fe8fb19SBen Gras #ifdef MALLOC_STATS
10382fe8fb19SBen Gras /*
10392fe8fb19SBen Gras  * Print to stderr in such a way as to (hopefully) avoid memory allocation.
10402fe8fb19SBen Gras  */
10412fe8fb19SBen Gras static void
malloc_printf(const char * format,...)10422fe8fb19SBen Gras malloc_printf(const char *format, ...)
10432fe8fb19SBen Gras {
10442fe8fb19SBen Gras 	char buf[4096];
10452fe8fb19SBen Gras 	va_list ap;
10462fe8fb19SBen Gras 
10472fe8fb19SBen Gras 	va_start(ap, format);
10482fe8fb19SBen Gras 	vsnprintf(buf, sizeof(buf), format, ap);
10492fe8fb19SBen Gras 	va_end(ap);
10502fe8fb19SBen Gras 	_malloc_message(buf, "", "", "");
10512fe8fb19SBen Gras }
10522fe8fb19SBen Gras #endif
10532fe8fb19SBen Gras 
10542fe8fb19SBen Gras /*
10552fe8fb19SBen Gras  * We don't want to depend on vsnprintf() for production builds, since that can
1056f14fb602SLionel Sambuc  * cause unnecessary bloat for static binaries.  size_t2s() provides minimal
10572fe8fb19SBen Gras  * integer printing functionality, so that malloc_printf() use can be limited to
10582fe8fb19SBen Gras  * MALLOC_STATS code.
10592fe8fb19SBen Gras  */
10602fe8fb19SBen Gras #define UMAX2S_BUFSIZE	21
10612fe8fb19SBen Gras static char *
size_t2s(size_t x,char * s)1062f14fb602SLionel Sambuc size_t2s(size_t x, char *s)
10632fe8fb19SBen Gras {
10642fe8fb19SBen Gras 	unsigned i;
10652fe8fb19SBen Gras 
10662fe8fb19SBen Gras 	/* Make sure UMAX2S_BUFSIZE is large enough. */
10672fe8fb19SBen Gras 	/* LINTED */
1068f14fb602SLionel Sambuc 	assert(sizeof(size_t) <= 8);
10692fe8fb19SBen Gras 
10702fe8fb19SBen Gras 	i = UMAX2S_BUFSIZE - 1;
10712fe8fb19SBen Gras 	s[i] = '\0';
10722fe8fb19SBen Gras 	do {
10732fe8fb19SBen Gras 		i--;
10742fe8fb19SBen Gras 		s[i] = "0123456789"[(int)x % 10];
10752fe8fb19SBen Gras 		x /= (uintmax_t)10LL;
10762fe8fb19SBen Gras 	} while (x > 0);
10772fe8fb19SBen Gras 
10782fe8fb19SBen Gras 	return (&s[i]);
10792fe8fb19SBen Gras }
10802fe8fb19SBen Gras 
10812fe8fb19SBen Gras /******************************************************************************/
10822fe8fb19SBen Gras 
10832fe8fb19SBen Gras static bool
base_pages_alloc(size_t minsize)10842fe8fb19SBen Gras base_pages_alloc(size_t minsize)
10852fe8fb19SBen Gras {
10862fe8fb19SBen Gras 	size_t csize = 0;
10872fe8fb19SBen Gras 
10882fe8fb19SBen Gras #ifdef USE_BRK
10892fe8fb19SBen Gras 	/*
10902fe8fb19SBen Gras 	 * Do special brk allocation here, since base allocations don't need to
10912fe8fb19SBen Gras 	 * be chunk-aligned.
10922fe8fb19SBen Gras 	 */
10932fe8fb19SBen Gras 	if (brk_prev != (void *)-1) {
10942fe8fb19SBen Gras 		void *brk_cur;
10952fe8fb19SBen Gras 		intptr_t incr;
10962fe8fb19SBen Gras 
10972fe8fb19SBen Gras 		if (minsize != 0)
10982fe8fb19SBen Gras 			csize = CHUNK_CEILING(minsize);
10992fe8fb19SBen Gras 
11002fe8fb19SBen Gras 		malloc_mutex_lock(&brk_mtx);
11012fe8fb19SBen Gras 		do {
11022fe8fb19SBen Gras 			/* Get the current end of brk. */
11032fe8fb19SBen Gras 			brk_cur = sbrk(0);
11042fe8fb19SBen Gras 
11052fe8fb19SBen Gras 			/*
11062fe8fb19SBen Gras 			 * Calculate how much padding is necessary to
11072fe8fb19SBen Gras 			 * chunk-align the end of brk.  Don't worry about
11082fe8fb19SBen Gras 			 * brk_cur not being chunk-aligned though.
11092fe8fb19SBen Gras 			 */
11102fe8fb19SBen Gras 			incr = (intptr_t)chunksize
11112fe8fb19SBen Gras 			    - (intptr_t)CHUNK_ADDR2OFFSET(brk_cur);
11122fe8fb19SBen Gras 			assert(incr >= 0);
11132fe8fb19SBen Gras 			if ((size_t)incr < minsize)
11142fe8fb19SBen Gras 				incr += csize;
11152fe8fb19SBen Gras 
11162fe8fb19SBen Gras 			brk_prev = sbrk(incr);
11172fe8fb19SBen Gras 			if (brk_prev == brk_cur) {
11182fe8fb19SBen Gras 				/* Success. */
11192fe8fb19SBen Gras 				malloc_mutex_unlock(&brk_mtx);
11202fe8fb19SBen Gras 				base_pages = brk_cur;
11212fe8fb19SBen Gras 				base_next_addr = base_pages;
11222fe8fb19SBen Gras 				base_past_addr = (void *)((uintptr_t)base_pages
11232fe8fb19SBen Gras 				    + incr);
11242fe8fb19SBen Gras #ifdef MALLOC_STATS
11252fe8fb19SBen Gras 				base_mapped += incr;
11262fe8fb19SBen Gras #endif
11272fe8fb19SBen Gras 				return (false);
11282fe8fb19SBen Gras 			}
11292fe8fb19SBen Gras 		} while (brk_prev != (void *)-1);
11302fe8fb19SBen Gras 		malloc_mutex_unlock(&brk_mtx);
11312fe8fb19SBen Gras 	}
11322fe8fb19SBen Gras 	if (minsize == 0) {
11332fe8fb19SBen Gras 		/*
11342fe8fb19SBen Gras 		 * Failure during initialization doesn't matter, so avoid
11352fe8fb19SBen Gras 		 * falling through to the mmap-based page mapping code.
11362fe8fb19SBen Gras 		 */
11372fe8fb19SBen Gras 		return (true);
11382fe8fb19SBen Gras 	}
11392fe8fb19SBen Gras #endif
11402fe8fb19SBen Gras 	assert(minsize != 0);
11412fe8fb19SBen Gras 	csize = PAGE_CEILING(minsize);
11422fe8fb19SBen Gras 	base_pages = pages_map(NULL, csize);
11432fe8fb19SBen Gras 	if (base_pages == NULL)
11442fe8fb19SBen Gras 		return (true);
11452fe8fb19SBen Gras 	base_next_addr = base_pages;
11462fe8fb19SBen Gras 	base_past_addr = (void *)((uintptr_t)base_pages + csize);
11472fe8fb19SBen Gras #ifdef MALLOC_STATS
11482fe8fb19SBen Gras 	base_mapped += csize;
11492fe8fb19SBen Gras #endif
11502fe8fb19SBen Gras 	return (false);
11512fe8fb19SBen Gras }
11522fe8fb19SBen Gras 
11532fe8fb19SBen Gras static void *
base_alloc(size_t size)11542fe8fb19SBen Gras base_alloc(size_t size)
11552fe8fb19SBen Gras {
11562fe8fb19SBen Gras 	void *ret;
11572fe8fb19SBen Gras 	size_t csize;
11582fe8fb19SBen Gras 
11592fe8fb19SBen Gras 	/* Round size up to nearest multiple of the cacheline size. */
11602fe8fb19SBen Gras 	csize = CACHELINE_CEILING(size);
11612fe8fb19SBen Gras 
11622fe8fb19SBen Gras 	malloc_mutex_lock(&base_mtx);
11632fe8fb19SBen Gras 
11642fe8fb19SBen Gras 	/* Make sure there's enough space for the allocation. */
11652fe8fb19SBen Gras 	if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) {
11662fe8fb19SBen Gras 		if (base_pages_alloc(csize)) {
11672fe8fb19SBen Gras 			ret = NULL;
11682fe8fb19SBen Gras 			goto RETURN;
11692fe8fb19SBen Gras 		}
11702fe8fb19SBen Gras 	}
11712fe8fb19SBen Gras 
11722fe8fb19SBen Gras 	/* Allocate. */
11732fe8fb19SBen Gras 	ret = base_next_addr;
11742fe8fb19SBen Gras 	base_next_addr = (void *)((uintptr_t)base_next_addr + csize);
11752fe8fb19SBen Gras 
11762fe8fb19SBen Gras RETURN:
11772fe8fb19SBen Gras 	malloc_mutex_unlock(&base_mtx);
11782fe8fb19SBen Gras 	return (ret);
11792fe8fb19SBen Gras }
11802fe8fb19SBen Gras 
11812fe8fb19SBen Gras static chunk_node_t *
base_chunk_node_alloc(void)11822fe8fb19SBen Gras base_chunk_node_alloc(void)
11832fe8fb19SBen Gras {
11842fe8fb19SBen Gras 	chunk_node_t *ret;
11852fe8fb19SBen Gras 
11862fe8fb19SBen Gras 	malloc_mutex_lock(&base_mtx);
11872fe8fb19SBen Gras 	if (base_chunk_nodes != NULL) {
11882fe8fb19SBen Gras 		ret = base_chunk_nodes;
11892fe8fb19SBen Gras 		/* LINTED */
11902fe8fb19SBen Gras 		base_chunk_nodes = *(chunk_node_t **)ret;
11912fe8fb19SBen Gras 		malloc_mutex_unlock(&base_mtx);
11922fe8fb19SBen Gras 	} else {
11932fe8fb19SBen Gras 		malloc_mutex_unlock(&base_mtx);
11942fe8fb19SBen Gras 		ret = (chunk_node_t *)base_alloc(sizeof(chunk_node_t));
11952fe8fb19SBen Gras 	}
11962fe8fb19SBen Gras 
11972fe8fb19SBen Gras 	return (ret);
11982fe8fb19SBen Gras }
11992fe8fb19SBen Gras 
12002fe8fb19SBen Gras static void
base_chunk_node_dealloc(chunk_node_t * node)12012fe8fb19SBen Gras base_chunk_node_dealloc(chunk_node_t *node)
12022fe8fb19SBen Gras {
12032fe8fb19SBen Gras 
12042fe8fb19SBen Gras 	malloc_mutex_lock(&base_mtx);
12052fe8fb19SBen Gras 	/* LINTED */
12062fe8fb19SBen Gras 	*(chunk_node_t **)node = base_chunk_nodes;
12072fe8fb19SBen Gras 	base_chunk_nodes = node;
12082fe8fb19SBen Gras 	malloc_mutex_unlock(&base_mtx);
12092fe8fb19SBen Gras }
12102fe8fb19SBen Gras 
12112fe8fb19SBen Gras /******************************************************************************/
12122fe8fb19SBen Gras 
12132fe8fb19SBen Gras #ifdef MALLOC_STATS
12142fe8fb19SBen Gras static void
stats_print(arena_t * arena)12152fe8fb19SBen Gras stats_print(arena_t *arena)
12162fe8fb19SBen Gras {
12172fe8fb19SBen Gras 	unsigned i;
12182fe8fb19SBen Gras 	int gap_start;
12192fe8fb19SBen Gras 
12202fe8fb19SBen Gras 	malloc_printf(
12212fe8fb19SBen Gras 	    "          allocated/mapped            nmalloc      ndalloc\n");
12222fe8fb19SBen Gras 
12232fe8fb19SBen Gras 	malloc_printf("small: %12zu %-12s %12llu %12llu\n",
12242fe8fb19SBen Gras 	    arena->stats.allocated_small, "", arena->stats.nmalloc_small,
12252fe8fb19SBen Gras 	    arena->stats.ndalloc_small);
12262fe8fb19SBen Gras 	malloc_printf("large: %12zu %-12s %12llu %12llu\n",
12272fe8fb19SBen Gras 	    arena->stats.allocated_large, "", arena->stats.nmalloc_large,
12282fe8fb19SBen Gras 	    arena->stats.ndalloc_large);
12292fe8fb19SBen Gras 	malloc_printf("total: %12zu/%-12zu %12llu %12llu\n",
12302fe8fb19SBen Gras 	    arena->stats.allocated_small + arena->stats.allocated_large,
12312fe8fb19SBen Gras 	    arena->stats.mapped,
12322fe8fb19SBen Gras 	    arena->stats.nmalloc_small + arena->stats.nmalloc_large,
12332fe8fb19SBen Gras 	    arena->stats.ndalloc_small + arena->stats.ndalloc_large);
12342fe8fb19SBen Gras 
12352fe8fb19SBen Gras 	malloc_printf("bins:     bin   size regs pgs  requests   newruns"
12362fe8fb19SBen Gras 	    "    reruns maxruns curruns\n");
12372fe8fb19SBen Gras 	for (i = 0, gap_start = -1; i < ntbins + nqbins + nsbins; i++) {
12382fe8fb19SBen Gras 		if (arena->bins[i].stats.nrequests == 0) {
12392fe8fb19SBen Gras 			if (gap_start == -1)
12402fe8fb19SBen Gras 				gap_start = i;
12412fe8fb19SBen Gras 		} else {
12422fe8fb19SBen Gras 			if (gap_start != -1) {
12432fe8fb19SBen Gras 				if (i > gap_start + 1) {
12442fe8fb19SBen Gras 					/* Gap of more than one size class. */
12452fe8fb19SBen Gras 					malloc_printf("[%u..%u]\n",
12462fe8fb19SBen Gras 					    gap_start, i - 1);
12472fe8fb19SBen Gras 				} else {
12482fe8fb19SBen Gras 					/* Gap of one size class. */
12492fe8fb19SBen Gras 					malloc_printf("[%u]\n", gap_start);
12502fe8fb19SBen Gras 				}
12512fe8fb19SBen Gras 				gap_start = -1;
12522fe8fb19SBen Gras 			}
12532fe8fb19SBen Gras 			malloc_printf(
12542fe8fb19SBen Gras 			    "%13u %1s %4u %4u %3u %9llu %9llu"
12552fe8fb19SBen Gras 			    " %9llu %7lu %7lu\n",
12562fe8fb19SBen Gras 			    i,
12572fe8fb19SBen Gras 			    i < ntbins ? "T" : i < ntbins + nqbins ? "Q" : "S",
12582fe8fb19SBen Gras 			    arena->bins[i].reg_size,
12592fe8fb19SBen Gras 			    arena->bins[i].nregs,
12602fe8fb19SBen Gras 			    arena->bins[i].run_size >> pagesize_2pow,
12612fe8fb19SBen Gras 			    arena->bins[i].stats.nrequests,
12622fe8fb19SBen Gras 			    arena->bins[i].stats.nruns,
12632fe8fb19SBen Gras 			    arena->bins[i].stats.reruns,
12642fe8fb19SBen Gras 			    arena->bins[i].stats.highruns,
12652fe8fb19SBen Gras 			    arena->bins[i].stats.curruns);
12662fe8fb19SBen Gras 		}
12672fe8fb19SBen Gras 	}
12682fe8fb19SBen Gras 	if (gap_start != -1) {
12692fe8fb19SBen Gras 		if (i > gap_start + 1) {
12702fe8fb19SBen Gras 			/* Gap of more than one size class. */
12712fe8fb19SBen Gras 			malloc_printf("[%u..%u]\n", gap_start, i - 1);
12722fe8fb19SBen Gras 		} else {
12732fe8fb19SBen Gras 			/* Gap of one size class. */
12742fe8fb19SBen Gras 			malloc_printf("[%u]\n", gap_start);
12752fe8fb19SBen Gras 		}
12762fe8fb19SBen Gras 	}
12772fe8fb19SBen Gras }
12782fe8fb19SBen Gras #endif
12792fe8fb19SBen Gras 
12802fe8fb19SBen Gras /*
12812fe8fb19SBen Gras  * End Utility functions/macros.
12822fe8fb19SBen Gras  */
12832fe8fb19SBen Gras /******************************************************************************/
12842fe8fb19SBen Gras /*
12852fe8fb19SBen Gras  * Begin chunk management functions.
12862fe8fb19SBen Gras  */
12872fe8fb19SBen Gras 
12882fe8fb19SBen Gras #ifndef lint
12892fe8fb19SBen Gras static inline int
chunk_comp(chunk_node_t * a,chunk_node_t * b)12902fe8fb19SBen Gras chunk_comp(chunk_node_t *a, chunk_node_t *b)
12912fe8fb19SBen Gras {
12922fe8fb19SBen Gras 
12932fe8fb19SBen Gras 	assert(a != NULL);
12942fe8fb19SBen Gras 	assert(b != NULL);
12952fe8fb19SBen Gras 
12962fe8fb19SBen Gras 	if ((uintptr_t)a->chunk < (uintptr_t)b->chunk)
12972fe8fb19SBen Gras 		return (-1);
12982fe8fb19SBen Gras 	else if (a->chunk == b->chunk)
12992fe8fb19SBen Gras 		return (0);
13002fe8fb19SBen Gras 	else
13012fe8fb19SBen Gras 		return (1);
13022fe8fb19SBen Gras }
13032fe8fb19SBen Gras 
13042fe8fb19SBen Gras /* Generate red-black tree code for chunks. */
13052fe8fb19SBen Gras RB_GENERATE_STATIC(chunk_tree_s, chunk_node_s, link, chunk_comp);
13062fe8fb19SBen Gras #endif
13072fe8fb19SBen Gras 
13082fe8fb19SBen Gras static void *
pages_map_align(void * addr,size_t size,int align)13092fe8fb19SBen Gras pages_map_align(void *addr, size_t size, int align)
13102fe8fb19SBen Gras {
13112fe8fb19SBen Gras 	void *ret;
13122fe8fb19SBen Gras 
13132fe8fb19SBen Gras 	/*
13142fe8fb19SBen Gras 	 * We don't use MAP_FIXED here, because it can cause the *replacement*
13152fe8fb19SBen Gras 	 * of existing mappings, and we only want to create new mappings.
13162fe8fb19SBen Gras 	 */
13172fe8fb19SBen Gras 	ret = mmap(addr, size, PROT_READ | PROT_WRITE,
13182fe8fb19SBen Gras 	    MAP_PRIVATE | MAP_ANON | MAP_ALIGNED(align), -1, 0);
13192fe8fb19SBen Gras 	assert(ret != NULL);
13202fe8fb19SBen Gras 
13212fe8fb19SBen Gras 	if (ret == MAP_FAILED)
13222fe8fb19SBen Gras 		ret = NULL;
13232fe8fb19SBen Gras 	else if (addr != NULL && ret != addr) {
13242fe8fb19SBen Gras 		/*
13252fe8fb19SBen Gras 		 * We succeeded in mapping memory, but not in the right place.
13262fe8fb19SBen Gras 		 */
13272fe8fb19SBen Gras 		if (munmap(ret, size) == -1) {
13282fe8fb19SBen Gras 			char buf[STRERROR_BUF];
13292fe8fb19SBen Gras 
13302fe8fb19SBen Gras 			STRERROR_R(errno, buf, sizeof(buf));
13312fe8fb19SBen Gras 			_malloc_message(getprogname(),
13322fe8fb19SBen Gras 			    ": (malloc) Error in munmap(): ", buf, "\n");
13332fe8fb19SBen Gras 			if (opt_abort)
13342fe8fb19SBen Gras 				abort();
13352fe8fb19SBen Gras 		}
13362fe8fb19SBen Gras 		ret = NULL;
13372fe8fb19SBen Gras 	}
13382fe8fb19SBen Gras 
13392fe8fb19SBen Gras 	assert(ret == NULL || (addr == NULL && ret != addr)
13402fe8fb19SBen Gras 	    || (addr != NULL && ret == addr));
13412fe8fb19SBen Gras 	return (ret);
13422fe8fb19SBen Gras }
13432fe8fb19SBen Gras 
13442fe8fb19SBen Gras static void *
pages_map(void * addr,size_t size)13452fe8fb19SBen Gras pages_map(void *addr, size_t size)
13462fe8fb19SBen Gras {
13472fe8fb19SBen Gras 
13482fe8fb19SBen Gras 	return pages_map_align(addr, size, 0);
13492fe8fb19SBen Gras }
13502fe8fb19SBen Gras 
13512fe8fb19SBen Gras static void
pages_unmap(void * addr,size_t size)13522fe8fb19SBen Gras pages_unmap(void *addr, size_t size)
13532fe8fb19SBen Gras {
13542fe8fb19SBen Gras 
13552fe8fb19SBen Gras 	if (munmap(addr, size) == -1) {
13562fe8fb19SBen Gras 		char buf[STRERROR_BUF];
13572fe8fb19SBen Gras 
13582fe8fb19SBen Gras 		STRERROR_R(errno, buf, sizeof(buf));
13592fe8fb19SBen Gras 		_malloc_message(getprogname(),
13602fe8fb19SBen Gras 		    ": (malloc) Error in munmap(): ", buf, "\n");
13612fe8fb19SBen Gras 		if (opt_abort)
13622fe8fb19SBen Gras 			abort();
13632fe8fb19SBen Gras 	}
13642fe8fb19SBen Gras }
13652fe8fb19SBen Gras 
13662fe8fb19SBen Gras static void *
chunk_alloc(size_t size)13672fe8fb19SBen Gras chunk_alloc(size_t size)
13682fe8fb19SBen Gras {
13692fe8fb19SBen Gras 	void *ret, *chunk;
13702fe8fb19SBen Gras 	chunk_node_t *tchunk, *delchunk;
13712fe8fb19SBen Gras 
13722fe8fb19SBen Gras 	assert(size != 0);
13732fe8fb19SBen Gras 	assert((size & chunksize_mask) == 0);
13742fe8fb19SBen Gras 
13752fe8fb19SBen Gras 	malloc_mutex_lock(&chunks_mtx);
13762fe8fb19SBen Gras 
13772fe8fb19SBen Gras 	if (size == chunksize) {
13782fe8fb19SBen Gras 		/*
13792fe8fb19SBen Gras 		 * Check for address ranges that were previously chunks and try
13802fe8fb19SBen Gras 		 * to use them.
13812fe8fb19SBen Gras 		 */
13822fe8fb19SBen Gras 
13832fe8fb19SBen Gras 		/* LINTED */
13842fe8fb19SBen Gras 		tchunk = RB_MIN(chunk_tree_s, &old_chunks);
13852fe8fb19SBen Gras 		while (tchunk != NULL) {
13862fe8fb19SBen Gras 			/* Found an address range.  Try to recycle it. */
13872fe8fb19SBen Gras 
13882fe8fb19SBen Gras 			chunk = tchunk->chunk;
13892fe8fb19SBen Gras 			delchunk = tchunk;
13902fe8fb19SBen Gras 			/* LINTED */
13912fe8fb19SBen Gras 			tchunk = RB_NEXT(chunk_tree_s, &old_chunks, delchunk);
13922fe8fb19SBen Gras 
13932fe8fb19SBen Gras 			/* Remove delchunk from the tree. */
13942fe8fb19SBen Gras 			/* LINTED */
13952fe8fb19SBen Gras 			RB_REMOVE(chunk_tree_s, &old_chunks, delchunk);
13962fe8fb19SBen Gras 			base_chunk_node_dealloc(delchunk);
13972fe8fb19SBen Gras 
13982fe8fb19SBen Gras #ifdef USE_BRK
13992fe8fb19SBen Gras 			if ((uintptr_t)chunk >= (uintptr_t)brk_base
14002fe8fb19SBen Gras 			    && (uintptr_t)chunk < (uintptr_t)brk_max) {
14012fe8fb19SBen Gras 				/* Re-use a previously freed brk chunk. */
14022fe8fb19SBen Gras 				ret = chunk;
14032fe8fb19SBen Gras 				goto RETURN;
14042fe8fb19SBen Gras 			}
14052fe8fb19SBen Gras #endif
14062fe8fb19SBen Gras 			if ((ret = pages_map(chunk, size)) != NULL) {
14072fe8fb19SBen Gras 				/* Success. */
14082fe8fb19SBen Gras 				goto RETURN;
14092fe8fb19SBen Gras 			}
14102fe8fb19SBen Gras 		}
14112fe8fb19SBen Gras 	}
14122fe8fb19SBen Gras 
14132fe8fb19SBen Gras 	/*
14142fe8fb19SBen Gras 	 * Try to over-allocate, but allow the OS to place the allocation
14152fe8fb19SBen Gras 	 * anywhere.  Beware of size_t wrap-around.
14162fe8fb19SBen Gras 	 */
14172fe8fb19SBen Gras 	if (size + chunksize > size) {
14182fe8fb19SBen Gras 		if ((ret = pages_map_align(NULL, size, chunksize_2pow))
14192fe8fb19SBen Gras 		    != NULL) {
14202fe8fb19SBen Gras 			goto RETURN;
14212fe8fb19SBen Gras 		}
14222fe8fb19SBen Gras 	}
14232fe8fb19SBen Gras 
14242fe8fb19SBen Gras #ifdef USE_BRK
14252fe8fb19SBen Gras 	/*
14262fe8fb19SBen Gras 	 * Try to create allocations in brk, in order to make full use of
14272fe8fb19SBen Gras 	 * limited address space.
14282fe8fb19SBen Gras 	 */
14292fe8fb19SBen Gras 	if (brk_prev != (void *)-1) {
14302fe8fb19SBen Gras 		void *brk_cur;
14312fe8fb19SBen Gras 		intptr_t incr;
14322fe8fb19SBen Gras 
14332fe8fb19SBen Gras 		/*
14342fe8fb19SBen Gras 		 * The loop is necessary to recover from races with other
14352fe8fb19SBen Gras 		 * threads that are using brk for something other than malloc.
14362fe8fb19SBen Gras 		 */
14372fe8fb19SBen Gras 		malloc_mutex_lock(&brk_mtx);
14382fe8fb19SBen Gras 		do {
14392fe8fb19SBen Gras 			/* Get the current end of brk. */
14402fe8fb19SBen Gras 			brk_cur = sbrk(0);
14412fe8fb19SBen Gras 
14422fe8fb19SBen Gras 			/*
14432fe8fb19SBen Gras 			 * Calculate how much padding is necessary to
14442fe8fb19SBen Gras 			 * chunk-align the end of brk.
14452fe8fb19SBen Gras 			 */
14462fe8fb19SBen Gras 			incr = (intptr_t)size
14472fe8fb19SBen Gras 			    - (intptr_t)CHUNK_ADDR2OFFSET(brk_cur);
14482fe8fb19SBen Gras 			if (incr == (intptr_t)size) {
14492fe8fb19SBen Gras 				ret = brk_cur;
14502fe8fb19SBen Gras 			} else {
14512fe8fb19SBen Gras 				ret = (void *)((intptr_t)brk_cur + incr);
14522fe8fb19SBen Gras 				incr += size;
14532fe8fb19SBen Gras 			}
14542fe8fb19SBen Gras 
14552fe8fb19SBen Gras 			brk_prev = sbrk(incr);
14562fe8fb19SBen Gras 			if (brk_prev == brk_cur) {
14572fe8fb19SBen Gras 				/* Success. */
14582fe8fb19SBen Gras 				malloc_mutex_unlock(&brk_mtx);
14592fe8fb19SBen Gras 				brk_max = (void *)((intptr_t)ret + size);
14602fe8fb19SBen Gras 				goto RETURN;
14612fe8fb19SBen Gras 			}
14622fe8fb19SBen Gras 		} while (brk_prev != (void *)-1);
14632fe8fb19SBen Gras 		malloc_mutex_unlock(&brk_mtx);
14642fe8fb19SBen Gras 	}
14652fe8fb19SBen Gras #endif
14662fe8fb19SBen Gras 
14672fe8fb19SBen Gras 	/* All strategies for allocation failed. */
14682fe8fb19SBen Gras 	ret = NULL;
14692fe8fb19SBen Gras RETURN:
14702fe8fb19SBen Gras 	if (ret != NULL) {
14712fe8fb19SBen Gras 		chunk_node_t key;
14722fe8fb19SBen Gras 		/*
14732fe8fb19SBen Gras 		 * Clean out any entries in old_chunks that overlap with the
14742fe8fb19SBen Gras 		 * memory we just allocated.
14752fe8fb19SBen Gras 		 */
14762fe8fb19SBen Gras 		key.chunk = ret;
14772fe8fb19SBen Gras 		/* LINTED */
14782fe8fb19SBen Gras 		tchunk = RB_NFIND(chunk_tree_s, &old_chunks, &key);
14792fe8fb19SBen Gras 		while (tchunk != NULL
14802fe8fb19SBen Gras 		    && (uintptr_t)tchunk->chunk >= (uintptr_t)ret
14812fe8fb19SBen Gras 		    && (uintptr_t)tchunk->chunk < (uintptr_t)ret + size) {
14822fe8fb19SBen Gras 			delchunk = tchunk;
14832fe8fb19SBen Gras 			/* LINTED */
14842fe8fb19SBen Gras 			tchunk = RB_NEXT(chunk_tree_s, &old_chunks, delchunk);
14852fe8fb19SBen Gras 			/* LINTED */
14862fe8fb19SBen Gras 			RB_REMOVE(chunk_tree_s, &old_chunks, delchunk);
14872fe8fb19SBen Gras 			base_chunk_node_dealloc(delchunk);
14882fe8fb19SBen Gras 		}
14892fe8fb19SBen Gras 
14902fe8fb19SBen Gras 	}
14912fe8fb19SBen Gras #ifdef MALLOC_STATS
14922fe8fb19SBen Gras 	if (ret != NULL) {
14932fe8fb19SBen Gras 		stats_chunks.nchunks += (size / chunksize);
14942fe8fb19SBen Gras 		stats_chunks.curchunks += (size / chunksize);
14952fe8fb19SBen Gras 	}
14962fe8fb19SBen Gras 	if (stats_chunks.curchunks > stats_chunks.highchunks)
14972fe8fb19SBen Gras 		stats_chunks.highchunks = stats_chunks.curchunks;
14982fe8fb19SBen Gras #endif
14992fe8fb19SBen Gras 	malloc_mutex_unlock(&chunks_mtx);
15002fe8fb19SBen Gras 
15012fe8fb19SBen Gras 	assert(CHUNK_ADDR2BASE(ret) == ret);
15022fe8fb19SBen Gras 	return (ret);
15032fe8fb19SBen Gras }
15042fe8fb19SBen Gras 
15052fe8fb19SBen Gras static void
chunk_dealloc(void * chunk,size_t size)15062fe8fb19SBen Gras chunk_dealloc(void *chunk, size_t size)
15072fe8fb19SBen Gras {
15082fe8fb19SBen Gras 	chunk_node_t *node;
15092fe8fb19SBen Gras 
15102fe8fb19SBen Gras 	assert(chunk != NULL);
15112fe8fb19SBen Gras 	assert(CHUNK_ADDR2BASE(chunk) == chunk);
15122fe8fb19SBen Gras 	assert(size != 0);
15132fe8fb19SBen Gras 	assert((size & chunksize_mask) == 0);
15142fe8fb19SBen Gras 
15152fe8fb19SBen Gras 	malloc_mutex_lock(&chunks_mtx);
15162fe8fb19SBen Gras 
15172fe8fb19SBen Gras #ifdef USE_BRK
15182fe8fb19SBen Gras 	if ((uintptr_t)chunk >= (uintptr_t)brk_base
15192fe8fb19SBen Gras 	    && (uintptr_t)chunk < (uintptr_t)brk_max) {
15202fe8fb19SBen Gras 		void *brk_cur;
15212fe8fb19SBen Gras 
15222fe8fb19SBen Gras 		malloc_mutex_lock(&brk_mtx);
15232fe8fb19SBen Gras 		/* Get the current end of brk. */
15242fe8fb19SBen Gras 		brk_cur = sbrk(0);
15252fe8fb19SBen Gras 
15262fe8fb19SBen Gras 		/*
15272fe8fb19SBen Gras 		 * Try to shrink the data segment if this chunk is at the end
15282fe8fb19SBen Gras 		 * of the data segment.  The sbrk() call here is subject to a
15292fe8fb19SBen Gras 		 * race condition with threads that use brk(2) or sbrk(2)
15302fe8fb19SBen Gras 		 * directly, but the alternative would be to leak memory for
15312fe8fb19SBen Gras 		 * the sake of poorly designed multi-threaded programs.
15322fe8fb19SBen Gras 		 */
15332fe8fb19SBen Gras 		if (brk_cur == brk_max
15342fe8fb19SBen Gras 		    && (void *)((uintptr_t)chunk + size) == brk_max
15352fe8fb19SBen Gras 		    && sbrk(-(intptr_t)size) == brk_max) {
15362fe8fb19SBen Gras 			malloc_mutex_unlock(&brk_mtx);
15372fe8fb19SBen Gras 			if (brk_prev == brk_max) {
15382fe8fb19SBen Gras 				/* Success. */
15392fe8fb19SBen Gras 				brk_prev = (void *)((intptr_t)brk_max
15402fe8fb19SBen Gras 				    - (intptr_t)size);
15412fe8fb19SBen Gras 				brk_max = brk_prev;
15422fe8fb19SBen Gras 			}
15432fe8fb19SBen Gras 		} else {
15442fe8fb19SBen Gras 			size_t offset;
15452fe8fb19SBen Gras 
15462fe8fb19SBen Gras 			malloc_mutex_unlock(&brk_mtx);
15472fe8fb19SBen Gras 			madvise(chunk, size, MADV_FREE);
15482fe8fb19SBen Gras 
15492fe8fb19SBen Gras 			/*
15502fe8fb19SBen Gras 			 * Iteratively create records of each chunk-sized
15512fe8fb19SBen Gras 			 * memory region that 'chunk' is comprised of, so that
15522fe8fb19SBen Gras 			 * the address range can be recycled if memory usage
15532fe8fb19SBen Gras 			 * increases later on.
15542fe8fb19SBen Gras 			 */
15552fe8fb19SBen Gras 			for (offset = 0; offset < size; offset += chunksize) {
15562fe8fb19SBen Gras 				node = base_chunk_node_alloc();
15572fe8fb19SBen Gras 				if (node == NULL)
15582fe8fb19SBen Gras 					break;
15592fe8fb19SBen Gras 
15602fe8fb19SBen Gras 				node->chunk = (void *)((uintptr_t)chunk
15612fe8fb19SBen Gras 				    + (uintptr_t)offset);
15622fe8fb19SBen Gras 				node->size = chunksize;
15632fe8fb19SBen Gras 				/* LINTED */
15642fe8fb19SBen Gras 				RB_INSERT(chunk_tree_s, &old_chunks, node);
15652fe8fb19SBen Gras 			}
15662fe8fb19SBen Gras 		}
15672fe8fb19SBen Gras 	} else {
15682fe8fb19SBen Gras #endif
15692fe8fb19SBen Gras 		pages_unmap(chunk, size);
15702fe8fb19SBen Gras 
15712fe8fb19SBen Gras 		/*
15722fe8fb19SBen Gras 		 * Make a record of the chunk's address, so that the address
15732fe8fb19SBen Gras 		 * range can be recycled if memory usage increases later on.
15742fe8fb19SBen Gras 		 * Don't bother to create entries if (size > chunksize), since
15752fe8fb19SBen Gras 		 * doing so could cause scalability issues for truly gargantuan
15762fe8fb19SBen Gras 		 * objects (many gigabytes or larger).
15772fe8fb19SBen Gras 		 */
15782fe8fb19SBen Gras 		if (size == chunksize) {
15792fe8fb19SBen Gras 			node = base_chunk_node_alloc();
15802fe8fb19SBen Gras 			if (node != NULL) {
15812fe8fb19SBen Gras 				node->chunk = (void *)(uintptr_t)chunk;
15822fe8fb19SBen Gras 				node->size = chunksize;
15832fe8fb19SBen Gras 				/* LINTED */
15842fe8fb19SBen Gras 				RB_INSERT(chunk_tree_s, &old_chunks, node);
15852fe8fb19SBen Gras 			}
15862fe8fb19SBen Gras 		}
15872fe8fb19SBen Gras #ifdef USE_BRK
15882fe8fb19SBen Gras 	}
15892fe8fb19SBen Gras #endif
15902fe8fb19SBen Gras 
15912fe8fb19SBen Gras #ifdef MALLOC_STATS
15922fe8fb19SBen Gras 	stats_chunks.curchunks -= (size / chunksize);
15932fe8fb19SBen Gras #endif
15942fe8fb19SBen Gras 	malloc_mutex_unlock(&chunks_mtx);
15952fe8fb19SBen Gras }
15962fe8fb19SBen Gras 
15972fe8fb19SBen Gras /*
15982fe8fb19SBen Gras  * End chunk management functions.
15992fe8fb19SBen Gras  */
16002fe8fb19SBen Gras /******************************************************************************/
16012fe8fb19SBen Gras /*
16022fe8fb19SBen Gras  * Begin arena.
16032fe8fb19SBen Gras  */
16042fe8fb19SBen Gras 
16052fe8fb19SBen Gras /*
16062fe8fb19SBen Gras  * Choose an arena based on a per-thread and (optimistically) per-CPU value.
16072fe8fb19SBen Gras  *
16082fe8fb19SBen Gras  * We maintain at least one block of arenas.  Usually there are more.
16092fe8fb19SBen Gras  * The blocks are $ncpu arenas in size.  Whole blocks are 'hashed'
16102fe8fb19SBen Gras  * amongst threads.  To accomplish this, next_arena advances only in
16112fe8fb19SBen Gras  * ncpu steps.
16122fe8fb19SBen Gras  */
16132fe8fb19SBen Gras static __noinline arena_t *
choose_arena_hard(void)16142fe8fb19SBen Gras choose_arena_hard(void)
16152fe8fb19SBen Gras {
16162fe8fb19SBen Gras 	unsigned i, curcpu;
16172fe8fb19SBen Gras 	arena_t **map;
16182fe8fb19SBen Gras 
16192fe8fb19SBen Gras 	/* Initialize the current block of arenas and advance to next. */
16202fe8fb19SBen Gras 	malloc_mutex_lock(&arenas_mtx);
16212fe8fb19SBen Gras 	assert(next_arena % ncpus == 0);
16222fe8fb19SBen Gras 	assert(narenas % ncpus == 0);
16232fe8fb19SBen Gras 	map = &arenas[next_arena];
16242fe8fb19SBen Gras 	set_arenas_map(map);
16252fe8fb19SBen Gras 	for (i = 0; i < ncpus; i++) {
16262fe8fb19SBen Gras 		if (arenas[next_arena] == NULL)
16272fe8fb19SBen Gras 			arenas_extend(next_arena);
16282fe8fb19SBen Gras 		next_arena = (next_arena + 1) % narenas;
16292fe8fb19SBen Gras 	}
16302fe8fb19SBen Gras 	malloc_mutex_unlock(&arenas_mtx);
16312fe8fb19SBen Gras 
16322fe8fb19SBen Gras 	/*
16332fe8fb19SBen Gras 	 * If we were unable to allocate an arena above, then default to
16342fe8fb19SBen Gras 	 * the first arena, which is always present.
16352fe8fb19SBen Gras 	 */
16362fe8fb19SBen Gras 	curcpu = thr_curcpu();
16372fe8fb19SBen Gras 	if (map[curcpu] != NULL)
16382fe8fb19SBen Gras 		return map[curcpu];
16392fe8fb19SBen Gras 	return arenas[0];
16402fe8fb19SBen Gras }
16412fe8fb19SBen Gras 
16422fe8fb19SBen Gras static inline arena_t *
choose_arena(void)16432fe8fb19SBen Gras choose_arena(void)
16442fe8fb19SBen Gras {
16452fe8fb19SBen Gras 	unsigned curcpu;
16462fe8fb19SBen Gras 	arena_t **map;
16472fe8fb19SBen Gras 
16482fe8fb19SBen Gras 	map = get_arenas_map();
16492fe8fb19SBen Gras 	curcpu = thr_curcpu();
16502fe8fb19SBen Gras 	if (__predict_true(map != NULL && map[curcpu] != NULL))
16512fe8fb19SBen Gras 		return map[curcpu];
16522fe8fb19SBen Gras 
16532fe8fb19SBen Gras         return choose_arena_hard();
16542fe8fb19SBen Gras }
16552fe8fb19SBen Gras 
16562fe8fb19SBen Gras #ifndef lint
16572fe8fb19SBen Gras static inline int
arena_chunk_comp(arena_chunk_t * a,arena_chunk_t * b)16582fe8fb19SBen Gras arena_chunk_comp(arena_chunk_t *a, arena_chunk_t *b)
16592fe8fb19SBen Gras {
16602fe8fb19SBen Gras 
16612fe8fb19SBen Gras 	assert(a != NULL);
16622fe8fb19SBen Gras 	assert(b != NULL);
16632fe8fb19SBen Gras 
16642fe8fb19SBen Gras 	if ((uintptr_t)a < (uintptr_t)b)
16652fe8fb19SBen Gras 		return (-1);
16662fe8fb19SBen Gras 	else if (a == b)
16672fe8fb19SBen Gras 		return (0);
16682fe8fb19SBen Gras 	else
16692fe8fb19SBen Gras 		return (1);
16702fe8fb19SBen Gras }
16712fe8fb19SBen Gras 
16722fe8fb19SBen Gras /* Generate red-black tree code for arena chunks. */
16732fe8fb19SBen Gras RB_GENERATE_STATIC(arena_chunk_tree_s, arena_chunk_s, link, arena_chunk_comp);
16742fe8fb19SBen Gras #endif
16752fe8fb19SBen Gras 
16762fe8fb19SBen Gras #ifndef lint
16772fe8fb19SBen Gras static inline int
arena_run_comp(arena_run_t * a,arena_run_t * b)16782fe8fb19SBen Gras arena_run_comp(arena_run_t *a, arena_run_t *b)
16792fe8fb19SBen Gras {
16802fe8fb19SBen Gras 
16812fe8fb19SBen Gras 	assert(a != NULL);
16822fe8fb19SBen Gras 	assert(b != NULL);
16832fe8fb19SBen Gras 
16842fe8fb19SBen Gras 	if ((uintptr_t)a < (uintptr_t)b)
16852fe8fb19SBen Gras 		return (-1);
16862fe8fb19SBen Gras 	else if (a == b)
16872fe8fb19SBen Gras 		return (0);
16882fe8fb19SBen Gras 	else
16892fe8fb19SBen Gras 		return (1);
16902fe8fb19SBen Gras }
16912fe8fb19SBen Gras 
16922fe8fb19SBen Gras /* Generate red-black tree code for arena runs. */
16932fe8fb19SBen Gras RB_GENERATE_STATIC(arena_run_tree_s, arena_run_s, link, arena_run_comp);
16942fe8fb19SBen Gras #endif
16952fe8fb19SBen Gras 
16962fe8fb19SBen Gras static inline void *
arena_run_reg_alloc(arena_run_t * run,arena_bin_t * bin)16972fe8fb19SBen Gras arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin)
16982fe8fb19SBen Gras {
16992fe8fb19SBen Gras 	void *ret;
17002fe8fb19SBen Gras 	unsigned i, mask, bit, regind;
17012fe8fb19SBen Gras 
17022fe8fb19SBen Gras 	assert(run->magic == ARENA_RUN_MAGIC);
17032fe8fb19SBen Gras 	assert(run->regs_minelm < bin->regs_mask_nelms);
17042fe8fb19SBen Gras 
17052fe8fb19SBen Gras 	/*
17062fe8fb19SBen Gras 	 * Move the first check outside the loop, so that run->regs_minelm can
17072fe8fb19SBen Gras 	 * be updated unconditionally, without the possibility of updating it
17082fe8fb19SBen Gras 	 * multiple times.
17092fe8fb19SBen Gras 	 */
17102fe8fb19SBen Gras 	i = run->regs_minelm;
17112fe8fb19SBen Gras 	mask = run->regs_mask[i];
17122fe8fb19SBen Gras 	if (mask != 0) {
17132fe8fb19SBen Gras 		/* Usable allocation found. */
17142fe8fb19SBen Gras 		bit = ffs((int)mask) - 1;
17152fe8fb19SBen Gras 
17162fe8fb19SBen Gras 		regind = ((i << (SIZEOF_INT_2POW + 3)) + bit);
17172fe8fb19SBen Gras 		ret = (void *)(((uintptr_t)run) + bin->reg0_offset
17182fe8fb19SBen Gras 		    + (bin->reg_size * regind));
17192fe8fb19SBen Gras 
17202fe8fb19SBen Gras 		/* Clear bit. */
17212fe8fb19SBen Gras 		mask ^= (1 << bit);
17222fe8fb19SBen Gras 		run->regs_mask[i] = mask;
17232fe8fb19SBen Gras 
17242fe8fb19SBen Gras 		return (ret);
17252fe8fb19SBen Gras 	}
17262fe8fb19SBen Gras 
17272fe8fb19SBen Gras 	for (i++; i < bin->regs_mask_nelms; i++) {
17282fe8fb19SBen Gras 		mask = run->regs_mask[i];
17292fe8fb19SBen Gras 		if (mask != 0) {
17302fe8fb19SBen Gras 			/* Usable allocation found. */
17312fe8fb19SBen Gras 			bit = ffs((int)mask) - 1;
17322fe8fb19SBen Gras 
17332fe8fb19SBen Gras 			regind = ((i << (SIZEOF_INT_2POW + 3)) + bit);
17342fe8fb19SBen Gras 			ret = (void *)(((uintptr_t)run) + bin->reg0_offset
17352fe8fb19SBen Gras 			    + (bin->reg_size * regind));
17362fe8fb19SBen Gras 
17372fe8fb19SBen Gras 			/* Clear bit. */
17382fe8fb19SBen Gras 			mask ^= (1 << bit);
17392fe8fb19SBen Gras 			run->regs_mask[i] = mask;
17402fe8fb19SBen Gras 
17412fe8fb19SBen Gras 			/*
17422fe8fb19SBen Gras 			 * Make a note that nothing before this element
17432fe8fb19SBen Gras 			 * contains a free region.
17442fe8fb19SBen Gras 			 */
17452fe8fb19SBen Gras 			run->regs_minelm = i; /* Low payoff: + (mask == 0); */
17462fe8fb19SBen Gras 
17472fe8fb19SBen Gras 			return (ret);
17482fe8fb19SBen Gras 		}
17492fe8fb19SBen Gras 	}
17502fe8fb19SBen Gras 	/* Not reached. */
17512fe8fb19SBen Gras 	/* LINTED */
17522fe8fb19SBen Gras 	assert(0);
17532fe8fb19SBen Gras 	return (NULL);
17542fe8fb19SBen Gras }
17552fe8fb19SBen Gras 
17562fe8fb19SBen Gras static inline void
arena_run_reg_dalloc(arena_run_t * run,arena_bin_t * bin,void * ptr,size_t size)17572fe8fb19SBen Gras arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size)
17582fe8fb19SBen Gras {
17592fe8fb19SBen Gras 	/*
17602fe8fb19SBen Gras 	 * To divide by a number D that is not a power of two we multiply
17612fe8fb19SBen Gras 	 * by (2^21 / D) and then right shift by 21 positions.
17622fe8fb19SBen Gras 	 *
17632fe8fb19SBen Gras 	 *   X / D
17642fe8fb19SBen Gras 	 *
17652fe8fb19SBen Gras 	 * becomes
17662fe8fb19SBen Gras 	 *
17672fe8fb19SBen Gras 	 *   (X * size_invs[(D >> QUANTUM_2POW_MIN) - 3]) >> SIZE_INV_SHIFT
17682fe8fb19SBen Gras 	 */
17692fe8fb19SBen Gras #define SIZE_INV_SHIFT 21
17702fe8fb19SBen Gras #define SIZE_INV(s) (((1 << SIZE_INV_SHIFT) / (s << QUANTUM_2POW_MIN)) + 1)
17712fe8fb19SBen Gras 	static const unsigned size_invs[] = {
17722fe8fb19SBen Gras 	    SIZE_INV(3),
17732fe8fb19SBen Gras 	    SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7),
17742fe8fb19SBen Gras 	    SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11),
17752fe8fb19SBen Gras 	    SIZE_INV(12),SIZE_INV(13), SIZE_INV(14), SIZE_INV(15),
17762fe8fb19SBen Gras 	    SIZE_INV(16),SIZE_INV(17), SIZE_INV(18), SIZE_INV(19),
17772fe8fb19SBen Gras 	    SIZE_INV(20),SIZE_INV(21), SIZE_INV(22), SIZE_INV(23),
17782fe8fb19SBen Gras 	    SIZE_INV(24),SIZE_INV(25), SIZE_INV(26), SIZE_INV(27),
17792fe8fb19SBen Gras 	    SIZE_INV(28),SIZE_INV(29), SIZE_INV(30), SIZE_INV(31)
17802fe8fb19SBen Gras #if (QUANTUM_2POW_MIN < 4)
17812fe8fb19SBen Gras 	    ,
17822fe8fb19SBen Gras 	    SIZE_INV(32), SIZE_INV(33), SIZE_INV(34), SIZE_INV(35),
17832fe8fb19SBen Gras 	    SIZE_INV(36), SIZE_INV(37), SIZE_INV(38), SIZE_INV(39),
17842fe8fb19SBen Gras 	    SIZE_INV(40), SIZE_INV(41), SIZE_INV(42), SIZE_INV(43),
17852fe8fb19SBen Gras 	    SIZE_INV(44), SIZE_INV(45), SIZE_INV(46), SIZE_INV(47),
17862fe8fb19SBen Gras 	    SIZE_INV(48), SIZE_INV(49), SIZE_INV(50), SIZE_INV(51),
17872fe8fb19SBen Gras 	    SIZE_INV(52), SIZE_INV(53), SIZE_INV(54), SIZE_INV(55),
17882fe8fb19SBen Gras 	    SIZE_INV(56), SIZE_INV(57), SIZE_INV(58), SIZE_INV(59),
17892fe8fb19SBen Gras 	    SIZE_INV(60), SIZE_INV(61), SIZE_INV(62), SIZE_INV(63)
17902fe8fb19SBen Gras #endif
17912fe8fb19SBen Gras 	};
17922fe8fb19SBen Gras 	unsigned diff, regind, elm, bit;
17932fe8fb19SBen Gras 
17942fe8fb19SBen Gras 	/* LINTED */
17952fe8fb19SBen Gras 	assert(run->magic == ARENA_RUN_MAGIC);
17962fe8fb19SBen Gras 	assert(((sizeof(size_invs)) / sizeof(unsigned)) + 3
17972fe8fb19SBen Gras 	    >= (SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN));
17982fe8fb19SBen Gras 
17992fe8fb19SBen Gras 	/*
18002fe8fb19SBen Gras 	 * Avoid doing division with a variable divisor if possible.  Using
18012fe8fb19SBen Gras 	 * actual division here can reduce allocator throughput by over 20%!
18022fe8fb19SBen Gras 	 */
18032fe8fb19SBen Gras 	diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset);
18042fe8fb19SBen Gras 	if ((size & (size - 1)) == 0) {
18052fe8fb19SBen Gras 		/*
18062fe8fb19SBen Gras 		 * log2_table allows fast division of a power of two in the
18072fe8fb19SBen Gras 		 * [1..128] range.
18082fe8fb19SBen Gras 		 *
18092fe8fb19SBen Gras 		 * (x / divisor) becomes (x >> log2_table[divisor - 1]).
18102fe8fb19SBen Gras 		 */
18112fe8fb19SBen Gras 		static const unsigned char log2_table[] = {
18122fe8fb19SBen Gras 		    0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4,
18132fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
18142fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18152fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
18162fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18172fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18182fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18192fe8fb19SBen Gras 		    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7
18202fe8fb19SBen Gras 		};
18212fe8fb19SBen Gras 
18222fe8fb19SBen Gras 		if (size <= 128)
18232fe8fb19SBen Gras 			regind = (diff >> log2_table[size - 1]);
18242fe8fb19SBen Gras 		else if (size <= 32768)
18252fe8fb19SBen Gras 			regind = diff >> (8 + log2_table[(size >> 8) - 1]);
18262fe8fb19SBen Gras 		else {
18272fe8fb19SBen Gras 			/*
18282fe8fb19SBen Gras 			 * The page size is too large for us to use the lookup
18292fe8fb19SBen Gras 			 * table.  Use real division.
18302fe8fb19SBen Gras 			 */
18312fe8fb19SBen Gras 			regind = (unsigned)(diff / size);
18322fe8fb19SBen Gras 		}
18332fe8fb19SBen Gras 	} else if (size <= ((sizeof(size_invs) / sizeof(unsigned))
18342fe8fb19SBen Gras 	    << QUANTUM_2POW_MIN) + 2) {
18352fe8fb19SBen Gras 		regind = size_invs[(size >> QUANTUM_2POW_MIN) - 3] * diff;
18362fe8fb19SBen Gras 		regind >>= SIZE_INV_SHIFT;
18372fe8fb19SBen Gras 	} else {
18382fe8fb19SBen Gras 		/*
18392fe8fb19SBen Gras 		 * size_invs isn't large enough to handle this size class, so
18402fe8fb19SBen Gras 		 * calculate regind using actual division.  This only happens
18412fe8fb19SBen Gras 		 * if the user increases small_max via the 'S' runtime
18422fe8fb19SBen Gras 		 * configuration option.
18432fe8fb19SBen Gras 		 */
18442fe8fb19SBen Gras 		regind = (unsigned)(diff / size);
18452fe8fb19SBen Gras 	};
18462fe8fb19SBen Gras 	assert(diff == regind * size);
18472fe8fb19SBen Gras 	assert(regind < bin->nregs);
18482fe8fb19SBen Gras 
18492fe8fb19SBen Gras 	elm = regind >> (SIZEOF_INT_2POW + 3);
18502fe8fb19SBen Gras 	if (elm < run->regs_minelm)
18512fe8fb19SBen Gras 		run->regs_minelm = elm;
18522fe8fb19SBen Gras 	bit = regind - (elm << (SIZEOF_INT_2POW + 3));
18532fe8fb19SBen Gras 	assert((run->regs_mask[elm] & (1 << bit)) == 0);
18542fe8fb19SBen Gras 	run->regs_mask[elm] |= (1 << bit);
18552fe8fb19SBen Gras #undef SIZE_INV
18562fe8fb19SBen Gras #undef SIZE_INV_SHIFT
18572fe8fb19SBen Gras }
18582fe8fb19SBen Gras 
18592fe8fb19SBen Gras static void
arena_run_split(arena_t * arena,arena_run_t * run,size_t size)18602fe8fb19SBen Gras arena_run_split(arena_t *arena, arena_run_t *run, size_t size)
18612fe8fb19SBen Gras {
18622fe8fb19SBen Gras 	arena_chunk_t *chunk;
18632fe8fb19SBen Gras 	unsigned run_ind, map_offset, total_pages, need_pages, rem_pages;
18642fe8fb19SBen Gras 	unsigned i;
18652fe8fb19SBen Gras 
18662fe8fb19SBen Gras 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
18672fe8fb19SBen Gras 	run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk)
18682fe8fb19SBen Gras 	    >> pagesize_2pow);
18692fe8fb19SBen Gras 	total_pages = chunk->map[run_ind].npages;
18702fe8fb19SBen Gras 	need_pages = (unsigned)(size >> pagesize_2pow);
18712fe8fb19SBen Gras 	assert(need_pages <= total_pages);
18722fe8fb19SBen Gras 	rem_pages = total_pages - need_pages;
18732fe8fb19SBen Gras 
18742fe8fb19SBen Gras 	/* Split enough pages from the front of run to fit allocation size. */
18752fe8fb19SBen Gras 	map_offset = run_ind;
18762fe8fb19SBen Gras 	for (i = 0; i < need_pages; i++) {
18772fe8fb19SBen Gras 		chunk->map[map_offset + i].npages = need_pages;
18782fe8fb19SBen Gras 		chunk->map[map_offset + i].pos = i;
18792fe8fb19SBen Gras 	}
18802fe8fb19SBen Gras 
18812fe8fb19SBen Gras 	/* Keep track of trailing unused pages for later use. */
18822fe8fb19SBen Gras 	if (rem_pages > 0) {
18832fe8fb19SBen Gras 		/* Update map for trailing pages. */
18842fe8fb19SBen Gras 		map_offset += need_pages;
18852fe8fb19SBen Gras 		chunk->map[map_offset].npages = rem_pages;
18862fe8fb19SBen Gras 		chunk->map[map_offset].pos = POS_FREE;
18872fe8fb19SBen Gras 		chunk->map[map_offset + rem_pages - 1].npages = rem_pages;
18882fe8fb19SBen Gras 		chunk->map[map_offset + rem_pages - 1].pos = POS_FREE;
18892fe8fb19SBen Gras 	}
18902fe8fb19SBen Gras 
18912fe8fb19SBen Gras 	chunk->pages_used += need_pages;
18922fe8fb19SBen Gras }
18932fe8fb19SBen Gras 
18942fe8fb19SBen Gras static arena_chunk_t *
arena_chunk_alloc(arena_t * arena)18952fe8fb19SBen Gras arena_chunk_alloc(arena_t *arena)
18962fe8fb19SBen Gras {
18972fe8fb19SBen Gras 	arena_chunk_t *chunk;
18982fe8fb19SBen Gras 
18992fe8fb19SBen Gras 	if (arena->spare != NULL) {
19002fe8fb19SBen Gras 		chunk = arena->spare;
19012fe8fb19SBen Gras 		arena->spare = NULL;
19022fe8fb19SBen Gras 
19032fe8fb19SBen Gras 		/* LINTED */
19042fe8fb19SBen Gras 		RB_INSERT(arena_chunk_tree_s, &arena->chunks, chunk);
19052fe8fb19SBen Gras 	} else {
19062fe8fb19SBen Gras 		chunk = (arena_chunk_t *)chunk_alloc(chunksize);
19072fe8fb19SBen Gras 		if (chunk == NULL)
19082fe8fb19SBen Gras 			return (NULL);
19092fe8fb19SBen Gras #ifdef MALLOC_STATS
19102fe8fb19SBen Gras 		arena->stats.mapped += chunksize;
19112fe8fb19SBen Gras #endif
19122fe8fb19SBen Gras 
19132fe8fb19SBen Gras 		chunk->arena = arena;
19142fe8fb19SBen Gras 
19152fe8fb19SBen Gras 		/* LINTED */
19162fe8fb19SBen Gras 		RB_INSERT(arena_chunk_tree_s, &arena->chunks, chunk);
19172fe8fb19SBen Gras 
19182fe8fb19SBen Gras 		/*
19192fe8fb19SBen Gras 		 * Claim that no pages are in use, since the header is merely
19202fe8fb19SBen Gras 		 * overhead.
19212fe8fb19SBen Gras 		 */
19222fe8fb19SBen Gras 		chunk->pages_used = 0;
19232fe8fb19SBen Gras 
19242fe8fb19SBen Gras 		chunk->max_frun_npages = chunk_npages -
19252fe8fb19SBen Gras 		    arena_chunk_header_npages;
19262fe8fb19SBen Gras 		chunk->min_frun_ind = arena_chunk_header_npages;
19272fe8fb19SBen Gras 
19282fe8fb19SBen Gras 		/*
19292fe8fb19SBen Gras 		 * Initialize enough of the map to support one maximal free run.
19302fe8fb19SBen Gras 		 */
19312fe8fb19SBen Gras 		chunk->map[arena_chunk_header_npages].npages = chunk_npages -
19322fe8fb19SBen Gras 		    arena_chunk_header_npages;
19332fe8fb19SBen Gras 		chunk->map[arena_chunk_header_npages].pos = POS_FREE;
19342fe8fb19SBen Gras 		chunk->map[chunk_npages - 1].npages = chunk_npages -
19352fe8fb19SBen Gras 		    arena_chunk_header_npages;
19362fe8fb19SBen Gras 		chunk->map[chunk_npages - 1].pos = POS_FREE;
19372fe8fb19SBen Gras 	}
19382fe8fb19SBen Gras 
19392fe8fb19SBen Gras 	return (chunk);
19402fe8fb19SBen Gras }
19412fe8fb19SBen Gras 
19422fe8fb19SBen Gras static void
arena_chunk_dealloc(arena_t * arena,arena_chunk_t * chunk)19432fe8fb19SBen Gras arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)
19442fe8fb19SBen Gras {
19452fe8fb19SBen Gras 
19462fe8fb19SBen Gras 	/*
19472fe8fb19SBen Gras 	 * Remove chunk from the chunk tree, regardless of whether this chunk
19482fe8fb19SBen Gras 	 * will be cached, so that the arena does not use it.
19492fe8fb19SBen Gras 	 */
19502fe8fb19SBen Gras 	/* LINTED */
19512fe8fb19SBen Gras 	RB_REMOVE(arena_chunk_tree_s, &chunk->arena->chunks, chunk);
19522fe8fb19SBen Gras 
19532fe8fb19SBen Gras 	if (opt_hint == false) {
19542fe8fb19SBen Gras 		if (arena->spare != NULL) {
19552fe8fb19SBen Gras 			chunk_dealloc((void *)arena->spare, chunksize);
19562fe8fb19SBen Gras #ifdef MALLOC_STATS
19572fe8fb19SBen Gras 			arena->stats.mapped -= chunksize;
19582fe8fb19SBen Gras #endif
19592fe8fb19SBen Gras 		}
19602fe8fb19SBen Gras 		arena->spare = chunk;
19612fe8fb19SBen Gras 	} else {
19622fe8fb19SBen Gras 		assert(arena->spare == NULL);
19632fe8fb19SBen Gras 		chunk_dealloc((void *)chunk, chunksize);
19642fe8fb19SBen Gras #ifdef MALLOC_STATS
19652fe8fb19SBen Gras 		arena->stats.mapped -= chunksize;
19662fe8fb19SBen Gras #endif
19672fe8fb19SBen Gras 	}
19682fe8fb19SBen Gras }
19692fe8fb19SBen Gras 
19702fe8fb19SBen Gras static arena_run_t *
arena_run_alloc(arena_t * arena,size_t size)19712fe8fb19SBen Gras arena_run_alloc(arena_t *arena, size_t size)
19722fe8fb19SBen Gras {
19732fe8fb19SBen Gras 	arena_chunk_t *chunk;
19742fe8fb19SBen Gras 	arena_run_t *run;
19752fe8fb19SBen Gras 	unsigned need_npages, limit_pages, compl_need_npages;
19762fe8fb19SBen Gras 
19772fe8fb19SBen Gras 	assert(size <= (chunksize - (arena_chunk_header_npages <<
19782fe8fb19SBen Gras 	    pagesize_2pow)));
19792fe8fb19SBen Gras 	assert((size & pagesize_mask) == 0);
19802fe8fb19SBen Gras 
19812fe8fb19SBen Gras 	/*
19822fe8fb19SBen Gras 	 * Search through arena's chunks in address order for a free run that is
19832fe8fb19SBen Gras 	 * large enough.  Look for the first fit.
19842fe8fb19SBen Gras 	 */
19852fe8fb19SBen Gras 	need_npages = (unsigned)(size >> pagesize_2pow);
19862fe8fb19SBen Gras 	limit_pages = chunk_npages - arena_chunk_header_npages;
19872fe8fb19SBen Gras 	compl_need_npages = limit_pages - need_npages;
19882fe8fb19SBen Gras 	/* LINTED */
19892fe8fb19SBen Gras 	RB_FOREACH(chunk, arena_chunk_tree_s, &arena->chunks) {
19902fe8fb19SBen Gras 		/*
19912fe8fb19SBen Gras 		 * Avoid searching this chunk if there are not enough
19922fe8fb19SBen Gras 		 * contiguous free pages for there to possibly be a large
19932fe8fb19SBen Gras 		 * enough free run.
19942fe8fb19SBen Gras 		 */
19952fe8fb19SBen Gras 		if (chunk->pages_used <= compl_need_npages &&
19962fe8fb19SBen Gras 		    need_npages <= chunk->max_frun_npages) {
19972fe8fb19SBen Gras 			arena_chunk_map_t *mapelm;
19982fe8fb19SBen Gras 			unsigned i;
19992fe8fb19SBen Gras 			unsigned max_frun_npages = 0;
20002fe8fb19SBen Gras 			unsigned min_frun_ind = chunk_npages;
20012fe8fb19SBen Gras 
20022fe8fb19SBen Gras 			assert(chunk->min_frun_ind >=
20032fe8fb19SBen Gras 			    arena_chunk_header_npages);
20042fe8fb19SBen Gras 			for (i = chunk->min_frun_ind; i < chunk_npages;) {
20052fe8fb19SBen Gras 				mapelm = &chunk->map[i];
20062fe8fb19SBen Gras 				if (mapelm->pos == POS_FREE) {
20072fe8fb19SBen Gras 					if (mapelm->npages >= need_npages) {
20082fe8fb19SBen Gras 						run = (arena_run_t *)
20092fe8fb19SBen Gras 						    ((uintptr_t)chunk + (i <<
20102fe8fb19SBen Gras 						    pagesize_2pow));
20112fe8fb19SBen Gras 						/* Update page map. */
20122fe8fb19SBen Gras 						arena_run_split(arena, run,
20132fe8fb19SBen Gras 						    size);
20142fe8fb19SBen Gras 						return (run);
20152fe8fb19SBen Gras 					}
20162fe8fb19SBen Gras 					if (mapelm->npages >
20172fe8fb19SBen Gras 					    max_frun_npages) {
20182fe8fb19SBen Gras 						max_frun_npages =
20192fe8fb19SBen Gras 						    mapelm->npages;
20202fe8fb19SBen Gras 					}
20212fe8fb19SBen Gras 					if (i < min_frun_ind) {
20222fe8fb19SBen Gras 						min_frun_ind = i;
20232fe8fb19SBen Gras 						if (i < chunk->min_frun_ind)
20242fe8fb19SBen Gras 							chunk->min_frun_ind = i;
20252fe8fb19SBen Gras 					}
20262fe8fb19SBen Gras 				}
20272fe8fb19SBen Gras 				i += mapelm->npages;
20282fe8fb19SBen Gras 			}
20292fe8fb19SBen Gras 			/*
20302fe8fb19SBen Gras 			 * Search failure.  Reset cached chunk->max_frun_npages.
20312fe8fb19SBen Gras 			 * chunk->min_frun_ind was already reset above (if
20322fe8fb19SBen Gras 			 * necessary).
20332fe8fb19SBen Gras 			 */
20342fe8fb19SBen Gras 			chunk->max_frun_npages = max_frun_npages;
20352fe8fb19SBen Gras 		}
20362fe8fb19SBen Gras 	}
20372fe8fb19SBen Gras 
20382fe8fb19SBen Gras 	/*
20392fe8fb19SBen Gras 	 * No usable runs.  Create a new chunk from which to allocate the run.
20402fe8fb19SBen Gras 	 */
20412fe8fb19SBen Gras 	chunk = arena_chunk_alloc(arena);
20422fe8fb19SBen Gras 	if (chunk == NULL)
20432fe8fb19SBen Gras 		return (NULL);
20442fe8fb19SBen Gras 	run = (arena_run_t *)((uintptr_t)chunk + (arena_chunk_header_npages <<
20452fe8fb19SBen Gras 	    pagesize_2pow));
20462fe8fb19SBen Gras 	/* Update page map. */
20472fe8fb19SBen Gras 	arena_run_split(arena, run, size);
20482fe8fb19SBen Gras 	return (run);
20492fe8fb19SBen Gras }
20502fe8fb19SBen Gras 
20512fe8fb19SBen Gras static void
arena_run_dalloc(arena_t * arena,arena_run_t * run,size_t size)20522fe8fb19SBen Gras arena_run_dalloc(arena_t *arena, arena_run_t *run, size_t size)
20532fe8fb19SBen Gras {
20542fe8fb19SBen Gras 	arena_chunk_t *chunk;
20552fe8fb19SBen Gras 	unsigned run_ind, run_pages;
20562fe8fb19SBen Gras 
20572fe8fb19SBen Gras 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
20582fe8fb19SBen Gras 
20592fe8fb19SBen Gras 	run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk)
20602fe8fb19SBen Gras 	    >> pagesize_2pow);
20612fe8fb19SBen Gras 	assert(run_ind >= arena_chunk_header_npages);
20622fe8fb19SBen Gras 	assert(run_ind < (chunksize >> pagesize_2pow));
20632fe8fb19SBen Gras 	run_pages = (unsigned)(size >> pagesize_2pow);
20642fe8fb19SBen Gras 	assert(run_pages == chunk->map[run_ind].npages);
20652fe8fb19SBen Gras 
20662fe8fb19SBen Gras 	/* Subtract pages from count of pages used in chunk. */
20672fe8fb19SBen Gras 	chunk->pages_used -= run_pages;
20682fe8fb19SBen Gras 
20692fe8fb19SBen Gras 	/* Mark run as deallocated. */
20702fe8fb19SBen Gras 	assert(chunk->map[run_ind].npages == run_pages);
20712fe8fb19SBen Gras 	chunk->map[run_ind].pos = POS_FREE;
20722fe8fb19SBen Gras 	assert(chunk->map[run_ind + run_pages - 1].npages == run_pages);
20732fe8fb19SBen Gras 	chunk->map[run_ind + run_pages - 1].pos = POS_FREE;
20742fe8fb19SBen Gras 
20752fe8fb19SBen Gras 	/*
20762fe8fb19SBen Gras 	 * Tell the kernel that we don't need the data in this run, but only if
20772fe8fb19SBen Gras 	 * requested via runtime configuration.
20782fe8fb19SBen Gras 	 */
20792fe8fb19SBen Gras 	if (opt_hint)
20802fe8fb19SBen Gras 		madvise(run, size, MADV_FREE);
20812fe8fb19SBen Gras 
20822fe8fb19SBen Gras 	/* Try to coalesce with neighboring runs. */
20832fe8fb19SBen Gras 	if (run_ind > arena_chunk_header_npages &&
20842fe8fb19SBen Gras 	    chunk->map[run_ind - 1].pos == POS_FREE) {
20852fe8fb19SBen Gras 		unsigned prev_npages;
20862fe8fb19SBen Gras 
20872fe8fb19SBen Gras 		/* Coalesce with previous run. */
20882fe8fb19SBen Gras 		prev_npages = chunk->map[run_ind - 1].npages;
20892fe8fb19SBen Gras 		run_ind -= prev_npages;
20902fe8fb19SBen Gras 		assert(chunk->map[run_ind].npages == prev_npages);
20912fe8fb19SBen Gras 		assert(chunk->map[run_ind].pos == POS_FREE);
20922fe8fb19SBen Gras 		run_pages += prev_npages;
20932fe8fb19SBen Gras 
20942fe8fb19SBen Gras 		chunk->map[run_ind].npages = run_pages;
20952fe8fb19SBen Gras 		assert(chunk->map[run_ind].pos == POS_FREE);
20962fe8fb19SBen Gras 		chunk->map[run_ind + run_pages - 1].npages = run_pages;
20972fe8fb19SBen Gras 		assert(chunk->map[run_ind + run_pages - 1].pos == POS_FREE);
20982fe8fb19SBen Gras 	}
20992fe8fb19SBen Gras 
21002fe8fb19SBen Gras 	if (run_ind + run_pages < chunk_npages &&
21012fe8fb19SBen Gras 	    chunk->map[run_ind + run_pages].pos == POS_FREE) {
21022fe8fb19SBen Gras 		unsigned next_npages;
21032fe8fb19SBen Gras 
21042fe8fb19SBen Gras 		/* Coalesce with next run. */
21052fe8fb19SBen Gras 		next_npages = chunk->map[run_ind + run_pages].npages;
21062fe8fb19SBen Gras 		run_pages += next_npages;
21072fe8fb19SBen Gras 		assert(chunk->map[run_ind + run_pages - 1].npages ==
21082fe8fb19SBen Gras 		    next_npages);
21092fe8fb19SBen Gras 		assert(chunk->map[run_ind + run_pages - 1].pos == POS_FREE);
21102fe8fb19SBen Gras 
21112fe8fb19SBen Gras 		chunk->map[run_ind].npages = run_pages;
21122fe8fb19SBen Gras 		chunk->map[run_ind].pos = POS_FREE;
21132fe8fb19SBen Gras 		chunk->map[run_ind + run_pages - 1].npages = run_pages;
21142fe8fb19SBen Gras 		assert(chunk->map[run_ind + run_pages - 1].pos == POS_FREE);
21152fe8fb19SBen Gras 	}
21162fe8fb19SBen Gras 
21172fe8fb19SBen Gras 	if (chunk->map[run_ind].npages > chunk->max_frun_npages)
21182fe8fb19SBen Gras 		chunk->max_frun_npages = chunk->map[run_ind].npages;
21192fe8fb19SBen Gras 	if (run_ind < chunk->min_frun_ind)
21202fe8fb19SBen Gras 		chunk->min_frun_ind = run_ind;
21212fe8fb19SBen Gras 
21222fe8fb19SBen Gras 	/* Deallocate chunk if it is now completely unused. */
21232fe8fb19SBen Gras 	if (chunk->pages_used == 0)
21242fe8fb19SBen Gras 		arena_chunk_dealloc(arena, chunk);
21252fe8fb19SBen Gras }
21262fe8fb19SBen Gras 
21272fe8fb19SBen Gras static arena_run_t *
arena_bin_nonfull_run_get(arena_t * arena,arena_bin_t * bin)21282fe8fb19SBen Gras arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
21292fe8fb19SBen Gras {
21302fe8fb19SBen Gras 	arena_run_t *run;
21312fe8fb19SBen Gras 	unsigned i, remainder;
21322fe8fb19SBen Gras 
21332fe8fb19SBen Gras 	/* Look for a usable run. */
21342fe8fb19SBen Gras 	/* LINTED */
21352fe8fb19SBen Gras 	if ((run = RB_MIN(arena_run_tree_s, &bin->runs)) != NULL) {
21362fe8fb19SBen Gras 		/* run is guaranteed to have available space. */
21372fe8fb19SBen Gras 		/* LINTED */
21382fe8fb19SBen Gras 		RB_REMOVE(arena_run_tree_s, &bin->runs, run);
21392fe8fb19SBen Gras #ifdef MALLOC_STATS
21402fe8fb19SBen Gras 		bin->stats.reruns++;
21412fe8fb19SBen Gras #endif
21422fe8fb19SBen Gras 		return (run);
21432fe8fb19SBen Gras 	}
21442fe8fb19SBen Gras 	/* No existing runs have any space available. */
21452fe8fb19SBen Gras 
21462fe8fb19SBen Gras 	/* Allocate a new run. */
21472fe8fb19SBen Gras 	run = arena_run_alloc(arena, bin->run_size);
21482fe8fb19SBen Gras 	if (run == NULL)
21492fe8fb19SBen Gras 		return (NULL);
21502fe8fb19SBen Gras 
21512fe8fb19SBen Gras 	/* Initialize run internals. */
21522fe8fb19SBen Gras 	run->bin = bin;
21532fe8fb19SBen Gras 
21542fe8fb19SBen Gras 	for (i = 0; i < bin->regs_mask_nelms; i++)
21552fe8fb19SBen Gras 		run->regs_mask[i] = UINT_MAX;
21562fe8fb19SBen Gras 	remainder = bin->nregs & ((1 << (SIZEOF_INT_2POW + 3)) - 1);
21572fe8fb19SBen Gras 	if (remainder != 0) {
21582fe8fb19SBen Gras 		/* The last element has spare bits that need to be unset. */
21592fe8fb19SBen Gras 		run->regs_mask[i] = (UINT_MAX >> ((1 << (SIZEOF_INT_2POW + 3))
21602fe8fb19SBen Gras 		    - remainder));
21612fe8fb19SBen Gras 	}
21622fe8fb19SBen Gras 
21632fe8fb19SBen Gras 	run->regs_minelm = 0;
21642fe8fb19SBen Gras 
21652fe8fb19SBen Gras 	run->nfree = bin->nregs;
21662fe8fb19SBen Gras #ifdef MALLOC_DEBUG
21672fe8fb19SBen Gras 	run->magic = ARENA_RUN_MAGIC;
21682fe8fb19SBen Gras #endif
21692fe8fb19SBen Gras 
21702fe8fb19SBen Gras #ifdef MALLOC_STATS
21712fe8fb19SBen Gras 	bin->stats.nruns++;
21722fe8fb19SBen Gras 	bin->stats.curruns++;
21732fe8fb19SBen Gras 	if (bin->stats.curruns > bin->stats.highruns)
21742fe8fb19SBen Gras 		bin->stats.highruns = bin->stats.curruns;
21752fe8fb19SBen Gras #endif
21762fe8fb19SBen Gras 	return (run);
21772fe8fb19SBen Gras }
21782fe8fb19SBen Gras 
21792fe8fb19SBen Gras /* bin->runcur must have space available before this function is called. */
21802fe8fb19SBen Gras static inline void *
arena_bin_malloc_easy(arena_t * arena,arena_bin_t * bin,arena_run_t * run)21812fe8fb19SBen Gras arena_bin_malloc_easy(arena_t *arena, arena_bin_t *bin, arena_run_t *run)
21822fe8fb19SBen Gras {
21832fe8fb19SBen Gras 	void *ret;
21842fe8fb19SBen Gras 
21852fe8fb19SBen Gras 	assert(run->magic == ARENA_RUN_MAGIC);
21862fe8fb19SBen Gras 	assert(run->nfree > 0);
21872fe8fb19SBen Gras 
21882fe8fb19SBen Gras 	ret = arena_run_reg_alloc(run, bin);
21892fe8fb19SBen Gras 	assert(ret != NULL);
21902fe8fb19SBen Gras 	run->nfree--;
21912fe8fb19SBen Gras 
21922fe8fb19SBen Gras 	return (ret);
21932fe8fb19SBen Gras }
21942fe8fb19SBen Gras 
21952fe8fb19SBen Gras /* Re-fill bin->runcur, then call arena_bin_malloc_easy(). */
21962fe8fb19SBen Gras static void *
arena_bin_malloc_hard(arena_t * arena,arena_bin_t * bin)21972fe8fb19SBen Gras arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
21982fe8fb19SBen Gras {
21992fe8fb19SBen Gras 
22002fe8fb19SBen Gras 	bin->runcur = arena_bin_nonfull_run_get(arena, bin);
22012fe8fb19SBen Gras 	if (bin->runcur == NULL)
22022fe8fb19SBen Gras 		return (NULL);
22032fe8fb19SBen Gras 	assert(bin->runcur->magic == ARENA_RUN_MAGIC);
22042fe8fb19SBen Gras 	assert(bin->runcur->nfree > 0);
22052fe8fb19SBen Gras 
22062fe8fb19SBen Gras 	return (arena_bin_malloc_easy(arena, bin, bin->runcur));
22072fe8fb19SBen Gras }
22082fe8fb19SBen Gras 
22092fe8fb19SBen Gras /*
22102fe8fb19SBen Gras  * Calculate bin->run_size such that it meets the following constraints:
22112fe8fb19SBen Gras  *
22122fe8fb19SBen Gras  *   *) bin->run_size >= min_run_size
22132fe8fb19SBen Gras  *   *) bin->run_size <= arena_maxclass
22142fe8fb19SBen Gras  *   *) bin->run_size <= RUN_MAX_SMALL
22152fe8fb19SBen Gras  *   *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed).
22162fe8fb19SBen Gras  *
22172fe8fb19SBen Gras  * bin->nregs, bin->regs_mask_nelms, and bin->reg0_offset are
22182fe8fb19SBen Gras  * also calculated here, since these settings are all interdependent.
22192fe8fb19SBen Gras  */
22202fe8fb19SBen Gras static size_t
arena_bin_run_size_calc(arena_bin_t * bin,size_t min_run_size)22212fe8fb19SBen Gras arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
22222fe8fb19SBen Gras {
22232fe8fb19SBen Gras 	size_t try_run_size, good_run_size;
22242fe8fb19SBen Gras 	unsigned good_nregs, good_mask_nelms, good_reg0_offset;
22252fe8fb19SBen Gras 	unsigned try_nregs, try_mask_nelms, try_reg0_offset;
22262fe8fb19SBen Gras 
22272fe8fb19SBen Gras 	assert(min_run_size >= pagesize);
22282fe8fb19SBen Gras 	assert(min_run_size <= arena_maxclass);
22292fe8fb19SBen Gras 	assert(min_run_size <= RUN_MAX_SMALL);
22302fe8fb19SBen Gras 
22312fe8fb19SBen Gras 	/*
22322fe8fb19SBen Gras 	 * Calculate known-valid settings before entering the run_size
22332fe8fb19SBen Gras 	 * expansion loop, so that the first part of the loop always copies
22342fe8fb19SBen Gras 	 * valid settings.
22352fe8fb19SBen Gras 	 *
22362fe8fb19SBen Gras 	 * The do..while loop iteratively reduces the number of regions until
22372fe8fb19SBen Gras 	 * the run header and the regions no longer overlap.  A closed formula
22382fe8fb19SBen Gras 	 * would be quite messy, since there is an interdependency between the
22392fe8fb19SBen Gras 	 * header's mask length and the number of regions.
22402fe8fb19SBen Gras 	 */
22412fe8fb19SBen Gras 	try_run_size = min_run_size;
22422fe8fb19SBen Gras 	try_nregs = (unsigned)(((try_run_size - sizeof(arena_run_t)) /
2243f14fb602SLionel Sambuc 	    bin->reg_size) + 1); /* Counter-act try_nregs-- in loop. */
22442fe8fb19SBen Gras 	do {
22452fe8fb19SBen Gras 		try_nregs--;
22462fe8fb19SBen Gras 		try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) +
22472fe8fb19SBen Gras 		    ((try_nregs & ((1 << (SIZEOF_INT_2POW + 3)) - 1)) ? 1 : 0);
22482fe8fb19SBen Gras 		try_reg0_offset = (unsigned)(try_run_size -
22492fe8fb19SBen Gras 		    (try_nregs * bin->reg_size));
22502fe8fb19SBen Gras 	} while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1))
22512fe8fb19SBen Gras 	    > try_reg0_offset);
22522fe8fb19SBen Gras 
22532fe8fb19SBen Gras 	/* run_size expansion loop. */
22542fe8fb19SBen Gras 	do {
22552fe8fb19SBen Gras 		/*
22562fe8fb19SBen Gras 		 * Copy valid settings before trying more aggressive settings.
22572fe8fb19SBen Gras 		 */
22582fe8fb19SBen Gras 		good_run_size = try_run_size;
22592fe8fb19SBen Gras 		good_nregs = try_nregs;
22602fe8fb19SBen Gras 		good_mask_nelms = try_mask_nelms;
22612fe8fb19SBen Gras 		good_reg0_offset = try_reg0_offset;
22622fe8fb19SBen Gras 
22632fe8fb19SBen Gras 		/* Try more aggressive settings. */
22642fe8fb19SBen Gras 		try_run_size += pagesize;
22652fe8fb19SBen Gras 		try_nregs = (unsigned)(((try_run_size - sizeof(arena_run_t)) /
22662fe8fb19SBen Gras 		    bin->reg_size) + 1); /* Counter-act try_nregs-- in loop. */
22672fe8fb19SBen Gras 		do {
22682fe8fb19SBen Gras 			try_nregs--;
22692fe8fb19SBen Gras 			try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) +
22702fe8fb19SBen Gras 			    ((try_nregs & ((1 << (SIZEOF_INT_2POW + 3)) - 1)) ?
22712fe8fb19SBen Gras 			    1 : 0);
22722fe8fb19SBen Gras 			try_reg0_offset = (unsigned)(try_run_size - (try_nregs *
22732fe8fb19SBen Gras 			    bin->reg_size));
22742fe8fb19SBen Gras 		} while (sizeof(arena_run_t) + (sizeof(unsigned) *
22752fe8fb19SBen Gras 		    (try_mask_nelms - 1)) > try_reg0_offset);
22762fe8fb19SBen Gras 	} while (try_run_size <= arena_maxclass && try_run_size <= RUN_MAX_SMALL
2277f14fb602SLionel Sambuc 	    && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX
2278f14fb602SLionel Sambuc 	    && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size);
22792fe8fb19SBen Gras 
22802fe8fb19SBen Gras 	assert(sizeof(arena_run_t) + (sizeof(unsigned) * (good_mask_nelms - 1))
22812fe8fb19SBen Gras 	    <= good_reg0_offset);
22822fe8fb19SBen Gras 	assert((good_mask_nelms << (SIZEOF_INT_2POW + 3)) >= good_nregs);
22832fe8fb19SBen Gras 
22842fe8fb19SBen Gras 	/* Copy final settings. */
22852fe8fb19SBen Gras 	bin->run_size = good_run_size;
22862fe8fb19SBen Gras 	bin->nregs = good_nregs;
22872fe8fb19SBen Gras 	bin->regs_mask_nelms = good_mask_nelms;
22882fe8fb19SBen Gras 	bin->reg0_offset = good_reg0_offset;
22892fe8fb19SBen Gras 
22902fe8fb19SBen Gras 	return (good_run_size);
22912fe8fb19SBen Gras }
22922fe8fb19SBen Gras 
22932fe8fb19SBen Gras static void *
arena_malloc(arena_t * arena,size_t size)22942fe8fb19SBen Gras arena_malloc(arena_t *arena, size_t size)
22952fe8fb19SBen Gras {
22962fe8fb19SBen Gras 	void *ret;
22972fe8fb19SBen Gras 
22982fe8fb19SBen Gras 	assert(arena != NULL);
22992fe8fb19SBen Gras 	assert(arena->magic == ARENA_MAGIC);
23002fe8fb19SBen Gras 	assert(size != 0);
23012fe8fb19SBen Gras 	assert(QUANTUM_CEILING(size) <= arena_maxclass);
23022fe8fb19SBen Gras 
23032fe8fb19SBen Gras 	if (size <= bin_maxclass) {
23042fe8fb19SBen Gras 		arena_bin_t *bin;
23052fe8fb19SBen Gras 		arena_run_t *run;
23062fe8fb19SBen Gras 
23072fe8fb19SBen Gras 		/* Small allocation. */
23082fe8fb19SBen Gras 
23092fe8fb19SBen Gras 		if (size < small_min) {
23102fe8fb19SBen Gras 			/* Tiny. */
23112fe8fb19SBen Gras 			size = pow2_ceil(size);
23122fe8fb19SBen Gras 			bin = &arena->bins[ffs((int)(size >> (TINY_MIN_2POW +
23132fe8fb19SBen Gras 			    1)))];
23142fe8fb19SBen Gras #if (!defined(NDEBUG) || defined(MALLOC_STATS))
23152fe8fb19SBen Gras 			/*
23162fe8fb19SBen Gras 			 * Bin calculation is always correct, but we may need
23172fe8fb19SBen Gras 			 * to fix size for the purposes of assertions and/or
23182fe8fb19SBen Gras 			 * stats accuracy.
23192fe8fb19SBen Gras 			 */
23202fe8fb19SBen Gras 			if (size < (1 << TINY_MIN_2POW))
23212fe8fb19SBen Gras 				size = (1 << TINY_MIN_2POW);
23222fe8fb19SBen Gras #endif
23232fe8fb19SBen Gras 		} else if (size <= small_max) {
23242fe8fb19SBen Gras 			/* Quantum-spaced. */
23252fe8fb19SBen Gras 			size = QUANTUM_CEILING(size);
23262fe8fb19SBen Gras 			bin = &arena->bins[ntbins + (size >> opt_quantum_2pow)
23272fe8fb19SBen Gras 			    - 1];
23282fe8fb19SBen Gras 		} else {
23292fe8fb19SBen Gras 			/* Sub-page. */
23302fe8fb19SBen Gras 			size = pow2_ceil(size);
23312fe8fb19SBen Gras 			bin = &arena->bins[ntbins + nqbins
23322fe8fb19SBen Gras 			    + (ffs((int)(size >> opt_small_max_2pow)) - 2)];
23332fe8fb19SBen Gras 		}
23342fe8fb19SBen Gras 		assert(size == bin->reg_size);
23352fe8fb19SBen Gras 
23362fe8fb19SBen Gras 		malloc_mutex_lock(&arena->mtx);
23372fe8fb19SBen Gras 		if ((run = bin->runcur) != NULL && run->nfree > 0)
23382fe8fb19SBen Gras 			ret = arena_bin_malloc_easy(arena, bin, run);
23392fe8fb19SBen Gras 		else
23402fe8fb19SBen Gras 			ret = arena_bin_malloc_hard(arena, bin);
23412fe8fb19SBen Gras 
23422fe8fb19SBen Gras 		if (ret == NULL) {
23432fe8fb19SBen Gras 			malloc_mutex_unlock(&arena->mtx);
23442fe8fb19SBen Gras 			return (NULL);
23452fe8fb19SBen Gras 		}
23462fe8fb19SBen Gras 
23472fe8fb19SBen Gras #ifdef MALLOC_STATS
23482fe8fb19SBen Gras 		bin->stats.nrequests++;
23492fe8fb19SBen Gras 		arena->stats.nmalloc_small++;
23502fe8fb19SBen Gras 		arena->stats.allocated_small += size;
23512fe8fb19SBen Gras #endif
23522fe8fb19SBen Gras 	} else {
23532fe8fb19SBen Gras 		/* Large allocation. */
23542fe8fb19SBen Gras 		size = PAGE_CEILING(size);
23552fe8fb19SBen Gras 		malloc_mutex_lock(&arena->mtx);
23562fe8fb19SBen Gras 		ret = (void *)arena_run_alloc(arena, size);
23572fe8fb19SBen Gras 		if (ret == NULL) {
23582fe8fb19SBen Gras 			malloc_mutex_unlock(&arena->mtx);
23592fe8fb19SBen Gras 			return (NULL);
23602fe8fb19SBen Gras 		}
23612fe8fb19SBen Gras #ifdef MALLOC_STATS
23622fe8fb19SBen Gras 		arena->stats.nmalloc_large++;
23632fe8fb19SBen Gras 		arena->stats.allocated_large += size;
23642fe8fb19SBen Gras #endif
23652fe8fb19SBen Gras 	}
23662fe8fb19SBen Gras 
23672fe8fb19SBen Gras 	malloc_mutex_unlock(&arena->mtx);
23682fe8fb19SBen Gras 
23692fe8fb19SBen Gras 	if (opt_junk)
23702fe8fb19SBen Gras 		memset(ret, 0xa5, size);
23712fe8fb19SBen Gras 	else if (opt_zero)
23722fe8fb19SBen Gras 		memset(ret, 0, size);
23732fe8fb19SBen Gras 	return (ret);
23742fe8fb19SBen Gras }
23752fe8fb19SBen Gras 
23762fe8fb19SBen Gras static inline void
arena_palloc_trim(arena_t * arena,arena_chunk_t * chunk,unsigned pageind,unsigned npages)23772fe8fb19SBen Gras arena_palloc_trim(arena_t *arena, arena_chunk_t *chunk, unsigned pageind,
23782fe8fb19SBen Gras     unsigned npages)
23792fe8fb19SBen Gras {
23802fe8fb19SBen Gras 	unsigned i;
23812fe8fb19SBen Gras 
23822fe8fb19SBen Gras 	assert(npages > 0);
23832fe8fb19SBen Gras 
23842fe8fb19SBen Gras 	/*
23852fe8fb19SBen Gras 	 * Modifiy the map such that arena_run_dalloc() sees the run as
23862fe8fb19SBen Gras 	 * separately allocated.
23872fe8fb19SBen Gras 	 */
23882fe8fb19SBen Gras 	for (i = 0; i < npages; i++) {
23892fe8fb19SBen Gras 		chunk->map[pageind + i].npages = npages;
23902fe8fb19SBen Gras 		chunk->map[pageind + i].pos = i;
23912fe8fb19SBen Gras 	}
23922fe8fb19SBen Gras 	arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)chunk + (pageind <<
23932fe8fb19SBen Gras 	    pagesize_2pow)), npages << pagesize_2pow);
23942fe8fb19SBen Gras }
23952fe8fb19SBen Gras 
23962fe8fb19SBen Gras /* Only handles large allocations that require more than page alignment. */
23972fe8fb19SBen Gras static void *
arena_palloc(arena_t * arena,size_t alignment,size_t size,size_t alloc_size)23982fe8fb19SBen Gras arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size)
23992fe8fb19SBen Gras {
24002fe8fb19SBen Gras 	void *ret;
24012fe8fb19SBen Gras 	size_t offset;
24022fe8fb19SBen Gras 	arena_chunk_t *chunk;
24032fe8fb19SBen Gras 	unsigned pageind, i, npages;
24042fe8fb19SBen Gras 
24052fe8fb19SBen Gras 	assert((size & pagesize_mask) == 0);
24062fe8fb19SBen Gras 	assert((alignment & pagesize_mask) == 0);
24072fe8fb19SBen Gras 
24082fe8fb19SBen Gras 	npages = (unsigned)(size >> pagesize_2pow);
24092fe8fb19SBen Gras 
24102fe8fb19SBen Gras 	malloc_mutex_lock(&arena->mtx);
24112fe8fb19SBen Gras 	ret = (void *)arena_run_alloc(arena, alloc_size);
24122fe8fb19SBen Gras 	if (ret == NULL) {
24132fe8fb19SBen Gras 		malloc_mutex_unlock(&arena->mtx);
24142fe8fb19SBen Gras 		return (NULL);
24152fe8fb19SBen Gras 	}
24162fe8fb19SBen Gras 
24172fe8fb19SBen Gras 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
24182fe8fb19SBen Gras 
24192fe8fb19SBen Gras 	offset = (uintptr_t)ret & (alignment - 1);
24202fe8fb19SBen Gras 	assert((offset & pagesize_mask) == 0);
24212fe8fb19SBen Gras 	assert(offset < alloc_size);
24222fe8fb19SBen Gras 	if (offset == 0) {
24232fe8fb19SBen Gras 		pageind = (unsigned)(((uintptr_t)ret - (uintptr_t)chunk) >>
24242fe8fb19SBen Gras 		    pagesize_2pow);
24252fe8fb19SBen Gras 
24262fe8fb19SBen Gras 		/* Update the map for the run to be kept. */
24272fe8fb19SBen Gras 		for (i = 0; i < npages; i++) {
24282fe8fb19SBen Gras 			chunk->map[pageind + i].npages = npages;
24292fe8fb19SBen Gras 			assert(chunk->map[pageind + i].pos == i);
24302fe8fb19SBen Gras 		}
24312fe8fb19SBen Gras 
24322fe8fb19SBen Gras 		/* Trim trailing space. */
24332fe8fb19SBen Gras 		arena_palloc_trim(arena, chunk, pageind + npages,
24342fe8fb19SBen Gras 		    (unsigned)((alloc_size - size) >> pagesize_2pow));
24352fe8fb19SBen Gras 	} else {
24362fe8fb19SBen Gras 		size_t leadsize, trailsize;
24372fe8fb19SBen Gras 
24382fe8fb19SBen Gras 		leadsize = alignment - offset;
24392fe8fb19SBen Gras 		ret = (void *)((uintptr_t)ret + leadsize);
24402fe8fb19SBen Gras 		pageind = (unsigned)(((uintptr_t)ret - (uintptr_t)chunk) >>
24412fe8fb19SBen Gras 		    pagesize_2pow);
24422fe8fb19SBen Gras 
24432fe8fb19SBen Gras 		/* Update the map for the run to be kept. */
24442fe8fb19SBen Gras 		for (i = 0; i < npages; i++) {
24452fe8fb19SBen Gras 			chunk->map[pageind + i].npages = npages;
24462fe8fb19SBen Gras 			chunk->map[pageind + i].pos = i;
24472fe8fb19SBen Gras 		}
24482fe8fb19SBen Gras 
24492fe8fb19SBen Gras 		/* Trim leading space. */
24502fe8fb19SBen Gras 		arena_palloc_trim(arena, chunk,
24512fe8fb19SBen Gras 		    (unsigned)(pageind - (leadsize >> pagesize_2pow)),
24522fe8fb19SBen Gras 		    (unsigned)(leadsize >> pagesize_2pow));
24532fe8fb19SBen Gras 
24542fe8fb19SBen Gras 		trailsize = alloc_size - leadsize - size;
24552fe8fb19SBen Gras 		if (trailsize != 0) {
24562fe8fb19SBen Gras 			/* Trim trailing space. */
24572fe8fb19SBen Gras 			assert(trailsize < alloc_size);
24582fe8fb19SBen Gras 			arena_palloc_trim(arena, chunk, pageind + npages,
24592fe8fb19SBen Gras 			    (unsigned)(trailsize >> pagesize_2pow));
24602fe8fb19SBen Gras 		}
24612fe8fb19SBen Gras 	}
24622fe8fb19SBen Gras 
24632fe8fb19SBen Gras #ifdef MALLOC_STATS
24642fe8fb19SBen Gras 	arena->stats.nmalloc_large++;
24652fe8fb19SBen Gras 	arena->stats.allocated_large += size;
24662fe8fb19SBen Gras #endif
24672fe8fb19SBen Gras 	malloc_mutex_unlock(&arena->mtx);
24682fe8fb19SBen Gras 
24692fe8fb19SBen Gras 	if (opt_junk)
24702fe8fb19SBen Gras 		memset(ret, 0xa5, size);
24712fe8fb19SBen Gras 	else if (opt_zero)
24722fe8fb19SBen Gras 		memset(ret, 0, size);
24732fe8fb19SBen Gras 	return (ret);
24742fe8fb19SBen Gras }
24752fe8fb19SBen Gras 
24762fe8fb19SBen Gras /* Return the size of the allocation pointed to by ptr. */
24772fe8fb19SBen Gras static size_t
arena_salloc(const void * ptr)24782fe8fb19SBen Gras arena_salloc(const void *ptr)
24792fe8fb19SBen Gras {
24802fe8fb19SBen Gras 	size_t ret;
24812fe8fb19SBen Gras 	arena_chunk_t *chunk;
24822fe8fb19SBen Gras 	arena_chunk_map_t *mapelm;
24832fe8fb19SBen Gras 	unsigned pageind;
24842fe8fb19SBen Gras 
24852fe8fb19SBen Gras 	assert(ptr != NULL);
24862fe8fb19SBen Gras 	assert(CHUNK_ADDR2BASE(ptr) != ptr);
24872fe8fb19SBen Gras 
24882fe8fb19SBen Gras 	/*
24892fe8fb19SBen Gras 	 * No arena data structures that we query here can change in a way that
24902fe8fb19SBen Gras 	 * affects this function, so we don't need to lock.
24912fe8fb19SBen Gras 	 */
24922fe8fb19SBen Gras 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
24932fe8fb19SBen Gras 	pageind = (unsigned)(((uintptr_t)ptr - (uintptr_t)chunk) >>
24942fe8fb19SBen Gras 	    pagesize_2pow);
24952fe8fb19SBen Gras 	mapelm = &chunk->map[pageind];
24962fe8fb19SBen Gras 	if (mapelm->pos != 0 || ptr != (char *)((uintptr_t)chunk) + (pageind <<
24972fe8fb19SBen Gras 	    pagesize_2pow)) {
24982fe8fb19SBen Gras 		arena_run_t *run;
24992fe8fb19SBen Gras 
25002fe8fb19SBen Gras 		pageind -= mapelm->pos;
25012fe8fb19SBen Gras 
25022fe8fb19SBen Gras 		run = (arena_run_t *)((uintptr_t)chunk + (pageind <<
25032fe8fb19SBen Gras 		    pagesize_2pow));
25042fe8fb19SBen Gras 		assert(run->magic == ARENA_RUN_MAGIC);
25052fe8fb19SBen Gras 		ret = run->bin->reg_size;
25062fe8fb19SBen Gras 	} else
25072fe8fb19SBen Gras 		ret = mapelm->npages << pagesize_2pow;
25082fe8fb19SBen Gras 
25092fe8fb19SBen Gras 	return (ret);
25102fe8fb19SBen Gras }
25112fe8fb19SBen Gras 
25122fe8fb19SBen Gras static void *
arena_ralloc(void * ptr,size_t size,size_t oldsize)25132fe8fb19SBen Gras arena_ralloc(void *ptr, size_t size, size_t oldsize)
25142fe8fb19SBen Gras {
25152fe8fb19SBen Gras 	void *ret;
25162fe8fb19SBen Gras 
25172fe8fb19SBen Gras 	/* Avoid moving the allocation if the size class would not change. */
25182fe8fb19SBen Gras 	if (size < small_min) {
25192fe8fb19SBen Gras 		if (oldsize < small_min &&
25202fe8fb19SBen Gras 		    ffs((int)(pow2_ceil(size) >> (TINY_MIN_2POW + 1)))
25212fe8fb19SBen Gras 		    == ffs((int)(pow2_ceil(oldsize) >> (TINY_MIN_2POW + 1))))
25222fe8fb19SBen Gras 			goto IN_PLACE;
25232fe8fb19SBen Gras 	} else if (size <= small_max) {
25242fe8fb19SBen Gras 		if (oldsize >= small_min && oldsize <= small_max &&
25252fe8fb19SBen Gras 		    (QUANTUM_CEILING(size) >> opt_quantum_2pow)
25262fe8fb19SBen Gras 		    == (QUANTUM_CEILING(oldsize) >> opt_quantum_2pow))
25272fe8fb19SBen Gras 			goto IN_PLACE;
25282fe8fb19SBen Gras 	} else {
25292fe8fb19SBen Gras 		/*
25302fe8fb19SBen Gras 		 * We make no attempt to resize runs here, though it would be
25312fe8fb19SBen Gras 		 * possible to do so.
25322fe8fb19SBen Gras 		 */
25332fe8fb19SBen Gras 		if (oldsize > small_max && PAGE_CEILING(size) == oldsize)
25342fe8fb19SBen Gras 			goto IN_PLACE;
25352fe8fb19SBen Gras 	}
25362fe8fb19SBen Gras 
25372fe8fb19SBen Gras 	/*
25382fe8fb19SBen Gras 	 * If we get here, then size and oldsize are different enough that we
25392fe8fb19SBen Gras 	 * need to use a different size class.  In that case, fall back to
25402fe8fb19SBen Gras 	 * allocating new space and copying.
25412fe8fb19SBen Gras 	 */
25422fe8fb19SBen Gras 	ret = arena_malloc(choose_arena(), size);
25432fe8fb19SBen Gras 	if (ret == NULL)
25442fe8fb19SBen Gras 		return (NULL);
25452fe8fb19SBen Gras 
25462fe8fb19SBen Gras 	/* Junk/zero-filling were already done by arena_malloc(). */
25472fe8fb19SBen Gras 	if (size < oldsize)
25482fe8fb19SBen Gras 		memcpy(ret, ptr, size);
25492fe8fb19SBen Gras 	else
25502fe8fb19SBen Gras 		memcpy(ret, ptr, oldsize);
25512fe8fb19SBen Gras 	idalloc(ptr);
25522fe8fb19SBen Gras 	return (ret);
25532fe8fb19SBen Gras IN_PLACE:
25542fe8fb19SBen Gras 	if (opt_junk && size < oldsize)
25552fe8fb19SBen Gras 		memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size);
25562fe8fb19SBen Gras 	else if (opt_zero && size > oldsize)
25572fe8fb19SBen Gras 		memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize);
25582fe8fb19SBen Gras 	return (ptr);
25592fe8fb19SBen Gras }
25602fe8fb19SBen Gras 
25612fe8fb19SBen Gras static void
arena_dalloc(arena_t * arena,arena_chunk_t * chunk,void * ptr)25622fe8fb19SBen Gras arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr)
25632fe8fb19SBen Gras {
25642fe8fb19SBen Gras 	unsigned pageind;
25652fe8fb19SBen Gras 	arena_chunk_map_t *mapelm;
25662fe8fb19SBen Gras 	size_t size;
25672fe8fb19SBen Gras 
25682fe8fb19SBen Gras 	assert(arena != NULL);
25692fe8fb19SBen Gras 	assert(arena->magic == ARENA_MAGIC);
25702fe8fb19SBen Gras 	assert(chunk->arena == arena);
25712fe8fb19SBen Gras 	assert(ptr != NULL);
25722fe8fb19SBen Gras 	assert(CHUNK_ADDR2BASE(ptr) != ptr);
25732fe8fb19SBen Gras 
25742fe8fb19SBen Gras 	pageind = (unsigned)(((uintptr_t)ptr - (uintptr_t)chunk) >>
25752fe8fb19SBen Gras 	    pagesize_2pow);
25762fe8fb19SBen Gras 	mapelm = &chunk->map[pageind];
25772fe8fb19SBen Gras 	if (mapelm->pos != 0 || ptr != (char *)((uintptr_t)chunk) + (pageind <<
25782fe8fb19SBen Gras 	    pagesize_2pow)) {
25792fe8fb19SBen Gras 		arena_run_t *run;
25802fe8fb19SBen Gras 		arena_bin_t *bin;
25812fe8fb19SBen Gras 
25822fe8fb19SBen Gras 		/* Small allocation. */
25832fe8fb19SBen Gras 
25842fe8fb19SBen Gras 		pageind -= mapelm->pos;
25852fe8fb19SBen Gras 
25862fe8fb19SBen Gras 		run = (arena_run_t *)((uintptr_t)chunk + (pageind <<
25872fe8fb19SBen Gras 		    pagesize_2pow));
25882fe8fb19SBen Gras 		assert(run->magic == ARENA_RUN_MAGIC);
25892fe8fb19SBen Gras 		bin = run->bin;
25902fe8fb19SBen Gras 		size = bin->reg_size;
25912fe8fb19SBen Gras 
25922fe8fb19SBen Gras 		if (opt_junk)
25932fe8fb19SBen Gras 			memset(ptr, 0x5a, size);
25942fe8fb19SBen Gras 
25952fe8fb19SBen Gras 		malloc_mutex_lock(&arena->mtx);
25962fe8fb19SBen Gras 		arena_run_reg_dalloc(run, bin, ptr, size);
25972fe8fb19SBen Gras 		run->nfree++;
25982fe8fb19SBen Gras 
25992fe8fb19SBen Gras 		if (run->nfree == bin->nregs) {
26002fe8fb19SBen Gras 			/* Deallocate run. */
26012fe8fb19SBen Gras 			if (run == bin->runcur)
26022fe8fb19SBen Gras 				bin->runcur = NULL;
26032fe8fb19SBen Gras 			else if (bin->nregs != 1) {
26042fe8fb19SBen Gras 				/*
26052fe8fb19SBen Gras 				 * This block's conditional is necessary because
26062fe8fb19SBen Gras 				 * if the run only contains one region, then it
26072fe8fb19SBen Gras 				 * never gets inserted into the non-full runs
26082fe8fb19SBen Gras 				 * tree.
26092fe8fb19SBen Gras 				 */
26102fe8fb19SBen Gras 				/* LINTED */
26112fe8fb19SBen Gras 				RB_REMOVE(arena_run_tree_s, &bin->runs, run);
26122fe8fb19SBen Gras 			}
26132fe8fb19SBen Gras #ifdef MALLOC_DEBUG
26142fe8fb19SBen Gras 			run->magic = 0;
26152fe8fb19SBen Gras #endif
26162fe8fb19SBen Gras 			arena_run_dalloc(arena, run, bin->run_size);
26172fe8fb19SBen Gras #ifdef MALLOC_STATS
26182fe8fb19SBen Gras 			bin->stats.curruns--;
26192fe8fb19SBen Gras #endif
26202fe8fb19SBen Gras 		} else if (run->nfree == 1 && run != bin->runcur) {
26212fe8fb19SBen Gras 			/*
26222fe8fb19SBen Gras 			 * Make sure that bin->runcur always refers to the
26232fe8fb19SBen Gras 			 * lowest non-full run, if one exists.
26242fe8fb19SBen Gras 			 */
26252fe8fb19SBen Gras 			if (bin->runcur == NULL)
26262fe8fb19SBen Gras 				bin->runcur = run;
26272fe8fb19SBen Gras 			else if ((uintptr_t)run < (uintptr_t)bin->runcur) {
26282fe8fb19SBen Gras 				/* Switch runcur. */
26292fe8fb19SBen Gras 				if (bin->runcur->nfree > 0) {
26302fe8fb19SBen Gras 					/* Insert runcur. */
26312fe8fb19SBen Gras 					/* LINTED */
26322fe8fb19SBen Gras 					RB_INSERT(arena_run_tree_s, &bin->runs,
26332fe8fb19SBen Gras 					    bin->runcur);
26342fe8fb19SBen Gras 				}
26352fe8fb19SBen Gras 				bin->runcur = run;
26362fe8fb19SBen Gras 			} else {
26372fe8fb19SBen Gras 				/* LINTED */
26382fe8fb19SBen Gras 				RB_INSERT(arena_run_tree_s, &bin->runs, run);
26392fe8fb19SBen Gras 			}
26402fe8fb19SBen Gras 		}
26412fe8fb19SBen Gras #ifdef MALLOC_STATS
26422fe8fb19SBen Gras 		arena->stats.allocated_small -= size;
26432fe8fb19SBen Gras 		arena->stats.ndalloc_small++;
26442fe8fb19SBen Gras #endif
26452fe8fb19SBen Gras 	} else {
26462fe8fb19SBen Gras 		/* Large allocation. */
26472fe8fb19SBen Gras 
26482fe8fb19SBen Gras 		size = mapelm->npages << pagesize_2pow;
26492fe8fb19SBen Gras 		assert((((uintptr_t)ptr) & pagesize_mask) == 0);
26502fe8fb19SBen Gras 
26512fe8fb19SBen Gras 		if (opt_junk)
26522fe8fb19SBen Gras 			memset(ptr, 0x5a, size);
26532fe8fb19SBen Gras 
26542fe8fb19SBen Gras 		malloc_mutex_lock(&arena->mtx);
26552fe8fb19SBen Gras 		arena_run_dalloc(arena, (arena_run_t *)ptr, size);
26562fe8fb19SBen Gras #ifdef MALLOC_STATS
26572fe8fb19SBen Gras 		arena->stats.allocated_large -= size;
26582fe8fb19SBen Gras 		arena->stats.ndalloc_large++;
26592fe8fb19SBen Gras #endif
26602fe8fb19SBen Gras 	}
26612fe8fb19SBen Gras 
26622fe8fb19SBen Gras 	malloc_mutex_unlock(&arena->mtx);
26632fe8fb19SBen Gras }
26642fe8fb19SBen Gras 
26652fe8fb19SBen Gras static bool
arena_new(arena_t * arena)26662fe8fb19SBen Gras arena_new(arena_t *arena)
26672fe8fb19SBen Gras {
26682fe8fb19SBen Gras 	unsigned i;
26692fe8fb19SBen Gras 	arena_bin_t *bin;
26702fe8fb19SBen Gras 	size_t prev_run_size;
26712fe8fb19SBen Gras 
26722fe8fb19SBen Gras 	malloc_mutex_init(&arena->mtx);
26732fe8fb19SBen Gras 
26742fe8fb19SBen Gras #ifdef MALLOC_STATS
26752fe8fb19SBen Gras 	memset(&arena->stats, 0, sizeof(arena_stats_t));
26762fe8fb19SBen Gras #endif
26772fe8fb19SBen Gras 
26782fe8fb19SBen Gras 	/* Initialize chunks. */
26792fe8fb19SBen Gras 	RB_INIT(&arena->chunks);
26802fe8fb19SBen Gras 	arena->spare = NULL;
26812fe8fb19SBen Gras 
26822fe8fb19SBen Gras 	/* Initialize bins. */
26832fe8fb19SBen Gras 	prev_run_size = pagesize;
26842fe8fb19SBen Gras 
26852fe8fb19SBen Gras 	/* (2^n)-spaced tiny bins. */
26862fe8fb19SBen Gras 	for (i = 0; i < ntbins; i++) {
26872fe8fb19SBen Gras 		bin = &arena->bins[i];
26882fe8fb19SBen Gras 		bin->runcur = NULL;
26892fe8fb19SBen Gras 		RB_INIT(&bin->runs);
26902fe8fb19SBen Gras 
26912fe8fb19SBen Gras 		bin->reg_size = (1 << (TINY_MIN_2POW + i));
26922fe8fb19SBen Gras 		prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
26932fe8fb19SBen Gras 
26942fe8fb19SBen Gras #ifdef MALLOC_STATS
26952fe8fb19SBen Gras 		memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
26962fe8fb19SBen Gras #endif
26972fe8fb19SBen Gras 	}
26982fe8fb19SBen Gras 
26992fe8fb19SBen Gras 	/* Quantum-spaced bins. */
27002fe8fb19SBen Gras 	for (; i < ntbins + nqbins; i++) {
27012fe8fb19SBen Gras 		bin = &arena->bins[i];
27022fe8fb19SBen Gras 		bin->runcur = NULL;
27032fe8fb19SBen Gras 		RB_INIT(&bin->runs);
27042fe8fb19SBen Gras 
27052fe8fb19SBen Gras 		bin->reg_size = quantum * (i - ntbins + 1);
27062fe8fb19SBen Gras /*
27072fe8fb19SBen Gras 		pow2_size = pow2_ceil(quantum * (i - ntbins + 1));
27082fe8fb19SBen Gras */
27092fe8fb19SBen Gras 		prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
27102fe8fb19SBen Gras 
27112fe8fb19SBen Gras #ifdef MALLOC_STATS
27122fe8fb19SBen Gras 		memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
27132fe8fb19SBen Gras #endif
27142fe8fb19SBen Gras 	}
27152fe8fb19SBen Gras 
27162fe8fb19SBen Gras 	/* (2^n)-spaced sub-page bins. */
27172fe8fb19SBen Gras 	for (; i < ntbins + nqbins + nsbins; i++) {
27182fe8fb19SBen Gras 		bin = &arena->bins[i];
27192fe8fb19SBen Gras 		bin->runcur = NULL;
27202fe8fb19SBen Gras 		RB_INIT(&bin->runs);
27212fe8fb19SBen Gras 
27222fe8fb19SBen Gras 		bin->reg_size = (small_max << (i - (ntbins + nqbins) + 1));
27232fe8fb19SBen Gras 
27242fe8fb19SBen Gras 		prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
27252fe8fb19SBen Gras 
27262fe8fb19SBen Gras #ifdef MALLOC_STATS
27272fe8fb19SBen Gras 		memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
27282fe8fb19SBen Gras #endif
27292fe8fb19SBen Gras 	}
27302fe8fb19SBen Gras 
27312fe8fb19SBen Gras #ifdef MALLOC_DEBUG
27322fe8fb19SBen Gras 	arena->magic = ARENA_MAGIC;
27332fe8fb19SBen Gras #endif
27342fe8fb19SBen Gras 
27352fe8fb19SBen Gras 	return (false);
27362fe8fb19SBen Gras }
27372fe8fb19SBen Gras 
27382fe8fb19SBen Gras /* Create a new arena and insert it into the arenas array at index ind. */
27392fe8fb19SBen Gras static arena_t *
arenas_extend(unsigned ind)27402fe8fb19SBen Gras arenas_extend(unsigned ind)
27412fe8fb19SBen Gras {
27422fe8fb19SBen Gras 	arena_t *ret;
27432fe8fb19SBen Gras 
27442fe8fb19SBen Gras 	/* Allocate enough space for trailing bins. */
27452fe8fb19SBen Gras 	ret = (arena_t *)base_alloc(sizeof(arena_t)
27462fe8fb19SBen Gras 	    + (sizeof(arena_bin_t) * (ntbins + nqbins + nsbins - 1)));
27472fe8fb19SBen Gras 	if (ret != NULL && arena_new(ret) == false) {
27482fe8fb19SBen Gras 		arenas[ind] = ret;
27492fe8fb19SBen Gras 		return (ret);
27502fe8fb19SBen Gras 	}
27512fe8fb19SBen Gras 	/* Only reached if there is an OOM error. */
27522fe8fb19SBen Gras 
27532fe8fb19SBen Gras 	/*
27542fe8fb19SBen Gras 	 * OOM here is quite inconvenient to propagate, since dealing with it
27552fe8fb19SBen Gras 	 * would require a check for failure in the fast path.  Instead, punt
27562fe8fb19SBen Gras 	 * by using arenas[0].  In practice, this is an extremely unlikely
27572fe8fb19SBen Gras 	 * failure.
27582fe8fb19SBen Gras 	 */
27592fe8fb19SBen Gras 	_malloc_message(getprogname(),
27602fe8fb19SBen Gras 	    ": (malloc) Error initializing arena\n", "", "");
27612fe8fb19SBen Gras 	if (opt_abort)
27622fe8fb19SBen Gras 		abort();
27632fe8fb19SBen Gras 
27642fe8fb19SBen Gras 	return (arenas[0]);
27652fe8fb19SBen Gras }
27662fe8fb19SBen Gras 
27672fe8fb19SBen Gras /*
27682fe8fb19SBen Gras  * End arena.
27692fe8fb19SBen Gras  */
27702fe8fb19SBen Gras /******************************************************************************/
27712fe8fb19SBen Gras /*
27722fe8fb19SBen Gras  * Begin general internal functions.
27732fe8fb19SBen Gras  */
27742fe8fb19SBen Gras 
27752fe8fb19SBen Gras static void *
huge_malloc(size_t size)27762fe8fb19SBen Gras huge_malloc(size_t size)
27772fe8fb19SBen Gras {
27782fe8fb19SBen Gras 	void *ret;
27792fe8fb19SBen Gras 	size_t csize;
27802fe8fb19SBen Gras 	chunk_node_t *node;
27812fe8fb19SBen Gras 
27822fe8fb19SBen Gras 	/* Allocate one or more contiguous chunks for this request. */
27832fe8fb19SBen Gras 
27842fe8fb19SBen Gras 	csize = CHUNK_CEILING(size);
27852fe8fb19SBen Gras 	if (csize == 0) {
27862fe8fb19SBen Gras 		/* size is large enough to cause size_t wrap-around. */
27872fe8fb19SBen Gras 		return (NULL);
27882fe8fb19SBen Gras 	}
27892fe8fb19SBen Gras 
27902fe8fb19SBen Gras 	/* Allocate a chunk node with which to track the chunk. */
27912fe8fb19SBen Gras 	node = base_chunk_node_alloc();
27922fe8fb19SBen Gras 	if (node == NULL)
27932fe8fb19SBen Gras 		return (NULL);
27942fe8fb19SBen Gras 
27952fe8fb19SBen Gras 	ret = chunk_alloc(csize);
27962fe8fb19SBen Gras 	if (ret == NULL) {
27972fe8fb19SBen Gras 		base_chunk_node_dealloc(node);
27982fe8fb19SBen Gras 		return (NULL);
27992fe8fb19SBen Gras 	}
28002fe8fb19SBen Gras 
28012fe8fb19SBen Gras 	/* Insert node into huge. */
28022fe8fb19SBen Gras 	node->chunk = ret;
28032fe8fb19SBen Gras 	node->size = csize;
28042fe8fb19SBen Gras 
28052fe8fb19SBen Gras 	malloc_mutex_lock(&chunks_mtx);
28062fe8fb19SBen Gras 	RB_INSERT(chunk_tree_s, &huge, node);
28072fe8fb19SBen Gras #ifdef MALLOC_STATS
28082fe8fb19SBen Gras 	huge_nmalloc++;
28092fe8fb19SBen Gras 	huge_allocated += csize;
28102fe8fb19SBen Gras #endif
28112fe8fb19SBen Gras 	malloc_mutex_unlock(&chunks_mtx);
28122fe8fb19SBen Gras 
28132fe8fb19SBen Gras 	if (opt_junk)
28142fe8fb19SBen Gras 		memset(ret, 0xa5, csize);
28152fe8fb19SBen Gras 	else if (opt_zero)
28162fe8fb19SBen Gras 		memset(ret, 0, csize);
28172fe8fb19SBen Gras 
28182fe8fb19SBen Gras 	return (ret);
28192fe8fb19SBen Gras }
28202fe8fb19SBen Gras 
28212fe8fb19SBen Gras /* Only handles large allocations that require more than chunk alignment. */
28222fe8fb19SBen Gras static void *
huge_palloc(size_t alignment,size_t size)28232fe8fb19SBen Gras huge_palloc(size_t alignment, size_t size)
28242fe8fb19SBen Gras {
28252fe8fb19SBen Gras 	void *ret;
28262fe8fb19SBen Gras 	size_t alloc_size, chunk_size, offset;
28272fe8fb19SBen Gras 	chunk_node_t *node;
28282fe8fb19SBen Gras 
28292fe8fb19SBen Gras 	/*
28302fe8fb19SBen Gras 	 * This allocation requires alignment that is even larger than chunk
28312fe8fb19SBen Gras 	 * alignment.  This means that huge_malloc() isn't good enough.
28322fe8fb19SBen Gras 	 *
28332fe8fb19SBen Gras 	 * Allocate almost twice as many chunks as are demanded by the size or
28342fe8fb19SBen Gras 	 * alignment, in order to assure the alignment can be achieved, then
28352fe8fb19SBen Gras 	 * unmap leading and trailing chunks.
28362fe8fb19SBen Gras 	 */
28372fe8fb19SBen Gras 	assert(alignment >= chunksize);
28382fe8fb19SBen Gras 
28392fe8fb19SBen Gras 	chunk_size = CHUNK_CEILING(size);
28402fe8fb19SBen Gras 
28412fe8fb19SBen Gras 	if (size >= alignment)
28422fe8fb19SBen Gras 		alloc_size = chunk_size + alignment - chunksize;
28432fe8fb19SBen Gras 	else
28442fe8fb19SBen Gras 		alloc_size = (alignment << 1) - chunksize;
28452fe8fb19SBen Gras 
28462fe8fb19SBen Gras 	/* Allocate a chunk node with which to track the chunk. */
28472fe8fb19SBen Gras 	node = base_chunk_node_alloc();
28482fe8fb19SBen Gras 	if (node == NULL)
28492fe8fb19SBen Gras 		return (NULL);
28502fe8fb19SBen Gras 
28512fe8fb19SBen Gras 	ret = chunk_alloc(alloc_size);
28522fe8fb19SBen Gras 	if (ret == NULL) {
28532fe8fb19SBen Gras 		base_chunk_node_dealloc(node);
28542fe8fb19SBen Gras 		return (NULL);
28552fe8fb19SBen Gras 	}
28562fe8fb19SBen Gras 
28572fe8fb19SBen Gras 	offset = (uintptr_t)ret & (alignment - 1);
28582fe8fb19SBen Gras 	assert((offset & chunksize_mask) == 0);
28592fe8fb19SBen Gras 	assert(offset < alloc_size);
28602fe8fb19SBen Gras 	if (offset == 0) {
28612fe8fb19SBen Gras 		/* Trim trailing space. */
28622fe8fb19SBen Gras 		chunk_dealloc((void *)((uintptr_t)ret + chunk_size), alloc_size
28632fe8fb19SBen Gras 		    - chunk_size);
28642fe8fb19SBen Gras 	} else {
28652fe8fb19SBen Gras 		size_t trailsize;
28662fe8fb19SBen Gras 
28672fe8fb19SBen Gras 		/* Trim leading space. */
28682fe8fb19SBen Gras 		chunk_dealloc(ret, alignment - offset);
28692fe8fb19SBen Gras 
28702fe8fb19SBen Gras 		ret = (void *)((uintptr_t)ret + (alignment - offset));
28712fe8fb19SBen Gras 
28722fe8fb19SBen Gras 		trailsize = alloc_size - (alignment - offset) - chunk_size;
28732fe8fb19SBen Gras 		if (trailsize != 0) {
28742fe8fb19SBen Gras 		    /* Trim trailing space. */
28752fe8fb19SBen Gras 		    assert(trailsize < alloc_size);
28762fe8fb19SBen Gras 		    chunk_dealloc((void *)((uintptr_t)ret + chunk_size),
28772fe8fb19SBen Gras 			trailsize);
28782fe8fb19SBen Gras 		}
28792fe8fb19SBen Gras 	}
28802fe8fb19SBen Gras 
28812fe8fb19SBen Gras 	/* Insert node into huge. */
28822fe8fb19SBen Gras 	node->chunk = ret;
28832fe8fb19SBen Gras 	node->size = chunk_size;
28842fe8fb19SBen Gras 
28852fe8fb19SBen Gras 	malloc_mutex_lock(&chunks_mtx);
28862fe8fb19SBen Gras 	RB_INSERT(chunk_tree_s, &huge, node);
28872fe8fb19SBen Gras #ifdef MALLOC_STATS
28882fe8fb19SBen Gras 	huge_nmalloc++;
28892fe8fb19SBen Gras 	huge_allocated += chunk_size;
28902fe8fb19SBen Gras #endif
28912fe8fb19SBen Gras 	malloc_mutex_unlock(&chunks_mtx);
28922fe8fb19SBen Gras 
28932fe8fb19SBen Gras 	if (opt_junk)
28942fe8fb19SBen Gras 		memset(ret, 0xa5, chunk_size);
28952fe8fb19SBen Gras 	else if (opt_zero)
28962fe8fb19SBen Gras 		memset(ret, 0, chunk_size);
28972fe8fb19SBen Gras 
28982fe8fb19SBen Gras 	return (ret);
28992fe8fb19SBen Gras }
29002fe8fb19SBen Gras 
29012fe8fb19SBen Gras static void *
huge_ralloc(void * ptr,size_t size,size_t oldsize)29022fe8fb19SBen Gras huge_ralloc(void *ptr, size_t size, size_t oldsize)
29032fe8fb19SBen Gras {
29042fe8fb19SBen Gras 	void *ret;
29052fe8fb19SBen Gras 
29062fe8fb19SBen Gras 	/* Avoid moving the allocation if the size class would not change. */
29072fe8fb19SBen Gras 	if (oldsize > arena_maxclass &&
29082fe8fb19SBen Gras 	    CHUNK_CEILING(size) == CHUNK_CEILING(oldsize)) {
29092fe8fb19SBen Gras 		if (opt_junk && size < oldsize) {
29102fe8fb19SBen Gras 			memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize
29112fe8fb19SBen Gras 			    - size);
29122fe8fb19SBen Gras 		} else if (opt_zero && size > oldsize) {
29132fe8fb19SBen Gras 			memset((void *)((uintptr_t)ptr + oldsize), 0, size
29142fe8fb19SBen Gras 			    - oldsize);
29152fe8fb19SBen Gras 		}
29162fe8fb19SBen Gras 		return (ptr);
29172fe8fb19SBen Gras 	}
29182fe8fb19SBen Gras 
29192fe8fb19SBen Gras 	if (CHUNK_ADDR2BASE(ptr) == ptr
29202fe8fb19SBen Gras #ifdef USE_BRK
29212fe8fb19SBen Gras 	    && ((uintptr_t)ptr < (uintptr_t)brk_base
29222fe8fb19SBen Gras 	    || (uintptr_t)ptr >= (uintptr_t)brk_max)
29232fe8fb19SBen Gras #endif
29242fe8fb19SBen Gras 	    ) {
29252fe8fb19SBen Gras 		chunk_node_t *node, key;
29262fe8fb19SBen Gras 		void *newptr;
29272fe8fb19SBen Gras 		size_t oldcsize;
29282fe8fb19SBen Gras 		size_t newcsize;
29292fe8fb19SBen Gras 
29302fe8fb19SBen Gras 		newcsize = CHUNK_CEILING(size);
29312fe8fb19SBen Gras 		oldcsize = CHUNK_CEILING(oldsize);
29322fe8fb19SBen Gras 		assert(oldcsize != newcsize);
29332fe8fb19SBen Gras 		if (newcsize == 0) {
29342fe8fb19SBen Gras 			/* size_t wrap-around */
29352fe8fb19SBen Gras 			return (NULL);
29362fe8fb19SBen Gras 		}
29372fe8fb19SBen Gras 
29382fe8fb19SBen Gras 		/*
29392fe8fb19SBen Gras 		 * Remove the old region from the tree now.  If mremap()
29402fe8fb19SBen Gras 		 * returns the region to the system, other thread may
29412fe8fb19SBen Gras 		 * map it for same huge allocation and insert it to the
29422fe8fb19SBen Gras 		 * tree before we acquire the mutex lock again.
29432fe8fb19SBen Gras 		 */
29442fe8fb19SBen Gras 		malloc_mutex_lock(&chunks_mtx);
29452fe8fb19SBen Gras 		key.chunk = __DECONST(void *, ptr);
29462fe8fb19SBen Gras 		/* LINTED */
29472fe8fb19SBen Gras 		node = RB_FIND(chunk_tree_s, &huge, &key);
29482fe8fb19SBen Gras 		assert(node != NULL);
29492fe8fb19SBen Gras 		assert(node->chunk == ptr);
29502fe8fb19SBen Gras 		assert(node->size == oldcsize);
29512fe8fb19SBen Gras 		RB_REMOVE(chunk_tree_s, &huge, node);
29522fe8fb19SBen Gras 		malloc_mutex_unlock(&chunks_mtx);
29532fe8fb19SBen Gras 
29542fe8fb19SBen Gras 		newptr = mremap(ptr, oldcsize, NULL, newcsize,
29552fe8fb19SBen Gras 		    MAP_ALIGNED(chunksize_2pow));
29562fe8fb19SBen Gras 		if (newptr == MAP_FAILED) {
29572fe8fb19SBen Gras 			/* We still own the old region. */
29582fe8fb19SBen Gras 			malloc_mutex_lock(&chunks_mtx);
29592fe8fb19SBen Gras 			RB_INSERT(chunk_tree_s, &huge, node);
29602fe8fb19SBen Gras 			malloc_mutex_unlock(&chunks_mtx);
29612fe8fb19SBen Gras 		} else {
29622fe8fb19SBen Gras 			assert(CHUNK_ADDR2BASE(newptr) == newptr);
29632fe8fb19SBen Gras 
29642fe8fb19SBen Gras 			/* Insert new or resized old region. */
29652fe8fb19SBen Gras 			malloc_mutex_lock(&chunks_mtx);
29662fe8fb19SBen Gras 			node->size = newcsize;
29672fe8fb19SBen Gras 			node->chunk = newptr;
29682fe8fb19SBen Gras 			RB_INSERT(chunk_tree_s, &huge, node);
29692fe8fb19SBen Gras #ifdef MALLOC_STATS
29702fe8fb19SBen Gras 			huge_nralloc++;
29712fe8fb19SBen Gras 			huge_allocated += newcsize - oldcsize;
29722fe8fb19SBen Gras 			if (newcsize > oldcsize) {
29732fe8fb19SBen Gras 				stats_chunks.curchunks +=
29742fe8fb19SBen Gras 				    (newcsize - oldcsize) / chunksize;
29752fe8fb19SBen Gras 				if (stats_chunks.curchunks >
29762fe8fb19SBen Gras 				    stats_chunks.highchunks)
29772fe8fb19SBen Gras 					stats_chunks.highchunks =
29782fe8fb19SBen Gras 					    stats_chunks.curchunks;
29792fe8fb19SBen Gras 			} else {
29802fe8fb19SBen Gras 				stats_chunks.curchunks -=
29812fe8fb19SBen Gras 				    (oldcsize - newcsize) / chunksize;
29822fe8fb19SBen Gras 			}
29832fe8fb19SBen Gras #endif
29842fe8fb19SBen Gras 			malloc_mutex_unlock(&chunks_mtx);
29852fe8fb19SBen Gras 
29862fe8fb19SBen Gras 			if (opt_junk && size < oldsize) {
29872fe8fb19SBen Gras 				memset((void *)((uintptr_t)newptr + size), 0x5a,
29882fe8fb19SBen Gras 				    newcsize - size);
29892fe8fb19SBen Gras 			} else if (opt_zero && size > oldsize) {
29902fe8fb19SBen Gras 				memset((void *)((uintptr_t)newptr + oldsize), 0,
29912fe8fb19SBen Gras 				    size - oldsize);
29922fe8fb19SBen Gras 			}
29932fe8fb19SBen Gras 			return (newptr);
29942fe8fb19SBen Gras 		}
29952fe8fb19SBen Gras 	}
29962fe8fb19SBen Gras 
29972fe8fb19SBen Gras 	/*
29982fe8fb19SBen Gras 	 * If we get here, then size and oldsize are different enough that we
29992fe8fb19SBen Gras 	 * need to use a different size class.  In that case, fall back to
30002fe8fb19SBen Gras 	 * allocating new space and copying.
30012fe8fb19SBen Gras 	 */
30022fe8fb19SBen Gras 	ret = huge_malloc(size);
30032fe8fb19SBen Gras 	if (ret == NULL)
30042fe8fb19SBen Gras 		return (NULL);
30052fe8fb19SBen Gras 
30062fe8fb19SBen Gras 	if (CHUNK_ADDR2BASE(ptr) == ptr) {
30072fe8fb19SBen Gras 		/* The old allocation is a chunk. */
30082fe8fb19SBen Gras 		if (size < oldsize)
30092fe8fb19SBen Gras 			memcpy(ret, ptr, size);
30102fe8fb19SBen Gras 		else
30112fe8fb19SBen Gras 			memcpy(ret, ptr, oldsize);
30122fe8fb19SBen Gras 	} else {
30132fe8fb19SBen Gras 		/* The old allocation is a region. */
30142fe8fb19SBen Gras 		assert(oldsize < size);
30152fe8fb19SBen Gras 		memcpy(ret, ptr, oldsize);
30162fe8fb19SBen Gras 	}
30172fe8fb19SBen Gras 	idalloc(ptr);
30182fe8fb19SBen Gras 	return (ret);
30192fe8fb19SBen Gras }
30202fe8fb19SBen Gras 
30212fe8fb19SBen Gras static void
huge_dalloc(void * ptr)30222fe8fb19SBen Gras huge_dalloc(void *ptr)
30232fe8fb19SBen Gras {
30242fe8fb19SBen Gras 	chunk_node_t key;
30252fe8fb19SBen Gras 	chunk_node_t *node;
30262fe8fb19SBen Gras 
30272fe8fb19SBen Gras 	malloc_mutex_lock(&chunks_mtx);
30282fe8fb19SBen Gras 
30292fe8fb19SBen Gras 	/* Extract from tree of huge allocations. */
30302fe8fb19SBen Gras 	key.chunk = ptr;
30312fe8fb19SBen Gras 	/* LINTED */
30322fe8fb19SBen Gras 	node = RB_FIND(chunk_tree_s, &huge, &key);
30332fe8fb19SBen Gras 	assert(node != NULL);
30342fe8fb19SBen Gras 	assert(node->chunk == ptr);
30352fe8fb19SBen Gras 	/* LINTED */
30362fe8fb19SBen Gras 	RB_REMOVE(chunk_tree_s, &huge, node);
30372fe8fb19SBen Gras 
30382fe8fb19SBen Gras #ifdef MALLOC_STATS
30392fe8fb19SBen Gras 	huge_ndalloc++;
30402fe8fb19SBen Gras 	huge_allocated -= node->size;
30412fe8fb19SBen Gras #endif
30422fe8fb19SBen Gras 
30432fe8fb19SBen Gras 	malloc_mutex_unlock(&chunks_mtx);
30442fe8fb19SBen Gras 
30452fe8fb19SBen Gras 	/* Unmap chunk. */
30462fe8fb19SBen Gras #ifdef USE_BRK
30472fe8fb19SBen Gras 	if (opt_junk)
30482fe8fb19SBen Gras 		memset(node->chunk, 0x5a, node->size);
30492fe8fb19SBen Gras #endif
30502fe8fb19SBen Gras 	chunk_dealloc(node->chunk, node->size);
30512fe8fb19SBen Gras 
30522fe8fb19SBen Gras 	base_chunk_node_dealloc(node);
30532fe8fb19SBen Gras }
30542fe8fb19SBen Gras 
30552fe8fb19SBen Gras static void *
imalloc(size_t size)30562fe8fb19SBen Gras imalloc(size_t size)
30572fe8fb19SBen Gras {
30582fe8fb19SBen Gras 	void *ret;
30592fe8fb19SBen Gras 
30602fe8fb19SBen Gras 	assert(size != 0);
30612fe8fb19SBen Gras 
30622fe8fb19SBen Gras 	if (size <= arena_maxclass)
30632fe8fb19SBen Gras 		ret = arena_malloc(choose_arena(), size);
30642fe8fb19SBen Gras 	else
30652fe8fb19SBen Gras 		ret = huge_malloc(size);
30662fe8fb19SBen Gras 
30672fe8fb19SBen Gras 	return (ret);
30682fe8fb19SBen Gras }
30692fe8fb19SBen Gras 
30702fe8fb19SBen Gras static void *
ipalloc(size_t alignment,size_t size)30712fe8fb19SBen Gras ipalloc(size_t alignment, size_t size)
30722fe8fb19SBen Gras {
30732fe8fb19SBen Gras 	void *ret;
30742fe8fb19SBen Gras 	size_t ceil_size;
30752fe8fb19SBen Gras 
30762fe8fb19SBen Gras 	/*
30772fe8fb19SBen Gras 	 * Round size up to the nearest multiple of alignment.
30782fe8fb19SBen Gras 	 *
30792fe8fb19SBen Gras 	 * This done, we can take advantage of the fact that for each small
30802fe8fb19SBen Gras 	 * size class, every object is aligned at the smallest power of two
30812fe8fb19SBen Gras 	 * that is non-zero in the base two representation of the size.  For
30822fe8fb19SBen Gras 	 * example:
30832fe8fb19SBen Gras 	 *
30842fe8fb19SBen Gras 	 *   Size |   Base 2 | Minimum alignment
30852fe8fb19SBen Gras 	 *   -----+----------+------------------
30862fe8fb19SBen Gras 	 *     96 |  1100000 |  32
30872fe8fb19SBen Gras 	 *    144 | 10100000 |  32
30882fe8fb19SBen Gras 	 *    192 | 11000000 |  64
30892fe8fb19SBen Gras 	 *
30902fe8fb19SBen Gras 	 * Depending on runtime settings, it is possible that arena_malloc()
30912fe8fb19SBen Gras 	 * will further round up to a power of two, but that never causes
30922fe8fb19SBen Gras 	 * correctness issues.
30932fe8fb19SBen Gras 	 */
30942fe8fb19SBen Gras 	ceil_size = (size + (alignment - 1)) & (-alignment);
30952fe8fb19SBen Gras 	/*
30962fe8fb19SBen Gras 	 * (ceil_size < size) protects against the combination of maximal
30972fe8fb19SBen Gras 	 * alignment and size greater than maximal alignment.
30982fe8fb19SBen Gras 	 */
30992fe8fb19SBen Gras 	if (ceil_size < size) {
31002fe8fb19SBen Gras 		/* size_t overflow. */
31012fe8fb19SBen Gras 		return (NULL);
31022fe8fb19SBen Gras 	}
31032fe8fb19SBen Gras 
31042fe8fb19SBen Gras 	if (ceil_size <= pagesize || (alignment <= pagesize
31052fe8fb19SBen Gras 	    && ceil_size <= arena_maxclass))
31062fe8fb19SBen Gras 		ret = arena_malloc(choose_arena(), ceil_size);
31072fe8fb19SBen Gras 	else {
31082fe8fb19SBen Gras 		size_t run_size;
31092fe8fb19SBen Gras 
31102fe8fb19SBen Gras 		/*
31112fe8fb19SBen Gras 		 * We can't achieve sub-page alignment, so round up alignment
31122fe8fb19SBen Gras 		 * permanently; it makes later calculations simpler.
31132fe8fb19SBen Gras 		 */
31142fe8fb19SBen Gras 		alignment = PAGE_CEILING(alignment);
31152fe8fb19SBen Gras 		ceil_size = PAGE_CEILING(size);
31162fe8fb19SBen Gras 		/*
31172fe8fb19SBen Gras 		 * (ceil_size < size) protects against very large sizes within
31182fe8fb19SBen Gras 		 * pagesize of SIZE_T_MAX.
31192fe8fb19SBen Gras 		 *
31202fe8fb19SBen Gras 		 * (ceil_size + alignment < ceil_size) protects against the
31212fe8fb19SBen Gras 		 * combination of maximal alignment and ceil_size large enough
31222fe8fb19SBen Gras 		 * to cause overflow.  This is similar to the first overflow
31232fe8fb19SBen Gras 		 * check above, but it needs to be repeated due to the new
31242fe8fb19SBen Gras 		 * ceil_size value, which may now be *equal* to maximal
31252fe8fb19SBen Gras 		 * alignment, whereas before we only detected overflow if the
31262fe8fb19SBen Gras 		 * original size was *greater* than maximal alignment.
31272fe8fb19SBen Gras 		 */
31282fe8fb19SBen Gras 		if (ceil_size < size || ceil_size + alignment < ceil_size) {
31292fe8fb19SBen Gras 			/* size_t overflow. */
31302fe8fb19SBen Gras 			return (NULL);
31312fe8fb19SBen Gras 		}
31322fe8fb19SBen Gras 
31332fe8fb19SBen Gras 		/*
31342fe8fb19SBen Gras 		 * Calculate the size of the over-size run that arena_palloc()
31352fe8fb19SBen Gras 		 * would need to allocate in order to guarantee the alignment.
31362fe8fb19SBen Gras 		 */
31372fe8fb19SBen Gras 		if (ceil_size >= alignment)
31382fe8fb19SBen Gras 			run_size = ceil_size + alignment - pagesize;
31392fe8fb19SBen Gras 		else {
31402fe8fb19SBen Gras 			/*
31412fe8fb19SBen Gras 			 * It is possible that (alignment << 1) will cause
31422fe8fb19SBen Gras 			 * overflow, but it doesn't matter because we also
31432fe8fb19SBen Gras 			 * subtract pagesize, which in the case of overflow
31442fe8fb19SBen Gras 			 * leaves us with a very large run_size.  That causes
31452fe8fb19SBen Gras 			 * the first conditional below to fail, which means
31462fe8fb19SBen Gras 			 * that the bogus run_size value never gets used for
31472fe8fb19SBen Gras 			 * anything important.
31482fe8fb19SBen Gras 			 */
31492fe8fb19SBen Gras 			run_size = (alignment << 1) - pagesize;
31502fe8fb19SBen Gras 		}
31512fe8fb19SBen Gras 
31522fe8fb19SBen Gras 		if (run_size <= arena_maxclass) {
31532fe8fb19SBen Gras 			ret = arena_palloc(choose_arena(), alignment, ceil_size,
31542fe8fb19SBen Gras 			    run_size);
31552fe8fb19SBen Gras 		} else if (alignment <= chunksize)
31562fe8fb19SBen Gras 			ret = huge_malloc(ceil_size);
31572fe8fb19SBen Gras 		else
31582fe8fb19SBen Gras 			ret = huge_palloc(alignment, ceil_size);
31592fe8fb19SBen Gras 	}
31602fe8fb19SBen Gras 
31612fe8fb19SBen Gras 	assert(((uintptr_t)ret & (alignment - 1)) == 0);
31622fe8fb19SBen Gras 	return (ret);
31632fe8fb19SBen Gras }
31642fe8fb19SBen Gras 
31652fe8fb19SBen Gras static void *
icalloc(size_t size)31662fe8fb19SBen Gras icalloc(size_t size)
31672fe8fb19SBen Gras {
31682fe8fb19SBen Gras 	void *ret;
31692fe8fb19SBen Gras 
31702fe8fb19SBen Gras 	if (size <= arena_maxclass) {
31712fe8fb19SBen Gras 		ret = arena_malloc(choose_arena(), size);
31722fe8fb19SBen Gras 		if (ret == NULL)
31732fe8fb19SBen Gras 			return (NULL);
31742fe8fb19SBen Gras 		memset(ret, 0, size);
31752fe8fb19SBen Gras 	} else {
31762fe8fb19SBen Gras 		/*
31772fe8fb19SBen Gras 		 * The virtual memory system provides zero-filled pages, so
31782fe8fb19SBen Gras 		 * there is no need to do so manually, unless opt_junk is
31792fe8fb19SBen Gras 		 * enabled, in which case huge_malloc() fills huge allocations
31802fe8fb19SBen Gras 		 * with junk.
31812fe8fb19SBen Gras 		 */
31822fe8fb19SBen Gras 		ret = huge_malloc(size);
31832fe8fb19SBen Gras 		if (ret == NULL)
31842fe8fb19SBen Gras 			return (NULL);
31852fe8fb19SBen Gras 
31862fe8fb19SBen Gras 		if (opt_junk)
31872fe8fb19SBen Gras 			memset(ret, 0, size);
31882fe8fb19SBen Gras #ifdef USE_BRK
31892fe8fb19SBen Gras 		else if ((uintptr_t)ret >= (uintptr_t)brk_base
31902fe8fb19SBen Gras 		    && (uintptr_t)ret < (uintptr_t)brk_max) {
31912fe8fb19SBen Gras 			/*
31922fe8fb19SBen Gras 			 * This may be a re-used brk chunk.  Therefore, zero
31932fe8fb19SBen Gras 			 * the memory.
31942fe8fb19SBen Gras 			 */
31952fe8fb19SBen Gras 			memset(ret, 0, size);
31962fe8fb19SBen Gras 		}
31972fe8fb19SBen Gras #endif
31982fe8fb19SBen Gras 	}
31992fe8fb19SBen Gras 
32002fe8fb19SBen Gras 	return (ret);
32012fe8fb19SBen Gras }
32022fe8fb19SBen Gras 
32032fe8fb19SBen Gras static size_t
isalloc(const void * ptr)32042fe8fb19SBen Gras isalloc(const void *ptr)
32052fe8fb19SBen Gras {
32062fe8fb19SBen Gras 	size_t ret;
32072fe8fb19SBen Gras 	arena_chunk_t *chunk;
32082fe8fb19SBen Gras 
32092fe8fb19SBen Gras 	assert(ptr != NULL);
32102fe8fb19SBen Gras 
32112fe8fb19SBen Gras 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
32122fe8fb19SBen Gras 	if (chunk != ptr) {
32132fe8fb19SBen Gras 		/* Region. */
32142fe8fb19SBen Gras 		assert(chunk->arena->magic == ARENA_MAGIC);
32152fe8fb19SBen Gras 
32162fe8fb19SBen Gras 		ret = arena_salloc(ptr);
32172fe8fb19SBen Gras 	} else {
32182fe8fb19SBen Gras 		chunk_node_t *node, key;
32192fe8fb19SBen Gras 
32202fe8fb19SBen Gras 		/* Chunk (huge allocation). */
32212fe8fb19SBen Gras 
32222fe8fb19SBen Gras 		malloc_mutex_lock(&chunks_mtx);
32232fe8fb19SBen Gras 
32242fe8fb19SBen Gras 		/* Extract from tree of huge allocations. */
32252fe8fb19SBen Gras 		key.chunk = __DECONST(void *, ptr);
32262fe8fb19SBen Gras 		/* LINTED */
32272fe8fb19SBen Gras 		node = RB_FIND(chunk_tree_s, &huge, &key);
32282fe8fb19SBen Gras 		assert(node != NULL);
32292fe8fb19SBen Gras 
32302fe8fb19SBen Gras 		ret = node->size;
32312fe8fb19SBen Gras 
32322fe8fb19SBen Gras 		malloc_mutex_unlock(&chunks_mtx);
32332fe8fb19SBen Gras 	}
32342fe8fb19SBen Gras 
32352fe8fb19SBen Gras 	return (ret);
32362fe8fb19SBen Gras }
32372fe8fb19SBen Gras 
32382fe8fb19SBen Gras static void *
iralloc(void * ptr,size_t size)32392fe8fb19SBen Gras iralloc(void *ptr, size_t size)
32402fe8fb19SBen Gras {
32412fe8fb19SBen Gras 	void *ret;
32422fe8fb19SBen Gras 	size_t oldsize;
32432fe8fb19SBen Gras 
32442fe8fb19SBen Gras 	assert(ptr != NULL);
32452fe8fb19SBen Gras 	assert(size != 0);
32462fe8fb19SBen Gras 
32472fe8fb19SBen Gras 	oldsize = isalloc(ptr);
32482fe8fb19SBen Gras 
32492fe8fb19SBen Gras 	if (size <= arena_maxclass)
32502fe8fb19SBen Gras 		ret = arena_ralloc(ptr, size, oldsize);
32512fe8fb19SBen Gras 	else
32522fe8fb19SBen Gras 		ret = huge_ralloc(ptr, size, oldsize);
32532fe8fb19SBen Gras 
32542fe8fb19SBen Gras 	return (ret);
32552fe8fb19SBen Gras }
32562fe8fb19SBen Gras 
32572fe8fb19SBen Gras static void
idalloc(void * ptr)32582fe8fb19SBen Gras idalloc(void *ptr)
32592fe8fb19SBen Gras {
32602fe8fb19SBen Gras 	arena_chunk_t *chunk;
32612fe8fb19SBen Gras 
32622fe8fb19SBen Gras 	assert(ptr != NULL);
32632fe8fb19SBen Gras 
32642fe8fb19SBen Gras 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
32652fe8fb19SBen Gras 	if (chunk != ptr) {
32662fe8fb19SBen Gras 		/* Region. */
32672fe8fb19SBen Gras 		arena_dalloc(chunk->arena, chunk, ptr);
32682fe8fb19SBen Gras 	} else
32692fe8fb19SBen Gras 		huge_dalloc(ptr);
32702fe8fb19SBen Gras }
32712fe8fb19SBen Gras 
32722fe8fb19SBen Gras static void
malloc_print_stats(void)32732fe8fb19SBen Gras malloc_print_stats(void)
32742fe8fb19SBen Gras {
32752fe8fb19SBen Gras 
32762fe8fb19SBen Gras 	if (opt_print_stats) {
32772fe8fb19SBen Gras 		char s[UMAX2S_BUFSIZE];
32782fe8fb19SBen Gras 		_malloc_message("___ Begin malloc statistics ___\n", "", "",
32792fe8fb19SBen Gras 		    "");
32802fe8fb19SBen Gras 		_malloc_message("Assertions ",
32812fe8fb19SBen Gras #ifdef NDEBUG
32822fe8fb19SBen Gras 		    "disabled",
32832fe8fb19SBen Gras #else
32842fe8fb19SBen Gras 		    "enabled",
32852fe8fb19SBen Gras #endif
32862fe8fb19SBen Gras 		    "\n", "");
32872fe8fb19SBen Gras 		_malloc_message("Boolean MALLOC_OPTIONS: ",
32882fe8fb19SBen Gras 		    opt_abort ? "A" : "a",
32892fe8fb19SBen Gras 		    opt_junk ? "J" : "j",
32902fe8fb19SBen Gras 		    opt_hint ? "H" : "h");
32912fe8fb19SBen Gras 		_malloc_message(opt_utrace ? "PU" : "Pu",
32922fe8fb19SBen Gras 		    opt_sysv ? "V" : "v",
32932fe8fb19SBen Gras 		    opt_xmalloc ? "X" : "x",
32942fe8fb19SBen Gras 		    opt_zero ? "Z\n" : "z\n");
32952fe8fb19SBen Gras 
3296f14fb602SLionel Sambuc 		_malloc_message("CPUs: ", size_t2s(ncpus, s), "\n", "");
3297f14fb602SLionel Sambuc 		_malloc_message("Max arenas: ", size_t2s(narenas, s), "\n", "");
3298f14fb602SLionel Sambuc 		_malloc_message("Pointer size: ", size_t2s(sizeof(void *), s),
32992fe8fb19SBen Gras 		    "\n", "");
3300f14fb602SLionel Sambuc 		_malloc_message("Quantum size: ", size_t2s(quantum, s), "\n", "");
3301f14fb602SLionel Sambuc 		_malloc_message("Max small size: ", size_t2s(small_max, s), "\n",
33022fe8fb19SBen Gras 		    "");
33032fe8fb19SBen Gras 
3304f14fb602SLionel Sambuc 		_malloc_message("Chunk size: ", size_t2s(chunksize, s), "", "");
3305f14fb602SLionel Sambuc 		_malloc_message(" (2^", size_t2s((size_t)opt_chunk_2pow, s),
3306f14fb602SLionel Sambuc 		    ")\n", "");
33072fe8fb19SBen Gras 
33082fe8fb19SBen Gras #ifdef MALLOC_STATS
33092fe8fb19SBen Gras 		{
33102fe8fb19SBen Gras 			size_t allocated, mapped;
33112fe8fb19SBen Gras 			unsigned i;
33122fe8fb19SBen Gras 			arena_t *arena;
33132fe8fb19SBen Gras 
33142fe8fb19SBen Gras 			/* Calculate and print allocated/mapped stats. */
33152fe8fb19SBen Gras 
33162fe8fb19SBen Gras 			/* arenas. */
33172fe8fb19SBen Gras 			for (i = 0, allocated = 0; i < narenas; i++) {
33182fe8fb19SBen Gras 				if (arenas[i] != NULL) {
33192fe8fb19SBen Gras 					malloc_mutex_lock(&arenas[i]->mtx);
33202fe8fb19SBen Gras 					allocated +=
33212fe8fb19SBen Gras 					    arenas[i]->stats.allocated_small;
33222fe8fb19SBen Gras 					allocated +=
33232fe8fb19SBen Gras 					    arenas[i]->stats.allocated_large;
33242fe8fb19SBen Gras 					malloc_mutex_unlock(&arenas[i]->mtx);
33252fe8fb19SBen Gras 				}
33262fe8fb19SBen Gras 			}
33272fe8fb19SBen Gras 
33282fe8fb19SBen Gras 			/* huge/base. */
33292fe8fb19SBen Gras 			malloc_mutex_lock(&chunks_mtx);
33302fe8fb19SBen Gras 			allocated += huge_allocated;
33312fe8fb19SBen Gras 			mapped = stats_chunks.curchunks * chunksize;
33322fe8fb19SBen Gras 			malloc_mutex_unlock(&chunks_mtx);
33332fe8fb19SBen Gras 
33342fe8fb19SBen Gras 			malloc_mutex_lock(&base_mtx);
33352fe8fb19SBen Gras 			mapped += base_mapped;
33362fe8fb19SBen Gras 			malloc_mutex_unlock(&base_mtx);
33372fe8fb19SBen Gras 
33382fe8fb19SBen Gras 			malloc_printf("Allocated: %zu, mapped: %zu\n",
33392fe8fb19SBen Gras 			    allocated, mapped);
33402fe8fb19SBen Gras 
33412fe8fb19SBen Gras 			/* Print chunk stats. */
33422fe8fb19SBen Gras 			{
33432fe8fb19SBen Gras 				chunk_stats_t chunks_stats;
33442fe8fb19SBen Gras 
33452fe8fb19SBen Gras 				malloc_mutex_lock(&chunks_mtx);
33462fe8fb19SBen Gras 				chunks_stats = stats_chunks;
33472fe8fb19SBen Gras 				malloc_mutex_unlock(&chunks_mtx);
33482fe8fb19SBen Gras 
33492fe8fb19SBen Gras 				malloc_printf("chunks: nchunks   "
33502fe8fb19SBen Gras 				    "highchunks    curchunks\n");
33512fe8fb19SBen Gras 				malloc_printf("  %13llu%13lu%13lu\n",
33522fe8fb19SBen Gras 				    chunks_stats.nchunks,
33532fe8fb19SBen Gras 				    chunks_stats.highchunks,
33542fe8fb19SBen Gras 				    chunks_stats.curchunks);
33552fe8fb19SBen Gras 			}
33562fe8fb19SBen Gras 
33572fe8fb19SBen Gras 			/* Print chunk stats. */
33582fe8fb19SBen Gras 			malloc_printf(
33592fe8fb19SBen Gras 			    "huge: nmalloc      ndalloc      "
33602fe8fb19SBen Gras 			    "nralloc    allocated\n");
33612fe8fb19SBen Gras 			malloc_printf(" %12llu %12llu %12llu %12zu\n",
33622fe8fb19SBen Gras 			    huge_nmalloc, huge_ndalloc, huge_nralloc,
33632fe8fb19SBen Gras 			    huge_allocated);
33642fe8fb19SBen Gras 
33652fe8fb19SBen Gras 			/* Print stats for each arena. */
33662fe8fb19SBen Gras 			for (i = 0; i < narenas; i++) {
33672fe8fb19SBen Gras 				arena = arenas[i];
33682fe8fb19SBen Gras 				if (arena != NULL) {
33692fe8fb19SBen Gras 					malloc_printf(
33702fe8fb19SBen Gras 					    "\narenas[%u] @ %p\n", i, arena);
33712fe8fb19SBen Gras 					malloc_mutex_lock(&arena->mtx);
33722fe8fb19SBen Gras 					stats_print(arena);
33732fe8fb19SBen Gras 					malloc_mutex_unlock(&arena->mtx);
33742fe8fb19SBen Gras 				}
33752fe8fb19SBen Gras 			}
33762fe8fb19SBen Gras 		}
33772fe8fb19SBen Gras #endif /* #ifdef MALLOC_STATS */
33782fe8fb19SBen Gras 		_malloc_message("--- End malloc statistics ---\n", "", "", "");
33792fe8fb19SBen Gras 	}
33802fe8fb19SBen Gras }
33812fe8fb19SBen Gras 
33822fe8fb19SBen Gras /*
33832fe8fb19SBen Gras  * FreeBSD's pthreads implementation calls malloc(3), so the malloc
33842fe8fb19SBen Gras  * implementation has to take pains to avoid infinite recursion during
33852fe8fb19SBen Gras  * initialization.
33862fe8fb19SBen Gras  */
33872fe8fb19SBen Gras static inline bool
malloc_init(void)33882fe8fb19SBen Gras malloc_init(void)
33892fe8fb19SBen Gras {
33902fe8fb19SBen Gras 
33912fe8fb19SBen Gras 	if (malloc_initialized == false)
33922fe8fb19SBen Gras 		return (malloc_init_hard());
33932fe8fb19SBen Gras 
33942fe8fb19SBen Gras 	return (false);
33952fe8fb19SBen Gras }
33962fe8fb19SBen Gras 
33972fe8fb19SBen Gras static bool
malloc_init_hard(void)33982fe8fb19SBen Gras malloc_init_hard(void)
33992fe8fb19SBen Gras {
34002fe8fb19SBen Gras 	unsigned i, j;
34012fe8fb19SBen Gras 	ssize_t linklen;
34022fe8fb19SBen Gras 	char buf[PATH_MAX + 1];
34032fe8fb19SBen Gras 	const char *opts = "";
3404f14fb602SLionel Sambuc 	int serrno;
34052fe8fb19SBen Gras 
34062fe8fb19SBen Gras 	malloc_mutex_lock(&init_lock);
34072fe8fb19SBen Gras 	if (malloc_initialized) {
34082fe8fb19SBen Gras 		/*
34092fe8fb19SBen Gras 		 * Another thread initialized the allocator before this one
34102fe8fb19SBen Gras 		 * acquired init_lock.
34112fe8fb19SBen Gras 		 */
34122fe8fb19SBen Gras 		malloc_mutex_unlock(&init_lock);
34132fe8fb19SBen Gras 		return (false);
34142fe8fb19SBen Gras 	}
34152fe8fb19SBen Gras 
3416f14fb602SLionel Sambuc 	serrno = errno;
34172fe8fb19SBen Gras 	/* Get number of CPUs. */
34182fe8fb19SBen Gras 	{
34192fe8fb19SBen Gras 		int mib[2];
34202fe8fb19SBen Gras 		size_t len;
34212fe8fb19SBen Gras 
34222fe8fb19SBen Gras 		mib[0] = CTL_HW;
34232fe8fb19SBen Gras 		mib[1] = HW_NCPU;
34242fe8fb19SBen Gras 		len = sizeof(ncpus);
34252fe8fb19SBen Gras 		if (sysctl(mib, 2, &ncpus, &len, (void *) 0, 0) == -1) {
34262fe8fb19SBen Gras 			/* Error. */
34272fe8fb19SBen Gras 			ncpus = 1;
34282fe8fb19SBen Gras 		}
34292fe8fb19SBen Gras 	}
34302fe8fb19SBen Gras 
34312fe8fb19SBen Gras 	/* Get page size. */
34322fe8fb19SBen Gras 	{
34332fe8fb19SBen Gras 		long result;
34342fe8fb19SBen Gras 
34352fe8fb19SBen Gras 		result = sysconf(_SC_PAGESIZE);
34362fe8fb19SBen Gras 		assert(result != -1);
34372fe8fb19SBen Gras 		pagesize = (unsigned) result;
34382fe8fb19SBen Gras 
34392fe8fb19SBen Gras 		/*
34402fe8fb19SBen Gras 		 * We assume that pagesize is a power of 2 when calculating
34412fe8fb19SBen Gras 		 * pagesize_mask and pagesize_2pow.
34422fe8fb19SBen Gras 		 */
34432fe8fb19SBen Gras 		assert(((result - 1) & result) == 0);
34442fe8fb19SBen Gras 		pagesize_mask = result - 1;
34452fe8fb19SBen Gras 		pagesize_2pow = ffs((int)result) - 1;
34462fe8fb19SBen Gras 	}
34472fe8fb19SBen Gras 
34482fe8fb19SBen Gras 	for (i = 0; i < 3; i++) {
34492fe8fb19SBen Gras 		/* Get runtime configuration. */
34502fe8fb19SBen Gras 		switch (i) {
34512fe8fb19SBen Gras 		case 0:
34522fe8fb19SBen Gras 			if ((linklen = readlink("/etc/malloc.conf", buf,
34532fe8fb19SBen Gras 						sizeof(buf) - 1)) != -1) {
34542fe8fb19SBen Gras 				/*
34552fe8fb19SBen Gras 				 * Use the contents of the "/etc/malloc.conf"
34562fe8fb19SBen Gras 				 * symbolic link's name.
34572fe8fb19SBen Gras 				 */
34582fe8fb19SBen Gras 				buf[linklen] = '\0';
34592fe8fb19SBen Gras 				opts = buf;
34602fe8fb19SBen Gras 			} else {
34612fe8fb19SBen Gras 				/* No configuration specified. */
34622fe8fb19SBen Gras 				buf[0] = '\0';
34632fe8fb19SBen Gras 				opts = buf;
34642fe8fb19SBen Gras 			}
34652fe8fb19SBen Gras 			break;
34662fe8fb19SBen Gras 		case 1:
34672fe8fb19SBen Gras 			if ((opts = getenv("MALLOC_OPTIONS")) != NULL &&
34682fe8fb19SBen Gras 			    issetugid() == 0) {
34692fe8fb19SBen Gras 				/*
34702fe8fb19SBen Gras 				 * Do nothing; opts is already initialized to
34712fe8fb19SBen Gras 				 * the value of the MALLOC_OPTIONS environment
34722fe8fb19SBen Gras 				 * variable.
34732fe8fb19SBen Gras 				 */
34742fe8fb19SBen Gras 			} else {
34752fe8fb19SBen Gras 				/* No configuration specified. */
34762fe8fb19SBen Gras 				buf[0] = '\0';
34772fe8fb19SBen Gras 				opts = buf;
34782fe8fb19SBen Gras 			}
34792fe8fb19SBen Gras 			break;
34802fe8fb19SBen Gras 		case 2:
34812fe8fb19SBen Gras 			if (_malloc_options != NULL) {
34822fe8fb19SBen Gras 			    /*
34832fe8fb19SBen Gras 			     * Use options that were compiled into the program.
34842fe8fb19SBen Gras 			     */
34852fe8fb19SBen Gras 			    opts = _malloc_options;
34862fe8fb19SBen Gras 			} else {
34872fe8fb19SBen Gras 				/* No configuration specified. */
34882fe8fb19SBen Gras 				buf[0] = '\0';
34892fe8fb19SBen Gras 				opts = buf;
34902fe8fb19SBen Gras 			}
34912fe8fb19SBen Gras 			break;
34922fe8fb19SBen Gras 		default:
34932fe8fb19SBen Gras 			/* NOTREACHED */
34942fe8fb19SBen Gras 			/* LINTED */
34952fe8fb19SBen Gras 			assert(false);
34962fe8fb19SBen Gras 		}
34972fe8fb19SBen Gras 
34982fe8fb19SBen Gras 		for (j = 0; opts[j] != '\0'; j++) {
34992fe8fb19SBen Gras 			switch (opts[j]) {
35002fe8fb19SBen Gras 			case 'a':
35012fe8fb19SBen Gras 				opt_abort = false;
35022fe8fb19SBen Gras 				break;
35032fe8fb19SBen Gras 			case 'A':
35042fe8fb19SBen Gras 				opt_abort = true;
35052fe8fb19SBen Gras 				break;
35062fe8fb19SBen Gras 			case 'h':
35072fe8fb19SBen Gras 				opt_hint = false;
35082fe8fb19SBen Gras 				break;
35092fe8fb19SBen Gras 			case 'H':
35102fe8fb19SBen Gras 				opt_hint = true;
35112fe8fb19SBen Gras 				break;
35122fe8fb19SBen Gras 			case 'j':
35132fe8fb19SBen Gras 				opt_junk = false;
35142fe8fb19SBen Gras 				break;
35152fe8fb19SBen Gras 			case 'J':
35162fe8fb19SBen Gras 				opt_junk = true;
35172fe8fb19SBen Gras 				break;
35182fe8fb19SBen Gras 			case 'k':
35192fe8fb19SBen Gras 				/*
35202fe8fb19SBen Gras 				 * Chunks always require at least one header
35212fe8fb19SBen Gras 				 * page, so chunks can never be smaller than
35222fe8fb19SBen Gras 				 * two pages.
35232fe8fb19SBen Gras 				 */
35242fe8fb19SBen Gras 				if (opt_chunk_2pow > pagesize_2pow + 1)
35252fe8fb19SBen Gras 					opt_chunk_2pow--;
35262fe8fb19SBen Gras 				break;
35272fe8fb19SBen Gras 			case 'K':
35282fe8fb19SBen Gras 				if (opt_chunk_2pow + 1 <
35292fe8fb19SBen Gras 				    (int)(sizeof(size_t) << 3))
35302fe8fb19SBen Gras 					opt_chunk_2pow++;
35312fe8fb19SBen Gras 				break;
35322fe8fb19SBen Gras 			case 'n':
35332fe8fb19SBen Gras 				opt_narenas_lshift--;
35342fe8fb19SBen Gras 				break;
35352fe8fb19SBen Gras 			case 'N':
35362fe8fb19SBen Gras 				opt_narenas_lshift++;
35372fe8fb19SBen Gras 				break;
35382fe8fb19SBen Gras 			case 'p':
35392fe8fb19SBen Gras 				opt_print_stats = false;
35402fe8fb19SBen Gras 				break;
35412fe8fb19SBen Gras 			case 'P':
35422fe8fb19SBen Gras 				opt_print_stats = true;
35432fe8fb19SBen Gras 				break;
35442fe8fb19SBen Gras 			case 'q':
35452fe8fb19SBen Gras 				if (opt_quantum_2pow > QUANTUM_2POW_MIN)
35462fe8fb19SBen Gras 					opt_quantum_2pow--;
35472fe8fb19SBen Gras 				break;
35482fe8fb19SBen Gras 			case 'Q':
35492fe8fb19SBen Gras 				if (opt_quantum_2pow < pagesize_2pow - 1)
35502fe8fb19SBen Gras 					opt_quantum_2pow++;
35512fe8fb19SBen Gras 				break;
35522fe8fb19SBen Gras 			case 's':
35532fe8fb19SBen Gras 				if (opt_small_max_2pow > QUANTUM_2POW_MIN)
35542fe8fb19SBen Gras 					opt_small_max_2pow--;
35552fe8fb19SBen Gras 				break;
35562fe8fb19SBen Gras 			case 'S':
35572fe8fb19SBen Gras 				if (opt_small_max_2pow < pagesize_2pow - 1)
35582fe8fb19SBen Gras 					opt_small_max_2pow++;
35592fe8fb19SBen Gras 				break;
35602fe8fb19SBen Gras 			case 'u':
35612fe8fb19SBen Gras 				opt_utrace = false;
35622fe8fb19SBen Gras 				break;
35632fe8fb19SBen Gras 			case 'U':
35642fe8fb19SBen Gras 				opt_utrace = true;
35652fe8fb19SBen Gras 				break;
35662fe8fb19SBen Gras 			case 'v':
35672fe8fb19SBen Gras 				opt_sysv = false;
35682fe8fb19SBen Gras 				break;
35692fe8fb19SBen Gras 			case 'V':
35702fe8fb19SBen Gras 				opt_sysv = true;
35712fe8fb19SBen Gras 				break;
35722fe8fb19SBen Gras 			case 'x':
35732fe8fb19SBen Gras 				opt_xmalloc = false;
35742fe8fb19SBen Gras 				break;
35752fe8fb19SBen Gras 			case 'X':
35762fe8fb19SBen Gras 				opt_xmalloc = true;
35772fe8fb19SBen Gras 				break;
35782fe8fb19SBen Gras 			case 'z':
35792fe8fb19SBen Gras 				opt_zero = false;
35802fe8fb19SBen Gras 				break;
35812fe8fb19SBen Gras 			case 'Z':
35822fe8fb19SBen Gras 				opt_zero = true;
35832fe8fb19SBen Gras 				break;
35842fe8fb19SBen Gras 			default: {
35852fe8fb19SBen Gras 				char cbuf[2];
35862fe8fb19SBen Gras 
35872fe8fb19SBen Gras 				cbuf[0] = opts[j];
35882fe8fb19SBen Gras 				cbuf[1] = '\0';
35892fe8fb19SBen Gras 				_malloc_message(getprogname(),
35902fe8fb19SBen Gras 				    ": (malloc) Unsupported character in "
35912fe8fb19SBen Gras 				    "malloc options: '", cbuf, "'\n");
35922fe8fb19SBen Gras 			}
35932fe8fb19SBen Gras 			}
35942fe8fb19SBen Gras 		}
35952fe8fb19SBen Gras 	}
3596f14fb602SLionel Sambuc 	errno = serrno;
35972fe8fb19SBen Gras 
35982fe8fb19SBen Gras 	/* Take care to call atexit() only once. */
35992fe8fb19SBen Gras 	if (opt_print_stats) {
36002fe8fb19SBen Gras 		/* Print statistics at exit. */
36012fe8fb19SBen Gras 		atexit(malloc_print_stats);
36022fe8fb19SBen Gras 	}
36032fe8fb19SBen Gras 
36042fe8fb19SBen Gras 	/* Set variables according to the value of opt_small_max_2pow. */
36052fe8fb19SBen Gras 	if (opt_small_max_2pow < opt_quantum_2pow)
36062fe8fb19SBen Gras 		opt_small_max_2pow = opt_quantum_2pow;
36072fe8fb19SBen Gras 	small_max = (1 << opt_small_max_2pow);
36082fe8fb19SBen Gras 
36092fe8fb19SBen Gras 	/* Set bin-related variables. */
36102fe8fb19SBen Gras 	bin_maxclass = (pagesize >> 1);
36112fe8fb19SBen Gras 	assert(opt_quantum_2pow >= TINY_MIN_2POW);
36122fe8fb19SBen Gras 	ntbins = (unsigned)(opt_quantum_2pow - TINY_MIN_2POW);
36132fe8fb19SBen Gras 	assert(ntbins <= opt_quantum_2pow);
36142fe8fb19SBen Gras 	nqbins = (unsigned)(small_max >> opt_quantum_2pow);
36152fe8fb19SBen Gras 	nsbins = (unsigned)(pagesize_2pow - opt_small_max_2pow - 1);
36162fe8fb19SBen Gras 
36172fe8fb19SBen Gras 	/* Set variables according to the value of opt_quantum_2pow. */
36182fe8fb19SBen Gras 	quantum = (1 << opt_quantum_2pow);
36192fe8fb19SBen Gras 	quantum_mask = quantum - 1;
36202fe8fb19SBen Gras 	if (ntbins > 0)
36212fe8fb19SBen Gras 		small_min = (quantum >> 1) + 1;
36222fe8fb19SBen Gras 	else
36232fe8fb19SBen Gras 		small_min = 1;
36242fe8fb19SBen Gras 	assert(small_min <= quantum);
36252fe8fb19SBen Gras 
36262fe8fb19SBen Gras 	/* Set variables according to the value of opt_chunk_2pow. */
36272fe8fb19SBen Gras 	chunksize = (1LU << opt_chunk_2pow);
36282fe8fb19SBen Gras 	chunksize_mask = chunksize - 1;
36292fe8fb19SBen Gras 	chunksize_2pow = (unsigned)opt_chunk_2pow;
36302fe8fb19SBen Gras 	chunk_npages = (unsigned)(chunksize >> pagesize_2pow);
36312fe8fb19SBen Gras 	{
36322fe8fb19SBen Gras 		unsigned header_size;
36332fe8fb19SBen Gras 
36342fe8fb19SBen Gras 		header_size = (unsigned)(sizeof(arena_chunk_t) +
36352fe8fb19SBen Gras 		    (sizeof(arena_chunk_map_t) * (chunk_npages - 1)));
36362fe8fb19SBen Gras 		arena_chunk_header_npages = (header_size >> pagesize_2pow);
36372fe8fb19SBen Gras 		if ((header_size & pagesize_mask) != 0)
36382fe8fb19SBen Gras 			arena_chunk_header_npages++;
36392fe8fb19SBen Gras 	}
36402fe8fb19SBen Gras 	arena_maxclass = chunksize - (arena_chunk_header_npages <<
36412fe8fb19SBen Gras 	    pagesize_2pow);
36422fe8fb19SBen Gras 
36432fe8fb19SBen Gras 	UTRACE(0, 0, 0);
36442fe8fb19SBen Gras 
36452fe8fb19SBen Gras #ifdef MALLOC_STATS
36462fe8fb19SBen Gras 	memset(&stats_chunks, 0, sizeof(chunk_stats_t));
36472fe8fb19SBen Gras #endif
36482fe8fb19SBen Gras 
36492fe8fb19SBen Gras 	/* Various sanity checks that regard configuration. */
36502fe8fb19SBen Gras 	assert(quantum >= sizeof(void *));
36512fe8fb19SBen Gras 	assert(quantum <= pagesize);
36522fe8fb19SBen Gras 	assert(chunksize >= pagesize);
36532fe8fb19SBen Gras 	assert(quantum * 4 <= chunksize);
36542fe8fb19SBen Gras 
36552fe8fb19SBen Gras 	/* Initialize chunks data. */
36562fe8fb19SBen Gras 	malloc_mutex_init(&chunks_mtx);
36572fe8fb19SBen Gras 	RB_INIT(&huge);
36582fe8fb19SBen Gras #ifdef USE_BRK
36592fe8fb19SBen Gras 	malloc_mutex_init(&brk_mtx);
36602fe8fb19SBen Gras 	brk_base = sbrk(0);
36612fe8fb19SBen Gras 	brk_prev = brk_base;
36622fe8fb19SBen Gras 	brk_max = brk_base;
36632fe8fb19SBen Gras #endif
36642fe8fb19SBen Gras #ifdef MALLOC_STATS
36652fe8fb19SBen Gras 	huge_nmalloc = 0;
36662fe8fb19SBen Gras 	huge_ndalloc = 0;
36672fe8fb19SBen Gras 	huge_nralloc = 0;
36682fe8fb19SBen Gras 	huge_allocated = 0;
36692fe8fb19SBen Gras #endif
36702fe8fb19SBen Gras 	RB_INIT(&old_chunks);
36712fe8fb19SBen Gras 
36722fe8fb19SBen Gras 	/* Initialize base allocation data structures. */
36732fe8fb19SBen Gras #ifdef MALLOC_STATS
36742fe8fb19SBen Gras 	base_mapped = 0;
36752fe8fb19SBen Gras #endif
36762fe8fb19SBen Gras #ifdef USE_BRK
36772fe8fb19SBen Gras 	/*
36782fe8fb19SBen Gras 	 * Allocate a base chunk here, since it doesn't actually have to be
36792fe8fb19SBen Gras 	 * chunk-aligned.  Doing this before allocating any other chunks allows
36802fe8fb19SBen Gras 	 * the use of space that would otherwise be wasted.
36812fe8fb19SBen Gras 	 */
36822fe8fb19SBen Gras 	base_pages_alloc(0);
36832fe8fb19SBen Gras #endif
36842fe8fb19SBen Gras 	base_chunk_nodes = NULL;
36852fe8fb19SBen Gras 	malloc_mutex_init(&base_mtx);
36862fe8fb19SBen Gras 
36872fe8fb19SBen Gras 	if (ncpus > 1) {
36882fe8fb19SBen Gras 		/*
36892fe8fb19SBen Gras 		 * For SMP systems, create four times as many arenas as there
36902fe8fb19SBen Gras 		 * are CPUs by default.
36912fe8fb19SBen Gras 		 */
36922fe8fb19SBen Gras 		opt_narenas_lshift += 2;
36932fe8fb19SBen Gras 	}
36942fe8fb19SBen Gras 
36952fe8fb19SBen Gras 	/* Determine how many arenas to use. */
36962fe8fb19SBen Gras 	narenas = ncpus;
36972fe8fb19SBen Gras 	if (opt_narenas_lshift > 0) {
36982fe8fb19SBen Gras 		if ((narenas << opt_narenas_lshift) > narenas)
36992fe8fb19SBen Gras 			narenas <<= opt_narenas_lshift;
37002fe8fb19SBen Gras 		/*
37012fe8fb19SBen Gras 		 * Make sure not to exceed the limits of what base_malloc()
37022fe8fb19SBen Gras 		 * can handle.
37032fe8fb19SBen Gras 		 */
37042fe8fb19SBen Gras 		if (narenas * sizeof(arena_t *) > chunksize)
37052fe8fb19SBen Gras 			narenas = (unsigned)(chunksize / sizeof(arena_t *));
37062fe8fb19SBen Gras 	} else if (opt_narenas_lshift < 0) {
37072fe8fb19SBen Gras 		if ((narenas << opt_narenas_lshift) < narenas)
37082fe8fb19SBen Gras 			narenas <<= opt_narenas_lshift;
37092fe8fb19SBen Gras 		/* Make sure there is at least one arena. */
37102fe8fb19SBen Gras 		if (narenas == 0)
37112fe8fb19SBen Gras 			narenas = 1;
37122fe8fb19SBen Gras 	}
37132fe8fb19SBen Gras 
37142fe8fb19SBen Gras 	next_arena = 0;
37152fe8fb19SBen Gras 
37162fe8fb19SBen Gras 	/* Allocate and initialize arenas. */
37172fe8fb19SBen Gras 	arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas);
37182fe8fb19SBen Gras 	if (arenas == NULL) {
37192fe8fb19SBen Gras 		malloc_mutex_unlock(&init_lock);
37202fe8fb19SBen Gras 		return (true);
37212fe8fb19SBen Gras 	}
37222fe8fb19SBen Gras 	/*
37232fe8fb19SBen Gras 	 * Zero the array.  In practice, this should always be pre-zeroed,
37242fe8fb19SBen Gras 	 * since it was just mmap()ed, but let's be sure.
37252fe8fb19SBen Gras 	 */
37262fe8fb19SBen Gras 	memset(arenas, 0, sizeof(arena_t *) * narenas);
37272fe8fb19SBen Gras 
37282fe8fb19SBen Gras 	/*
37292fe8fb19SBen Gras 	 * Initialize one arena here.  The rest are lazily created in
37302fe8fb19SBen Gras 	 * arena_choose_hard().
37312fe8fb19SBen Gras 	 */
37322fe8fb19SBen Gras 	arenas_extend(0);
37332fe8fb19SBen Gras 	if (arenas[0] == NULL) {
37342fe8fb19SBen Gras 		malloc_mutex_unlock(&init_lock);
37352fe8fb19SBen Gras 		return (true);
37362fe8fb19SBen Gras 	}
37372fe8fb19SBen Gras 
37382fe8fb19SBen Gras 	malloc_mutex_init(&arenas_mtx);
37392fe8fb19SBen Gras 
37402fe8fb19SBen Gras 	malloc_initialized = true;
37412fe8fb19SBen Gras 	malloc_mutex_unlock(&init_lock);
37422fe8fb19SBen Gras 	return (false);
37432fe8fb19SBen Gras }
37442fe8fb19SBen Gras 
37452fe8fb19SBen Gras /*
37462fe8fb19SBen Gras  * End general internal functions.
37472fe8fb19SBen Gras  */
37482fe8fb19SBen Gras /******************************************************************************/
37492fe8fb19SBen Gras /*
37502fe8fb19SBen Gras  * Begin malloc(3)-compatible functions.
37512fe8fb19SBen Gras  */
37522fe8fb19SBen Gras 
37532fe8fb19SBen Gras void *
malloc(size_t size)37542fe8fb19SBen Gras malloc(size_t size)
37552fe8fb19SBen Gras {
37562fe8fb19SBen Gras 	void *ret;
37572fe8fb19SBen Gras 
37582fe8fb19SBen Gras 	if (malloc_init()) {
37592fe8fb19SBen Gras 		ret = NULL;
37602fe8fb19SBen Gras 		goto RETURN;
37612fe8fb19SBen Gras 	}
37622fe8fb19SBen Gras 
37632fe8fb19SBen Gras 	if (size == 0) {
37642fe8fb19SBen Gras 		if (opt_sysv == false)
37652fe8fb19SBen Gras 			size = 1;
37662fe8fb19SBen Gras 		else {
37672fe8fb19SBen Gras 			ret = NULL;
37682fe8fb19SBen Gras 			goto RETURN;
37692fe8fb19SBen Gras 		}
37702fe8fb19SBen Gras 	}
37712fe8fb19SBen Gras 
37722fe8fb19SBen Gras 	ret = imalloc(size);
37732fe8fb19SBen Gras 
37742fe8fb19SBen Gras RETURN:
37752fe8fb19SBen Gras 	if (ret == NULL) {
37762fe8fb19SBen Gras 		if (opt_xmalloc) {
37772fe8fb19SBen Gras 			_malloc_message(getprogname(),
37782fe8fb19SBen Gras 			    ": (malloc) Error in malloc(): out of memory\n", "",
37792fe8fb19SBen Gras 			    "");
37802fe8fb19SBen Gras 			abort();
37812fe8fb19SBen Gras 		}
37822fe8fb19SBen Gras 		errno = ENOMEM;
37832fe8fb19SBen Gras 	}
37842fe8fb19SBen Gras 
37852fe8fb19SBen Gras 	UTRACE(0, size, ret);
37862fe8fb19SBen Gras 	return (ret);
37872fe8fb19SBen Gras }
37882fe8fb19SBen Gras 
37892fe8fb19SBen Gras int
posix_memalign(void ** memptr,size_t alignment,size_t size)37902fe8fb19SBen Gras posix_memalign(void **memptr, size_t alignment, size_t size)
37912fe8fb19SBen Gras {
37922fe8fb19SBen Gras 	int ret;
37932fe8fb19SBen Gras 	void *result;
37942fe8fb19SBen Gras 
37952fe8fb19SBen Gras 	if (malloc_init())
37962fe8fb19SBen Gras 		result = NULL;
37972fe8fb19SBen Gras 	else {
37982fe8fb19SBen Gras 		/* Make sure that alignment is a large enough power of 2. */
37992fe8fb19SBen Gras 		if (((alignment - 1) & alignment) != 0
38002fe8fb19SBen Gras 		    || alignment < sizeof(void *)) {
38012fe8fb19SBen Gras 			if (opt_xmalloc) {
38022fe8fb19SBen Gras 				_malloc_message(getprogname(),
38032fe8fb19SBen Gras 				    ": (malloc) Error in posix_memalign(): "
38042fe8fb19SBen Gras 				    "invalid alignment\n", "", "");
38052fe8fb19SBen Gras 				abort();
38062fe8fb19SBen Gras 			}
38072fe8fb19SBen Gras 			result = NULL;
38082fe8fb19SBen Gras 			ret = EINVAL;
38092fe8fb19SBen Gras 			goto RETURN;
38102fe8fb19SBen Gras 		}
38112fe8fb19SBen Gras 
38122fe8fb19SBen Gras 		result = ipalloc(alignment, size);
38132fe8fb19SBen Gras 	}
38142fe8fb19SBen Gras 
38152fe8fb19SBen Gras 	if (result == NULL) {
38162fe8fb19SBen Gras 		if (opt_xmalloc) {
38172fe8fb19SBen Gras 			_malloc_message(getprogname(),
38182fe8fb19SBen Gras 			": (malloc) Error in posix_memalign(): out of memory\n",
38192fe8fb19SBen Gras 			"", "");
38202fe8fb19SBen Gras 			abort();
38212fe8fb19SBen Gras 		}
38222fe8fb19SBen Gras 		ret = ENOMEM;
38232fe8fb19SBen Gras 		goto RETURN;
38242fe8fb19SBen Gras 	}
38252fe8fb19SBen Gras 
38262fe8fb19SBen Gras 	*memptr = result;
38272fe8fb19SBen Gras 	ret = 0;
38282fe8fb19SBen Gras 
38292fe8fb19SBen Gras RETURN:
38302fe8fb19SBen Gras 	UTRACE(0, size, result);
38312fe8fb19SBen Gras 	return (ret);
38322fe8fb19SBen Gras }
38332fe8fb19SBen Gras 
38342fe8fb19SBen Gras void *
calloc(size_t num,size_t size)38352fe8fb19SBen Gras calloc(size_t num, size_t size)
38362fe8fb19SBen Gras {
38372fe8fb19SBen Gras 	void *ret;
38382fe8fb19SBen Gras 	size_t num_size;
38392fe8fb19SBen Gras 
38402fe8fb19SBen Gras 	if (malloc_init()) {
38412fe8fb19SBen Gras 		num_size = 0;
38422fe8fb19SBen Gras 		ret = NULL;
38432fe8fb19SBen Gras 		goto RETURN;
38442fe8fb19SBen Gras 	}
38452fe8fb19SBen Gras 
38462fe8fb19SBen Gras 	num_size = num * size;
38472fe8fb19SBen Gras 	if (num_size == 0) {
38482fe8fb19SBen Gras 		if ((opt_sysv == false) && ((num == 0) || (size == 0)))
38492fe8fb19SBen Gras 			num_size = 1;
38502fe8fb19SBen Gras 		else {
38512fe8fb19SBen Gras 			ret = NULL;
38522fe8fb19SBen Gras 			goto RETURN;
38532fe8fb19SBen Gras 		}
38542fe8fb19SBen Gras 	/*
38552fe8fb19SBen Gras 	 * Try to avoid division here.  We know that it isn't possible to
38562fe8fb19SBen Gras 	 * overflow during multiplication if neither operand uses any of the
38572fe8fb19SBen Gras 	 * most significant half of the bits in a size_t.
38582fe8fb19SBen Gras 	 */
38592fe8fb19SBen Gras 	} else if ((unsigned long long)((num | size) &
38602fe8fb19SBen Gras 	   ((unsigned long long)SIZE_T_MAX << (sizeof(size_t) << 2))) &&
38612fe8fb19SBen Gras 	   (num_size / size != num)) {
38622fe8fb19SBen Gras 		/* size_t overflow. */
38632fe8fb19SBen Gras 		ret = NULL;
38642fe8fb19SBen Gras 		goto RETURN;
38652fe8fb19SBen Gras 	}
38662fe8fb19SBen Gras 
38672fe8fb19SBen Gras 	ret = icalloc(num_size);
38682fe8fb19SBen Gras 
38692fe8fb19SBen Gras RETURN:
38702fe8fb19SBen Gras 	if (ret == NULL) {
38712fe8fb19SBen Gras 		if (opt_xmalloc) {
38722fe8fb19SBen Gras 			_malloc_message(getprogname(),
38732fe8fb19SBen Gras 			    ": (malloc) Error in calloc(): out of memory\n", "",
38742fe8fb19SBen Gras 			    "");
38752fe8fb19SBen Gras 			abort();
38762fe8fb19SBen Gras 		}
38772fe8fb19SBen Gras 		errno = ENOMEM;
38782fe8fb19SBen Gras 	}
38792fe8fb19SBen Gras 
38802fe8fb19SBen Gras 	UTRACE(0, num_size, ret);
38812fe8fb19SBen Gras 	return (ret);
38822fe8fb19SBen Gras }
38832fe8fb19SBen Gras 
38842fe8fb19SBen Gras void *
realloc(void * ptr,size_t size)38852fe8fb19SBen Gras realloc(void *ptr, size_t size)
38862fe8fb19SBen Gras {
38872fe8fb19SBen Gras 	void *ret;
38882fe8fb19SBen Gras 
38892fe8fb19SBen Gras 	if (size == 0) {
38902fe8fb19SBen Gras 		if (opt_sysv == false)
38912fe8fb19SBen Gras 			size = 1;
38922fe8fb19SBen Gras 		else {
38932fe8fb19SBen Gras 			if (ptr != NULL)
38942fe8fb19SBen Gras 				idalloc(ptr);
38952fe8fb19SBen Gras 			ret = NULL;
38962fe8fb19SBen Gras 			goto RETURN;
38972fe8fb19SBen Gras 		}
38982fe8fb19SBen Gras 	}
38992fe8fb19SBen Gras 
39002fe8fb19SBen Gras 	if (ptr != NULL) {
39012fe8fb19SBen Gras 		assert(malloc_initialized);
39022fe8fb19SBen Gras 
39032fe8fb19SBen Gras 		ret = iralloc(ptr, size);
39042fe8fb19SBen Gras 
39052fe8fb19SBen Gras 		if (ret == NULL) {
39062fe8fb19SBen Gras 			if (opt_xmalloc) {
39072fe8fb19SBen Gras 				_malloc_message(getprogname(),
39082fe8fb19SBen Gras 				    ": (malloc) Error in realloc(): out of "
39092fe8fb19SBen Gras 				    "memory\n", "", "");
39102fe8fb19SBen Gras 				abort();
39112fe8fb19SBen Gras 			}
39122fe8fb19SBen Gras 			errno = ENOMEM;
39132fe8fb19SBen Gras 		}
39142fe8fb19SBen Gras 	} else {
39152fe8fb19SBen Gras 		if (malloc_init())
39162fe8fb19SBen Gras 			ret = NULL;
39172fe8fb19SBen Gras 		else
39182fe8fb19SBen Gras 			ret = imalloc(size);
39192fe8fb19SBen Gras 
39202fe8fb19SBen Gras 		if (ret == NULL) {
39212fe8fb19SBen Gras 			if (opt_xmalloc) {
39222fe8fb19SBen Gras 				_malloc_message(getprogname(),
39232fe8fb19SBen Gras 				    ": (malloc) Error in realloc(): out of "
39242fe8fb19SBen Gras 				    "memory\n", "", "");
39252fe8fb19SBen Gras 				abort();
39262fe8fb19SBen Gras 			}
39272fe8fb19SBen Gras 			errno = ENOMEM;
39282fe8fb19SBen Gras 		}
39292fe8fb19SBen Gras 	}
39302fe8fb19SBen Gras 
39312fe8fb19SBen Gras RETURN:
39322fe8fb19SBen Gras 	UTRACE(ptr, size, ret);
39332fe8fb19SBen Gras 	return (ret);
39342fe8fb19SBen Gras }
39352fe8fb19SBen Gras 
39362fe8fb19SBen Gras void
free(void * ptr)39372fe8fb19SBen Gras free(void *ptr)
39382fe8fb19SBen Gras {
39392fe8fb19SBen Gras 
39402fe8fb19SBen Gras 	UTRACE(ptr, 0, 0);
39412fe8fb19SBen Gras 	if (ptr != NULL) {
39422fe8fb19SBen Gras 		assert(malloc_initialized);
39432fe8fb19SBen Gras 
39442fe8fb19SBen Gras 		idalloc(ptr);
39452fe8fb19SBen Gras 	}
39462fe8fb19SBen Gras }
39472fe8fb19SBen Gras 
39482fe8fb19SBen Gras /*
39492fe8fb19SBen Gras  * End malloc(3)-compatible functions.
39502fe8fb19SBen Gras  */
39512fe8fb19SBen Gras /******************************************************************************/
39522fe8fb19SBen Gras /*
39532fe8fb19SBen Gras  * Begin non-standard functions.
39542fe8fb19SBen Gras  */
39552fe8fb19SBen Gras #ifndef __NetBSD__
39562fe8fb19SBen Gras size_t
malloc_usable_size(const void * ptr)39572fe8fb19SBen Gras malloc_usable_size(const void *ptr)
39582fe8fb19SBen Gras {
39592fe8fb19SBen Gras 
39602fe8fb19SBen Gras 	assert(ptr != NULL);
39612fe8fb19SBen Gras 
39622fe8fb19SBen Gras 	return (isalloc(ptr));
39632fe8fb19SBen Gras }
39642fe8fb19SBen Gras #endif
39652fe8fb19SBen Gras 
39662fe8fb19SBen Gras /*
39672fe8fb19SBen Gras  * End non-standard functions.
39682fe8fb19SBen Gras  */
39692fe8fb19SBen Gras /******************************************************************************/
39702fe8fb19SBen Gras /*
39712fe8fb19SBen Gras  * Begin library-private functions, used by threading libraries for protection
39722fe8fb19SBen Gras  * of malloc during fork().  These functions are only called if the program is
39732fe8fb19SBen Gras  * running in threaded mode, so there is no need to check whether the program
39742fe8fb19SBen Gras  * is threaded here.
39752fe8fb19SBen Gras  */
39762fe8fb19SBen Gras 
39772fe8fb19SBen Gras void
_malloc_prefork(void)39782fe8fb19SBen Gras _malloc_prefork(void)
39792fe8fb19SBen Gras {
39802fe8fb19SBen Gras 	unsigned i;
39812fe8fb19SBen Gras 
39822fe8fb19SBen Gras 	/* Acquire all mutexes in a safe order. */
39832fe8fb19SBen Gras 
39842fe8fb19SBen Gras 	malloc_mutex_lock(&arenas_mtx);
39852fe8fb19SBen Gras 	for (i = 0; i < narenas; i++) {
39862fe8fb19SBen Gras 		if (arenas[i] != NULL)
39872fe8fb19SBen Gras 			malloc_mutex_lock(&arenas[i]->mtx);
39882fe8fb19SBen Gras 	}
39892fe8fb19SBen Gras 
39902fe8fb19SBen Gras 	malloc_mutex_lock(&base_mtx);
39912fe8fb19SBen Gras 
39922fe8fb19SBen Gras 	malloc_mutex_lock(&chunks_mtx);
39932fe8fb19SBen Gras }
39942fe8fb19SBen Gras 
39952fe8fb19SBen Gras void
_malloc_postfork(void)39962fe8fb19SBen Gras _malloc_postfork(void)
39972fe8fb19SBen Gras {
39982fe8fb19SBen Gras 	unsigned i;
39992fe8fb19SBen Gras 
40002fe8fb19SBen Gras 	/* Release all mutexes, now that fork() has completed. */
40012fe8fb19SBen Gras 
40022fe8fb19SBen Gras 	malloc_mutex_unlock(&chunks_mtx);
40032fe8fb19SBen Gras 
40042fe8fb19SBen Gras 	malloc_mutex_unlock(&base_mtx);
40052fe8fb19SBen Gras 
40062fe8fb19SBen Gras 	for (i = 0; i < narenas; i++) {
40072fe8fb19SBen Gras 		if (arenas[i] != NULL)
40082fe8fb19SBen Gras 			malloc_mutex_unlock(&arenas[i]->mtx);
40092fe8fb19SBen Gras 	}
40102fe8fb19SBen Gras 	malloc_mutex_unlock(&arenas_mtx);
40112fe8fb19SBen Gras }
40122fe8fb19SBen Gras 
40132fe8fb19SBen Gras /*
40142fe8fb19SBen Gras  * End library-private functions.
40152fe8fb19SBen Gras  */
40162fe8fb19SBen Gras /******************************************************************************/
4017