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