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