17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51d530678Sraf * Common Development and Distribution License (the "License"). 61d530678Sraf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 211d530678Sraf 227c478bd9Sstevel@tonic-gate /* 23db3659e5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #include <mtmalloc.h> 287c478bd9Sstevel@tonic-gate #include "mtmalloc_impl.h" 297c478bd9Sstevel@tonic-gate #include <unistd.h> 307c478bd9Sstevel@tonic-gate #include <synch.h> 317c478bd9Sstevel@tonic-gate #include <thread.h> 321d530678Sraf #include <pthread.h> 337c478bd9Sstevel@tonic-gate #include <stdio.h> 347c478bd9Sstevel@tonic-gate #include <limits.h> 357c478bd9Sstevel@tonic-gate #include <errno.h> 367c478bd9Sstevel@tonic-gate #include <string.h> 377c478bd9Sstevel@tonic-gate #include <strings.h> 387c478bd9Sstevel@tonic-gate #include <sys/param.h> 397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate /* 427c478bd9Sstevel@tonic-gate * To turn on the asserts just compile -DDEBUG 437c478bd9Sstevel@tonic-gate */ 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate #ifndef DEBUG 467c478bd9Sstevel@tonic-gate #define NDEBUG 477c478bd9Sstevel@tonic-gate #endif 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include <assert.h> 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate /* 527c478bd9Sstevel@tonic-gate * The MT hot malloc implementation contained herein is designed to be 537c478bd9Sstevel@tonic-gate * plug-compatible with the libc version of malloc. It is not intended 547c478bd9Sstevel@tonic-gate * to replace that implementation until we decide that it is ok to break 557c478bd9Sstevel@tonic-gate * customer apps (Solaris 3.0). 567c478bd9Sstevel@tonic-gate * 577c478bd9Sstevel@tonic-gate * For requests up to 2^^16, the allocator initializes itself into NCPUS 587c478bd9Sstevel@tonic-gate * worth of chains of caches. When a memory request is made, the calling thread 597c478bd9Sstevel@tonic-gate * is vectored into one of NCPUS worth of caches. The LWP id gives us a cheap, 607c478bd9Sstevel@tonic-gate * contention-reducing index to use, eventually, this should be replaced with 617c478bd9Sstevel@tonic-gate * the actual CPU sequence number, when an interface to get it is available. 627c478bd9Sstevel@tonic-gate * 637c478bd9Sstevel@tonic-gate * Once the thread is vectored into one of the list of caches the real 647c478bd9Sstevel@tonic-gate * allocation of the memory begins. The size is determined to figure out which 657c478bd9Sstevel@tonic-gate * bucket the allocation should be satisfied from. The management of free 667c478bd9Sstevel@tonic-gate * buckets is done via a bitmask. A free bucket is represented by a 1. The 677c478bd9Sstevel@tonic-gate * first free bit represents the first free bucket. The position of the bit, 687c478bd9Sstevel@tonic-gate * represents the position of the bucket in the arena. 697c478bd9Sstevel@tonic-gate * 707c478bd9Sstevel@tonic-gate * When the memory from the arena is handed out, the address of the cache 717c478bd9Sstevel@tonic-gate * control structure is written in the word preceeding the returned memory. 727c478bd9Sstevel@tonic-gate * This cache control address is used during free() to mark the buffer free 737c478bd9Sstevel@tonic-gate * in the cache control structure. 747c478bd9Sstevel@tonic-gate * 757c478bd9Sstevel@tonic-gate * When all available memory in a cache has been depleted, a new chunk of memory 767c478bd9Sstevel@tonic-gate * is allocated via sbrk(). The new cache is allocated from this chunk of memory 777c478bd9Sstevel@tonic-gate * and initialized in the function create_cache(). New caches are installed at 787c478bd9Sstevel@tonic-gate * the front of a singly linked list of the same size memory pools. This helps 797c478bd9Sstevel@tonic-gate * to ensure that there will tend to be available memory in the beginning of the 807c478bd9Sstevel@tonic-gate * list. 817c478bd9Sstevel@tonic-gate * 827c478bd9Sstevel@tonic-gate * Long linked lists hurt performance. To decrease this effect, there is a 837c478bd9Sstevel@tonic-gate * tunable, requestsize, that bumps up the sbrk allocation size and thus 847c478bd9Sstevel@tonic-gate * increases the number of available blocks within an arena. We also keep 857c478bd9Sstevel@tonic-gate * a "hint" for each cache list, which is the last cache in the list allocated 867c478bd9Sstevel@tonic-gate * from. This lowers the cost of searching if there are a lot of fully 877c478bd9Sstevel@tonic-gate * allocated blocks at the front of the list. 887c478bd9Sstevel@tonic-gate * 897c478bd9Sstevel@tonic-gate * For requests greater than 2^^16 (oversize allocations), there are two pieces 907c478bd9Sstevel@tonic-gate * of overhead. There is the OVERHEAD used to hold the cache addr 917c478bd9Sstevel@tonic-gate * (&oversize_list), plus an oversize_t structure to further describe the block. 927c478bd9Sstevel@tonic-gate * 937c478bd9Sstevel@tonic-gate * The oversize list is kept as defragmented as possible by coalescing 947c478bd9Sstevel@tonic-gate * freed oversized allocations with adjacent neighbors. 957c478bd9Sstevel@tonic-gate * 967c478bd9Sstevel@tonic-gate * Addresses handed out are stored in a hash table, and are aligned on 977c478bd9Sstevel@tonic-gate * MTMALLOC_MIN_ALIGN-byte boundaries at both ends. Request sizes are rounded-up 987c478bd9Sstevel@tonic-gate * where necessary in order to achieve this. This eases the implementation of 997c478bd9Sstevel@tonic-gate * MTDEBUGPATTERN and MTINITPATTERN, particularly where coalescing occurs. 1007c478bd9Sstevel@tonic-gate * 1017c478bd9Sstevel@tonic-gate * A memalign allocation takes memalign header overhead. There's two 1027c478bd9Sstevel@tonic-gate * types of memalign headers distinguished by MTMALLOC_MEMALIGN_MAGIC 1037c478bd9Sstevel@tonic-gate * and MTMALLOC_MEMALIGN_MIN_MAGIC. When the size of memory taken to 1047c478bd9Sstevel@tonic-gate * get to the aligned address from malloc'ed address is the minimum size 1057c478bd9Sstevel@tonic-gate * OVERHEAD, we create a header taking only one OVERHEAD space with magic 1067c478bd9Sstevel@tonic-gate * number MTMALLOC_MEMALIGN_MIN_MAGIC, and we know by subtracting OVERHEAD 1077c478bd9Sstevel@tonic-gate * from memaligned address, we can get to the malloc'ed address. Otherwise, 1087c478bd9Sstevel@tonic-gate * we create a memalign header taking two OVERHEAD space, one stores 1097c478bd9Sstevel@tonic-gate * MTMALLOC_MEMALIGN_MAGIC magic number, the other one points back to the 1107c478bd9Sstevel@tonic-gate * malloc'ed address. 1117c478bd9Sstevel@tonic-gate */ 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 1147c478bd9Sstevel@tonic-gate #include <arpa/inet.h> /* for htonl() */ 1157c478bd9Sstevel@tonic-gate #endif 1167c478bd9Sstevel@tonic-gate 1177c478bd9Sstevel@tonic-gate static void * morecore(size_t); 1187c478bd9Sstevel@tonic-gate static void create_cache(cache_t *, size_t bufsize, uint_t hunks); 1197c478bd9Sstevel@tonic-gate static void * malloc_internal(size_t, percpu_t *); 1207c478bd9Sstevel@tonic-gate static void * oversize(size_t); 1217c478bd9Sstevel@tonic-gate static oversize_t *find_oversize(size_t); 1227c478bd9Sstevel@tonic-gate static void add_oversize(oversize_t *); 1237c478bd9Sstevel@tonic-gate static void copy_pattern(uint32_t, void *, size_t); 1247c478bd9Sstevel@tonic-gate static void * verify_pattern(uint32_t, void *, size_t); 1257c478bd9Sstevel@tonic-gate static void reinit_cpu_list(void); 1267c478bd9Sstevel@tonic-gate static void reinit_cache(cache_t *); 1277c478bd9Sstevel@tonic-gate static void free_oversize(oversize_t *); 1287c478bd9Sstevel@tonic-gate static oversize_t *oversize_header_alloc(uintptr_t, size_t); 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate /* 1317c478bd9Sstevel@tonic-gate * oversize hash table stuff 1327c478bd9Sstevel@tonic-gate */ 1337c478bd9Sstevel@tonic-gate #define NUM_BUCKETS 67 /* must be prime */ 1347c478bd9Sstevel@tonic-gate #define HASH_OVERSIZE(caddr) ((uintptr_t)(caddr) % NUM_BUCKETS) 1357c478bd9Sstevel@tonic-gate oversize_t *ovsz_hashtab[NUM_BUCKETS]; 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate #define ALIGN(x, a) ((((uintptr_t)(x) + ((uintptr_t)(a) - 1)) \ 1387c478bd9Sstevel@tonic-gate & ~((uintptr_t)(a) - 1))) 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate /* need this to deal with little endianess of x86 */ 1417c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 1427c478bd9Sstevel@tonic-gate #define FLIP_EM(x) htonl((x)) 1437c478bd9Sstevel@tonic-gate #else 1447c478bd9Sstevel@tonic-gate #define FLIP_EM(x) (x) 1457c478bd9Sstevel@tonic-gate #endif 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate #define INSERT_ONLY 0 1487c478bd9Sstevel@tonic-gate #define COALESCE_LEFT 0x00000001 1497c478bd9Sstevel@tonic-gate #define COALESCE_RIGHT 0x00000002 1507c478bd9Sstevel@tonic-gate #define COALESCE_WITH_BOTH_SIDES (COALESCE_LEFT | COALESCE_RIGHT) 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate #define OVERHEAD 8 /* size needed to write cache addr */ 1537c478bd9Sstevel@tonic-gate #define HUNKSIZE 8192 /* just a multiplier */ 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate #define MAX_CACHED_SHIFT 16 /* 64K is the max cached size */ 1567c478bd9Sstevel@tonic-gate #define MAX_CACHED (1 << MAX_CACHED_SHIFT) 1577c478bd9Sstevel@tonic-gate #define MIN_CACHED_SHIFT 4 /* smaller requests rounded up */ 1587c478bd9Sstevel@tonic-gate #define MTMALLOC_MIN_ALIGN 8 /* min guaranteed alignment */ 1597c478bd9Sstevel@tonic-gate 16070911a0dSrm88369 /* maximum size before overflow */ 16170911a0dSrm88369 #define MAX_MTMALLOC (SIZE_MAX - (SIZE_MAX % MTMALLOC_MIN_ALIGN) \ 16270911a0dSrm88369 - OVSZ_HEADER_SIZE) 16370911a0dSrm88369 1647c478bd9Sstevel@tonic-gate #define NUM_CACHES (MAX_CACHED_SHIFT - MIN_CACHED_SHIFT + 1) 1657c478bd9Sstevel@tonic-gate #define CACHELIST_SIZE ALIGN(NUM_CACHES * sizeof (cache_head_t), \ 1667c478bd9Sstevel@tonic-gate CACHE_COHERENCY_UNIT) 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate #define MINSIZE 9 /* for requestsize, tunable */ 1697c478bd9Sstevel@tonic-gate #define MAXSIZE 256 /* arbitrary, big enough, for requestsize */ 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate #define FREEPATTERN 0xdeadbeef /* debug fill pattern for free buf */ 1727c478bd9Sstevel@tonic-gate #define INITPATTERN 0xbaddcafe /* debug fill pattern for new buf */ 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate #define misaligned(p) ((unsigned)(p) & (sizeof (int) - 1)) 1757c478bd9Sstevel@tonic-gate #define IS_OVERSIZE(x, y) (((x) < (y)) && (((x) > MAX_CACHED)? 1 : 0)) 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate static long requestsize = MINSIZE; /* 9 pages per cache; tunable; 9 is min */ 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate static uint_t cpu_mask; 1807c478bd9Sstevel@tonic-gate static curcpu_func curcpu; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate static int32_t debugopt; 1837c478bd9Sstevel@tonic-gate static int32_t reinit; 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate static percpu_t *cpu_list; 1867c478bd9Sstevel@tonic-gate static oversize_t oversize_list; 1871d530678Sraf static mutex_t oversize_lock = DEFAULTMUTEX; 1887c478bd9Sstevel@tonic-gate 1891d530678Sraf static int ncpus = 0; 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate #define MTMALLOC_OVERSIZE_MAGIC ((uintptr_t)&oversize_list) 1927c478bd9Sstevel@tonic-gate #define MTMALLOC_MEMALIGN_MAGIC ((uintptr_t)&oversize_list + 1) 1937c478bd9Sstevel@tonic-gate #define MTMALLOC_MEMALIGN_MIN_MAGIC ((uintptr_t)&oversize_list + 2) 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate /* 1967c478bd9Sstevel@tonic-gate * We require allocations handed out to be aligned on MTMALLOC_MIN_ALIGN-byte 1977c478bd9Sstevel@tonic-gate * boundaries. We round up sizeof (oversize_t) (when necessary) to ensure that 1987c478bd9Sstevel@tonic-gate * this is achieved. 1997c478bd9Sstevel@tonic-gate */ 2007c478bd9Sstevel@tonic-gate #define OVSZ_SIZE (ALIGN(sizeof (oversize_t), MTMALLOC_MIN_ALIGN)) 2017c478bd9Sstevel@tonic-gate #define OVSZ_HEADER_SIZE (OVSZ_SIZE + OVERHEAD) 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate /* 2047c478bd9Sstevel@tonic-gate * memalign header takes 2 OVERHEAD space. One for memalign magic, and the 2057c478bd9Sstevel@tonic-gate * other one points back to the start address of originally allocated space. 2067c478bd9Sstevel@tonic-gate */ 2077c478bd9Sstevel@tonic-gate #define MEMALIGN_HEADER_SIZE 2 * OVERHEAD 2087c478bd9Sstevel@tonic-gate #define MEMALIGN_HEADER_ALLOC(x, shift, malloc_addr)\ 2097c478bd9Sstevel@tonic-gate if (shift == OVERHEAD)\ 2107c478bd9Sstevel@tonic-gate *((uintptr_t *)((caddr_t)x - OVERHEAD)) = \ 2117c478bd9Sstevel@tonic-gate MTMALLOC_MEMALIGN_MIN_MAGIC; \ 2127c478bd9Sstevel@tonic-gate else {\ 2137c478bd9Sstevel@tonic-gate *((uintptr_t *)((caddr_t)x - OVERHEAD)) = \ 2147c478bd9Sstevel@tonic-gate MTMALLOC_MEMALIGN_MAGIC; \ 2157c478bd9Sstevel@tonic-gate *((uintptr_t *)((caddr_t)x - 2 * OVERHEAD)) = \ 2167c478bd9Sstevel@tonic-gate (uintptr_t)malloc_addr; \ 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate 2198f2954faSraf /* 2208f2954faSraf * Add big to the oversize hash table at the head of the relevant bucket. 2218f2954faSraf */ 2228f2954faSraf static void 2238f2954faSraf insert_hash(oversize_t *big) 2248f2954faSraf { 2258f2954faSraf caddr_t ret = big->addr; 2268f2954faSraf int bucket = HASH_OVERSIZE(ret); 2278f2954faSraf 2288f2954faSraf assert(MUTEX_HELD(&oversize_lock)); 2298f2954faSraf big->hash_next = ovsz_hashtab[bucket]; 2308f2954faSraf ovsz_hashtab[bucket] = big; 2318f2954faSraf } 2328f2954faSraf 2337c478bd9Sstevel@tonic-gate void * 2347c478bd9Sstevel@tonic-gate malloc(size_t bytes) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate percpu_t *list_rotor; 2377c478bd9Sstevel@tonic-gate uint_t list_index; 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate if (bytes > MAX_CACHED) 2407c478bd9Sstevel@tonic-gate return (oversize(bytes)); 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate list_index = (curcpu() & cpu_mask); 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate list_rotor = &cpu_list[list_index]; 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate return (malloc_internal(bytes, list_rotor)); 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate void * 2507c478bd9Sstevel@tonic-gate realloc(void * ptr, size_t bytes) 2517c478bd9Sstevel@tonic-gate { 2527c478bd9Sstevel@tonic-gate void *new, *data_ptr; 2537c478bd9Sstevel@tonic-gate cache_t *cacheptr; 2547c478bd9Sstevel@tonic-gate caddr_t mem; 2557c478bd9Sstevel@tonic-gate size_t shift = 0; 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate if (ptr == NULL) 2587c478bd9Sstevel@tonic-gate return (malloc(bytes)); 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate if (bytes == 0) { 2617c478bd9Sstevel@tonic-gate free(ptr); 2627c478bd9Sstevel@tonic-gate return (NULL); 2637c478bd9Sstevel@tonic-gate } 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate data_ptr = ptr; 2667c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 2677c478bd9Sstevel@tonic-gate 268db3659e5SSurya Prakki /* 269db3659e5SSurya Prakki * Optimization possibility : 270db3659e5SSurya Prakki * p = malloc(64); 271db3659e5SSurya Prakki * q = realloc(p, 64); 272db3659e5SSurya Prakki * q can be same as p. 273db3659e5SSurya Prakki * Apply this optimization for the normal 274db3659e5SSurya Prakki * sized caches for now. 275db3659e5SSurya Prakki */ 276db3659e5SSurya Prakki if (*(uintptr_t *)mem < MTMALLOC_OVERSIZE_MAGIC || 277db3659e5SSurya Prakki *(uintptr_t *)mem > MTMALLOC_MEMALIGN_MIN_MAGIC) { 278db3659e5SSurya Prakki cacheptr = (cache_t *)*(uintptr_t *)mem; 279db3659e5SSurya Prakki if (bytes <= (cacheptr->mt_size - OVERHEAD)) 280db3659e5SSurya Prakki return (ptr); 281db3659e5SSurya Prakki } 282db3659e5SSurya Prakki 2837c478bd9Sstevel@tonic-gate new = malloc(bytes); 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate if (new == NULL) 2867c478bd9Sstevel@tonic-gate return (NULL); 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate /* 2897c478bd9Sstevel@tonic-gate * If new == ptr, ptr has previously been freed. Passing a freed pointer 2907c478bd9Sstevel@tonic-gate * to realloc() is not allowed - unless the caller specifically states 2917c478bd9Sstevel@tonic-gate * otherwise, in which case we must avoid freeing ptr (ie new) before we 2927c478bd9Sstevel@tonic-gate * return new. There is (obviously) no requirement to memcpy() ptr to 2937c478bd9Sstevel@tonic-gate * new before we return. 2947c478bd9Sstevel@tonic-gate */ 2957c478bd9Sstevel@tonic-gate if (new == ptr) { 2967c478bd9Sstevel@tonic-gate if (!(debugopt & MTDOUBLEFREE)) 2977c478bd9Sstevel@tonic-gate abort(); 2987c478bd9Sstevel@tonic-gate return (new); 2997c478bd9Sstevel@tonic-gate } 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MAGIC) { 3027c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 3037c478bd9Sstevel@tonic-gate ptr = (void *)*(uintptr_t *)mem; 3047c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 3057c478bd9Sstevel@tonic-gate shift = (size_t)((uintptr_t)data_ptr - (uintptr_t)ptr); 3067c478bd9Sstevel@tonic-gate } else if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MIN_MAGIC) { 3077c478bd9Sstevel@tonic-gate ptr = (void *) mem; 3087c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 3097c478bd9Sstevel@tonic-gate shift = OVERHEAD; 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_OVERSIZE_MAGIC) { 3137c478bd9Sstevel@tonic-gate oversize_t *old; 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate old = (oversize_t *)(mem - OVSZ_SIZE); 3167c478bd9Sstevel@tonic-gate (void) memcpy(new, data_ptr, MIN(bytes, old->size - shift)); 3177c478bd9Sstevel@tonic-gate free(ptr); 3187c478bd9Sstevel@tonic-gate return (new); 3197c478bd9Sstevel@tonic-gate } 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate cacheptr = (cache_t *)*(uintptr_t *)mem; 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate (void) memcpy(new, data_ptr, 3247c478bd9Sstevel@tonic-gate MIN(cacheptr->mt_size - OVERHEAD - shift, bytes)); 3257c478bd9Sstevel@tonic-gate free(ptr); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate return (new); 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate void * 3317c478bd9Sstevel@tonic-gate calloc(size_t nelem, size_t bytes) 3327c478bd9Sstevel@tonic-gate { 3337c478bd9Sstevel@tonic-gate void * ptr; 334*31c6d826SRichard Lowe size_t size; 335*31c6d826SRichard Lowe 336*31c6d826SRichard Lowe if (nelem == 0 || bytes == 0) { 337*31c6d826SRichard Lowe size = 0; 338*31c6d826SRichard Lowe } else { 339*31c6d826SRichard Lowe size = nelem * bytes; 340*31c6d826SRichard Lowe 341*31c6d826SRichard Lowe /* check for overflow */ 342*31c6d826SRichard Lowe if ((size / nelem) != bytes) { 343*31c6d826SRichard Lowe errno = ENOMEM; 344*31c6d826SRichard Lowe return (NULL); 345*31c6d826SRichard Lowe } 346*31c6d826SRichard Lowe } 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate ptr = malloc(size); 3497c478bd9Sstevel@tonic-gate if (ptr == NULL) 3507c478bd9Sstevel@tonic-gate return (NULL); 3511d530678Sraf (void) memset(ptr, 0, size); 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate return (ptr); 3547c478bd9Sstevel@tonic-gate } 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate void 3577c478bd9Sstevel@tonic-gate free(void * ptr) 3587c478bd9Sstevel@tonic-gate { 3597c478bd9Sstevel@tonic-gate cache_t *cacheptr; 3607c478bd9Sstevel@tonic-gate caddr_t mem; 3617c478bd9Sstevel@tonic-gate int32_t i; 3627c478bd9Sstevel@tonic-gate caddr_t freeblocks; 3637c478bd9Sstevel@tonic-gate uintptr_t offset; 3647c478bd9Sstevel@tonic-gate uchar_t mask; 3657c478bd9Sstevel@tonic-gate int32_t which_bit, num_bytes; 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate if (ptr == NULL) 3687c478bd9Sstevel@tonic-gate return; 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MAGIC) { 3737c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 3747c478bd9Sstevel@tonic-gate ptr = (void *)*(uintptr_t *)mem; 3757c478bd9Sstevel@tonic-gate mem = (caddr_t)ptr - OVERHEAD; 3767c478bd9Sstevel@tonic-gate } else if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MIN_MAGIC) { 3777c478bd9Sstevel@tonic-gate ptr = (void *) mem; 3787c478bd9Sstevel@tonic-gate mem -= OVERHEAD; 3797c478bd9Sstevel@tonic-gate } 3807c478bd9Sstevel@tonic-gate 3817c478bd9Sstevel@tonic-gate if (*(uintptr_t *)mem == MTMALLOC_OVERSIZE_MAGIC) { 3827c478bd9Sstevel@tonic-gate oversize_t *big, **opp; 3837c478bd9Sstevel@tonic-gate int bucket; 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate big = (oversize_t *)(mem - OVSZ_SIZE); 3867c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate bucket = HASH_OVERSIZE(big->addr); 3897c478bd9Sstevel@tonic-gate for (opp = &ovsz_hashtab[bucket]; *opp != NULL; 3907c478bd9Sstevel@tonic-gate opp = &(*opp)->hash_next) 3917c478bd9Sstevel@tonic-gate if (*opp == big) 3927c478bd9Sstevel@tonic-gate break; 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate if (*opp == NULL) { 3957c478bd9Sstevel@tonic-gate if (!(debugopt & MTDOUBLEFREE)) 3967c478bd9Sstevel@tonic-gate abort(); 3977c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 3987c478bd9Sstevel@tonic-gate return; 3997c478bd9Sstevel@tonic-gate } 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate *opp = big->hash_next; /* remove big from the hash table */ 4027c478bd9Sstevel@tonic-gate big->hash_next = NULL; 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 4057c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, ptr, big->size); 4067c478bd9Sstevel@tonic-gate add_oversize(big); 4077c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 4087c478bd9Sstevel@tonic-gate return; 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate cacheptr = (cache_t *)*(uintptr_t *)mem; 4127c478bd9Sstevel@tonic-gate freeblocks = cacheptr->mt_freelist; 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate /* 4157c478bd9Sstevel@tonic-gate * This is the distance measured in bits into the arena. 4167c478bd9Sstevel@tonic-gate * The value of offset is in bytes but there is a 1-1 correlation 4177c478bd9Sstevel@tonic-gate * between distance into the arena and distance into the 4187c478bd9Sstevel@tonic-gate * freelist bitmask. 4197c478bd9Sstevel@tonic-gate */ 4207c478bd9Sstevel@tonic-gate offset = mem - cacheptr->mt_arena; 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate /* 4237c478bd9Sstevel@tonic-gate * i is total number of bits to offset into freelist bitmask. 4247c478bd9Sstevel@tonic-gate */ 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate i = offset / cacheptr->mt_size; 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate num_bytes = i >> 3; 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate /* 4317c478bd9Sstevel@tonic-gate * which_bit is the bit offset into the byte in the freelist. 4327c478bd9Sstevel@tonic-gate * if our freelist bitmask looks like 0xf3 and we are freeing 4337c478bd9Sstevel@tonic-gate * block 5 (ie: the 6th block) our mask will be 0xf7 after 4347c478bd9Sstevel@tonic-gate * the free. Things go left to right that's why the mask is 0x80 4357c478bd9Sstevel@tonic-gate * and not 0x01. 4367c478bd9Sstevel@tonic-gate */ 4377c478bd9Sstevel@tonic-gate which_bit = i - (num_bytes << 3); 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate mask = 0x80 >> which_bit; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate freeblocks += num_bytes; 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 4447c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, ptr, cacheptr->mt_size - OVERHEAD); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate (void) mutex_lock(&cacheptr->mt_cache_lock); 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate if (*freeblocks & mask) { 4497c478bd9Sstevel@tonic-gate if (!(debugopt & MTDOUBLEFREE)) 4507c478bd9Sstevel@tonic-gate abort(); 4517c478bd9Sstevel@tonic-gate } else { 4527c478bd9Sstevel@tonic-gate *freeblocks |= mask; 4537c478bd9Sstevel@tonic-gate cacheptr->mt_nfree++; 4547c478bd9Sstevel@tonic-gate } 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cacheptr->mt_cache_lock); 4577c478bd9Sstevel@tonic-gate } 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate void * 4607c478bd9Sstevel@tonic-gate memalign(size_t alignment, size_t size) 4617c478bd9Sstevel@tonic-gate { 4627c478bd9Sstevel@tonic-gate size_t alloc_size; 4637c478bd9Sstevel@tonic-gate uintptr_t offset; 4647c478bd9Sstevel@tonic-gate void *alloc_buf; 4657c478bd9Sstevel@tonic-gate void *ret_buf; 4667c478bd9Sstevel@tonic-gate 467db3659e5SSurya Prakki if (size == 0 || alignment == 0 || misaligned(alignment) || 4687c478bd9Sstevel@tonic-gate (alignment & (alignment - 1)) != 0) { 4697c478bd9Sstevel@tonic-gate errno = EINVAL; 4707c478bd9Sstevel@tonic-gate return (NULL); 4717c478bd9Sstevel@tonic-gate } 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate /* <= MTMALLOC_MIN_ALIGN, malloc can provide directly */ 4747c478bd9Sstevel@tonic-gate if (alignment <= MTMALLOC_MIN_ALIGN) 4757c478bd9Sstevel@tonic-gate return (malloc(size)); 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate alloc_size = size + alignment - MTMALLOC_MIN_ALIGN; 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate if (alloc_size < size) { /* overflow */ 4807c478bd9Sstevel@tonic-gate errno = ENOMEM; 4817c478bd9Sstevel@tonic-gate return (NULL); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate alloc_buf = malloc(alloc_size); 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate if (alloc_buf == NULL) 4877c478bd9Sstevel@tonic-gate /* malloc sets errno */ 4887c478bd9Sstevel@tonic-gate return (NULL); 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate /* 4917c478bd9Sstevel@tonic-gate * If alloc_size > MAX_CACHED, malloc() will have returned a multiple of 4927c478bd9Sstevel@tonic-gate * MTMALLOC_MIN_ALIGN, having rounded-up alloc_size if necessary. Since 4937c478bd9Sstevel@tonic-gate * we will use alloc_size to return the excess fragments to the free 4947c478bd9Sstevel@tonic-gate * list, we also round-up alloc_size if necessary. 4957c478bd9Sstevel@tonic-gate */ 4967c478bd9Sstevel@tonic-gate if ((alloc_size > MAX_CACHED) && 4977c478bd9Sstevel@tonic-gate (alloc_size & (MTMALLOC_MIN_ALIGN - 1))) 4987c478bd9Sstevel@tonic-gate alloc_size = ALIGN(alloc_size, MTMALLOC_MIN_ALIGN); 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate if ((offset = (uintptr_t)alloc_buf & (alignment - 1)) == 0) { 5017c478bd9Sstevel@tonic-gate /* aligned correctly */ 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate size_t frag_size = alloc_size - 5047c478bd9Sstevel@tonic-gate (size + MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE); 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate /* 5077c478bd9Sstevel@tonic-gate * If the leftover piece of the memory > MAX_CACHED, 5087c478bd9Sstevel@tonic-gate * split off the piece and return it back to the freelist. 5097c478bd9Sstevel@tonic-gate */ 5107c478bd9Sstevel@tonic-gate if (IS_OVERSIZE(frag_size, alloc_size)) { 5117c478bd9Sstevel@tonic-gate oversize_t *orig, *tail; 5127c478bd9Sstevel@tonic-gate uintptr_t taddr; 5137c478bd9Sstevel@tonic-gate size_t data_size; 5147c478bd9Sstevel@tonic-gate taddr = ALIGN((uintptr_t)alloc_buf + size, 5157c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 5167c478bd9Sstevel@tonic-gate data_size = taddr - (uintptr_t)alloc_buf; 5177c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 5187c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 5197c478bd9Sstevel@tonic-gate frag_size = orig->size - data_size - 5207c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE; 5217c478bd9Sstevel@tonic-gate orig->size = data_size; 5227c478bd9Sstevel@tonic-gate tail = oversize_header_alloc(taddr, frag_size); 5237c478bd9Sstevel@tonic-gate free_oversize(tail); 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate ret_buf = alloc_buf; 5267c478bd9Sstevel@tonic-gate } else { 5277c478bd9Sstevel@tonic-gate uchar_t oversize_bits = 0; 5287c478bd9Sstevel@tonic-gate size_t head_sz, data_sz, tail_sz; 5297c478bd9Sstevel@tonic-gate uintptr_t ret_addr, taddr, shift, tshift; 5308f2954faSraf oversize_t *orig, *tail, *big; 5317c478bd9Sstevel@tonic-gate size_t tsize; 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate /* needs to be aligned */ 5347c478bd9Sstevel@tonic-gate shift = alignment - offset; 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate assert(shift >= MTMALLOC_MIN_ALIGN); 5377c478bd9Sstevel@tonic-gate 5387c478bd9Sstevel@tonic-gate ret_addr = ((uintptr_t)alloc_buf + shift); 5397c478bd9Sstevel@tonic-gate ret_buf = (void *)ret_addr; 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate if (alloc_size <= MAX_CACHED) { 5427c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, alloc_buf); 5437c478bd9Sstevel@tonic-gate return (ret_buf); 5447c478bd9Sstevel@tonic-gate } 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate /* 5477c478bd9Sstevel@tonic-gate * Only check for the fragments when the memory is allocted 5487c478bd9Sstevel@tonic-gate * from oversize_list. Split off a fragment and return it 5497c478bd9Sstevel@tonic-gate * to the oversize freelist when it's > MAX_CACHED. 5507c478bd9Sstevel@tonic-gate */ 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate head_sz = shift - MAX(MEMALIGN_HEADER_SIZE, OVSZ_HEADER_SIZE); 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate tail_sz = alloc_size - 5557c478bd9Sstevel@tonic-gate (shift + size + MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE); 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate oversize_bits |= IS_OVERSIZE(head_sz, alloc_size) | 5587c478bd9Sstevel@tonic-gate IS_OVERSIZE(size, alloc_size) << DATA_SHIFT | 5597c478bd9Sstevel@tonic-gate IS_OVERSIZE(tail_sz, alloc_size) << TAIL_SHIFT; 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate switch (oversize_bits) { 5627c478bd9Sstevel@tonic-gate case NONE_OVERSIZE: 5637c478bd9Sstevel@tonic-gate case DATA_OVERSIZE: 5647c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, 5657c478bd9Sstevel@tonic-gate alloc_buf); 5667c478bd9Sstevel@tonic-gate break; 5677c478bd9Sstevel@tonic-gate case HEAD_OVERSIZE: 5687c478bd9Sstevel@tonic-gate /* 5697c478bd9Sstevel@tonic-gate * If we can extend data > MAX_CACHED and have 5707c478bd9Sstevel@tonic-gate * head still > MAX_CACHED, we split head-end 5717c478bd9Sstevel@tonic-gate * as the case of head-end and data oversized, 5727c478bd9Sstevel@tonic-gate * otherwise just create memalign header. 5737c478bd9Sstevel@tonic-gate */ 5747c478bd9Sstevel@tonic-gate tsize = (shift + size) - (MAX_CACHED + 8 + 5757c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE); 5767c478bd9Sstevel@tonic-gate 5777c478bd9Sstevel@tonic-gate if (!IS_OVERSIZE(tsize, alloc_size)) { 5787c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, 5797c478bd9Sstevel@tonic-gate alloc_buf); 5807c478bd9Sstevel@tonic-gate break; 5817c478bd9Sstevel@tonic-gate } else { 5827c478bd9Sstevel@tonic-gate tsize += OVSZ_HEADER_SIZE; 5837c478bd9Sstevel@tonic-gate taddr = ALIGN((uintptr_t)alloc_buf + 5847c478bd9Sstevel@tonic-gate tsize, MTMALLOC_MIN_ALIGN); 5857c478bd9Sstevel@tonic-gate tshift = ret_addr - taddr; 5867c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, tshift, 5877c478bd9Sstevel@tonic-gate taddr); 5887c478bd9Sstevel@tonic-gate ret_addr = taddr; 5897c478bd9Sstevel@tonic-gate shift = ret_addr - (uintptr_t)alloc_buf; 5907c478bd9Sstevel@tonic-gate } 5917c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 5927c478bd9Sstevel@tonic-gate case HEAD_AND_DATA_OVERSIZE: 5937c478bd9Sstevel@tonic-gate /* 5947c478bd9Sstevel@tonic-gate * Split off the head fragment and 5957c478bd9Sstevel@tonic-gate * return it back to oversize freelist. 5967c478bd9Sstevel@tonic-gate * Create oversize header for the piece 5977c478bd9Sstevel@tonic-gate * of (data + tail fragment). 5987c478bd9Sstevel@tonic-gate */ 5997c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 6007c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 6018f2954faSraf big = oversize_header_alloc(ret_addr - 602db3659e5SSurya Prakki OVSZ_HEADER_SIZE, (orig->size - shift)); 6038f2954faSraf (void) mutex_lock(&oversize_lock); 6048f2954faSraf insert_hash(big); 6058f2954faSraf (void) mutex_unlock(&oversize_lock); 6067c478bd9Sstevel@tonic-gate orig->size = shift - OVSZ_HEADER_SIZE; 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate /* free up the head fragment */ 6097c478bd9Sstevel@tonic-gate free_oversize(orig); 6107c478bd9Sstevel@tonic-gate break; 6117c478bd9Sstevel@tonic-gate case TAIL_OVERSIZE: 6127c478bd9Sstevel@tonic-gate /* 6137c478bd9Sstevel@tonic-gate * If we can extend data > MAX_CACHED and have 6147c478bd9Sstevel@tonic-gate * tail-end still > MAX_CACHED, we split tail 6157c478bd9Sstevel@tonic-gate * end, otherwise just create memalign header. 6167c478bd9Sstevel@tonic-gate */ 6177c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 6187c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 6197c478bd9Sstevel@tonic-gate tsize = orig->size - (MAX_CACHED + 8 + 6207c478bd9Sstevel@tonic-gate shift + OVSZ_HEADER_SIZE + 6217c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 6227c478bd9Sstevel@tonic-gate if (!IS_OVERSIZE(tsize, alloc_size)) { 6237c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_addr, shift, 6247c478bd9Sstevel@tonic-gate alloc_buf); 6257c478bd9Sstevel@tonic-gate break; 6267c478bd9Sstevel@tonic-gate } else { 6277c478bd9Sstevel@tonic-gate size = MAX_CACHED + 8; 6287c478bd9Sstevel@tonic-gate } 6297c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 6307c478bd9Sstevel@tonic-gate case DATA_AND_TAIL_OVERSIZE: 6317c478bd9Sstevel@tonic-gate /* 6327c478bd9Sstevel@tonic-gate * Split off the tail fragment and 6337c478bd9Sstevel@tonic-gate * return it back to oversize freelist. 6347c478bd9Sstevel@tonic-gate * Create memalign header and adjust 6357c478bd9Sstevel@tonic-gate * the size for the piece of 6367c478bd9Sstevel@tonic-gate * (head fragment + data). 6377c478bd9Sstevel@tonic-gate */ 6387c478bd9Sstevel@tonic-gate taddr = ALIGN(ret_addr + size, 6397c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 6407c478bd9Sstevel@tonic-gate data_sz = (size_t)(taddr - 6417c478bd9Sstevel@tonic-gate (uintptr_t)alloc_buf); 6427c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 6437c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 6447c478bd9Sstevel@tonic-gate tsize = orig->size - data_sz; 6457c478bd9Sstevel@tonic-gate orig->size = data_sz; 6467c478bd9Sstevel@tonic-gate MEMALIGN_HEADER_ALLOC(ret_buf, shift, 6477c478bd9Sstevel@tonic-gate alloc_buf); 6487c478bd9Sstevel@tonic-gate tsize -= OVSZ_HEADER_SIZE; 6497c478bd9Sstevel@tonic-gate tail = oversize_header_alloc(taddr, tsize); 6507c478bd9Sstevel@tonic-gate free_oversize(tail); 6517c478bd9Sstevel@tonic-gate break; 6527c478bd9Sstevel@tonic-gate case HEAD_AND_TAIL_OVERSIZE: 6537c478bd9Sstevel@tonic-gate /* 6547c478bd9Sstevel@tonic-gate * Split off the head fragment. 6557c478bd9Sstevel@tonic-gate * We try to free up tail-end when we can 6567c478bd9Sstevel@tonic-gate * extend data size to (MAX_CACHED + 8) 6577c478bd9Sstevel@tonic-gate * and remain tail-end oversized. 6587c478bd9Sstevel@tonic-gate * The bottom line is all split pieces 6597c478bd9Sstevel@tonic-gate * should be oversize in size. 6607c478bd9Sstevel@tonic-gate */ 6617c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 6627c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 6637c478bd9Sstevel@tonic-gate tsize = orig->size - (MAX_CACHED + 8 + 6647c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE + shift + 6657c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate if (!IS_OVERSIZE(tsize, alloc_size)) { 6687c478bd9Sstevel@tonic-gate /* 6697c478bd9Sstevel@tonic-gate * If the chunk is not big enough 6707c478bd9Sstevel@tonic-gate * to make both data and tail oversize 6717c478bd9Sstevel@tonic-gate * we just keep them as one piece. 6727c478bd9Sstevel@tonic-gate */ 6738f2954faSraf big = oversize_header_alloc(ret_addr - 6747c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE, 6757c478bd9Sstevel@tonic-gate orig->size - shift); 6768f2954faSraf (void) mutex_lock(&oversize_lock); 6778f2954faSraf insert_hash(big); 6788f2954faSraf (void) mutex_unlock(&oversize_lock); 679db3659e5SSurya Prakki orig->size = shift - OVSZ_HEADER_SIZE; 6807c478bd9Sstevel@tonic-gate free_oversize(orig); 6817c478bd9Sstevel@tonic-gate break; 6827c478bd9Sstevel@tonic-gate } else { 6837c478bd9Sstevel@tonic-gate /* 6847c478bd9Sstevel@tonic-gate * extend data size > MAX_CACHED 6857c478bd9Sstevel@tonic-gate * and handle it as head, data, tail 6867c478bd9Sstevel@tonic-gate * are all oversized. 6877c478bd9Sstevel@tonic-gate */ 6887c478bd9Sstevel@tonic-gate size = MAX_CACHED + 8; 6897c478bd9Sstevel@tonic-gate } 6907c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 6917c478bd9Sstevel@tonic-gate case ALL_OVERSIZE: 6927c478bd9Sstevel@tonic-gate /* 6937c478bd9Sstevel@tonic-gate * split off the head and tail fragments, 6947c478bd9Sstevel@tonic-gate * return them back to the oversize freelist. 6957c478bd9Sstevel@tonic-gate * Alloc oversize header for data seg. 6967c478bd9Sstevel@tonic-gate */ 6977c478bd9Sstevel@tonic-gate orig = (oversize_t *)((uintptr_t)alloc_buf - 6987c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE); 6997c478bd9Sstevel@tonic-gate tsize = orig->size; 7007c478bd9Sstevel@tonic-gate orig->size = shift - OVSZ_HEADER_SIZE; 7017c478bd9Sstevel@tonic-gate free_oversize(orig); 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate taddr = ALIGN(ret_addr + size, 7047c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 7057c478bd9Sstevel@tonic-gate data_sz = taddr - ret_addr; 7067c478bd9Sstevel@tonic-gate assert(tsize > (shift + data_sz + 7077c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE)); 7087c478bd9Sstevel@tonic-gate tail_sz = tsize - 7097c478bd9Sstevel@tonic-gate (shift + data_sz + OVSZ_HEADER_SIZE); 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate /* create oversize header for data seg */ 7128f2954faSraf big = oversize_header_alloc(ret_addr - 7137c478bd9Sstevel@tonic-gate OVSZ_HEADER_SIZE, data_sz); 7148f2954faSraf (void) mutex_lock(&oversize_lock); 7158f2954faSraf insert_hash(big); 7168f2954faSraf (void) mutex_unlock(&oversize_lock); 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate /* create oversize header for tail fragment */ 7197c478bd9Sstevel@tonic-gate tail = oversize_header_alloc(taddr, tail_sz); 7207c478bd9Sstevel@tonic-gate free_oversize(tail); 7217c478bd9Sstevel@tonic-gate break; 7227c478bd9Sstevel@tonic-gate default: 7237c478bd9Sstevel@tonic-gate /* should not reach here */ 7247c478bd9Sstevel@tonic-gate assert(0); 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate } 7277c478bd9Sstevel@tonic-gate return (ret_buf); 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate 7317c478bd9Sstevel@tonic-gate void * 7327c478bd9Sstevel@tonic-gate valloc(size_t size) 7337c478bd9Sstevel@tonic-gate { 7347c478bd9Sstevel@tonic-gate static unsigned pagesize; 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate if (size == 0) 7377c478bd9Sstevel@tonic-gate return (NULL); 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate if (!pagesize) 7407c478bd9Sstevel@tonic-gate pagesize = sysconf(_SC_PAGESIZE); 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate return (memalign(pagesize, size)); 7437c478bd9Sstevel@tonic-gate } 7447c478bd9Sstevel@tonic-gate 7457c478bd9Sstevel@tonic-gate void 7467c478bd9Sstevel@tonic-gate mallocctl(int cmd, long value) 7477c478bd9Sstevel@tonic-gate { 7487c478bd9Sstevel@tonic-gate switch (cmd) { 7497c478bd9Sstevel@tonic-gate 7507c478bd9Sstevel@tonic-gate case MTDEBUGPATTERN: 7517c478bd9Sstevel@tonic-gate /* 7527c478bd9Sstevel@tonic-gate * Reinitialize free blocks in case malloc() is called prior 7537c478bd9Sstevel@tonic-gate * to mallocctl(). 7547c478bd9Sstevel@tonic-gate */ 7557c478bd9Sstevel@tonic-gate if (value && !(debugopt & cmd)) { 7567c478bd9Sstevel@tonic-gate reinit++; 7577c478bd9Sstevel@tonic-gate debugopt |= cmd; 7587c478bd9Sstevel@tonic-gate reinit_cpu_list(); 7597c478bd9Sstevel@tonic-gate } 7607c478bd9Sstevel@tonic-gate /*FALLTHRU*/ 7617c478bd9Sstevel@tonic-gate case MTDOUBLEFREE: 7627c478bd9Sstevel@tonic-gate case MTINITBUFFER: 7637c478bd9Sstevel@tonic-gate if (value) 7647c478bd9Sstevel@tonic-gate debugopt |= cmd; 7657c478bd9Sstevel@tonic-gate else 7667c478bd9Sstevel@tonic-gate debugopt &= ~cmd; 7677c478bd9Sstevel@tonic-gate break; 7687c478bd9Sstevel@tonic-gate case MTCHUNKSIZE: 7697c478bd9Sstevel@tonic-gate if (value >= MINSIZE && value <= MAXSIZE) 7707c478bd9Sstevel@tonic-gate requestsize = value; 7717c478bd9Sstevel@tonic-gate break; 7727c478bd9Sstevel@tonic-gate default: 7737c478bd9Sstevel@tonic-gate break; 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate } 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate /* 7781d530678Sraf * Initialization function, called from the init section of the library. 7791d530678Sraf * No locking is required here because we are single-threaded during 7801d530678Sraf * library initialization. 7817c478bd9Sstevel@tonic-gate */ 7821d530678Sraf static void 7837c478bd9Sstevel@tonic-gate setup_caches(void) 7847c478bd9Sstevel@tonic-gate { 7857c478bd9Sstevel@tonic-gate uintptr_t oldbrk; 7867c478bd9Sstevel@tonic-gate uintptr_t newbrk; 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate size_t cache_space_needed; 7897c478bd9Sstevel@tonic-gate size_t padding; 7907c478bd9Sstevel@tonic-gate 7917c478bd9Sstevel@tonic-gate curcpu_func new_curcpu; 7927c478bd9Sstevel@tonic-gate uint_t new_cpu_mask; 7937c478bd9Sstevel@tonic-gate percpu_t *new_cpu_list; 7947c478bd9Sstevel@tonic-gate 7957c478bd9Sstevel@tonic-gate uint_t i, j; 7967c478bd9Sstevel@tonic-gate uintptr_t list_addr; 7977c478bd9Sstevel@tonic-gate 7981d530678Sraf /* 7991d530678Sraf * Get a decent "current cpu identifier", to be used to reduce 8001d530678Sraf * contention. Eventually, this should be replaced by an interface 8011d530678Sraf * to get the actual CPU sequence number in libthread/liblwp. 8021d530678Sraf */ 8031d530678Sraf new_curcpu = (curcpu_func)thr_self; 8047c478bd9Sstevel@tonic-gate if ((ncpus = 2 * sysconf(_SC_NPROCESSORS_CONF)) <= 0) 8057c478bd9Sstevel@tonic-gate ncpus = 4; /* decent default value */ 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate /* round ncpus up to a power of 2 */ 8087c478bd9Sstevel@tonic-gate while (ncpus & (ncpus - 1)) 8097c478bd9Sstevel@tonic-gate ncpus++; 8107c478bd9Sstevel@tonic-gate 8117c478bd9Sstevel@tonic-gate new_cpu_mask = ncpus - 1; /* create the cpu mask */ 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate /* 8147c478bd9Sstevel@tonic-gate * We now do some magic with the brk. What we want to get in the 8157c478bd9Sstevel@tonic-gate * end is a bunch of well-aligned stuff in a big initial allocation. 8167c478bd9Sstevel@tonic-gate * Along the way, we do sanity checks to make sure no one else has 8177c478bd9Sstevel@tonic-gate * touched the brk (which shouldn't happen, but it's always good to 8187c478bd9Sstevel@tonic-gate * check) 8197c478bd9Sstevel@tonic-gate * 8207c478bd9Sstevel@tonic-gate * First, make sure sbrk is sane, and store the current brk in oldbrk. 8217c478bd9Sstevel@tonic-gate */ 8227c478bd9Sstevel@tonic-gate oldbrk = (uintptr_t)sbrk(0); 8231d530678Sraf if ((void *)oldbrk == (void *)-1) 8241d530678Sraf abort(); /* sbrk is broken -- we're doomed. */ 8257c478bd9Sstevel@tonic-gate 8267c478bd9Sstevel@tonic-gate /* 8277c478bd9Sstevel@tonic-gate * Now, align the brk to a multiple of CACHE_COHERENCY_UNIT, so that 8287c478bd9Sstevel@tonic-gate * the percpu structures and cache lists will be properly aligned. 8297c478bd9Sstevel@tonic-gate * 8307c478bd9Sstevel@tonic-gate * 2. All hunks will be page-aligned, assuming HUNKSIZE >= PAGESIZE, 8317c478bd9Sstevel@tonic-gate * so they can be paged out individually. 8327c478bd9Sstevel@tonic-gate */ 8337c478bd9Sstevel@tonic-gate newbrk = ALIGN(oldbrk, CACHE_COHERENCY_UNIT); 8341d530678Sraf if (newbrk != oldbrk && (uintptr_t)sbrk(newbrk - oldbrk) != oldbrk) 8351d530678Sraf abort(); /* sbrk is broken -- we're doomed. */ 8367c478bd9Sstevel@tonic-gate 8377c478bd9Sstevel@tonic-gate /* 8387c478bd9Sstevel@tonic-gate * For each cpu, there is one percpu_t and a list of caches 8397c478bd9Sstevel@tonic-gate */ 8407c478bd9Sstevel@tonic-gate cache_space_needed = ncpus * (sizeof (percpu_t) + CACHELIST_SIZE); 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate new_cpu_list = (percpu_t *)sbrk(cache_space_needed); 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate if (new_cpu_list == (percpu_t *)-1 || 8451d530678Sraf (uintptr_t)new_cpu_list != newbrk) 8461d530678Sraf abort(); /* sbrk is broken -- we're doomed. */ 8477c478bd9Sstevel@tonic-gate 8487c478bd9Sstevel@tonic-gate /* 8497c478bd9Sstevel@tonic-gate * Finally, align the brk to HUNKSIZE so that all hunks are 8507c478bd9Sstevel@tonic-gate * page-aligned, to avoid edge-effects. 8517c478bd9Sstevel@tonic-gate */ 8527c478bd9Sstevel@tonic-gate 8537c478bd9Sstevel@tonic-gate newbrk = (uintptr_t)new_cpu_list + cache_space_needed; 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate padding = ALIGN(newbrk, HUNKSIZE) - newbrk; 8567c478bd9Sstevel@tonic-gate 8571d530678Sraf if (padding > 0 && (uintptr_t)sbrk(padding) != newbrk) 8581d530678Sraf abort(); /* sbrk is broken -- we're doomed. */ 8597c478bd9Sstevel@tonic-gate 8607c478bd9Sstevel@tonic-gate list_addr = ((uintptr_t)new_cpu_list + (sizeof (percpu_t) * ncpus)); 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate /* initialize the percpu list */ 8637c478bd9Sstevel@tonic-gate for (i = 0; i < ncpus; i++) { 8647c478bd9Sstevel@tonic-gate new_cpu_list[i].mt_caches = (cache_head_t *)list_addr; 8657c478bd9Sstevel@tonic-gate for (j = 0; j < NUM_CACHES; j++) { 8667c478bd9Sstevel@tonic-gate new_cpu_list[i].mt_caches[j].mt_cache = NULL; 8677c478bd9Sstevel@tonic-gate new_cpu_list[i].mt_caches[j].mt_hint = NULL; 8687c478bd9Sstevel@tonic-gate } 8697c478bd9Sstevel@tonic-gate 8701d530678Sraf (void) mutex_init(&new_cpu_list[i].mt_parent_lock, 8711d530678Sraf USYNC_THREAD, NULL); 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate /* get the correct cache list alignment */ 8747c478bd9Sstevel@tonic-gate list_addr += CACHELIST_SIZE; 8757c478bd9Sstevel@tonic-gate } 8767c478bd9Sstevel@tonic-gate 8777c478bd9Sstevel@tonic-gate /* 8787c478bd9Sstevel@tonic-gate * Initialize oversize listhead 8797c478bd9Sstevel@tonic-gate */ 8807c478bd9Sstevel@tonic-gate oversize_list.next_bysize = &oversize_list; 8817c478bd9Sstevel@tonic-gate oversize_list.prev_bysize = &oversize_list; 8827c478bd9Sstevel@tonic-gate oversize_list.next_byaddr = &oversize_list; 8837c478bd9Sstevel@tonic-gate oversize_list.prev_byaddr = &oversize_list; 8847c478bd9Sstevel@tonic-gate oversize_list.addr = NULL; 8857c478bd9Sstevel@tonic-gate oversize_list.size = 0; /* sentinal */ 8867c478bd9Sstevel@tonic-gate 8877c478bd9Sstevel@tonic-gate /* 8881d530678Sraf * Now install the global variables. 8897c478bd9Sstevel@tonic-gate */ 8907c478bd9Sstevel@tonic-gate curcpu = new_curcpu; 8917c478bd9Sstevel@tonic-gate cpu_mask = new_cpu_mask; 8927c478bd9Sstevel@tonic-gate cpu_list = new_cpu_list; 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate static void 8967c478bd9Sstevel@tonic-gate create_cache(cache_t *cp, size_t size, uint_t chunksize) 8977c478bd9Sstevel@tonic-gate { 8987c478bd9Sstevel@tonic-gate long nblocks; 8997c478bd9Sstevel@tonic-gate 9001d530678Sraf (void) mutex_init(&cp->mt_cache_lock, USYNC_THREAD, NULL); 9017c478bd9Sstevel@tonic-gate cp->mt_size = size; 9027c478bd9Sstevel@tonic-gate cp->mt_freelist = ((caddr_t)cp + sizeof (cache_t)); 9037c478bd9Sstevel@tonic-gate cp->mt_span = chunksize * HUNKSIZE - sizeof (cache_t); 9047c478bd9Sstevel@tonic-gate cp->mt_hunks = chunksize; 9057c478bd9Sstevel@tonic-gate /* 9067c478bd9Sstevel@tonic-gate * rough calculation. We will need to adjust later. 9077c478bd9Sstevel@tonic-gate */ 9087c478bd9Sstevel@tonic-gate nblocks = cp->mt_span / cp->mt_size; 9097c478bd9Sstevel@tonic-gate nblocks >>= 3; 9107c478bd9Sstevel@tonic-gate if (nblocks == 0) { /* less than 8 free blocks in this pool */ 9117c478bd9Sstevel@tonic-gate int32_t numblocks = 0; 9127c478bd9Sstevel@tonic-gate long i = cp->mt_span; 9137c478bd9Sstevel@tonic-gate size_t sub = cp->mt_size; 9147c478bd9Sstevel@tonic-gate uchar_t mask = 0; 9157c478bd9Sstevel@tonic-gate 9167c478bd9Sstevel@tonic-gate while (i > sub) { 9177c478bd9Sstevel@tonic-gate numblocks++; 9187c478bd9Sstevel@tonic-gate i -= sub; 9197c478bd9Sstevel@tonic-gate } 9207c478bd9Sstevel@tonic-gate nblocks = numblocks; 9217c478bd9Sstevel@tonic-gate cp->mt_arena = (caddr_t)ALIGN(cp->mt_freelist + 8, 8); 9227c478bd9Sstevel@tonic-gate cp->mt_nfree = numblocks; 9237c478bd9Sstevel@tonic-gate while (numblocks--) { 9247c478bd9Sstevel@tonic-gate mask |= 0x80 >> numblocks; 9257c478bd9Sstevel@tonic-gate } 9267c478bd9Sstevel@tonic-gate *(cp->mt_freelist) = mask; 9277c478bd9Sstevel@tonic-gate } else { 9287c478bd9Sstevel@tonic-gate cp->mt_arena = (caddr_t)ALIGN((caddr_t)cp->mt_freelist + 9297c478bd9Sstevel@tonic-gate nblocks, 32); 9307c478bd9Sstevel@tonic-gate /* recompute nblocks */ 9317c478bd9Sstevel@tonic-gate nblocks = (uintptr_t)((caddr_t)cp->mt_freelist + 9327c478bd9Sstevel@tonic-gate cp->mt_span - cp->mt_arena) / cp->mt_size; 9337c478bd9Sstevel@tonic-gate cp->mt_nfree = ((nblocks >> 3) << 3); 9347c478bd9Sstevel@tonic-gate /* Set everything to free */ 9357c478bd9Sstevel@tonic-gate (void) memset(cp->mt_freelist, 0xff, nblocks >> 3); 9367c478bd9Sstevel@tonic-gate } 9377c478bd9Sstevel@tonic-gate 9387c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 9397c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, cp->mt_arena, cp->mt_size * nblocks); 9407c478bd9Sstevel@tonic-gate 9417c478bd9Sstevel@tonic-gate cp->mt_next = NULL; 9427c478bd9Sstevel@tonic-gate } 9437c478bd9Sstevel@tonic-gate 9447c478bd9Sstevel@tonic-gate static void 9457c478bd9Sstevel@tonic-gate reinit_cpu_list(void) 9467c478bd9Sstevel@tonic-gate { 9477c478bd9Sstevel@tonic-gate oversize_t *wp = oversize_list.next_bysize; 9487c478bd9Sstevel@tonic-gate percpu_t *cpuptr; 9497c478bd9Sstevel@tonic-gate cache_t *thiscache; 9507c478bd9Sstevel@tonic-gate cache_head_t *cachehead; 9517c478bd9Sstevel@tonic-gate 9527c478bd9Sstevel@tonic-gate /* Reinitialize free oversize blocks. */ 9537c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 9547c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 9557c478bd9Sstevel@tonic-gate for (; wp != &oversize_list; wp = wp->next_bysize) 9567c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, wp->addr, wp->size); 9577c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate /* Reinitialize free blocks. */ 9607c478bd9Sstevel@tonic-gate for (cpuptr = &cpu_list[0]; cpuptr < &cpu_list[ncpus]; cpuptr++) { 9617c478bd9Sstevel@tonic-gate (void) mutex_lock(&cpuptr->mt_parent_lock); 9627c478bd9Sstevel@tonic-gate for (cachehead = &cpuptr->mt_caches[0]; cachehead < 9637c478bd9Sstevel@tonic-gate &cpuptr->mt_caches[NUM_CACHES]; cachehead++) { 9647c478bd9Sstevel@tonic-gate for (thiscache = cachehead->mt_cache; thiscache != NULL; 9657c478bd9Sstevel@tonic-gate thiscache = thiscache->mt_next) { 9667c478bd9Sstevel@tonic-gate (void) mutex_lock(&thiscache->mt_cache_lock); 9677c478bd9Sstevel@tonic-gate if (thiscache->mt_nfree == 0) { 9687c478bd9Sstevel@tonic-gate (void) mutex_unlock( 9697c478bd9Sstevel@tonic-gate &thiscache->mt_cache_lock); 9707c478bd9Sstevel@tonic-gate continue; 9717c478bd9Sstevel@tonic-gate } 9727c478bd9Sstevel@tonic-gate if (thiscache != NULL) 9737c478bd9Sstevel@tonic-gate reinit_cache(thiscache); 9747c478bd9Sstevel@tonic-gate (void) mutex_unlock(&thiscache->mt_cache_lock); 9757c478bd9Sstevel@tonic-gate } 9767c478bd9Sstevel@tonic-gate } 9777c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cpuptr->mt_parent_lock); 9787c478bd9Sstevel@tonic-gate } 9797c478bd9Sstevel@tonic-gate reinit = 0; 9807c478bd9Sstevel@tonic-gate } 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate static void 9837c478bd9Sstevel@tonic-gate reinit_cache(cache_t *thiscache) 9847c478bd9Sstevel@tonic-gate { 9857c478bd9Sstevel@tonic-gate uint32_t *freeblocks; /* not a uintptr_t on purpose */ 9867c478bd9Sstevel@tonic-gate int32_t i, n; 9877c478bd9Sstevel@tonic-gate caddr_t ret; 9887c478bd9Sstevel@tonic-gate 9897c478bd9Sstevel@tonic-gate freeblocks = (uint32_t *)thiscache->mt_freelist; 9907c478bd9Sstevel@tonic-gate while (freeblocks < (uint32_t *)thiscache->mt_arena) { 9917c478bd9Sstevel@tonic-gate if (*freeblocks & 0xffffffff) { 9927c478bd9Sstevel@tonic-gate for (i = 0; i < 32; i++) { 9937c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i)) { 9947c478bd9Sstevel@tonic-gate n = (uintptr_t)(((freeblocks - 995db3659e5SSurya Prakki (uint32_t *)thiscache->mt_freelist) 996db3659e5SSurya Prakki << 5) + i) * thiscache->mt_size; 9977c478bd9Sstevel@tonic-gate ret = thiscache->mt_arena + n; 9987c478bd9Sstevel@tonic-gate ret += OVERHEAD; 9997c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, ret, 10007c478bd9Sstevel@tonic-gate thiscache->mt_size); 10017c478bd9Sstevel@tonic-gate } 10027c478bd9Sstevel@tonic-gate } 10037c478bd9Sstevel@tonic-gate } 10047c478bd9Sstevel@tonic-gate freeblocks++; 10057c478bd9Sstevel@tonic-gate } 10067c478bd9Sstevel@tonic-gate } 10077c478bd9Sstevel@tonic-gate 10087c478bd9Sstevel@tonic-gate static void * 10097c478bd9Sstevel@tonic-gate malloc_internal(size_t size, percpu_t *cpuptr) 10107c478bd9Sstevel@tonic-gate { 10117c478bd9Sstevel@tonic-gate cache_head_t *cachehead; 10127c478bd9Sstevel@tonic-gate cache_t *thiscache, *hintcache; 10137c478bd9Sstevel@tonic-gate int32_t i, n, logsz, bucket; 10147c478bd9Sstevel@tonic-gate uint32_t index; 10157c478bd9Sstevel@tonic-gate uint32_t *freeblocks; /* not a uintptr_t on purpose */ 10167c478bd9Sstevel@tonic-gate caddr_t ret; 10177c478bd9Sstevel@tonic-gate 10187c478bd9Sstevel@tonic-gate logsz = MIN_CACHED_SHIFT; 10197c478bd9Sstevel@tonic-gate 10207c478bd9Sstevel@tonic-gate while (size > (1 << logsz)) 10217c478bd9Sstevel@tonic-gate logsz++; 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate bucket = logsz - MIN_CACHED_SHIFT; 10247c478bd9Sstevel@tonic-gate 10257c478bd9Sstevel@tonic-gate (void) mutex_lock(&cpuptr->mt_parent_lock); 10267c478bd9Sstevel@tonic-gate 10277c478bd9Sstevel@tonic-gate /* 10287c478bd9Sstevel@tonic-gate * Find a cache of the appropriate size with free buffers. 10297c478bd9Sstevel@tonic-gate * 10307c478bd9Sstevel@tonic-gate * We don't need to lock each cache as we check their mt_nfree count, 10317c478bd9Sstevel@tonic-gate * since: 10327c478bd9Sstevel@tonic-gate * 1. We are only looking for caches with mt_nfree > 0. If a 10337c478bd9Sstevel@tonic-gate * free happens during our search, it will increment mt_nfree, 10347c478bd9Sstevel@tonic-gate * which will not effect the test. 10357c478bd9Sstevel@tonic-gate * 2. Allocations can decrement mt_nfree, but they can't happen 10367c478bd9Sstevel@tonic-gate * as long as we hold mt_parent_lock. 10377c478bd9Sstevel@tonic-gate */ 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate cachehead = &cpuptr->mt_caches[bucket]; 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate /* Search through the list, starting at the mt_hint */ 10427c478bd9Sstevel@tonic-gate thiscache = cachehead->mt_hint; 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate while (thiscache != NULL && thiscache->mt_nfree == 0) 10457c478bd9Sstevel@tonic-gate thiscache = thiscache->mt_next; 10467c478bd9Sstevel@tonic-gate 10477c478bd9Sstevel@tonic-gate if (thiscache == NULL) { 10487c478bd9Sstevel@tonic-gate /* wrap around -- search up to the hint */ 10497c478bd9Sstevel@tonic-gate thiscache = cachehead->mt_cache; 10507c478bd9Sstevel@tonic-gate hintcache = cachehead->mt_hint; 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate while (thiscache != NULL && thiscache != hintcache && 10537c478bd9Sstevel@tonic-gate thiscache->mt_nfree == 0) 10547c478bd9Sstevel@tonic-gate thiscache = thiscache->mt_next; 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate if (thiscache == hintcache) 10577c478bd9Sstevel@tonic-gate thiscache = NULL; 10587c478bd9Sstevel@tonic-gate } 10597c478bd9Sstevel@tonic-gate 10607c478bd9Sstevel@tonic-gate 10617c478bd9Sstevel@tonic-gate if (thiscache == NULL) { /* there are no free caches */ 10627c478bd9Sstevel@tonic-gate int32_t thisrequest = requestsize; 10637c478bd9Sstevel@tonic-gate int32_t buffer_size = (1 << logsz) + OVERHEAD; 10647c478bd9Sstevel@tonic-gate 10657c478bd9Sstevel@tonic-gate thiscache = (cache_t *)morecore(thisrequest * HUNKSIZE); 10667c478bd9Sstevel@tonic-gate 10677c478bd9Sstevel@tonic-gate if (thiscache == (cache_t *)-1) { 10687c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cpuptr->mt_parent_lock); 10697c478bd9Sstevel@tonic-gate errno = EAGAIN; 10707c478bd9Sstevel@tonic-gate return (NULL); 10717c478bd9Sstevel@tonic-gate } 10727c478bd9Sstevel@tonic-gate create_cache(thiscache, buffer_size, thisrequest); 10737c478bd9Sstevel@tonic-gate 10747c478bd9Sstevel@tonic-gate /* link in the new block at the beginning of the list */ 10757c478bd9Sstevel@tonic-gate thiscache->mt_next = cachehead->mt_cache; 10767c478bd9Sstevel@tonic-gate cachehead->mt_cache = thiscache; 10777c478bd9Sstevel@tonic-gate } 10787c478bd9Sstevel@tonic-gate 10797c478bd9Sstevel@tonic-gate /* update the hint to the cache we found or created */ 10807c478bd9Sstevel@tonic-gate cachehead->mt_hint = thiscache; 10817c478bd9Sstevel@tonic-gate 10827c478bd9Sstevel@tonic-gate /* thiscache now points to a cache with available space */ 10837c478bd9Sstevel@tonic-gate (void) mutex_lock(&thiscache->mt_cache_lock); 10847c478bd9Sstevel@tonic-gate 10857c478bd9Sstevel@tonic-gate freeblocks = (uint32_t *)thiscache->mt_freelist; 10867c478bd9Sstevel@tonic-gate while (freeblocks < (uint32_t *)thiscache->mt_arena) { 10877c478bd9Sstevel@tonic-gate if (*freeblocks & 0xffffffff) 10887c478bd9Sstevel@tonic-gate break; 10897c478bd9Sstevel@tonic-gate freeblocks++; 10907c478bd9Sstevel@tonic-gate if (freeblocks < (uint32_t *)thiscache->mt_arena && 10917c478bd9Sstevel@tonic-gate *freeblocks & 0xffffffff) 10927c478bd9Sstevel@tonic-gate break; 10937c478bd9Sstevel@tonic-gate freeblocks++; 10947c478bd9Sstevel@tonic-gate if (freeblocks < (uint32_t *)thiscache->mt_arena && 10957c478bd9Sstevel@tonic-gate *freeblocks & 0xffffffff) 10967c478bd9Sstevel@tonic-gate break; 10977c478bd9Sstevel@tonic-gate freeblocks++; 10987c478bd9Sstevel@tonic-gate if (freeblocks < (uint32_t *)thiscache->mt_arena && 10997c478bd9Sstevel@tonic-gate *freeblocks & 0xffffffff) 11007c478bd9Sstevel@tonic-gate break; 11017c478bd9Sstevel@tonic-gate freeblocks++; 11027c478bd9Sstevel@tonic-gate } 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate /* 11057c478bd9Sstevel@tonic-gate * the offset from mt_freelist to freeblocks is the offset into 11067c478bd9Sstevel@tonic-gate * the arena. Be sure to include the offset into freeblocks 11077c478bd9Sstevel@tonic-gate * of the bitmask. n is the offset. 11087c478bd9Sstevel@tonic-gate */ 11097c478bd9Sstevel@tonic-gate for (i = 0; i < 32; ) { 11107c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 11117c478bd9Sstevel@tonic-gate break; 11127c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 11137c478bd9Sstevel@tonic-gate break; 11147c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 11157c478bd9Sstevel@tonic-gate break; 11167c478bd9Sstevel@tonic-gate if (FLIP_EM(*freeblocks) & (0x80000000 >> i++)) 11177c478bd9Sstevel@tonic-gate break; 11187c478bd9Sstevel@tonic-gate } 11197c478bd9Sstevel@tonic-gate index = 0x80000000 >> --i; 11207c478bd9Sstevel@tonic-gate 11217c478bd9Sstevel@tonic-gate 11227c478bd9Sstevel@tonic-gate *freeblocks &= FLIP_EM(~index); 11237c478bd9Sstevel@tonic-gate 11247c478bd9Sstevel@tonic-gate thiscache->mt_nfree--; 11257c478bd9Sstevel@tonic-gate 11267c478bd9Sstevel@tonic-gate (void) mutex_unlock(&thiscache->mt_cache_lock); 11277c478bd9Sstevel@tonic-gate (void) mutex_unlock(&cpuptr->mt_parent_lock); 11287c478bd9Sstevel@tonic-gate 11297c478bd9Sstevel@tonic-gate n = (uintptr_t)(((freeblocks - (uint32_t *)thiscache->mt_freelist) << 5) 11307c478bd9Sstevel@tonic-gate + i) * thiscache->mt_size; 11317c478bd9Sstevel@tonic-gate /* 11327c478bd9Sstevel@tonic-gate * Now you have the offset in n, you've changed the free mask 11337c478bd9Sstevel@tonic-gate * in the freelist. Nothing left to do but find the block 11347c478bd9Sstevel@tonic-gate * in the arena and put the value of thiscache in the word 11357c478bd9Sstevel@tonic-gate * ahead of the handed out address and return the memory 11367c478bd9Sstevel@tonic-gate * back to the user. 11377c478bd9Sstevel@tonic-gate */ 11387c478bd9Sstevel@tonic-gate ret = thiscache->mt_arena + n; 11397c478bd9Sstevel@tonic-gate 11407c478bd9Sstevel@tonic-gate /* Store the cache addr for this buf. Makes free go fast. */ 11417c478bd9Sstevel@tonic-gate *(uintptr_t *)ret = (uintptr_t)thiscache; 11427c478bd9Sstevel@tonic-gate 11437c478bd9Sstevel@tonic-gate /* 11447c478bd9Sstevel@tonic-gate * This assert makes sure we don't hand out memory that is not 11457c478bd9Sstevel@tonic-gate * owned by this cache. 11467c478bd9Sstevel@tonic-gate */ 11477c478bd9Sstevel@tonic-gate assert(ret + thiscache->mt_size <= thiscache->mt_freelist + 11487c478bd9Sstevel@tonic-gate thiscache->mt_span); 11497c478bd9Sstevel@tonic-gate 11507c478bd9Sstevel@tonic-gate ret += OVERHEAD; 11517c478bd9Sstevel@tonic-gate 11527c478bd9Sstevel@tonic-gate assert(((uintptr_t)ret & 7) == 0); /* are we 8 byte aligned */ 11537c478bd9Sstevel@tonic-gate 11547c478bd9Sstevel@tonic-gate if (reinit == 0 && (debugopt & MTDEBUGPATTERN)) 11557c478bd9Sstevel@tonic-gate if (verify_pattern(FREEPATTERN, ret, size)) 11567c478bd9Sstevel@tonic-gate abort(); /* reference after free */ 11577c478bd9Sstevel@tonic-gate 11587c478bd9Sstevel@tonic-gate if (debugopt & MTINITBUFFER) 11597c478bd9Sstevel@tonic-gate copy_pattern(INITPATTERN, ret, size); 11607c478bd9Sstevel@tonic-gate return ((void *)ret); 11617c478bd9Sstevel@tonic-gate } 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate static void * 11647c478bd9Sstevel@tonic-gate morecore(size_t bytes) 11657c478bd9Sstevel@tonic-gate { 11667c478bd9Sstevel@tonic-gate void * ret; 11677c478bd9Sstevel@tonic-gate 11687c478bd9Sstevel@tonic-gate if (bytes > LONG_MAX) { 11697c478bd9Sstevel@tonic-gate intptr_t wad; 11707c478bd9Sstevel@tonic-gate /* 11717c478bd9Sstevel@tonic-gate * The request size is too big. We need to do this in 11727c478bd9Sstevel@tonic-gate * chunks. Sbrk only takes an int for an arg. 11737c478bd9Sstevel@tonic-gate */ 11747c478bd9Sstevel@tonic-gate if (bytes == ULONG_MAX) 11757c478bd9Sstevel@tonic-gate return ((void *)-1); 11767c478bd9Sstevel@tonic-gate 11777c478bd9Sstevel@tonic-gate ret = sbrk(0); 11787c478bd9Sstevel@tonic-gate wad = LONG_MAX; 11797c478bd9Sstevel@tonic-gate while (wad > 0) { 11807c478bd9Sstevel@tonic-gate if (sbrk(wad) == (void *)-1) { 11817c478bd9Sstevel@tonic-gate if (ret != sbrk(0)) 11827c478bd9Sstevel@tonic-gate (void) sbrk(-LONG_MAX); 11837c478bd9Sstevel@tonic-gate return ((void *)-1); 11847c478bd9Sstevel@tonic-gate } 11857c478bd9Sstevel@tonic-gate bytes -= LONG_MAX; 11867c478bd9Sstevel@tonic-gate wad = bytes; 11877c478bd9Sstevel@tonic-gate } 11887c478bd9Sstevel@tonic-gate } else 11897c478bd9Sstevel@tonic-gate ret = sbrk(bytes); 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate return (ret); 11927c478bd9Sstevel@tonic-gate } 11937c478bd9Sstevel@tonic-gate 11947c478bd9Sstevel@tonic-gate 11957c478bd9Sstevel@tonic-gate static void * 11967c478bd9Sstevel@tonic-gate oversize(size_t size) 11977c478bd9Sstevel@tonic-gate { 11987c478bd9Sstevel@tonic-gate caddr_t ret; 11997c478bd9Sstevel@tonic-gate oversize_t *big; 12007c478bd9Sstevel@tonic-gate 120170911a0dSrm88369 /* make sure we will not overflow */ 120270911a0dSrm88369 if (size > MAX_MTMALLOC) { 120370911a0dSrm88369 errno = ENOMEM; 120470911a0dSrm88369 return (NULL); 120570911a0dSrm88369 } 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate /* 12087c478bd9Sstevel@tonic-gate * Since we ensure every address we hand back is 12097c478bd9Sstevel@tonic-gate * MTMALLOC_MIN_ALIGN-byte aligned, ALIGNing size ensures that the 12107c478bd9Sstevel@tonic-gate * memory handed out is MTMALLOC_MIN_ALIGN-byte aligned at both ends. 12117c478bd9Sstevel@tonic-gate * This eases the implementation of MTDEBUGPATTERN and MTINITPATTERN, 12127c478bd9Sstevel@tonic-gate * particularly where coalescing occurs. 12137c478bd9Sstevel@tonic-gate */ 12147c478bd9Sstevel@tonic-gate size = ALIGN(size, MTMALLOC_MIN_ALIGN); 12157c478bd9Sstevel@tonic-gate 121670911a0dSrm88369 /* 121770911a0dSrm88369 * The idea with the global lock is that we are sure to 121870911a0dSrm88369 * block in the kernel anyway since given an oversize alloc 121970911a0dSrm88369 * we are sure to have to call morecore(); 122070911a0dSrm88369 */ 122170911a0dSrm88369 (void) mutex_lock(&oversize_lock); 122270911a0dSrm88369 12237c478bd9Sstevel@tonic-gate if ((big = find_oversize(size)) != NULL) { 12247c478bd9Sstevel@tonic-gate if (reinit == 0 && (debugopt & MTDEBUGPATTERN)) 12257c478bd9Sstevel@tonic-gate if (verify_pattern(FREEPATTERN, big->addr, size)) 12267c478bd9Sstevel@tonic-gate abort(); /* reference after free */ 12277c478bd9Sstevel@tonic-gate } else { 12287c478bd9Sstevel@tonic-gate /* Get more 8-byte aligned memory from heap */ 12297c478bd9Sstevel@tonic-gate ret = morecore(size + OVSZ_HEADER_SIZE); 12307c478bd9Sstevel@tonic-gate if (ret == (caddr_t)-1) { 12317c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 12327c478bd9Sstevel@tonic-gate errno = ENOMEM; 12337c478bd9Sstevel@tonic-gate return (NULL); 12347c478bd9Sstevel@tonic-gate } 12357c478bd9Sstevel@tonic-gate big = oversize_header_alloc((uintptr_t)ret, size); 12367c478bd9Sstevel@tonic-gate } 12377c478bd9Sstevel@tonic-gate ret = big->addr; 12387c478bd9Sstevel@tonic-gate 12398f2954faSraf insert_hash(big); 12407c478bd9Sstevel@tonic-gate 12417c478bd9Sstevel@tonic-gate if (debugopt & MTINITBUFFER) 12427c478bd9Sstevel@tonic-gate copy_pattern(INITPATTERN, ret, size); 12437c478bd9Sstevel@tonic-gate 12447c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 12457c478bd9Sstevel@tonic-gate assert(((uintptr_t)ret & 7) == 0); /* are we 8 byte aligned */ 12467c478bd9Sstevel@tonic-gate return ((void *)ret); 12477c478bd9Sstevel@tonic-gate } 12487c478bd9Sstevel@tonic-gate 12497c478bd9Sstevel@tonic-gate static void 12507c478bd9Sstevel@tonic-gate insert_oversize(oversize_t *op, oversize_t *nx) 12517c478bd9Sstevel@tonic-gate { 12527c478bd9Sstevel@tonic-gate oversize_t *sp; 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate /* locate correct insertion point in size-ordered list */ 12557c478bd9Sstevel@tonic-gate for (sp = oversize_list.next_bysize; 12567c478bd9Sstevel@tonic-gate sp != &oversize_list && (op->size > sp->size); 12577c478bd9Sstevel@tonic-gate sp = sp->next_bysize) 12587c478bd9Sstevel@tonic-gate ; 12597c478bd9Sstevel@tonic-gate 12607c478bd9Sstevel@tonic-gate /* link into size-ordered list */ 12617c478bd9Sstevel@tonic-gate op->next_bysize = sp; 12627c478bd9Sstevel@tonic-gate op->prev_bysize = sp->prev_bysize; 12637c478bd9Sstevel@tonic-gate op->prev_bysize->next_bysize = op; 12647c478bd9Sstevel@tonic-gate op->next_bysize->prev_bysize = op; 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate /* 12677c478bd9Sstevel@tonic-gate * link item into address-ordered list 12687c478bd9Sstevel@tonic-gate * (caller provides insertion point as an optimization) 12697c478bd9Sstevel@tonic-gate */ 12707c478bd9Sstevel@tonic-gate op->next_byaddr = nx; 12717c478bd9Sstevel@tonic-gate op->prev_byaddr = nx->prev_byaddr; 12727c478bd9Sstevel@tonic-gate op->prev_byaddr->next_byaddr = op; 12737c478bd9Sstevel@tonic-gate op->next_byaddr->prev_byaddr = op; 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate } 12767c478bd9Sstevel@tonic-gate 12777c478bd9Sstevel@tonic-gate static void 12787c478bd9Sstevel@tonic-gate unlink_oversize(oversize_t *lp) 12797c478bd9Sstevel@tonic-gate { 12807c478bd9Sstevel@tonic-gate /* unlink from address list */ 12817c478bd9Sstevel@tonic-gate lp->prev_byaddr->next_byaddr = lp->next_byaddr; 12827c478bd9Sstevel@tonic-gate lp->next_byaddr->prev_byaddr = lp->prev_byaddr; 12837c478bd9Sstevel@tonic-gate 12847c478bd9Sstevel@tonic-gate /* unlink from size list */ 12857c478bd9Sstevel@tonic-gate lp->prev_bysize->next_bysize = lp->next_bysize; 12867c478bd9Sstevel@tonic-gate lp->next_bysize->prev_bysize = lp->prev_bysize; 12877c478bd9Sstevel@tonic-gate } 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate static void 12907c478bd9Sstevel@tonic-gate position_oversize_by_size(oversize_t *op) 12917c478bd9Sstevel@tonic-gate { 12927c478bd9Sstevel@tonic-gate oversize_t *sp; 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate if (op->size > op->next_bysize->size || 12957c478bd9Sstevel@tonic-gate op->size < op->prev_bysize->size) { 12967c478bd9Sstevel@tonic-gate 12977c478bd9Sstevel@tonic-gate /* unlink from size list */ 12987c478bd9Sstevel@tonic-gate op->prev_bysize->next_bysize = op->next_bysize; 12997c478bd9Sstevel@tonic-gate op->next_bysize->prev_bysize = op->prev_bysize; 13007c478bd9Sstevel@tonic-gate 13017c478bd9Sstevel@tonic-gate /* locate correct insertion point in size-ordered list */ 13027c478bd9Sstevel@tonic-gate for (sp = oversize_list.next_bysize; 13037c478bd9Sstevel@tonic-gate sp != &oversize_list && (op->size > sp->size); 13047c478bd9Sstevel@tonic-gate sp = sp->next_bysize) 13057c478bd9Sstevel@tonic-gate ; 13067c478bd9Sstevel@tonic-gate 13077c478bd9Sstevel@tonic-gate /* link into size-ordered list */ 13087c478bd9Sstevel@tonic-gate op->next_bysize = sp; 13097c478bd9Sstevel@tonic-gate op->prev_bysize = sp->prev_bysize; 13107c478bd9Sstevel@tonic-gate op->prev_bysize->next_bysize = op; 13117c478bd9Sstevel@tonic-gate op->next_bysize->prev_bysize = op; 13127c478bd9Sstevel@tonic-gate } 13137c478bd9Sstevel@tonic-gate } 13147c478bd9Sstevel@tonic-gate 13157c478bd9Sstevel@tonic-gate static void 13167c478bd9Sstevel@tonic-gate add_oversize(oversize_t *lp) 13177c478bd9Sstevel@tonic-gate { 13187c478bd9Sstevel@tonic-gate int merge_flags = INSERT_ONLY; 13197c478bd9Sstevel@tonic-gate oversize_t *nx; /* ptr to item right of insertion point */ 13207c478bd9Sstevel@tonic-gate oversize_t *pv; /* ptr to item left of insertion point */ 13217c478bd9Sstevel@tonic-gate uint_t size_lp, size_pv, size_nx; 13227c478bd9Sstevel@tonic-gate uintptr_t endp_lp, endp_pv, endp_nx; 13237c478bd9Sstevel@tonic-gate 13247c478bd9Sstevel@tonic-gate /* 13257c478bd9Sstevel@tonic-gate * Locate insertion point in address-ordered list 13267c478bd9Sstevel@tonic-gate */ 13277c478bd9Sstevel@tonic-gate 13287c478bd9Sstevel@tonic-gate for (nx = oversize_list.next_byaddr; 13297c478bd9Sstevel@tonic-gate nx != &oversize_list && (lp->addr > nx->addr); 13307c478bd9Sstevel@tonic-gate nx = nx->next_byaddr) 13317c478bd9Sstevel@tonic-gate ; 13327c478bd9Sstevel@tonic-gate 13337c478bd9Sstevel@tonic-gate /* 13347c478bd9Sstevel@tonic-gate * Determine how to add chunk to oversize freelist 13357c478bd9Sstevel@tonic-gate */ 13367c478bd9Sstevel@tonic-gate 13377c478bd9Sstevel@tonic-gate size_lp = OVSZ_HEADER_SIZE + lp->size; 13387c478bd9Sstevel@tonic-gate endp_lp = ALIGN((uintptr_t)lp + size_lp, MTMALLOC_MIN_ALIGN); 13397c478bd9Sstevel@tonic-gate size_lp = endp_lp - (uintptr_t)lp; 13407c478bd9Sstevel@tonic-gate 13417c478bd9Sstevel@tonic-gate pv = nx->prev_byaddr; 13427c478bd9Sstevel@tonic-gate 13437c478bd9Sstevel@tonic-gate if (pv->size) { 13447c478bd9Sstevel@tonic-gate 13457c478bd9Sstevel@tonic-gate size_pv = OVSZ_HEADER_SIZE + pv->size; 13467c478bd9Sstevel@tonic-gate endp_pv = ALIGN((uintptr_t)pv + size_pv, 13477c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 13487c478bd9Sstevel@tonic-gate size_pv = endp_pv - (uintptr_t)pv; 13497c478bd9Sstevel@tonic-gate 13507c478bd9Sstevel@tonic-gate /* Check for adjacency with left chunk */ 13517c478bd9Sstevel@tonic-gate if ((uintptr_t)lp == endp_pv) 13527c478bd9Sstevel@tonic-gate merge_flags |= COALESCE_LEFT; 13537c478bd9Sstevel@tonic-gate } 13547c478bd9Sstevel@tonic-gate 13557c478bd9Sstevel@tonic-gate if (nx->size) { 13567c478bd9Sstevel@tonic-gate 13577c478bd9Sstevel@tonic-gate /* Check for adjacency with right chunk */ 13587c478bd9Sstevel@tonic-gate if ((uintptr_t)nx == endp_lp) { 13597c478bd9Sstevel@tonic-gate size_nx = OVSZ_HEADER_SIZE + nx->size; 13607c478bd9Sstevel@tonic-gate endp_nx = ALIGN((uintptr_t)nx + size_nx, 13617c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 13627c478bd9Sstevel@tonic-gate size_nx = endp_nx - (uintptr_t)nx; 13637c478bd9Sstevel@tonic-gate merge_flags |= COALESCE_RIGHT; 13647c478bd9Sstevel@tonic-gate } 13657c478bd9Sstevel@tonic-gate } 13667c478bd9Sstevel@tonic-gate 13677c478bd9Sstevel@tonic-gate /* 13687c478bd9Sstevel@tonic-gate * If MTDEBUGPATTERN==1, lp->addr will have been overwritten with 13697c478bd9Sstevel@tonic-gate * FREEPATTERN for lp->size bytes. If we can merge, the oversize 13707c478bd9Sstevel@tonic-gate * header(s) that will also become part of the memory available for 13717c478bd9Sstevel@tonic-gate * reallocation (ie lp and/or nx) must also be overwritten with 13727c478bd9Sstevel@tonic-gate * FREEPATTERN or we will SIGABRT when this memory is next reallocated. 13737c478bd9Sstevel@tonic-gate */ 13747c478bd9Sstevel@tonic-gate switch (merge_flags) { 13757c478bd9Sstevel@tonic-gate 13767c478bd9Sstevel@tonic-gate case INSERT_ONLY: /* Coalescing not possible */ 13777c478bd9Sstevel@tonic-gate insert_oversize(lp, nx); 13787c478bd9Sstevel@tonic-gate break; 13797c478bd9Sstevel@tonic-gate case COALESCE_LEFT: 13807c478bd9Sstevel@tonic-gate pv->size += size_lp; 13817c478bd9Sstevel@tonic-gate position_oversize_by_size(pv); 13827c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 13837c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, lp, OVSZ_HEADER_SIZE); 13847c478bd9Sstevel@tonic-gate break; 13857c478bd9Sstevel@tonic-gate case COALESCE_RIGHT: 13867c478bd9Sstevel@tonic-gate unlink_oversize(nx); 13877c478bd9Sstevel@tonic-gate lp->size += size_nx; 13887c478bd9Sstevel@tonic-gate insert_oversize(lp, pv->next_byaddr); 13897c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) 13907c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, nx, OVSZ_HEADER_SIZE); 13917c478bd9Sstevel@tonic-gate break; 13927c478bd9Sstevel@tonic-gate case COALESCE_WITH_BOTH_SIDES: /* Merge (with right) to the left */ 13937c478bd9Sstevel@tonic-gate pv->size += size_lp + size_nx; 13947c478bd9Sstevel@tonic-gate unlink_oversize(nx); 13957c478bd9Sstevel@tonic-gate position_oversize_by_size(pv); 13967c478bd9Sstevel@tonic-gate if (debugopt & MTDEBUGPATTERN) { 13977c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, lp, OVSZ_HEADER_SIZE); 13987c478bd9Sstevel@tonic-gate copy_pattern(FREEPATTERN, nx, OVSZ_HEADER_SIZE); 13997c478bd9Sstevel@tonic-gate } 14007c478bd9Sstevel@tonic-gate break; 14017c478bd9Sstevel@tonic-gate } 14027c478bd9Sstevel@tonic-gate } 14037c478bd9Sstevel@tonic-gate 14047c478bd9Sstevel@tonic-gate /* 14057c478bd9Sstevel@tonic-gate * Find memory on our list that is at least size big. If we find a block that is 14067c478bd9Sstevel@tonic-gate * big enough, we break it up and return the associated oversize_t struct back 14077c478bd9Sstevel@tonic-gate * to the calling client. Any leftover piece of that block is returned to the 14087c478bd9Sstevel@tonic-gate * freelist. 14097c478bd9Sstevel@tonic-gate */ 14107c478bd9Sstevel@tonic-gate static oversize_t * 14117c478bd9Sstevel@tonic-gate find_oversize(size_t size) 14127c478bd9Sstevel@tonic-gate { 14137c478bd9Sstevel@tonic-gate oversize_t *wp = oversize_list.next_bysize; 14147c478bd9Sstevel@tonic-gate while (wp != &oversize_list && size > wp->size) 14157c478bd9Sstevel@tonic-gate wp = wp->next_bysize; 14167c478bd9Sstevel@tonic-gate 14177c478bd9Sstevel@tonic-gate if (wp == &oversize_list) /* empty list or nothing big enough */ 14187c478bd9Sstevel@tonic-gate return (NULL); 14197c478bd9Sstevel@tonic-gate /* breaking up a chunk of memory */ 14207c478bd9Sstevel@tonic-gate if ((long)((wp->size - (size + OVSZ_HEADER_SIZE + MTMALLOC_MIN_ALIGN))) 14217c478bd9Sstevel@tonic-gate > MAX_CACHED) { 14227c478bd9Sstevel@tonic-gate caddr_t off; 14237c478bd9Sstevel@tonic-gate oversize_t *np; 14247c478bd9Sstevel@tonic-gate size_t osize; 14257c478bd9Sstevel@tonic-gate off = (caddr_t)ALIGN(wp->addr + size, 14267c478bd9Sstevel@tonic-gate MTMALLOC_MIN_ALIGN); 14277c478bd9Sstevel@tonic-gate osize = wp->size; 14287c478bd9Sstevel@tonic-gate wp->size = (size_t)(off - wp->addr); 14297c478bd9Sstevel@tonic-gate np = oversize_header_alloc((uintptr_t)off, 14307c478bd9Sstevel@tonic-gate osize - (wp->size + OVSZ_HEADER_SIZE)); 14317c478bd9Sstevel@tonic-gate if ((long)np->size < 0) 14327c478bd9Sstevel@tonic-gate abort(); 14337c478bd9Sstevel@tonic-gate unlink_oversize(wp); 14347c478bd9Sstevel@tonic-gate add_oversize(np); 14357c478bd9Sstevel@tonic-gate } else { 14367c478bd9Sstevel@tonic-gate unlink_oversize(wp); 14377c478bd9Sstevel@tonic-gate } 14387c478bd9Sstevel@tonic-gate return (wp); 14397c478bd9Sstevel@tonic-gate } 14407c478bd9Sstevel@tonic-gate 14417c478bd9Sstevel@tonic-gate static void 14427c478bd9Sstevel@tonic-gate copy_pattern(uint32_t pattern, void *buf_arg, size_t size) 14437c478bd9Sstevel@tonic-gate { 14447c478bd9Sstevel@tonic-gate uint32_t *bufend = (uint32_t *)((char *)buf_arg + size); 14457c478bd9Sstevel@tonic-gate uint32_t *buf = buf_arg; 14467c478bd9Sstevel@tonic-gate 14477c478bd9Sstevel@tonic-gate while (buf < bufend - 3) { 14487c478bd9Sstevel@tonic-gate buf[3] = buf[2] = buf[1] = buf[0] = pattern; 14497c478bd9Sstevel@tonic-gate buf += 4; 14507c478bd9Sstevel@tonic-gate } 14517c478bd9Sstevel@tonic-gate while (buf < bufend) 14527c478bd9Sstevel@tonic-gate *buf++ = pattern; 14537c478bd9Sstevel@tonic-gate } 14547c478bd9Sstevel@tonic-gate 14557c478bd9Sstevel@tonic-gate static void * 14567c478bd9Sstevel@tonic-gate verify_pattern(uint32_t pattern, void *buf_arg, size_t size) 14577c478bd9Sstevel@tonic-gate { 14587c478bd9Sstevel@tonic-gate uint32_t *bufend = (uint32_t *)((char *)buf_arg + size); 14597c478bd9Sstevel@tonic-gate uint32_t *buf; 14607c478bd9Sstevel@tonic-gate 14617c478bd9Sstevel@tonic-gate for (buf = buf_arg; buf < bufend; buf++) 14627c478bd9Sstevel@tonic-gate if (*buf != pattern) 14637c478bd9Sstevel@tonic-gate return (buf); 14647c478bd9Sstevel@tonic-gate return (NULL); 14657c478bd9Sstevel@tonic-gate } 14667c478bd9Sstevel@tonic-gate 14677c478bd9Sstevel@tonic-gate static void 14687c478bd9Sstevel@tonic-gate free_oversize(oversize_t *ovp) 14697c478bd9Sstevel@tonic-gate { 14707c478bd9Sstevel@tonic-gate assert(((uintptr_t)ovp->addr & 7) == 0); /* are we 8 byte aligned */ 14717c478bd9Sstevel@tonic-gate assert(ovp->size > MAX_CACHED); 14727c478bd9Sstevel@tonic-gate 14737c478bd9Sstevel@tonic-gate ovp->next_bysize = ovp->prev_bysize = NULL; 14747c478bd9Sstevel@tonic-gate ovp->next_byaddr = ovp->prev_byaddr = NULL; 14757c478bd9Sstevel@tonic-gate (void) mutex_lock(&oversize_lock); 14767c478bd9Sstevel@tonic-gate add_oversize(ovp); 14777c478bd9Sstevel@tonic-gate (void) mutex_unlock(&oversize_lock); 14787c478bd9Sstevel@tonic-gate } 14797c478bd9Sstevel@tonic-gate 14807c478bd9Sstevel@tonic-gate static oversize_t * 14817c478bd9Sstevel@tonic-gate oversize_header_alloc(uintptr_t mem, size_t size) 14827c478bd9Sstevel@tonic-gate { 14837c478bd9Sstevel@tonic-gate oversize_t *ovsz_hdr; 14847c478bd9Sstevel@tonic-gate 14857c478bd9Sstevel@tonic-gate assert(size > MAX_CACHED); 14867c478bd9Sstevel@tonic-gate 14877c478bd9Sstevel@tonic-gate ovsz_hdr = (oversize_t *)mem; 14887c478bd9Sstevel@tonic-gate ovsz_hdr->prev_bysize = NULL; 14897c478bd9Sstevel@tonic-gate ovsz_hdr->next_bysize = NULL; 14907c478bd9Sstevel@tonic-gate ovsz_hdr->prev_byaddr = NULL; 14917c478bd9Sstevel@tonic-gate ovsz_hdr->next_byaddr = NULL; 14927c478bd9Sstevel@tonic-gate ovsz_hdr->hash_next = NULL; 14937c478bd9Sstevel@tonic-gate ovsz_hdr->size = size; 14947c478bd9Sstevel@tonic-gate mem += OVSZ_SIZE; 14957c478bd9Sstevel@tonic-gate *(uintptr_t *)mem = MTMALLOC_OVERSIZE_MAGIC; 14967c478bd9Sstevel@tonic-gate mem += OVERHEAD; 14977c478bd9Sstevel@tonic-gate assert(((uintptr_t)mem & 7) == 0); /* are we 8 byte aligned */ 14987c478bd9Sstevel@tonic-gate ovsz_hdr->addr = (caddr_t)mem; 14997c478bd9Sstevel@tonic-gate return (ovsz_hdr); 15007c478bd9Sstevel@tonic-gate } 15011d530678Sraf 15021d530678Sraf static void 15031d530678Sraf malloc_prepare() 15041d530678Sraf { 15051d530678Sraf percpu_t *cpuptr; 15061d530678Sraf cache_head_t *cachehead; 15071d530678Sraf cache_t *thiscache; 15081d530678Sraf 15091d530678Sraf (void) mutex_lock(&oversize_lock); 15101d530678Sraf for (cpuptr = &cpu_list[0]; cpuptr < &cpu_list[ncpus]; cpuptr++) { 15111d530678Sraf (void) mutex_lock(&cpuptr->mt_parent_lock); 15121d530678Sraf for (cachehead = &cpuptr->mt_caches[0]; 15131d530678Sraf cachehead < &cpuptr->mt_caches[NUM_CACHES]; 15141d530678Sraf cachehead++) { 15151d530678Sraf for (thiscache = cachehead->mt_cache; 15161d530678Sraf thiscache != NULL; 15171d530678Sraf thiscache = thiscache->mt_next) { 15181d530678Sraf (void) mutex_lock( 15191d530678Sraf &thiscache->mt_cache_lock); 15201d530678Sraf } 15211d530678Sraf } 15221d530678Sraf } 15231d530678Sraf } 15241d530678Sraf 15251d530678Sraf static void 15261d530678Sraf malloc_release() 15271d530678Sraf { 15281d530678Sraf percpu_t *cpuptr; 15291d530678Sraf cache_head_t *cachehead; 15301d530678Sraf cache_t *thiscache; 15311d530678Sraf 15321d530678Sraf for (cpuptr = &cpu_list[ncpus - 1]; cpuptr >= &cpu_list[0]; cpuptr--) { 15331d530678Sraf for (cachehead = &cpuptr->mt_caches[NUM_CACHES - 1]; 15341d530678Sraf cachehead >= &cpuptr->mt_caches[0]; 15351d530678Sraf cachehead--) { 15361d530678Sraf for (thiscache = cachehead->mt_cache; 15371d530678Sraf thiscache != NULL; 15381d530678Sraf thiscache = thiscache->mt_next) { 15391d530678Sraf (void) mutex_unlock( 15401d530678Sraf &thiscache->mt_cache_lock); 15411d530678Sraf } 15421d530678Sraf } 15431d530678Sraf (void) mutex_unlock(&cpuptr->mt_parent_lock); 15441d530678Sraf } 15451d530678Sraf (void) mutex_unlock(&oversize_lock); 15461d530678Sraf } 15471d530678Sraf 15481d530678Sraf #pragma init(malloc_init) 15491d530678Sraf static void 15501d530678Sraf malloc_init(void) 15511d530678Sraf { 15521d530678Sraf /* 15531d530678Sraf * This works in the init section for this library 15541d530678Sraf * because setup_caches() doesn't call anything in libc 15551d530678Sraf * that calls malloc(). If it did, disaster would ensue. 15561d530678Sraf * 15571d530678Sraf * For this to work properly, this library must be the first 15581d530678Sraf * one to have its init section called (after libc) by the 15591d530678Sraf * dynamic linker. If some other library's init section 15601d530678Sraf * ran first and called malloc(), disaster would ensue. 15611d530678Sraf * Because this is an interposer library for malloc(), the 15621d530678Sraf * dynamic linker arranges for its init section to run first. 15631d530678Sraf */ 15641d530678Sraf (void) setup_caches(); 15651d530678Sraf 15661d530678Sraf (void) pthread_atfork(malloc_prepare, malloc_release, malloc_release); 15671d530678Sraf } 1568