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