1*6b3ee451Smrg /* $NetBSD: jemalloc.c,v 1.64 2023/12/13 23:53:50 mrg Exp $ */
2e7a9e46bSad
38a475bcbSad /*-
48a475bcbSad * Copyright (C) 2006,2007 Jason Evans <jasone@FreeBSD.org>.
5562025fdSad * Copyright (C) 2023 Andrew Doran <ad@NetBSD.org>.
68a475bcbSad * All rights reserved.
78a475bcbSad *
88a475bcbSad * Redistribution and use in source and binary forms, with or without
98a475bcbSad * modification, are permitted provided that the following conditions
108a475bcbSad * are met:
118a475bcbSad * 1. Redistributions of source code must retain the above copyright
128a475bcbSad * notice(s), this list of conditions and the following disclaimer as
138a475bcbSad * the first lines of this file unmodified other than the possible
148a475bcbSad * addition of one or more copyright notices.
158a475bcbSad * 2. Redistributions in binary form must reproduce the above copyright
168a475bcbSad * notice(s), this list of conditions and the following disclaimer in
178a475bcbSad * the documentation and/or other materials provided with the
188a475bcbSad * distribution.
198a475bcbSad *
208a475bcbSad * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
218a475bcbSad * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
228a475bcbSad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
238a475bcbSad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
248a475bcbSad * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
258a475bcbSad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
268a475bcbSad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
278a475bcbSad * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
288a475bcbSad * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
298a475bcbSad * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
308a475bcbSad * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
318a475bcbSad *
328a475bcbSad *******************************************************************************
338a475bcbSad *
348a475bcbSad * This allocator implementation is designed to provide scalable performance
358a475bcbSad * for multi-threaded programs on multi-processor systems. The following
368a475bcbSad * features are included for this purpose:
378a475bcbSad *
388a475bcbSad * + Multiple arenas are used if there are multiple CPUs, which reduces lock
398a475bcbSad * contention and cache sloshing.
408a475bcbSad *
418a475bcbSad * + Cache line sharing between arenas is avoided for internal data
428a475bcbSad * structures.
438a475bcbSad *
448a475bcbSad * + Memory is managed in chunks and runs (chunks can be split into runs),
458a475bcbSad * rather than as individual pages. This provides a constant-time
468a475bcbSad * mechanism for associating allocations with particular arenas.
478a475bcbSad *
488a475bcbSad * Allocation requests are rounded up to the nearest size class, and no record
498a475bcbSad * of the original request size is maintained. Allocations are broken into
508a475bcbSad * categories according to size class. Assuming runtime defaults, 4 kB pages
518a475bcbSad * and a 16 byte quantum, the size classes in each category are as follows:
528a475bcbSad *
538a475bcbSad * |=====================================|
548a475bcbSad * | Category | Subcategory | Size |
558a475bcbSad * |=====================================|
568a475bcbSad * | Small | Tiny | 2 |
578a475bcbSad * | | | 4 |
588a475bcbSad * | | | 8 |
598a475bcbSad * | |----------------+---------|
608a475bcbSad * | | Quantum-spaced | 16 |
618a475bcbSad * | | | 32 |
628a475bcbSad * | | | 48 |
638a475bcbSad * | | | ... |
648a475bcbSad * | | | 480 |
658a475bcbSad * | | | 496 |
668a475bcbSad * | | | 512 |
678a475bcbSad * | |----------------+---------|
688a475bcbSad * | | Sub-page | 1 kB |
698a475bcbSad * | | | 2 kB |
708a475bcbSad * |=====================================|
718a475bcbSad * | Large | 4 kB |
728a475bcbSad * | | 8 kB |
738a475bcbSad * | | 12 kB |
748a475bcbSad * | | ... |
758a475bcbSad * | | 1012 kB |
768a475bcbSad * | | 1016 kB |
778a475bcbSad * | | 1020 kB |
788a475bcbSad * |=====================================|
798a475bcbSad * | Huge | 1 MB |
808a475bcbSad * | | 2 MB |
818a475bcbSad * | | 3 MB |
828a475bcbSad * | | ... |
838a475bcbSad * |=====================================|
848a475bcbSad *
858a475bcbSad * A different mechanism is used for each category:
868a475bcbSad *
878a475bcbSad * Small : Each size class is segregated into its own set of runs. Each run
888a475bcbSad * maintains a bitmap of which regions are free/allocated.
898a475bcbSad *
908a475bcbSad * Large : Each allocation is backed by a dedicated run. Metadata are stored
918a475bcbSad * in the associated arena chunk header maps.
928a475bcbSad *
938a475bcbSad * Huge : Each allocation is backed by a dedicated contiguous set of chunks.
948a475bcbSad * Metadata are stored in a separate red-black tree.
958a475bcbSad *
968a475bcbSad *******************************************************************************
978a475bcbSad */
988a475bcbSad
99e7a9e46bSad /* LINTLIBRARY */
100e7a9e46bSad
1018a475bcbSad /*
1028a475bcbSad * MALLOC_PRODUCTION disables assertions and statistics gathering. It also
1038a475bcbSad * defaults the A and J runtime options to off. These settings are appropriate
1048a475bcbSad * for production systems.
1058a475bcbSad */
106e7a9e46bSad #define MALLOC_PRODUCTION
1078a475bcbSad
1088a475bcbSad #ifndef MALLOC_PRODUCTION
1098a475bcbSad # define MALLOC_DEBUG
1108a475bcbSad #endif
1118a475bcbSad
1128a475bcbSad #include <sys/cdefs.h>
113e7a9e46bSad /* __FBSDID("$FreeBSD: src/lib/libc/stdlib/malloc.c,v 1.147 2007/06/15 22:00:16 jasone Exp $"); */
114*6b3ee451Smrg __RCSID("$NetBSD: jemalloc.c,v 1.64 2023/12/13 23:53:50 mrg Exp $");
1158a475bcbSad
1168a475bcbSad #include "namespace.h"
1178a475bcbSad #include <sys/mman.h>
1188a475bcbSad #include <sys/param.h>
1198a475bcbSad #include <sys/time.h>
1208a475bcbSad #include <sys/types.h>
1218a475bcbSad #include <sys/sysctl.h>
122562025fdSad #include <sys/rbtree.h>
1238a475bcbSad #include <sys/uio.h>
1248a475bcbSad #include <sys/ktrace.h> /* Must come after several other sys/ includes. */
1258a475bcbSad
1268a475bcbSad #include <errno.h>
1278a475bcbSad #include <limits.h>
1288a475bcbSad #include <pthread.h>
1298a475bcbSad #include <sched.h>
1308a475bcbSad #include <stdarg.h>
1318a475bcbSad #include <stdbool.h>
1328a475bcbSad #include <stdio.h>
1338a475bcbSad #include <stdint.h>
1348a475bcbSad #include <stdlib.h>
1358a475bcbSad #include <string.h>
1368a475bcbSad #include <strings.h>
1378a475bcbSad #include <unistd.h>
138*6b3ee451Smrg #include <malloc.h>
1398a475bcbSad
140e7a9e46bSad #include <reentrant.h>
14186ef91b8Schristos #include "extern.h"
14286ef91b8Schristos
1430a20cdb1Schristos #define STRERROR_R(a, b, c) strerror_r_ss(a, b, c);
1448a475bcbSad
1458a475bcbSad /* MALLOC_STATS enables statistics calculation. */
1468a475bcbSad #ifndef MALLOC_PRODUCTION
1478a475bcbSad # define MALLOC_STATS
1488a475bcbSad #endif
1498a475bcbSad
1508a475bcbSad #ifdef MALLOC_DEBUG
1518a475bcbSad # ifdef NDEBUG
1528a475bcbSad # undef NDEBUG
1538a475bcbSad # endif
1548a475bcbSad #else
1558a475bcbSad # ifndef NDEBUG
1568a475bcbSad # define NDEBUG
1578a475bcbSad # endif
1588a475bcbSad #endif
1598a475bcbSad #include <assert.h>
1608a475bcbSad
1618a475bcbSad #ifdef MALLOC_DEBUG
1628a475bcbSad /* Disable inlining to make debugging easier. */
1638a475bcbSad # define inline
1648a475bcbSad #endif
1658a475bcbSad
166562025fdSad /*
167562025fdSad * Compare two pointers of 64/32 bit width and produce a ternary 32-bit
168562025fdSad * indicator without using conditionals that maintains the expected
169562025fdSad * ordering: [negative, equal, positive].
170562025fdSad *
171562025fdSad * XXX it depends on twos complement arithemetic.
172562025fdSad * XXX maybe should be a built-in for rbtree?
173562025fdSad */
174562025fdSad static inline int
ptrcmp(const void * pa,const void * pb)175562025fdSad ptrcmp(const void *pa, const void *pb)
176562025fdSad {
177562025fdSad #ifdef _LP64
178a52713e5Sad const uintptr_t a = (uintptr_t)pa, b = (uintptr_t)pb;
179a52713e5Sad const uintptr_t diff = a - b;
180562025fdSad assert(((a | b) & 1) == 0);
181a52713e5Sad return (int)(diff >> 32) | ((unsigned)diff >> 1);
182562025fdSad #else
183744309c5Smrg return (intptr_t)pa - (intptr_t)pb;
184562025fdSad #endif
185562025fdSad }
186562025fdSad
1878a475bcbSad /* Size of stack-allocated buffer passed to strerror_r(). */
1888a475bcbSad #define STRERROR_BUF 64
1898a475bcbSad
1908a475bcbSad /* Minimum alignment of allocations is 2^QUANTUM_2POW_MIN bytes. */
191ce92aca1Smartin
192ce92aca1Smartin /*
193ce92aca1Smartin * If you touch the TINY_MIN_2POW definition for any architecture, please
194ce92aca1Smartin * make sure to adjust the corresponding definition for JEMALLOC_TINY_MIN_2POW
195ce92aca1Smartin * in the gcc 4.8 tree in dist/gcc/tree-ssa-ccp.c and verify that a native
196ce92aca1Smartin * gcc is still buildable!
197ce92aca1Smartin */
198ce92aca1Smartin
1998a475bcbSad #ifdef __i386__
2008a475bcbSad # define QUANTUM_2POW_MIN 4
2018a475bcbSad # define SIZEOF_PTR_2POW 2
2028a475bcbSad # define USE_BRK
2038a475bcbSad #endif
2048a475bcbSad #ifdef __ia64__
2058a475bcbSad # define QUANTUM_2POW_MIN 4
2068a475bcbSad # define SIZEOF_PTR_2POW 3
2078a475bcbSad #endif
208547b3a3bSmatt #ifdef __aarch64__
209547b3a3bSmatt # define QUANTUM_2POW_MIN 4
210547b3a3bSmatt # define SIZEOF_PTR_2POW 3
2112de10fd6Sjoerg # define TINY_MIN_2POW 3
212547b3a3bSmatt #endif
2138a475bcbSad #ifdef __alpha__
2148a475bcbSad # define QUANTUM_2POW_MIN 4
2158a475bcbSad # define SIZEOF_PTR_2POW 3
21603dbd387Sskrll # define TINY_MIN_2POW 3
2178a475bcbSad #endif
2188a475bcbSad #ifdef __sparc64__
2198a475bcbSad # define QUANTUM_2POW_MIN 4
2208a475bcbSad # define SIZEOF_PTR_2POW 3
221ce92aca1Smartin # define TINY_MIN_2POW 3
2228a475bcbSad #endif
2238a475bcbSad #ifdef __amd64__
2248a475bcbSad # define QUANTUM_2POW_MIN 4
2258a475bcbSad # define SIZEOF_PTR_2POW 3
226ce92aca1Smartin # define TINY_MIN_2POW 3
2278a475bcbSad #endif
2288a475bcbSad #ifdef __arm__
2298a475bcbSad # define QUANTUM_2POW_MIN 3
2308a475bcbSad # define SIZEOF_PTR_2POW 2
2318a475bcbSad # define USE_BRK
23203dbd387Sskrll # ifdef __ARM_EABI__
23303dbd387Sskrll # define TINY_MIN_2POW 3
23403dbd387Sskrll # endif
2358a475bcbSad #endif
2368a475bcbSad #ifdef __powerpc__
2378a475bcbSad # define QUANTUM_2POW_MIN 4
2388a475bcbSad # define SIZEOF_PTR_2POW 2
2398a475bcbSad # define USE_BRK
240b67fdf35Smartin # define TINY_MIN_2POW 3
2418a475bcbSad #endif
2427ed9cc8eShe #if defined(__sparc__) && !defined(__sparc64__)
243e7a9e46bSad # define QUANTUM_2POW_MIN 4
244e7a9e46bSad # define SIZEOF_PTR_2POW 2
245e7a9e46bSad # define USE_BRK
246e7a9e46bSad #endif
247623c8b30Smatt #ifdef __or1k__
248623c8b30Smatt # define QUANTUM_2POW_MIN 4
249623c8b30Smatt # define SIZEOF_PTR_2POW 2
250623c8b30Smatt # define USE_BRK
251623c8b30Smatt #endif
252e7a9e46bSad #ifdef __vax__
253e7a9e46bSad # define QUANTUM_2POW_MIN 4
254e7a9e46bSad # define SIZEOF_PTR_2POW 2
255e7a9e46bSad # define USE_BRK
256e7a9e46bSad #endif
257e7a9e46bSad #ifdef __sh__
258e7a9e46bSad # define QUANTUM_2POW_MIN 4
259e7a9e46bSad # define SIZEOF_PTR_2POW 2
260e7a9e46bSad # define USE_BRK
261e7a9e46bSad #endif
262e7a9e46bSad #ifdef __m68k__
263e7a9e46bSad # define QUANTUM_2POW_MIN 4
264e7a9e46bSad # define SIZEOF_PTR_2POW 2
265e7a9e46bSad # define USE_BRK
266e7a9e46bSad #endif
26775b842b8Sskrll #if defined(__mips__)
2680a0fe53eSmatt # ifdef _LP64
2690a0fe53eSmatt # define SIZEOF_PTR_2POW 3
2700a0fe53eSmatt # define TINY_MIN_2POW 3
2710a0fe53eSmatt # else
272e7a9e46bSad # define SIZEOF_PTR_2POW 2
2730a0fe53eSmatt # endif
2740a0fe53eSmatt # define QUANTUM_2POW_MIN 4
275e7a9e46bSad # define USE_BRK
276e7a9e46bSad #endif
27775b842b8Sskrll #if defined(__riscv__)
27875b842b8Sskrll # ifdef _LP64
27975b842b8Sskrll # define SIZEOF_PTR_2POW 3
28075b842b8Sskrll # define TINY_MIN_2POW 3
28175b842b8Sskrll # else
28275b842b8Sskrll # define SIZEOF_PTR_2POW 2
28375b842b8Sskrll # endif
28475b842b8Sskrll # define QUANTUM_2POW_MIN 4
28575b842b8Sskrll # define USE_BRK
28675b842b8Sskrll #endif
2873465d8dbSad #ifdef __hppa__
2883465d8dbSad # define QUANTUM_2POW_MIN 4
289f9ef5aa2Sskrll # define TINY_MIN_2POW 4
2903465d8dbSad # define SIZEOF_PTR_2POW 2
2913465d8dbSad # define USE_BRK
2923465d8dbSad #endif
2938a475bcbSad
2948a475bcbSad #define SIZEOF_PTR (1 << SIZEOF_PTR_2POW)
2958a475bcbSad
2968a475bcbSad /* sizeof(int) == (1 << SIZEOF_INT_2POW). */
2978a475bcbSad #ifndef SIZEOF_INT_2POW
2988a475bcbSad # define SIZEOF_INT_2POW 2
2998a475bcbSad #endif
3008a475bcbSad
3018a475bcbSad /*
3028a475bcbSad * Size and alignment of memory chunks that are allocated by the OS's virtual
3038a475bcbSad * memory system.
3048a475bcbSad */
3058a475bcbSad #define CHUNK_2POW_DEFAULT 20
3068a475bcbSad
3078a475bcbSad /*
3088a475bcbSad * Maximum size of L1 cache line. This is used to avoid cache line aliasing,
3098a475bcbSad * so over-estimates are okay (up to a point), but under-estimates will
3108a475bcbSad * negatively affect performance.
3118a475bcbSad */
3128a475bcbSad #define CACHELINE_2POW 6
3138a475bcbSad #define CACHELINE ((size_t)(1 << CACHELINE_2POW))
3148a475bcbSad
3158a475bcbSad /* Smallest size class to support. */
31603dbd387Sskrll #ifndef TINY_MIN_2POW
31703dbd387Sskrll #define TINY_MIN_2POW 2
31803dbd387Sskrll #endif
3198a475bcbSad
3208a475bcbSad /*
3218a475bcbSad * Maximum size class that is a multiple of the quantum, but not (necessarily)
3228a475bcbSad * a power of 2. Above this size, allocations are rounded up to the nearest
3238a475bcbSad * power of 2.
3248a475bcbSad */
3258a475bcbSad #define SMALL_MAX_2POW_DEFAULT 9
3268a475bcbSad #define SMALL_MAX_DEFAULT (1 << SMALL_MAX_2POW_DEFAULT)
3278a475bcbSad
3288a475bcbSad /*
32951cca89dSnjoly * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized
33051cca89dSnjoly * as small as possible such that this setting is still honored, without
33151cca89dSnjoly * violating other constraints. The goal is to make runs as small as possible
33251cca89dSnjoly * without exceeding a per run external fragmentation threshold.
3338a475bcbSad *
33451cca89dSnjoly * We use binary fixed point math for overhead computations, where the binary
33551cca89dSnjoly * point is implicitly RUN_BFP bits to the left.
3368a475bcbSad *
33751cca89dSnjoly * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be
33851cca89dSnjoly * honored for some/all object sizes, since there is one bit of header overhead
33951cca89dSnjoly * per object (plus a constant). This constraint is relaxed (ignored) for runs
34051cca89dSnjoly * that are so small that the per-region overhead is greater than:
34151cca89dSnjoly *
34251cca89dSnjoly * (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP))
3438a475bcbSad */
34451cca89dSnjoly #define RUN_BFP 12
34551cca89dSnjoly /* \/ Implicit binary fixed point. */
34651cca89dSnjoly #define RUN_MAX_OVRHD 0x0000003dU
34751cca89dSnjoly #define RUN_MAX_OVRHD_RELAX 0x00001800U
3488a475bcbSad
3498a475bcbSad /* Put a cap on small object run size. This overrides RUN_MAX_OVRHD. */
3508a475bcbSad #define RUN_MAX_SMALL_2POW 15
3518a475bcbSad #define RUN_MAX_SMALL (1 << RUN_MAX_SMALL_2POW)
3528a475bcbSad
3538a475bcbSad /******************************************************************************/
3548a475bcbSad
355e7a9e46bSad #define malloc_mutex_t mutex_t
356e7a9e46bSad
357e7a9e46bSad /* Set to true once the allocator has been initialized. */
358e7a9e46bSad static bool malloc_initialized = false;
359e7a9e46bSad
36026ba8048Schristos #ifdef _REENTRANT
361e7a9e46bSad /* Used to avoid initialization races. */
362e7a9e46bSad static mutex_t init_lock = MUTEX_INITIALIZER;
363e7a9e46bSad #endif
3648a475bcbSad
3658a475bcbSad /******************************************************************************/
3668a475bcbSad /*
3678a475bcbSad * Statistics data structures.
3688a475bcbSad */
3698a475bcbSad
3708a475bcbSad #ifdef MALLOC_STATS
3718a475bcbSad
3728a475bcbSad typedef struct malloc_bin_stats_s malloc_bin_stats_t;
3738a475bcbSad struct malloc_bin_stats_s {
3748a475bcbSad /*
3758a475bcbSad * Number of allocation requests that corresponded to the size of this
3768a475bcbSad * bin.
3778a475bcbSad */
3788a475bcbSad uint64_t nrequests;
3798a475bcbSad
3808a475bcbSad /* Total number of runs created for this bin's size class. */
3818a475bcbSad uint64_t nruns;
3828a475bcbSad
3838a475bcbSad /*
3848a475bcbSad * Total number of runs reused by extracting them from the runs tree for
3858a475bcbSad * this bin's size class.
3868a475bcbSad */
3878a475bcbSad uint64_t reruns;
3888a475bcbSad
3898a475bcbSad /* High-water mark for this bin. */
3908a475bcbSad unsigned long highruns;
3918a475bcbSad
3928a475bcbSad /* Current number of runs in this bin. */
3938a475bcbSad unsigned long curruns;
3948a475bcbSad };
3958a475bcbSad
3968a475bcbSad typedef struct arena_stats_s arena_stats_t;
3978a475bcbSad struct arena_stats_s {
3988a475bcbSad /* Number of bytes currently mapped. */
3998a475bcbSad size_t mapped;
4008a475bcbSad
4018a475bcbSad /* Per-size-category statistics. */
4028a475bcbSad size_t allocated_small;
4038a475bcbSad uint64_t nmalloc_small;
4048a475bcbSad uint64_t ndalloc_small;
4058a475bcbSad
4068a475bcbSad size_t allocated_large;
4078a475bcbSad uint64_t nmalloc_large;
4088a475bcbSad uint64_t ndalloc_large;
4098a475bcbSad };
4108a475bcbSad
4118a475bcbSad typedef struct chunk_stats_s chunk_stats_t;
4128a475bcbSad struct chunk_stats_s {
4138a475bcbSad /* Number of chunks that were allocated. */
4148a475bcbSad uint64_t nchunks;
4158a475bcbSad
4168a475bcbSad /* High-water mark for number of chunks allocated. */
4178a475bcbSad unsigned long highchunks;
4188a475bcbSad
4198a475bcbSad /*
4208a475bcbSad * Current number of chunks allocated. This value isn't maintained for
4218a475bcbSad * any other purpose, so keep track of it in order to be able to set
4228a475bcbSad * highchunks.
4238a475bcbSad */
4248a475bcbSad unsigned long curchunks;
4258a475bcbSad };
4268a475bcbSad
4278a475bcbSad #endif /* #ifdef MALLOC_STATS */
4288a475bcbSad
4298a475bcbSad /******************************************************************************/
4308a475bcbSad /*
4318a475bcbSad * Chunk data structures.
4328a475bcbSad */
4338a475bcbSad
4348a475bcbSad /* Tree of chunks. */
4358a475bcbSad typedef struct chunk_node_s chunk_node_t;
4368a475bcbSad struct chunk_node_s {
4378a475bcbSad /* Linkage for the chunk tree. */
438562025fdSad rb_node_t link;
4398a475bcbSad
4408a475bcbSad /*
4418a475bcbSad * Pointer to the chunk that this tree node is responsible for. In some
4428a475bcbSad * (but certainly not all) cases, this data structure is placed at the
4438a475bcbSad * beginning of the corresponding chunk, so this field may point to this
4448a475bcbSad * node.
4458a475bcbSad */
4468a475bcbSad void *chunk;
4478a475bcbSad
4488a475bcbSad /* Total chunk size. */
4498a475bcbSad size_t size;
4508a475bcbSad };
4518a475bcbSad typedef struct chunk_tree_s chunk_tree_t;
452562025fdSad
453562025fdSad static int chunk_comp(void *, const void *, const void *);
454562025fdSad
455562025fdSad static const rb_tree_ops_t chunk_tree_ops = {
456562025fdSad .rbto_compare_nodes = chunk_comp,
457562025fdSad .rbto_compare_key = chunk_comp,
458562025fdSad .rbto_node_offset = offsetof(struct chunk_node_s, link),
459562025fdSad };
4608a475bcbSad
4618a475bcbSad /******************************************************************************/
4628a475bcbSad /*
4638a475bcbSad * Arena data structures.
4648a475bcbSad */
4658a475bcbSad
4668a475bcbSad typedef struct arena_s arena_t;
4678a475bcbSad typedef struct arena_bin_s arena_bin_t;
4688a475bcbSad
4698a475bcbSad typedef struct arena_chunk_map_s arena_chunk_map_t;
4708a475bcbSad struct arena_chunk_map_s {
4718a475bcbSad /* Number of pages in run. */
4728a475bcbSad uint32_t npages;
4738a475bcbSad /*
4748a475bcbSad * Position within run. For a free run, this is POS_FREE for the first
4758a475bcbSad * and last pages. The POS_FREE special value makes it possible to
4768a475bcbSad * quickly coalesce free runs.
4778a475bcbSad *
4788a475bcbSad * This is the limiting factor for chunksize; there can be at most 2^31
4798a475bcbSad * pages in a run.
4808a475bcbSad */
4818a475bcbSad #define POS_FREE ((uint32_t)0xffffffffU)
4828a475bcbSad uint32_t pos;
4838a475bcbSad };
4848a475bcbSad
4858a475bcbSad /* Arena chunk header. */
4868a475bcbSad typedef struct arena_chunk_s arena_chunk_t;
4878a475bcbSad struct arena_chunk_s {
488562025fdSad /* Linkage for the arena's chunk tree. */
489562025fdSad rb_node_t link;
490562025fdSad
4918a475bcbSad /* Arena that owns the chunk. */
4928a475bcbSad arena_t *arena;
4938a475bcbSad
4948a475bcbSad /*
4958a475bcbSad * Number of pages in use. This is maintained in order to make
4968a475bcbSad * detection of empty chunks fast.
4978a475bcbSad */
4988a475bcbSad uint32_t pages_used;
4998a475bcbSad
5008a475bcbSad /*
5018a475bcbSad * Every time a free run larger than this value is created/coalesced,
5028a475bcbSad * this value is increased. The only way that the value decreases is if
5038a475bcbSad * arena_run_alloc() fails to find a free run as large as advertised by
5048a475bcbSad * this value.
5058a475bcbSad */
5068a475bcbSad uint32_t max_frun_npages;
5078a475bcbSad
5088a475bcbSad /*
5098a475bcbSad * Every time a free run that starts at an earlier page than this value
5108a475bcbSad * is created/coalesced, this value is decreased. It is reset in a
5118a475bcbSad * similar fashion to max_frun_npages.
5128a475bcbSad */
5138a475bcbSad uint32_t min_frun_ind;
5148a475bcbSad
5158a475bcbSad /*
5168a475bcbSad * Map of pages within chunk that keeps track of free/large/small. For
5178a475bcbSad * free runs, only the map entries for the first and last pages are
5188a475bcbSad * kept up to date, so that free runs can be quickly coalesced.
5198a475bcbSad */
5208a475bcbSad arena_chunk_map_t map[1]; /* Dynamically sized. */
5218a475bcbSad };
5228a475bcbSad typedef struct arena_chunk_tree_s arena_chunk_tree_t;
523562025fdSad
524562025fdSad static int arena_chunk_comp(void *, const void *, const void *);
525562025fdSad
526562025fdSad static const rb_tree_ops_t arena_chunk_tree_ops = {
527562025fdSad .rbto_compare_nodes = arena_chunk_comp,
528562025fdSad .rbto_compare_key = arena_chunk_comp,
529562025fdSad .rbto_node_offset = offsetof(struct arena_chunk_s, link),
530562025fdSad };
5318a475bcbSad
5328a475bcbSad typedef struct arena_run_s arena_run_t;
5338a475bcbSad struct arena_run_s {
5348a475bcbSad /* Linkage for run trees. */
535562025fdSad rb_node_t link;
5368a475bcbSad
5378a475bcbSad #ifdef MALLOC_DEBUG
5388a475bcbSad uint32_t magic;
5398a475bcbSad # define ARENA_RUN_MAGIC 0x384adf93
5408a475bcbSad #endif
5418a475bcbSad
5428a475bcbSad /* Bin this run is associated with. */
5438a475bcbSad arena_bin_t *bin;
5448a475bcbSad
5458a475bcbSad /* Index of first element that might have a free region. */
5468a475bcbSad unsigned regs_minelm;
5478a475bcbSad
5488a475bcbSad /* Number of free regions in run. */
5498a475bcbSad unsigned nfree;
5508a475bcbSad
5518a475bcbSad /* Bitmask of in-use regions (0: in use, 1: free). */
5528a475bcbSad unsigned regs_mask[1]; /* Dynamically sized. */
5538a475bcbSad };
5548a475bcbSad typedef struct arena_run_tree_s arena_run_tree_t;
555562025fdSad
556562025fdSad static int arena_run_comp(void *, const void *, const void *);
557562025fdSad
558562025fdSad static const rb_tree_ops_t arena_run_tree_ops = {
559562025fdSad .rbto_compare_nodes = arena_run_comp,
560562025fdSad .rbto_compare_key = arena_run_comp,
561562025fdSad .rbto_node_offset = offsetof(struct arena_run_s, link),
562562025fdSad };
5638a475bcbSad
5648a475bcbSad struct arena_bin_s {
5658a475bcbSad /*
5668a475bcbSad * Current run being used to service allocations of this bin's size
5678a475bcbSad * class.
5688a475bcbSad */
5698a475bcbSad arena_run_t *runcur;
5708a475bcbSad
5718a475bcbSad /*
5728a475bcbSad * Tree of non-full runs. This tree is used when looking for an
5738a475bcbSad * existing run when runcur is no longer usable. We choose the
5748a475bcbSad * non-full run that is lowest in memory; this policy tends to keep
5758a475bcbSad * objects packed well, and it can also help reduce the number of
5768a475bcbSad * almost-empty chunks.
5778a475bcbSad */
578562025fdSad rb_tree_t runs;
5798a475bcbSad
5808a475bcbSad /* Size of regions in a run for this bin's size class. */
5818a475bcbSad size_t reg_size;
5828a475bcbSad
5838a475bcbSad /* Total size of a run for this bin's size class. */
5848a475bcbSad size_t run_size;
5858a475bcbSad
5868a475bcbSad /* Total number of regions in a run for this bin's size class. */
5878a475bcbSad uint32_t nregs;
5888a475bcbSad
5898a475bcbSad /* Number of elements in a run's regs_mask for this bin's size class. */
5908a475bcbSad uint32_t regs_mask_nelms;
5918a475bcbSad
5928a475bcbSad /* Offset of first region in a run for this bin's size class. */
5938a475bcbSad uint32_t reg0_offset;
5948a475bcbSad
5958a475bcbSad #ifdef MALLOC_STATS
5968a475bcbSad /* Bin statistics. */
5978a475bcbSad malloc_bin_stats_t stats;
5988a475bcbSad #endif
5998a475bcbSad };
6008a475bcbSad
6018a475bcbSad struct arena_s {
6028a475bcbSad #ifdef MALLOC_DEBUG
6038a475bcbSad uint32_t magic;
6048a475bcbSad # define ARENA_MAGIC 0x947d3d24
6058a475bcbSad #endif
6068a475bcbSad
6078a475bcbSad /* All operations on this arena require that mtx be locked. */
6088a475bcbSad malloc_mutex_t mtx;
6098a475bcbSad
6108a475bcbSad #ifdef MALLOC_STATS
6118a475bcbSad arena_stats_t stats;
6128a475bcbSad #endif
6138a475bcbSad
6148a475bcbSad /*
6158a475bcbSad * Tree of chunks this arena manages.
6168a475bcbSad */
617562025fdSad rb_tree_t chunks;
6188a475bcbSad
6198a475bcbSad /*
6208a475bcbSad * In order to avoid rapid chunk allocation/deallocation when an arena
6218a475bcbSad * oscillates right on the cusp of needing a new chunk, cache the most
6228a475bcbSad * recently freed chunk. This caching is disabled by opt_hint.
6238a475bcbSad *
6248a475bcbSad * There is one spare chunk per arena, rather than one spare total, in
6258a475bcbSad * order to avoid interactions between multiple threads that could make
6268a475bcbSad * a single spare inadequate.
6278a475bcbSad */
6288a475bcbSad arena_chunk_t *spare;
6298a475bcbSad
6308a475bcbSad /*
6318a475bcbSad * bins is used to store rings of free regions of the following sizes,
6328a475bcbSad * assuming a 16-byte quantum, 4kB pagesize, and default MALLOC_OPTIONS.
6338a475bcbSad *
6348a475bcbSad * bins[i] | size |
6358a475bcbSad * --------+------+
6368a475bcbSad * 0 | 2 |
6378a475bcbSad * 1 | 4 |
6388a475bcbSad * 2 | 8 |
6398a475bcbSad * --------+------+
6408a475bcbSad * 3 | 16 |
6418a475bcbSad * 4 | 32 |
6428a475bcbSad * 5 | 48 |
6438a475bcbSad * 6 | 64 |
6448a475bcbSad * : :
6458a475bcbSad * : :
6468a475bcbSad * 33 | 496 |
6478a475bcbSad * 34 | 512 |
6488a475bcbSad * --------+------+
6498a475bcbSad * 35 | 1024 |
6508a475bcbSad * 36 | 2048 |
6518a475bcbSad * --------+------+
6528a475bcbSad */
6538a475bcbSad arena_bin_t bins[1]; /* Dynamically sized. */
6548a475bcbSad };
6558a475bcbSad
6568a475bcbSad /******************************************************************************/
6578a475bcbSad /*
6588a475bcbSad * Data.
6598a475bcbSad */
6608a475bcbSad
6618a475bcbSad /* Number of CPUs. */
6628a475bcbSad static unsigned ncpus;
6638a475bcbSad
6648a475bcbSad /* VM page size. */
6658a475bcbSad static size_t pagesize;
6668a475bcbSad static size_t pagesize_mask;
6678de47c09Schristos static int pagesize_2pow;
6688a475bcbSad
6698a475bcbSad /* Various bin-related settings. */
6708a475bcbSad static size_t bin_maxclass; /* Max size class for bins. */
6718a475bcbSad static unsigned ntbins; /* Number of (2^n)-spaced tiny bins. */
6728a475bcbSad static unsigned nqbins; /* Number of quantum-spaced bins. */
6738a475bcbSad static unsigned nsbins; /* Number of (2^n)-spaced sub-page bins. */
6748a475bcbSad static size_t small_min;
6758a475bcbSad static size_t small_max;
6768a475bcbSad
6778a475bcbSad /* Various quantum-related settings. */
6788a475bcbSad static size_t quantum;
6798a475bcbSad static size_t quantum_mask; /* (quantum - 1). */
6808a475bcbSad
6818a475bcbSad /* Various chunk-related settings. */
6828a475bcbSad static size_t chunksize;
6838a475bcbSad static size_t chunksize_mask; /* (chunksize - 1). */
68414bfffc9Syamt static int chunksize_2pow;
6858a475bcbSad static unsigned chunk_npages;
6868a475bcbSad static unsigned arena_chunk_header_npages;
6878a475bcbSad static size_t arena_maxclass; /* Max size class for arenas. */
6888a475bcbSad
6898a475bcbSad /********/
6908a475bcbSad /*
6918a475bcbSad * Chunks.
6928a475bcbSad */
6938a475bcbSad
69426ba8048Schristos #ifdef _REENTRANT
6958a475bcbSad /* Protects chunk-related data structures. */
6968a475bcbSad static malloc_mutex_t chunks_mtx;
69726ba8048Schristos #endif
6988a475bcbSad
6998a475bcbSad /* Tree of chunks that are stand-alone huge allocations. */
700562025fdSad static rb_tree_t huge;
7018a475bcbSad
7028a475bcbSad #ifdef USE_BRK
7038a475bcbSad /*
7048a475bcbSad * Try to use brk for chunk-size allocations, due to address space constraints.
7058a475bcbSad */
7068a475bcbSad /*
7078a475bcbSad * Protects sbrk() calls. This must be separate from chunks_mtx, since
7088a475bcbSad * base_pages_alloc() also uses sbrk(), but cannot lock chunks_mtx (doing so
7098a475bcbSad * could cause recursive lock acquisition).
7108a475bcbSad */
71115c525fbSchristos #ifdef _REENTRANT
7128a475bcbSad static malloc_mutex_t brk_mtx;
71315c525fbSchristos #endif
7148a475bcbSad /* Result of first sbrk(0) call. */
7158a475bcbSad static void *brk_base;
7168a475bcbSad /* Current end of brk, or ((void *)-1) if brk is exhausted. */
7178a475bcbSad static void *brk_prev;
7188a475bcbSad /* Current upper limit on brk addresses. */
7198a475bcbSad static void *brk_max;
7208a475bcbSad #endif
7218a475bcbSad
7228a475bcbSad #ifdef MALLOC_STATS
7238a475bcbSad /* Huge allocation statistics. */
7248a475bcbSad static uint64_t huge_nmalloc;
7258a475bcbSad static uint64_t huge_ndalloc;
726b79fded2Syamt static uint64_t huge_nralloc;
7278a475bcbSad static size_t huge_allocated;
7288a475bcbSad #endif
7298a475bcbSad
7308a475bcbSad /*
7318a475bcbSad * Tree of chunks that were previously allocated. This is used when allocating
7328a475bcbSad * chunks, in an attempt to re-use address space.
7338a475bcbSad */
734562025fdSad static rb_tree_t old_chunks;
7358a475bcbSad
7368a475bcbSad /****************************/
7378a475bcbSad /*
7388a475bcbSad * base (internal allocation).
7398a475bcbSad */
7408a475bcbSad
7418a475bcbSad /*
7428a475bcbSad * Current pages that are being used for internal memory allocations. These
7438a475bcbSad * pages are carved up in cacheline-size quanta, so that there is no chance of
7448a475bcbSad * false cache line sharing.
7458a475bcbSad */
7468a475bcbSad static void *base_pages;
7478a475bcbSad static void *base_next_addr;
7488a475bcbSad static void *base_past_addr; /* Addr immediately past base_pages. */
7498a475bcbSad static chunk_node_t *base_chunk_nodes; /* LIFO cache of chunk nodes. */
75026ba8048Schristos #ifdef _REENTRANT
7518a475bcbSad static malloc_mutex_t base_mtx;
75226ba8048Schristos #endif
7538a475bcbSad #ifdef MALLOC_STATS
7548a475bcbSad static size_t base_mapped;
7558a475bcbSad #endif
7568a475bcbSad
7578a475bcbSad /********/
7588a475bcbSad /*
7598a475bcbSad * Arenas.
7608a475bcbSad */
7618a475bcbSad
7628a475bcbSad /*
7638a475bcbSad * Arenas that are used to service external requests. Not all elements of the
7648a475bcbSad * arenas array are necessarily used; arenas are created lazily as needed.
7658a475bcbSad */
7668a475bcbSad static arena_t **arenas;
76726ba8048Schristos #ifdef _REENTRANT
7688a475bcbSad static malloc_mutex_t arenas_mtx; /* Protects arenas initialization. */
76926ba8048Schristos #endif
7708a475bcbSad
7718a475bcbSad #ifdef MALLOC_STATS
7728a475bcbSad /* Chunk statistics. */
7738a475bcbSad static chunk_stats_t stats_chunks;
7748a475bcbSad #endif
7758a475bcbSad
7768a475bcbSad /*******************************/
7778a475bcbSad /*
7788a475bcbSad * Runtime configuration options.
7798a475bcbSad */
7808a475bcbSad const char *_malloc_options;
7818a475bcbSad
7828a475bcbSad #ifndef MALLOC_PRODUCTION
7838a475bcbSad static bool opt_abort = true;
7848a475bcbSad static bool opt_junk = true;
7858a475bcbSad #else
7868a475bcbSad static bool opt_abort = false;
7878a475bcbSad static bool opt_junk = false;
7888a475bcbSad #endif
7898a475bcbSad static bool opt_hint = false;
7908a475bcbSad static bool opt_print_stats = false;
7918de47c09Schristos static int opt_quantum_2pow = QUANTUM_2POW_MIN;
7928de47c09Schristos static int opt_small_max_2pow = SMALL_MAX_2POW_DEFAULT;
7938de47c09Schristos static int opt_chunk_2pow = CHUNK_2POW_DEFAULT;
7948a475bcbSad static bool opt_utrace = false;
7958a475bcbSad static bool opt_sysv = false;
7968a475bcbSad static bool opt_xmalloc = false;
7978a475bcbSad static bool opt_zero = false;
7988a475bcbSad
7998a475bcbSad typedef struct {
8008a475bcbSad void *p;
8018a475bcbSad size_t s;
8028a475bcbSad void *r;
8038a475bcbSad } malloc_utrace_t;
8048a475bcbSad
805bf5f78d5Sad /* Sprinkle branch hints for the compiler and CPU. */
806bf5f78d5Sad #define OPT(a) __predict_false(opt_##a)
807bf5f78d5Sad #define NOT_OPT(a) __predict_true(!opt_##a)
808bf5f78d5Sad
809bf5f78d5Sad /* Trace malloc/free for ktrace/kdump. */
8108a475bcbSad #define UTRACE(a, b, c) \
811bf5f78d5Sad if (OPT(utrace)) { \
812e7a9e46bSad malloc_utrace_t ut; \
813e7a9e46bSad ut.p = a; \
814e7a9e46bSad ut.s = b; \
815e7a9e46bSad ut.r = c; \
816bf5f78d5Sad utrace("malloc", &ut, sizeof(ut)); \
8178a475bcbSad }
8188a475bcbSad
8198a475bcbSad /******************************************************************************/
8208a475bcbSad /*
8218a475bcbSad * Begin function prototypes for non-inline static functions.
8228a475bcbSad */
8238a475bcbSad
8248a475bcbSad static void wrtmessage(const char *p1, const char *p2, const char *p3,
8258a475bcbSad const char *p4);
8268a475bcbSad #ifdef MALLOC_STATS
8278a475bcbSad static void malloc_printf(const char *format, ...);
8288a475bcbSad #endif
829d703a148Schristos static char *size_t2s(size_t x, char *s);
8308a475bcbSad static bool base_pages_alloc(size_t minsize);
8318a475bcbSad static void *base_alloc(size_t size);
8328a475bcbSad static chunk_node_t *base_chunk_node_alloc(void);
8338a475bcbSad static void base_chunk_node_dealloc(chunk_node_t *node);
8348a475bcbSad #ifdef MALLOC_STATS
8358a475bcbSad static void stats_print(arena_t *arena);
8368a475bcbSad #endif
8378a475bcbSad static void *pages_map(void *addr, size_t size);
83814bfffc9Syamt static void *pages_map_align(void *addr, size_t size, int align);
8398a475bcbSad static void pages_unmap(void *addr, size_t size);
8408a475bcbSad static void *chunk_alloc(size_t size);
8418a475bcbSad static void chunk_dealloc(void *chunk, size_t size);
8428a475bcbSad static void arena_run_split(arena_t *arena, arena_run_t *run, size_t size);
8438a475bcbSad static arena_chunk_t *arena_chunk_alloc(arena_t *arena);
8448a475bcbSad static void arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk);
8458a475bcbSad static arena_run_t *arena_run_alloc(arena_t *arena, size_t size);
8468a475bcbSad static void arena_run_dalloc(arena_t *arena, arena_run_t *run, size_t size);
8478a475bcbSad static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin);
8488a475bcbSad static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin);
8498a475bcbSad static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size);
8508a475bcbSad static void *arena_malloc(arena_t *arena, size_t size);
8518a475bcbSad static void *arena_palloc(arena_t *arena, size_t alignment, size_t size,
8528a475bcbSad size_t alloc_size);
8538a475bcbSad static size_t arena_salloc(const void *ptr);
8548a475bcbSad static void *arena_ralloc(void *ptr, size_t size, size_t oldsize);
8558a475bcbSad static void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr);
856bf5f78d5Sad static void arena_new(arena_t *arena);
857bf5f78d5Sad static arena_t *arenas_extend(void);
8588a475bcbSad static void *huge_malloc(size_t size);
8598a475bcbSad static void *huge_palloc(size_t alignment, size_t size);
8608a475bcbSad static void *huge_ralloc(void *ptr, size_t size, size_t oldsize);
8618a475bcbSad static void huge_dalloc(void *ptr);
8628a475bcbSad static void *imalloc(size_t size);
8638a475bcbSad static void *ipalloc(size_t alignment, size_t size);
8648a475bcbSad static void *icalloc(size_t size);
8658a475bcbSad static size_t isalloc(const void *ptr);
8668a475bcbSad static void *iralloc(void *ptr, size_t size);
8678a475bcbSad static void idalloc(void *ptr);
8688a475bcbSad static void malloc_print_stats(void);
8698a475bcbSad static bool malloc_init_hard(void);
8708a475bcbSad
8718a475bcbSad /*
8728a475bcbSad * End function prototypes.
8738a475bcbSad */
8748a475bcbSad /******************************************************************************/
8758a475bcbSad /*
8768a475bcbSad * Begin mutex.
8778a475bcbSad */
8788a475bcbSad
879e7a9e46bSad #define malloc_mutex_init(m) mutex_init(m, NULL)
880e7a9e46bSad #define malloc_mutex_lock(m) mutex_lock(m)
881e7a9e46bSad #define malloc_mutex_unlock(m) mutex_unlock(m)
8828a475bcbSad
8838a475bcbSad /*
8848a475bcbSad * End mutex.
8858a475bcbSad */
8868a475bcbSad /******************************************************************************/
8878a475bcbSad /*
8888a475bcbSad * Begin Utility functions/macros.
8898a475bcbSad */
8908a475bcbSad
8918a475bcbSad /* Return the chunk address for allocation address a. */
8928a475bcbSad #define CHUNK_ADDR2BASE(a) \
8938a475bcbSad ((void *)((uintptr_t)(a) & ~chunksize_mask))
8948a475bcbSad
8958a475bcbSad /* Return the chunk offset of address a. */
8968a475bcbSad #define CHUNK_ADDR2OFFSET(a) \
8978a475bcbSad ((size_t)((uintptr_t)(a) & chunksize_mask))
8988a475bcbSad
8998a475bcbSad /* Return the smallest chunk multiple that is >= s. */
9008a475bcbSad #define CHUNK_CEILING(s) \
9018a475bcbSad (((s) + chunksize_mask) & ~chunksize_mask)
9028a475bcbSad
9038a475bcbSad /* Return the smallest cacheline multiple that is >= s. */
9048a475bcbSad #define CACHELINE_CEILING(s) \
9058a475bcbSad (((s) + (CACHELINE - 1)) & ~(CACHELINE - 1))
9068a475bcbSad
9078a475bcbSad /* Return the smallest quantum multiple that is >= a. */
9088a475bcbSad #define QUANTUM_CEILING(a) \
9098a475bcbSad (((a) + quantum_mask) & ~quantum_mask)
9108a475bcbSad
9118a475bcbSad /* Return the smallest pagesize multiple that is >= s. */
9128a475bcbSad #define PAGE_CEILING(s) \
9138a475bcbSad (((s) + pagesize_mask) & ~pagesize_mask)
9148a475bcbSad
9158a475bcbSad /* Compute the smallest power of 2 that is >= x. */
9168a475bcbSad static inline size_t
pow2_ceil(size_t x)9178a475bcbSad pow2_ceil(size_t x)
9188a475bcbSad {
9198a475bcbSad
9208a475bcbSad x--;
9218a475bcbSad x |= x >> 1;
9228a475bcbSad x |= x >> 2;
9238a475bcbSad x |= x >> 4;
9248a475bcbSad x |= x >> 8;
9258a475bcbSad x |= x >> 16;
9268a475bcbSad #if (SIZEOF_PTR == 8)
9278a475bcbSad x |= x >> 32;
9288a475bcbSad #endif
9298a475bcbSad x++;
9308a475bcbSad return (x);
9318a475bcbSad }
9328a475bcbSad
9338a475bcbSad static void
wrtmessage(const char * p1,const char * p2,const char * p3,const char * p4)9348a475bcbSad wrtmessage(const char *p1, const char *p2, const char *p3, const char *p4)
9358a475bcbSad {
9368a475bcbSad
93786ef91b8Schristos write(STDERR_FILENO, p1, strlen(p1));
93886ef91b8Schristos write(STDERR_FILENO, p2, strlen(p2));
93986ef91b8Schristos write(STDERR_FILENO, p3, strlen(p3));
94086ef91b8Schristos write(STDERR_FILENO, p4, strlen(p4));
9418a475bcbSad }
9428a475bcbSad
9438a475bcbSad void (*_malloc_message)(const char *p1, const char *p2, const char *p3,
9448a475bcbSad const char *p4) = wrtmessage;
9458a475bcbSad
9468a475bcbSad #ifdef MALLOC_STATS
9478a475bcbSad /*
9488a475bcbSad * Print to stderr in such a way as to (hopefully) avoid memory allocation.
9498a475bcbSad */
9508a475bcbSad static void
malloc_printf(const char * format,...)9518a475bcbSad malloc_printf(const char *format, ...)
9528a475bcbSad {
9538a475bcbSad char buf[4096];
9548a475bcbSad va_list ap;
9558a475bcbSad
9568a475bcbSad va_start(ap, format);
9578a475bcbSad vsnprintf(buf, sizeof(buf), format, ap);
9588a475bcbSad va_end(ap);
9598a475bcbSad _malloc_message(buf, "", "", "");
9608a475bcbSad }
9618a475bcbSad #endif
9628a475bcbSad
9638a475bcbSad /*
9648a475bcbSad * We don't want to depend on vsnprintf() for production builds, since that can
965d703a148Schristos * cause unnecessary bloat for static binaries. size_t2s() provides minimal
9668a475bcbSad * integer printing functionality, so that malloc_printf() use can be limited to
9678a475bcbSad * MALLOC_STATS code.
9688a475bcbSad */
9698a475bcbSad #define UMAX2S_BUFSIZE 21
9708a475bcbSad static char *
size_t2s(size_t x,char * s)971d703a148Schristos size_t2s(size_t x, char *s)
9728a475bcbSad {
9738a475bcbSad unsigned i;
9748a475bcbSad
9758a475bcbSad /* Make sure UMAX2S_BUFSIZE is large enough. */
976687cd24eSyamt /* LINTED */
977c59812a8Schristos assert(sizeof(size_t) <= 8);
9788a475bcbSad
9798a475bcbSad i = UMAX2S_BUFSIZE - 1;
9808a475bcbSad s[i] = '\0';
9818a475bcbSad do {
9828a475bcbSad i--;
983e7a9e46bSad s[i] = "0123456789"[(int)x % 10];
984e7a9e46bSad x /= (uintmax_t)10LL;
9858a475bcbSad } while (x > 0);
9868a475bcbSad
9878a475bcbSad return (&s[i]);
9888a475bcbSad }
9898a475bcbSad
9908a475bcbSad /******************************************************************************/
9918a475bcbSad
9928a475bcbSad static bool
base_pages_alloc(size_t minsize)9938a475bcbSad base_pages_alloc(size_t minsize)
9948a475bcbSad {
995e7a9e46bSad size_t csize = 0;
9968a475bcbSad
9978a475bcbSad #ifdef USE_BRK
9988a475bcbSad /*
9998a475bcbSad * Do special brk allocation here, since base allocations don't need to
10008a475bcbSad * be chunk-aligned.
10018a475bcbSad */
10028a475bcbSad if (brk_prev != (void *)-1) {
10038a475bcbSad void *brk_cur;
10048a475bcbSad intptr_t incr;
10058a475bcbSad
10068a475bcbSad if (minsize != 0)
10078a475bcbSad csize = CHUNK_CEILING(minsize);
10088a475bcbSad
10098a475bcbSad malloc_mutex_lock(&brk_mtx);
10108a475bcbSad do {
10118a475bcbSad /* Get the current end of brk. */
10128a475bcbSad brk_cur = sbrk(0);
10138a475bcbSad
10148a475bcbSad /*
10158a475bcbSad * Calculate how much padding is necessary to
10168a475bcbSad * chunk-align the end of brk. Don't worry about
10178a475bcbSad * brk_cur not being chunk-aligned though.
10188a475bcbSad */
10198a475bcbSad incr = (intptr_t)chunksize
10208a475bcbSad - (intptr_t)CHUNK_ADDR2OFFSET(brk_cur);
10212360d084Slukem assert(incr >= 0);
10222360d084Slukem if ((size_t)incr < minsize)
10238a475bcbSad incr += csize;
10248a475bcbSad
10258a475bcbSad brk_prev = sbrk(incr);
10268a475bcbSad if (brk_prev == brk_cur) {
10278a475bcbSad /* Success. */
10288a475bcbSad malloc_mutex_unlock(&brk_mtx);
10298a475bcbSad base_pages = brk_cur;
10308a475bcbSad base_next_addr = base_pages;
10318a475bcbSad base_past_addr = (void *)((uintptr_t)base_pages
10328a475bcbSad + incr);
10338a475bcbSad #ifdef MALLOC_STATS
10348a475bcbSad base_mapped += incr;
10358a475bcbSad #endif
10368a475bcbSad return (false);
10378a475bcbSad }
10388a475bcbSad } while (brk_prev != (void *)-1);
10398a475bcbSad malloc_mutex_unlock(&brk_mtx);
10408a475bcbSad }
10418a475bcbSad if (minsize == 0) {
10428a475bcbSad /*
10438a475bcbSad * Failure during initialization doesn't matter, so avoid
10448a475bcbSad * falling through to the mmap-based page mapping code.
10458a475bcbSad */
10468a475bcbSad return (true);
10478a475bcbSad }
10488a475bcbSad #endif
10498a475bcbSad assert(minsize != 0);
10508a475bcbSad csize = PAGE_CEILING(minsize);
10518a475bcbSad base_pages = pages_map(NULL, csize);
10528a475bcbSad if (base_pages == NULL)
10538a475bcbSad return (true);
10548a475bcbSad base_next_addr = base_pages;
10558a475bcbSad base_past_addr = (void *)((uintptr_t)base_pages + csize);
10568a475bcbSad #ifdef MALLOC_STATS
10578a475bcbSad base_mapped += csize;
10588a475bcbSad #endif
10598a475bcbSad return (false);
10608a475bcbSad }
10618a475bcbSad
10628a475bcbSad static void *
base_alloc(size_t size)10638a475bcbSad base_alloc(size_t size)
10648a475bcbSad {
10658a475bcbSad void *ret;
10668a475bcbSad size_t csize;
10678a475bcbSad
10688a475bcbSad /* Round size up to nearest multiple of the cacheline size. */
10698a475bcbSad csize = CACHELINE_CEILING(size);
10708a475bcbSad
10718a475bcbSad malloc_mutex_lock(&base_mtx);
10728a475bcbSad
10738a475bcbSad /* Make sure there's enough space for the allocation. */
10748a475bcbSad if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) {
10758a475bcbSad if (base_pages_alloc(csize)) {
10768a475bcbSad ret = NULL;
10778a475bcbSad goto RETURN;
10788a475bcbSad }
10798a475bcbSad }
10808a475bcbSad
10818a475bcbSad /* Allocate. */
10828a475bcbSad ret = base_next_addr;
10838a475bcbSad base_next_addr = (void *)((uintptr_t)base_next_addr + csize);
10848a475bcbSad
10858a475bcbSad RETURN:
10868a475bcbSad malloc_mutex_unlock(&base_mtx);
10878a475bcbSad return (ret);
10888a475bcbSad }
10898a475bcbSad
10908a475bcbSad static chunk_node_t *
base_chunk_node_alloc(void)10918a475bcbSad base_chunk_node_alloc(void)
10928a475bcbSad {
10938a475bcbSad chunk_node_t *ret;
10948a475bcbSad
10958a475bcbSad malloc_mutex_lock(&base_mtx);
10968a475bcbSad if (base_chunk_nodes != NULL) {
10978a475bcbSad ret = base_chunk_nodes;
1098e7a9e46bSad /* LINTED */
10998a475bcbSad base_chunk_nodes = *(chunk_node_t **)ret;
11008a475bcbSad malloc_mutex_unlock(&base_mtx);
11018a475bcbSad } else {
11028a475bcbSad malloc_mutex_unlock(&base_mtx);
11038a475bcbSad ret = (chunk_node_t *)base_alloc(sizeof(chunk_node_t));
11048a475bcbSad }
11058a475bcbSad
11068a475bcbSad return (ret);
11078a475bcbSad }
11088a475bcbSad
11098a475bcbSad static void
base_chunk_node_dealloc(chunk_node_t * node)11108a475bcbSad base_chunk_node_dealloc(chunk_node_t *node)
11118a475bcbSad {
11128a475bcbSad
11138a475bcbSad malloc_mutex_lock(&base_mtx);
1114e7a9e46bSad /* LINTED */
11158a475bcbSad *(chunk_node_t **)node = base_chunk_nodes;
11168a475bcbSad base_chunk_nodes = node;
11178a475bcbSad malloc_mutex_unlock(&base_mtx);
11188a475bcbSad }
11198a475bcbSad
11208a475bcbSad /******************************************************************************/
11218a475bcbSad
11228a475bcbSad #ifdef MALLOC_STATS
11238a475bcbSad static void
stats_print(arena_t * arena)11248a475bcbSad stats_print(arena_t *arena)
11258a475bcbSad {
1126bf5f78d5Sad const unsigned minusone = (unsigned)-1;
1127bf5f78d5Sad unsigned i, gap_start;
11288a475bcbSad
11298a475bcbSad malloc_printf(
11308a475bcbSad " allocated/mapped nmalloc ndalloc\n");
1131e7a9e46bSad
1132e7a9e46bSad malloc_printf("small: %12zu %-12s %12llu %12llu\n",
11338a475bcbSad arena->stats.allocated_small, "", arena->stats.nmalloc_small,
11348a475bcbSad arena->stats.ndalloc_small);
1135e7a9e46bSad malloc_printf("large: %12zu %-12s %12llu %12llu\n",
11368a475bcbSad arena->stats.allocated_large, "", arena->stats.nmalloc_large,
11378a475bcbSad arena->stats.ndalloc_large);
1138e7a9e46bSad malloc_printf("total: %12zu/%-12zu %12llu %12llu\n",
11398a475bcbSad arena->stats.allocated_small + arena->stats.allocated_large,
11408a475bcbSad arena->stats.mapped,
11418a475bcbSad arena->stats.nmalloc_small + arena->stats.nmalloc_large,
11428a475bcbSad arena->stats.ndalloc_small + arena->stats.ndalloc_large);
11438a475bcbSad
11448a475bcbSad malloc_printf("bins: bin size regs pgs requests newruns"
11458a475bcbSad " reruns maxruns curruns\n");
1146bf5f78d5Sad for (i = 0, gap_start = minusone; i < ntbins + nqbins + nsbins; i++) {
11478a475bcbSad if (arena->bins[i].stats.nrequests == 0) {
1148bf5f78d5Sad if (gap_start == minusone)
11498a475bcbSad gap_start = i;
11508a475bcbSad } else {
1151bf5f78d5Sad if (gap_start != minusone) {
11528a475bcbSad if (i > gap_start + 1) {
11538a475bcbSad /* Gap of more than one size class. */
11548a475bcbSad malloc_printf("[%u..%u]\n",
11558a475bcbSad gap_start, i - 1);
11568a475bcbSad } else {
11578a475bcbSad /* Gap of one size class. */
11588a475bcbSad malloc_printf("[%u]\n", gap_start);
11598a475bcbSad }
1160bf5f78d5Sad gap_start = minusone;
11618a475bcbSad }
11628a475bcbSad malloc_printf(
11638a475bcbSad "%13u %1s %4u %4u %3u %9llu %9llu"
11648a475bcbSad " %9llu %7lu %7lu\n",
11658a475bcbSad i,
11668a475bcbSad i < ntbins ? "T" : i < ntbins + nqbins ? "Q" : "S",
11678a475bcbSad arena->bins[i].reg_size,
11688a475bcbSad arena->bins[i].nregs,
11698a475bcbSad arena->bins[i].run_size >> pagesize_2pow,
11708a475bcbSad arena->bins[i].stats.nrequests,
11718a475bcbSad arena->bins[i].stats.nruns,
11728a475bcbSad arena->bins[i].stats.reruns,
11738a475bcbSad arena->bins[i].stats.highruns,
11748a475bcbSad arena->bins[i].stats.curruns);
11758a475bcbSad }
11768a475bcbSad }
1177bf5f78d5Sad if (gap_start != minusone) {
11788a475bcbSad if (i > gap_start + 1) {
11798a475bcbSad /* Gap of more than one size class. */
11808a475bcbSad malloc_printf("[%u..%u]\n", gap_start, i - 1);
11818a475bcbSad } else {
11828a475bcbSad /* Gap of one size class. */
11838a475bcbSad malloc_printf("[%u]\n", gap_start);
11848a475bcbSad }
11858a475bcbSad }
11868a475bcbSad }
11878a475bcbSad #endif
11888a475bcbSad
11898a475bcbSad /*
11908a475bcbSad * End Utility functions/macros.
11918a475bcbSad */
11928a475bcbSad /******************************************************************************/
11938a475bcbSad /*
11948a475bcbSad * Begin chunk management functions.
11958a475bcbSad */
11968a475bcbSad
1197562025fdSad static int
chunk_comp(void * context,const void * va,const void * vb)1198562025fdSad chunk_comp(void *context, const void *va, const void *vb)
11998a475bcbSad {
1200562025fdSad const chunk_node_t *a = va, *b = vb;
12018a475bcbSad
12028a475bcbSad assert(a != NULL);
12038a475bcbSad assert(b != NULL);
12048a475bcbSad
1205562025fdSad return ptrcmp(a->chunk, b->chunk);
12068a475bcbSad }
12078a475bcbSad
12088a475bcbSad static void *
pages_map_align(void * addr,size_t size,int align)120914bfffc9Syamt pages_map_align(void *addr, size_t size, int align)
12108a475bcbSad {
12118a475bcbSad void *ret;
12128a475bcbSad
12138a475bcbSad /*
12148a475bcbSad * We don't use MAP_FIXED here, because it can cause the *replacement*
12158a475bcbSad * of existing mappings, and we only want to create new mappings.
12168a475bcbSad */
121714bfffc9Syamt ret = mmap(addr, size, PROT_READ | PROT_WRITE,
121814bfffc9Syamt MAP_PRIVATE | MAP_ANON | MAP_ALIGNED(align), -1, 0);
12198a475bcbSad assert(ret != NULL);
12208a475bcbSad
12218a475bcbSad if (ret == MAP_FAILED)
12228a475bcbSad ret = NULL;
12238a475bcbSad else if (addr != NULL && ret != addr) {
12248a475bcbSad /*
12258a475bcbSad * We succeeded in mapping memory, but not in the right place.
12268a475bcbSad */
12278a475bcbSad if (munmap(ret, size) == -1) {
12288a475bcbSad char buf[STRERROR_BUF];
12298a475bcbSad
123086ef91b8Schristos STRERROR_R(errno, buf, sizeof(buf));
123186ef91b8Schristos _malloc_message(getprogname(),
12328a475bcbSad ": (malloc) Error in munmap(): ", buf, "\n");
1233bf5f78d5Sad if (OPT(abort))
12348a475bcbSad abort();
12358a475bcbSad }
12368a475bcbSad ret = NULL;
12378a475bcbSad }
12388a475bcbSad
12398a475bcbSad assert(ret == NULL || (addr == NULL && ret != addr)
12408a475bcbSad || (addr != NULL && ret == addr));
12418a475bcbSad return (ret);
12428a475bcbSad }
12438a475bcbSad
124414bfffc9Syamt static void *
pages_map(void * addr,size_t size)124514bfffc9Syamt pages_map(void *addr, size_t size)
124614bfffc9Syamt {
124714bfffc9Syamt
124814bfffc9Syamt return pages_map_align(addr, size, 0);
124914bfffc9Syamt }
125014bfffc9Syamt
12518a475bcbSad static void
pages_unmap(void * addr,size_t size)12528a475bcbSad pages_unmap(void *addr, size_t size)
12538a475bcbSad {
12548a475bcbSad
12558a475bcbSad if (munmap(addr, size) == -1) {
12568a475bcbSad char buf[STRERROR_BUF];
12578a475bcbSad
125886ef91b8Schristos STRERROR_R(errno, buf, sizeof(buf));
125986ef91b8Schristos _malloc_message(getprogname(),
12608a475bcbSad ": (malloc) Error in munmap(): ", buf, "\n");
1261bf5f78d5Sad if (OPT(abort))
12628a475bcbSad abort();
12638a475bcbSad }
12648a475bcbSad }
12658a475bcbSad
12668a475bcbSad static void *
chunk_alloc(size_t size)12678a475bcbSad chunk_alloc(size_t size)
12688a475bcbSad {
12698a475bcbSad void *ret, *chunk;
12708a475bcbSad chunk_node_t *tchunk, *delchunk;
12718a475bcbSad
12728a475bcbSad assert(size != 0);
12738a475bcbSad assert((size & chunksize_mask) == 0);
12748a475bcbSad
12758a475bcbSad malloc_mutex_lock(&chunks_mtx);
12768a475bcbSad
12778a475bcbSad if (size == chunksize) {
12788a475bcbSad /*
12798a475bcbSad * Check for address ranges that were previously chunks and try
12808a475bcbSad * to use them.
12818a475bcbSad */
12828a475bcbSad
1283562025fdSad tchunk = RB_TREE_MIN(&old_chunks);
12848a475bcbSad while (tchunk != NULL) {
12858a475bcbSad /* Found an address range. Try to recycle it. */
12868a475bcbSad
12878a475bcbSad chunk = tchunk->chunk;
12888a475bcbSad delchunk = tchunk;
1289562025fdSad tchunk = RB_TREE_NEXT(&old_chunks, delchunk);
12908a475bcbSad
12918a475bcbSad /* Remove delchunk from the tree. */
1292562025fdSad rb_tree_remove_node(&old_chunks, delchunk);
12938a475bcbSad base_chunk_node_dealloc(delchunk);
12948a475bcbSad
12958a475bcbSad #ifdef USE_BRK
12968a475bcbSad if ((uintptr_t)chunk >= (uintptr_t)brk_base
12978a475bcbSad && (uintptr_t)chunk < (uintptr_t)brk_max) {
12988a475bcbSad /* Re-use a previously freed brk chunk. */
12998a475bcbSad ret = chunk;
13008a475bcbSad goto RETURN;
13018a475bcbSad }
13028a475bcbSad #endif
13038a475bcbSad if ((ret = pages_map(chunk, size)) != NULL) {
13048a475bcbSad /* Success. */
13058a475bcbSad goto RETURN;
13068a475bcbSad }
13078a475bcbSad }
13088a475bcbSad }
13098a475bcbSad
13108a475bcbSad /*
13118a475bcbSad * Try to over-allocate, but allow the OS to place the allocation
13128a475bcbSad * anywhere. Beware of size_t wrap-around.
13138a475bcbSad */
13148a475bcbSad if (size + chunksize > size) {
131514bfffc9Syamt if ((ret = pages_map_align(NULL, size, chunksize_2pow))
131614bfffc9Syamt != NULL) {
13178a475bcbSad goto RETURN;
13188a475bcbSad }
13198a475bcbSad }
13208a475bcbSad
13218a475bcbSad #ifdef USE_BRK
13228a475bcbSad /*
13238a475bcbSad * Try to create allocations in brk, in order to make full use of
13248a475bcbSad * limited address space.
13258a475bcbSad */
13268a475bcbSad if (brk_prev != (void *)-1) {
13278a475bcbSad void *brk_cur;
13288a475bcbSad intptr_t incr;
13298a475bcbSad
13308a475bcbSad /*
13318a475bcbSad * The loop is necessary to recover from races with other
13328a475bcbSad * threads that are using brk for something other than malloc.
13338a475bcbSad */
13348a475bcbSad malloc_mutex_lock(&brk_mtx);
13358a475bcbSad do {
13368a475bcbSad /* Get the current end of brk. */
13378a475bcbSad brk_cur = sbrk(0);
13388a475bcbSad
13398a475bcbSad /*
13408a475bcbSad * Calculate how much padding is necessary to
13418a475bcbSad * chunk-align the end of brk.
13428a475bcbSad */
13438a475bcbSad incr = (intptr_t)size
13448a475bcbSad - (intptr_t)CHUNK_ADDR2OFFSET(brk_cur);
13452360d084Slukem if (incr == (intptr_t)size) {
13468a475bcbSad ret = brk_cur;
13478a475bcbSad } else {
13488a475bcbSad ret = (void *)((intptr_t)brk_cur + incr);
13498a475bcbSad incr += size;
13508a475bcbSad }
13518a475bcbSad
13528a475bcbSad brk_prev = sbrk(incr);
13538a475bcbSad if (brk_prev == brk_cur) {
13548a475bcbSad /* Success. */
13558a475bcbSad malloc_mutex_unlock(&brk_mtx);
13568a475bcbSad brk_max = (void *)((intptr_t)ret + size);
13578a475bcbSad goto RETURN;
13588a475bcbSad }
13598a475bcbSad } while (brk_prev != (void *)-1);
13608a475bcbSad malloc_mutex_unlock(&brk_mtx);
13618a475bcbSad }
13628a475bcbSad #endif
13638a475bcbSad
13648a475bcbSad /* All strategies for allocation failed. */
13658a475bcbSad ret = NULL;
13668a475bcbSad RETURN:
13678a475bcbSad if (ret != NULL) {
13688a475bcbSad chunk_node_t key;
13698a475bcbSad /*
13708a475bcbSad * Clean out any entries in old_chunks that overlap with the
13718a475bcbSad * memory we just allocated.
13728a475bcbSad */
13738a475bcbSad key.chunk = ret;
1374562025fdSad tchunk = rb_tree_find_node_geq(&old_chunks, &key);
13758a475bcbSad while (tchunk != NULL
13768a475bcbSad && (uintptr_t)tchunk->chunk >= (uintptr_t)ret
13778a475bcbSad && (uintptr_t)tchunk->chunk < (uintptr_t)ret + size) {
13788a475bcbSad delchunk = tchunk;
1379562025fdSad tchunk = RB_TREE_NEXT(&old_chunks, delchunk);
1380562025fdSad rb_tree_remove_node(&old_chunks, delchunk);
13818a475bcbSad base_chunk_node_dealloc(delchunk);
13828a475bcbSad }
13838a475bcbSad
13848a475bcbSad }
13858a475bcbSad #ifdef MALLOC_STATS
13868a475bcbSad if (ret != NULL) {
13878a475bcbSad stats_chunks.nchunks += (size / chunksize);
13888a475bcbSad stats_chunks.curchunks += (size / chunksize);
13898a475bcbSad }
13908a475bcbSad if (stats_chunks.curchunks > stats_chunks.highchunks)
13918a475bcbSad stats_chunks.highchunks = stats_chunks.curchunks;
13928a475bcbSad #endif
13938a475bcbSad malloc_mutex_unlock(&chunks_mtx);
13948a475bcbSad
13958a475bcbSad assert(CHUNK_ADDR2BASE(ret) == ret);
13968a475bcbSad return (ret);
13978a475bcbSad }
13988a475bcbSad
13998a475bcbSad static void
chunk_dealloc(void * chunk,size_t size)14008a475bcbSad chunk_dealloc(void *chunk, size_t size)
14018a475bcbSad {
14028a475bcbSad chunk_node_t *node;
14038a475bcbSad
14048a475bcbSad assert(chunk != NULL);
14058a475bcbSad assert(CHUNK_ADDR2BASE(chunk) == chunk);
14068a475bcbSad assert(size != 0);
14078a475bcbSad assert((size & chunksize_mask) == 0);
14088a475bcbSad
14098a475bcbSad malloc_mutex_lock(&chunks_mtx);
14108a475bcbSad
14118a475bcbSad #ifdef USE_BRK
14128a475bcbSad if ((uintptr_t)chunk >= (uintptr_t)brk_base
14138a475bcbSad && (uintptr_t)chunk < (uintptr_t)brk_max) {
14148a475bcbSad void *brk_cur;
14158a475bcbSad
14168a475bcbSad malloc_mutex_lock(&brk_mtx);
14178a475bcbSad /* Get the current end of brk. */
14188a475bcbSad brk_cur = sbrk(0);
14198a475bcbSad
14208a475bcbSad /*
14218a475bcbSad * Try to shrink the data segment if this chunk is at the end
14228a475bcbSad * of the data segment. The sbrk() call here is subject to a
14238a475bcbSad * race condition with threads that use brk(2) or sbrk(2)
14248a475bcbSad * directly, but the alternative would be to leak memory for
14258a475bcbSad * the sake of poorly designed multi-threaded programs.
14268a475bcbSad */
14278a475bcbSad if (brk_cur == brk_max
14288a475bcbSad && (void *)((uintptr_t)chunk + size) == brk_max
14298a475bcbSad && sbrk(-(intptr_t)size) == brk_max) {
14308a475bcbSad malloc_mutex_unlock(&brk_mtx);
14318a475bcbSad if (brk_prev == brk_max) {
14328a475bcbSad /* Success. */
14338a475bcbSad brk_prev = (void *)((intptr_t)brk_max
14348a475bcbSad - (intptr_t)size);
14358a475bcbSad brk_max = brk_prev;
14368a475bcbSad }
14378a475bcbSad } else {
14388a475bcbSad size_t offset;
14398a475bcbSad
14408a475bcbSad malloc_mutex_unlock(&brk_mtx);
14418a475bcbSad madvise(chunk, size, MADV_FREE);
14428a475bcbSad
14438a475bcbSad /*
14448a475bcbSad * Iteratively create records of each chunk-sized
14458a475bcbSad * memory region that 'chunk' is comprised of, so that
14468a475bcbSad * the address range can be recycled if memory usage
14478a475bcbSad * increases later on.
14488a475bcbSad */
14498a475bcbSad for (offset = 0; offset < size; offset += chunksize) {
14508a475bcbSad node = base_chunk_node_alloc();
14518a475bcbSad if (node == NULL)
14528a475bcbSad break;
14538a475bcbSad
14548a475bcbSad node->chunk = (void *)((uintptr_t)chunk
14558a475bcbSad + (uintptr_t)offset);
14568a475bcbSad node->size = chunksize;
1457562025fdSad rb_tree_insert_node(&old_chunks, node);
14588a475bcbSad }
14598a475bcbSad }
14608a475bcbSad } else {
14618a475bcbSad #endif
14628a475bcbSad pages_unmap(chunk, size);
14638a475bcbSad
14648a475bcbSad /*
14658a475bcbSad * Make a record of the chunk's address, so that the address
14668a475bcbSad * range can be recycled if memory usage increases later on.
14678a475bcbSad * Don't bother to create entries if (size > chunksize), since
14688a475bcbSad * doing so could cause scalability issues for truly gargantuan
14698a475bcbSad * objects (many gigabytes or larger).
14708a475bcbSad */
14718a475bcbSad if (size == chunksize) {
14728a475bcbSad node = base_chunk_node_alloc();
14738a475bcbSad if (node != NULL) {
14748a475bcbSad node->chunk = (void *)(uintptr_t)chunk;
14758a475bcbSad node->size = chunksize;
1476562025fdSad rb_tree_insert_node(&old_chunks, node);
14778a475bcbSad }
14788a475bcbSad }
14798a475bcbSad #ifdef USE_BRK
14808a475bcbSad }
14818a475bcbSad #endif
14828a475bcbSad
14838a475bcbSad #ifdef MALLOC_STATS
14848a475bcbSad stats_chunks.curchunks -= (size / chunksize);
14858a475bcbSad #endif
14868a475bcbSad malloc_mutex_unlock(&chunks_mtx);
14878a475bcbSad }
14888a475bcbSad
14898a475bcbSad /*
14908a475bcbSad * End chunk management functions.
14918a475bcbSad */
14928a475bcbSad /******************************************************************************/
14938a475bcbSad /*
14948a475bcbSad * Begin arena.
14958a475bcbSad */
14968a475bcbSad
14978a475bcbSad /*
1498bf5f78d5Sad * Choose a per-CPU arena.
14998a475bcbSad */
1500380c3da0Sad static __noinline arena_t *
choose_arena_hard(void)1501380c3da0Sad choose_arena_hard(void)
15028a475bcbSad {
15038a475bcbSad
1504bf5f78d5Sad assert(arenas[0] != NULL);
1505bf5f78d5Sad
15068a475bcbSad malloc_mutex_lock(&arenas_mtx);
1507bf5f78d5Sad for (unsigned i = 1; i < ncpus; i++)
1508bf5f78d5Sad if (arenas[i] == NULL)
1509bf5f78d5Sad arenas[i] = arenas_extend();
15108a475bcbSad malloc_mutex_unlock(&arenas_mtx);
15118a475bcbSad
1512bf5f78d5Sad return arenas[thr_curcpu()];
15138a475bcbSad }
15148a475bcbSad
1515380c3da0Sad static inline arena_t *
choose_arena(void)1516380c3da0Sad choose_arena(void)
1517380c3da0Sad {
1518bf5f78d5Sad arena_t *arena;
1519380c3da0Sad
1520bf5f78d5Sad /* NB: when libpthread is absent, thr_curcpu() always returns zero. */
1521bf5f78d5Sad arena = arenas[thr_curcpu()];
1522bf5f78d5Sad if (__predict_true(arena != NULL))
1523bf5f78d5Sad return arena;
1524380c3da0Sad
1525380c3da0Sad return choose_arena_hard();
1526380c3da0Sad }
1527380c3da0Sad
1528562025fdSad static int
arena_chunk_comp(void * context,const void * va,const void * vb)1529562025fdSad arena_chunk_comp(void *context, const void *va, const void *vb)
1530562025fdSad {
1531562025fdSad const arena_chunk_t *a = va, *b = vb;
1532562025fdSad int diff;
1533562025fdSad
1534562025fdSad assert(a != NULL);
1535562025fdSad assert(b != NULL);
1536562025fdSad
1537562025fdSad if ((diff = a->max_frun_npages - b->max_frun_npages) != 0)
1538562025fdSad return diff;
1539562025fdSad return ptrcmp(a, b);
1540562025fdSad }
1541562025fdSad
1542562025fdSad static int
arena_run_comp(void * context,const void * a,const void * b)1543562025fdSad arena_run_comp(void *context, const void *a, const void *b)
15448a475bcbSad {
15458a475bcbSad
15468a475bcbSad assert(a != NULL);
15478a475bcbSad assert(b != NULL);
15488a475bcbSad
1549562025fdSad return ptrcmp(a, b);
15508a475bcbSad }
15518a475bcbSad
15528a475bcbSad static inline void *
arena_run_reg_alloc(arena_run_t * run,arena_bin_t * bin)15538a475bcbSad arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin)
15548a475bcbSad {
15558a475bcbSad void *ret;
15568a475bcbSad unsigned i, mask, bit, regind;
15578a475bcbSad
15588a475bcbSad assert(run->magic == ARENA_RUN_MAGIC);
15598a475bcbSad assert(run->regs_minelm < bin->regs_mask_nelms);
15608a475bcbSad
15618a475bcbSad /*
15628a475bcbSad * Move the first check outside the loop, so that run->regs_minelm can
15638a475bcbSad * be updated unconditionally, without the possibility of updating it
15648a475bcbSad * multiple times.
15658a475bcbSad */
15668a475bcbSad i = run->regs_minelm;
15678a475bcbSad mask = run->regs_mask[i];
15688a475bcbSad if (mask != 0) {
15698a475bcbSad /* Usable allocation found. */
15708a475bcbSad bit = ffs((int)mask) - 1;
15718a475bcbSad
15728a475bcbSad regind = ((i << (SIZEOF_INT_2POW + 3)) + bit);
15738a475bcbSad ret = (void *)(((uintptr_t)run) + bin->reg0_offset
15748a475bcbSad + (bin->reg_size * regind));
15758a475bcbSad
15768a475bcbSad /* Clear bit. */
1577f34d1bddSkamil mask ^= (1U << bit);
15788a475bcbSad run->regs_mask[i] = mask;
15798a475bcbSad
15808a475bcbSad return (ret);
15818a475bcbSad }
15828a475bcbSad
15838a475bcbSad for (i++; i < bin->regs_mask_nelms; i++) {
15848a475bcbSad mask = run->regs_mask[i];
15858a475bcbSad if (mask != 0) {
15868a475bcbSad /* Usable allocation found. */
15878a475bcbSad bit = ffs((int)mask) - 1;
15888a475bcbSad
15898a475bcbSad regind = ((i << (SIZEOF_INT_2POW + 3)) + bit);
15908a475bcbSad ret = (void *)(((uintptr_t)run) + bin->reg0_offset
15918a475bcbSad + (bin->reg_size * regind));
15928a475bcbSad
15938a475bcbSad /* Clear bit. */
1594f34d1bddSkamil mask ^= (1U << bit);
15958a475bcbSad run->regs_mask[i] = mask;
15968a475bcbSad
15978a475bcbSad /*
15988a475bcbSad * Make a note that nothing before this element
15998a475bcbSad * contains a free region.
16008a475bcbSad */
16018a475bcbSad run->regs_minelm = i; /* Low payoff: + (mask == 0); */
16028a475bcbSad
16038a475bcbSad return (ret);
16048a475bcbSad }
16058a475bcbSad }
16068a475bcbSad /* Not reached. */
1607687cd24eSyamt /* LINTED */
16088a475bcbSad assert(0);
16098a475bcbSad return (NULL);
16108a475bcbSad }
16118a475bcbSad
16128a475bcbSad static inline void
arena_run_reg_dalloc(arena_run_t * run,arena_bin_t * bin,void * ptr,size_t size)16138a475bcbSad arena_run_reg_dalloc(arena_run_t *run, arena_bin_t *bin, void *ptr, size_t size)
16148a475bcbSad {
16158a475bcbSad /*
16168a475bcbSad * To divide by a number D that is not a power of two we multiply
16178a475bcbSad * by (2^21 / D) and then right shift by 21 positions.
16188a475bcbSad *
16198a475bcbSad * X / D
16208a475bcbSad *
16218a475bcbSad * becomes
16228a475bcbSad *
16238a475bcbSad * (X * size_invs[(D >> QUANTUM_2POW_MIN) - 3]) >> SIZE_INV_SHIFT
16248a475bcbSad */
16258a475bcbSad #define SIZE_INV_SHIFT 21
16268a475bcbSad #define SIZE_INV(s) (((1 << SIZE_INV_SHIFT) / (s << QUANTUM_2POW_MIN)) + 1)
16278a475bcbSad static const unsigned size_invs[] = {
16288a475bcbSad SIZE_INV(3),
16298a475bcbSad SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7),
16308a475bcbSad SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11),
16318a475bcbSad SIZE_INV(12),SIZE_INV(13), SIZE_INV(14), SIZE_INV(15),
16328a475bcbSad SIZE_INV(16),SIZE_INV(17), SIZE_INV(18), SIZE_INV(19),
16338a475bcbSad SIZE_INV(20),SIZE_INV(21), SIZE_INV(22), SIZE_INV(23),
16348a475bcbSad SIZE_INV(24),SIZE_INV(25), SIZE_INV(26), SIZE_INV(27),
16358a475bcbSad SIZE_INV(28),SIZE_INV(29), SIZE_INV(30), SIZE_INV(31)
16368a475bcbSad #if (QUANTUM_2POW_MIN < 4)
16378a475bcbSad ,
16388a475bcbSad SIZE_INV(32), SIZE_INV(33), SIZE_INV(34), SIZE_INV(35),
16398a475bcbSad SIZE_INV(36), SIZE_INV(37), SIZE_INV(38), SIZE_INV(39),
16408a475bcbSad SIZE_INV(40), SIZE_INV(41), SIZE_INV(42), SIZE_INV(43),
16418a475bcbSad SIZE_INV(44), SIZE_INV(45), SIZE_INV(46), SIZE_INV(47),
16428a475bcbSad SIZE_INV(48), SIZE_INV(49), SIZE_INV(50), SIZE_INV(51),
16438a475bcbSad SIZE_INV(52), SIZE_INV(53), SIZE_INV(54), SIZE_INV(55),
16448a475bcbSad SIZE_INV(56), SIZE_INV(57), SIZE_INV(58), SIZE_INV(59),
16458a475bcbSad SIZE_INV(60), SIZE_INV(61), SIZE_INV(62), SIZE_INV(63)
16468a475bcbSad #endif
16478a475bcbSad };
16488a475bcbSad unsigned diff, regind, elm, bit;
16498a475bcbSad
1650687cd24eSyamt /* LINTED */
16518a475bcbSad assert(run->magic == ARENA_RUN_MAGIC);
16528a475bcbSad assert(((sizeof(size_invs)) / sizeof(unsigned)) + 3
16538a475bcbSad >= (SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN));
16548a475bcbSad
16558a475bcbSad /*
16568a475bcbSad * Avoid doing division with a variable divisor if possible. Using
16578a475bcbSad * actual division here can reduce allocator throughput by over 20%!
16588a475bcbSad */
16598a475bcbSad diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset);
16608a475bcbSad if ((size & (size - 1)) == 0) {
16618a475bcbSad /*
16628a475bcbSad * log2_table allows fast division of a power of two in the
16638a475bcbSad * [1..128] range.
16648a475bcbSad *
16658a475bcbSad * (x / divisor) becomes (x >> log2_table[divisor - 1]).
16668a475bcbSad */
16678a475bcbSad static const unsigned char log2_table[] = {
16688a475bcbSad 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4,
16698a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
16708a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16718a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
16728a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16738a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16748a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16758a475bcbSad 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7
16768a475bcbSad };
16778a475bcbSad
16788a475bcbSad if (size <= 128)
16798a475bcbSad regind = (diff >> log2_table[size - 1]);
16808a475bcbSad else if (size <= 32768)
16818a475bcbSad regind = diff >> (8 + log2_table[(size >> 8) - 1]);
16828a475bcbSad else {
16838a475bcbSad /*
16848a475bcbSad * The page size is too large for us to use the lookup
16858a475bcbSad * table. Use real division.
16868a475bcbSad */
16878de47c09Schristos regind = (unsigned)(diff / size);
16888a475bcbSad }
168913122b28Sad } else if (size <= (((sizeof(size_invs) / sizeof(unsigned)) + 2)
169013122b28Sad << QUANTUM_2POW_MIN)) {
16918a475bcbSad regind = size_invs[(size >> QUANTUM_2POW_MIN) - 3] * diff;
16928a475bcbSad regind >>= SIZE_INV_SHIFT;
16938a475bcbSad } else {
16948a475bcbSad /*
16958a475bcbSad * size_invs isn't large enough to handle this size class, so
16968a475bcbSad * calculate regind using actual division. This only happens
16978a475bcbSad * if the user increases small_max via the 'S' runtime
16988a475bcbSad * configuration option.
16998a475bcbSad */
17008de47c09Schristos regind = (unsigned)(diff / size);
17018a475bcbSad };
17028a475bcbSad assert(diff == regind * size);
17038a475bcbSad assert(regind < bin->nregs);
17048a475bcbSad
17058a475bcbSad elm = regind >> (SIZEOF_INT_2POW + 3);
17068a475bcbSad if (elm < run->regs_minelm)
17078a475bcbSad run->regs_minelm = elm;
17088a475bcbSad bit = regind - (elm << (SIZEOF_INT_2POW + 3));
1709f34d1bddSkamil assert((run->regs_mask[elm] & (1U << bit)) == 0);
1710f34d1bddSkamil run->regs_mask[elm] |= (1U << bit);
17118a475bcbSad #undef SIZE_INV
17128a475bcbSad #undef SIZE_INV_SHIFT
17138a475bcbSad }
17148a475bcbSad
17158a475bcbSad static void
arena_run_split(arena_t * arena,arena_run_t * run,size_t size)17168a475bcbSad arena_run_split(arena_t *arena, arena_run_t *run, size_t size)
17178a475bcbSad {
17188a475bcbSad arena_chunk_t *chunk;
17198a475bcbSad unsigned run_ind, map_offset, total_pages, need_pages, rem_pages;
17208a475bcbSad unsigned i;
17218a475bcbSad
17228a475bcbSad chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
17238a475bcbSad run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk)
17248a475bcbSad >> pagesize_2pow);
17258a475bcbSad total_pages = chunk->map[run_ind].npages;
17268de47c09Schristos need_pages = (unsigned)(size >> pagesize_2pow);
17278a475bcbSad assert(need_pages <= total_pages);
17288a475bcbSad rem_pages = total_pages - need_pages;
17298a475bcbSad
17308a475bcbSad /* Split enough pages from the front of run to fit allocation size. */
17318a475bcbSad map_offset = run_ind;
17328a475bcbSad for (i = 0; i < need_pages; i++) {
17338a475bcbSad chunk->map[map_offset + i].npages = need_pages;
17348a475bcbSad chunk->map[map_offset + i].pos = i;
17358a475bcbSad }
17368a475bcbSad
17378a475bcbSad /* Keep track of trailing unused pages for later use. */
17388a475bcbSad if (rem_pages > 0) {
17398a475bcbSad /* Update map for trailing pages. */
17408a475bcbSad map_offset += need_pages;
17418a475bcbSad chunk->map[map_offset].npages = rem_pages;
17428a475bcbSad chunk->map[map_offset].pos = POS_FREE;
17438a475bcbSad chunk->map[map_offset + rem_pages - 1].npages = rem_pages;
17448a475bcbSad chunk->map[map_offset + rem_pages - 1].pos = POS_FREE;
17458a475bcbSad }
17468a475bcbSad
17478a475bcbSad chunk->pages_used += need_pages;
17488a475bcbSad }
17498a475bcbSad
17508a475bcbSad static arena_chunk_t *
arena_chunk_alloc(arena_t * arena)17518a475bcbSad arena_chunk_alloc(arena_t *arena)
17528a475bcbSad {
17538a475bcbSad arena_chunk_t *chunk;
17548a475bcbSad
17558a475bcbSad if (arena->spare != NULL) {
17568a475bcbSad chunk = arena->spare;
17578a475bcbSad arena->spare = NULL;
17588a475bcbSad
1759562025fdSad rb_tree_insert_node(&arena->chunks, chunk);
17608a475bcbSad } else {
17618a475bcbSad chunk = (arena_chunk_t *)chunk_alloc(chunksize);
17628a475bcbSad if (chunk == NULL)
17638a475bcbSad return (NULL);
17648a475bcbSad #ifdef MALLOC_STATS
17658a475bcbSad arena->stats.mapped += chunksize;
17668a475bcbSad #endif
17678a475bcbSad
17688a475bcbSad chunk->arena = arena;
17698a475bcbSad
17708a475bcbSad /*
17718a475bcbSad * Claim that no pages are in use, since the header is merely
17728a475bcbSad * overhead.
17738a475bcbSad */
17748a475bcbSad chunk->pages_used = 0;
17758a475bcbSad
17768a475bcbSad chunk->max_frun_npages = chunk_npages -
17778a475bcbSad arena_chunk_header_npages;
17788a475bcbSad chunk->min_frun_ind = arena_chunk_header_npages;
17798a475bcbSad
17808a475bcbSad /*
17818a475bcbSad * Initialize enough of the map to support one maximal free run.
17828a475bcbSad */
17838a475bcbSad chunk->map[arena_chunk_header_npages].npages = chunk_npages -
17848a475bcbSad arena_chunk_header_npages;
17858a475bcbSad chunk->map[arena_chunk_header_npages].pos = POS_FREE;
17868a475bcbSad chunk->map[chunk_npages - 1].npages = chunk_npages -
17878a475bcbSad arena_chunk_header_npages;
17888a475bcbSad chunk->map[chunk_npages - 1].pos = POS_FREE;
1789c75fe7b8Sjoerg
1790562025fdSad rb_tree_insert_node(&arena->chunks, chunk);
17918a475bcbSad }
17928a475bcbSad
17938a475bcbSad return (chunk);
17948a475bcbSad }
17958a475bcbSad
17968a475bcbSad static void
arena_chunk_dealloc(arena_t * arena,arena_chunk_t * chunk)17978a475bcbSad arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)
17988a475bcbSad {
17998a475bcbSad
18008a475bcbSad /*
18018a475bcbSad * Remove chunk from the chunk tree, regardless of whether this chunk
18028a475bcbSad * will be cached, so that the arena does not use it.
18038a475bcbSad */
1804562025fdSad rb_tree_remove_node(&chunk->arena->chunks, chunk);
18058a475bcbSad
1806bf5f78d5Sad if (NOT_OPT(hint)) {
18078a475bcbSad if (arena->spare != NULL) {
18088a475bcbSad chunk_dealloc((void *)arena->spare, chunksize);
18098a475bcbSad #ifdef MALLOC_STATS
18108a475bcbSad arena->stats.mapped -= chunksize;
18118a475bcbSad #endif
18128a475bcbSad }
18138a475bcbSad arena->spare = chunk;
18148a475bcbSad } else {
18158a475bcbSad assert(arena->spare == NULL);
18168a475bcbSad chunk_dealloc((void *)chunk, chunksize);
18178a475bcbSad #ifdef MALLOC_STATS
18188a475bcbSad arena->stats.mapped -= chunksize;
18198a475bcbSad #endif
18208a475bcbSad }
18218a475bcbSad }
18228a475bcbSad
18238a475bcbSad static arena_run_t *
arena_run_alloc(arena_t * arena,size_t size)18248a475bcbSad arena_run_alloc(arena_t *arena, size_t size)
18258a475bcbSad {
1826562025fdSad arena_chunk_t *chunk;
18278a475bcbSad arena_run_t *run;
1828c75fe7b8Sjoerg unsigned need_npages;
18298a475bcbSad
18308a475bcbSad assert(size <= (chunksize - (arena_chunk_header_npages <<
18318a475bcbSad pagesize_2pow)));
18328a475bcbSad assert((size & pagesize_mask) == 0);
18338a475bcbSad
18348a475bcbSad /*
1835c75fe7b8Sjoerg * Search through the arena chunk tree for a large enough free run.
1836c75fe7b8Sjoerg * Tree order ensures that any exact fit is picked immediately or
1837c75fe7b8Sjoerg * otherwise the lowest address of the next size.
18388a475bcbSad */
18398de47c09Schristos need_npages = (unsigned)(size >> pagesize_2pow);
1840e7a9e46bSad /* LINTED */
1841c75fe7b8Sjoerg for (;;) {
1842562025fdSad rb_node_t *node = arena->chunks.rbt_root;
1843c75fe7b8Sjoerg chunk = NULL;
1844562025fdSad while (!RB_SENTINEL_P(node)) {
1845562025fdSad assert(offsetof(struct arena_chunk_s, link) == 0);
1846562025fdSad arena_chunk_t *chunk_tmp = (arena_chunk_t *)node;
1847c75fe7b8Sjoerg if (chunk_tmp->max_frun_npages == need_npages) {
1848c75fe7b8Sjoerg chunk = chunk_tmp;
1849c75fe7b8Sjoerg break;
1850c75fe7b8Sjoerg }
1851c75fe7b8Sjoerg if (chunk_tmp->max_frun_npages < need_npages) {
1852562025fdSad node = node->rb_nodes[1];
1853c75fe7b8Sjoerg continue;
1854c75fe7b8Sjoerg }
1855c75fe7b8Sjoerg chunk = chunk_tmp;
1856562025fdSad node = node->rb_nodes[0];
1857c75fe7b8Sjoerg }
1858c75fe7b8Sjoerg if (chunk == NULL)
1859c75fe7b8Sjoerg break;
18608a475bcbSad /*
1861c75fe7b8Sjoerg * At this point, the chunk must have a cached run size large
1862c75fe7b8Sjoerg * enough to fit the allocation.
18638a475bcbSad */
1864c75fe7b8Sjoerg assert(need_npages <= chunk->max_frun_npages);
1865c75fe7b8Sjoerg {
18668a475bcbSad arena_chunk_map_t *mapelm;
18678a475bcbSad unsigned i;
18688a475bcbSad unsigned max_frun_npages = 0;
18698a475bcbSad unsigned min_frun_ind = chunk_npages;
18708a475bcbSad
18718a475bcbSad assert(chunk->min_frun_ind >=
18728a475bcbSad arena_chunk_header_npages);
18738a475bcbSad for (i = chunk->min_frun_ind; i < chunk_npages;) {
18748a475bcbSad mapelm = &chunk->map[i];
18758a475bcbSad if (mapelm->pos == POS_FREE) {
18768a475bcbSad if (mapelm->npages >= need_npages) {
18778a475bcbSad run = (arena_run_t *)
18788a475bcbSad ((uintptr_t)chunk + (i <<
18798a475bcbSad pagesize_2pow));
18808a475bcbSad /* Update page map. */
18818a475bcbSad arena_run_split(arena, run,
18828a475bcbSad size);
18838a475bcbSad return (run);
18848a475bcbSad }
18858a475bcbSad if (mapelm->npages >
18868a475bcbSad max_frun_npages) {
18878a475bcbSad max_frun_npages =
18888a475bcbSad mapelm->npages;
18898a475bcbSad }
18908a475bcbSad if (i < min_frun_ind) {
18918a475bcbSad min_frun_ind = i;
18928a475bcbSad if (i < chunk->min_frun_ind)
18938a475bcbSad chunk->min_frun_ind = i;
18948a475bcbSad }
18958a475bcbSad }
18968a475bcbSad i += mapelm->npages;
18978a475bcbSad }
18988a475bcbSad /*
18998a475bcbSad * Search failure. Reset cached chunk->max_frun_npages.
19008a475bcbSad * chunk->min_frun_ind was already reset above (if
19018a475bcbSad * necessary).
19028a475bcbSad */
1903562025fdSad rb_tree_remove_node(&arena->chunks, chunk);
19048a475bcbSad chunk->max_frun_npages = max_frun_npages;
1905562025fdSad rb_tree_insert_node(&arena->chunks, chunk);
19068a475bcbSad }
19078a475bcbSad }
19088a475bcbSad
19098a475bcbSad /*
19108a475bcbSad * No usable runs. Create a new chunk from which to allocate the run.
19118a475bcbSad */
19128a475bcbSad chunk = arena_chunk_alloc(arena);
19138a475bcbSad if (chunk == NULL)
19148a475bcbSad return (NULL);
19158a475bcbSad run = (arena_run_t *)((uintptr_t)chunk + (arena_chunk_header_npages <<
19168a475bcbSad pagesize_2pow));
19178a475bcbSad /* Update page map. */
19188a475bcbSad arena_run_split(arena, run, size);
19198a475bcbSad return (run);
19208a475bcbSad }
19218a475bcbSad
19228a475bcbSad static void
arena_run_dalloc(arena_t * arena,arena_run_t * run,size_t size)19238a475bcbSad arena_run_dalloc(arena_t *arena, arena_run_t *run, size_t size)
19248a475bcbSad {
19258a475bcbSad arena_chunk_t *chunk;
19268a475bcbSad unsigned run_ind, run_pages;
19278a475bcbSad
19288a475bcbSad chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
19298a475bcbSad
19308a475bcbSad run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk)
19318a475bcbSad >> pagesize_2pow);
19328a475bcbSad assert(run_ind >= arena_chunk_header_npages);
19338a475bcbSad assert(run_ind < (chunksize >> pagesize_2pow));
19348de47c09Schristos run_pages = (unsigned)(size >> pagesize_2pow);
19358a475bcbSad assert(run_pages == chunk->map[run_ind].npages);
19368a475bcbSad
19378a475bcbSad /* Subtract pages from count of pages used in chunk. */
19388a475bcbSad chunk->pages_used -= run_pages;
19398a475bcbSad
19408a475bcbSad /* Mark run as deallocated. */
19418a475bcbSad assert(chunk->map[run_ind].npages == run_pages);
19428a475bcbSad chunk->map[run_ind].pos = POS_FREE;
19438a475bcbSad assert(chunk->map[run_ind + run_pages - 1].npages == run_pages);
19448a475bcbSad chunk->map[run_ind + run_pages - 1].pos = POS_FREE;
19458a475bcbSad
19468a475bcbSad /*
19478a475bcbSad * Tell the kernel that we don't need the data in this run, but only if
19488a475bcbSad * requested via runtime configuration.
19498a475bcbSad */
1950bf5f78d5Sad if (OPT(hint))
19518a475bcbSad madvise(run, size, MADV_FREE);
19528a475bcbSad
19538a475bcbSad /* Try to coalesce with neighboring runs. */
19548a475bcbSad if (run_ind > arena_chunk_header_npages &&
19558a475bcbSad chunk->map[run_ind - 1].pos == POS_FREE) {
19568a475bcbSad unsigned prev_npages;
19578a475bcbSad
19588a475bcbSad /* Coalesce with previous run. */
19598a475bcbSad prev_npages = chunk->map[run_ind - 1].npages;
19608a475bcbSad run_ind -= prev_npages;
19618a475bcbSad assert(chunk->map[run_ind].npages == prev_npages);
19628a475bcbSad assert(chunk->map[run_ind].pos == POS_FREE);
19638a475bcbSad run_pages += prev_npages;
19648a475bcbSad
19658a475bcbSad chunk->map[run_ind].npages = run_pages;
19668a475bcbSad assert(chunk->map[run_ind].pos == POS_FREE);
19678a475bcbSad chunk->map[run_ind + run_pages - 1].npages = run_pages;
19688a475bcbSad assert(chunk->map[run_ind + run_pages - 1].pos == POS_FREE);
19698a475bcbSad }
19708a475bcbSad
19718a475bcbSad if (run_ind + run_pages < chunk_npages &&
19728a475bcbSad chunk->map[run_ind + run_pages].pos == POS_FREE) {
19738a475bcbSad unsigned next_npages;
19748a475bcbSad
19758a475bcbSad /* Coalesce with next run. */
19768a475bcbSad next_npages = chunk->map[run_ind + run_pages].npages;
19778a475bcbSad run_pages += next_npages;
19788a475bcbSad assert(chunk->map[run_ind + run_pages - 1].npages ==
19798a475bcbSad next_npages);
19808a475bcbSad assert(chunk->map[run_ind + run_pages - 1].pos == POS_FREE);
19818a475bcbSad
19828a475bcbSad chunk->map[run_ind].npages = run_pages;
19838a475bcbSad chunk->map[run_ind].pos = POS_FREE;
19848a475bcbSad chunk->map[run_ind + run_pages - 1].npages = run_pages;
19858a475bcbSad assert(chunk->map[run_ind + run_pages - 1].pos == POS_FREE);
19868a475bcbSad }
19878a475bcbSad
1988c75fe7b8Sjoerg if (chunk->map[run_ind].npages > chunk->max_frun_npages) {
1989562025fdSad rb_tree_remove_node(&arena->chunks, chunk);
19908a475bcbSad chunk->max_frun_npages = chunk->map[run_ind].npages;
1991562025fdSad rb_tree_insert_node(&arena->chunks, chunk);
1992c75fe7b8Sjoerg }
19938a475bcbSad if (run_ind < chunk->min_frun_ind)
19948a475bcbSad chunk->min_frun_ind = run_ind;
19958a475bcbSad
19968a475bcbSad /* Deallocate chunk if it is now completely unused. */
19978a475bcbSad if (chunk->pages_used == 0)
19988a475bcbSad arena_chunk_dealloc(arena, chunk);
19998a475bcbSad }
20008a475bcbSad
20018a475bcbSad static arena_run_t *
arena_bin_nonfull_run_get(arena_t * arena,arena_bin_t * bin)20028a475bcbSad arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
20038a475bcbSad {
20048a475bcbSad arena_run_t *run;
20058a475bcbSad unsigned i, remainder;
20068a475bcbSad
20078a475bcbSad /* Look for a usable run. */
2008562025fdSad if ((run = RB_TREE_MIN(&bin->runs)) != NULL) {
20098a475bcbSad /* run is guaranteed to have available space. */
2010562025fdSad rb_tree_remove_node(&bin->runs, run);
20118a475bcbSad #ifdef MALLOC_STATS
20128a475bcbSad bin->stats.reruns++;
20138a475bcbSad #endif
20148a475bcbSad return (run);
20158a475bcbSad }
20168a475bcbSad /* No existing runs have any space available. */
20178a475bcbSad
20188a475bcbSad /* Allocate a new run. */
20198a475bcbSad run = arena_run_alloc(arena, bin->run_size);
20208a475bcbSad if (run == NULL)
20218a475bcbSad return (NULL);
20228a475bcbSad
20238a475bcbSad /* Initialize run internals. */
20248a475bcbSad run->bin = bin;
20258a475bcbSad
20268a475bcbSad for (i = 0; i < bin->regs_mask_nelms; i++)
20278a475bcbSad run->regs_mask[i] = UINT_MAX;
20288a475bcbSad remainder = bin->nregs & ((1 << (SIZEOF_INT_2POW + 3)) - 1);
20298a475bcbSad if (remainder != 0) {
20308a475bcbSad /* The last element has spare bits that need to be unset. */
20318a475bcbSad run->regs_mask[i] = (UINT_MAX >> ((1 << (SIZEOF_INT_2POW + 3))
20328a475bcbSad - remainder));
20338a475bcbSad }
20348a475bcbSad
20358a475bcbSad run->regs_minelm = 0;
20368a475bcbSad
20378a475bcbSad run->nfree = bin->nregs;
20388a475bcbSad #ifdef MALLOC_DEBUG
20398a475bcbSad run->magic = ARENA_RUN_MAGIC;
20408a475bcbSad #endif
20418a475bcbSad
20428a475bcbSad #ifdef MALLOC_STATS
20438a475bcbSad bin->stats.nruns++;
20448a475bcbSad bin->stats.curruns++;
20458a475bcbSad if (bin->stats.curruns > bin->stats.highruns)
20468a475bcbSad bin->stats.highruns = bin->stats.curruns;
20478a475bcbSad #endif
20488a475bcbSad return (run);
20498a475bcbSad }
20508a475bcbSad
20518a475bcbSad /* bin->runcur must have space available before this function is called. */
20528a475bcbSad static inline void *
arena_bin_malloc_easy(arena_t * arena,arena_bin_t * bin,arena_run_t * run)20538a475bcbSad arena_bin_malloc_easy(arena_t *arena, arena_bin_t *bin, arena_run_t *run)
20548a475bcbSad {
20558a475bcbSad void *ret;
20568a475bcbSad
20578a475bcbSad assert(run->magic == ARENA_RUN_MAGIC);
20588a475bcbSad assert(run->nfree > 0);
20598a475bcbSad
20608a475bcbSad ret = arena_run_reg_alloc(run, bin);
20618a475bcbSad assert(ret != NULL);
20628a475bcbSad run->nfree--;
20638a475bcbSad
20648a475bcbSad return (ret);
20658a475bcbSad }
20668a475bcbSad
20678a475bcbSad /* Re-fill bin->runcur, then call arena_bin_malloc_easy(). */
20688a475bcbSad static void *
arena_bin_malloc_hard(arena_t * arena,arena_bin_t * bin)20698a475bcbSad arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
20708a475bcbSad {
20718a475bcbSad
20728a475bcbSad bin->runcur = arena_bin_nonfull_run_get(arena, bin);
20738a475bcbSad if (bin->runcur == NULL)
20748a475bcbSad return (NULL);
20758a475bcbSad assert(bin->runcur->magic == ARENA_RUN_MAGIC);
20768a475bcbSad assert(bin->runcur->nfree > 0);
20778a475bcbSad
20788a475bcbSad return (arena_bin_malloc_easy(arena, bin, bin->runcur));
20798a475bcbSad }
20808a475bcbSad
20818a475bcbSad /*
20828a475bcbSad * Calculate bin->run_size such that it meets the following constraints:
20838a475bcbSad *
20848a475bcbSad * *) bin->run_size >= min_run_size
20858a475bcbSad * *) bin->run_size <= arena_maxclass
20868a475bcbSad * *) bin->run_size <= RUN_MAX_SMALL
20878a475bcbSad * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed).
20888a475bcbSad *
20898a475bcbSad * bin->nregs, bin->regs_mask_nelms, and bin->reg0_offset are
20908a475bcbSad * also calculated here, since these settings are all interdependent.
20918a475bcbSad */
20928a475bcbSad static size_t
arena_bin_run_size_calc(arena_bin_t * bin,size_t min_run_size)20938a475bcbSad arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
20948a475bcbSad {
20958a475bcbSad size_t try_run_size, good_run_size;
20968a475bcbSad unsigned good_nregs, good_mask_nelms, good_reg0_offset;
20978a475bcbSad unsigned try_nregs, try_mask_nelms, try_reg0_offset;
20988a475bcbSad
20998a475bcbSad assert(min_run_size >= pagesize);
21008a475bcbSad assert(min_run_size <= arena_maxclass);
21018a475bcbSad assert(min_run_size <= RUN_MAX_SMALL);
21028a475bcbSad
21038a475bcbSad /*
21048a475bcbSad * Calculate known-valid settings before entering the run_size
21058a475bcbSad * expansion loop, so that the first part of the loop always copies
21068a475bcbSad * valid settings.
21078a475bcbSad *
21088a475bcbSad * The do..while loop iteratively reduces the number of regions until
21098a475bcbSad * the run header and the regions no longer overlap. A closed formula
21108a475bcbSad * would be quite messy, since there is an interdependency between the
21118a475bcbSad * header's mask length and the number of regions.
21128a475bcbSad */
21138a475bcbSad try_run_size = min_run_size;
21148de47c09Schristos try_nregs = (unsigned)(((try_run_size - sizeof(arena_run_t)) /
211551cca89dSnjoly bin->reg_size) + 1); /* Counter-act try_nregs-- in loop. */
21168a475bcbSad do {
21178a475bcbSad try_nregs--;
21188a475bcbSad try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) +
21198a475bcbSad ((try_nregs & ((1 << (SIZEOF_INT_2POW + 3)) - 1)) ? 1 : 0);
21208de47c09Schristos try_reg0_offset = (unsigned)(try_run_size -
21218de47c09Schristos (try_nregs * bin->reg_size));
21228a475bcbSad } while (sizeof(arena_run_t) + (sizeof(unsigned) * (try_mask_nelms - 1))
21238a475bcbSad > try_reg0_offset);
21248a475bcbSad
21258a475bcbSad /* run_size expansion loop. */
21268a475bcbSad do {
21278a475bcbSad /*
21288a475bcbSad * Copy valid settings before trying more aggressive settings.
21298a475bcbSad */
21308a475bcbSad good_run_size = try_run_size;
21318a475bcbSad good_nregs = try_nregs;
21328a475bcbSad good_mask_nelms = try_mask_nelms;
21338a475bcbSad good_reg0_offset = try_reg0_offset;
21348a475bcbSad
21358a475bcbSad /* Try more aggressive settings. */
21368a475bcbSad try_run_size += pagesize;
21378de47c09Schristos try_nregs = (unsigned)(((try_run_size - sizeof(arena_run_t)) /
21388de47c09Schristos bin->reg_size) + 1); /* Counter-act try_nregs-- in loop. */
21398a475bcbSad do {
21408a475bcbSad try_nregs--;
21418a475bcbSad try_mask_nelms = (try_nregs >> (SIZEOF_INT_2POW + 3)) +
21428a475bcbSad ((try_nregs & ((1 << (SIZEOF_INT_2POW + 3)) - 1)) ?
21438a475bcbSad 1 : 0);
21448de47c09Schristos try_reg0_offset = (unsigned)(try_run_size - (try_nregs *
21458de47c09Schristos bin->reg_size));
21468a475bcbSad } while (sizeof(arena_run_t) + (sizeof(unsigned) *
21478a475bcbSad (try_mask_nelms - 1)) > try_reg0_offset);
21488a475bcbSad } while (try_run_size <= arena_maxclass && try_run_size <= RUN_MAX_SMALL
214951cca89dSnjoly && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX
215051cca89dSnjoly && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size);
21518a475bcbSad
21528a475bcbSad assert(sizeof(arena_run_t) + (sizeof(unsigned) * (good_mask_nelms - 1))
21538a475bcbSad <= good_reg0_offset);
21548a475bcbSad assert((good_mask_nelms << (SIZEOF_INT_2POW + 3)) >= good_nregs);
21558a475bcbSad
21568a475bcbSad /* Copy final settings. */
21578a475bcbSad bin->run_size = good_run_size;
21588a475bcbSad bin->nregs = good_nregs;
21598a475bcbSad bin->regs_mask_nelms = good_mask_nelms;
21608a475bcbSad bin->reg0_offset = good_reg0_offset;
21618a475bcbSad
21628a475bcbSad return (good_run_size);
21638a475bcbSad }
21648a475bcbSad
21658a475bcbSad static void *
arena_malloc(arena_t * arena,size_t size)21668a475bcbSad arena_malloc(arena_t *arena, size_t size)
21678a475bcbSad {
21688a475bcbSad void *ret;
21698a475bcbSad
21708a475bcbSad assert(arena != NULL);
21718a475bcbSad assert(arena->magic == ARENA_MAGIC);
21728a475bcbSad assert(size != 0);
21738a475bcbSad assert(QUANTUM_CEILING(size) <= arena_maxclass);
21748a475bcbSad
21758a475bcbSad if (size <= bin_maxclass) {
21768a475bcbSad arena_bin_t *bin;
21778a475bcbSad arena_run_t *run;
21788a475bcbSad
21798a475bcbSad /* Small allocation. */
21808a475bcbSad
21818a475bcbSad if (size < small_min) {
21828a475bcbSad /* Tiny. */
21838a475bcbSad size = pow2_ceil(size);
21848a475bcbSad bin = &arena->bins[ffs((int)(size >> (TINY_MIN_2POW +
21858a475bcbSad 1)))];
21868a475bcbSad #if (!defined(NDEBUG) || defined(MALLOC_STATS))
21878a475bcbSad /*
21888a475bcbSad * Bin calculation is always correct, but we may need
21898a475bcbSad * to fix size for the purposes of assertions and/or
21908a475bcbSad * stats accuracy.
21918a475bcbSad */
21928a475bcbSad if (size < (1 << TINY_MIN_2POW))
21938a475bcbSad size = (1 << TINY_MIN_2POW);
21948a475bcbSad #endif
21958a475bcbSad } else if (size <= small_max) {
21968a475bcbSad /* Quantum-spaced. */
21978a475bcbSad size = QUANTUM_CEILING(size);
21988a475bcbSad bin = &arena->bins[ntbins + (size >> opt_quantum_2pow)
21998a475bcbSad - 1];
22008a475bcbSad } else {
22018a475bcbSad /* Sub-page. */
22028a475bcbSad size = pow2_ceil(size);
22038a475bcbSad bin = &arena->bins[ntbins + nqbins
22048a475bcbSad + (ffs((int)(size >> opt_small_max_2pow)) - 2)];
22058a475bcbSad }
22068a475bcbSad assert(size == bin->reg_size);
22078a475bcbSad
22088a475bcbSad malloc_mutex_lock(&arena->mtx);
22098a475bcbSad if ((run = bin->runcur) != NULL && run->nfree > 0)
22108a475bcbSad ret = arena_bin_malloc_easy(arena, bin, run);
22118a475bcbSad else
22128a475bcbSad ret = arena_bin_malloc_hard(arena, bin);
22138a475bcbSad
22148a475bcbSad if (ret == NULL) {
22158a475bcbSad malloc_mutex_unlock(&arena->mtx);
22168a475bcbSad return (NULL);
22178a475bcbSad }
22188a475bcbSad
22198a475bcbSad #ifdef MALLOC_STATS
22208a475bcbSad bin->stats.nrequests++;
22218a475bcbSad arena->stats.nmalloc_small++;
22228a475bcbSad arena->stats.allocated_small += size;
22238a475bcbSad #endif
22248a475bcbSad } else {
22258a475bcbSad /* Large allocation. */
22268a475bcbSad size = PAGE_CEILING(size);
22278a475bcbSad malloc_mutex_lock(&arena->mtx);
22288a475bcbSad ret = (void *)arena_run_alloc(arena, size);
22298a475bcbSad if (ret == NULL) {
22308a475bcbSad malloc_mutex_unlock(&arena->mtx);
22318a475bcbSad return (NULL);
22328a475bcbSad }
22338a475bcbSad #ifdef MALLOC_STATS
22348a475bcbSad arena->stats.nmalloc_large++;
22358a475bcbSad arena->stats.allocated_large += size;
22368a475bcbSad #endif
22378a475bcbSad }
22388a475bcbSad
22398a475bcbSad malloc_mutex_unlock(&arena->mtx);
22408a475bcbSad
2241bf5f78d5Sad if (OPT(junk))
22428a475bcbSad memset(ret, 0xa5, size);
2243bf5f78d5Sad else if (OPT(zero))
22448a475bcbSad memset(ret, 0, size);
22458a475bcbSad return (ret);
22468a475bcbSad }
22478a475bcbSad
22488a475bcbSad static inline void
arena_palloc_trim(arena_t * arena,arena_chunk_t * chunk,unsigned pageind,unsigned npages)22498a475bcbSad arena_palloc_trim(arena_t *arena, arena_chunk_t *chunk, unsigned pageind,
22508a475bcbSad unsigned npages)
22518a475bcbSad {
22528a475bcbSad unsigned i;
22538a475bcbSad
22548a475bcbSad assert(npages > 0);
22558a475bcbSad
22568a475bcbSad /*
22578a475bcbSad * Modifiy the map such that arena_run_dalloc() sees the run as
22588a475bcbSad * separately allocated.
22598a475bcbSad */
22608a475bcbSad for (i = 0; i < npages; i++) {
22618a475bcbSad chunk->map[pageind + i].npages = npages;
22628a475bcbSad chunk->map[pageind + i].pos = i;
22638a475bcbSad }
22648a475bcbSad arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)chunk + (pageind <<
22658a475bcbSad pagesize_2pow)), npages << pagesize_2pow);
22668a475bcbSad }
22678a475bcbSad
22688a475bcbSad /* Only handles large allocations that require more than page alignment. */
22698a475bcbSad static void *
arena_palloc(arena_t * arena,size_t alignment,size_t size,size_t alloc_size)22708a475bcbSad arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size)
22718a475bcbSad {
22728a475bcbSad void *ret;
22738a475bcbSad size_t offset;
22748a475bcbSad arena_chunk_t *chunk;
22758a475bcbSad unsigned pageind, i, npages;
22768a475bcbSad
22778a475bcbSad assert((size & pagesize_mask) == 0);
22788a475bcbSad assert((alignment & pagesize_mask) == 0);
22798a475bcbSad
22808de47c09Schristos npages = (unsigned)(size >> pagesize_2pow);
22818a475bcbSad
22828a475bcbSad malloc_mutex_lock(&arena->mtx);
22838a475bcbSad ret = (void *)arena_run_alloc(arena, alloc_size);
22848a475bcbSad if (ret == NULL) {
22858a475bcbSad malloc_mutex_unlock(&arena->mtx);
22868a475bcbSad return (NULL);
22878a475bcbSad }
22888a475bcbSad
22898a475bcbSad chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
22908a475bcbSad
22918a475bcbSad offset = (uintptr_t)ret & (alignment - 1);
22928a475bcbSad assert((offset & pagesize_mask) == 0);
22938a475bcbSad assert(offset < alloc_size);
22948a475bcbSad if (offset == 0) {
22958de47c09Schristos pageind = (unsigned)(((uintptr_t)ret - (uintptr_t)chunk) >>
22968a475bcbSad pagesize_2pow);
22978a475bcbSad
22988a475bcbSad /* Update the map for the run to be kept. */
22998a475bcbSad for (i = 0; i < npages; i++) {
23008a475bcbSad chunk->map[pageind + i].npages = npages;
23018a475bcbSad assert(chunk->map[pageind + i].pos == i);
23028a475bcbSad }
23038a475bcbSad
23048a475bcbSad /* Trim trailing space. */
23058a475bcbSad arena_palloc_trim(arena, chunk, pageind + npages,
23068de47c09Schristos (unsigned)((alloc_size - size) >> pagesize_2pow));
23078a475bcbSad } else {
23088a475bcbSad size_t leadsize, trailsize;
23098a475bcbSad
23108a475bcbSad leadsize = alignment - offset;
23118a475bcbSad ret = (void *)((uintptr_t)ret + leadsize);
23128de47c09Schristos pageind = (unsigned)(((uintptr_t)ret - (uintptr_t)chunk) >>
23138a475bcbSad pagesize_2pow);
23148a475bcbSad
23158a475bcbSad /* Update the map for the run to be kept. */
23168a475bcbSad for (i = 0; i < npages; i++) {
23178a475bcbSad chunk->map[pageind + i].npages = npages;
23188a475bcbSad chunk->map[pageind + i].pos = i;
23198a475bcbSad }
23208a475bcbSad
23218a475bcbSad /* Trim leading space. */
23228de47c09Schristos arena_palloc_trim(arena, chunk,
23238de47c09Schristos (unsigned)(pageind - (leadsize >> pagesize_2pow)),
23248de47c09Schristos (unsigned)(leadsize >> pagesize_2pow));
23258a475bcbSad
23268a475bcbSad trailsize = alloc_size - leadsize - size;
23278a475bcbSad if (trailsize != 0) {
23288a475bcbSad /* Trim trailing space. */
23298a475bcbSad assert(trailsize < alloc_size);
23308a475bcbSad arena_palloc_trim(arena, chunk, pageind + npages,
23318de47c09Schristos (unsigned)(trailsize >> pagesize_2pow));
23328a475bcbSad }
23338a475bcbSad }
23348a475bcbSad
23358a475bcbSad #ifdef MALLOC_STATS
23368a475bcbSad arena->stats.nmalloc_large++;
23378a475bcbSad arena->stats.allocated_large += size;
23388a475bcbSad #endif
23398a475bcbSad malloc_mutex_unlock(&arena->mtx);
23408a475bcbSad
2341bf5f78d5Sad if (OPT(junk))
23428a475bcbSad memset(ret, 0xa5, size);
2343bf5f78d5Sad else if (OPT(zero))
23448a475bcbSad memset(ret, 0, size);
23458a475bcbSad return (ret);
23468a475bcbSad }
23478a475bcbSad
23488a475bcbSad /* Return the size of the allocation pointed to by ptr. */
23498a475bcbSad static size_t
arena_salloc(const void * ptr)23508a475bcbSad arena_salloc(const void *ptr)
23518a475bcbSad {
23528a475bcbSad size_t ret;
23538a475bcbSad arena_chunk_t *chunk;
23548a475bcbSad arena_chunk_map_t *mapelm;
23558a475bcbSad unsigned pageind;
23568a475bcbSad
23578a475bcbSad assert(ptr != NULL);
23588a475bcbSad assert(CHUNK_ADDR2BASE(ptr) != ptr);
23598a475bcbSad
23608a475bcbSad /*
23618a475bcbSad * No arena data structures that we query here can change in a way that
23628a475bcbSad * affects this function, so we don't need to lock.
23638a475bcbSad */
23648a475bcbSad chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
23658de47c09Schristos pageind = (unsigned)(((uintptr_t)ptr - (uintptr_t)chunk) >>
23668de47c09Schristos pagesize_2pow);
23678a475bcbSad mapelm = &chunk->map[pageind];
236846de0853Ssimonb if (mapelm->pos != 0 || ptr != (char *)((uintptr_t)chunk) + (pageind <<
236946de0853Ssimonb pagesize_2pow)) {
237046de0853Ssimonb arena_run_t *run;
23718a475bcbSad
23728a475bcbSad pageind -= mapelm->pos;
23738a475bcbSad
237446de0853Ssimonb run = (arena_run_t *)((uintptr_t)chunk + (pageind <<
237546de0853Ssimonb pagesize_2pow));
23768a475bcbSad assert(run->magic == ARENA_RUN_MAGIC);
23778a475bcbSad ret = run->bin->reg_size;
23788a475bcbSad } else
23798a475bcbSad ret = mapelm->npages << pagesize_2pow;
23808a475bcbSad
23818a475bcbSad return (ret);
23828a475bcbSad }
23838a475bcbSad
23848a475bcbSad static void *
arena_ralloc(void * ptr,size_t size,size_t oldsize)23858a475bcbSad arena_ralloc(void *ptr, size_t size, size_t oldsize)
23868a475bcbSad {
23878a475bcbSad void *ret;
23888a475bcbSad
23898a475bcbSad /* Avoid moving the allocation if the size class would not change. */
23908a475bcbSad if (size < small_min) {
23918a475bcbSad if (oldsize < small_min &&
23928a475bcbSad ffs((int)(pow2_ceil(size) >> (TINY_MIN_2POW + 1)))
23938a475bcbSad == ffs((int)(pow2_ceil(oldsize) >> (TINY_MIN_2POW + 1))))
23948a475bcbSad goto IN_PLACE;
23958a475bcbSad } else if (size <= small_max) {
23968a475bcbSad if (oldsize >= small_min && oldsize <= small_max &&
23978a475bcbSad (QUANTUM_CEILING(size) >> opt_quantum_2pow)
23988a475bcbSad == (QUANTUM_CEILING(oldsize) >> opt_quantum_2pow))
23998a475bcbSad goto IN_PLACE;
24008a475bcbSad } else {
24018a475bcbSad /*
24028a475bcbSad * We make no attempt to resize runs here, though it would be
24038a475bcbSad * possible to do so.
24048a475bcbSad */
24058a475bcbSad if (oldsize > small_max && PAGE_CEILING(size) == oldsize)
24068a475bcbSad goto IN_PLACE;
24078a475bcbSad }
24088a475bcbSad
24098a475bcbSad /*
24108a475bcbSad * If we get here, then size and oldsize are different enough that we
24118a475bcbSad * need to use a different size class. In that case, fall back to
24128a475bcbSad * allocating new space and copying.
24138a475bcbSad */
24148a475bcbSad ret = arena_malloc(choose_arena(), size);
24158a475bcbSad if (ret == NULL)
24168a475bcbSad return (NULL);
24178a475bcbSad
24188a475bcbSad /* Junk/zero-filling were already done by arena_malloc(). */
24198a475bcbSad if (size < oldsize)
24208a475bcbSad memcpy(ret, ptr, size);
24218a475bcbSad else
24228a475bcbSad memcpy(ret, ptr, oldsize);
24238a475bcbSad idalloc(ptr);
24248a475bcbSad return (ret);
24258a475bcbSad IN_PLACE:
2426bf5f78d5Sad if (OPT(junk) && size < oldsize)
24278a475bcbSad memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size);
2428bf5f78d5Sad else if (OPT(zero) && size > oldsize)
24298a475bcbSad memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize);
24308a475bcbSad return (ptr);
24318a475bcbSad }
24328a475bcbSad
24338a475bcbSad static void
arena_dalloc(arena_t * arena,arena_chunk_t * chunk,void * ptr)24348a475bcbSad arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr)
24358a475bcbSad {
24368a475bcbSad unsigned pageind;
24378a475bcbSad arena_chunk_map_t *mapelm;
24388a475bcbSad size_t size;
24398a475bcbSad
24408a475bcbSad assert(arena != NULL);
24418a475bcbSad assert(arena->magic == ARENA_MAGIC);
24428a475bcbSad assert(chunk->arena == arena);
24438a475bcbSad assert(ptr != NULL);
24448a475bcbSad assert(CHUNK_ADDR2BASE(ptr) != ptr);
24458a475bcbSad
24468de47c09Schristos pageind = (unsigned)(((uintptr_t)ptr - (uintptr_t)chunk) >>
24478de47c09Schristos pagesize_2pow);
24488a475bcbSad mapelm = &chunk->map[pageind];
244946de0853Ssimonb if (mapelm->pos != 0 || ptr != (char *)((uintptr_t)chunk) + (pageind <<
245046de0853Ssimonb pagesize_2pow)) {
245146de0853Ssimonb arena_run_t *run;
24528a475bcbSad arena_bin_t *bin;
24538a475bcbSad
24548a475bcbSad /* Small allocation. */
24558a475bcbSad
24568a475bcbSad pageind -= mapelm->pos;
24578a475bcbSad
245846de0853Ssimonb run = (arena_run_t *)((uintptr_t)chunk + (pageind <<
245946de0853Ssimonb pagesize_2pow));
24608a475bcbSad assert(run->magic == ARENA_RUN_MAGIC);
24618a475bcbSad bin = run->bin;
24628a475bcbSad size = bin->reg_size;
24638a475bcbSad
2464bf5f78d5Sad if (OPT(junk))
24658a475bcbSad memset(ptr, 0x5a, size);
24668a475bcbSad
24678a475bcbSad malloc_mutex_lock(&arena->mtx);
24688a475bcbSad arena_run_reg_dalloc(run, bin, ptr, size);
24698a475bcbSad run->nfree++;
24708a475bcbSad
24718a475bcbSad if (run->nfree == bin->nregs) {
24728a475bcbSad /* Deallocate run. */
24738a475bcbSad if (run == bin->runcur)
24748a475bcbSad bin->runcur = NULL;
24758a475bcbSad else if (bin->nregs != 1) {
24768a475bcbSad /*
24778a475bcbSad * This block's conditional is necessary because
24788a475bcbSad * if the run only contains one region, then it
24798a475bcbSad * never gets inserted into the non-full runs
24808a475bcbSad * tree.
24818a475bcbSad */
2482562025fdSad rb_tree_remove_node(&bin->runs, run);
24838a475bcbSad }
24848a475bcbSad #ifdef MALLOC_DEBUG
24858a475bcbSad run->magic = 0;
24868a475bcbSad #endif
24878a475bcbSad arena_run_dalloc(arena, run, bin->run_size);
24888a475bcbSad #ifdef MALLOC_STATS
24898a475bcbSad bin->stats.curruns--;
24908a475bcbSad #endif
24918a475bcbSad } else if (run->nfree == 1 && run != bin->runcur) {
24928a475bcbSad /*
24938a475bcbSad * Make sure that bin->runcur always refers to the
24948a475bcbSad * lowest non-full run, if one exists.
24958a475bcbSad */
24968a475bcbSad if (bin->runcur == NULL)
24978a475bcbSad bin->runcur = run;
24988a475bcbSad else if ((uintptr_t)run < (uintptr_t)bin->runcur) {
24998a475bcbSad /* Switch runcur. */
25008a475bcbSad if (bin->runcur->nfree > 0) {
25018a475bcbSad /* Insert runcur. */
2502562025fdSad rb_tree_insert_node(&bin->runs, bin->runcur);
25038a475bcbSad }
25048a475bcbSad bin->runcur = run;
2505e7a9e46bSad } else {
2506562025fdSad rb_tree_insert_node(&bin->runs, run);
25078a475bcbSad }
2508e7a9e46bSad }
25098a475bcbSad #ifdef MALLOC_STATS
25108a475bcbSad arena->stats.allocated_small -= size;
25118a475bcbSad arena->stats.ndalloc_small++;
25128a475bcbSad #endif
25138a475bcbSad } else {
25148a475bcbSad /* Large allocation. */
25158a475bcbSad
25168a475bcbSad size = mapelm->npages << pagesize_2pow;
25178a475bcbSad assert((((uintptr_t)ptr) & pagesize_mask) == 0);
25188a475bcbSad
2519bf5f78d5Sad if (OPT(junk))
25208a475bcbSad memset(ptr, 0x5a, size);
25218a475bcbSad
25228a475bcbSad malloc_mutex_lock(&arena->mtx);
25238a475bcbSad arena_run_dalloc(arena, (arena_run_t *)ptr, size);
25248a475bcbSad #ifdef MALLOC_STATS
25258a475bcbSad arena->stats.allocated_large -= size;
25268a475bcbSad arena->stats.ndalloc_large++;
25278a475bcbSad #endif
25288a475bcbSad }
25298a475bcbSad
25308a475bcbSad malloc_mutex_unlock(&arena->mtx);
25318a475bcbSad }
25328a475bcbSad
2533bf5f78d5Sad static void
arena_new(arena_t * arena)25348a475bcbSad arena_new(arena_t *arena)
25358a475bcbSad {
25368a475bcbSad unsigned i;
25378a475bcbSad arena_bin_t *bin;
2538e7a9e46bSad size_t prev_run_size;
25398a475bcbSad
25408a475bcbSad malloc_mutex_init(&arena->mtx);
25418a475bcbSad
25428a475bcbSad #ifdef MALLOC_STATS
25438a475bcbSad memset(&arena->stats, 0, sizeof(arena_stats_t));
25448a475bcbSad #endif
25458a475bcbSad
25468a475bcbSad /* Initialize chunks. */
2547562025fdSad rb_tree_init(&arena->chunks, &arena_chunk_tree_ops);
25488a475bcbSad arena->spare = NULL;
25498a475bcbSad
25508a475bcbSad /* Initialize bins. */
25518a475bcbSad prev_run_size = pagesize;
25528a475bcbSad
25538a475bcbSad /* (2^n)-spaced tiny bins. */
25548a475bcbSad for (i = 0; i < ntbins; i++) {
25558a475bcbSad bin = &arena->bins[i];
25568a475bcbSad bin->runcur = NULL;
2557562025fdSad rb_tree_init(&bin->runs, &arena_run_tree_ops);
25588a475bcbSad
25598a475bcbSad bin->reg_size = (1 << (TINY_MIN_2POW + i));
25608a475bcbSad prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
25618a475bcbSad
25628a475bcbSad #ifdef MALLOC_STATS
25638a475bcbSad memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
25648a475bcbSad #endif
25658a475bcbSad }
25668a475bcbSad
25678a475bcbSad /* Quantum-spaced bins. */
25688a475bcbSad for (; i < ntbins + nqbins; i++) {
25698a475bcbSad bin = &arena->bins[i];
25708a475bcbSad bin->runcur = NULL;
2571562025fdSad rb_tree_init(&bin->runs, &arena_run_tree_ops);
25728a475bcbSad
25738a475bcbSad bin->reg_size = quantum * (i - ntbins + 1);
2574e7a9e46bSad /*
25758a475bcbSad pow2_size = pow2_ceil(quantum * (i - ntbins + 1));
2576e7a9e46bSad */
25778a475bcbSad prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
25788a475bcbSad
25798a475bcbSad #ifdef MALLOC_STATS
25808a475bcbSad memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
25818a475bcbSad #endif
25828a475bcbSad }
25838a475bcbSad
25848a475bcbSad /* (2^n)-spaced sub-page bins. */
25858a475bcbSad for (; i < ntbins + nqbins + nsbins; i++) {
25868a475bcbSad bin = &arena->bins[i];
25878a475bcbSad bin->runcur = NULL;
2588562025fdSad rb_tree_init(&bin->runs, &arena_run_tree_ops);
25898a475bcbSad
25908a475bcbSad bin->reg_size = (small_max << (i - (ntbins + nqbins) + 1));
25918a475bcbSad
25928a475bcbSad prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
25938a475bcbSad
25948a475bcbSad #ifdef MALLOC_STATS
25958a475bcbSad memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
25968a475bcbSad #endif
25978a475bcbSad }
25988a475bcbSad
25998a475bcbSad #ifdef MALLOC_DEBUG
26008a475bcbSad arena->magic = ARENA_MAGIC;
26018a475bcbSad #endif
26028a475bcbSad }
26038a475bcbSad
26048a475bcbSad /* Create a new arena and insert it into the arenas array at index ind. */
26058a475bcbSad static arena_t *
arenas_extend(void)2606bf5f78d5Sad arenas_extend(void)
26078a475bcbSad {
26088a475bcbSad arena_t *ret;
26098a475bcbSad
26108a475bcbSad /* Allocate enough space for trailing bins. */
26118a475bcbSad ret = (arena_t *)base_alloc(sizeof(arena_t)
26128a475bcbSad + (sizeof(arena_bin_t) * (ntbins + nqbins + nsbins - 1)));
2613bf5f78d5Sad if (ret != NULL) {
2614bf5f78d5Sad arena_new(ret);
26158a475bcbSad return (ret);
26168a475bcbSad }
26178a475bcbSad /* Only reached if there is an OOM error. */
26188a475bcbSad
26198a475bcbSad /*
26208a475bcbSad * OOM here is quite inconvenient to propagate, since dealing with it
26218a475bcbSad * would require a check for failure in the fast path. Instead, punt
26228a475bcbSad * by using arenas[0]. In practice, this is an extremely unlikely
26238a475bcbSad * failure.
26248a475bcbSad */
262586ef91b8Schristos _malloc_message(getprogname(),
26268a475bcbSad ": (malloc) Error initializing arena\n", "", "");
2627bf5f78d5Sad if (OPT(abort))
26288a475bcbSad abort();
26298a475bcbSad
26308a475bcbSad return (arenas[0]);
26318a475bcbSad }
26328a475bcbSad
26338a475bcbSad /*
26348a475bcbSad * End arena.
26358a475bcbSad */
26368a475bcbSad /******************************************************************************/
26378a475bcbSad /*
26388a475bcbSad * Begin general internal functions.
26398a475bcbSad */
26408a475bcbSad
26418a475bcbSad static void *
huge_malloc(size_t size)26428a475bcbSad huge_malloc(size_t size)
26438a475bcbSad {
26448a475bcbSad void *ret;
26458a475bcbSad size_t csize;
26468a475bcbSad chunk_node_t *node;
26478a475bcbSad
26488a475bcbSad /* Allocate one or more contiguous chunks for this request. */
26498a475bcbSad
26508a475bcbSad csize = CHUNK_CEILING(size);
26518a475bcbSad if (csize == 0) {
26528a475bcbSad /* size is large enough to cause size_t wrap-around. */
26538a475bcbSad return (NULL);
26548a475bcbSad }
26558a475bcbSad
26568a475bcbSad /* Allocate a chunk node with which to track the chunk. */
26578a475bcbSad node = base_chunk_node_alloc();
26588a475bcbSad if (node == NULL)
26598a475bcbSad return (NULL);
26608a475bcbSad
26618a475bcbSad ret = chunk_alloc(csize);
26628a475bcbSad if (ret == NULL) {
26638a475bcbSad base_chunk_node_dealloc(node);
26648a475bcbSad return (NULL);
26658a475bcbSad }
26668a475bcbSad
26678a475bcbSad /* Insert node into huge. */
26688a475bcbSad node->chunk = ret;
26698a475bcbSad node->size = csize;
26708a475bcbSad
26718a475bcbSad malloc_mutex_lock(&chunks_mtx);
2672562025fdSad rb_tree_insert_node(&huge, node);
26738a475bcbSad #ifdef MALLOC_STATS
26748a475bcbSad huge_nmalloc++;
26758a475bcbSad huge_allocated += csize;
26768a475bcbSad #endif
26778a475bcbSad malloc_mutex_unlock(&chunks_mtx);
26788a475bcbSad
2679bf5f78d5Sad if (OPT(junk))
26808a475bcbSad memset(ret, 0xa5, csize);
2681bf5f78d5Sad else if (OPT(zero))
26828a475bcbSad memset(ret, 0, csize);
26838a475bcbSad
26848a475bcbSad return (ret);
26858a475bcbSad }
26868a475bcbSad
26878a475bcbSad /* Only handles large allocations that require more than chunk alignment. */
26888a475bcbSad static void *
huge_palloc(size_t alignment,size_t size)26898a475bcbSad huge_palloc(size_t alignment, size_t size)
26908a475bcbSad {
26918a475bcbSad void *ret;
26928a475bcbSad size_t alloc_size, chunk_size, offset;
26938a475bcbSad chunk_node_t *node;
26948a475bcbSad
26958a475bcbSad /*
26968a475bcbSad * This allocation requires alignment that is even larger than chunk
26978a475bcbSad * alignment. This means that huge_malloc() isn't good enough.
26988a475bcbSad *
26998a475bcbSad * Allocate almost twice as many chunks as are demanded by the size or
27008a475bcbSad * alignment, in order to assure the alignment can be achieved, then
27018a475bcbSad * unmap leading and trailing chunks.
27028a475bcbSad */
27038a475bcbSad assert(alignment >= chunksize);
27048a475bcbSad
27058a475bcbSad chunk_size = CHUNK_CEILING(size);
27068a475bcbSad
27078a475bcbSad if (size >= alignment)
27088a475bcbSad alloc_size = chunk_size + alignment - chunksize;
27098a475bcbSad else
27108a475bcbSad alloc_size = (alignment << 1) - chunksize;
27118a475bcbSad
27128a475bcbSad /* Allocate a chunk node with which to track the chunk. */
27138a475bcbSad node = base_chunk_node_alloc();
27148a475bcbSad if (node == NULL)
27158a475bcbSad return (NULL);
27168a475bcbSad
27178a475bcbSad ret = chunk_alloc(alloc_size);
27188a475bcbSad if (ret == NULL) {
27198a475bcbSad base_chunk_node_dealloc(node);
27208a475bcbSad return (NULL);
27218a475bcbSad }
27228a475bcbSad
27238a475bcbSad offset = (uintptr_t)ret & (alignment - 1);
27248a475bcbSad assert((offset & chunksize_mask) == 0);
27258a475bcbSad assert(offset < alloc_size);
27268a475bcbSad if (offset == 0) {
27278a475bcbSad /* Trim trailing space. */
27288a475bcbSad chunk_dealloc((void *)((uintptr_t)ret + chunk_size), alloc_size
27298a475bcbSad - chunk_size);
27308a475bcbSad } else {
27318a475bcbSad size_t trailsize;
27328a475bcbSad
27338a475bcbSad /* Trim leading space. */
27348a475bcbSad chunk_dealloc(ret, alignment - offset);
27358a475bcbSad
27368a475bcbSad ret = (void *)((uintptr_t)ret + (alignment - offset));
27378a475bcbSad
27388a475bcbSad trailsize = alloc_size - (alignment - offset) - chunk_size;
27398a475bcbSad if (trailsize != 0) {
27408a475bcbSad /* Trim trailing space. */
27418a475bcbSad assert(trailsize < alloc_size);
27428a475bcbSad chunk_dealloc((void *)((uintptr_t)ret + chunk_size),
27438a475bcbSad trailsize);
27448a475bcbSad }
27458a475bcbSad }
27468a475bcbSad
27478a475bcbSad /* Insert node into huge. */
27488a475bcbSad node->chunk = ret;
27498a475bcbSad node->size = chunk_size;
27508a475bcbSad
27518a475bcbSad malloc_mutex_lock(&chunks_mtx);
2752562025fdSad rb_tree_insert_node(&huge, node);
27538a475bcbSad #ifdef MALLOC_STATS
27548a475bcbSad huge_nmalloc++;
27558a475bcbSad huge_allocated += chunk_size;
27568a475bcbSad #endif
27578a475bcbSad malloc_mutex_unlock(&chunks_mtx);
27588a475bcbSad
2759bf5f78d5Sad if (OPT(junk))
27608a475bcbSad memset(ret, 0xa5, chunk_size);
2761bf5f78d5Sad else if (OPT(zero))
27628a475bcbSad memset(ret, 0, chunk_size);
27638a475bcbSad
27648a475bcbSad return (ret);
27658a475bcbSad }
27668a475bcbSad
27678a475bcbSad static void *
huge_ralloc(void * ptr,size_t size,size_t oldsize)27688a475bcbSad huge_ralloc(void *ptr, size_t size, size_t oldsize)
27698a475bcbSad {
27708a475bcbSad void *ret;
27718a475bcbSad
27728a475bcbSad /* Avoid moving the allocation if the size class would not change. */
27738a475bcbSad if (oldsize > arena_maxclass &&
27748a475bcbSad CHUNK_CEILING(size) == CHUNK_CEILING(oldsize)) {
2775bf5f78d5Sad if (OPT(junk) && size < oldsize) {
27768a475bcbSad memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize
27778a475bcbSad - size);
2778bf5f78d5Sad } else if (OPT(zero) && size > oldsize) {
27798a475bcbSad memset((void *)((uintptr_t)ptr + oldsize), 0, size
27808a475bcbSad - oldsize);
27818a475bcbSad }
27828a475bcbSad return (ptr);
27838a475bcbSad }
27848a475bcbSad
2785b79fded2Syamt if (CHUNK_ADDR2BASE(ptr) == ptr
2786b79fded2Syamt #ifdef USE_BRK
2787b79fded2Syamt && ((uintptr_t)ptr < (uintptr_t)brk_base
2788b79fded2Syamt || (uintptr_t)ptr >= (uintptr_t)brk_max)
2789b79fded2Syamt #endif
2790b79fded2Syamt ) {
2791b79fded2Syamt chunk_node_t *node, key;
2792b79fded2Syamt void *newptr;
2793b79fded2Syamt size_t oldcsize;
2794b79fded2Syamt size_t newcsize;
2795b79fded2Syamt
2796b79fded2Syamt newcsize = CHUNK_CEILING(size);
2797b79fded2Syamt oldcsize = CHUNK_CEILING(oldsize);
2798b79fded2Syamt assert(oldcsize != newcsize);
2799b79fded2Syamt if (newcsize == 0) {
2800b79fded2Syamt /* size_t wrap-around */
2801b79fded2Syamt return (NULL);
2802b79fded2Syamt }
2803b79fded2Syamt
28043df6d336Senami /*
28053df6d336Senami * Remove the old region from the tree now. If mremap()
28063df6d336Senami * returns the region to the system, other thread may
28073df6d336Senami * map it for same huge allocation and insert it to the
28083df6d336Senami * tree before we acquire the mutex lock again.
28093df6d336Senami */
2810b79fded2Syamt malloc_mutex_lock(&chunks_mtx);
2811bf5f78d5Sad key.chunk = __UNCONST(ptr);
2812562025fdSad node = rb_tree_find_node(&huge, &key);
2813b79fded2Syamt assert(node != NULL);
2814b79fded2Syamt assert(node->chunk == ptr);
2815b79fded2Syamt assert(node->size == oldcsize);
2816562025fdSad rb_tree_remove_node(&huge, node);
28173df6d336Senami malloc_mutex_unlock(&chunks_mtx);
28183df6d336Senami
28193df6d336Senami newptr = mremap(ptr, oldcsize, NULL, newcsize,
28203df6d336Senami MAP_ALIGNED(chunksize_2pow));
28213df6d336Senami if (newptr == MAP_FAILED) {
28223df6d336Senami /* We still own the old region. */
28233df6d336Senami malloc_mutex_lock(&chunks_mtx);
2824562025fdSad rb_tree_insert_node(&huge, node);
28253df6d336Senami malloc_mutex_unlock(&chunks_mtx);
28263df6d336Senami } else {
28273df6d336Senami assert(CHUNK_ADDR2BASE(newptr) == newptr);
28283df6d336Senami
28293df6d336Senami /* Insert new or resized old region. */
28303df6d336Senami malloc_mutex_lock(&chunks_mtx);
28313df6d336Senami node->size = newcsize;
2832b79fded2Syamt node->chunk = newptr;
2833562025fdSad rb_tree_insert_node(&huge, node);
2834b79fded2Syamt #ifdef MALLOC_STATS
2835b79fded2Syamt huge_nralloc++;
2836b79fded2Syamt huge_allocated += newcsize - oldcsize;
2837b79fded2Syamt if (newcsize > oldcsize) {
2838b79fded2Syamt stats_chunks.curchunks +=
2839b79fded2Syamt (newcsize - oldcsize) / chunksize;
2840b79fded2Syamt if (stats_chunks.curchunks >
2841b79fded2Syamt stats_chunks.highchunks)
2842b79fded2Syamt stats_chunks.highchunks =
2843b79fded2Syamt stats_chunks.curchunks;
2844b79fded2Syamt } else {
2845b79fded2Syamt stats_chunks.curchunks -=
2846b79fded2Syamt (oldcsize - newcsize) / chunksize;
2847b79fded2Syamt }
2848b79fded2Syamt #endif
2849b79fded2Syamt malloc_mutex_unlock(&chunks_mtx);
2850b79fded2Syamt
2851bf5f78d5Sad if (OPT(junk) && size < oldsize) {
2852b79fded2Syamt memset((void *)((uintptr_t)newptr + size), 0x5a,
2853b79fded2Syamt newcsize - size);
2854bf5f78d5Sad } else if (OPT(zero) && size > oldsize) {
2855b79fded2Syamt memset((void *)((uintptr_t)newptr + oldsize), 0,
2856b79fded2Syamt size - oldsize);
2857b79fded2Syamt }
2858b79fded2Syamt return (newptr);
2859b79fded2Syamt }
2860b79fded2Syamt }
2861b79fded2Syamt
28628a475bcbSad /*
28638a475bcbSad * If we get here, then size and oldsize are different enough that we
28648a475bcbSad * need to use a different size class. In that case, fall back to
28658a475bcbSad * allocating new space and copying.
28668a475bcbSad */
28678a475bcbSad ret = huge_malloc(size);
28688a475bcbSad if (ret == NULL)
28698a475bcbSad return (NULL);
28708a475bcbSad
28718a475bcbSad if (CHUNK_ADDR2BASE(ptr) == ptr) {
28728a475bcbSad /* The old allocation is a chunk. */
28738a475bcbSad if (size < oldsize)
28748a475bcbSad memcpy(ret, ptr, size);
28758a475bcbSad else
28768a475bcbSad memcpy(ret, ptr, oldsize);
28778a475bcbSad } else {
28788a475bcbSad /* The old allocation is a region. */
28798a475bcbSad assert(oldsize < size);
28808a475bcbSad memcpy(ret, ptr, oldsize);
28818a475bcbSad }
28828a475bcbSad idalloc(ptr);
28838a475bcbSad return (ret);
28848a475bcbSad }
28858a475bcbSad
28868a475bcbSad static void
huge_dalloc(void * ptr)28878a475bcbSad huge_dalloc(void *ptr)
28888a475bcbSad {
28898a475bcbSad chunk_node_t key;
28908a475bcbSad chunk_node_t *node;
28918a475bcbSad
28928a475bcbSad malloc_mutex_lock(&chunks_mtx);
28938a475bcbSad
28948a475bcbSad /* Extract from tree of huge allocations. */
28958a475bcbSad key.chunk = ptr;
2896562025fdSad node = rb_tree_find_node(&huge, &key);
28978a475bcbSad assert(node != NULL);
28988a475bcbSad assert(node->chunk == ptr);
2899562025fdSad rb_tree_remove_node(&huge, node);
29008a475bcbSad
29018a475bcbSad #ifdef MALLOC_STATS
29028a475bcbSad huge_ndalloc++;
29038a475bcbSad huge_allocated -= node->size;
29048a475bcbSad #endif
29058a475bcbSad
29068a475bcbSad malloc_mutex_unlock(&chunks_mtx);
29078a475bcbSad
29088a475bcbSad /* Unmap chunk. */
29098a475bcbSad #ifdef USE_BRK
2910bf5f78d5Sad if (OPT(junk))
29118a475bcbSad memset(node->chunk, 0x5a, node->size);
29128a475bcbSad #endif
29138a475bcbSad chunk_dealloc(node->chunk, node->size);
29148a475bcbSad
29158a475bcbSad base_chunk_node_dealloc(node);
29168a475bcbSad }
29178a475bcbSad
29188a475bcbSad static void *
imalloc(size_t size)29198a475bcbSad imalloc(size_t size)
29208a475bcbSad {
29218a475bcbSad void *ret;
29228a475bcbSad
29238a475bcbSad assert(size != 0);
29248a475bcbSad
29258a475bcbSad if (size <= arena_maxclass)
29268a475bcbSad ret = arena_malloc(choose_arena(), size);
29278a475bcbSad else
29288a475bcbSad ret = huge_malloc(size);
29298a475bcbSad
29308a475bcbSad return (ret);
29318a475bcbSad }
29328a475bcbSad
29338a475bcbSad static void *
ipalloc(size_t alignment,size_t size)29348a475bcbSad ipalloc(size_t alignment, size_t size)
29358a475bcbSad {
29368a475bcbSad void *ret;
29378a475bcbSad size_t ceil_size;
29388a475bcbSad
29398a475bcbSad /*
29408a475bcbSad * Round size up to the nearest multiple of alignment.
29418a475bcbSad *
29428a475bcbSad * This done, we can take advantage of the fact that for each small
29438a475bcbSad * size class, every object is aligned at the smallest power of two
29448a475bcbSad * that is non-zero in the base two representation of the size. For
29458a475bcbSad * example:
29468a475bcbSad *
29478a475bcbSad * Size | Base 2 | Minimum alignment
29488a475bcbSad * -----+----------+------------------
29498a475bcbSad * 96 | 1100000 | 32
29508a475bcbSad * 144 | 10100000 | 32
29518a475bcbSad * 192 | 11000000 | 64
29528a475bcbSad *
29538a475bcbSad * Depending on runtime settings, it is possible that arena_malloc()
29548a475bcbSad * will further round up to a power of two, but that never causes
29558a475bcbSad * correctness issues.
29568a475bcbSad */
29578a475bcbSad ceil_size = (size + (alignment - 1)) & (-alignment);
29588a475bcbSad /*
29598a475bcbSad * (ceil_size < size) protects against the combination of maximal
29608a475bcbSad * alignment and size greater than maximal alignment.
29618a475bcbSad */
29628a475bcbSad if (ceil_size < size) {
29638a475bcbSad /* size_t overflow. */
29648a475bcbSad return (NULL);
29658a475bcbSad }
29668a475bcbSad
29678a475bcbSad if (ceil_size <= pagesize || (alignment <= pagesize
29688a475bcbSad && ceil_size <= arena_maxclass))
29698a475bcbSad ret = arena_malloc(choose_arena(), ceil_size);
29708a475bcbSad else {
29718a475bcbSad size_t run_size;
29728a475bcbSad
29738a475bcbSad /*
29748a475bcbSad * We can't achieve sub-page alignment, so round up alignment
29758a475bcbSad * permanently; it makes later calculations simpler.
29768a475bcbSad */
29778a475bcbSad alignment = PAGE_CEILING(alignment);
29788a475bcbSad ceil_size = PAGE_CEILING(size);
29798a475bcbSad /*
29808a475bcbSad * (ceil_size < size) protects against very large sizes within
29818a475bcbSad * pagesize of SIZE_T_MAX.
29828a475bcbSad *
29838a475bcbSad * (ceil_size + alignment < ceil_size) protects against the
29848a475bcbSad * combination of maximal alignment and ceil_size large enough
29858a475bcbSad * to cause overflow. This is similar to the first overflow
29868a475bcbSad * check above, but it needs to be repeated due to the new
29878a475bcbSad * ceil_size value, which may now be *equal* to maximal
29888a475bcbSad * alignment, whereas before we only detected overflow if the
29898a475bcbSad * original size was *greater* than maximal alignment.
29908a475bcbSad */
29918a475bcbSad if (ceil_size < size || ceil_size + alignment < ceil_size) {
29928a475bcbSad /* size_t overflow. */
29938a475bcbSad return (NULL);
29948a475bcbSad }
29958a475bcbSad
29968a475bcbSad /*
29978a475bcbSad * Calculate the size of the over-size run that arena_palloc()
29988a475bcbSad * would need to allocate in order to guarantee the alignment.
29998a475bcbSad */
30008a475bcbSad if (ceil_size >= alignment)
30018a475bcbSad run_size = ceil_size + alignment - pagesize;
30028a475bcbSad else {
30038a475bcbSad /*
30048a475bcbSad * It is possible that (alignment << 1) will cause
30058a475bcbSad * overflow, but it doesn't matter because we also
30068a475bcbSad * subtract pagesize, which in the case of overflow
30078a475bcbSad * leaves us with a very large run_size. That causes
30088a475bcbSad * the first conditional below to fail, which means
30098a475bcbSad * that the bogus run_size value never gets used for
30108a475bcbSad * anything important.
30118a475bcbSad */
30128a475bcbSad run_size = (alignment << 1) - pagesize;
30138a475bcbSad }
30148a475bcbSad
30158a475bcbSad if (run_size <= arena_maxclass) {
30168a475bcbSad ret = arena_palloc(choose_arena(), alignment, ceil_size,
30178a475bcbSad run_size);
30188a475bcbSad } else if (alignment <= chunksize)
30198a475bcbSad ret = huge_malloc(ceil_size);
30208a475bcbSad else
30218a475bcbSad ret = huge_palloc(alignment, ceil_size);
30228a475bcbSad }
30238a475bcbSad
30248a475bcbSad assert(((uintptr_t)ret & (alignment - 1)) == 0);
30258a475bcbSad return (ret);
30268a475bcbSad }
30278a475bcbSad
30288a475bcbSad static void *
icalloc(size_t size)30298a475bcbSad icalloc(size_t size)
30308a475bcbSad {
30318a475bcbSad void *ret;
30328a475bcbSad
30338a475bcbSad if (size <= arena_maxclass) {
30348a475bcbSad ret = arena_malloc(choose_arena(), size);
30358a475bcbSad if (ret == NULL)
30368a475bcbSad return (NULL);
30378a475bcbSad memset(ret, 0, size);
30388a475bcbSad } else {
30398a475bcbSad /*
30408a475bcbSad * The virtual memory system provides zero-filled pages, so
30418a475bcbSad * there is no need to do so manually, unless opt_junk is
30428a475bcbSad * enabled, in which case huge_malloc() fills huge allocations
30438a475bcbSad * with junk.
30448a475bcbSad */
30458a475bcbSad ret = huge_malloc(size);
30468a475bcbSad if (ret == NULL)
30478a475bcbSad return (NULL);
30488a475bcbSad
3049bf5f78d5Sad if (OPT(junk))
30508a475bcbSad memset(ret, 0, size);
30518a475bcbSad #ifdef USE_BRK
30528a475bcbSad else if ((uintptr_t)ret >= (uintptr_t)brk_base
30538a475bcbSad && (uintptr_t)ret < (uintptr_t)brk_max) {
30548a475bcbSad /*
30558a475bcbSad * This may be a re-used brk chunk. Therefore, zero
30568a475bcbSad * the memory.
30578a475bcbSad */
30588a475bcbSad memset(ret, 0, size);
30598a475bcbSad }
30608a475bcbSad #endif
30618a475bcbSad }
30628a475bcbSad
30638a475bcbSad return (ret);
30648a475bcbSad }
30658a475bcbSad
30668a475bcbSad static size_t
isalloc(const void * ptr)30678a475bcbSad isalloc(const void *ptr)
30688a475bcbSad {
30698a475bcbSad size_t ret;
30708a475bcbSad arena_chunk_t *chunk;
30718a475bcbSad
30728a475bcbSad assert(ptr != NULL);
30738a475bcbSad
30748a475bcbSad chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
30758a475bcbSad if (chunk != ptr) {
30768a475bcbSad /* Region. */
30778a475bcbSad assert(chunk->arena->magic == ARENA_MAGIC);
30788a475bcbSad
30798a475bcbSad ret = arena_salloc(ptr);
30808a475bcbSad } else {
30818a475bcbSad chunk_node_t *node, key;
30828a475bcbSad
30838a475bcbSad /* Chunk (huge allocation). */
30848a475bcbSad
30858a475bcbSad malloc_mutex_lock(&chunks_mtx);
30868a475bcbSad
30878a475bcbSad /* Extract from tree of huge allocations. */
3088bf5f78d5Sad key.chunk = __UNCONST(ptr);
3089562025fdSad node = rb_tree_find_node(&huge, &key);
30908a475bcbSad assert(node != NULL);
30918a475bcbSad
30928a475bcbSad ret = node->size;
30938a475bcbSad
30948a475bcbSad malloc_mutex_unlock(&chunks_mtx);
30958a475bcbSad }
30968a475bcbSad
30978a475bcbSad return (ret);
30988a475bcbSad }
30998a475bcbSad
31008a475bcbSad static void *
iralloc(void * ptr,size_t size)31018a475bcbSad iralloc(void *ptr, size_t size)
31028a475bcbSad {
31038a475bcbSad void *ret;
31048a475bcbSad size_t oldsize;
31058a475bcbSad
31068a475bcbSad assert(ptr != NULL);
31078a475bcbSad assert(size != 0);
31088a475bcbSad
31098a475bcbSad oldsize = isalloc(ptr);
31108a475bcbSad
31118a475bcbSad if (size <= arena_maxclass)
31128a475bcbSad ret = arena_ralloc(ptr, size, oldsize);
31138a475bcbSad else
31148a475bcbSad ret = huge_ralloc(ptr, size, oldsize);
31158a475bcbSad
31168a475bcbSad return (ret);
31178a475bcbSad }
31188a475bcbSad
31198a475bcbSad static void
idalloc(void * ptr)31208a475bcbSad idalloc(void *ptr)
31218a475bcbSad {
31228a475bcbSad arena_chunk_t *chunk;
31238a475bcbSad
31248a475bcbSad assert(ptr != NULL);
31258a475bcbSad
31268a475bcbSad chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
31278a475bcbSad if (chunk != ptr) {
31288a475bcbSad /* Region. */
31298a475bcbSad arena_dalloc(chunk->arena, chunk, ptr);
31308a475bcbSad } else
31318a475bcbSad huge_dalloc(ptr);
31328a475bcbSad }
31338a475bcbSad
31348a475bcbSad static void
malloc_print_stats(void)31358a475bcbSad malloc_print_stats(void)
31368a475bcbSad {
31378a475bcbSad
3138bf5f78d5Sad if (OPT(print_stats)) {
31398a475bcbSad char s[UMAX2S_BUFSIZE];
31408a475bcbSad _malloc_message("___ Begin malloc statistics ___\n", "", "",
31418a475bcbSad "");
31428a475bcbSad _malloc_message("Assertions ",
31438a475bcbSad #ifdef NDEBUG
31448a475bcbSad "disabled",
31458a475bcbSad #else
31468a475bcbSad "enabled",
31478a475bcbSad #endif
31488a475bcbSad "\n", "");
31498a475bcbSad _malloc_message("Boolean MALLOC_OPTIONS: ",
31508a475bcbSad opt_abort ? "A" : "a",
31518a475bcbSad opt_junk ? "J" : "j",
31528a475bcbSad opt_hint ? "H" : "h");
3153bf5f78d5Sad _malloc_message(OPT(utrace) ? "PU" : "Pu",
31548a475bcbSad opt_sysv ? "V" : "v",
31558a475bcbSad opt_xmalloc ? "X" : "x",
31568a475bcbSad opt_zero ? "Z\n" : "z\n");
31578a475bcbSad
3158d703a148Schristos _malloc_message("CPUs: ", size_t2s(ncpus, s), "\n", "");
3159d703a148Schristos _malloc_message("Pointer size: ", size_t2s(sizeof(void *), s),
31608a475bcbSad "\n", "");
3161d703a148Schristos _malloc_message("Quantum size: ", size_t2s(quantum, s), "\n", "");
3162d703a148Schristos _malloc_message("Max small size: ", size_t2s(small_max, s), "\n",
31638a475bcbSad "");
31648a475bcbSad
3165d703a148Schristos _malloc_message("Chunk size: ", size_t2s(chunksize, s), "", "");
3166d703a148Schristos _malloc_message(" (2^", size_t2s((size_t)opt_chunk_2pow, s),
3167e1a2f47fSmatt ")\n", "");
31688a475bcbSad
31698a475bcbSad #ifdef MALLOC_STATS
31708a475bcbSad {
31718a475bcbSad size_t allocated, mapped;
31728a475bcbSad unsigned i;
31738a475bcbSad arena_t *arena;
31748a475bcbSad
31758a475bcbSad /* Calculate and print allocated/mapped stats. */
31768a475bcbSad
31778a475bcbSad /* arenas. */
3178bf5f78d5Sad for (i = 0, allocated = 0; i < ncpus; i++) {
31798a475bcbSad if (arenas[i] != NULL) {
31808a475bcbSad malloc_mutex_lock(&arenas[i]->mtx);
31818a475bcbSad allocated +=
31828a475bcbSad arenas[i]->stats.allocated_small;
31838a475bcbSad allocated +=
31848a475bcbSad arenas[i]->stats.allocated_large;
31858a475bcbSad malloc_mutex_unlock(&arenas[i]->mtx);
31868a475bcbSad }
31878a475bcbSad }
31888a475bcbSad
31898a475bcbSad /* huge/base. */
31908a475bcbSad malloc_mutex_lock(&chunks_mtx);
31918a475bcbSad allocated += huge_allocated;
31928a475bcbSad mapped = stats_chunks.curchunks * chunksize;
31938a475bcbSad malloc_mutex_unlock(&chunks_mtx);
31948a475bcbSad
31958a475bcbSad malloc_mutex_lock(&base_mtx);
31968a475bcbSad mapped += base_mapped;
31978a475bcbSad malloc_mutex_unlock(&base_mtx);
31988a475bcbSad
31998a475bcbSad malloc_printf("Allocated: %zu, mapped: %zu\n",
32008a475bcbSad allocated, mapped);
32018a475bcbSad
32028a475bcbSad /* Print chunk stats. */
32038a475bcbSad {
32048a475bcbSad chunk_stats_t chunks_stats;
32058a475bcbSad
32068a475bcbSad malloc_mutex_lock(&chunks_mtx);
32078a475bcbSad chunks_stats = stats_chunks;
32088a475bcbSad malloc_mutex_unlock(&chunks_mtx);
32098a475bcbSad
32108a475bcbSad malloc_printf("chunks: nchunks "
32118a475bcbSad "highchunks curchunks\n");
32128a475bcbSad malloc_printf(" %13llu%13lu%13lu\n",
32138a475bcbSad chunks_stats.nchunks,
32148a475bcbSad chunks_stats.highchunks,
32158a475bcbSad chunks_stats.curchunks);
32168a475bcbSad }
32178a475bcbSad
32188a475bcbSad /* Print chunk stats. */
32198a475bcbSad malloc_printf(
3220b79fded2Syamt "huge: nmalloc ndalloc "
3221b79fded2Syamt "nralloc allocated\n");
3222b79fded2Syamt malloc_printf(" %12llu %12llu %12llu %12zu\n",
3223b79fded2Syamt huge_nmalloc, huge_ndalloc, huge_nralloc,
3224b79fded2Syamt huge_allocated);
32258a475bcbSad
32268a475bcbSad /* Print stats for each arena. */
3227bf5f78d5Sad for (i = 0; i < ncpus; i++) {
32288a475bcbSad arena = arenas[i];
32298a475bcbSad if (arena != NULL) {
32308a475bcbSad malloc_printf(
3231e7a9e46bSad "\narenas[%u] @ %p\n", i, arena);
32328a475bcbSad malloc_mutex_lock(&arena->mtx);
32338a475bcbSad stats_print(arena);
32348a475bcbSad malloc_mutex_unlock(&arena->mtx);
32358a475bcbSad }
32368a475bcbSad }
32378a475bcbSad }
32388a475bcbSad #endif /* #ifdef MALLOC_STATS */
32398a475bcbSad _malloc_message("--- End malloc statistics ---\n", "", "", "");
32408a475bcbSad }
32418a475bcbSad }
32428a475bcbSad
32438a475bcbSad /*
3244bf5f78d5Sad * libpthread might call malloc(3), so the malloc implementation has to take
3245bf5f78d5Sad * pains to avoid infinite recursion during initialization.
32468a475bcbSad */
32478a475bcbSad static inline bool
malloc_init(void)32488a475bcbSad malloc_init(void)
32498a475bcbSad {
32508a475bcbSad
3251bf5f78d5Sad if (__predict_false(malloc_initialized == false))
32528a475bcbSad return (malloc_init_hard());
32538a475bcbSad
32548a475bcbSad return (false);
32558a475bcbSad }
32568a475bcbSad
32578a475bcbSad static bool
malloc_init_hard(void)32588a475bcbSad malloc_init_hard(void)
32598a475bcbSad {
32608a475bcbSad unsigned i, j;
32618de47c09Schristos ssize_t linklen;
32628a475bcbSad char buf[PATH_MAX + 1];
3263e7a9e46bSad const char *opts = "";
32641ff5a5dfSchristos int serrno;
32658a475bcbSad
32668a475bcbSad malloc_mutex_lock(&init_lock);
32678a475bcbSad if (malloc_initialized) {
32688a475bcbSad /*
32698a475bcbSad * Another thread initialized the allocator before this one
32708a475bcbSad * acquired init_lock.
32718a475bcbSad */
32728a475bcbSad malloc_mutex_unlock(&init_lock);
32738a475bcbSad return (false);
32748a475bcbSad }
32758a475bcbSad
3276713ea189Schristos serrno = errno;
32778a475bcbSad /* Get number of CPUs. */
32788a475bcbSad {
32798a475bcbSad int mib[2];
32808a475bcbSad size_t len;
32818a475bcbSad
32828a475bcbSad mib[0] = CTL_HW;
32838a475bcbSad mib[1] = HW_NCPU;
32848a475bcbSad len = sizeof(ncpus);
32858a475bcbSad if (sysctl(mib, 2, &ncpus, &len, (void *) 0, 0) == -1) {
32868a475bcbSad /* Error. */
32878a475bcbSad ncpus = 1;
32888a475bcbSad }
32898a475bcbSad }
32908a475bcbSad
32918a475bcbSad /* Get page size. */
32928a475bcbSad {
32938a475bcbSad long result;
32948a475bcbSad
3295bf5f78d5Sad result = getpagesize();
32968a475bcbSad assert(result != -1);
32978a475bcbSad pagesize = (unsigned) result;
32988a475bcbSad
32998a475bcbSad /*
33008a475bcbSad * We assume that pagesize is a power of 2 when calculating
33018a475bcbSad * pagesize_mask and pagesize_2pow.
33028a475bcbSad */
33038a475bcbSad assert(((result - 1) & result) == 0);
33048a475bcbSad pagesize_mask = result - 1;
33058a475bcbSad pagesize_2pow = ffs((int)result) - 1;
33068a475bcbSad }
33078a475bcbSad
33088a475bcbSad for (i = 0; i < 3; i++) {
33098a475bcbSad /* Get runtime configuration. */
33108a475bcbSad switch (i) {
33118a475bcbSad case 0:
33128a475bcbSad if ((linklen = readlink("/etc/malloc.conf", buf,
33138a475bcbSad sizeof(buf) - 1)) != -1) {
33148a475bcbSad /*
33158a475bcbSad * Use the contents of the "/etc/malloc.conf"
33168a475bcbSad * symbolic link's name.
33178a475bcbSad */
33188a475bcbSad buf[linklen] = '\0';
33198a475bcbSad opts = buf;
33208a475bcbSad } else {
33218a475bcbSad /* No configuration specified. */
33228a475bcbSad buf[0] = '\0';
33238a475bcbSad opts = buf;
33248a475bcbSad }
33258a475bcbSad break;
33268a475bcbSad case 1:
332788261d4eSad if ((opts = getenv("MALLOC_OPTIONS")) != NULL &&
332888261d4eSad issetugid() == 0) {
33298a475bcbSad /*
33308a475bcbSad * Do nothing; opts is already initialized to
33318a475bcbSad * the value of the MALLOC_OPTIONS environment
33328a475bcbSad * variable.
33338a475bcbSad */
33348a475bcbSad } else {
33358a475bcbSad /* No configuration specified. */
33368a475bcbSad buf[0] = '\0';
33378a475bcbSad opts = buf;
33388a475bcbSad }
33398a475bcbSad break;
33408a475bcbSad case 2:
33418a475bcbSad if (_malloc_options != NULL) {
33428a475bcbSad /*
33438a475bcbSad * Use options that were compiled into the program.
33448a475bcbSad */
33458a475bcbSad opts = _malloc_options;
33468a475bcbSad } else {
33478a475bcbSad /* No configuration specified. */
33488a475bcbSad buf[0] = '\0';
33498a475bcbSad opts = buf;
33508a475bcbSad }
33518a475bcbSad break;
33528a475bcbSad default:
33538a475bcbSad /* NOTREACHED */
3354687cd24eSyamt /* LINTED */
33558a475bcbSad assert(false);
33568a475bcbSad }
33578a475bcbSad
33588a475bcbSad for (j = 0; opts[j] != '\0'; j++) {
33598a475bcbSad switch (opts[j]) {
33608a475bcbSad case 'a':
33618a475bcbSad opt_abort = false;
33628a475bcbSad break;
33638a475bcbSad case 'A':
33648a475bcbSad opt_abort = true;
33658a475bcbSad break;
33668a475bcbSad case 'h':
33678a475bcbSad opt_hint = false;
33688a475bcbSad break;
33698a475bcbSad case 'H':
33708a475bcbSad opt_hint = true;
33718a475bcbSad break;
33728a475bcbSad case 'j':
33738a475bcbSad opt_junk = false;
33748a475bcbSad break;
33758a475bcbSad case 'J':
33768a475bcbSad opt_junk = true;
33778a475bcbSad break;
33788a475bcbSad case 'k':
33798a475bcbSad /*
33808a475bcbSad * Chunks always require at least one header
33818a475bcbSad * page, so chunks can never be smaller than
33828a475bcbSad * two pages.
33838a475bcbSad */
33848a475bcbSad if (opt_chunk_2pow > pagesize_2pow + 1)
33858a475bcbSad opt_chunk_2pow--;
33868a475bcbSad break;
33878a475bcbSad case 'K':
33882360d084Slukem if (opt_chunk_2pow + 1 <
33892360d084Slukem (int)(sizeof(size_t) << 3))
33908a475bcbSad opt_chunk_2pow++;
33918a475bcbSad break;
33928a475bcbSad case 'p':
33938a475bcbSad opt_print_stats = false;
33948a475bcbSad break;
33958a475bcbSad case 'P':
33968a475bcbSad opt_print_stats = true;
33978a475bcbSad break;
33988a475bcbSad case 'q':
33998a475bcbSad if (opt_quantum_2pow > QUANTUM_2POW_MIN)
34008a475bcbSad opt_quantum_2pow--;
34018a475bcbSad break;
34028a475bcbSad case 'Q':
34038a475bcbSad if (opt_quantum_2pow < pagesize_2pow - 1)
34048a475bcbSad opt_quantum_2pow++;
34058a475bcbSad break;
34068a475bcbSad case 's':
34078a475bcbSad if (opt_small_max_2pow > QUANTUM_2POW_MIN)
34088a475bcbSad opt_small_max_2pow--;
34098a475bcbSad break;
34108a475bcbSad case 'S':
34118a475bcbSad if (opt_small_max_2pow < pagesize_2pow - 1)
34128a475bcbSad opt_small_max_2pow++;
34138a475bcbSad break;
34148a475bcbSad case 'u':
34158a475bcbSad opt_utrace = false;
34168a475bcbSad break;
34178a475bcbSad case 'U':
34188a475bcbSad opt_utrace = true;
34198a475bcbSad break;
34208a475bcbSad case 'v':
34218a475bcbSad opt_sysv = false;
34228a475bcbSad break;
34238a475bcbSad case 'V':
34248a475bcbSad opt_sysv = true;
34258a475bcbSad break;
34268a475bcbSad case 'x':
34278a475bcbSad opt_xmalloc = false;
34288a475bcbSad break;
34298a475bcbSad case 'X':
34308a475bcbSad opt_xmalloc = true;
34318a475bcbSad break;
34328a475bcbSad case 'z':
34338a475bcbSad opt_zero = false;
34348a475bcbSad break;
34358a475bcbSad case 'Z':
34368a475bcbSad opt_zero = true;
34378a475bcbSad break;
34388a475bcbSad default: {
34398a475bcbSad char cbuf[2];
34408a475bcbSad
34418a475bcbSad cbuf[0] = opts[j];
34428a475bcbSad cbuf[1] = '\0';
344386ef91b8Schristos _malloc_message(getprogname(),
34448a475bcbSad ": (malloc) Unsupported character in "
34458a475bcbSad "malloc options: '", cbuf, "'\n");
34468a475bcbSad }
34478a475bcbSad }
34488a475bcbSad }
34498a475bcbSad }
3450713ea189Schristos errno = serrno;
34518a475bcbSad
34528a475bcbSad /* Take care to call atexit() only once. */
3453bf5f78d5Sad if (OPT(print_stats)) {
34548a475bcbSad /* Print statistics at exit. */
34558a475bcbSad atexit(malloc_print_stats);
34568a475bcbSad }
34578a475bcbSad
34588a475bcbSad /* Set variables according to the value of opt_small_max_2pow. */
34598a475bcbSad if (opt_small_max_2pow < opt_quantum_2pow)
34608a475bcbSad opt_small_max_2pow = opt_quantum_2pow;
3461bf5f78d5Sad small_max = (1U << opt_small_max_2pow);
34628a475bcbSad
34638a475bcbSad /* Set bin-related variables. */
34648a475bcbSad bin_maxclass = (pagesize >> 1);
34658a475bcbSad assert(opt_quantum_2pow >= TINY_MIN_2POW);
34668de47c09Schristos ntbins = (unsigned)(opt_quantum_2pow - TINY_MIN_2POW);
3467bf5f78d5Sad assert(ntbins <= (unsigned)opt_quantum_2pow);
34688de47c09Schristos nqbins = (unsigned)(small_max >> opt_quantum_2pow);
34698de47c09Schristos nsbins = (unsigned)(pagesize_2pow - opt_small_max_2pow - 1);
34708a475bcbSad
34718a475bcbSad /* Set variables according to the value of opt_quantum_2pow. */
34728a475bcbSad quantum = (1 << opt_quantum_2pow);
34738a475bcbSad quantum_mask = quantum - 1;
34748a475bcbSad if (ntbins > 0)
34758a475bcbSad small_min = (quantum >> 1) + 1;
34768a475bcbSad else
34778a475bcbSad small_min = 1;
34788a475bcbSad assert(small_min <= quantum);
34798a475bcbSad
34808a475bcbSad /* Set variables according to the value of opt_chunk_2pow. */
34818a475bcbSad chunksize = (1LU << opt_chunk_2pow);
34828a475bcbSad chunksize_mask = chunksize - 1;
34838de47c09Schristos chunksize_2pow = (unsigned)opt_chunk_2pow;
34848de47c09Schristos chunk_npages = (unsigned)(chunksize >> pagesize_2pow);
34858a475bcbSad {
34868a475bcbSad unsigned header_size;
34878a475bcbSad
34888de47c09Schristos header_size = (unsigned)(sizeof(arena_chunk_t) +
34898de47c09Schristos (sizeof(arena_chunk_map_t) * (chunk_npages - 1)));
34908a475bcbSad arena_chunk_header_npages = (header_size >> pagesize_2pow);
34918a475bcbSad if ((header_size & pagesize_mask) != 0)
34928a475bcbSad arena_chunk_header_npages++;
34938a475bcbSad }
34948a475bcbSad arena_maxclass = chunksize - (arena_chunk_header_npages <<
34958a475bcbSad pagesize_2pow);
34968a475bcbSad
34978a475bcbSad UTRACE(0, 0, 0);
34988a475bcbSad
34998a475bcbSad #ifdef MALLOC_STATS
35008a475bcbSad memset(&stats_chunks, 0, sizeof(chunk_stats_t));
35018a475bcbSad #endif
35028a475bcbSad
35038a475bcbSad /* Various sanity checks that regard configuration. */
35048a475bcbSad assert(quantum >= sizeof(void *));
35058a475bcbSad assert(quantum <= pagesize);
35068a475bcbSad assert(chunksize >= pagesize);
35078a475bcbSad assert(quantum * 4 <= chunksize);
35088a475bcbSad
35098a475bcbSad /* Initialize chunks data. */
35108a475bcbSad malloc_mutex_init(&chunks_mtx);
3511562025fdSad rb_tree_init(&huge, &chunk_tree_ops);
35128a475bcbSad #ifdef USE_BRK
35138a475bcbSad malloc_mutex_init(&brk_mtx);
35148a475bcbSad brk_base = sbrk(0);
35158a475bcbSad brk_prev = brk_base;
35168a475bcbSad brk_max = brk_base;
35178a475bcbSad #endif
35188a475bcbSad #ifdef MALLOC_STATS
35198a475bcbSad huge_nmalloc = 0;
35208a475bcbSad huge_ndalloc = 0;
3521b79fded2Syamt huge_nralloc = 0;
35228a475bcbSad huge_allocated = 0;
35238a475bcbSad #endif
3524562025fdSad rb_tree_init(&old_chunks, &chunk_tree_ops);
35258a475bcbSad
35268a475bcbSad /* Initialize base allocation data structures. */
35278a475bcbSad #ifdef MALLOC_STATS
35288a475bcbSad base_mapped = 0;
35298a475bcbSad #endif
35308a475bcbSad #ifdef USE_BRK
35318a475bcbSad /*
35328a475bcbSad * Allocate a base chunk here, since it doesn't actually have to be
35338a475bcbSad * chunk-aligned. Doing this before allocating any other chunks allows
35348a475bcbSad * the use of space that would otherwise be wasted.
35358a475bcbSad */
35368a475bcbSad base_pages_alloc(0);
35378a475bcbSad #endif
35388a475bcbSad base_chunk_nodes = NULL;
35398a475bcbSad malloc_mutex_init(&base_mtx);
35408a475bcbSad
35418a475bcbSad /* Allocate and initialize arenas. */
3542bf5f78d5Sad arenas = (arena_t **)base_alloc(sizeof(arena_t *) * ncpus);
35438a475bcbSad if (arenas == NULL) {
35448a475bcbSad malloc_mutex_unlock(&init_lock);
35458a475bcbSad return (true);
35468a475bcbSad }
35478a475bcbSad /*
35488a475bcbSad * Zero the array. In practice, this should always be pre-zeroed,
35498a475bcbSad * since it was just mmap()ed, but let's be sure.
35508a475bcbSad */
3551bf5f78d5Sad memset(arenas, 0, sizeof(arena_t *) * ncpus);
35528a475bcbSad
35538a475bcbSad /*
35548a475bcbSad * Initialize one arena here. The rest are lazily created in
35558a475bcbSad * arena_choose_hard().
35568a475bcbSad */
3557bf5f78d5Sad if ((arenas[0] = arenas_extend()) == NULL) {
35588a475bcbSad malloc_mutex_unlock(&init_lock);
35598a475bcbSad return (true);
35608a475bcbSad }
35618a475bcbSad
35628a475bcbSad malloc_mutex_init(&arenas_mtx);
35638a475bcbSad
35648a475bcbSad malloc_initialized = true;
35658a475bcbSad malloc_mutex_unlock(&init_lock);
35668a475bcbSad return (false);
35678a475bcbSad }
35688a475bcbSad
35698a475bcbSad /*
35708a475bcbSad * End general internal functions.
35718a475bcbSad */
35728a475bcbSad /******************************************************************************/
35738a475bcbSad /*
35748a475bcbSad * Begin malloc(3)-compatible functions.
35758a475bcbSad */
35768a475bcbSad
35778a475bcbSad void *
malloc(size_t size)35788a475bcbSad malloc(size_t size)
35798a475bcbSad {
35808a475bcbSad void *ret;
35818a475bcbSad
3582bf5f78d5Sad if (__predict_false(malloc_init())) {
35838a475bcbSad ret = NULL;
35848a475bcbSad goto RETURN;
35858a475bcbSad }
35868a475bcbSad
3587bf5f78d5Sad if (__predict_false(size == 0)) {
3588bf5f78d5Sad if (NOT_OPT(sysv))
35898a475bcbSad size = 1;
35908a475bcbSad else {
35918a475bcbSad ret = NULL;
35928a475bcbSad goto RETURN;
35938a475bcbSad }
35948a475bcbSad }
35958a475bcbSad
35968a475bcbSad ret = imalloc(size);
35978a475bcbSad
35988a475bcbSad RETURN:
3599bf5f78d5Sad if (__predict_false(ret == NULL)) {
3600bf5f78d5Sad if (OPT(xmalloc)) {
360186ef91b8Schristos _malloc_message(getprogname(),
36028a475bcbSad ": (malloc) Error in malloc(): out of memory\n", "",
36038a475bcbSad "");
36048a475bcbSad abort();
36058a475bcbSad }
36068a475bcbSad errno = ENOMEM;
36078a475bcbSad }
36088a475bcbSad
36098a475bcbSad UTRACE(0, size, ret);
36108a475bcbSad return (ret);
36118a475bcbSad }
36128a475bcbSad
36138a475bcbSad int
posix_memalign(void ** memptr,size_t alignment,size_t size)36148a475bcbSad posix_memalign(void **memptr, size_t alignment, size_t size)
36158a475bcbSad {
36168a475bcbSad int ret;
36178a475bcbSad void *result;
36188a475bcbSad
3619bf5f78d5Sad if (__predict_false(malloc_init()))
36208a475bcbSad result = NULL;
36218a475bcbSad else {
36228a475bcbSad /* Make sure that alignment is a large enough power of 2. */
36238a475bcbSad if (((alignment - 1) & alignment) != 0
36248a475bcbSad || alignment < sizeof(void *)) {
3625bf5f78d5Sad if (OPT(xmalloc)) {
362686ef91b8Schristos _malloc_message(getprogname(),
36278a475bcbSad ": (malloc) Error in posix_memalign(): "
36288a475bcbSad "invalid alignment\n", "", "");
36298a475bcbSad abort();
36308a475bcbSad }
36318a475bcbSad result = NULL;
36328a475bcbSad ret = EINVAL;
36338a475bcbSad goto RETURN;
36348a475bcbSad }
36358a475bcbSad
363657fcbaebSad if (size == 0) {
363757fcbaebSad if (NOT_OPT(sysv))
363857fcbaebSad size = 1;
363957fcbaebSad else {
364057fcbaebSad result = NULL;
364157fcbaebSad ret = 0;
364257fcbaebSad goto RETURN;
364357fcbaebSad }
364457fcbaebSad }
36458a475bcbSad result = ipalloc(alignment, size);
36468a475bcbSad }
36478a475bcbSad
3648bf5f78d5Sad if (__predict_false(result == NULL)) {
3649bf5f78d5Sad if (OPT(xmalloc)) {
365086ef91b8Schristos _malloc_message(getprogname(),
36518a475bcbSad ": (malloc) Error in posix_memalign(): out of memory\n",
36528a475bcbSad "", "");
36538a475bcbSad abort();
36548a475bcbSad }
36558a475bcbSad ret = ENOMEM;
36568a475bcbSad goto RETURN;
36578a475bcbSad }
36588a475bcbSad
36598a475bcbSad *memptr = result;
36608a475bcbSad ret = 0;
36618a475bcbSad
36628a475bcbSad RETURN:
36638a475bcbSad UTRACE(0, size, result);
36648a475bcbSad return (ret);
36658a475bcbSad }
36668a475bcbSad
36678a475bcbSad void *
calloc(size_t num,size_t size)36688a475bcbSad calloc(size_t num, size_t size)
36698a475bcbSad {
36708a475bcbSad void *ret;
36718a475bcbSad size_t num_size;
36728a475bcbSad
3673bf5f78d5Sad if (__predict_false(malloc_init())) {
36748a475bcbSad num_size = 0;
36758a475bcbSad ret = NULL;
36768a475bcbSad goto RETURN;
36778a475bcbSad }
36788a475bcbSad
36798a475bcbSad num_size = num * size;
3680bf5f78d5Sad if (__predict_false(num_size == 0)) {
3681bf5f78d5Sad if (NOT_OPT(sysv) && ((num == 0) || (size == 0)))
36828a475bcbSad num_size = 1;
36838a475bcbSad else {
36848a475bcbSad ret = NULL;
36858a475bcbSad goto RETURN;
36868a475bcbSad }
36878a475bcbSad /*
36888a475bcbSad * Try to avoid division here. We know that it isn't possible to
36898a475bcbSad * overflow during multiplication if neither operand uses any of the
36908a475bcbSad * most significant half of the bits in a size_t.
36918a475bcbSad */
3692e7a9e46bSad } else if ((unsigned long long)((num | size) &
3693e7a9e46bSad ((unsigned long long)SIZE_T_MAX << (sizeof(size_t) << 2))) &&
3694e7a9e46bSad (num_size / size != num)) {
36958a475bcbSad /* size_t overflow. */
36968a475bcbSad ret = NULL;
36978a475bcbSad goto RETURN;
36988a475bcbSad }
36998a475bcbSad
37008a475bcbSad ret = icalloc(num_size);
37018a475bcbSad
37028a475bcbSad RETURN:
3703a3628a91Sad if (__predict_false(ret == NULL)) {
3704bf5f78d5Sad if (OPT(xmalloc)) {
370586ef91b8Schristos _malloc_message(getprogname(),
37068a475bcbSad ": (malloc) Error in calloc(): out of memory\n", "",
37078a475bcbSad "");
37088a475bcbSad abort();
37098a475bcbSad }
37108a475bcbSad errno = ENOMEM;
37118a475bcbSad }
37128a475bcbSad
37138a475bcbSad UTRACE(0, num_size, ret);
37148a475bcbSad return (ret);
37158a475bcbSad }
37168a475bcbSad
37178a475bcbSad void *
realloc(void * ptr,size_t size)37188a475bcbSad realloc(void *ptr, size_t size)
37198a475bcbSad {
37208a475bcbSad void *ret;
37218a475bcbSad
3722bf5f78d5Sad if (__predict_false(size == 0)) {
3723bf5f78d5Sad if (NOT_OPT(sysv))
37248a475bcbSad size = 1;
37258a475bcbSad else {
37268a475bcbSad if (ptr != NULL)
37278a475bcbSad idalloc(ptr);
37288a475bcbSad ret = NULL;
37298a475bcbSad goto RETURN;
37308a475bcbSad }
37318a475bcbSad }
37328a475bcbSad
3733bf5f78d5Sad if (__predict_true(ptr != NULL)) {
37348a475bcbSad assert(malloc_initialized);
37358a475bcbSad
37368a475bcbSad ret = iralloc(ptr, size);
37378a475bcbSad
3738a3628a91Sad if (__predict_false(ret == NULL)) {
3739bf5f78d5Sad if (OPT(xmalloc)) {
374086ef91b8Schristos _malloc_message(getprogname(),
37418a475bcbSad ": (malloc) Error in realloc(): out of "
37428a475bcbSad "memory\n", "", "");
37438a475bcbSad abort();
37448a475bcbSad }
37458a475bcbSad errno = ENOMEM;
37468a475bcbSad }
37478a475bcbSad } else {
3748bf5f78d5Sad if (__predict_false(malloc_init()))
37498a475bcbSad ret = NULL;
37508a475bcbSad else
37518a475bcbSad ret = imalloc(size);
37528a475bcbSad
3753a3628a91Sad if (__predict_false(ret == NULL)) {
3754bf5f78d5Sad if (OPT(xmalloc)) {
375586ef91b8Schristos _malloc_message(getprogname(),
37568a475bcbSad ": (malloc) Error in realloc(): out of "
37578a475bcbSad "memory\n", "", "");
37588a475bcbSad abort();
37598a475bcbSad }
37608a475bcbSad errno = ENOMEM;
37618a475bcbSad }
37628a475bcbSad }
37638a475bcbSad
37648a475bcbSad RETURN:
37658a475bcbSad UTRACE(ptr, size, ret);
37668a475bcbSad return (ret);
37678a475bcbSad }
37688a475bcbSad
37698a475bcbSad void
free(void * ptr)37708a475bcbSad free(void *ptr)
37718a475bcbSad {
37728a475bcbSad
37738a475bcbSad UTRACE(ptr, 0, 0);
3774a3628a91Sad if (__predict_true(ptr != NULL)) {
37758a475bcbSad assert(malloc_initialized);
37768a475bcbSad
37778a475bcbSad idalloc(ptr);
37788a475bcbSad }
37798a475bcbSad }
37808a475bcbSad
37818a475bcbSad /*
37828a475bcbSad * End malloc(3)-compatible functions.
37838a475bcbSad */
37848a475bcbSad /******************************************************************************/
37858a475bcbSad /*
37868a475bcbSad * Begin non-standard functions.
37878a475bcbSad */
37888a475bcbSad size_t
malloc_usable_size(const void * ptr)37898a475bcbSad malloc_usable_size(const void *ptr)
37908a475bcbSad {
37918a475bcbSad
37928a475bcbSad assert(ptr != NULL);
37938a475bcbSad
37948a475bcbSad return (isalloc(ptr));
37958a475bcbSad }
37968a475bcbSad
37978a475bcbSad /*
37988a475bcbSad * End non-standard functions.
37998a475bcbSad */
38008a475bcbSad /******************************************************************************/
38018a475bcbSad /*
38028a475bcbSad * Begin library-private functions, used by threading libraries for protection
38038a475bcbSad * of malloc during fork(). These functions are only called if the program is
38048a475bcbSad * running in threaded mode, so there is no need to check whether the program
38058a475bcbSad * is threaded here.
38068a475bcbSad */
38078a475bcbSad
38088a475bcbSad void
_malloc_prefork(void)38098a475bcbSad _malloc_prefork(void)
38108a475bcbSad {
38118a475bcbSad unsigned i;
38128a475bcbSad
38138a475bcbSad /* Acquire all mutexes in a safe order. */
3814174eb28aSjoerg malloc_mutex_lock(&init_lock);
38158a475bcbSad malloc_mutex_lock(&arenas_mtx);
3816bf5f78d5Sad for (i = 0; i < ncpus; i++) {
38178a475bcbSad if (arenas[i] != NULL)
38188a475bcbSad malloc_mutex_lock(&arenas[i]->mtx);
38198a475bcbSad }
38208a475bcbSad malloc_mutex_lock(&chunks_mtx);
3821174eb28aSjoerg malloc_mutex_lock(&base_mtx);
3822174eb28aSjoerg #ifdef USE_BRK
3823174eb28aSjoerg malloc_mutex_lock(&brk_mtx);
3824174eb28aSjoerg #endif
38258a475bcbSad }
38268a475bcbSad
38278a475bcbSad void
_malloc_postfork(void)38288a475bcbSad _malloc_postfork(void)
38298a475bcbSad {
38308a475bcbSad unsigned i;
38318a475bcbSad
38328a475bcbSad /* Release all mutexes, now that fork() has completed. */
3833174eb28aSjoerg #ifdef USE_BRK
3834174eb28aSjoerg malloc_mutex_unlock(&brk_mtx);
3835174eb28aSjoerg #endif
3836174eb28aSjoerg malloc_mutex_unlock(&base_mtx);
38378a475bcbSad malloc_mutex_unlock(&chunks_mtx);
38388a475bcbSad
3839bf5f78d5Sad for (i = ncpus; i-- > 0; ) {
38408a475bcbSad if (arenas[i] != NULL)
38418a475bcbSad malloc_mutex_unlock(&arenas[i]->mtx);
38428a475bcbSad }
38438a475bcbSad malloc_mutex_unlock(&arenas_mtx);
3844174eb28aSjoerg malloc_mutex_unlock(&init_lock);
38458a475bcbSad }
38468a475bcbSad
38478409cf4aSjoerg void
_malloc_postfork_child(void)38488409cf4aSjoerg _malloc_postfork_child(void)
38498409cf4aSjoerg {
38508409cf4aSjoerg unsigned i;
38518409cf4aSjoerg
38528409cf4aSjoerg /* Release all mutexes, now that fork() has completed. */
38538409cf4aSjoerg #ifdef USE_BRK
38548409cf4aSjoerg malloc_mutex_init(&brk_mtx);
38558409cf4aSjoerg #endif
38568409cf4aSjoerg malloc_mutex_init(&base_mtx);
38578409cf4aSjoerg malloc_mutex_init(&chunks_mtx);
38588409cf4aSjoerg
3859bf5f78d5Sad for (i = ncpus; i-- > 0; ) {
38608409cf4aSjoerg if (arenas[i] != NULL)
38618409cf4aSjoerg malloc_mutex_init(&arenas[i]->mtx);
38628409cf4aSjoerg }
38638409cf4aSjoerg malloc_mutex_init(&arenas_mtx);
38648409cf4aSjoerg malloc_mutex_init(&init_lock);
38658409cf4aSjoerg }
38668409cf4aSjoerg
38678a475bcbSad /*
38688a475bcbSad * End library-private functions.
38698a475bcbSad */
38708a475bcbSad /******************************************************************************/
3871