1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include <mtmalloc.h> 30*7c478bd9Sstevel@tonic-gate #include "mtmalloc_impl.h" 31*7c478bd9Sstevel@tonic-gate #include <unistd.h> 32*7c478bd9Sstevel@tonic-gate #include <synch.h> 33*7c478bd9Sstevel@tonic-gate #include <thread.h> 34*7c478bd9Sstevel@tonic-gate #include <stdio.h> 35*7c478bd9Sstevel@tonic-gate #include <limits.h> 36*7c478bd9Sstevel@tonic-gate #include <errno.h> 37*7c478bd9Sstevel@tonic-gate #include <string.h> 38*7c478bd9Sstevel@tonic-gate #include <strings.h> 39*7c478bd9Sstevel@tonic-gate #include <sys/param.h> 40*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 41*7c478bd9Sstevel@tonic-gate 42*7c478bd9Sstevel@tonic-gate /* 43*7c478bd9Sstevel@tonic-gate * To turn on the asserts just compile -DDEBUG 44*7c478bd9Sstevel@tonic-gate */ 45*7c478bd9Sstevel@tonic-gate 46*7c478bd9Sstevel@tonic-gate #ifndef DEBUG 47*7c478bd9Sstevel@tonic-gate #define NDEBUG 48*7c478bd9Sstevel@tonic-gate #endif 49*7c478bd9Sstevel@tonic-gate 50*7c478bd9Sstevel@tonic-gate #include <assert.h> 51*7c478bd9Sstevel@tonic-gate 52*7c478bd9Sstevel@tonic-gate /* 53*7c478bd9Sstevel@tonic-gate * The MT hot malloc implementation contained herein is designed to be 54*7c478bd9Sstevel@tonic-gate * plug-compatible with the libc version of malloc. It is not intended 55*7c478bd9Sstevel@tonic-gate * to replace that implementation until we decide that it is ok to break 56*7c478bd9Sstevel@tonic-gate * customer apps (Solaris 3.0). 57*7c478bd9Sstevel@tonic-gate * 58*7c478bd9Sstevel@tonic-gate * For requests up to 2^^16, the allocator initializes itself into NCPUS 59*7c478bd9Sstevel@tonic-gate * worth of chains of caches. When a memory request is made, the calling thread 60*7c478bd9Sstevel@tonic-gate * is vectored into one of NCPUS worth of caches. The LWP id gives us a cheap, 61*7c478bd9Sstevel@tonic-gate * contention-reducing index to use, eventually, this should be replaced with 62*7c478bd9Sstevel@tonic-gate * the actual CPU sequence number, when an interface to get it is available. 63*7c478bd9Sstevel@tonic-gate * 64*7c478bd9Sstevel@tonic-gate * Once the thread is vectored into one of the list of caches the real 65*7c478bd9Sstevel@tonic-gate * allocation of the memory begins. The size is determined to figure out which 66*7c478bd9Sstevel@tonic-gate * bucket the allocation should be satisfied from. The management of free 67*7c478bd9Sstevel@tonic-gate * buckets is done via a bitmask. A free bucket is represented by a 1. The 68*7c478bd9Sstevel@tonic-gate * first free bit represents the first free bucket. The position of the bit, 69*7c478bd9Sstevel@tonic-gate * represents the position of the bucket in the arena. 70*7c478bd9Sstevel@tonic-gate * 71*7c478bd9Sstevel@tonic-gate * When the memory from the arena is handed out, the address of the cache 72*7c478bd9Sstevel@tonic-gate * control structure is written in the word preceeding the returned memory. 73*7c478bd9Sstevel@tonic-gate * This cache control address is used during free() to mark the buffer free 74*7c478bd9Sstevel@tonic-gate * in the cache control structure. 75*7c478bd9Sstevel@tonic-gate * 76*7c478bd9Sstevel@tonic-gate * When all available memory in a cache has been depleted, a new chunk of memory 77*7c478bd9Sstevel@tonic-gate * is allocated via sbrk(). The new cache is allocated from this chunk of memory 78*7c478bd9Sstevel@tonic-gate * and initialized in the function create_cache(). New caches are installed at 79*7c478bd9Sstevel@tonic-gate * the front of a singly linked list of the same size memory pools. This helps 80*7c478bd9Sstevel@tonic-gate * to ensure that there will tend to be available memory in the beginning of the 81*7c478bd9Sstevel@tonic-gate * list. 82*7c478bd9Sstevel@tonic-gate * 83*7c478bd9Sstevel@tonic-gate * Long linked lists hurt performance. To decrease this effect, there is a 84*7c478bd9Sstevel@tonic-gate * tunable, requestsize, that bumps up the sbrk allocation size and thus 85*7c478bd9Sstevel@tonic-gate * increases the number of available blocks within an arena. We also keep 86*7c478bd9Sstevel@tonic-gate * a "hint" for each cache list, which is the last cache in the list allocated 87*7c478bd9Sstevel@tonic-gate * from. This lowers the cost of searching if there are a lot of fully 88*7c478bd9Sstevel@tonic-gate * allocated blocks at the front of the list. 89*7c478bd9Sstevel@tonic-gate * 90*7c478bd9Sstevel@tonic-gate * For requests greater than 2^^16 (oversize allocations), there are two pieces 91*7c478bd9Sstevel@tonic-gate * of overhead. There is the OVERHEAD used to hold the cache addr 92*7c478bd9Sstevel@tonic-gate * (&oversize_list), plus an oversize_t structure to further describe the block. 93*7c478bd9Sstevel@tonic-gate * 94*7c478bd9Sstevel@tonic-gate * The oversize list is kept as defragmented as possible by coalescing 95*7c478bd9Sstevel@tonic-gate * freed oversized allocations with adjacent neighbors. 96*7c478bd9Sstevel@tonic-gate * 97*7c478bd9Sstevel@tonic-gate * Addresses handed out are stored in a hash table, and are aligned on 98*7c478bd9Sstevel@tonic-gate * MTMALLOC_MIN_ALIGN-byte boundaries at both ends. Request sizes are rounded-up 99*7c478bd9Sstevel@tonic-gate * where necessary in order to achieve this. This eases the implementation of 100*7c478bd9Sstevel@tonic-gate * MTDEBUGPATTERN and MTINITPATTERN, particularly where coalescing occurs. 101*7c478bd9Sstevel@tonic-gate * 102*7c478bd9Sstevel@tonic-gate * A memalign allocation takes memalign header overhead. There's two 103*7c478bd9Sstevel@tonic-gate * types of memalign headers distinguished by MTMALLOC_MEMALIGN_MAGIC 104*7c478bd9Sstevel@tonic-gate * and MTMALLOC_MEMALIGN_MIN_MAGIC. When the size of memory taken to 105*7c478bd9Sstevel@tonic-gate * get to the aligned address from malloc'ed address is the minimum size 106*7c478bd9Sstevel@tonic-gate * OVERHEAD, we create a header taking only one OVERHEAD space with magic 107*7c478bd9Sstevel@tonic-gate * number MTMALLOC_MEMALIGN_MIN_MAGIC, and we know by subtracting OVERHEAD 108*7c478bd9Sstevel@tonic-gate * from memaligned address, we can get to the malloc'ed address. Otherwise, 109*7c478bd9Sstevel@tonic-gate * we create a memalign header taking two OVERHEAD space, one stores 110*7c478bd9Sstevel@tonic-gate * MTMALLOC_MEMALIGN_MAGIC magic number, the other one points back to the 111*7c478bd9Sstevel@tonic-gate * malloc'ed address. 112*7c478bd9Sstevel@tonic-gate */ 113*7c478bd9Sstevel@tonic-gate 114*7c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 115*7c478bd9Sstevel@tonic-gate #include <arpa/inet.h> /* for htonl() */ 116*7c478bd9Sstevel@tonic-gate #endif 117*7c478bd9Sstevel@tonic-gate 118*7c478bd9Sstevel@tonic-gate static void * morecore(size_t); 119*7c478bd9Sstevel@tonic-gate static int setup_caches(void); 120*7c478bd9Sstevel@tonic-gate static void create_cache(cache_t *, size_t bufsize, uint_t hunks); 121*7c478bd9Sstevel@tonic-gate static void * malloc_internal(size_t, percpu_t *); 122*7c478bd9Sstevel@tonic-gate static void * oversize(size_t); 123*7c478bd9Sstevel@tonic-gate static oversize_t *find_oversize(size_t); 124*7c478bd9Sstevel@tonic-gate static void add_oversize(oversize_t *); 125*7c478bd9Sstevel@tonic-gate static void copy_pattern(uint32_t, void *, size_t); 126*7c478bd9Sstevel@tonic-gate static void * verify_pattern(uint32_t, void *, size_t); 127*7c478bd9Sstevel@tonic-gate static void reinit_cpu_list(void); 128*7c478bd9Sstevel@tonic-gate static void reinit_cache(cache_t *); 129*7c478bd9Sstevel@tonic-gate static void free_oversize(oversize_t *); 130*7c478bd9Sstevel@tonic-gate static oversize_t *oversize_header_alloc(uintptr_t, size_t); 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate /* 133*7c478bd9Sstevel@tonic-gate * oversize hash table stuff 134*7c478bd9Sstevel@tonic-gate */ 135*7c478bd9Sstevel@tonic-gate #define NUM_BUCKETS 67 /* must be prime */ 136*7c478bd9Sstevel@tonic-gate #define HASH_OVERSIZE(caddr) ((uintptr_t)(caddr) % NUM_BUCKETS) 137*7c478bd9Sstevel@tonic-gate oversize_t *ovsz_hashtab[NUM_BUCKETS]; 138*7c478bd9Sstevel@tonic-gate 139*7c478bd9Sstevel@tonic-gate /* 140*7c478bd9Sstevel@tonic-gate * Gets a decent "current cpu identifier", to be used to reduce contention. 141*7c478bd9Sstevel@tonic-gate * Eventually, this should be replaced by an interface to get the actual 142*7c478bd9Sstevel@tonic-gate * CPU sequence number in libthread/liblwp. 143*7c478bd9Sstevel@tonic-gate */ 144*7c478bd9Sstevel@tonic-gate extern uint_t _thr_self(); 145*7c478bd9Sstevel@tonic-gate #pragma weak _thr_self 146*7c478bd9Sstevel@tonic-gate #define get_curcpu_func() (curcpu_func)_thr_self 147*7c478bd9Sstevel@tonic-gate 148*7c478bd9Sstevel@tonic-gate #define ALIGN(x, a) ((((uintptr_t)(x) + ((uintptr_t)(a) - 1)) \ 149*7c478bd9Sstevel@tonic-gate & ~((uintptr_t)(a) - 1))) 150*7c478bd9Sstevel@tonic-gate 151*7c478bd9Sstevel@tonic-gate /* need this to deal with little endianess of x86 */ 152*7c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 153*7c478bd9Sstevel@tonic-gate #define FLIP_EM(x) htonl((x)) 154*7c478bd9Sstevel@tonic-gate #else 155*7c478bd9Sstevel@tonic-gate #define FLIP_EM(x) (x) 156*7c478bd9Sstevel@tonic-gate #endif 157*7c478bd9Sstevel@tonic-gate 158*7c478bd9Sstevel@tonic-gate #define INSERT_ONLY 0 159*7c478bd9Sstevel@tonic-gate #define COALESCE_LEFT 0x00000001 160*7c478bd9Sstevel@tonic-gate #define COALESCE_RIGHT 0x00000002 161*7c478bd9Sstevel@tonic-gate #define COALESCE_WITH_BOTH_SIDES (COALESCE_LEFT | COALESCE_RIGHT) 162*7c478bd9Sstevel@tonic-gate 163*7c478bd9Sstevel@tonic-gate #define OVERHEAD 8 /* size needed to write cache addr */ 164*7c478bd9Sstevel@tonic-gate #define HUNKSIZE 8192 /* just a multiplier */ 165*7c478bd9Sstevel@tonic-gate 166*7c478bd9Sstevel@tonic-gate #define MAX_CACHED_SHIFT 16 /* 64K is the max cached size */ 167*7c478bd9Sstevel@tonic-gate #define MAX_CACHED (1 << MAX_CACHED_SHIFT) 168*7c478bd9Sstevel@tonic-gate #define MIN_CACHED_SHIFT 4 /* smaller requests rounded up */ 169*7c478bd9Sstevel@tonic-gate #define MTMALLOC_MIN_ALIGN 8 /* min guaranteed alignment */ 170*7c478bd9Sstevel@tonic-gate 171*7c478bd9Sstevel@tonic-gate #define NUM_CACHES (MAX_CACHED_SHIFT - MIN_CACHED_SHIFT + 1) 172*7c478bd9Sstevel@tonic-gate #define CACHELIST_SIZE ALIGN(NUM_CACHES * sizeof (cache_head_t), \ 173*7c478bd9Sstevel@tonic-gate CACHE_COHERENCY_UNIT) 174*7c478bd9Sstevel@tonic-gate 175*7c478bd9Sstevel@tonic-gate #define MINSIZE 9 /* for requestsize, tunable */ 176*7c478bd9Sstevel@tonic-gate #define MAXSIZE 256 /* arbitrary, big enough, for requestsize */ 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate #define FREEPATTERN 0xdeadbeef /* debug fill pattern for free buf */ 179*7c478bd9Sstevel@tonic-gate #define INITPATTERN 0xbaddcafe /* debug fill pattern for new buf */ 180*7c478bd9Sstevel@tonic-gate 181*7c478bd9Sstevel@tonic-gate #define misaligned(p) ((unsigned)(p) & (sizeof (int) - 1)) 182*7c478bd9Sstevel@tonic-gate #define IS_OVERSIZE(x, y) (((x) < (y)) && (((x) > MAX_CACHED)? 1 : 0)) 183*7c478bd9Sstevel@tonic-gate 184*7c478bd9Sstevel@tonic-gate static long requestsize = MINSIZE; /* 9 pages per cache; tunable; 9 is min */ 185*7c478bd9Sstevel@tonic-gate 186*7c478bd9Sstevel@tonic-gate static uint_t cpu_mask; 187*7c478bd9Sstevel@tonic-gate static curcpu_func curcpu; 188*7c478bd9Sstevel@tonic-gate 189*7c478bd9Sstevel@tonic-gate static int32_t debugopt; 190*7c478bd9Sstevel@tonic-gate static int32_t reinit; 191*7c478bd9Sstevel@tonic-gate 192*7c478bd9Sstevel@tonic-gate static percpu_t *cpu_list; 193*7c478bd9Sstevel@tonic-gate static oversize_t oversize_list; 194*7c478bd9Sstevel@tonic-gate static mutex_t oversize_lock; 195*7c478bd9Sstevel@tonic-gate 196*7c478bd9Sstevel@tonic-gate static int ncpus; 197*7c478bd9Sstevel@tonic-gate 198*7c478bd9Sstevel@tonic-gate #define MTMALLOC_OVERSIZE_MAGIC ((uintptr_t)&oversize_list) 199*7c478bd9Sstevel@tonic-gate #define MTMALLOC_MEMALIGN_MAGIC ((uintptr_t)&oversize_list + 1) 200*7c478bd9Sstevel@tonic-gate #define MTMALLOC_MEMALIGN_MIN_MAGIC ((uintptr_t)&oversize_list + 2) 201*7c478bd9Sstevel@tonic-gate 202*7c478bd9Sstevel@tonic-gate /* 203*7c478bd9Sstevel@tonic-gate * We require allocations handed out to be aligned on MTMALLOC_MIN_ALIGN-byte 204*7c478bd9Sstevel@tonic-gate * boundaries. We round up sizeof (oversize_t) (when necessary) to ensure that 205*7c478bd9Sstevel@tonic-gate * this is achieved. 206*7c478bd9Sstevel@tonic-gate */ 207*7c478bd9Sstevel@tonic-gate #define OVSZ_SIZE (ALIGN(sizeof (oversize_t), MTMALLOC_MIN_ALIGN)) 208*7c478bd9Sstevel@tonic-gate #define OVSZ_HEADER_SIZE (OVSZ_SIZE + OVERHEAD) 209*7c478bd9Sstevel@tonic-gate 210*7c478bd9Sstevel@tonic-gate /* 211*7c478bd9Sstevel@tonic-gate * memalign header takes 2 OVERHEAD space. One for memalign magic, and the 212*7c478bd9Sstevel@tonic-gate * other one points back to the start address of originally allocated space. 213*7c478bd9Sstevel@tonic-gate */ 214*7c478bd9Sstevel@tonic-gate #define MEMALIGN_HEADER_SIZE 2 * OVERHEAD 215*7c478bd9Sstevel@tonic-gate #define MEMALIGN_HEADER_ALLOC(x, shift, malloc_addr)\ 216*7c478bd9Sstevel@tonic-gate if (shift == OVERHEAD)\ 217*7c478bd9Sstevel@tonic-gate *((uintptr_t *)((caddr_t)x - OVERHEAD)) = \ 218*7c478bd9Sstevel@tonic-gate MTMALLOC_MEMALIGN_MIN_MAGIC; \ 219*7c478bd9Sstevel@tonic-gate else {\ 220*7c478bd9Sstevel@tonic-gate *((uintptr_t *)((caddr_t)x - OVERHEAD)) = \ 221*7c478bd9Sstevel@tonic-gate MTMALLOC_MEMALIGN_MAGIC; \ 222*7c478bd9Sstevel@tonic-gate *((uintptr_t *)((caddr_t)x - 2 * OVERHEAD)) = \ 223*7c478bd9Sstevel@tonic-gate (uintptr_t)malloc_addr; \ 224*7c478bd9Sstevel@tonic-gate } 225*7c478bd9Sstevel@tonic-gate 226*7c478bd9Sstevel@tonic-gate void * 227*7c478bd9Sstevel@tonic-gate malloc(size_t bytes) 228*7c478bd9Sstevel@tonic-gate { 229*7c478bd9Sstevel@tonic-gate percpu_t *list_rotor; 230*7c478bd9Sstevel@tonic-gate uint_t list_index; 231*7c478bd9Sstevel@tonic-gate 232*7c478bd9Sstevel@tonic-gate /* 233*7c478bd9Sstevel@tonic-gate * this test is due to linking with libthread. 234*7c478bd9Sstevel@tonic-gate * There are malloc calls prior to this library 235*7c478bd9Sstevel@tonic-gate * being initialized. 236*7c478bd9Sstevel@tonic-gate * 237*7c478bd9Sstevel@tonic-gate * If setup_caches fails, we set ENOMEM and return NULL 238*7c478bd9Sstevel@tonic-gate */ 239*7c478bd9Sstevel@tonic-gate if (cpu_list == (percpu_t *)NULL) { 240*7c478bd9Sstevel@tonic-gate if (setup_caches() == 0) { 241*7c478bd9Sstevel@tonic-gate errno = ENOMEM; 242*7c478bd9Sstevel@tonic-gate return (NULL); 243*7c478bd9Sstevel@tonic-gate } 244*7c478bd9Sstevel@tonic-gate } 245*7c478bd9Sstevel@tonic-gate 246*7c478bd9Sstevel@tonic-gate if (bytes > MAX_CACHED) 247*7c478bd9Sstevel@tonic-gate return (oversize(bytes)); 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate list_index = (curcpu() & cpu_mask); 250*7c478bd9Sstevel@tonic-gate 251*7c478bd9Sstevel@tonic-gate list_rotor = &cpu_list[list_index]; 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate return (malloc_internal(bytes, list_rotor)); 254*7c478bd9Sstevel@tonic-gate } 255*7c478bd9Sstevel@tonic-gate 256*7c478bd9Sstevel@tonic-gate void * 257*7c478bd9Sstevel@tonic-gate realloc(void * ptr, size_t bytes) 258*7c478bd9Sstevel@tonic-gate { 259*7c478bd9Sstevel@tonic-gate void *new, *data_ptr; 260*7c478bd9Sstevel@tonic-gate cache_t *cacheptr; 261*7c478bd9Sstevel@tonic-gate caddr_t mem; 262*7c478bd9Sstevel@tonic-gate size_t shift = 0; 263*7c478bd9Sstevel@tonic-gate 264*7c478bd9Sstevel@tonic-gate if (ptr == NULL) 265*7c478bd9Sstevel@tonic-gate return (malloc(bytes)); 266*7c478bd9Sstevel@tonic-gate 267*7c478bd9Sstevel@tonic-gate if (bytes == 0) { 268*7c478bd9Sstevel@tonic-gate free(ptr); 269*7c478bd9Sstevel@tonic-gate return (NULL); 270*7c478bd9Sstevel@tonic-gate } 271*7c478bd9Sstevel@tonic-gate 272*7c478bd9Sstevel@tonic-gate data_ptr = ptr; 273*7c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 274*7c478bd9Sstevel@tonic-gate 275*7c478bd9Sstevel@tonic-gate new = malloc(bytes); 276*7c478bd9Sstevel@tonic-gate 277*7c478bd9Sstevel@tonic-gate if (new == NULL) 278*7c478bd9Sstevel@tonic-gate return (NULL); 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate /* 281*7c478bd9Sstevel@tonic-gate * If new == ptr, ptr has previously been freed. Passing a freed pointer 282*7c478bd9Sstevel@tonic-gate * to realloc() is not allowed - unless the caller specifically states 283*7c478bd9Sstevel@tonic-gate * otherwise, in which case we must avoid freeing ptr (ie new) before we 284*7c478bd9Sstevel@tonic-gate * return new. There is (obviously) no requirement to memcpy() ptr to 285*7c478bd9Sstevel@tonic-gate * new before we return. 286*7c478bd9Sstevel@tonic-gate */ 287*7c478bd9Sstevel@tonic-gate if (new == ptr) { 288*7c478bd9Sstevel@tonic-gate if (!(debugopt & MTDOUBLEFREE)) 289*7c478bd9Sstevel@tonic-gate abort(); 290*7c478bd9Sstevel@tonic-gate return (new); 291*7c478bd9Sstevel@tonic-gate } 292*7c478bd9Sstevel@tonic-gate 293*7c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MAGIC) { 294*7c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 295*7c478bd9Sstevel@tonic-gate ptr = (void *)*(uintptr_t *)mem; 296*7c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 297*7c478bd9Sstevel@tonic-gate shift = (size_t)((uintptr_t)data_ptr - (uintptr_t)ptr); 298*7c478bd9Sstevel@tonic-gate } else if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MIN_MAGIC) { 299*7c478bd9Sstevel@tonic-gate ptr = (void *) mem; 300*7c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 301*7c478bd9Sstevel@tonic-gate shift = OVERHEAD; 302*7c478bd9Sstevel@tonic-gate } 303*7c478bd9Sstevel@tonic-gate 304*7c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_OVERSIZE_MAGIC) { 305*7c478bd9Sstevel@tonic-gate oversize_t *old; 306*7c478bd9Sstevel@tonic-gate 307*7c478bd9Sstevel@tonic-gate old = (oversize_t *)(mem - OVSZ_SIZE); 308*7c478bd9Sstevel@tonic-gate (void) memcpy(new, data_ptr, MIN(bytes, old->size - shift)); 309*7c478bd9Sstevel@tonic-gate free(ptr); 310*7c478bd9Sstevel@tonic-gate return (new); 311*7c478bd9Sstevel@tonic-gate } 312*7c478bd9Sstevel@tonic-gate 313*7c478bd9Sstevel@tonic-gate cacheptr = (cache_t *)*(uintptr_t *)mem; 314*7c478bd9Sstevel@tonic-gate 315*7c478bd9Sstevel@tonic-gate (void) memcpy(new, data_ptr, 316*7c478bd9Sstevel@tonic-gate MIN(cacheptr->mt_size - OVERHEAD - shift, bytes)); 317*7c478bd9Sstevel@tonic-gate free(ptr); 318*7c478bd9Sstevel@tonic-gate 319*7c478bd9Sstevel@tonic-gate return (new); 320*7c478bd9Sstevel@tonic-gate } 321*7c478bd9Sstevel@tonic-gate 322*7c478bd9Sstevel@tonic-gate void * 323*7c478bd9Sstevel@tonic-gate calloc(size_t nelem, size_t bytes) 324*7c478bd9Sstevel@tonic-gate { 325*7c478bd9Sstevel@tonic-gate void * ptr; 326*7c478bd9Sstevel@tonic-gate size_t size = nelem * bytes; 327*7c478bd9Sstevel@tonic-gate 328*7c478bd9Sstevel@tonic-gate ptr = malloc(size); 329*7c478bd9Sstevel@tonic-gate if (ptr == NULL) 330*7c478bd9Sstevel@tonic-gate return (NULL); 331*7c478bd9Sstevel@tonic-gate bzero(ptr, size); 332*7c478bd9Sstevel@tonic-gate 333*7c478bd9Sstevel@tonic-gate return (ptr); 334*7c478bd9Sstevel@tonic-gate } 335*7c478bd9Sstevel@tonic-gate 336*7c478bd9Sstevel@tonic-gate void 337*7c478bd9Sstevel@tonic-gate free(void * ptr) 338*7c478bd9Sstevel@tonic-gate { 339*7c478bd9Sstevel@tonic-gate cache_t *cacheptr; 340*7c478bd9Sstevel@tonic-gate caddr_t mem; 341*7c478bd9Sstevel@tonic-gate int32_t i; 342*7c478bd9Sstevel@tonic-gate caddr_t freeblocks; 343*7c478bd9Sstevel@tonic-gate uintptr_t offset; 344*7c478bd9Sstevel@tonic-gate uchar_t mask; 345*7c478bd9Sstevel@tonic-gate int32_t which_bit, num_bytes; 346*7c478bd9Sstevel@tonic-gate 347*7c478bd9Sstevel@tonic-gate if (ptr == NULL) 348*7c478bd9Sstevel@tonic-gate return; 349*7c478bd9Sstevel@tonic-gate 350*7c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 351*7c478bd9Sstevel@tonic-gate 352*7c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MAGIC) { 353*7c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 354*7c478bd9Sstevel@tonic-gate ptr = (void *)*(uintptr_t *)mem; 355*7c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 356*7c478bd9Sstevel@tonic-gate } else if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MIN_MAGIC) { 357*7c478bd9Sstevel@tonic-gate ptr = (void *) mem; 358*7c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 359*7c478bd9Sstevel@tonic-gate } 360*7c478bd9Sstevel@tonic-gate 361*7c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_OVERSIZE_MAGIC) { 362*7c478bd9Sstevel@tonic-gate oversize_t *big, **opp; 363*7c478bd9Sstevel@tonic-gate int bucket; 364*7c478bd9Sstevel@tonic-gate 365*7c478bd9Sstevel@tonic-gate big = (oversize_t *)(mem - OVSZ_SIZE); 366*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 367*7c478bd9Sstevel@tonic-gate 368*7c478bd9Sstevel@tonic-gate bucket = HASH_OVERSIZE(big->addr); 369*7c478bd9Sstevel@tonic-gate for (opp = &ovsz_hashtab[bucket]; *opp != NULL; 370*7c478bd9Sstevel@tonic-gate opp = &(*opp)->hash_next) 371*7c478bd9Sstevel@tonic-gate if (*opp == big) 372*7c478bd9Sstevel@tonic-gate break; 373*7c478bd9Sstevel@tonic-gate 374*7c478bd9Sstevel@tonic-gate if (*opp == NULL) { 375*7c478bd9Sstevel@tonic-gate if (!(debugopt & MTDOUBLEFREE)) 376*7c478bd9Sstevel@tonic-gate abort(); 377*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 378*7c478bd9Sstevel@tonic-gate return; 379*7c478bd9Sstevel@tonic-gate } 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate *opp = big->hash_next; /* remove big from the hash table */ 382*7c478bd9Sstevel@tonic-gate big->hash_next = NULL; 383*7c478bd9Sstevel@tonic-gate 384*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 385*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, ptr, big->size); 386*7c478bd9Sstevel@tonic-gate add_oversize(big); 387*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 388*7c478bd9Sstevel@tonic-gate return; 389*7c478bd9Sstevel@tonic-gate } 390*7c478bd9Sstevel@tonic-gate 391*7c478bd9Sstevel@tonic-gate cacheptr = (cache_t *)*(uintptr_t *)mem; 392*7c478bd9Sstevel@tonic-gate freeblocks = cacheptr->mt_freelist; 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate /* 395*7c478bd9Sstevel@tonic-gate * This is the distance measured in bits into the arena. 396*7c478bd9Sstevel@tonic-gate * The value of offset is in bytes but there is a 1-1 correlation 397*7c478bd9Sstevel@tonic-gate * between distance into the arena and distance into the 398*7c478bd9Sstevel@tonic-gate * freelist bitmask. 399*7c478bd9Sstevel@tonic-gate */ 400*7c478bd9Sstevel@tonic-gate offset = mem - cacheptr->mt_arena; 401*7c478bd9Sstevel@tonic-gate 402*7c478bd9Sstevel@tonic-gate /* 403*7c478bd9Sstevel@tonic-gate * i is total number of bits to offset into freelist bitmask. 404*7c478bd9Sstevel@tonic-gate */ 405*7c478bd9Sstevel@tonic-gate 406*7c478bd9Sstevel@tonic-gate i = offset / cacheptr->mt_size; 407*7c478bd9Sstevel@tonic-gate 408*7c478bd9Sstevel@tonic-gate num_bytes = i >> 3; 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate /* 411*7c478bd9Sstevel@tonic-gate * which_bit is the bit offset into the byte in the freelist. 412*7c478bd9Sstevel@tonic-gate * if our freelist bitmask looks like 0xf3 and we are freeing 413*7c478bd9Sstevel@tonic-gate * block 5 (ie: the 6th block) our mask will be 0xf7 after 414*7c478bd9Sstevel@tonic-gate * the free. Things go left to right that's why the mask is 0x80 415*7c478bd9Sstevel@tonic-gate * and not 0x01. 416*7c478bd9Sstevel@tonic-gate */ 417*7c478bd9Sstevel@tonic-gate which_bit = i - (num_bytes << 3); 418*7c478bd9Sstevel@tonic-gate 419*7c478bd9Sstevel@tonic-gate mask = 0x80 >> which_bit; 420*7c478bd9Sstevel@tonic-gate 421*7c478bd9Sstevel@tonic-gate freeblocks += num_bytes; 422*7c478bd9Sstevel@tonic-gate 423*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 424*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, ptr, cacheptr->mt_size - OVERHEAD); 425*7c478bd9Sstevel@tonic-gate 426*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&cacheptr->mt_cache_lock); 427*7c478bd9Sstevel@tonic-gate 428*7c478bd9Sstevel@tonic-gate if (*freeblocks & mask) { 429*7c478bd9Sstevel@tonic-gate if (!(debugopt & MTDOUBLEFREE)) 430*7c478bd9Sstevel@tonic-gate abort(); 431*7c478bd9Sstevel@tonic-gate } else { 432*7c478bd9Sstevel@tonic-gate *freeblocks |= mask; 433*7c478bd9Sstevel@tonic-gate cacheptr->mt_nfree++; 434*7c478bd9Sstevel@tonic-gate } 435*7c478bd9Sstevel@tonic-gate 436*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cacheptr->mt_cache_lock); 437*7c478bd9Sstevel@tonic-gate } 438*7c478bd9Sstevel@tonic-gate 439*7c478bd9Sstevel@tonic-gate void * 440*7c478bd9Sstevel@tonic-gate memalign(size_t alignment, size_t size) 441*7c478bd9Sstevel@tonic-gate { 442*7c478bd9Sstevel@tonic-gate size_t alloc_size; 443*7c478bd9Sstevel@tonic-gate uintptr_t offset; 444*7c478bd9Sstevel@tonic-gate void *alloc_buf; 445*7c478bd9Sstevel@tonic-gate void *ret_buf; 446*7c478bd9Sstevel@tonic-gate 447*7c478bd9Sstevel@tonic-gate if (size == 0 || alignment == 0 || 448*7c478bd9Sstevel@tonic-gate misaligned(alignment) || 449*7c478bd9Sstevel@tonic-gate (alignment & (alignment - 1)) != 0) { 450*7c478bd9Sstevel@tonic-gate errno = EINVAL; 451*7c478bd9Sstevel@tonic-gate return (NULL); 452*7c478bd9Sstevel@tonic-gate } 453*7c478bd9Sstevel@tonic-gate 454*7c478bd9Sstevel@tonic-gate /* <= MTMALLOC_MIN_ALIGN, malloc can provide directly */ 455*7c478bd9Sstevel@tonic-gate if (alignment <= MTMALLOC_MIN_ALIGN) 456*7c478bd9Sstevel@tonic-gate return (malloc(size)); 457*7c478bd9Sstevel@tonic-gate 458*7c478bd9Sstevel@tonic-gate alloc_size = size + alignment - MTMALLOC_MIN_ALIGN; 459*7c478bd9Sstevel@tonic-gate 460*7c478bd9Sstevel@tonic-gate if (alloc_size < size) { /* overflow */ 461*7c478bd9Sstevel@tonic-gate errno = ENOMEM; 462*7c478bd9Sstevel@tonic-gate return (NULL); 463*7c478bd9Sstevel@tonic-gate } 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate alloc_buf = malloc(alloc_size); 466*7c478bd9Sstevel@tonic-gate 467*7c478bd9Sstevel@tonic-gate if (alloc_buf == NULL) 468*7c478bd9Sstevel@tonic-gate /* malloc sets errno */ 469*7c478bd9Sstevel@tonic-gate return (NULL); 470*7c478bd9Sstevel@tonic-gate 471*7c478bd9Sstevel@tonic-gate /* 472*7c478bd9Sstevel@tonic-gate * If alloc_size > MAX_CACHED, malloc() will have returned a multiple of 473*7c478bd9Sstevel@tonic-gate * MTMALLOC_MIN_ALIGN, having rounded-up alloc_size if necessary. Since 474*7c478bd9Sstevel@tonic-gate * we will use alloc_size to return the excess fragments to the free 475*7c478bd9Sstevel@tonic-gate * list, we also round-up alloc_size if necessary. 476*7c478bd9Sstevel@tonic-gate */ 477*7c478bd9Sstevel@tonic-gate if ((alloc_size > MAX_CACHED) && 478*7c478bd9Sstevel@tonic-gate (alloc_size & (MTMALLOC_MIN_ALIGN - 1))) 479*7c478bd9Sstevel@tonic-gate alloc_size = ALIGN(alloc_size, MTMALLOC_MIN_ALIGN); 480*7c478bd9Sstevel@tonic-gate 481*7c478bd9Sstevel@tonic-gate if ((offset = (uintptr_t)alloc_buf & (alignment - 1)) == 0) { 482*7c478bd9Sstevel@tonic-gate /* aligned correctly */ 483*7c478bd9Sstevel@tonic-gate 484*7c478bd9Sstevel@tonic-gate size_t frag_size = alloc_size - 485*7c478bd9Sstevel@tonic-gate (size + MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE); 486*7c478bd9Sstevel@tonic-gate 487*7c478bd9Sstevel@tonic-gate /* 488*7c478bd9Sstevel@tonic-gate * If the leftover piece of the memory > MAX_CACHED, 489*7c478bd9Sstevel@tonic-gate * split off the piece and return it back to the freelist. 490*7c478bd9Sstevel@tonic-gate */ 491*7c478bd9Sstevel@tonic-gate if (IS_OVERSIZE(frag_size, alloc_size)) { 492*7c478bd9Sstevel@tonic-gate oversize_t *orig, *tail; 493*7c478bd9Sstevel@tonic-gate uintptr_t taddr; 494*7c478bd9Sstevel@tonic-gate size_t data_size; 495*7c478bd9Sstevel@tonic-gate taddr = ALIGN((uintptr_t)alloc_buf + size, 496*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 497*7c478bd9Sstevel@tonic-gate data_size = taddr - (uintptr_t)alloc_buf; 498*7c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 499*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 500*7c478bd9Sstevel@tonic-gate frag_size = orig->size - data_size - 501*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE; 502*7c478bd9Sstevel@tonic-gate orig->size = data_size; 503*7c478bd9Sstevel@tonic-gate tail = oversize_header_alloc(taddr, frag_size); 504*7c478bd9Sstevel@tonic-gate free_oversize(tail); 505*7c478bd9Sstevel@tonic-gate } 506*7c478bd9Sstevel@tonic-gate ret_buf = alloc_buf; 507*7c478bd9Sstevel@tonic-gate } else { 508*7c478bd9Sstevel@tonic-gate uchar_t oversize_bits = 0; 509*7c478bd9Sstevel@tonic-gate size_t head_sz, data_sz, tail_sz; 510*7c478bd9Sstevel@tonic-gate uintptr_t ret_addr, taddr, shift, tshift; 511*7c478bd9Sstevel@tonic-gate oversize_t *orig, *tail; 512*7c478bd9Sstevel@tonic-gate size_t tsize; 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate /* needs to be aligned */ 515*7c478bd9Sstevel@tonic-gate shift = alignment - offset; 516*7c478bd9Sstevel@tonic-gate 517*7c478bd9Sstevel@tonic-gate assert(shift >= MTMALLOC_MIN_ALIGN); 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate ret_addr = ((uintptr_t)alloc_buf + shift); 520*7c478bd9Sstevel@tonic-gate ret_buf = (void *)ret_addr; 521*7c478bd9Sstevel@tonic-gate 522*7c478bd9Sstevel@tonic-gate if (alloc_size <= MAX_CACHED) { 523*7c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, alloc_buf); 524*7c478bd9Sstevel@tonic-gate return (ret_buf); 525*7c478bd9Sstevel@tonic-gate } 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate /* 528*7c478bd9Sstevel@tonic-gate * Only check for the fragments when the memory is allocted 529*7c478bd9Sstevel@tonic-gate * from oversize_list. Split off a fragment and return it 530*7c478bd9Sstevel@tonic-gate * to the oversize freelist when it's > MAX_CACHED. 531*7c478bd9Sstevel@tonic-gate */ 532*7c478bd9Sstevel@tonic-gate 533*7c478bd9Sstevel@tonic-gate head_sz = shift - MAX(MEMALIGN_HEADER_SIZE, OVSZ_HEADER_SIZE); 534*7c478bd9Sstevel@tonic-gate 535*7c478bd9Sstevel@tonic-gate tail_sz = alloc_size - 536*7c478bd9Sstevel@tonic-gate (shift + size + MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE); 537*7c478bd9Sstevel@tonic-gate 538*7c478bd9Sstevel@tonic-gate oversize_bits |= IS_OVERSIZE(head_sz, alloc_size) | 539*7c478bd9Sstevel@tonic-gate IS_OVERSIZE(size, alloc_size) << DATA_SHIFT | 540*7c478bd9Sstevel@tonic-gate IS_OVERSIZE(tail_sz, alloc_size) << TAIL_SHIFT; 541*7c478bd9Sstevel@tonic-gate 542*7c478bd9Sstevel@tonic-gate switch (oversize_bits) { 543*7c478bd9Sstevel@tonic-gate case NONE_OVERSIZE: 544*7c478bd9Sstevel@tonic-gate case DATA_OVERSIZE: 545*7c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, 546*7c478bd9Sstevel@tonic-gate alloc_buf); 547*7c478bd9Sstevel@tonic-gate break; 548*7c478bd9Sstevel@tonic-gate case HEAD_OVERSIZE: 549*7c478bd9Sstevel@tonic-gate /* 550*7c478bd9Sstevel@tonic-gate * If we can extend data > MAX_CACHED and have 551*7c478bd9Sstevel@tonic-gate * head still > MAX_CACHED, we split head-end 552*7c478bd9Sstevel@tonic-gate * as the case of head-end and data oversized, 553*7c478bd9Sstevel@tonic-gate * otherwise just create memalign header. 554*7c478bd9Sstevel@tonic-gate */ 555*7c478bd9Sstevel@tonic-gate tsize = (shift + size) - (MAX_CACHED + 8 + 556*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE); 557*7c478bd9Sstevel@tonic-gate 558*7c478bd9Sstevel@tonic-gate if (!IS_OVERSIZE(tsize, alloc_size)) { 559*7c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, 560*7c478bd9Sstevel@tonic-gate alloc_buf); 561*7c478bd9Sstevel@tonic-gate break; 562*7c478bd9Sstevel@tonic-gate } else { 563*7c478bd9Sstevel@tonic-gate tsize += OVSZ_HEADER_SIZE; 564*7c478bd9Sstevel@tonic-gate taddr = ALIGN((uintptr_t)alloc_buf + 565*7c478bd9Sstevel@tonic-gate tsize, MTMALLOC_MIN_ALIGN); 566*7c478bd9Sstevel@tonic-gate tshift = ret_addr - taddr; 567*7c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, tshift, 568*7c478bd9Sstevel@tonic-gate taddr); 569*7c478bd9Sstevel@tonic-gate ret_addr = taddr; 570*7c478bd9Sstevel@tonic-gate shift = ret_addr - (uintptr_t)alloc_buf; 571*7c478bd9Sstevel@tonic-gate } 572*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 573*7c478bd9Sstevel@tonic-gate case HEAD_AND_DATA_OVERSIZE: 574*7c478bd9Sstevel@tonic-gate /* 575*7c478bd9Sstevel@tonic-gate * Split off the head fragment and 576*7c478bd9Sstevel@tonic-gate * return it back to oversize freelist. 577*7c478bd9Sstevel@tonic-gate * Create oversize header for the piece 578*7c478bd9Sstevel@tonic-gate * of (data + tail fragment). 579*7c478bd9Sstevel@tonic-gate */ 580*7c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 581*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 582*7c478bd9Sstevel@tonic-gate (void) oversize_header_alloc(ret_addr - 583*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE, 584*7c478bd9Sstevel@tonic-gate (orig->size - shift)); 585*7c478bd9Sstevel@tonic-gate orig->size = shift - OVSZ_HEADER_SIZE; 586*7c478bd9Sstevel@tonic-gate 587*7c478bd9Sstevel@tonic-gate /* free up the head fragment */ 588*7c478bd9Sstevel@tonic-gate free_oversize(orig); 589*7c478bd9Sstevel@tonic-gate break; 590*7c478bd9Sstevel@tonic-gate case TAIL_OVERSIZE: 591*7c478bd9Sstevel@tonic-gate /* 592*7c478bd9Sstevel@tonic-gate * If we can extend data > MAX_CACHED and have 593*7c478bd9Sstevel@tonic-gate * tail-end still > MAX_CACHED, we split tail 594*7c478bd9Sstevel@tonic-gate * end, otherwise just create memalign header. 595*7c478bd9Sstevel@tonic-gate */ 596*7c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 597*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 598*7c478bd9Sstevel@tonic-gate tsize = orig->size - (MAX_CACHED + 8 + 599*7c478bd9Sstevel@tonic-gate shift + OVSZ_HEADER_SIZE + 600*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 601*7c478bd9Sstevel@tonic-gate if (!IS_OVERSIZE(tsize, alloc_size)) { 602*7c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, 603*7c478bd9Sstevel@tonic-gate alloc_buf); 604*7c478bd9Sstevel@tonic-gate break; 605*7c478bd9Sstevel@tonic-gate } else { 606*7c478bd9Sstevel@tonic-gate size = MAX_CACHED + 8; 607*7c478bd9Sstevel@tonic-gate } 608*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 609*7c478bd9Sstevel@tonic-gate case DATA_AND_TAIL_OVERSIZE: 610*7c478bd9Sstevel@tonic-gate /* 611*7c478bd9Sstevel@tonic-gate * Split off the tail fragment and 612*7c478bd9Sstevel@tonic-gate * return it back to oversize freelist. 613*7c478bd9Sstevel@tonic-gate * Create memalign header and adjust 614*7c478bd9Sstevel@tonic-gate * the size for the piece of 615*7c478bd9Sstevel@tonic-gate * (head fragment + data). 616*7c478bd9Sstevel@tonic-gate */ 617*7c478bd9Sstevel@tonic-gate taddr = ALIGN(ret_addr + size, 618*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 619*7c478bd9Sstevel@tonic-gate data_sz = (size_t)(taddr - 620*7c478bd9Sstevel@tonic-gate (uintptr_t)alloc_buf); 621*7c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 622*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 623*7c478bd9Sstevel@tonic-gate tsize = orig->size - data_sz; 624*7c478bd9Sstevel@tonic-gate orig->size = data_sz; 625*7c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_buf, shift, 626*7c478bd9Sstevel@tonic-gate alloc_buf); 627*7c478bd9Sstevel@tonic-gate tsize -= OVSZ_HEADER_SIZE; 628*7c478bd9Sstevel@tonic-gate tail = oversize_header_alloc(taddr, tsize); 629*7c478bd9Sstevel@tonic-gate free_oversize(tail); 630*7c478bd9Sstevel@tonic-gate break; 631*7c478bd9Sstevel@tonic-gate case HEAD_AND_TAIL_OVERSIZE: 632*7c478bd9Sstevel@tonic-gate /* 633*7c478bd9Sstevel@tonic-gate * Split off the head fragment. 634*7c478bd9Sstevel@tonic-gate * We try to free up tail-end when we can 635*7c478bd9Sstevel@tonic-gate * extend data size to (MAX_CACHED + 8) 636*7c478bd9Sstevel@tonic-gate * and remain tail-end oversized. 637*7c478bd9Sstevel@tonic-gate * The bottom line is all split pieces 638*7c478bd9Sstevel@tonic-gate * should be oversize in size. 639*7c478bd9Sstevel@tonic-gate */ 640*7c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 641*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 642*7c478bd9Sstevel@tonic-gate tsize = orig->size - (MAX_CACHED + 8 + 643*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE + shift + 644*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 645*7c478bd9Sstevel@tonic-gate 646*7c478bd9Sstevel@tonic-gate if (!IS_OVERSIZE(tsize, alloc_size)) { 647*7c478bd9Sstevel@tonic-gate /* 648*7c478bd9Sstevel@tonic-gate * If the chunk is not big enough 649*7c478bd9Sstevel@tonic-gate * to make both data and tail oversize 650*7c478bd9Sstevel@tonic-gate * we just keep them as one piece. 651*7c478bd9Sstevel@tonic-gate */ 652*7c478bd9Sstevel@tonic-gate (void) oversize_header_alloc(ret_addr - 653*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE, 654*7c478bd9Sstevel@tonic-gate orig->size - shift); 655*7c478bd9Sstevel@tonic-gate orig->size = shift - 656*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE; 657*7c478bd9Sstevel@tonic-gate free_oversize(orig); 658*7c478bd9Sstevel@tonic-gate break; 659*7c478bd9Sstevel@tonic-gate } else { 660*7c478bd9Sstevel@tonic-gate /* 661*7c478bd9Sstevel@tonic-gate * extend data size > MAX_CACHED 662*7c478bd9Sstevel@tonic-gate * and handle it as head, data, tail 663*7c478bd9Sstevel@tonic-gate * are all oversized. 664*7c478bd9Sstevel@tonic-gate */ 665*7c478bd9Sstevel@tonic-gate size = MAX_CACHED + 8; 666*7c478bd9Sstevel@tonic-gate } 667*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 668*7c478bd9Sstevel@tonic-gate case ALL_OVERSIZE: 669*7c478bd9Sstevel@tonic-gate /* 670*7c478bd9Sstevel@tonic-gate * split off the head and tail fragments, 671*7c478bd9Sstevel@tonic-gate * return them back to the oversize freelist. 672*7c478bd9Sstevel@tonic-gate * Alloc oversize header for data seg. 673*7c478bd9Sstevel@tonic-gate */ 674*7c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 675*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 676*7c478bd9Sstevel@tonic-gate tsize = orig->size; 677*7c478bd9Sstevel@tonic-gate orig->size = shift - OVSZ_HEADER_SIZE; 678*7c478bd9Sstevel@tonic-gate free_oversize(orig); 679*7c478bd9Sstevel@tonic-gate 680*7c478bd9Sstevel@tonic-gate taddr = ALIGN(ret_addr + size, 681*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 682*7c478bd9Sstevel@tonic-gate data_sz = taddr - ret_addr; 683*7c478bd9Sstevel@tonic-gate assert(tsize > (shift + data_sz + 684*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE)); 685*7c478bd9Sstevel@tonic-gate tail_sz = tsize - 686*7c478bd9Sstevel@tonic-gate (shift + data_sz + OVSZ_HEADER_SIZE); 687*7c478bd9Sstevel@tonic-gate 688*7c478bd9Sstevel@tonic-gate /* create oversize header for data seg */ 689*7c478bd9Sstevel@tonic-gate (void) oversize_header_alloc(ret_addr - 690*7c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE, data_sz); 691*7c478bd9Sstevel@tonic-gate 692*7c478bd9Sstevel@tonic-gate /* create oversize header for tail fragment */ 693*7c478bd9Sstevel@tonic-gate tail = oversize_header_alloc(taddr, tail_sz); 694*7c478bd9Sstevel@tonic-gate free_oversize(tail); 695*7c478bd9Sstevel@tonic-gate break; 696*7c478bd9Sstevel@tonic-gate default: 697*7c478bd9Sstevel@tonic-gate /* should not reach here */ 698*7c478bd9Sstevel@tonic-gate assert(0); 699*7c478bd9Sstevel@tonic-gate } 700*7c478bd9Sstevel@tonic-gate } 701*7c478bd9Sstevel@tonic-gate return (ret_buf); 702*7c478bd9Sstevel@tonic-gate } 703*7c478bd9Sstevel@tonic-gate 704*7c478bd9Sstevel@tonic-gate 705*7c478bd9Sstevel@tonic-gate void * 706*7c478bd9Sstevel@tonic-gate valloc(size_t size) 707*7c478bd9Sstevel@tonic-gate { 708*7c478bd9Sstevel@tonic-gate static unsigned pagesize; 709*7c478bd9Sstevel@tonic-gate 710*7c478bd9Sstevel@tonic-gate if (size == 0) 711*7c478bd9Sstevel@tonic-gate return (NULL); 712*7c478bd9Sstevel@tonic-gate 713*7c478bd9Sstevel@tonic-gate if (!pagesize) 714*7c478bd9Sstevel@tonic-gate pagesize = sysconf(_SC_PAGESIZE); 715*7c478bd9Sstevel@tonic-gate 716*7c478bd9Sstevel@tonic-gate return (memalign(pagesize, size)); 717*7c478bd9Sstevel@tonic-gate } 718*7c478bd9Sstevel@tonic-gate 719*7c478bd9Sstevel@tonic-gate void 720*7c478bd9Sstevel@tonic-gate mallocctl(int cmd, long value) 721*7c478bd9Sstevel@tonic-gate { 722*7c478bd9Sstevel@tonic-gate switch (cmd) { 723*7c478bd9Sstevel@tonic-gate 724*7c478bd9Sstevel@tonic-gate case MTDEBUGPATTERN: 725*7c478bd9Sstevel@tonic-gate /* 726*7c478bd9Sstevel@tonic-gate * Reinitialize free blocks in case malloc() is called prior 727*7c478bd9Sstevel@tonic-gate * to mallocctl(). 728*7c478bd9Sstevel@tonic-gate */ 729*7c478bd9Sstevel@tonic-gate if (value && !(debugopt & cmd)) { 730*7c478bd9Sstevel@tonic-gate reinit++; 731*7c478bd9Sstevel@tonic-gate debugopt |= cmd; 732*7c478bd9Sstevel@tonic-gate reinit_cpu_list(); 733*7c478bd9Sstevel@tonic-gate } 734*7c478bd9Sstevel@tonic-gate /*FALLTHRU*/ 735*7c478bd9Sstevel@tonic-gate case MTDOUBLEFREE: 736*7c478bd9Sstevel@tonic-gate case MTINITBUFFER: 737*7c478bd9Sstevel@tonic-gate if (value) 738*7c478bd9Sstevel@tonic-gate debugopt |= cmd; 739*7c478bd9Sstevel@tonic-gate else 740*7c478bd9Sstevel@tonic-gate debugopt &= ~cmd; 741*7c478bd9Sstevel@tonic-gate break; 742*7c478bd9Sstevel@tonic-gate case MTCHUNKSIZE: 743*7c478bd9Sstevel@tonic-gate if (value >= MINSIZE && value <= MAXSIZE) 744*7c478bd9Sstevel@tonic-gate requestsize = value; 745*7c478bd9Sstevel@tonic-gate break; 746*7c478bd9Sstevel@tonic-gate default: 747*7c478bd9Sstevel@tonic-gate break; 748*7c478bd9Sstevel@tonic-gate } 749*7c478bd9Sstevel@tonic-gate } 750*7c478bd9Sstevel@tonic-gate 751*7c478bd9Sstevel@tonic-gate /* 752*7c478bd9Sstevel@tonic-gate * if this function is changed, update the fallback code in setup_caches to 753*7c478bd9Sstevel@tonic-gate * set ncpus to the number of possible return values. (currently 1) 754*7c478bd9Sstevel@tonic-gate */ 755*7c478bd9Sstevel@tonic-gate static uint_t 756*7c478bd9Sstevel@tonic-gate fallback_curcpu(void) 757*7c478bd9Sstevel@tonic-gate { 758*7c478bd9Sstevel@tonic-gate return (0); 759*7c478bd9Sstevel@tonic-gate } 760*7c478bd9Sstevel@tonic-gate 761*7c478bd9Sstevel@tonic-gate /* 762*7c478bd9Sstevel@tonic-gate * Returns non-zero on success, zero on failure. 763*7c478bd9Sstevel@tonic-gate * 764*7c478bd9Sstevel@tonic-gate * This carefully doesn't set cpu_list until initialization is finished. 765*7c478bd9Sstevel@tonic-gate */ 766*7c478bd9Sstevel@tonic-gate static int 767*7c478bd9Sstevel@tonic-gate setup_caches(void) 768*7c478bd9Sstevel@tonic-gate { 769*7c478bd9Sstevel@tonic-gate static mutex_t init_lock = DEFAULTMUTEX; 770*7c478bd9Sstevel@tonic-gate 771*7c478bd9Sstevel@tonic-gate uintptr_t oldbrk; 772*7c478bd9Sstevel@tonic-gate uintptr_t newbrk; 773*7c478bd9Sstevel@tonic-gate 774*7c478bd9Sstevel@tonic-gate size_t cache_space_needed; 775*7c478bd9Sstevel@tonic-gate size_t padding; 776*7c478bd9Sstevel@tonic-gate 777*7c478bd9Sstevel@tonic-gate curcpu_func new_curcpu; 778*7c478bd9Sstevel@tonic-gate uint_t new_cpu_mask; 779*7c478bd9Sstevel@tonic-gate percpu_t *new_cpu_list; 780*7c478bd9Sstevel@tonic-gate 781*7c478bd9Sstevel@tonic-gate uint_t i, j; 782*7c478bd9Sstevel@tonic-gate uintptr_t list_addr; 783*7c478bd9Sstevel@tonic-gate 784*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&init_lock); 785*7c478bd9Sstevel@tonic-gate if (cpu_list != NULL) { 786*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&init_lock); 787*7c478bd9Sstevel@tonic-gate return (1); /* success -- already initialized */ 788*7c478bd9Sstevel@tonic-gate } 789*7c478bd9Sstevel@tonic-gate 790*7c478bd9Sstevel@tonic-gate new_curcpu = get_curcpu_func(); 791*7c478bd9Sstevel@tonic-gate if (new_curcpu == NULL) { 792*7c478bd9Sstevel@tonic-gate new_curcpu = fallback_curcpu; 793*7c478bd9Sstevel@tonic-gate ncpus = 1; 794*7c478bd9Sstevel@tonic-gate } else { 795*7c478bd9Sstevel@tonic-gate if ((ncpus = 2 * sysconf(_SC_NPROCESSORS_CONF)) <= 0) 796*7c478bd9Sstevel@tonic-gate ncpus = 4; /* decent default value */ 797*7c478bd9Sstevel@tonic-gate } 798*7c478bd9Sstevel@tonic-gate assert(ncpus > 0); 799*7c478bd9Sstevel@tonic-gate 800*7c478bd9Sstevel@tonic-gate /* round ncpus up to a power of 2 */ 801*7c478bd9Sstevel@tonic-gate while (ncpus & (ncpus - 1)) 802*7c478bd9Sstevel@tonic-gate ncpus++; 803*7c478bd9Sstevel@tonic-gate 804*7c478bd9Sstevel@tonic-gate new_cpu_mask = ncpus - 1; /* create the cpu mask */ 805*7c478bd9Sstevel@tonic-gate 806*7c478bd9Sstevel@tonic-gate /* 807*7c478bd9Sstevel@tonic-gate * We now do some magic with the brk. What we want to get in the 808*7c478bd9Sstevel@tonic-gate * end is a bunch of well-aligned stuff in a big initial allocation. 809*7c478bd9Sstevel@tonic-gate * Along the way, we do sanity checks to make sure no one else has 810*7c478bd9Sstevel@tonic-gate * touched the brk (which shouldn't happen, but it's always good to 811*7c478bd9Sstevel@tonic-gate * check) 812*7c478bd9Sstevel@tonic-gate * 813*7c478bd9Sstevel@tonic-gate * First, make sure sbrk is sane, and store the current brk in oldbrk. 814*7c478bd9Sstevel@tonic-gate */ 815*7c478bd9Sstevel@tonic-gate oldbrk = (uintptr_t)sbrk(0); 816*7c478bd9Sstevel@tonic-gate if ((void *)oldbrk == (void *)-1) { 817*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&init_lock); 818*7c478bd9Sstevel@tonic-gate return (0); /* sbrk is broken -- we're doomed. */ 819*7c478bd9Sstevel@tonic-gate } 820*7c478bd9Sstevel@tonic-gate 821*7c478bd9Sstevel@tonic-gate /* 822*7c478bd9Sstevel@tonic-gate * Now, align the brk to a multiple of CACHE_COHERENCY_UNIT, so that 823*7c478bd9Sstevel@tonic-gate * the percpu structures and cache lists will be properly aligned. 824*7c478bd9Sstevel@tonic-gate * 825*7c478bd9Sstevel@tonic-gate * 2. All hunks will be page-aligned, assuming HUNKSIZE >= PAGESIZE, 826*7c478bd9Sstevel@tonic-gate * so they can be paged out individually. 827*7c478bd9Sstevel@tonic-gate */ 828*7c478bd9Sstevel@tonic-gate newbrk = ALIGN(oldbrk, CACHE_COHERENCY_UNIT); 829*7c478bd9Sstevel@tonic-gate if (newbrk != oldbrk && (uintptr_t)sbrk(newbrk - oldbrk) != oldbrk) { 830*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&init_lock); 831*7c478bd9Sstevel@tonic-gate return (0); /* someone else sbrked */ 832*7c478bd9Sstevel@tonic-gate } 833*7c478bd9Sstevel@tonic-gate 834*7c478bd9Sstevel@tonic-gate /* 835*7c478bd9Sstevel@tonic-gate * For each cpu, there is one percpu_t and a list of caches 836*7c478bd9Sstevel@tonic-gate */ 837*7c478bd9Sstevel@tonic-gate cache_space_needed = ncpus * (sizeof (percpu_t) + CACHELIST_SIZE); 838*7c478bd9Sstevel@tonic-gate 839*7c478bd9Sstevel@tonic-gate new_cpu_list = (percpu_t *)sbrk(cache_space_needed); 840*7c478bd9Sstevel@tonic-gate 841*7c478bd9Sstevel@tonic-gate if (new_cpu_list == (percpu_t *)-1 || 842*7c478bd9Sstevel@tonic-gate (uintptr_t)new_cpu_list != newbrk) { 843*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&init_lock); 844*7c478bd9Sstevel@tonic-gate return (0); /* someone else sbrked */ 845*7c478bd9Sstevel@tonic-gate } 846*7c478bd9Sstevel@tonic-gate 847*7c478bd9Sstevel@tonic-gate /* 848*7c478bd9Sstevel@tonic-gate * Finally, align the brk to HUNKSIZE so that all hunks are 849*7c478bd9Sstevel@tonic-gate * page-aligned, to avoid edge-effects. 850*7c478bd9Sstevel@tonic-gate */ 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate newbrk = (uintptr_t)new_cpu_list + cache_space_needed; 853*7c478bd9Sstevel@tonic-gate 854*7c478bd9Sstevel@tonic-gate padding = ALIGN(newbrk, HUNKSIZE) - newbrk; 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate if (padding > 0 && (uintptr_t)sbrk(padding) != newbrk) { 857*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&init_lock); 858*7c478bd9Sstevel@tonic-gate return (0); /* someone else sbrked */ 859*7c478bd9Sstevel@tonic-gate } 860*7c478bd9Sstevel@tonic-gate 861*7c478bd9Sstevel@tonic-gate list_addr = ((uintptr_t)new_cpu_list + (sizeof (percpu_t) * ncpus)); 862*7c478bd9Sstevel@tonic-gate 863*7c478bd9Sstevel@tonic-gate /* initialize the percpu list */ 864*7c478bd9Sstevel@tonic-gate for (i = 0; i < ncpus; i++) { 865*7c478bd9Sstevel@tonic-gate new_cpu_list[i].mt_caches = (cache_head_t *)list_addr; 866*7c478bd9Sstevel@tonic-gate for (j = 0; j < NUM_CACHES; j++) { 867*7c478bd9Sstevel@tonic-gate new_cpu_list[i].mt_caches[j].mt_cache = NULL; 868*7c478bd9Sstevel@tonic-gate new_cpu_list[i].mt_caches[j].mt_hint = NULL; 869*7c478bd9Sstevel@tonic-gate } 870*7c478bd9Sstevel@tonic-gate 871*7c478bd9Sstevel@tonic-gate bzero(&new_cpu_list[i].mt_parent_lock, sizeof (mutex_t)); 872*7c478bd9Sstevel@tonic-gate 873*7c478bd9Sstevel@tonic-gate /* get the correct cache list alignment */ 874*7c478bd9Sstevel@tonic-gate list_addr += CACHELIST_SIZE; 875*7c478bd9Sstevel@tonic-gate } 876*7c478bd9Sstevel@tonic-gate 877*7c478bd9Sstevel@tonic-gate /* 878*7c478bd9Sstevel@tonic-gate * Initialize oversize listhead 879*7c478bd9Sstevel@tonic-gate */ 880*7c478bd9Sstevel@tonic-gate oversize_list.next_bysize = &oversize_list; 881*7c478bd9Sstevel@tonic-gate oversize_list.prev_bysize = &oversize_list; 882*7c478bd9Sstevel@tonic-gate oversize_list.next_byaddr = &oversize_list; 883*7c478bd9Sstevel@tonic-gate oversize_list.prev_byaddr = &oversize_list; 884*7c478bd9Sstevel@tonic-gate oversize_list.addr = NULL; 885*7c478bd9Sstevel@tonic-gate oversize_list.size = 0; /* sentinal */ 886*7c478bd9Sstevel@tonic-gate 887*7c478bd9Sstevel@tonic-gate /* 888*7c478bd9Sstevel@tonic-gate * now install the global variables, leaving cpu_list for last, so that 889*7c478bd9Sstevel@tonic-gate * there aren't any race conditions. 890*7c478bd9Sstevel@tonic-gate */ 891*7c478bd9Sstevel@tonic-gate curcpu = new_curcpu; 892*7c478bd9Sstevel@tonic-gate cpu_mask = new_cpu_mask; 893*7c478bd9Sstevel@tonic-gate cpu_list = new_cpu_list; 894*7c478bd9Sstevel@tonic-gate 895*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&init_lock); 896*7c478bd9Sstevel@tonic-gate 897*7c478bd9Sstevel@tonic-gate return (1); 898*7c478bd9Sstevel@tonic-gate } 899*7c478bd9Sstevel@tonic-gate 900*7c478bd9Sstevel@tonic-gate static void 901*7c478bd9Sstevel@tonic-gate create_cache(cache_t *cp, size_t size, uint_t chunksize) 902*7c478bd9Sstevel@tonic-gate { 903*7c478bd9Sstevel@tonic-gate long nblocks; 904*7c478bd9Sstevel@tonic-gate 905*7c478bd9Sstevel@tonic-gate bzero(&cp->mt_cache_lock, sizeof (mutex_t)); 906*7c478bd9Sstevel@tonic-gate cp->mt_size = size; 907*7c478bd9Sstevel@tonic-gate cp->mt_freelist = ((caddr_t)cp + sizeof (cache_t)); 908*7c478bd9Sstevel@tonic-gate cp->mt_span = chunksize * HUNKSIZE - sizeof (cache_t); 909*7c478bd9Sstevel@tonic-gate cp->mt_hunks = chunksize; 910*7c478bd9Sstevel@tonic-gate /* 911*7c478bd9Sstevel@tonic-gate * rough calculation. We will need to adjust later. 912*7c478bd9Sstevel@tonic-gate */ 913*7c478bd9Sstevel@tonic-gate nblocks = cp->mt_span / cp->mt_size; 914*7c478bd9Sstevel@tonic-gate nblocks >>= 3; 915*7c478bd9Sstevel@tonic-gate if (nblocks == 0) { /* less than 8 free blocks in this pool */ 916*7c478bd9Sstevel@tonic-gate int32_t numblocks = 0; 917*7c478bd9Sstevel@tonic-gate long i = cp->mt_span; 918*7c478bd9Sstevel@tonic-gate size_t sub = cp->mt_size; 919*7c478bd9Sstevel@tonic-gate uchar_t mask = 0; 920*7c478bd9Sstevel@tonic-gate 921*7c478bd9Sstevel@tonic-gate while (i > sub) { 922*7c478bd9Sstevel@tonic-gate numblocks++; 923*7c478bd9Sstevel@tonic-gate i -= sub; 924*7c478bd9Sstevel@tonic-gate } 925*7c478bd9Sstevel@tonic-gate nblocks = numblocks; 926*7c478bd9Sstevel@tonic-gate cp->mt_arena = (caddr_t)ALIGN(cp->mt_freelist + 8, 8); 927*7c478bd9Sstevel@tonic-gate cp->mt_nfree = numblocks; 928*7c478bd9Sstevel@tonic-gate while (numblocks--) { 929*7c478bd9Sstevel@tonic-gate mask |= 0x80 >> numblocks; 930*7c478bd9Sstevel@tonic-gate } 931*7c478bd9Sstevel@tonic-gate *(cp->mt_freelist) = mask; 932*7c478bd9Sstevel@tonic-gate } else { 933*7c478bd9Sstevel@tonic-gate cp->mt_arena = (caddr_t)ALIGN((caddr_t)cp->mt_freelist + 934*7c478bd9Sstevel@tonic-gate nblocks, 32); 935*7c478bd9Sstevel@tonic-gate /* recompute nblocks */ 936*7c478bd9Sstevel@tonic-gate nblocks = (uintptr_t)((caddr_t)cp->mt_freelist + 937*7c478bd9Sstevel@tonic-gate cp->mt_span - cp->mt_arena) / cp->mt_size; 938*7c478bd9Sstevel@tonic-gate cp->mt_nfree = ((nblocks >> 3) << 3); 939*7c478bd9Sstevel@tonic-gate /* Set everything to free */ 940*7c478bd9Sstevel@tonic-gate (void) memset(cp->mt_freelist, 0xff, nblocks >> 3); 941*7c478bd9Sstevel@tonic-gate } 942*7c478bd9Sstevel@tonic-gate 943*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 944*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, cp->mt_arena, cp->mt_size * nblocks); 945*7c478bd9Sstevel@tonic-gate 946*7c478bd9Sstevel@tonic-gate cp->mt_next = NULL; 947*7c478bd9Sstevel@tonic-gate } 948*7c478bd9Sstevel@tonic-gate 949*7c478bd9Sstevel@tonic-gate static void 950*7c478bd9Sstevel@tonic-gate reinit_cpu_list(void) 951*7c478bd9Sstevel@tonic-gate { 952*7c478bd9Sstevel@tonic-gate oversize_t *wp = oversize_list.next_bysize; 953*7c478bd9Sstevel@tonic-gate percpu_t *cpuptr; 954*7c478bd9Sstevel@tonic-gate cache_t *thiscache; 955*7c478bd9Sstevel@tonic-gate cache_head_t *cachehead; 956*7c478bd9Sstevel@tonic-gate 957*7c478bd9Sstevel@tonic-gate if (wp == NULL || cpu_list == NULL) { 958*7c478bd9Sstevel@tonic-gate reinit = 0; 959*7c478bd9Sstevel@tonic-gate return; 960*7c478bd9Sstevel@tonic-gate } 961*7c478bd9Sstevel@tonic-gate 962*7c478bd9Sstevel@tonic-gate /* Reinitialize free oversize blocks. */ 963*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 964*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 965*7c478bd9Sstevel@tonic-gate for (; wp != &oversize_list; wp = wp->next_bysize) 966*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, wp->addr, wp->size); 967*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 968*7c478bd9Sstevel@tonic-gate 969*7c478bd9Sstevel@tonic-gate /* Reinitialize free blocks. */ 970*7c478bd9Sstevel@tonic-gate for (cpuptr = &cpu_list[0]; cpuptr < &cpu_list[ncpus]; cpuptr++) { 971*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&cpuptr->mt_parent_lock); 972*7c478bd9Sstevel@tonic-gate for (cachehead = &cpuptr->mt_caches[0]; cachehead < 973*7c478bd9Sstevel@tonic-gate &cpuptr->mt_caches[NUM_CACHES]; cachehead++) { 974*7c478bd9Sstevel@tonic-gate for (thiscache = cachehead->mt_cache; thiscache != NULL; 975*7c478bd9Sstevel@tonic-gate thiscache = thiscache->mt_next) { 976*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&thiscache->mt_cache_lock); 977*7c478bd9Sstevel@tonic-gate if (thiscache->mt_nfree == 0) { 978*7c478bd9Sstevel@tonic-gate (void) mutex_unlock( 979*7c478bd9Sstevel@tonic-gate &thiscache->mt_cache_lock); 980*7c478bd9Sstevel@tonic-gate continue; 981*7c478bd9Sstevel@tonic-gate } 982*7c478bd9Sstevel@tonic-gate if (thiscache != NULL) 983*7c478bd9Sstevel@tonic-gate reinit_cache(thiscache); 984*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&thiscache->mt_cache_lock); 985*7c478bd9Sstevel@tonic-gate } 986*7c478bd9Sstevel@tonic-gate } 987*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cpuptr->mt_parent_lock); 988*7c478bd9Sstevel@tonic-gate } 989*7c478bd9Sstevel@tonic-gate reinit = 0; 990*7c478bd9Sstevel@tonic-gate } 991*7c478bd9Sstevel@tonic-gate 992*7c478bd9Sstevel@tonic-gate static void 993*7c478bd9Sstevel@tonic-gate reinit_cache(cache_t *thiscache) 994*7c478bd9Sstevel@tonic-gate { 995*7c478bd9Sstevel@tonic-gate uint32_t *freeblocks; /* not a uintptr_t on purpose */ 996*7c478bd9Sstevel@tonic-gate int32_t i, n; 997*7c478bd9Sstevel@tonic-gate caddr_t ret; 998*7c478bd9Sstevel@tonic-gate 999*7c478bd9Sstevel@tonic-gate freeblocks = (uint32_t *)thiscache->mt_freelist; 1000*7c478bd9Sstevel@tonic-gate while (freeblocks < (uint32_t *)thiscache->mt_arena) { 1001*7c478bd9Sstevel@tonic-gate if (*freeblocks & 0xffffffff) { 1002*7c478bd9Sstevel@tonic-gate for (i = 0; i < 32; i++) { 1003*7c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i)) { 1004*7c478bd9Sstevel@tonic-gate n = (uintptr_t)(((freeblocks - 1005*7c478bd9Sstevel@tonic-gate (uint32_t *)thiscache->mt_freelist) << 5) 1006*7c478bd9Sstevel@tonic-gate + i) * thiscache->mt_size; 1007*7c478bd9Sstevel@tonic-gate ret = thiscache->mt_arena + n; 1008*7c478bd9Sstevel@tonic-gate ret += OVERHEAD; 1009*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, ret, 1010*7c478bd9Sstevel@tonic-gate thiscache->mt_size); 1011*7c478bd9Sstevel@tonic-gate } 1012*7c478bd9Sstevel@tonic-gate } 1013*7c478bd9Sstevel@tonic-gate } 1014*7c478bd9Sstevel@tonic-gate freeblocks++; 1015*7c478bd9Sstevel@tonic-gate } 1016*7c478bd9Sstevel@tonic-gate } 1017*7c478bd9Sstevel@tonic-gate 1018*7c478bd9Sstevel@tonic-gate static void * 1019*7c478bd9Sstevel@tonic-gate malloc_internal(size_t size, percpu_t *cpuptr) 1020*7c478bd9Sstevel@tonic-gate { 1021*7c478bd9Sstevel@tonic-gate cache_head_t *cachehead; 1022*7c478bd9Sstevel@tonic-gate cache_t *thiscache, *hintcache; 1023*7c478bd9Sstevel@tonic-gate int32_t i, n, logsz, bucket; 1024*7c478bd9Sstevel@tonic-gate uint32_t index; 1025*7c478bd9Sstevel@tonic-gate uint32_t *freeblocks; /* not a uintptr_t on purpose */ 1026*7c478bd9Sstevel@tonic-gate caddr_t ret; 1027*7c478bd9Sstevel@tonic-gate 1028*7c478bd9Sstevel@tonic-gate logsz = MIN_CACHED_SHIFT; 1029*7c478bd9Sstevel@tonic-gate 1030*7c478bd9Sstevel@tonic-gate while (size > (1 << logsz)) 1031*7c478bd9Sstevel@tonic-gate logsz++; 1032*7c478bd9Sstevel@tonic-gate 1033*7c478bd9Sstevel@tonic-gate bucket = logsz - MIN_CACHED_SHIFT; 1034*7c478bd9Sstevel@tonic-gate 1035*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&cpuptr->mt_parent_lock); 1036*7c478bd9Sstevel@tonic-gate 1037*7c478bd9Sstevel@tonic-gate /* 1038*7c478bd9Sstevel@tonic-gate * Find a cache of the appropriate size with free buffers. 1039*7c478bd9Sstevel@tonic-gate * 1040*7c478bd9Sstevel@tonic-gate * We don't need to lock each cache as we check their mt_nfree count, 1041*7c478bd9Sstevel@tonic-gate * since: 1042*7c478bd9Sstevel@tonic-gate * 1. We are only looking for caches with mt_nfree > 0. If a 1043*7c478bd9Sstevel@tonic-gate * free happens during our search, it will increment mt_nfree, 1044*7c478bd9Sstevel@tonic-gate * which will not effect the test. 1045*7c478bd9Sstevel@tonic-gate * 2. Allocations can decrement mt_nfree, but they can't happen 1046*7c478bd9Sstevel@tonic-gate * as long as we hold mt_parent_lock. 1047*7c478bd9Sstevel@tonic-gate */ 1048*7c478bd9Sstevel@tonic-gate 1049*7c478bd9Sstevel@tonic-gate cachehead = &cpuptr->mt_caches[bucket]; 1050*7c478bd9Sstevel@tonic-gate 1051*7c478bd9Sstevel@tonic-gate /* Search through the list, starting at the mt_hint */ 1052*7c478bd9Sstevel@tonic-gate thiscache = cachehead->mt_hint; 1053*7c478bd9Sstevel@tonic-gate 1054*7c478bd9Sstevel@tonic-gate while (thiscache != NULL && thiscache->mt_nfree == 0) 1055*7c478bd9Sstevel@tonic-gate thiscache = thiscache->mt_next; 1056*7c478bd9Sstevel@tonic-gate 1057*7c478bd9Sstevel@tonic-gate if (thiscache == NULL) { 1058*7c478bd9Sstevel@tonic-gate /* wrap around -- search up to the hint */ 1059*7c478bd9Sstevel@tonic-gate thiscache = cachehead->mt_cache; 1060*7c478bd9Sstevel@tonic-gate hintcache = cachehead->mt_hint; 1061*7c478bd9Sstevel@tonic-gate 1062*7c478bd9Sstevel@tonic-gate while (thiscache != NULL && thiscache != hintcache && 1063*7c478bd9Sstevel@tonic-gate thiscache->mt_nfree == 0) 1064*7c478bd9Sstevel@tonic-gate thiscache = thiscache->mt_next; 1065*7c478bd9Sstevel@tonic-gate 1066*7c478bd9Sstevel@tonic-gate if (thiscache == hintcache) 1067*7c478bd9Sstevel@tonic-gate thiscache = NULL; 1068*7c478bd9Sstevel@tonic-gate } 1069*7c478bd9Sstevel@tonic-gate 1070*7c478bd9Sstevel@tonic-gate 1071*7c478bd9Sstevel@tonic-gate if (thiscache == NULL) { /* there are no free caches */ 1072*7c478bd9Sstevel@tonic-gate int32_t thisrequest = requestsize; 1073*7c478bd9Sstevel@tonic-gate int32_t buffer_size = (1 << logsz) + OVERHEAD; 1074*7c478bd9Sstevel@tonic-gate 1075*7c478bd9Sstevel@tonic-gate thiscache = (cache_t *)morecore(thisrequest * HUNKSIZE); 1076*7c478bd9Sstevel@tonic-gate 1077*7c478bd9Sstevel@tonic-gate if (thiscache == (cache_t *)-1) { 1078*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cpuptr->mt_parent_lock); 1079*7c478bd9Sstevel@tonic-gate errno = EAGAIN; 1080*7c478bd9Sstevel@tonic-gate return (NULL); 1081*7c478bd9Sstevel@tonic-gate } 1082*7c478bd9Sstevel@tonic-gate create_cache(thiscache, buffer_size, thisrequest); 1083*7c478bd9Sstevel@tonic-gate 1084*7c478bd9Sstevel@tonic-gate /* link in the new block at the beginning of the list */ 1085*7c478bd9Sstevel@tonic-gate thiscache->mt_next = cachehead->mt_cache; 1086*7c478bd9Sstevel@tonic-gate cachehead->mt_cache = thiscache; 1087*7c478bd9Sstevel@tonic-gate } 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate /* update the hint to the cache we found or created */ 1090*7c478bd9Sstevel@tonic-gate cachehead->mt_hint = thiscache; 1091*7c478bd9Sstevel@tonic-gate 1092*7c478bd9Sstevel@tonic-gate /* thiscache now points to a cache with available space */ 1093*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&thiscache->mt_cache_lock); 1094*7c478bd9Sstevel@tonic-gate 1095*7c478bd9Sstevel@tonic-gate freeblocks = (uint32_t *)thiscache->mt_freelist; 1096*7c478bd9Sstevel@tonic-gate while (freeblocks < (uint32_t *)thiscache->mt_arena) { 1097*7c478bd9Sstevel@tonic-gate if (*freeblocks & 0xffffffff) 1098*7c478bd9Sstevel@tonic-gate break; 1099*7c478bd9Sstevel@tonic-gate freeblocks++; 1100*7c478bd9Sstevel@tonic-gate if (freeblocks < (uint32_t *)thiscache->mt_arena && 1101*7c478bd9Sstevel@tonic-gate *freeblocks & 0xffffffff) 1102*7c478bd9Sstevel@tonic-gate break; 1103*7c478bd9Sstevel@tonic-gate freeblocks++; 1104*7c478bd9Sstevel@tonic-gate if (freeblocks < (uint32_t *)thiscache->mt_arena && 1105*7c478bd9Sstevel@tonic-gate *freeblocks & 0xffffffff) 1106*7c478bd9Sstevel@tonic-gate break; 1107*7c478bd9Sstevel@tonic-gate freeblocks++; 1108*7c478bd9Sstevel@tonic-gate if (freeblocks < (uint32_t *)thiscache->mt_arena && 1109*7c478bd9Sstevel@tonic-gate *freeblocks & 0xffffffff) 1110*7c478bd9Sstevel@tonic-gate break; 1111*7c478bd9Sstevel@tonic-gate freeblocks++; 1112*7c478bd9Sstevel@tonic-gate } 1113*7c478bd9Sstevel@tonic-gate 1114*7c478bd9Sstevel@tonic-gate /* 1115*7c478bd9Sstevel@tonic-gate * the offset from mt_freelist to freeblocks is the offset into 1116*7c478bd9Sstevel@tonic-gate * the arena. Be sure to include the offset into freeblocks 1117*7c478bd9Sstevel@tonic-gate * of the bitmask. n is the offset. 1118*7c478bd9Sstevel@tonic-gate */ 1119*7c478bd9Sstevel@tonic-gate for (i = 0; i < 32; ) { 1120*7c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 1121*7c478bd9Sstevel@tonic-gate break; 1122*7c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 1123*7c478bd9Sstevel@tonic-gate break; 1124*7c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 1125*7c478bd9Sstevel@tonic-gate break; 1126*7c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 1127*7c478bd9Sstevel@tonic-gate break; 1128*7c478bd9Sstevel@tonic-gate } 1129*7c478bd9Sstevel@tonic-gate index = 0x80000000 >> --i; 1130*7c478bd9Sstevel@tonic-gate 1131*7c478bd9Sstevel@tonic-gate 1132*7c478bd9Sstevel@tonic-gate *freeblocks &= FLIP_EM(~index); 1133*7c478bd9Sstevel@tonic-gate 1134*7c478bd9Sstevel@tonic-gate thiscache->mt_nfree--; 1135*7c478bd9Sstevel@tonic-gate 1136*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&thiscache->mt_cache_lock); 1137*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cpuptr->mt_parent_lock); 1138*7c478bd9Sstevel@tonic-gate 1139*7c478bd9Sstevel@tonic-gate n = (uintptr_t)(((freeblocks - (uint32_t *)thiscache->mt_freelist) << 5) 1140*7c478bd9Sstevel@tonic-gate + i) * thiscache->mt_size; 1141*7c478bd9Sstevel@tonic-gate /* 1142*7c478bd9Sstevel@tonic-gate * Now you have the offset in n, you've changed the free mask 1143*7c478bd9Sstevel@tonic-gate * in the freelist. Nothing left to do but find the block 1144*7c478bd9Sstevel@tonic-gate * in the arena and put the value of thiscache in the word 1145*7c478bd9Sstevel@tonic-gate * ahead of the handed out address and return the memory 1146*7c478bd9Sstevel@tonic-gate * back to the user. 1147*7c478bd9Sstevel@tonic-gate */ 1148*7c478bd9Sstevel@tonic-gate ret = thiscache->mt_arena + n; 1149*7c478bd9Sstevel@tonic-gate 1150*7c478bd9Sstevel@tonic-gate /* Store the cache addr for this buf. Makes free go fast. */ 1151*7c478bd9Sstevel@tonic-gate *(uintptr_t *)ret = (uintptr_t)thiscache; 1152*7c478bd9Sstevel@tonic-gate 1153*7c478bd9Sstevel@tonic-gate /* 1154*7c478bd9Sstevel@tonic-gate * This assert makes sure we don't hand out memory that is not 1155*7c478bd9Sstevel@tonic-gate * owned by this cache. 1156*7c478bd9Sstevel@tonic-gate */ 1157*7c478bd9Sstevel@tonic-gate assert(ret + thiscache->mt_size <= thiscache->mt_freelist + 1158*7c478bd9Sstevel@tonic-gate thiscache->mt_span); 1159*7c478bd9Sstevel@tonic-gate 1160*7c478bd9Sstevel@tonic-gate ret += OVERHEAD; 1161*7c478bd9Sstevel@tonic-gate 1162*7c478bd9Sstevel@tonic-gate assert(((uintptr_t)ret & 7) == 0); /* are we 8 byte aligned */ 1163*7c478bd9Sstevel@tonic-gate 1164*7c478bd9Sstevel@tonic-gate if (reinit == 0 && (debugopt & MTDEBUGPATTERN)) 1165*7c478bd9Sstevel@tonic-gate if (verify_pattern(FREEPATTERN, ret, size)) 1166*7c478bd9Sstevel@tonic-gate abort(); /* reference after free */ 1167*7c478bd9Sstevel@tonic-gate 1168*7c478bd9Sstevel@tonic-gate if (debugopt & MTINITBUFFER) 1169*7c478bd9Sstevel@tonic-gate copy_pattern(INITPATTERN, ret, size); 1170*7c478bd9Sstevel@tonic-gate return ((void *)ret); 1171*7c478bd9Sstevel@tonic-gate } 1172*7c478bd9Sstevel@tonic-gate 1173*7c478bd9Sstevel@tonic-gate static void * 1174*7c478bd9Sstevel@tonic-gate morecore(size_t bytes) 1175*7c478bd9Sstevel@tonic-gate { 1176*7c478bd9Sstevel@tonic-gate void * ret; 1177*7c478bd9Sstevel@tonic-gate 1178*7c478bd9Sstevel@tonic-gate if (bytes > LONG_MAX) { 1179*7c478bd9Sstevel@tonic-gate intptr_t wad; 1180*7c478bd9Sstevel@tonic-gate /* 1181*7c478bd9Sstevel@tonic-gate * The request size is too big. We need to do this in 1182*7c478bd9Sstevel@tonic-gate * chunks. Sbrk only takes an int for an arg. 1183*7c478bd9Sstevel@tonic-gate */ 1184*7c478bd9Sstevel@tonic-gate if (bytes == ULONG_MAX) 1185*7c478bd9Sstevel@tonic-gate return ((void *)-1); 1186*7c478bd9Sstevel@tonic-gate 1187*7c478bd9Sstevel@tonic-gate ret = sbrk(0); 1188*7c478bd9Sstevel@tonic-gate wad = LONG_MAX; 1189*7c478bd9Sstevel@tonic-gate while (wad > 0) { 1190*7c478bd9Sstevel@tonic-gate if (sbrk(wad) == (void *)-1) { 1191*7c478bd9Sstevel@tonic-gate if (ret != sbrk(0)) 1192*7c478bd9Sstevel@tonic-gate (void) sbrk(-LONG_MAX); 1193*7c478bd9Sstevel@tonic-gate return ((void *)-1); 1194*7c478bd9Sstevel@tonic-gate } 1195*7c478bd9Sstevel@tonic-gate bytes -= LONG_MAX; 1196*7c478bd9Sstevel@tonic-gate wad = bytes; 1197*7c478bd9Sstevel@tonic-gate } 1198*7c478bd9Sstevel@tonic-gate } else 1199*7c478bd9Sstevel@tonic-gate ret = sbrk(bytes); 1200*7c478bd9Sstevel@tonic-gate 1201*7c478bd9Sstevel@tonic-gate return (ret); 1202*7c478bd9Sstevel@tonic-gate } 1203*7c478bd9Sstevel@tonic-gate 1204*7c478bd9Sstevel@tonic-gate 1205*7c478bd9Sstevel@tonic-gate static void * 1206*7c478bd9Sstevel@tonic-gate oversize(size_t size) 1207*7c478bd9Sstevel@tonic-gate { 1208*7c478bd9Sstevel@tonic-gate caddr_t ret; 1209*7c478bd9Sstevel@tonic-gate oversize_t *big; 1210*7c478bd9Sstevel@tonic-gate int bucket; 1211*7c478bd9Sstevel@tonic-gate 1212*7c478bd9Sstevel@tonic-gate /* 1213*7c478bd9Sstevel@tonic-gate * The idea with the global lock is that we are sure to 1214*7c478bd9Sstevel@tonic-gate * block in the kernel anyway since given an oversize alloc 1215*7c478bd9Sstevel@tonic-gate * we are sure to have to call morecore(); 1216*7c478bd9Sstevel@tonic-gate */ 1217*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 1218*7c478bd9Sstevel@tonic-gate 1219*7c478bd9Sstevel@tonic-gate /* 1220*7c478bd9Sstevel@tonic-gate * Since we ensure every address we hand back is 1221*7c478bd9Sstevel@tonic-gate * MTMALLOC_MIN_ALIGN-byte aligned, ALIGNing size ensures that the 1222*7c478bd9Sstevel@tonic-gate * memory handed out is MTMALLOC_MIN_ALIGN-byte aligned at both ends. 1223*7c478bd9Sstevel@tonic-gate * This eases the implementation of MTDEBUGPATTERN and MTINITPATTERN, 1224*7c478bd9Sstevel@tonic-gate * particularly where coalescing occurs. 1225*7c478bd9Sstevel@tonic-gate */ 1226*7c478bd9Sstevel@tonic-gate size = ALIGN(size, MTMALLOC_MIN_ALIGN); 1227*7c478bd9Sstevel@tonic-gate 1228*7c478bd9Sstevel@tonic-gate if ((big = find_oversize(size)) != NULL) { 1229*7c478bd9Sstevel@tonic-gate if (reinit == 0 && (debugopt & MTDEBUGPATTERN)) 1230*7c478bd9Sstevel@tonic-gate if (verify_pattern(FREEPATTERN, big->addr, size)) 1231*7c478bd9Sstevel@tonic-gate abort(); /* reference after free */ 1232*7c478bd9Sstevel@tonic-gate } else { 1233*7c478bd9Sstevel@tonic-gate /* Get more 8-byte aligned memory from heap */ 1234*7c478bd9Sstevel@tonic-gate ret = morecore(size + OVSZ_HEADER_SIZE); 1235*7c478bd9Sstevel@tonic-gate if (ret == (caddr_t)-1) { 1236*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 1237*7c478bd9Sstevel@tonic-gate errno = ENOMEM; 1238*7c478bd9Sstevel@tonic-gate return (NULL); 1239*7c478bd9Sstevel@tonic-gate } 1240*7c478bd9Sstevel@tonic-gate big = oversize_header_alloc((uintptr_t)ret, size); 1241*7c478bd9Sstevel@tonic-gate } 1242*7c478bd9Sstevel@tonic-gate ret = big->addr; 1243*7c478bd9Sstevel@tonic-gate 1244*7c478bd9Sstevel@tonic-gate /* Add big to the hash table at the head of the relevant bucket. */ 1245*7c478bd9Sstevel@tonic-gate bucket = HASH_OVERSIZE(ret); 1246*7c478bd9Sstevel@tonic-gate big->hash_next = ovsz_hashtab[bucket]; 1247*7c478bd9Sstevel@tonic-gate ovsz_hashtab[bucket] = big; 1248*7c478bd9Sstevel@tonic-gate 1249*7c478bd9Sstevel@tonic-gate if (debugopt & MTINITBUFFER) 1250*7c478bd9Sstevel@tonic-gate copy_pattern(INITPATTERN, ret, size); 1251*7c478bd9Sstevel@tonic-gate 1252*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 1253*7c478bd9Sstevel@tonic-gate assert(((uintptr_t)ret & 7) == 0); /* are we 8 byte aligned */ 1254*7c478bd9Sstevel@tonic-gate return ((void *)ret); 1255*7c478bd9Sstevel@tonic-gate } 1256*7c478bd9Sstevel@tonic-gate 1257*7c478bd9Sstevel@tonic-gate static void 1258*7c478bd9Sstevel@tonic-gate insert_oversize(oversize_t *op, oversize_t *nx) 1259*7c478bd9Sstevel@tonic-gate { 1260*7c478bd9Sstevel@tonic-gate oversize_t *sp; 1261*7c478bd9Sstevel@tonic-gate 1262*7c478bd9Sstevel@tonic-gate /* locate correct insertion point in size-ordered list */ 1263*7c478bd9Sstevel@tonic-gate for (sp = oversize_list.next_bysize; 1264*7c478bd9Sstevel@tonic-gate sp != &oversize_list && (op->size > sp->size); 1265*7c478bd9Sstevel@tonic-gate sp = sp->next_bysize) 1266*7c478bd9Sstevel@tonic-gate ; 1267*7c478bd9Sstevel@tonic-gate 1268*7c478bd9Sstevel@tonic-gate /* link into size-ordered list */ 1269*7c478bd9Sstevel@tonic-gate op->next_bysize = sp; 1270*7c478bd9Sstevel@tonic-gate op->prev_bysize = sp->prev_bysize; 1271*7c478bd9Sstevel@tonic-gate op->prev_bysize->next_bysize = op; 1272*7c478bd9Sstevel@tonic-gate op->next_bysize->prev_bysize = op; 1273*7c478bd9Sstevel@tonic-gate 1274*7c478bd9Sstevel@tonic-gate /* 1275*7c478bd9Sstevel@tonic-gate * link item into address-ordered list 1276*7c478bd9Sstevel@tonic-gate * (caller provides insertion point as an optimization) 1277*7c478bd9Sstevel@tonic-gate */ 1278*7c478bd9Sstevel@tonic-gate op->next_byaddr = nx; 1279*7c478bd9Sstevel@tonic-gate op->prev_byaddr = nx->prev_byaddr; 1280*7c478bd9Sstevel@tonic-gate op->prev_byaddr->next_byaddr = op; 1281*7c478bd9Sstevel@tonic-gate op->next_byaddr->prev_byaddr = op; 1282*7c478bd9Sstevel@tonic-gate 1283*7c478bd9Sstevel@tonic-gate } 1284*7c478bd9Sstevel@tonic-gate 1285*7c478bd9Sstevel@tonic-gate static void 1286*7c478bd9Sstevel@tonic-gate unlink_oversize(oversize_t *lp) 1287*7c478bd9Sstevel@tonic-gate { 1288*7c478bd9Sstevel@tonic-gate /* unlink from address list */ 1289*7c478bd9Sstevel@tonic-gate lp->prev_byaddr->next_byaddr = lp->next_byaddr; 1290*7c478bd9Sstevel@tonic-gate lp->next_byaddr->prev_byaddr = lp->prev_byaddr; 1291*7c478bd9Sstevel@tonic-gate 1292*7c478bd9Sstevel@tonic-gate /* unlink from size list */ 1293*7c478bd9Sstevel@tonic-gate lp->prev_bysize->next_bysize = lp->next_bysize; 1294*7c478bd9Sstevel@tonic-gate lp->next_bysize->prev_bysize = lp->prev_bysize; 1295*7c478bd9Sstevel@tonic-gate } 1296*7c478bd9Sstevel@tonic-gate 1297*7c478bd9Sstevel@tonic-gate static void 1298*7c478bd9Sstevel@tonic-gate position_oversize_by_size(oversize_t *op) 1299*7c478bd9Sstevel@tonic-gate { 1300*7c478bd9Sstevel@tonic-gate oversize_t *sp; 1301*7c478bd9Sstevel@tonic-gate 1302*7c478bd9Sstevel@tonic-gate if (op->size > op->next_bysize->size || 1303*7c478bd9Sstevel@tonic-gate op->size < op->prev_bysize->size) { 1304*7c478bd9Sstevel@tonic-gate 1305*7c478bd9Sstevel@tonic-gate /* unlink from size list */ 1306*7c478bd9Sstevel@tonic-gate op->prev_bysize->next_bysize = op->next_bysize; 1307*7c478bd9Sstevel@tonic-gate op->next_bysize->prev_bysize = op->prev_bysize; 1308*7c478bd9Sstevel@tonic-gate 1309*7c478bd9Sstevel@tonic-gate /* locate correct insertion point in size-ordered list */ 1310*7c478bd9Sstevel@tonic-gate for (sp = oversize_list.next_bysize; 1311*7c478bd9Sstevel@tonic-gate sp != &oversize_list && (op->size > sp->size); 1312*7c478bd9Sstevel@tonic-gate sp = sp->next_bysize) 1313*7c478bd9Sstevel@tonic-gate ; 1314*7c478bd9Sstevel@tonic-gate 1315*7c478bd9Sstevel@tonic-gate /* link into size-ordered list */ 1316*7c478bd9Sstevel@tonic-gate op->next_bysize = sp; 1317*7c478bd9Sstevel@tonic-gate op->prev_bysize = sp->prev_bysize; 1318*7c478bd9Sstevel@tonic-gate op->prev_bysize->next_bysize = op; 1319*7c478bd9Sstevel@tonic-gate op->next_bysize->prev_bysize = op; 1320*7c478bd9Sstevel@tonic-gate } 1321*7c478bd9Sstevel@tonic-gate } 1322*7c478bd9Sstevel@tonic-gate 1323*7c478bd9Sstevel@tonic-gate static void 1324*7c478bd9Sstevel@tonic-gate add_oversize(oversize_t *lp) 1325*7c478bd9Sstevel@tonic-gate { 1326*7c478bd9Sstevel@tonic-gate int merge_flags = INSERT_ONLY; 1327*7c478bd9Sstevel@tonic-gate oversize_t *nx; /* ptr to item right of insertion point */ 1328*7c478bd9Sstevel@tonic-gate oversize_t *pv; /* ptr to item left of insertion point */ 1329*7c478bd9Sstevel@tonic-gate uint_t size_lp, size_pv, size_nx; 1330*7c478bd9Sstevel@tonic-gate uintptr_t endp_lp, endp_pv, endp_nx; 1331*7c478bd9Sstevel@tonic-gate 1332*7c478bd9Sstevel@tonic-gate /* 1333*7c478bd9Sstevel@tonic-gate * Locate insertion point in address-ordered list 1334*7c478bd9Sstevel@tonic-gate */ 1335*7c478bd9Sstevel@tonic-gate 1336*7c478bd9Sstevel@tonic-gate for (nx = oversize_list.next_byaddr; 1337*7c478bd9Sstevel@tonic-gate nx != &oversize_list && (lp->addr > nx->addr); 1338*7c478bd9Sstevel@tonic-gate nx = nx->next_byaddr) 1339*7c478bd9Sstevel@tonic-gate ; 1340*7c478bd9Sstevel@tonic-gate 1341*7c478bd9Sstevel@tonic-gate /* 1342*7c478bd9Sstevel@tonic-gate * Determine how to add chunk to oversize freelist 1343*7c478bd9Sstevel@tonic-gate */ 1344*7c478bd9Sstevel@tonic-gate 1345*7c478bd9Sstevel@tonic-gate size_lp = OVSZ_HEADER_SIZE + lp->size; 1346*7c478bd9Sstevel@tonic-gate endp_lp = ALIGN((uintptr_t)lp + size_lp, MTMALLOC_MIN_ALIGN); 1347*7c478bd9Sstevel@tonic-gate size_lp = endp_lp - (uintptr_t)lp; 1348*7c478bd9Sstevel@tonic-gate 1349*7c478bd9Sstevel@tonic-gate pv = nx->prev_byaddr; 1350*7c478bd9Sstevel@tonic-gate 1351*7c478bd9Sstevel@tonic-gate if (pv->size) { 1352*7c478bd9Sstevel@tonic-gate 1353*7c478bd9Sstevel@tonic-gate size_pv = OVSZ_HEADER_SIZE + pv->size; 1354*7c478bd9Sstevel@tonic-gate endp_pv = ALIGN((uintptr_t)pv + size_pv, 1355*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 1356*7c478bd9Sstevel@tonic-gate size_pv = endp_pv - (uintptr_t)pv; 1357*7c478bd9Sstevel@tonic-gate 1358*7c478bd9Sstevel@tonic-gate /* Check for adjacency with left chunk */ 1359*7c478bd9Sstevel@tonic-gate if ((uintptr_t)lp == endp_pv) 1360*7c478bd9Sstevel@tonic-gate merge_flags |= COALESCE_LEFT; 1361*7c478bd9Sstevel@tonic-gate } 1362*7c478bd9Sstevel@tonic-gate 1363*7c478bd9Sstevel@tonic-gate if (nx->size) { 1364*7c478bd9Sstevel@tonic-gate 1365*7c478bd9Sstevel@tonic-gate /* Check for adjacency with right chunk */ 1366*7c478bd9Sstevel@tonic-gate if ((uintptr_t)nx == endp_lp) { 1367*7c478bd9Sstevel@tonic-gate size_nx = OVSZ_HEADER_SIZE + nx->size; 1368*7c478bd9Sstevel@tonic-gate endp_nx = ALIGN((uintptr_t)nx + size_nx, 1369*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 1370*7c478bd9Sstevel@tonic-gate size_nx = endp_nx - (uintptr_t)nx; 1371*7c478bd9Sstevel@tonic-gate merge_flags |= COALESCE_RIGHT; 1372*7c478bd9Sstevel@tonic-gate } 1373*7c478bd9Sstevel@tonic-gate } 1374*7c478bd9Sstevel@tonic-gate 1375*7c478bd9Sstevel@tonic-gate /* 1376*7c478bd9Sstevel@tonic-gate * If MTDEBUGPATTERN==1, lp->addr will have been overwritten with 1377*7c478bd9Sstevel@tonic-gate * FREEPATTERN for lp->size bytes. If we can merge, the oversize 1378*7c478bd9Sstevel@tonic-gate * header(s) that will also become part of the memory available for 1379*7c478bd9Sstevel@tonic-gate * reallocation (ie lp and/or nx) must also be overwritten with 1380*7c478bd9Sstevel@tonic-gate * FREEPATTERN or we will SIGABRT when this memory is next reallocated. 1381*7c478bd9Sstevel@tonic-gate */ 1382*7c478bd9Sstevel@tonic-gate switch (merge_flags) { 1383*7c478bd9Sstevel@tonic-gate 1384*7c478bd9Sstevel@tonic-gate case INSERT_ONLY: /* Coalescing not possible */ 1385*7c478bd9Sstevel@tonic-gate insert_oversize(lp, nx); 1386*7c478bd9Sstevel@tonic-gate break; 1387*7c478bd9Sstevel@tonic-gate case COALESCE_LEFT: 1388*7c478bd9Sstevel@tonic-gate pv->size += size_lp; 1389*7c478bd9Sstevel@tonic-gate position_oversize_by_size(pv); 1390*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 1391*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, lp, OVSZ_HEADER_SIZE); 1392*7c478bd9Sstevel@tonic-gate break; 1393*7c478bd9Sstevel@tonic-gate case COALESCE_RIGHT: 1394*7c478bd9Sstevel@tonic-gate unlink_oversize(nx); 1395*7c478bd9Sstevel@tonic-gate lp->size += size_nx; 1396*7c478bd9Sstevel@tonic-gate insert_oversize(lp, pv->next_byaddr); 1397*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 1398*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, nx, OVSZ_HEADER_SIZE); 1399*7c478bd9Sstevel@tonic-gate break; 1400*7c478bd9Sstevel@tonic-gate case COALESCE_WITH_BOTH_SIDES: /* Merge (with right) to the left */ 1401*7c478bd9Sstevel@tonic-gate pv->size += size_lp + size_nx; 1402*7c478bd9Sstevel@tonic-gate unlink_oversize(nx); 1403*7c478bd9Sstevel@tonic-gate position_oversize_by_size(pv); 1404*7c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) { 1405*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, lp, OVSZ_HEADER_SIZE); 1406*7c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, nx, OVSZ_HEADER_SIZE); 1407*7c478bd9Sstevel@tonic-gate } 1408*7c478bd9Sstevel@tonic-gate break; 1409*7c478bd9Sstevel@tonic-gate } 1410*7c478bd9Sstevel@tonic-gate } 1411*7c478bd9Sstevel@tonic-gate 1412*7c478bd9Sstevel@tonic-gate /* 1413*7c478bd9Sstevel@tonic-gate * Find memory on our list that is at least size big. If we find a block that is 1414*7c478bd9Sstevel@tonic-gate * big enough, we break it up and return the associated oversize_t struct back 1415*7c478bd9Sstevel@tonic-gate * to the calling client. Any leftover piece of that block is returned to the 1416*7c478bd9Sstevel@tonic-gate * freelist. 1417*7c478bd9Sstevel@tonic-gate */ 1418*7c478bd9Sstevel@tonic-gate static oversize_t * 1419*7c478bd9Sstevel@tonic-gate find_oversize(size_t size) 1420*7c478bd9Sstevel@tonic-gate { 1421*7c478bd9Sstevel@tonic-gate oversize_t *wp = oversize_list.next_bysize; 1422*7c478bd9Sstevel@tonic-gate while (wp != &oversize_list && size > wp->size) 1423*7c478bd9Sstevel@tonic-gate wp = wp->next_bysize; 1424*7c478bd9Sstevel@tonic-gate 1425*7c478bd9Sstevel@tonic-gate if (wp == &oversize_list) /* empty list or nothing big enough */ 1426*7c478bd9Sstevel@tonic-gate return (NULL); 1427*7c478bd9Sstevel@tonic-gate /* breaking up a chunk of memory */ 1428*7c478bd9Sstevel@tonic-gate if ((long)((wp->size - (size + OVSZ_HEADER_SIZE + MTMALLOC_MIN_ALIGN))) 1429*7c478bd9Sstevel@tonic-gate > MAX_CACHED) { 1430*7c478bd9Sstevel@tonic-gate caddr_t off; 1431*7c478bd9Sstevel@tonic-gate oversize_t *np; 1432*7c478bd9Sstevel@tonic-gate size_t osize; 1433*7c478bd9Sstevel@tonic-gate off = (caddr_t)ALIGN(wp->addr + size, 1434*7c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 1435*7c478bd9Sstevel@tonic-gate osize = wp->size; 1436*7c478bd9Sstevel@tonic-gate wp->size = (size_t)(off - wp->addr); 1437*7c478bd9Sstevel@tonic-gate np = oversize_header_alloc((uintptr_t)off, 1438*7c478bd9Sstevel@tonic-gate osize - (wp->size + OVSZ_HEADER_SIZE)); 1439*7c478bd9Sstevel@tonic-gate if ((long)np->size < 0) 1440*7c478bd9Sstevel@tonic-gate abort(); 1441*7c478bd9Sstevel@tonic-gate unlink_oversize(wp); 1442*7c478bd9Sstevel@tonic-gate add_oversize(np); 1443*7c478bd9Sstevel@tonic-gate } else { 1444*7c478bd9Sstevel@tonic-gate unlink_oversize(wp); 1445*7c478bd9Sstevel@tonic-gate } 1446*7c478bd9Sstevel@tonic-gate return (wp); 1447*7c478bd9Sstevel@tonic-gate } 1448*7c478bd9Sstevel@tonic-gate 1449*7c478bd9Sstevel@tonic-gate static void 1450*7c478bd9Sstevel@tonic-gate copy_pattern(uint32_t pattern, void *buf_arg, size_t size) 1451*7c478bd9Sstevel@tonic-gate { 1452*7c478bd9Sstevel@tonic-gate uint32_t *bufend = (uint32_t *)((char *)buf_arg + size); 1453*7c478bd9Sstevel@tonic-gate uint32_t *buf = buf_arg; 1454*7c478bd9Sstevel@tonic-gate 1455*7c478bd9Sstevel@tonic-gate while (buf < bufend - 3) { 1456*7c478bd9Sstevel@tonic-gate buf[3] = buf[2] = buf[1] = buf[0] = pattern; 1457*7c478bd9Sstevel@tonic-gate buf += 4; 1458*7c478bd9Sstevel@tonic-gate } 1459*7c478bd9Sstevel@tonic-gate while (buf < bufend) 1460*7c478bd9Sstevel@tonic-gate *buf++ = pattern; 1461*7c478bd9Sstevel@tonic-gate } 1462*7c478bd9Sstevel@tonic-gate 1463*7c478bd9Sstevel@tonic-gate static void * 1464*7c478bd9Sstevel@tonic-gate verify_pattern(uint32_t pattern, void *buf_arg, size_t size) 1465*7c478bd9Sstevel@tonic-gate { 1466*7c478bd9Sstevel@tonic-gate uint32_t *bufend = (uint32_t *)((char *)buf_arg + size); 1467*7c478bd9Sstevel@tonic-gate uint32_t *buf; 1468*7c478bd9Sstevel@tonic-gate 1469*7c478bd9Sstevel@tonic-gate for (buf = buf_arg; buf < bufend; buf++) 1470*7c478bd9Sstevel@tonic-gate if (*buf != pattern) 1471*7c478bd9Sstevel@tonic-gate return (buf); 1472*7c478bd9Sstevel@tonic-gate return (NULL); 1473*7c478bd9Sstevel@tonic-gate } 1474*7c478bd9Sstevel@tonic-gate 1475*7c478bd9Sstevel@tonic-gate static void 1476*7c478bd9Sstevel@tonic-gate free_oversize(oversize_t *ovp) 1477*7c478bd9Sstevel@tonic-gate { 1478*7c478bd9Sstevel@tonic-gate assert(((uintptr_t)ovp->addr & 7) == 0); /* are we 8 byte aligned */ 1479*7c478bd9Sstevel@tonic-gate assert(ovp->size > MAX_CACHED); 1480*7c478bd9Sstevel@tonic-gate 1481*7c478bd9Sstevel@tonic-gate ovp->next_bysize = ovp->prev_bysize = NULL; 1482*7c478bd9Sstevel@tonic-gate ovp->next_byaddr = ovp->prev_byaddr = NULL; 1483*7c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 1484*7c478bd9Sstevel@tonic-gate add_oversize(ovp); 1485*7c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 1486*7c478bd9Sstevel@tonic-gate } 1487*7c478bd9Sstevel@tonic-gate 1488*7c478bd9Sstevel@tonic-gate static oversize_t * 1489*7c478bd9Sstevel@tonic-gate oversize_header_alloc(uintptr_t mem, size_t size) 1490*7c478bd9Sstevel@tonic-gate { 1491*7c478bd9Sstevel@tonic-gate oversize_t *ovsz_hdr; 1492*7c478bd9Sstevel@tonic-gate 1493*7c478bd9Sstevel@tonic-gate assert(size > MAX_CACHED); 1494*7c478bd9Sstevel@tonic-gate 1495*7c478bd9Sstevel@tonic-gate ovsz_hdr = (oversize_t *)mem; 1496*7c478bd9Sstevel@tonic-gate ovsz_hdr->prev_bysize = NULL; 1497*7c478bd9Sstevel@tonic-gate ovsz_hdr->next_bysize = NULL; 1498*7c478bd9Sstevel@tonic-gate ovsz_hdr->prev_byaddr = NULL; 1499*7c478bd9Sstevel@tonic-gate ovsz_hdr->next_byaddr = NULL; 1500*7c478bd9Sstevel@tonic-gate ovsz_hdr->hash_next = NULL; 1501*7c478bd9Sstevel@tonic-gate ovsz_hdr->size = size; 1502*7c478bd9Sstevel@tonic-gate mem += OVSZ_SIZE; 1503*7c478bd9Sstevel@tonic-gate *(uintptr_t *)mem = MTMALLOC_OVERSIZE_MAGIC; 1504*7c478bd9Sstevel@tonic-gate mem += OVERHEAD; 1505*7c478bd9Sstevel@tonic-gate assert(((uintptr_t)mem & 7) == 0); /* are we 8 byte aligned */ 1506*7c478bd9Sstevel@tonic-gate ovsz_hdr->addr = (caddr_t)mem; 1507*7c478bd9Sstevel@tonic-gate return (ovsz_hdr); 1508*7c478bd9Sstevel@tonic-gate } 1509