xref: /onnv-gate/usr/src/lib/libmtmalloc/common/mtmalloc.c (revision 8754:eb4a954b9317)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53866Sraf  * Common Development and Distribution License (the "License").
63866Sraf  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
213866Sraf 
220Sstevel@tonic-gate /*
23*8754SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <mtmalloc.h>
280Sstevel@tonic-gate #include "mtmalloc_impl.h"
290Sstevel@tonic-gate #include <unistd.h>
300Sstevel@tonic-gate #include <synch.h>
310Sstevel@tonic-gate #include <thread.h>
323866Sraf #include <pthread.h>
330Sstevel@tonic-gate #include <stdio.h>
340Sstevel@tonic-gate #include <limits.h>
350Sstevel@tonic-gate #include <errno.h>
360Sstevel@tonic-gate #include <string.h>
370Sstevel@tonic-gate #include <strings.h>
380Sstevel@tonic-gate #include <sys/param.h>
390Sstevel@tonic-gate #include <sys/sysmacros.h>
400Sstevel@tonic-gate 
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate  * To turn on the asserts just compile -DDEBUG
430Sstevel@tonic-gate  */
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #ifndef	DEBUG
460Sstevel@tonic-gate #define	NDEBUG
470Sstevel@tonic-gate #endif
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include <assert.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate /*
520Sstevel@tonic-gate  * The MT hot malloc implementation contained herein is designed to be
530Sstevel@tonic-gate  * plug-compatible with the libc version of malloc. It is not intended
540Sstevel@tonic-gate  * to replace that implementation until we decide that it is ok to break
550Sstevel@tonic-gate  * customer apps (Solaris 3.0).
560Sstevel@tonic-gate  *
570Sstevel@tonic-gate  * For requests up to 2^^16, the allocator initializes itself into NCPUS
580Sstevel@tonic-gate  * worth of chains of caches. When a memory request is made, the calling thread
590Sstevel@tonic-gate  * is vectored into one of NCPUS worth of caches.  The LWP id gives us a cheap,
600Sstevel@tonic-gate  * contention-reducing index to use, eventually, this should be replaced with
610Sstevel@tonic-gate  * the actual CPU sequence number, when an interface to get it is available.
620Sstevel@tonic-gate  *
630Sstevel@tonic-gate  * Once the thread is vectored into one of the list of caches the real
640Sstevel@tonic-gate  * allocation of the memory begins. The size is determined to figure out which
650Sstevel@tonic-gate  * bucket the allocation should be satisfied from. The management of free
660Sstevel@tonic-gate  * buckets is done via a bitmask. A free bucket is represented by a 1. The
670Sstevel@tonic-gate  * first free bit represents the first free bucket. The position of the bit,
680Sstevel@tonic-gate  * represents the position of the bucket in the arena.
690Sstevel@tonic-gate  *
700Sstevel@tonic-gate  * When the memory from the arena is handed out, the address of the cache
710Sstevel@tonic-gate  * control structure is written in the word preceeding the returned memory.
720Sstevel@tonic-gate  * This cache control address is used during free() to mark the buffer free
730Sstevel@tonic-gate  * in the cache control structure.
740Sstevel@tonic-gate  *
750Sstevel@tonic-gate  * When all available memory in a cache has been depleted, a new chunk of memory
760Sstevel@tonic-gate  * is allocated via sbrk(). The new cache is allocated from this chunk of memory
770Sstevel@tonic-gate  * and initialized in the function create_cache(). New caches are installed at
780Sstevel@tonic-gate  * the front of a singly linked list of the same size memory pools. This helps
790Sstevel@tonic-gate  * to ensure that there will tend to be available memory in the beginning of the
800Sstevel@tonic-gate  * list.
810Sstevel@tonic-gate  *
820Sstevel@tonic-gate  * Long linked lists hurt performance. To decrease this effect, there is a
830Sstevel@tonic-gate  * tunable, requestsize, that bumps up the sbrk allocation size and thus
840Sstevel@tonic-gate  * increases the number of available blocks within an arena.  We also keep
850Sstevel@tonic-gate  * a "hint" for each cache list, which is the last cache in the list allocated
860Sstevel@tonic-gate  * from.  This lowers the cost of searching if there are a lot of fully
870Sstevel@tonic-gate  * allocated blocks at the front of the list.
880Sstevel@tonic-gate  *
890Sstevel@tonic-gate  * For requests greater than 2^^16 (oversize allocations), there are two pieces
900Sstevel@tonic-gate  * of overhead. There is the OVERHEAD used to hold the cache addr
910Sstevel@tonic-gate  * (&oversize_list), plus an oversize_t structure to further describe the block.
920Sstevel@tonic-gate  *
930Sstevel@tonic-gate  * The oversize list is kept as defragmented as possible by coalescing
940Sstevel@tonic-gate  * freed oversized allocations with adjacent neighbors.
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  * Addresses handed out are stored in a hash table, and are aligned on
970Sstevel@tonic-gate  * MTMALLOC_MIN_ALIGN-byte boundaries at both ends. Request sizes are rounded-up
980Sstevel@tonic-gate  * where necessary in order to achieve this. This eases the implementation of
990Sstevel@tonic-gate  * MTDEBUGPATTERN and MTINITPATTERN, particularly where coalescing occurs.
1000Sstevel@tonic-gate  *
1010Sstevel@tonic-gate  * A memalign allocation takes memalign header overhead.  There's two
1020Sstevel@tonic-gate  * types of memalign headers distinguished by MTMALLOC_MEMALIGN_MAGIC
1030Sstevel@tonic-gate  * and MTMALLOC_MEMALIGN_MIN_MAGIC.  When the size of memory taken to
1040Sstevel@tonic-gate  * get to the aligned address from malloc'ed address is the minimum size
1050Sstevel@tonic-gate  * OVERHEAD, we create a header taking only one OVERHEAD space with magic
1060Sstevel@tonic-gate  * number MTMALLOC_MEMALIGN_MIN_MAGIC, and we know by subtracting OVERHEAD
1070Sstevel@tonic-gate  * from memaligned address, we can get to the malloc'ed address. Otherwise,
1080Sstevel@tonic-gate  * we create a memalign header taking two OVERHEAD space, one stores
1090Sstevel@tonic-gate  * MTMALLOC_MEMALIGN_MAGIC magic number, the other one points back to the
1100Sstevel@tonic-gate  * malloc'ed address.
1110Sstevel@tonic-gate  */
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate #if defined(__i386) || defined(__amd64)
1140Sstevel@tonic-gate #include <arpa/inet.h>	/* for htonl() */
1150Sstevel@tonic-gate #endif
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate static void * morecore(size_t);
1180Sstevel@tonic-gate static void create_cache(cache_t *, size_t bufsize, uint_t hunks);
1190Sstevel@tonic-gate static void * malloc_internal(size_t, percpu_t *);
1200Sstevel@tonic-gate static void * oversize(size_t);
1210Sstevel@tonic-gate static oversize_t *find_oversize(size_t);
1220Sstevel@tonic-gate static void add_oversize(oversize_t *);
1230Sstevel@tonic-gate static void copy_pattern(uint32_t, void *, size_t);
1240Sstevel@tonic-gate static void * verify_pattern(uint32_t, void *, size_t);
1250Sstevel@tonic-gate static void reinit_cpu_list(void);
1260Sstevel@tonic-gate static void reinit_cache(cache_t *);
1270Sstevel@tonic-gate static void free_oversize(oversize_t *);
1280Sstevel@tonic-gate static oversize_t *oversize_header_alloc(uintptr_t, size_t);
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate /*
1310Sstevel@tonic-gate  * oversize hash table stuff
1320Sstevel@tonic-gate  */
1330Sstevel@tonic-gate #define	NUM_BUCKETS	67	/* must be prime */
1340Sstevel@tonic-gate #define	HASH_OVERSIZE(caddr)	((uintptr_t)(caddr) % NUM_BUCKETS)
1350Sstevel@tonic-gate oversize_t *ovsz_hashtab[NUM_BUCKETS];
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate #define	ALIGN(x, a)	((((uintptr_t)(x) + ((uintptr_t)(a) - 1)) \
1380Sstevel@tonic-gate 			& ~((uintptr_t)(a) - 1)))
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate /* need this to deal with little endianess of x86 */
1410Sstevel@tonic-gate #if defined(__i386) || defined(__amd64)
1420Sstevel@tonic-gate #define	FLIP_EM(x)	htonl((x))
1430Sstevel@tonic-gate #else
1440Sstevel@tonic-gate #define	FLIP_EM(x)	(x)
1450Sstevel@tonic-gate #endif
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate #define	INSERT_ONLY			0
1480Sstevel@tonic-gate #define	COALESCE_LEFT			0x00000001
1490Sstevel@tonic-gate #define	COALESCE_RIGHT			0x00000002
1500Sstevel@tonic-gate #define	COALESCE_WITH_BOTH_SIDES	(COALESCE_LEFT | COALESCE_RIGHT)
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate #define	OVERHEAD	8	/* size needed to write cache addr */
1530Sstevel@tonic-gate #define	HUNKSIZE	8192	/* just a multiplier */
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate #define	MAX_CACHED_SHIFT	16	/* 64K is the max cached size */
1560Sstevel@tonic-gate #define	MAX_CACHED		(1 << MAX_CACHED_SHIFT)
1570Sstevel@tonic-gate #define	MIN_CACHED_SHIFT	4	/* smaller requests rounded up */
1580Sstevel@tonic-gate #define	MTMALLOC_MIN_ALIGN	8	/* min guaranteed alignment */
1590Sstevel@tonic-gate 
1601412Srm88369 /* maximum size before overflow */
1611412Srm88369 #define	MAX_MTMALLOC	(SIZE_MAX - (SIZE_MAX % MTMALLOC_MIN_ALIGN) \
1621412Srm88369 			- OVSZ_HEADER_SIZE)
1631412Srm88369 
1640Sstevel@tonic-gate #define	NUM_CACHES	(MAX_CACHED_SHIFT - MIN_CACHED_SHIFT + 1)
1650Sstevel@tonic-gate #define	CACHELIST_SIZE	ALIGN(NUM_CACHES * sizeof (cache_head_t), \
1660Sstevel@tonic-gate     CACHE_COHERENCY_UNIT)
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate #define	MINSIZE		9	/* for requestsize, tunable */
1690Sstevel@tonic-gate #define	MAXSIZE		256	/* arbitrary, big enough, for requestsize */
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate #define	FREEPATTERN	0xdeadbeef /* debug fill pattern for free buf */
1720Sstevel@tonic-gate #define	INITPATTERN	0xbaddcafe /* debug fill pattern for new buf */
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate #define	misaligned(p)	((unsigned)(p) & (sizeof (int) - 1))
1750Sstevel@tonic-gate #define	IS_OVERSIZE(x, y)	(((x) < (y)) && (((x) > MAX_CACHED)? 1 : 0))
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate static long requestsize = MINSIZE; /* 9 pages per cache; tunable; 9 is min */
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate static uint_t cpu_mask;
1800Sstevel@tonic-gate static curcpu_func curcpu;
1810Sstevel@tonic-gate 
1820Sstevel@tonic-gate static int32_t debugopt;
1830Sstevel@tonic-gate static int32_t reinit;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate static percpu_t *cpu_list;
1860Sstevel@tonic-gate static oversize_t oversize_list;
1873866Sraf static mutex_t oversize_lock = DEFAULTMUTEX;
1880Sstevel@tonic-gate 
1893866Sraf static int ncpus = 0;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate #define	MTMALLOC_OVERSIZE_MAGIC		((uintptr_t)&oversize_list)
1920Sstevel@tonic-gate #define	MTMALLOC_MEMALIGN_MAGIC		((uintptr_t)&oversize_list + 1)
1930Sstevel@tonic-gate #define	MTMALLOC_MEMALIGN_MIN_MAGIC	((uintptr_t)&oversize_list + 2)
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate /*
1960Sstevel@tonic-gate  * We require allocations handed out to be aligned on MTMALLOC_MIN_ALIGN-byte
1970Sstevel@tonic-gate  * boundaries. We round up sizeof (oversize_t) (when necessary) to ensure that
1980Sstevel@tonic-gate  * this is achieved.
1990Sstevel@tonic-gate  */
2000Sstevel@tonic-gate #define	OVSZ_SIZE		(ALIGN(sizeof (oversize_t), MTMALLOC_MIN_ALIGN))
2010Sstevel@tonic-gate #define	OVSZ_HEADER_SIZE	(OVSZ_SIZE + OVERHEAD)
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate /*
2040Sstevel@tonic-gate  * memalign header takes 2 OVERHEAD space.  One for memalign magic, and the
2050Sstevel@tonic-gate  * other one points back to the start address of originally allocated space.
2060Sstevel@tonic-gate  */
2070Sstevel@tonic-gate #define	MEMALIGN_HEADER_SIZE	2 * OVERHEAD
2080Sstevel@tonic-gate #define	MEMALIGN_HEADER_ALLOC(x, shift, malloc_addr)\
2090Sstevel@tonic-gate 	if (shift == OVERHEAD)\
2100Sstevel@tonic-gate 		*((uintptr_t *)((caddr_t)x - OVERHEAD)) = \
2110Sstevel@tonic-gate 			MTMALLOC_MEMALIGN_MIN_MAGIC; \
2120Sstevel@tonic-gate 	else {\
2130Sstevel@tonic-gate 		*((uintptr_t *)((caddr_t)x - OVERHEAD)) = \
2140Sstevel@tonic-gate 			MTMALLOC_MEMALIGN_MAGIC; \
2150Sstevel@tonic-gate 		*((uintptr_t *)((caddr_t)x - 2 * OVERHEAD)) = \
2160Sstevel@tonic-gate 			(uintptr_t)malloc_addr; \
2170Sstevel@tonic-gate 	}
2180Sstevel@tonic-gate 
2197166Sraf /*
2207166Sraf  * Add big to the oversize hash table at the head of the relevant bucket.
2217166Sraf  */
2227166Sraf static void
insert_hash(oversize_t * big)2237166Sraf insert_hash(oversize_t *big)
2247166Sraf {
2257166Sraf 	caddr_t ret = big->addr;
2267166Sraf 	int bucket = HASH_OVERSIZE(ret);
2277166Sraf 
2287166Sraf 	assert(MUTEX_HELD(&oversize_lock));
2297166Sraf 	big->hash_next = ovsz_hashtab[bucket];
2307166Sraf 	ovsz_hashtab[bucket] = big;
2317166Sraf }
2327166Sraf 
2330Sstevel@tonic-gate void *
malloc(size_t bytes)2340Sstevel@tonic-gate malloc(size_t bytes)
2350Sstevel@tonic-gate {
2360Sstevel@tonic-gate 	percpu_t *list_rotor;
2370Sstevel@tonic-gate 	uint_t	list_index;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	if (bytes > MAX_CACHED)
2400Sstevel@tonic-gate 		return (oversize(bytes));
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	list_index = (curcpu() & cpu_mask);
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	list_rotor = &cpu_list[list_index];
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	return (malloc_internal(bytes, list_rotor));
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate void *
realloc(void * ptr,size_t bytes)2500Sstevel@tonic-gate realloc(void * ptr, size_t bytes)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	void *new, *data_ptr;
2530Sstevel@tonic-gate 	cache_t *cacheptr;
2540Sstevel@tonic-gate 	caddr_t mem;
2550Sstevel@tonic-gate 	size_t shift = 0;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	if (ptr == NULL)
2580Sstevel@tonic-gate 		return (malloc(bytes));
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 	if (bytes == 0) {
2610Sstevel@tonic-gate 		free(ptr);
2620Sstevel@tonic-gate 		return (NULL);
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	data_ptr = ptr;
2660Sstevel@tonic-gate 	mem = (caddr_t)ptr - OVERHEAD;
2670Sstevel@tonic-gate 
268*8754SSurya.Prakki@Sun.COM 	/*
269*8754SSurya.Prakki@Sun.COM 	 * Optimization possibility :
270*8754SSurya.Prakki@Sun.COM 	 *	p = malloc(64);
271*8754SSurya.Prakki@Sun.COM 	 *	q = realloc(p, 64);
272*8754SSurya.Prakki@Sun.COM 	 * q can be same as p.
273*8754SSurya.Prakki@Sun.COM 	 * Apply this optimization for the normal
274*8754SSurya.Prakki@Sun.COM 	 * sized caches for now.
275*8754SSurya.Prakki@Sun.COM 	 */
276*8754SSurya.Prakki@Sun.COM 	if (*(uintptr_t *)mem < MTMALLOC_OVERSIZE_MAGIC ||
277*8754SSurya.Prakki@Sun.COM 	    *(uintptr_t *)mem > MTMALLOC_MEMALIGN_MIN_MAGIC) {
278*8754SSurya.Prakki@Sun.COM 		cacheptr = (cache_t *)*(uintptr_t *)mem;
279*8754SSurya.Prakki@Sun.COM 		if (bytes <= (cacheptr->mt_size - OVERHEAD))
280*8754SSurya.Prakki@Sun.COM 			return (ptr);
281*8754SSurya.Prakki@Sun.COM 	}
282*8754SSurya.Prakki@Sun.COM 
2830Sstevel@tonic-gate 	new = malloc(bytes);
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	if (new == NULL)
2860Sstevel@tonic-gate 		return (NULL);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	/*
2890Sstevel@tonic-gate 	 * If new == ptr, ptr has previously been freed. Passing a freed pointer
2900Sstevel@tonic-gate 	 * to realloc() is not allowed - unless the caller specifically states
2910Sstevel@tonic-gate 	 * otherwise, in which case we must avoid freeing ptr (ie new) before we
2920Sstevel@tonic-gate 	 * return new. There is (obviously) no requirement to memcpy() ptr to
2930Sstevel@tonic-gate 	 * new before we return.
2940Sstevel@tonic-gate 	 */
2950Sstevel@tonic-gate 	if (new == ptr) {
2960Sstevel@tonic-gate 		if (!(debugopt & MTDOUBLEFREE))
2970Sstevel@tonic-gate 			abort();
2980Sstevel@tonic-gate 		return (new);
2990Sstevel@tonic-gate 	}
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MAGIC) {
3020Sstevel@tonic-gate 		mem -= OVERHEAD;
3030Sstevel@tonic-gate 		ptr = (void *)*(uintptr_t *)mem;
3040Sstevel@tonic-gate 		mem = (caddr_t)ptr - OVERHEAD;
3050Sstevel@tonic-gate 		shift = (size_t)((uintptr_t)data_ptr - (uintptr_t)ptr);
3060Sstevel@tonic-gate 	} else if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MIN_MAGIC) {
3070Sstevel@tonic-gate 		ptr = (void *) mem;
3080Sstevel@tonic-gate 		mem -= OVERHEAD;
3090Sstevel@tonic-gate 		shift = OVERHEAD;
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	if (*(uintptr_t *)mem == MTMALLOC_OVERSIZE_MAGIC) {
3130Sstevel@tonic-gate 		oversize_t *old;
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 		old = (oversize_t *)(mem - OVSZ_SIZE);
3160Sstevel@tonic-gate 		(void) memcpy(new, data_ptr, MIN(bytes, old->size - shift));
3170Sstevel@tonic-gate 		free(ptr);
3180Sstevel@tonic-gate 		return (new);
3190Sstevel@tonic-gate 	}
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	cacheptr = (cache_t *)*(uintptr_t *)mem;
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	(void) memcpy(new, data_ptr,
324*8754SSurya.Prakki@Sun.COM 	    MIN(cacheptr->mt_size - OVERHEAD - shift, bytes));
3250Sstevel@tonic-gate 	free(ptr);
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 	return (new);
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate void *
calloc(size_t nelem,size_t bytes)3310Sstevel@tonic-gate calloc(size_t nelem, size_t bytes)
3320Sstevel@tonic-gate {
3330Sstevel@tonic-gate 	void * ptr;
3340Sstevel@tonic-gate 	size_t size = nelem * bytes;
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	ptr = malloc(size);
3370Sstevel@tonic-gate 	if (ptr == NULL)
3380Sstevel@tonic-gate 		return (NULL);
3393866Sraf 	(void) memset(ptr, 0, size);
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	return (ptr);
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate void
free(void * ptr)3450Sstevel@tonic-gate free(void * ptr)
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate 	cache_t *cacheptr;
3480Sstevel@tonic-gate 	caddr_t mem;
3490Sstevel@tonic-gate 	int32_t i;
3500Sstevel@tonic-gate 	caddr_t freeblocks;
3510Sstevel@tonic-gate 	uintptr_t offset;
3520Sstevel@tonic-gate 	uchar_t mask;
3530Sstevel@tonic-gate 	int32_t which_bit, num_bytes;
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	if (ptr == NULL)
3560Sstevel@tonic-gate 		return;
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	mem = (caddr_t)ptr - OVERHEAD;
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MAGIC) {
3610Sstevel@tonic-gate 		mem -= OVERHEAD;
3620Sstevel@tonic-gate 		ptr = (void *)*(uintptr_t *)mem;
3630Sstevel@tonic-gate 		mem = (caddr_t)ptr - OVERHEAD;
3640Sstevel@tonic-gate 	} else if (*(uintptr_t *)mem == MTMALLOC_MEMALIGN_MIN_MAGIC) {
3650Sstevel@tonic-gate 		ptr = (void *) mem;
3660Sstevel@tonic-gate 		mem -= OVERHEAD;
3670Sstevel@tonic-gate 	}
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	if (*(uintptr_t *)mem == MTMALLOC_OVERSIZE_MAGIC) {
3700Sstevel@tonic-gate 		oversize_t *big, **opp;
3710Sstevel@tonic-gate 		int bucket;
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 		big = (oversize_t *)(mem - OVSZ_SIZE);
3740Sstevel@tonic-gate 		(void) mutex_lock(&oversize_lock);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 		bucket = HASH_OVERSIZE(big->addr);
3770Sstevel@tonic-gate 		for (opp = &ovsz_hashtab[bucket]; *opp != NULL;
3780Sstevel@tonic-gate 		    opp = &(*opp)->hash_next)
3790Sstevel@tonic-gate 			if (*opp == big)
3800Sstevel@tonic-gate 				break;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 		if (*opp == NULL) {
3830Sstevel@tonic-gate 			if (!(debugopt & MTDOUBLEFREE))
3840Sstevel@tonic-gate 				abort();
3850Sstevel@tonic-gate 			(void) mutex_unlock(&oversize_lock);
3860Sstevel@tonic-gate 			return;
3870Sstevel@tonic-gate 		}
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 		*opp = big->hash_next;	/* remove big from the hash table */
3900Sstevel@tonic-gate 		big->hash_next = NULL;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 		if (debugopt & MTDEBUGPATTERN)
3930Sstevel@tonic-gate 			copy_pattern(FREEPATTERN, ptr, big->size);
3940Sstevel@tonic-gate 		add_oversize(big);
3950Sstevel@tonic-gate 		(void) mutex_unlock(&oversize_lock);
3960Sstevel@tonic-gate 		return;
3970Sstevel@tonic-gate 	}
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 	cacheptr = (cache_t *)*(uintptr_t *)mem;
4000Sstevel@tonic-gate 	freeblocks = cacheptr->mt_freelist;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	/*
4030Sstevel@tonic-gate 	 * This is the distance measured in bits into the arena.
4040Sstevel@tonic-gate 	 * The value of offset is in bytes but there is a 1-1 correlation
4050Sstevel@tonic-gate 	 * between distance into the arena and distance into the
4060Sstevel@tonic-gate 	 * freelist bitmask.
4070Sstevel@tonic-gate 	 */
4080Sstevel@tonic-gate 	offset = mem - cacheptr->mt_arena;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	/*
4110Sstevel@tonic-gate 	 * i is total number of bits to offset into freelist bitmask.
4120Sstevel@tonic-gate 	 */
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	i = offset / cacheptr->mt_size;
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	num_bytes = i >> 3;
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	/*
4190Sstevel@tonic-gate 	 * which_bit is the bit offset into the byte in the freelist.
4200Sstevel@tonic-gate 	 * if our freelist bitmask looks like 0xf3 and we are freeing
4210Sstevel@tonic-gate 	 * block 5 (ie: the 6th block) our mask will be 0xf7 after
4220Sstevel@tonic-gate 	 * the free. Things go left to right that's why the mask is 0x80
4230Sstevel@tonic-gate 	 * and not 0x01.
4240Sstevel@tonic-gate 	 */
4250Sstevel@tonic-gate 	which_bit = i - (num_bytes << 3);
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	mask = 0x80 >> which_bit;
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	freeblocks += num_bytes;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if (debugopt & MTDEBUGPATTERN)
4320Sstevel@tonic-gate 		copy_pattern(FREEPATTERN, ptr, cacheptr->mt_size - OVERHEAD);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	(void) mutex_lock(&cacheptr->mt_cache_lock);
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	if (*freeblocks & mask) {
4370Sstevel@tonic-gate 		if (!(debugopt & MTDOUBLEFREE))
4380Sstevel@tonic-gate 			abort();
4390Sstevel@tonic-gate 	} else {
4400Sstevel@tonic-gate 		*freeblocks |= mask;
4410Sstevel@tonic-gate 		cacheptr->mt_nfree++;
4420Sstevel@tonic-gate 	}
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	(void) mutex_unlock(&cacheptr->mt_cache_lock);
4450Sstevel@tonic-gate }
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate void *
memalign(size_t alignment,size_t size)4480Sstevel@tonic-gate memalign(size_t alignment, size_t size)
4490Sstevel@tonic-gate {
4500Sstevel@tonic-gate 	size_t alloc_size;
4510Sstevel@tonic-gate 	uintptr_t offset;
4520Sstevel@tonic-gate 	void *alloc_buf;
4530Sstevel@tonic-gate 	void *ret_buf;
4540Sstevel@tonic-gate 
455*8754SSurya.Prakki@Sun.COM 	if (size == 0 || alignment == 0 || misaligned(alignment) ||
456*8754SSurya.Prakki@Sun.COM 	    (alignment & (alignment - 1)) != 0) {
4570Sstevel@tonic-gate 		errno = EINVAL;
4580Sstevel@tonic-gate 		return (NULL);
4590Sstevel@tonic-gate 	}
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	/* <= MTMALLOC_MIN_ALIGN, malloc can provide directly */
4620Sstevel@tonic-gate 	if (alignment <= MTMALLOC_MIN_ALIGN)
4630Sstevel@tonic-gate 		return (malloc(size));
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	alloc_size = size + alignment - MTMALLOC_MIN_ALIGN;
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	if (alloc_size < size) { /* overflow */
4680Sstevel@tonic-gate 		errno = ENOMEM;
4690Sstevel@tonic-gate 		return (NULL);
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 	alloc_buf = malloc(alloc_size);
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	if (alloc_buf == NULL)
4750Sstevel@tonic-gate 		/* malloc sets errno */
4760Sstevel@tonic-gate 		return (NULL);
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	/*
4790Sstevel@tonic-gate 	 * If alloc_size > MAX_CACHED, malloc() will have returned a multiple of
4800Sstevel@tonic-gate 	 * MTMALLOC_MIN_ALIGN, having rounded-up alloc_size if necessary. Since
4810Sstevel@tonic-gate 	 * we will use alloc_size to return the excess fragments to the free
4820Sstevel@tonic-gate 	 * list, we also round-up alloc_size if necessary.
4830Sstevel@tonic-gate 	 */
4840Sstevel@tonic-gate 	if ((alloc_size > MAX_CACHED) &&
4850Sstevel@tonic-gate 	    (alloc_size & (MTMALLOC_MIN_ALIGN - 1)))
4860Sstevel@tonic-gate 		alloc_size = ALIGN(alloc_size, MTMALLOC_MIN_ALIGN);
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	if ((offset = (uintptr_t)alloc_buf & (alignment - 1)) == 0) {
4890Sstevel@tonic-gate 		/* aligned correctly */
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 		size_t frag_size = alloc_size -
492*8754SSurya.Prakki@Sun.COM 		    (size + MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE);
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 		/*
4950Sstevel@tonic-gate 		 * If the leftover piece of the memory > MAX_CACHED,
4960Sstevel@tonic-gate 		 * split off the piece and return it back to the freelist.
4970Sstevel@tonic-gate 		 */
4980Sstevel@tonic-gate 		if (IS_OVERSIZE(frag_size, alloc_size)) {
4990Sstevel@tonic-gate 			oversize_t *orig, *tail;
5000Sstevel@tonic-gate 			uintptr_t taddr;
5010Sstevel@tonic-gate 			size_t data_size;
5020Sstevel@tonic-gate 			taddr = ALIGN((uintptr_t)alloc_buf + size,
503*8754SSurya.Prakki@Sun.COM 			    MTMALLOC_MIN_ALIGN);
5040Sstevel@tonic-gate 			data_size = taddr - (uintptr_t)alloc_buf;
5050Sstevel@tonic-gate 			orig = (oversize_t *)((uintptr_t)alloc_buf -
506*8754SSurya.Prakki@Sun.COM 			    OVSZ_HEADER_SIZE);
5070Sstevel@tonic-gate 			frag_size = orig->size - data_size -
508*8754SSurya.Prakki@Sun.COM 			    OVSZ_HEADER_SIZE;
5090Sstevel@tonic-gate 			orig->size = data_size;
5100Sstevel@tonic-gate 			tail = oversize_header_alloc(taddr, frag_size);
5110Sstevel@tonic-gate 			free_oversize(tail);
5120Sstevel@tonic-gate 		}
5130Sstevel@tonic-gate 		ret_buf = alloc_buf;
5140Sstevel@tonic-gate 	} else {
5150Sstevel@tonic-gate 		uchar_t	oversize_bits = 0;
5160Sstevel@tonic-gate 		size_t	head_sz, data_sz, tail_sz;
5170Sstevel@tonic-gate 		uintptr_t ret_addr, taddr, shift, tshift;
5187166Sraf 		oversize_t *orig, *tail, *big;
5190Sstevel@tonic-gate 		size_t tsize;
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 		/* needs to be aligned */
5220Sstevel@tonic-gate 		shift = alignment - offset;
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 		assert(shift >= MTMALLOC_MIN_ALIGN);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 		ret_addr = ((uintptr_t)alloc_buf + shift);
5270Sstevel@tonic-gate 		ret_buf = (void *)ret_addr;
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 		if (alloc_size <= MAX_CACHED) {
5300Sstevel@tonic-gate 			MEMALIGN_HEADER_ALLOC(ret_addr, shift, alloc_buf);
5310Sstevel@tonic-gate 			return (ret_buf);
5320Sstevel@tonic-gate 		}
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 		/*
5350Sstevel@tonic-gate 		 * Only check for the fragments when the memory is allocted
5360Sstevel@tonic-gate 		 * from oversize_list.  Split off a fragment and return it
5370Sstevel@tonic-gate 		 * to the oversize freelist when it's > MAX_CACHED.
5380Sstevel@tonic-gate 		 */
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		head_sz = shift - MAX(MEMALIGN_HEADER_SIZE, OVSZ_HEADER_SIZE);
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 		tail_sz = alloc_size -
543*8754SSurya.Prakki@Sun.COM 		    (shift + size + MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE);
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 		oversize_bits |= IS_OVERSIZE(head_sz, alloc_size) |
546*8754SSurya.Prakki@Sun.COM 		    IS_OVERSIZE(size, alloc_size) << DATA_SHIFT |
547*8754SSurya.Prakki@Sun.COM 		    IS_OVERSIZE(tail_sz, alloc_size) << TAIL_SHIFT;
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 		switch (oversize_bits) {
5500Sstevel@tonic-gate 			case NONE_OVERSIZE:
5510Sstevel@tonic-gate 			case DATA_OVERSIZE:
5520Sstevel@tonic-gate 				MEMALIGN_HEADER_ALLOC(ret_addr, shift,
553*8754SSurya.Prakki@Sun.COM 				    alloc_buf);
5540Sstevel@tonic-gate 				break;
5550Sstevel@tonic-gate 			case HEAD_OVERSIZE:
5560Sstevel@tonic-gate 				/*
5570Sstevel@tonic-gate 				 * If we can extend data > MAX_CACHED and have
5580Sstevel@tonic-gate 				 * head still > MAX_CACHED, we split head-end
5590Sstevel@tonic-gate 				 * as the case of head-end and data oversized,
5600Sstevel@tonic-gate 				 * otherwise just create memalign header.
5610Sstevel@tonic-gate 				 */
5620Sstevel@tonic-gate 				tsize = (shift + size) - (MAX_CACHED + 8 +
563*8754SSurya.Prakki@Sun.COM 				    MTMALLOC_MIN_ALIGN + OVSZ_HEADER_SIZE);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 				if (!IS_OVERSIZE(tsize, alloc_size)) {
5660Sstevel@tonic-gate 					MEMALIGN_HEADER_ALLOC(ret_addr, shift,
567*8754SSurya.Prakki@Sun.COM 					    alloc_buf);
5680Sstevel@tonic-gate 					break;
5690Sstevel@tonic-gate 				} else {
5700Sstevel@tonic-gate 					tsize += OVSZ_HEADER_SIZE;
5710Sstevel@tonic-gate 					taddr = ALIGN((uintptr_t)alloc_buf +
572*8754SSurya.Prakki@Sun.COM 					    tsize, MTMALLOC_MIN_ALIGN);
5730Sstevel@tonic-gate 					tshift = ret_addr - taddr;
5740Sstevel@tonic-gate 					MEMALIGN_HEADER_ALLOC(ret_addr, tshift,
575*8754SSurya.Prakki@Sun.COM 					    taddr);
5760Sstevel@tonic-gate 					ret_addr = taddr;
5770Sstevel@tonic-gate 					shift = ret_addr - (uintptr_t)alloc_buf;
5780Sstevel@tonic-gate 				}
5790Sstevel@tonic-gate 				/* FALLTHROUGH */
5800Sstevel@tonic-gate 			case HEAD_AND_DATA_OVERSIZE:
5810Sstevel@tonic-gate 				/*
5820Sstevel@tonic-gate 				 * Split off the head fragment and
5830Sstevel@tonic-gate 				 * return it back to oversize freelist.
5840Sstevel@tonic-gate 				 * Create oversize header for the piece
5850Sstevel@tonic-gate 				 * of (data + tail fragment).
5860Sstevel@tonic-gate 				 */
5870Sstevel@tonic-gate 				orig = (oversize_t *)((uintptr_t)alloc_buf -
588*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE);
5897166Sraf 				big = oversize_header_alloc(ret_addr -
590*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE, (orig->size - shift));
5917166Sraf 				(void) mutex_lock(&oversize_lock);
5927166Sraf 				insert_hash(big);
5937166Sraf 				(void) mutex_unlock(&oversize_lock);
5940Sstevel@tonic-gate 				orig->size = shift - OVSZ_HEADER_SIZE;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 				/* free up the head fragment */
5970Sstevel@tonic-gate 				free_oversize(orig);
5980Sstevel@tonic-gate 				break;
5990Sstevel@tonic-gate 			case TAIL_OVERSIZE:
6000Sstevel@tonic-gate 				/*
6010Sstevel@tonic-gate 				 * If we can extend data > MAX_CACHED and have
6020Sstevel@tonic-gate 				 * tail-end still > MAX_CACHED, we split tail
6030Sstevel@tonic-gate 				 * end, otherwise just create memalign header.
6040Sstevel@tonic-gate 				 */
6050Sstevel@tonic-gate 				orig = (oversize_t *)((uintptr_t)alloc_buf -
606*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE);
6070Sstevel@tonic-gate 				tsize =  orig->size - (MAX_CACHED + 8 +
608*8754SSurya.Prakki@Sun.COM 				    shift + OVSZ_HEADER_SIZE +
609*8754SSurya.Prakki@Sun.COM 				    MTMALLOC_MIN_ALIGN);
6100Sstevel@tonic-gate 				if (!IS_OVERSIZE(tsize, alloc_size)) {
6110Sstevel@tonic-gate 					MEMALIGN_HEADER_ALLOC(ret_addr, shift,
612*8754SSurya.Prakki@Sun.COM 					    alloc_buf);
6130Sstevel@tonic-gate 					break;
6140Sstevel@tonic-gate 				} else {
6150Sstevel@tonic-gate 					size = MAX_CACHED + 8;
6160Sstevel@tonic-gate 				}
6170Sstevel@tonic-gate 				/* FALLTHROUGH */
6180Sstevel@tonic-gate 			case DATA_AND_TAIL_OVERSIZE:
6190Sstevel@tonic-gate 				/*
6200Sstevel@tonic-gate 				 * Split off the tail fragment and
6210Sstevel@tonic-gate 				 * return it back to oversize freelist.
6220Sstevel@tonic-gate 				 * Create memalign header and adjust
6230Sstevel@tonic-gate 				 * the size for the piece of
6240Sstevel@tonic-gate 				 * (head fragment + data).
6250Sstevel@tonic-gate 				 */
6260Sstevel@tonic-gate 				taddr = ALIGN(ret_addr + size,
627*8754SSurya.Prakki@Sun.COM 				    MTMALLOC_MIN_ALIGN);
6280Sstevel@tonic-gate 				data_sz = (size_t)(taddr -
629*8754SSurya.Prakki@Sun.COM 				    (uintptr_t)alloc_buf);
6300Sstevel@tonic-gate 				orig = (oversize_t *)((uintptr_t)alloc_buf -
631*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE);
6320Sstevel@tonic-gate 				tsize = orig->size - data_sz;
6330Sstevel@tonic-gate 				orig->size = data_sz;
6340Sstevel@tonic-gate 				MEMALIGN_HEADER_ALLOC(ret_buf, shift,
635*8754SSurya.Prakki@Sun.COM 				    alloc_buf);
6360Sstevel@tonic-gate 				tsize -= OVSZ_HEADER_SIZE;
6370Sstevel@tonic-gate 				tail = oversize_header_alloc(taddr,  tsize);
6380Sstevel@tonic-gate 				free_oversize(tail);
6390Sstevel@tonic-gate 				break;
6400Sstevel@tonic-gate 			case HEAD_AND_TAIL_OVERSIZE:
6410Sstevel@tonic-gate 				/*
6420Sstevel@tonic-gate 				 * Split off the head fragment.
6430Sstevel@tonic-gate 				 * We try to free up tail-end when we can
6440Sstevel@tonic-gate 				 * extend data size to (MAX_CACHED + 8)
6450Sstevel@tonic-gate 				 * and remain tail-end oversized.
6460Sstevel@tonic-gate 				 * The bottom line is all split pieces
6470Sstevel@tonic-gate 				 * should be oversize in size.
6480Sstevel@tonic-gate 				 */
6490Sstevel@tonic-gate 				orig = (oversize_t *)((uintptr_t)alloc_buf -
650*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE);
6510Sstevel@tonic-gate 				tsize =  orig->size - (MAX_CACHED + 8 +
652*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE + shift +
653*8754SSurya.Prakki@Sun.COM 				    MTMALLOC_MIN_ALIGN);
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 				if (!IS_OVERSIZE(tsize, alloc_size)) {
6560Sstevel@tonic-gate 					/*
6570Sstevel@tonic-gate 					 * If the chunk is not big enough
6580Sstevel@tonic-gate 					 * to make both data and tail oversize
6590Sstevel@tonic-gate 					 * we just keep them as one piece.
6600Sstevel@tonic-gate 					 */
6617166Sraf 					big = oversize_header_alloc(ret_addr -
662*8754SSurya.Prakki@Sun.COM 					    OVSZ_HEADER_SIZE,
663*8754SSurya.Prakki@Sun.COM 					    orig->size - shift);
6647166Sraf 					(void) mutex_lock(&oversize_lock);
6657166Sraf 					insert_hash(big);
6667166Sraf 					(void) mutex_unlock(&oversize_lock);
667*8754SSurya.Prakki@Sun.COM 					orig->size = shift - OVSZ_HEADER_SIZE;
6680Sstevel@tonic-gate 					free_oversize(orig);
6690Sstevel@tonic-gate 					break;
6700Sstevel@tonic-gate 				} else {
6710Sstevel@tonic-gate 					/*
6720Sstevel@tonic-gate 					 * extend data size > MAX_CACHED
6730Sstevel@tonic-gate 					 * and handle it as head, data, tail
6740Sstevel@tonic-gate 					 * are all oversized.
6750Sstevel@tonic-gate 					 */
6760Sstevel@tonic-gate 					size = MAX_CACHED + 8;
6770Sstevel@tonic-gate 				}
6780Sstevel@tonic-gate 				/* FALLTHROUGH */
6790Sstevel@tonic-gate 			case ALL_OVERSIZE:
6800Sstevel@tonic-gate 				/*
6810Sstevel@tonic-gate 				 * split off the head and tail fragments,
6820Sstevel@tonic-gate 				 * return them back to the oversize freelist.
6830Sstevel@tonic-gate 				 * Alloc oversize header for data seg.
6840Sstevel@tonic-gate 				 */
6850Sstevel@tonic-gate 				orig = (oversize_t *)((uintptr_t)alloc_buf -
686*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE);
6870Sstevel@tonic-gate 				tsize = orig->size;
6880Sstevel@tonic-gate 				orig->size = shift - OVSZ_HEADER_SIZE;
6890Sstevel@tonic-gate 				free_oversize(orig);
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 				taddr = ALIGN(ret_addr + size,
692*8754SSurya.Prakki@Sun.COM 				    MTMALLOC_MIN_ALIGN);
6930Sstevel@tonic-gate 				data_sz = taddr - ret_addr;
6940Sstevel@tonic-gate 				assert(tsize > (shift + data_sz +
695*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE));
6960Sstevel@tonic-gate 				tail_sz = tsize -
697*8754SSurya.Prakki@Sun.COM 				    (shift + data_sz + OVSZ_HEADER_SIZE);
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 				/* create oversize header for data seg */
7007166Sraf 				big = oversize_header_alloc(ret_addr -
701*8754SSurya.Prakki@Sun.COM 				    OVSZ_HEADER_SIZE, data_sz);
7027166Sraf 				(void) mutex_lock(&oversize_lock);
7037166Sraf 				insert_hash(big);
7047166Sraf 				(void) mutex_unlock(&oversize_lock);
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 				/* create oversize header for tail fragment */
7070Sstevel@tonic-gate 				tail = oversize_header_alloc(taddr, tail_sz);
7080Sstevel@tonic-gate 				free_oversize(tail);
7090Sstevel@tonic-gate 				break;
7100Sstevel@tonic-gate 			default:
7110Sstevel@tonic-gate 				/* should not reach here */
7120Sstevel@tonic-gate 				assert(0);
7130Sstevel@tonic-gate 		}
7140Sstevel@tonic-gate 	}
7150Sstevel@tonic-gate 	return (ret_buf);
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate void *
valloc(size_t size)7200Sstevel@tonic-gate valloc(size_t size)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate 	static unsigned pagesize;
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	if (size == 0)
7250Sstevel@tonic-gate 		return (NULL);
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	if (!pagesize)
7280Sstevel@tonic-gate 		pagesize = sysconf(_SC_PAGESIZE);
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	return (memalign(pagesize, size));
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate void
mallocctl(int cmd,long value)7340Sstevel@tonic-gate mallocctl(int cmd, long value)
7350Sstevel@tonic-gate {
7360Sstevel@tonic-gate 	switch (cmd) {
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 	case MTDEBUGPATTERN:
7390Sstevel@tonic-gate 		/*
7400Sstevel@tonic-gate 		 * Reinitialize free blocks in case malloc() is called prior
7410Sstevel@tonic-gate 		 * to mallocctl().
7420Sstevel@tonic-gate 		 */
7430Sstevel@tonic-gate 		if (value && !(debugopt & cmd)) {
7440Sstevel@tonic-gate 			reinit++;
7450Sstevel@tonic-gate 			debugopt |= cmd;
7460Sstevel@tonic-gate 			reinit_cpu_list();
7470Sstevel@tonic-gate 		}
7480Sstevel@tonic-gate 		/*FALLTHRU*/
7490Sstevel@tonic-gate 	case MTDOUBLEFREE:
7500Sstevel@tonic-gate 	case MTINITBUFFER:
7510Sstevel@tonic-gate 		if (value)
7520Sstevel@tonic-gate 			debugopt |= cmd;
7530Sstevel@tonic-gate 		else
7540Sstevel@tonic-gate 			debugopt &= ~cmd;
7550Sstevel@tonic-gate 		break;
7560Sstevel@tonic-gate 	case MTCHUNKSIZE:
7570Sstevel@tonic-gate 		if (value >= MINSIZE && value <= MAXSIZE)
7580Sstevel@tonic-gate 			requestsize = value;
7590Sstevel@tonic-gate 		break;
7600Sstevel@tonic-gate 	default:
7610Sstevel@tonic-gate 		break;
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate /*
7663866Sraf  * Initialization function, called from the init section of the library.
7673866Sraf  * No locking is required here because we are single-threaded during
7683866Sraf  * library initialization.
7690Sstevel@tonic-gate  */
7703866Sraf static void
setup_caches(void)7710Sstevel@tonic-gate setup_caches(void)
7720Sstevel@tonic-gate {
7730Sstevel@tonic-gate 	uintptr_t oldbrk;
7740Sstevel@tonic-gate 	uintptr_t newbrk;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	size_t cache_space_needed;
7770Sstevel@tonic-gate 	size_t padding;
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	curcpu_func new_curcpu;
7800Sstevel@tonic-gate 	uint_t new_cpu_mask;
7810Sstevel@tonic-gate 	percpu_t *new_cpu_list;
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	uint_t i, j;
7840Sstevel@tonic-gate 	uintptr_t list_addr;
7850Sstevel@tonic-gate 
7863866Sraf 	/*
7873866Sraf 	 * Get a decent "current cpu identifier", to be used to reduce
7883866Sraf 	 * contention.  Eventually, this should be replaced by an interface
7893866Sraf 	 * to get the actual CPU sequence number in libthread/liblwp.
7903866Sraf 	 */
7913866Sraf 	new_curcpu = (curcpu_func)thr_self;
7923866Sraf 	if ((ncpus = 2 * sysconf(_SC_NPROCESSORS_CONF)) <= 0)
7933866Sraf 		ncpus = 4; /* decent default value */
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	/* round ncpus up to a power of 2 */
7960Sstevel@tonic-gate 	while (ncpus & (ncpus - 1))
7970Sstevel@tonic-gate 		ncpus++;
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	new_cpu_mask = ncpus - 1;	/* create the cpu mask */
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	/*
8020Sstevel@tonic-gate 	 * We now do some magic with the brk.  What we want to get in the
8030Sstevel@tonic-gate 	 * end is a bunch of well-aligned stuff in a big initial allocation.
8040Sstevel@tonic-gate 	 * Along the way, we do sanity checks to make sure no one else has
8050Sstevel@tonic-gate 	 * touched the brk (which shouldn't happen, but it's always good to
8060Sstevel@tonic-gate 	 * check)
8070Sstevel@tonic-gate 	 *
8080Sstevel@tonic-gate 	 * First, make sure sbrk is sane, and store the current brk in oldbrk.
8090Sstevel@tonic-gate 	 */
8100Sstevel@tonic-gate 	oldbrk = (uintptr_t)sbrk(0);
8113866Sraf 	if ((void *)oldbrk == (void *)-1)
8123866Sraf 		abort();	/* sbrk is broken -- we're doomed. */
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	/*
8150Sstevel@tonic-gate 	 * Now, align the brk to a multiple of CACHE_COHERENCY_UNIT, so that
8160Sstevel@tonic-gate 	 * the percpu structures and cache lists will be properly aligned.
8170Sstevel@tonic-gate 	 *
8180Sstevel@tonic-gate 	 *   2.  All hunks will be page-aligned, assuming HUNKSIZE >= PAGESIZE,
8190Sstevel@tonic-gate 	 *	so they can be paged out individually.
8200Sstevel@tonic-gate 	 */
8210Sstevel@tonic-gate 	newbrk = ALIGN(oldbrk, CACHE_COHERENCY_UNIT);
8223866Sraf 	if (newbrk != oldbrk && (uintptr_t)sbrk(newbrk - oldbrk) != oldbrk)
8233866Sraf 		abort();	/* sbrk is broken -- we're doomed. */
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	/*
8260Sstevel@tonic-gate 	 * For each cpu, there is one percpu_t and a list of caches
8270Sstevel@tonic-gate 	 */
8280Sstevel@tonic-gate 	cache_space_needed = ncpus * (sizeof (percpu_t) + CACHELIST_SIZE);
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	new_cpu_list = (percpu_t *)sbrk(cache_space_needed);
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	if (new_cpu_list == (percpu_t *)-1 ||
8333866Sraf 	    (uintptr_t)new_cpu_list != newbrk)
8343866Sraf 		abort();	/* sbrk is broken -- we're doomed. */
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	/*
8370Sstevel@tonic-gate 	 * Finally, align the brk to HUNKSIZE so that all hunks are
8380Sstevel@tonic-gate 	 * page-aligned, to avoid edge-effects.
8390Sstevel@tonic-gate 	 */
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	newbrk = (uintptr_t)new_cpu_list + cache_space_needed;
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	padding = ALIGN(newbrk, HUNKSIZE) - newbrk;
8440Sstevel@tonic-gate 
8453866Sraf 	if (padding > 0 && (uintptr_t)sbrk(padding) != newbrk)
8463866Sraf 		abort();	/* sbrk is broken -- we're doomed. */
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	list_addr = ((uintptr_t)new_cpu_list + (sizeof (percpu_t) * ncpus));
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	/* initialize the percpu list */
8510Sstevel@tonic-gate 	for (i = 0; i < ncpus; i++) {
8520Sstevel@tonic-gate 		new_cpu_list[i].mt_caches = (cache_head_t *)list_addr;
8530Sstevel@tonic-gate 		for (j = 0; j < NUM_CACHES; j++) {
8540Sstevel@tonic-gate 			new_cpu_list[i].mt_caches[j].mt_cache = NULL;
8550Sstevel@tonic-gate 			new_cpu_list[i].mt_caches[j].mt_hint = NULL;
8560Sstevel@tonic-gate 		}
8570Sstevel@tonic-gate 
8583866Sraf 		(void) mutex_init(&new_cpu_list[i].mt_parent_lock,
8593866Sraf 		    USYNC_THREAD, NULL);
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 		/* get the correct cache list alignment */
8620Sstevel@tonic-gate 		list_addr += CACHELIST_SIZE;
8630Sstevel@tonic-gate 	}
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 	/*
8660Sstevel@tonic-gate 	 * Initialize oversize listhead
8670Sstevel@tonic-gate 	 */
8680Sstevel@tonic-gate 	oversize_list.next_bysize = &oversize_list;
8690Sstevel@tonic-gate 	oversize_list.prev_bysize = &oversize_list;
8700Sstevel@tonic-gate 	oversize_list.next_byaddr = &oversize_list;
8710Sstevel@tonic-gate 	oversize_list.prev_byaddr = &oversize_list;
8720Sstevel@tonic-gate 	oversize_list.addr = NULL;
8730Sstevel@tonic-gate 	oversize_list.size = 0;		/* sentinal */
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	/*
8763866Sraf 	 * Now install the global variables.
8770Sstevel@tonic-gate 	 */
8780Sstevel@tonic-gate 	curcpu = new_curcpu;
8790Sstevel@tonic-gate 	cpu_mask = new_cpu_mask;
8800Sstevel@tonic-gate 	cpu_list = new_cpu_list;
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate static void
create_cache(cache_t * cp,size_t size,uint_t chunksize)8840Sstevel@tonic-gate create_cache(cache_t *cp, size_t size, uint_t chunksize)
8850Sstevel@tonic-gate {
8860Sstevel@tonic-gate 	long nblocks;
8870Sstevel@tonic-gate 
8883866Sraf 	(void) mutex_init(&cp->mt_cache_lock, USYNC_THREAD, NULL);
8890Sstevel@tonic-gate 	cp->mt_size = size;
8900Sstevel@tonic-gate 	cp->mt_freelist = ((caddr_t)cp + sizeof (cache_t));
8910Sstevel@tonic-gate 	cp->mt_span = chunksize * HUNKSIZE - sizeof (cache_t);
8920Sstevel@tonic-gate 	cp->mt_hunks = chunksize;
8930Sstevel@tonic-gate 	/*
8940Sstevel@tonic-gate 	 * rough calculation. We will need to adjust later.
8950Sstevel@tonic-gate 	 */
8960Sstevel@tonic-gate 	nblocks = cp->mt_span / cp->mt_size;
8970Sstevel@tonic-gate 	nblocks >>= 3;
8980Sstevel@tonic-gate 	if (nblocks == 0) { /* less than 8 free blocks in this pool */
8990Sstevel@tonic-gate 		int32_t numblocks = 0;
9000Sstevel@tonic-gate 		long i = cp->mt_span;
9010Sstevel@tonic-gate 		size_t sub = cp->mt_size;
9020Sstevel@tonic-gate 		uchar_t mask = 0;
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 		while (i > sub) {
9050Sstevel@tonic-gate 			numblocks++;
9060Sstevel@tonic-gate 			i -= sub;
9070Sstevel@tonic-gate 		}
9080Sstevel@tonic-gate 		nblocks = numblocks;
9090Sstevel@tonic-gate 		cp->mt_arena = (caddr_t)ALIGN(cp->mt_freelist + 8, 8);
9100Sstevel@tonic-gate 		cp->mt_nfree = numblocks;
9110Sstevel@tonic-gate 		while (numblocks--) {
9120Sstevel@tonic-gate 			mask |= 0x80 >> numblocks;
9130Sstevel@tonic-gate 		}
9140Sstevel@tonic-gate 		*(cp->mt_freelist) = mask;
9150Sstevel@tonic-gate 	} else {
9160Sstevel@tonic-gate 		cp->mt_arena = (caddr_t)ALIGN((caddr_t)cp->mt_freelist +
917*8754SSurya.Prakki@Sun.COM 		    nblocks, 32);
9180Sstevel@tonic-gate 		/* recompute nblocks */
9190Sstevel@tonic-gate 		nblocks = (uintptr_t)((caddr_t)cp->mt_freelist +
920*8754SSurya.Prakki@Sun.COM 		    cp->mt_span - cp->mt_arena) / cp->mt_size;
9210Sstevel@tonic-gate 		cp->mt_nfree = ((nblocks >> 3) << 3);
9220Sstevel@tonic-gate 		/* Set everything to free */
9230Sstevel@tonic-gate 		(void) memset(cp->mt_freelist, 0xff, nblocks >> 3);
9240Sstevel@tonic-gate 	}
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	if (debugopt & MTDEBUGPATTERN)
9270Sstevel@tonic-gate 		copy_pattern(FREEPATTERN, cp->mt_arena, cp->mt_size * nblocks);
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	cp->mt_next = NULL;
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate static void
reinit_cpu_list(void)9330Sstevel@tonic-gate reinit_cpu_list(void)
9340Sstevel@tonic-gate {
9350Sstevel@tonic-gate 	oversize_t *wp = oversize_list.next_bysize;
9360Sstevel@tonic-gate 	percpu_t *cpuptr;
9370Sstevel@tonic-gate 	cache_t *thiscache;
9380Sstevel@tonic-gate 	cache_head_t *cachehead;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	/* Reinitialize free oversize blocks. */
9410Sstevel@tonic-gate 	(void) mutex_lock(&oversize_lock);
9420Sstevel@tonic-gate 	if (debugopt & MTDEBUGPATTERN)
9430Sstevel@tonic-gate 		for (; wp != &oversize_list; wp = wp->next_bysize)
9440Sstevel@tonic-gate 			copy_pattern(FREEPATTERN, wp->addr, wp->size);
9450Sstevel@tonic-gate 	(void) mutex_unlock(&oversize_lock);
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	/* Reinitialize free blocks. */
9480Sstevel@tonic-gate 	for (cpuptr = &cpu_list[0]; cpuptr < &cpu_list[ncpus]; cpuptr++) {
9490Sstevel@tonic-gate 		(void) mutex_lock(&cpuptr->mt_parent_lock);
9500Sstevel@tonic-gate 		for (cachehead = &cpuptr->mt_caches[0]; cachehead <
951*8754SSurya.Prakki@Sun.COM 		    &cpuptr->mt_caches[NUM_CACHES]; cachehead++) {
9520Sstevel@tonic-gate 			for (thiscache = cachehead->mt_cache; thiscache != NULL;
953*8754SSurya.Prakki@Sun.COM 			    thiscache = thiscache->mt_next) {
9540Sstevel@tonic-gate 				(void) mutex_lock(&thiscache->mt_cache_lock);
9550Sstevel@tonic-gate 				if (thiscache->mt_nfree == 0) {
9560Sstevel@tonic-gate 					(void) mutex_unlock(
9570Sstevel@tonic-gate 					    &thiscache->mt_cache_lock);
9580Sstevel@tonic-gate 					continue;
9590Sstevel@tonic-gate 				}
9600Sstevel@tonic-gate 				if (thiscache != NULL)
9610Sstevel@tonic-gate 					reinit_cache(thiscache);
9620Sstevel@tonic-gate 				(void) mutex_unlock(&thiscache->mt_cache_lock);
9630Sstevel@tonic-gate 			}
9640Sstevel@tonic-gate 		}
9650Sstevel@tonic-gate 		(void) mutex_unlock(&cpuptr->mt_parent_lock);
9660Sstevel@tonic-gate 	}
9670Sstevel@tonic-gate 	reinit = 0;
9680Sstevel@tonic-gate }
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate static void
reinit_cache(cache_t * thiscache)9710Sstevel@tonic-gate reinit_cache(cache_t *thiscache)
9720Sstevel@tonic-gate {
9730Sstevel@tonic-gate 	uint32_t *freeblocks; /* not a uintptr_t on purpose */
9740Sstevel@tonic-gate 	int32_t i, n;
9750Sstevel@tonic-gate 	caddr_t ret;
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 	freeblocks = (uint32_t *)thiscache->mt_freelist;
9780Sstevel@tonic-gate 	while (freeblocks < (uint32_t *)thiscache->mt_arena) {
9790Sstevel@tonic-gate 		if (*freeblocks & 0xffffffff) {
980*8754SSurya.Prakki@Sun.COM 			for (i = 0; i < 32; i++) {
981*8754SSurya.Prakki@Sun.COM 				if (FLIP_EM(*freeblocks) & (0x80000000 >> i)) {
982*8754SSurya.Prakki@Sun.COM 					n = (uintptr_t)(((freeblocks -
983*8754SSurya.Prakki@Sun.COM 					    (uint32_t *)thiscache->mt_freelist)
984*8754SSurya.Prakki@Sun.COM 					    << 5) + i) * thiscache->mt_size;
985*8754SSurya.Prakki@Sun.COM 					ret = thiscache->mt_arena + n;
986*8754SSurya.Prakki@Sun.COM 					ret += OVERHEAD;
987*8754SSurya.Prakki@Sun.COM 					copy_pattern(FREEPATTERN, ret,
988*8754SSurya.Prakki@Sun.COM 					    thiscache->mt_size);
989*8754SSurya.Prakki@Sun.COM 				}
9900Sstevel@tonic-gate 			}
9910Sstevel@tonic-gate 		}
9920Sstevel@tonic-gate 		freeblocks++;
9930Sstevel@tonic-gate 	}
9940Sstevel@tonic-gate }
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate static void *
malloc_internal(size_t size,percpu_t * cpuptr)9970Sstevel@tonic-gate malloc_internal(size_t size, percpu_t *cpuptr)
9980Sstevel@tonic-gate {
9990Sstevel@tonic-gate 	cache_head_t *cachehead;
10000Sstevel@tonic-gate 	cache_t *thiscache, *hintcache;
10010Sstevel@tonic-gate 	int32_t i, n, logsz, bucket;
10020Sstevel@tonic-gate 	uint32_t index;
10030Sstevel@tonic-gate 	uint32_t *freeblocks; /* not a uintptr_t on purpose */
10040Sstevel@tonic-gate 	caddr_t ret;
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 	logsz = MIN_CACHED_SHIFT;
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 	while (size > (1 << logsz))
10090Sstevel@tonic-gate 		logsz++;
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	bucket = logsz - MIN_CACHED_SHIFT;
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	(void) mutex_lock(&cpuptr->mt_parent_lock);
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	/*
10160Sstevel@tonic-gate 	 * Find a cache of the appropriate size with free buffers.
10170Sstevel@tonic-gate 	 *
10180Sstevel@tonic-gate 	 * We don't need to lock each cache as we check their mt_nfree count,
10190Sstevel@tonic-gate 	 * since:
10200Sstevel@tonic-gate 	 *	1.  We are only looking for caches with mt_nfree > 0.  If a
10210Sstevel@tonic-gate 	 *	   free happens during our search, it will increment mt_nfree,
10220Sstevel@tonic-gate 	 *	   which will not effect the test.
10230Sstevel@tonic-gate 	 *	2.  Allocations can decrement mt_nfree, but they can't happen
10240Sstevel@tonic-gate 	 *	   as long as we hold mt_parent_lock.
10250Sstevel@tonic-gate 	 */
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 	cachehead = &cpuptr->mt_caches[bucket];
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 	/* Search through the list, starting at the mt_hint */
10300Sstevel@tonic-gate 	thiscache = cachehead->mt_hint;
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	while (thiscache != NULL && thiscache->mt_nfree == 0)
10330Sstevel@tonic-gate 		thiscache = thiscache->mt_next;
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	if (thiscache == NULL) {
10360Sstevel@tonic-gate 		/* wrap around -- search up to the hint */
10370Sstevel@tonic-gate 		thiscache = cachehead->mt_cache;
10380Sstevel@tonic-gate 		hintcache = cachehead->mt_hint;
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 		while (thiscache != NULL && thiscache != hintcache &&
10410Sstevel@tonic-gate 		    thiscache->mt_nfree == 0)
10420Sstevel@tonic-gate 			thiscache = thiscache->mt_next;
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 		if (thiscache == hintcache)
10450Sstevel@tonic-gate 			thiscache = NULL;
10460Sstevel@tonic-gate 	}
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate 	if (thiscache == NULL) { /* there are no free caches */
10500Sstevel@tonic-gate 		int32_t thisrequest = requestsize;
10510Sstevel@tonic-gate 		int32_t buffer_size = (1 << logsz) + OVERHEAD;
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 		thiscache = (cache_t *)morecore(thisrequest * HUNKSIZE);
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 		if (thiscache == (cache_t *)-1) {
1056*8754SSurya.Prakki@Sun.COM 			(void) mutex_unlock(&cpuptr->mt_parent_lock);
1057*8754SSurya.Prakki@Sun.COM 			errno = EAGAIN;
1058*8754SSurya.Prakki@Sun.COM 			return (NULL);
10590Sstevel@tonic-gate 		}
10600Sstevel@tonic-gate 		create_cache(thiscache, buffer_size, thisrequest);
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 		/* link in the new block at the beginning of the list */
10630Sstevel@tonic-gate 		thiscache->mt_next = cachehead->mt_cache;
10640Sstevel@tonic-gate 		cachehead->mt_cache = thiscache;
10650Sstevel@tonic-gate 	}
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	/* update the hint to the cache we found or created */
10680Sstevel@tonic-gate 	cachehead->mt_hint = thiscache;
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 	/* thiscache now points to a cache with available space */
10710Sstevel@tonic-gate 	(void) mutex_lock(&thiscache->mt_cache_lock);
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 	freeblocks = (uint32_t *)thiscache->mt_freelist;
10740Sstevel@tonic-gate 	while (freeblocks < (uint32_t *)thiscache->mt_arena) {
10750Sstevel@tonic-gate 		if (*freeblocks & 0xffffffff)
10760Sstevel@tonic-gate 			break;
10770Sstevel@tonic-gate 		freeblocks++;
10780Sstevel@tonic-gate 		if (freeblocks < (uint32_t *)thiscache->mt_arena &&
10790Sstevel@tonic-gate 		    *freeblocks & 0xffffffff)
10800Sstevel@tonic-gate 			break;
10810Sstevel@tonic-gate 		freeblocks++;
10820Sstevel@tonic-gate 		if (freeblocks < (uint32_t *)thiscache->mt_arena &&
10830Sstevel@tonic-gate 		    *freeblocks & 0xffffffff)
10840Sstevel@tonic-gate 			break;
10850Sstevel@tonic-gate 		freeblocks++;
10860Sstevel@tonic-gate 		if (freeblocks < (uint32_t *)thiscache->mt_arena &&
10870Sstevel@tonic-gate 		    *freeblocks & 0xffffffff)
10880Sstevel@tonic-gate 			break;
10890Sstevel@tonic-gate 		freeblocks++;
10900Sstevel@tonic-gate 	}
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 	/*
10930Sstevel@tonic-gate 	 * the offset from mt_freelist to freeblocks is the offset into
10940Sstevel@tonic-gate 	 * the arena. Be sure to include the offset into freeblocks
10950Sstevel@tonic-gate 	 * of the bitmask. n is the offset.
10960Sstevel@tonic-gate 	 */
10970Sstevel@tonic-gate 	for (i = 0; i < 32; ) {
10980Sstevel@tonic-gate 		if (FLIP_EM(*freeblocks) & (0x80000000 >> i++))
10990Sstevel@tonic-gate 			break;
11000Sstevel@tonic-gate 		if (FLIP_EM(*freeblocks) & (0x80000000 >> i++))
11010Sstevel@tonic-gate 			break;
11020Sstevel@tonic-gate 		if (FLIP_EM(*freeblocks) & (0x80000000 >> i++))
11030Sstevel@tonic-gate 			break;
11040Sstevel@tonic-gate 		if (FLIP_EM(*freeblocks) & (0x80000000 >> i++))
11050Sstevel@tonic-gate 			break;
11060Sstevel@tonic-gate 	}
11070Sstevel@tonic-gate 	index = 0x80000000 >> --i;
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 	*freeblocks &= FLIP_EM(~index);
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate 	thiscache->mt_nfree--;
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	(void) mutex_unlock(&thiscache->mt_cache_lock);
11150Sstevel@tonic-gate 	(void) mutex_unlock(&cpuptr->mt_parent_lock);
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	n = (uintptr_t)(((freeblocks - (uint32_t *)thiscache->mt_freelist) << 5)
1118*8754SSurya.Prakki@Sun.COM 	    + i) * thiscache->mt_size;
11190Sstevel@tonic-gate 	/*
11200Sstevel@tonic-gate 	 * Now you have the offset in n, you've changed the free mask
11210Sstevel@tonic-gate 	 * in the freelist. Nothing left to do but find the block
11220Sstevel@tonic-gate 	 * in the arena and put the value of thiscache in the word
11230Sstevel@tonic-gate 	 * ahead of the handed out address and return the memory
11240Sstevel@tonic-gate 	 * back to the user.
11250Sstevel@tonic-gate 	 */
11260Sstevel@tonic-gate 	ret = thiscache->mt_arena + n;
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 	/* Store the cache addr for this buf. Makes free go fast. */
11290Sstevel@tonic-gate 	*(uintptr_t *)ret = (uintptr_t)thiscache;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	/*
11320Sstevel@tonic-gate 	 * This assert makes sure we don't hand out memory that is not
11330Sstevel@tonic-gate 	 * owned by this cache.
11340Sstevel@tonic-gate 	 */
11350Sstevel@tonic-gate 	assert(ret + thiscache->mt_size <= thiscache->mt_freelist +
1136*8754SSurya.Prakki@Sun.COM 	    thiscache->mt_span);
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	ret += OVERHEAD;
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 	assert(((uintptr_t)ret & 7) == 0); /* are we 8 byte aligned */
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	if (reinit == 0 && (debugopt & MTDEBUGPATTERN))
11430Sstevel@tonic-gate 		if (verify_pattern(FREEPATTERN, ret, size))
11440Sstevel@tonic-gate 			abort();	/* reference after free */
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 	if (debugopt & MTINITBUFFER)
11470Sstevel@tonic-gate 		copy_pattern(INITPATTERN, ret, size);
11480Sstevel@tonic-gate 	return ((void *)ret);
11490Sstevel@tonic-gate }
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate static void *
morecore(size_t bytes)11520Sstevel@tonic-gate morecore(size_t bytes)
11530Sstevel@tonic-gate {
11540Sstevel@tonic-gate 	void * ret;
11550Sstevel@tonic-gate 
11560Sstevel@tonic-gate 	if (bytes > LONG_MAX) {
11570Sstevel@tonic-gate 		intptr_t wad;
11580Sstevel@tonic-gate 		/*
11590Sstevel@tonic-gate 		 * The request size is too big. We need to do this in
11600Sstevel@tonic-gate 		 * chunks. Sbrk only takes an int for an arg.
11610Sstevel@tonic-gate 		 */
11620Sstevel@tonic-gate 		if (bytes == ULONG_MAX)
11630Sstevel@tonic-gate 			return ((void *)-1);
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 		ret = sbrk(0);
11660Sstevel@tonic-gate 		wad = LONG_MAX;
11670Sstevel@tonic-gate 		while (wad > 0) {
11680Sstevel@tonic-gate 			if (sbrk(wad) == (void *)-1) {
11690Sstevel@tonic-gate 				if (ret != sbrk(0))
11700Sstevel@tonic-gate 					(void) sbrk(-LONG_MAX);
11710Sstevel@tonic-gate 				return ((void *)-1);
11720Sstevel@tonic-gate 			}
11730Sstevel@tonic-gate 			bytes -= LONG_MAX;
11740Sstevel@tonic-gate 			wad = bytes;
11750Sstevel@tonic-gate 		}
11760Sstevel@tonic-gate 	} else
11770Sstevel@tonic-gate 		ret = sbrk(bytes);
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 	return (ret);
11800Sstevel@tonic-gate }
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate static void *
oversize(size_t size)11840Sstevel@tonic-gate oversize(size_t size)
11850Sstevel@tonic-gate {
11860Sstevel@tonic-gate 	caddr_t ret;
11870Sstevel@tonic-gate 	oversize_t *big;
11880Sstevel@tonic-gate 
11891412Srm88369 	/* make sure we will not overflow */
11901412Srm88369 	if (size > MAX_MTMALLOC) {
11911412Srm88369 		errno = ENOMEM;
11921412Srm88369 		return (NULL);
11931412Srm88369 	}
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 	/*
11960Sstevel@tonic-gate 	 * Since we ensure every address we hand back is
11970Sstevel@tonic-gate 	 * MTMALLOC_MIN_ALIGN-byte aligned, ALIGNing size ensures that the
11980Sstevel@tonic-gate 	 * memory handed out is MTMALLOC_MIN_ALIGN-byte aligned at both ends.
11990Sstevel@tonic-gate 	 * This eases the implementation of MTDEBUGPATTERN and MTINITPATTERN,
12000Sstevel@tonic-gate 	 * particularly where coalescing occurs.
12010Sstevel@tonic-gate 	 */
12020Sstevel@tonic-gate 	size = ALIGN(size, MTMALLOC_MIN_ALIGN);
12030Sstevel@tonic-gate 
12041412Srm88369 	/*
12051412Srm88369 	 * The idea with the global lock is that we are sure to
12061412Srm88369 	 * block in the kernel anyway since given an oversize alloc
12071412Srm88369 	 * we are sure to have to call morecore();
12081412Srm88369 	 */
12091412Srm88369 	(void) mutex_lock(&oversize_lock);
12101412Srm88369 
12110Sstevel@tonic-gate 	if ((big = find_oversize(size)) != NULL) {
12120Sstevel@tonic-gate 		if (reinit == 0 && (debugopt & MTDEBUGPATTERN))
12130Sstevel@tonic-gate 			if (verify_pattern(FREEPATTERN, big->addr, size))
12140Sstevel@tonic-gate 				abort();	/* reference after free */
12150Sstevel@tonic-gate 	} else {
12160Sstevel@tonic-gate 		/* Get more 8-byte aligned memory from heap */
12170Sstevel@tonic-gate 		ret = morecore(size + OVSZ_HEADER_SIZE);
12180Sstevel@tonic-gate 		if (ret == (caddr_t)-1) {
12190Sstevel@tonic-gate 			(void) mutex_unlock(&oversize_lock);
12200Sstevel@tonic-gate 			errno = ENOMEM;
12210Sstevel@tonic-gate 			return (NULL);
12220Sstevel@tonic-gate 		}
12230Sstevel@tonic-gate 		big = oversize_header_alloc((uintptr_t)ret, size);
12240Sstevel@tonic-gate 	}
12250Sstevel@tonic-gate 	ret = big->addr;
12260Sstevel@tonic-gate 
12277166Sraf 	insert_hash(big);
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate 	if (debugopt & MTINITBUFFER)
12300Sstevel@tonic-gate 		copy_pattern(INITPATTERN, ret, size);
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	(void) mutex_unlock(&oversize_lock);
12330Sstevel@tonic-gate 	assert(((uintptr_t)ret & 7) == 0); /* are we 8 byte aligned */
12340Sstevel@tonic-gate 	return ((void *)ret);
12350Sstevel@tonic-gate }
12360Sstevel@tonic-gate 
12370Sstevel@tonic-gate static void
insert_oversize(oversize_t * op,oversize_t * nx)12380Sstevel@tonic-gate insert_oversize(oversize_t *op, oversize_t *nx)
12390Sstevel@tonic-gate {
12400Sstevel@tonic-gate 	oversize_t *sp;
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	/* locate correct insertion point in size-ordered list */
12430Sstevel@tonic-gate 	for (sp = oversize_list.next_bysize;
12440Sstevel@tonic-gate 	    sp != &oversize_list && (op->size > sp->size);
12450Sstevel@tonic-gate 	    sp = sp->next_bysize)
12460Sstevel@tonic-gate 		;
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate 	/* link into size-ordered list */
12490Sstevel@tonic-gate 	op->next_bysize = sp;
12500Sstevel@tonic-gate 	op->prev_bysize = sp->prev_bysize;
12510Sstevel@tonic-gate 	op->prev_bysize->next_bysize = op;
12520Sstevel@tonic-gate 	op->next_bysize->prev_bysize = op;
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	/*
12550Sstevel@tonic-gate 	 * link item into address-ordered list
12560Sstevel@tonic-gate 	 * (caller provides insertion point as an optimization)
12570Sstevel@tonic-gate 	 */
12580Sstevel@tonic-gate 	op->next_byaddr = nx;
12590Sstevel@tonic-gate 	op->prev_byaddr = nx->prev_byaddr;
12600Sstevel@tonic-gate 	op->prev_byaddr->next_byaddr = op;
12610Sstevel@tonic-gate 	op->next_byaddr->prev_byaddr = op;
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate }
12640Sstevel@tonic-gate 
12650Sstevel@tonic-gate static void
unlink_oversize(oversize_t * lp)12660Sstevel@tonic-gate unlink_oversize(oversize_t *lp)
12670Sstevel@tonic-gate {
12680Sstevel@tonic-gate 	/* unlink from address list */
12690Sstevel@tonic-gate 	lp->prev_byaddr->next_byaddr = lp->next_byaddr;
12700Sstevel@tonic-gate 	lp->next_byaddr->prev_byaddr = lp->prev_byaddr;
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	/* unlink from size list */
12730Sstevel@tonic-gate 	lp->prev_bysize->next_bysize = lp->next_bysize;
12740Sstevel@tonic-gate 	lp->next_bysize->prev_bysize = lp->prev_bysize;
12750Sstevel@tonic-gate }
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate static void
position_oversize_by_size(oversize_t * op)12780Sstevel@tonic-gate position_oversize_by_size(oversize_t *op)
12790Sstevel@tonic-gate {
12800Sstevel@tonic-gate 	oversize_t *sp;
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 	if (op->size > op->next_bysize->size ||
12830Sstevel@tonic-gate 	    op->size < op->prev_bysize->size) {
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 		/* unlink from size list */
12860Sstevel@tonic-gate 		op->prev_bysize->next_bysize = op->next_bysize;
12870Sstevel@tonic-gate 		op->next_bysize->prev_bysize = op->prev_bysize;
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate 		/* locate correct insertion point in size-ordered list */
12900Sstevel@tonic-gate 		for (sp = oversize_list.next_bysize;
12910Sstevel@tonic-gate 		    sp != &oversize_list && (op->size > sp->size);
12920Sstevel@tonic-gate 		    sp = sp->next_bysize)
12930Sstevel@tonic-gate 			;
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 		/* link into size-ordered list */
12960Sstevel@tonic-gate 		op->next_bysize = sp;
12970Sstevel@tonic-gate 		op->prev_bysize = sp->prev_bysize;
12980Sstevel@tonic-gate 		op->prev_bysize->next_bysize = op;
12990Sstevel@tonic-gate 		op->next_bysize->prev_bysize = op;
13000Sstevel@tonic-gate 	}
13010Sstevel@tonic-gate }
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate static void
add_oversize(oversize_t * lp)13040Sstevel@tonic-gate add_oversize(oversize_t *lp)
13050Sstevel@tonic-gate {
13060Sstevel@tonic-gate 	int merge_flags = INSERT_ONLY;
13070Sstevel@tonic-gate 	oversize_t *nx;  	/* ptr to item right of insertion point */
13080Sstevel@tonic-gate 	oversize_t *pv;  	/* ptr to item left of insertion point */
13090Sstevel@tonic-gate 	uint_t size_lp, size_pv, size_nx;
13100Sstevel@tonic-gate 	uintptr_t endp_lp, endp_pv, endp_nx;
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	/*
13130Sstevel@tonic-gate 	 * Locate insertion point in address-ordered list
13140Sstevel@tonic-gate 	 */
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate 	for (nx = oversize_list.next_byaddr;
13170Sstevel@tonic-gate 	    nx != &oversize_list && (lp->addr > nx->addr);
13180Sstevel@tonic-gate 	    nx = nx->next_byaddr)
13190Sstevel@tonic-gate 		;
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 	/*
13220Sstevel@tonic-gate 	 * Determine how to add chunk to oversize freelist
13230Sstevel@tonic-gate 	 */
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate 	size_lp = OVSZ_HEADER_SIZE + lp->size;
13260Sstevel@tonic-gate 	endp_lp = ALIGN((uintptr_t)lp + size_lp, MTMALLOC_MIN_ALIGN);
13270Sstevel@tonic-gate 	size_lp = endp_lp - (uintptr_t)lp;
13280Sstevel@tonic-gate 
13290Sstevel@tonic-gate 	pv = nx->prev_byaddr;
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 	if (pv->size) {
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate 		size_pv = OVSZ_HEADER_SIZE + pv->size;
13340Sstevel@tonic-gate 		endp_pv = ALIGN((uintptr_t)pv + size_pv,
13350Sstevel@tonic-gate 		    MTMALLOC_MIN_ALIGN);
13360Sstevel@tonic-gate 		size_pv = endp_pv - (uintptr_t)pv;
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 		/* Check for adjacency with left chunk */
13390Sstevel@tonic-gate 		if ((uintptr_t)lp == endp_pv)
13400Sstevel@tonic-gate 			merge_flags |= COALESCE_LEFT;
13410Sstevel@tonic-gate 	}
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate 	if (nx->size) {
13440Sstevel@tonic-gate 
1345*8754SSurya.Prakki@Sun.COM 		/* Check for adjacency with right chunk */
1346*8754SSurya.Prakki@Sun.COM 		if ((uintptr_t)nx == endp_lp) {
1347*8754SSurya.Prakki@Sun.COM 			size_nx = OVSZ_HEADER_SIZE + nx->size;
1348*8754SSurya.Prakki@Sun.COM 			endp_nx = ALIGN((uintptr_t)nx + size_nx,
1349*8754SSurya.Prakki@Sun.COM 			    MTMALLOC_MIN_ALIGN);
1350*8754SSurya.Prakki@Sun.COM 			size_nx = endp_nx - (uintptr_t)nx;
1351*8754SSurya.Prakki@Sun.COM 			merge_flags |= COALESCE_RIGHT;
1352*8754SSurya.Prakki@Sun.COM 		}
13530Sstevel@tonic-gate 	}
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 	/*
13560Sstevel@tonic-gate 	 * If MTDEBUGPATTERN==1, lp->addr will have been overwritten with
13570Sstevel@tonic-gate 	 * FREEPATTERN for lp->size bytes. If we can merge, the oversize
13580Sstevel@tonic-gate 	 * header(s) that will also become part of the memory available for
13590Sstevel@tonic-gate 	 * reallocation (ie lp and/or nx) must also be overwritten with
13600Sstevel@tonic-gate 	 * FREEPATTERN or we will SIGABRT when this memory is next reallocated.
13610Sstevel@tonic-gate 	 */
13620Sstevel@tonic-gate 	switch (merge_flags) {
13630Sstevel@tonic-gate 
13640Sstevel@tonic-gate 	case INSERT_ONLY:		/* Coalescing not possible */
13650Sstevel@tonic-gate 		insert_oversize(lp, nx);
13660Sstevel@tonic-gate 		break;
13670Sstevel@tonic-gate 	case COALESCE_LEFT:
13680Sstevel@tonic-gate 		pv->size += size_lp;
13690Sstevel@tonic-gate 		position_oversize_by_size(pv);
13700Sstevel@tonic-gate 		if (debugopt & MTDEBUGPATTERN)
13710Sstevel@tonic-gate 			copy_pattern(FREEPATTERN, lp, OVSZ_HEADER_SIZE);
13720Sstevel@tonic-gate 		break;
13730Sstevel@tonic-gate 	case COALESCE_RIGHT:
13740Sstevel@tonic-gate 		unlink_oversize(nx);
13750Sstevel@tonic-gate 		lp->size += size_nx;
13760Sstevel@tonic-gate 		insert_oversize(lp, pv->next_byaddr);
13770Sstevel@tonic-gate 		if (debugopt & MTDEBUGPATTERN)
13780Sstevel@tonic-gate 			copy_pattern(FREEPATTERN, nx, OVSZ_HEADER_SIZE);
13790Sstevel@tonic-gate 		break;
13800Sstevel@tonic-gate 	case COALESCE_WITH_BOTH_SIDES:	/* Merge (with right) to the left */
13810Sstevel@tonic-gate 		pv->size += size_lp + size_nx;
13820Sstevel@tonic-gate 		unlink_oversize(nx);
13830Sstevel@tonic-gate 		position_oversize_by_size(pv);
13840Sstevel@tonic-gate 		if (debugopt & MTDEBUGPATTERN) {
13850Sstevel@tonic-gate 			copy_pattern(FREEPATTERN, lp, OVSZ_HEADER_SIZE);
13860Sstevel@tonic-gate 			copy_pattern(FREEPATTERN, nx, OVSZ_HEADER_SIZE);
13870Sstevel@tonic-gate 		}
13880Sstevel@tonic-gate 		break;
13890Sstevel@tonic-gate 	}
13900Sstevel@tonic-gate }
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate /*
13930Sstevel@tonic-gate  * Find memory on our list that is at least size big. If we find a block that is
13940Sstevel@tonic-gate  * big enough, we break it up and return the associated oversize_t struct back
13950Sstevel@tonic-gate  * to the calling client. Any leftover piece of that block is returned to the
13960Sstevel@tonic-gate  * freelist.
13970Sstevel@tonic-gate  */
13980Sstevel@tonic-gate static oversize_t *
find_oversize(size_t size)13990Sstevel@tonic-gate find_oversize(size_t size)
14000Sstevel@tonic-gate {
14010Sstevel@tonic-gate 	oversize_t *wp = oversize_list.next_bysize;
14020Sstevel@tonic-gate 	while (wp != &oversize_list && size > wp->size)
14030Sstevel@tonic-gate 		wp = wp->next_bysize;
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 	if (wp == &oversize_list) /* empty list or nothing big enough */
14060Sstevel@tonic-gate 		return (NULL);
14070Sstevel@tonic-gate 	/* breaking up a chunk of memory */
14080Sstevel@tonic-gate 	if ((long)((wp->size - (size + OVSZ_HEADER_SIZE + MTMALLOC_MIN_ALIGN)))
14090Sstevel@tonic-gate 	    > MAX_CACHED) {
14100Sstevel@tonic-gate 		caddr_t off;
14110Sstevel@tonic-gate 		oversize_t *np;
14120Sstevel@tonic-gate 		size_t osize;
14130Sstevel@tonic-gate 		off = (caddr_t)ALIGN(wp->addr + size,
14140Sstevel@tonic-gate 		    MTMALLOC_MIN_ALIGN);
14150Sstevel@tonic-gate 		osize = wp->size;
14160Sstevel@tonic-gate 		wp->size = (size_t)(off - wp->addr);
14170Sstevel@tonic-gate 		np = oversize_header_alloc((uintptr_t)off,
14180Sstevel@tonic-gate 		    osize - (wp->size + OVSZ_HEADER_SIZE));
14190Sstevel@tonic-gate 		if ((long)np->size < 0)
14200Sstevel@tonic-gate 			abort();
14210Sstevel@tonic-gate 		unlink_oversize(wp);
14220Sstevel@tonic-gate 		add_oversize(np);
14230Sstevel@tonic-gate 	} else {
14240Sstevel@tonic-gate 		unlink_oversize(wp);
14250Sstevel@tonic-gate 	}
14260Sstevel@tonic-gate 	return (wp);
14270Sstevel@tonic-gate }
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate static void
copy_pattern(uint32_t pattern,void * buf_arg,size_t size)14300Sstevel@tonic-gate copy_pattern(uint32_t pattern, void *buf_arg, size_t size)
14310Sstevel@tonic-gate {
14320Sstevel@tonic-gate 	uint32_t *bufend = (uint32_t *)((char *)buf_arg + size);
14330Sstevel@tonic-gate 	uint32_t *buf = buf_arg;
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate 	while (buf < bufend - 3) {
14360Sstevel@tonic-gate 		buf[3] = buf[2] = buf[1] = buf[0] = pattern;
14370Sstevel@tonic-gate 		buf += 4;
14380Sstevel@tonic-gate 	}
14390Sstevel@tonic-gate 	while (buf < bufend)
14400Sstevel@tonic-gate 		*buf++ = pattern;
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate static void *
verify_pattern(uint32_t pattern,void * buf_arg,size_t size)14440Sstevel@tonic-gate verify_pattern(uint32_t pattern, void *buf_arg, size_t size)
14450Sstevel@tonic-gate {
14460Sstevel@tonic-gate 	uint32_t *bufend = (uint32_t *)((char *)buf_arg + size);
14470Sstevel@tonic-gate 	uint32_t *buf;
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate 	for (buf = buf_arg; buf < bufend; buf++)
14500Sstevel@tonic-gate 		if (*buf != pattern)
14510Sstevel@tonic-gate 			return (buf);
14520Sstevel@tonic-gate 	return (NULL);
14530Sstevel@tonic-gate }
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate static void
free_oversize(oversize_t * ovp)14560Sstevel@tonic-gate free_oversize(oversize_t *ovp)
14570Sstevel@tonic-gate {
14580Sstevel@tonic-gate 	assert(((uintptr_t)ovp->addr & 7) == 0); /* are we 8 byte aligned */
14590Sstevel@tonic-gate 	assert(ovp->size > MAX_CACHED);
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 	ovp->next_bysize = ovp->prev_bysize = NULL;
14620Sstevel@tonic-gate 	ovp->next_byaddr = ovp->prev_byaddr = NULL;
14630Sstevel@tonic-gate 	(void) mutex_lock(&oversize_lock);
14640Sstevel@tonic-gate 	add_oversize(ovp);
14650Sstevel@tonic-gate 	(void) mutex_unlock(&oversize_lock);
14660Sstevel@tonic-gate }
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate static oversize_t *
oversize_header_alloc(uintptr_t mem,size_t size)14690Sstevel@tonic-gate oversize_header_alloc(uintptr_t mem, size_t size)
14700Sstevel@tonic-gate {
14710Sstevel@tonic-gate 	oversize_t *ovsz_hdr;
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	assert(size > MAX_CACHED);
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 	ovsz_hdr = (oversize_t *)mem;
14760Sstevel@tonic-gate 	ovsz_hdr->prev_bysize = NULL;
14770Sstevel@tonic-gate 	ovsz_hdr->next_bysize = NULL;
14780Sstevel@tonic-gate 	ovsz_hdr->prev_byaddr = NULL;
14790Sstevel@tonic-gate 	ovsz_hdr->next_byaddr = NULL;
14800Sstevel@tonic-gate 	ovsz_hdr->hash_next = NULL;
14810Sstevel@tonic-gate 	ovsz_hdr->size = size;
14820Sstevel@tonic-gate 	mem += OVSZ_SIZE;
14830Sstevel@tonic-gate 	*(uintptr_t *)mem = MTMALLOC_OVERSIZE_MAGIC;
14840Sstevel@tonic-gate 	mem += OVERHEAD;
14850Sstevel@tonic-gate 	assert(((uintptr_t)mem & 7) == 0); /* are we 8 byte aligned */
14860Sstevel@tonic-gate 	ovsz_hdr->addr = (caddr_t)mem;
14870Sstevel@tonic-gate 	return (ovsz_hdr);
14880Sstevel@tonic-gate }
14893866Sraf 
14903866Sraf static void
malloc_prepare()14913866Sraf malloc_prepare()
14923866Sraf {
14933866Sraf 	percpu_t *cpuptr;
14943866Sraf 	cache_head_t *cachehead;
14953866Sraf 	cache_t *thiscache;
14963866Sraf 
14973866Sraf 	(void) mutex_lock(&oversize_lock);
14983866Sraf 	for (cpuptr = &cpu_list[0]; cpuptr < &cpu_list[ncpus]; cpuptr++) {
14993866Sraf 		(void) mutex_lock(&cpuptr->mt_parent_lock);
15003866Sraf 		for (cachehead = &cpuptr->mt_caches[0];
15013866Sraf 		    cachehead < &cpuptr->mt_caches[NUM_CACHES];
15023866Sraf 		    cachehead++) {
15033866Sraf 			for (thiscache = cachehead->mt_cache;
15043866Sraf 			    thiscache != NULL;
15053866Sraf 			    thiscache = thiscache->mt_next) {
15063866Sraf 				(void) mutex_lock(
15073866Sraf 				    &thiscache->mt_cache_lock);
15083866Sraf 			}
15093866Sraf 		}
15103866Sraf 	}
15113866Sraf }
15123866Sraf 
15133866Sraf static void
malloc_release()15143866Sraf malloc_release()
15153866Sraf {
15163866Sraf 	percpu_t *cpuptr;
15173866Sraf 	cache_head_t *cachehead;
15183866Sraf 	cache_t *thiscache;
15193866Sraf 
15203866Sraf 	for (cpuptr = &cpu_list[ncpus - 1]; cpuptr >= &cpu_list[0]; cpuptr--) {
15213866Sraf 		for (cachehead = &cpuptr->mt_caches[NUM_CACHES - 1];
15223866Sraf 		    cachehead >= &cpuptr->mt_caches[0];
15233866Sraf 		    cachehead--) {
15243866Sraf 			for (thiscache = cachehead->mt_cache;
15253866Sraf 			    thiscache != NULL;
15263866Sraf 			    thiscache = thiscache->mt_next) {
15273866Sraf 				(void) mutex_unlock(
15283866Sraf 				    &thiscache->mt_cache_lock);
15293866Sraf 			}
15303866Sraf 		}
15313866Sraf 		(void) mutex_unlock(&cpuptr->mt_parent_lock);
15323866Sraf 	}
15333866Sraf 	(void) mutex_unlock(&oversize_lock);
15343866Sraf }
15353866Sraf 
15363866Sraf #pragma init(malloc_init)
15373866Sraf static void
malloc_init(void)15383866Sraf malloc_init(void)
15393866Sraf {
15403866Sraf 	/*
15413866Sraf 	 * This works in the init section for this library
15423866Sraf 	 * because setup_caches() doesn't call anything in libc
15433866Sraf 	 * that calls malloc().  If it did, disaster would ensue.
15443866Sraf 	 *
15453866Sraf 	 * For this to work properly, this library must be the first
15463866Sraf 	 * one to have its init section called (after libc) by the
15473866Sraf 	 * dynamic linker.  If some other library's init section
15483866Sraf 	 * ran first and called malloc(), disaster would ensue.
15493866Sraf 	 * Because this is an interposer library for malloc(), the
15503866Sraf 	 * dynamic linker arranges for its init section to run first.
15513866Sraf 	 */
15523866Sraf 	(void) setup_caches();
15533866Sraf 
15543866Sraf 	(void) pthread_atfork(malloc_prepare, malloc_release, malloc_release);
15553866Sraf }
1556