xref: /freebsd/sys/vm/uma_core.c (revision 099a0e588cbe1bbc56a565bf57d722621b47a866)
18355f576SJeff Roberson /*
2f461cf22SJeff Roberson  * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
38355f576SJeff Roberson  * All rights reserved.
48355f576SJeff Roberson  *
58355f576SJeff Roberson  * Redistribution and use in source and binary forms, with or without
68355f576SJeff Roberson  * modification, are permitted provided that the following conditions
78355f576SJeff Roberson  * are met:
88355f576SJeff Roberson  * 1. Redistributions of source code must retain the above copyright
98355f576SJeff Roberson  *    notice unmodified, this list of conditions, and the following
108355f576SJeff Roberson  *    disclaimer.
118355f576SJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
128355f576SJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
138355f576SJeff Roberson  *    documentation and/or other materials provided with the distribution.
148355f576SJeff Roberson  *
158355f576SJeff Roberson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168355f576SJeff Roberson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178355f576SJeff Roberson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188355f576SJeff Roberson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198355f576SJeff Roberson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
208355f576SJeff Roberson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
218355f576SJeff Roberson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
228355f576SJeff Roberson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238355f576SJeff Roberson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
248355f576SJeff Roberson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
258355f576SJeff Roberson  */
268355f576SJeff Roberson 
278355f576SJeff Roberson /*
288355f576SJeff Roberson  * uma_core.c  Implementation of the Universal Memory allocator
298355f576SJeff Roberson  *
308355f576SJeff Roberson  * This allocator is intended to replace the multitude of similar object caches
318355f576SJeff Roberson  * in the standard FreeBSD kernel.  The intent is to be flexible as well as
328355f576SJeff Roberson  * effecient.  A primary design goal is to return unused memory to the rest of
338355f576SJeff Roberson  * the system.  This will make the system as a whole more flexible due to the
348355f576SJeff Roberson  * ability to move memory to subsystems which most need it instead of leaving
358355f576SJeff Roberson  * pools of reserved memory unused.
368355f576SJeff Roberson  *
378355f576SJeff Roberson  * The basic ideas stem from similar slab/zone based allocators whose algorithms
388355f576SJeff Roberson  * are well known.
398355f576SJeff Roberson  *
408355f576SJeff Roberson  */
418355f576SJeff Roberson 
428355f576SJeff Roberson /*
438355f576SJeff Roberson  * TODO:
448355f576SJeff Roberson  *	- Improve memory usage for large allocations
458355f576SJeff Roberson  *	- Investigate cache size adjustments
468355f576SJeff Roberson  */
478355f576SJeff Roberson 
48874651b1SDavid E. O'Brien #include <sys/cdefs.h>
49874651b1SDavid E. O'Brien __FBSDID("$FreeBSD$");
50874651b1SDavid E. O'Brien 
518355f576SJeff Roberson /* I should really use ktr.. */
528355f576SJeff Roberson /*
538355f576SJeff Roberson #define UMA_DEBUG 1
548355f576SJeff Roberson #define UMA_DEBUG_ALLOC 1
558355f576SJeff Roberson #define UMA_DEBUG_ALLOC_1 1
568355f576SJeff Roberson */
578355f576SJeff Roberson 
588355f576SJeff Roberson #include "opt_param.h"
598355f576SJeff Roberson #include <sys/param.h>
608355f576SJeff Roberson #include <sys/systm.h>
618355f576SJeff Roberson #include <sys/kernel.h>
628355f576SJeff Roberson #include <sys/types.h>
638355f576SJeff Roberson #include <sys/queue.h>
648355f576SJeff Roberson #include <sys/malloc.h>
658355f576SJeff Roberson #include <sys/lock.h>
668355f576SJeff Roberson #include <sys/sysctl.h>
678355f576SJeff Roberson #include <sys/mutex.h>
684c1cc01cSJohn Baldwin #include <sys/proc.h>
698355f576SJeff Roberson #include <sys/smp.h>
7086bbae32SJeff Roberson #include <sys/vmmeter.h>
718522511bSHartmut Brandt #include <sys/mbuf.h>
7286bbae32SJeff Roberson 
738355f576SJeff Roberson #include <vm/vm.h>
748355f576SJeff Roberson #include <vm/vm_object.h>
758355f576SJeff Roberson #include <vm/vm_page.h>
768355f576SJeff Roberson #include <vm/vm_param.h>
778355f576SJeff Roberson #include <vm/vm_map.h>
788355f576SJeff Roberson #include <vm/vm_kern.h>
798355f576SJeff Roberson #include <vm/vm_extern.h>
808355f576SJeff Roberson #include <vm/uma.h>
818355f576SJeff Roberson #include <vm/uma_int.h>
82639c9550SJeff Roberson #include <vm/uma_dbg.h>
838355f576SJeff Roberson 
8448eea375SJeff Roberson #include <machine/vmparam.h>
8548eea375SJeff Roberson 
868355f576SJeff Roberson /*
87099a0e58SBosko Milekic  * This is the zone and keg from which all zones are spawned.  The idea is that
88099a0e58SBosko Milekic  * even the zone & keg heads are allocated from the allocator, so we use the
89099a0e58SBosko Milekic  * bss section to bootstrap us.
908355f576SJeff Roberson  */
91099a0e58SBosko Milekic static struct uma_keg masterkeg;
92099a0e58SBosko Milekic static struct uma_zone masterzone_k;
93099a0e58SBosko Milekic static struct uma_zone masterzone_z;
94099a0e58SBosko Milekic static uma_zone_t kegs = &masterzone_k;
95099a0e58SBosko Milekic static uma_zone_t zones = &masterzone_z;
968355f576SJeff Roberson 
978355f576SJeff Roberson /* This is the zone from which all of uma_slab_t's are allocated. */
988355f576SJeff Roberson static uma_zone_t slabzone;
99099a0e58SBosko Milekic static uma_zone_t slabrefzone;	/* With refcounters (for UMA_ZONE_REFCNT) */
1008355f576SJeff Roberson 
1018355f576SJeff Roberson /*
1028355f576SJeff Roberson  * The initial hash tables come out of this zone so they can be allocated
1038355f576SJeff Roberson  * prior to malloc coming up.
1048355f576SJeff Roberson  */
1058355f576SJeff Roberson static uma_zone_t hashzone;
1068355f576SJeff Roberson 
107961647dfSJeff Roberson static MALLOC_DEFINE(M_UMAHASH, "UMAHash", "UMA Hash Buckets");
108961647dfSJeff Roberson 
1098355f576SJeff Roberson /*
11086bbae32SJeff Roberson  * Are we allowed to allocate buckets?
11186bbae32SJeff Roberson  */
11286bbae32SJeff Roberson static int bucketdisable = 1;
11386bbae32SJeff Roberson 
114099a0e58SBosko Milekic /* Linked list of all kegs in the system */
115099a0e58SBosko Milekic static LIST_HEAD(,uma_keg) uma_kegs = LIST_HEAD_INITIALIZER(&uma_kegs);
1168355f576SJeff Roberson 
117099a0e58SBosko Milekic /* This mutex protects the keg list */
1188355f576SJeff Roberson static struct mtx uma_mtx;
1198355f576SJeff Roberson 
120d88797c2SBosko Milekic /* These are the pcpu cache locks */
121d88797c2SBosko Milekic static struct mtx uma_pcpu_mtx[MAXCPU];
122d88797c2SBosko Milekic 
1238355f576SJeff Roberson /* Linked list of boot time pages */
1248355f576SJeff Roberson static LIST_HEAD(,uma_slab) uma_boot_pages =
1258355f576SJeff Roberson     LIST_HEAD_INITIALIZER(&uma_boot_pages);
1268355f576SJeff Roberson 
1278355f576SJeff Roberson /* Count of free boottime pages */
1288355f576SJeff Roberson static int uma_boot_free = 0;
1298355f576SJeff Roberson 
1308355f576SJeff Roberson /* Is the VM done starting up? */
1318355f576SJeff Roberson static int booted = 0;
1328355f576SJeff Roberson 
1339643769aSJeff Roberson /*
1349643769aSJeff Roberson  * This is the handle used to schedule events that need to happen
1359643769aSJeff Roberson  * outside of the allocation fast path.
1369643769aSJeff Roberson  */
1378355f576SJeff Roberson static struct callout uma_callout;
1389643769aSJeff Roberson #define	UMA_TIMEOUT	20		/* Seconds for callout interval. */
1398355f576SJeff Roberson 
1408355f576SJeff Roberson /*
1418355f576SJeff Roberson  * This structure is passed as the zone ctor arg so that I don't have to create
1428355f576SJeff Roberson  * a special allocation function just for zones.
1438355f576SJeff Roberson  */
1448355f576SJeff Roberson struct uma_zctor_args {
1458355f576SJeff Roberson 	char *name;
146c3bdc05fSAndrew R. Reiter 	size_t size;
1478355f576SJeff Roberson 	uma_ctor ctor;
1488355f576SJeff Roberson 	uma_dtor dtor;
1498355f576SJeff Roberson 	uma_init uminit;
1508355f576SJeff Roberson 	uma_fini fini;
151099a0e58SBosko Milekic 	uma_keg_t keg;
152099a0e58SBosko Milekic 	int align;
153099a0e58SBosko Milekic 	u_int16_t flags;
154099a0e58SBosko Milekic };
155099a0e58SBosko Milekic 
156099a0e58SBosko Milekic struct uma_kctor_args {
157099a0e58SBosko Milekic 	uma_zone_t zone;
158099a0e58SBosko Milekic 	size_t size;
159099a0e58SBosko Milekic 	uma_init uminit;
160099a0e58SBosko Milekic 	uma_fini fini;
1618355f576SJeff Roberson 	int align;
1628355f576SJeff Roberson 	u_int16_t flags;
1638355f576SJeff Roberson };
1648355f576SJeff Roberson 
165cae33c14SJeff Roberson struct uma_bucket_zone {
166cae33c14SJeff Roberson 	uma_zone_t	ubz_zone;
167cae33c14SJeff Roberson 	char		*ubz_name;
168cae33c14SJeff Roberson 	int		ubz_entries;
169cae33c14SJeff Roberson };
170cae33c14SJeff Roberson 
171cae33c14SJeff Roberson #define	BUCKET_MAX	128
172cae33c14SJeff Roberson 
173cae33c14SJeff Roberson struct uma_bucket_zone bucket_zones[] = {
174cae33c14SJeff Roberson 	{ NULL, "16 Bucket", 16 },
175cae33c14SJeff Roberson 	{ NULL, "32 Bucket", 32 },
176cae33c14SJeff Roberson 	{ NULL, "64 Bucket", 64 },
177cae33c14SJeff Roberson 	{ NULL, "128 Bucket", 128 },
178cae33c14SJeff Roberson 	{ NULL, NULL, 0}
179cae33c14SJeff Roberson };
180cae33c14SJeff Roberson 
181cae33c14SJeff Roberson #define	BUCKET_SHIFT	4
182cae33c14SJeff Roberson #define	BUCKET_ZONES	((BUCKET_MAX >> BUCKET_SHIFT) + 1)
183cae33c14SJeff Roberson 
184cae33c14SJeff Roberson uint8_t bucket_size[BUCKET_ZONES];
185cae33c14SJeff Roberson 
1868355f576SJeff Roberson /* Prototypes.. */
1878355f576SJeff Roberson 
1888355f576SJeff Roberson static void *obj_alloc(uma_zone_t, int, u_int8_t *, int);
1898355f576SJeff Roberson static void *page_alloc(uma_zone_t, int, u_int8_t *, int);
190009b6fcbSJeff Roberson static void *startup_alloc(uma_zone_t, int, u_int8_t *, int);
1918355f576SJeff Roberson static void page_free(void *, int, u_int8_t);
1928355f576SJeff Roberson static uma_slab_t slab_zalloc(uma_zone_t, int);
1939643769aSJeff Roberson static void cache_drain(uma_zone_t);
1948355f576SJeff Roberson static void bucket_drain(uma_zone_t, uma_bucket_t);
195aaa8bb16SJeff Roberson static void bucket_cache_drain(uma_zone_t zone);
196099a0e58SBosko Milekic static void keg_ctor(void *, int, void *);
197099a0e58SBosko Milekic static void keg_dtor(void *, int, void *);
1988355f576SJeff Roberson static void zone_ctor(void *, int, void *);
1999c2cd7e5SJeff Roberson static void zone_dtor(void *, int, void *);
2008355f576SJeff Roberson static void zero_init(void *, int);
2018355f576SJeff Roberson static void zone_small_init(uma_zone_t zone);
2028355f576SJeff Roberson static void zone_large_init(uma_zone_t zone);
2038355f576SJeff Roberson static void zone_foreach(void (*zfunc)(uma_zone_t));
2048355f576SJeff Roberson static void zone_timeout(uma_zone_t zone);
2050aef6126SJeff Roberson static int hash_alloc(struct uma_hash *);
2060aef6126SJeff Roberson static int hash_expand(struct uma_hash *, struct uma_hash *);
2070aef6126SJeff Roberson static void hash_free(struct uma_hash *hash);
2088355f576SJeff Roberson static void uma_timeout(void *);
2098355f576SJeff Roberson static void uma_startup3(void);
210bbee39c6SJeff Roberson static void *uma_zalloc_internal(uma_zone_t, void *, int);
21186bbae32SJeff Roberson static void uma_zfree_internal(uma_zone_t, void *, void *, int);
21286bbae32SJeff Roberson static void bucket_enable(void);
213cae33c14SJeff Roberson static void bucket_init(void);
214cae33c14SJeff Roberson static uma_bucket_t bucket_alloc(int, int);
215cae33c14SJeff Roberson static void bucket_free(uma_bucket_t);
216cae33c14SJeff Roberson static void bucket_zone_drain(void);
217bbee39c6SJeff Roberson static int uma_zalloc_bucket(uma_zone_t zone, int flags);
218bbee39c6SJeff Roberson static uma_slab_t uma_zone_slab(uma_zone_t zone, int flags);
219bbee39c6SJeff Roberson static void *uma_slab_alloc(uma_zone_t zone, uma_slab_t slab);
2209643769aSJeff Roberson static void zone_drain(uma_zone_t);
221099a0e58SBosko Milekic static void uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit,
222099a0e58SBosko Milekic     uma_fini fini, int align, u_int16_t flags);
223bbee39c6SJeff Roberson 
2248355f576SJeff Roberson void uma_print_zone(uma_zone_t);
2258355f576SJeff Roberson void uma_print_stats(void);
2268355f576SJeff Roberson static int sysctl_vm_zone(SYSCTL_HANDLER_ARGS);
2278355f576SJeff Roberson 
2288355f576SJeff Roberson SYSCTL_OID(_vm, OID_AUTO, zone, CTLTYPE_STRING|CTLFLAG_RD,
2298355f576SJeff Roberson     NULL, 0, sysctl_vm_zone, "A", "Zone Info");
2308355f576SJeff Roberson SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL);
2318355f576SJeff Roberson 
23286bbae32SJeff Roberson /*
23386bbae32SJeff Roberson  * This routine checks to see whether or not it's safe to enable buckets.
23486bbae32SJeff Roberson  */
23586bbae32SJeff Roberson 
23686bbae32SJeff Roberson static void
23786bbae32SJeff Roberson bucket_enable(void)
23886bbae32SJeff Roberson {
23986bbae32SJeff Roberson 	if (cnt.v_free_count < cnt.v_free_min)
24086bbae32SJeff Roberson 		bucketdisable = 1;
24186bbae32SJeff Roberson 	else
24286bbae32SJeff Roberson 		bucketdisable = 0;
24386bbae32SJeff Roberson }
24486bbae32SJeff Roberson 
245cae33c14SJeff Roberson static void
246cae33c14SJeff Roberson bucket_init(void)
247cae33c14SJeff Roberson {
248cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
249cae33c14SJeff Roberson 	int i;
250cae33c14SJeff Roberson 	int j;
251cae33c14SJeff Roberson 
252cae33c14SJeff Roberson 	for (i = 0, j = 0; bucket_zones[j].ubz_entries != 0; j++) {
253cae33c14SJeff Roberson 		int size;
254cae33c14SJeff Roberson 
255cae33c14SJeff Roberson 		ubz = &bucket_zones[j];
256cae33c14SJeff Roberson 		size = roundup(sizeof(struct uma_bucket), sizeof(void *));
257cae33c14SJeff Roberson 		size += sizeof(void *) * ubz->ubz_entries;
258cae33c14SJeff Roberson 		ubz->ubz_zone = uma_zcreate(ubz->ubz_name, size,
259b60f5b79SJeff Roberson 		    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
260cae33c14SJeff Roberson 		for (; i <= ubz->ubz_entries; i += (1 << BUCKET_SHIFT))
261cae33c14SJeff Roberson 			bucket_size[i >> BUCKET_SHIFT] = j;
262cae33c14SJeff Roberson 	}
263cae33c14SJeff Roberson }
264cae33c14SJeff Roberson 
265cae33c14SJeff Roberson static uma_bucket_t
266cae33c14SJeff Roberson bucket_alloc(int entries, int bflags)
267cae33c14SJeff Roberson {
268cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
269cae33c14SJeff Roberson 	uma_bucket_t bucket;
270cae33c14SJeff Roberson 	int idx;
271cae33c14SJeff Roberson 
272cae33c14SJeff Roberson 	/*
273cae33c14SJeff Roberson 	 * This is to stop us from allocating per cpu buckets while we're
274cae33c14SJeff Roberson 	 * running out of UMA_BOOT_PAGES.  Otherwise, we would exhaust the
275cae33c14SJeff Roberson 	 * boot pages.  This also prevents us from allocating buckets in
276cae33c14SJeff Roberson 	 * low memory situations.
277cae33c14SJeff Roberson 	 */
278cae33c14SJeff Roberson 
279cae33c14SJeff Roberson 	if (bucketdisable)
280cae33c14SJeff Roberson 		return (NULL);
281cae33c14SJeff Roberson 	idx = howmany(entries, 1 << BUCKET_SHIFT);
282cae33c14SJeff Roberson 	ubz = &bucket_zones[bucket_size[idx]];
283cae33c14SJeff Roberson 	bucket = uma_zalloc_internal(ubz->ubz_zone, NULL, bflags);
284cae33c14SJeff Roberson 	if (bucket) {
285cae33c14SJeff Roberson #ifdef INVARIANTS
286cae33c14SJeff Roberson 		bzero(bucket->ub_bucket, sizeof(void *) * ubz->ubz_entries);
287cae33c14SJeff Roberson #endif
288cae33c14SJeff Roberson 		bucket->ub_cnt = 0;
289cae33c14SJeff Roberson 		bucket->ub_entries = ubz->ubz_entries;
290cae33c14SJeff Roberson 	}
291cae33c14SJeff Roberson 
292cae33c14SJeff Roberson 	return (bucket);
293cae33c14SJeff Roberson }
294cae33c14SJeff Roberson 
295cae33c14SJeff Roberson static void
296cae33c14SJeff Roberson bucket_free(uma_bucket_t bucket)
297cae33c14SJeff Roberson {
298cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
299cae33c14SJeff Roberson 	int idx;
300cae33c14SJeff Roberson 
301cae33c14SJeff Roberson 	idx = howmany(bucket->ub_entries, 1 << BUCKET_SHIFT);
302cae33c14SJeff Roberson 	ubz = &bucket_zones[bucket_size[idx]];
303cae33c14SJeff Roberson 	uma_zfree_internal(ubz->ubz_zone, bucket, NULL, 0);
304cae33c14SJeff Roberson }
305cae33c14SJeff Roberson 
306cae33c14SJeff Roberson static void
307cae33c14SJeff Roberson bucket_zone_drain(void)
308cae33c14SJeff Roberson {
309cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
310cae33c14SJeff Roberson 
311cae33c14SJeff Roberson 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++)
312cae33c14SJeff Roberson 		zone_drain(ubz->ubz_zone);
313cae33c14SJeff Roberson }
314cae33c14SJeff Roberson 
3158355f576SJeff Roberson 
3168355f576SJeff Roberson /*
3178355f576SJeff Roberson  * Routine called by timeout which is used to fire off some time interval
3189643769aSJeff Roberson  * based calculations.  (stats, hash size, etc.)
3198355f576SJeff Roberson  *
3208355f576SJeff Roberson  * Arguments:
3218355f576SJeff Roberson  *	arg   Unused
3228355f576SJeff Roberson  *
3238355f576SJeff Roberson  * Returns:
3248355f576SJeff Roberson  *	Nothing
3258355f576SJeff Roberson  */
3268355f576SJeff Roberson static void
3278355f576SJeff Roberson uma_timeout(void *unused)
3288355f576SJeff Roberson {
32986bbae32SJeff Roberson 	bucket_enable();
3308355f576SJeff Roberson 	zone_foreach(zone_timeout);
3318355f576SJeff Roberson 
3328355f576SJeff Roberson 	/* Reschedule this event */
3339643769aSJeff Roberson 	callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
3348355f576SJeff Roberson }
3358355f576SJeff Roberson 
3368355f576SJeff Roberson /*
3379643769aSJeff Roberson  * Routine to perform timeout driven calculations.  This expands the
3389643769aSJeff Roberson  * hashes and does per cpu statistics aggregation.
3398355f576SJeff Roberson  *
3408355f576SJeff Roberson  *  Arguments:
3418355f576SJeff Roberson  *	zone  The zone to operate on
3428355f576SJeff Roberson  *
3438355f576SJeff Roberson  *  Returns:
3448355f576SJeff Roberson  *	Nothing
3458355f576SJeff Roberson  */
3468355f576SJeff Roberson static void
3478355f576SJeff Roberson zone_timeout(uma_zone_t zone)
3488355f576SJeff Roberson {
349099a0e58SBosko Milekic 	uma_keg_t keg;
3508355f576SJeff Roberson 	uma_cache_t cache;
3518355f576SJeff Roberson 	u_int64_t alloc;
3528355f576SJeff Roberson 	int cpu;
3538355f576SJeff Roberson 
354099a0e58SBosko Milekic 	keg = zone->uz_keg;
3558355f576SJeff Roberson 	alloc = 0;
3568355f576SJeff Roberson 
3578355f576SJeff Roberson 	/*
3588355f576SJeff Roberson 	 * Aggregate per cpu cache statistics back to the zone.
3598355f576SJeff Roberson 	 *
360aaa8bb16SJeff Roberson 	 * XXX This should be done in the sysctl handler.
361aaa8bb16SJeff Roberson 	 *
3628355f576SJeff Roberson 	 * I may rewrite this to set a flag in the per cpu cache instead of
3638355f576SJeff Roberson 	 * locking.  If the flag is not cleared on the next round I will have
3648355f576SJeff Roberson 	 * to lock and do it here instead so that the statistics don't get too
3658355f576SJeff Roberson 	 * far out of sync.
3668355f576SJeff Roberson 	 */
367099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZFLAG_INTERNAL)) {
368b6c71225SJohn Baldwin 		for (cpu = 0; cpu <= mp_maxid; cpu++) {
3698355f576SJeff Roberson 			if (CPU_ABSENT(cpu))
3708355f576SJeff Roberson 				continue;
371d88797c2SBosko Milekic 			CPU_LOCK(cpu);
3728355f576SJeff Roberson 			cache = &zone->uz_cpu[cpu];
3738355f576SJeff Roberson 			/* Add them up, and reset */
3748355f576SJeff Roberson 			alloc += cache->uc_allocs;
3758355f576SJeff Roberson 			cache->uc_allocs = 0;
376d88797c2SBosko Milekic 			CPU_UNLOCK(cpu);
3778355f576SJeff Roberson 		}
3788355f576SJeff Roberson 	}
3798355f576SJeff Roberson 
3808355f576SJeff Roberson 	/* Now push these stats back into the zone.. */
3818355f576SJeff Roberson 	ZONE_LOCK(zone);
3828355f576SJeff Roberson 	zone->uz_allocs += alloc;
3838355f576SJeff Roberson 
3848355f576SJeff Roberson 	/*
3858355f576SJeff Roberson 	 * Expand the zone hash table.
3868355f576SJeff Roberson 	 *
3878355f576SJeff Roberson 	 * This is done if the number of slabs is larger than the hash size.
3888355f576SJeff Roberson 	 * What I'm trying to do here is completely reduce collisions.  This
3898355f576SJeff Roberson 	 * may be a little aggressive.  Should I allow for two collisions max?
3908355f576SJeff Roberson 	 */
3918355f576SJeff Roberson 
392099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH &&
393099a0e58SBosko Milekic 	    keg->uk_pages / keg->uk_ppera >= keg->uk_hash.uh_hashsize) {
3940aef6126SJeff Roberson 		struct uma_hash newhash;
3950aef6126SJeff Roberson 		struct uma_hash oldhash;
3960aef6126SJeff Roberson 		int ret;
3975300d9ddSJeff Roberson 
3980aef6126SJeff Roberson 		/*
3990aef6126SJeff Roberson 		 * This is so involved because allocating and freeing
4000aef6126SJeff Roberson 		 * while the zone lock is held will lead to deadlock.
4010aef6126SJeff Roberson 		 * I have to do everything in stages and check for
4020aef6126SJeff Roberson 		 * races.
4030aef6126SJeff Roberson 		 */
404099a0e58SBosko Milekic 		newhash = keg->uk_hash;
4055300d9ddSJeff Roberson 		ZONE_UNLOCK(zone);
4060aef6126SJeff Roberson 		ret = hash_alloc(&newhash);
4075300d9ddSJeff Roberson 		ZONE_LOCK(zone);
4080aef6126SJeff Roberson 		if (ret) {
409099a0e58SBosko Milekic 			if (hash_expand(&keg->uk_hash, &newhash)) {
410099a0e58SBosko Milekic 				oldhash = keg->uk_hash;
411099a0e58SBosko Milekic 				keg->uk_hash = newhash;
4120aef6126SJeff Roberson 			} else
4130aef6126SJeff Roberson 				oldhash = newhash;
4140aef6126SJeff Roberson 
4150aef6126SJeff Roberson 			ZONE_UNLOCK(zone);
4160aef6126SJeff Roberson 			hash_free(&oldhash);
4170aef6126SJeff Roberson 			ZONE_LOCK(zone);
4180aef6126SJeff Roberson 		}
4195300d9ddSJeff Roberson 	}
4208355f576SJeff Roberson 	ZONE_UNLOCK(zone);
4218355f576SJeff Roberson }
4228355f576SJeff Roberson 
4238355f576SJeff Roberson /*
4245300d9ddSJeff Roberson  * Allocate and zero fill the next sized hash table from the appropriate
4255300d9ddSJeff Roberson  * backing store.
4265300d9ddSJeff Roberson  *
4275300d9ddSJeff Roberson  * Arguments:
4280aef6126SJeff Roberson  *	hash  A new hash structure with the old hash size in uh_hashsize
4295300d9ddSJeff Roberson  *
4305300d9ddSJeff Roberson  * Returns:
4310aef6126SJeff Roberson  *	1 on sucess and 0 on failure.
4325300d9ddSJeff Roberson  */
43337c84183SPoul-Henning Kamp static int
4340aef6126SJeff Roberson hash_alloc(struct uma_hash *hash)
4355300d9ddSJeff Roberson {
4360aef6126SJeff Roberson 	int oldsize;
4375300d9ddSJeff Roberson 	int alloc;
4385300d9ddSJeff Roberson 
4390aef6126SJeff Roberson 	oldsize = hash->uh_hashsize;
4400aef6126SJeff Roberson 
4415300d9ddSJeff Roberson 	/* We're just going to go to a power of two greater */
4420aef6126SJeff Roberson 	if (oldsize)  {
4430aef6126SJeff Roberson 		hash->uh_hashsize = oldsize * 2;
4440aef6126SJeff Roberson 		alloc = sizeof(hash->uh_slab_hash[0]) * hash->uh_hashsize;
4450aef6126SJeff Roberson 		hash->uh_slab_hash = (struct slabhead *)malloc(alloc,
446961647dfSJeff Roberson 		    M_UMAHASH, M_NOWAIT);
4475300d9ddSJeff Roberson 	} else {
4480aef6126SJeff Roberson 		alloc = sizeof(hash->uh_slab_hash[0]) * UMA_HASH_SIZE_INIT;
4490aef6126SJeff Roberson 		hash->uh_slab_hash = uma_zalloc_internal(hashzone, NULL,
450a163d034SWarner Losh 		    M_WAITOK);
4510aef6126SJeff Roberson 		hash->uh_hashsize = UMA_HASH_SIZE_INIT;
4525300d9ddSJeff Roberson 	}
4530aef6126SJeff Roberson 	if (hash->uh_slab_hash) {
4540aef6126SJeff Roberson 		bzero(hash->uh_slab_hash, alloc);
4550aef6126SJeff Roberson 		hash->uh_hashmask = hash->uh_hashsize - 1;
4560aef6126SJeff Roberson 		return (1);
4570aef6126SJeff Roberson 	}
4585300d9ddSJeff Roberson 
4590aef6126SJeff Roberson 	return (0);
4605300d9ddSJeff Roberson }
4615300d9ddSJeff Roberson 
4625300d9ddSJeff Roberson /*
46364f051e9SJeff Roberson  * Expands the hash table for HASH zones.  This is done from zone_timeout
46464f051e9SJeff Roberson  * to reduce collisions.  This must not be done in the regular allocation
46564f051e9SJeff Roberson  * path, otherwise, we can recurse on the vm while allocating pages.
4668355f576SJeff Roberson  *
4678355f576SJeff Roberson  * Arguments:
4680aef6126SJeff Roberson  *	oldhash  The hash you want to expand
4690aef6126SJeff Roberson  *	newhash  The hash structure for the new table
4708355f576SJeff Roberson  *
4718355f576SJeff Roberson  * Returns:
4728355f576SJeff Roberson  *	Nothing
4738355f576SJeff Roberson  *
4748355f576SJeff Roberson  * Discussion:
4758355f576SJeff Roberson  */
4760aef6126SJeff Roberson static int
4770aef6126SJeff Roberson hash_expand(struct uma_hash *oldhash, struct uma_hash *newhash)
4788355f576SJeff Roberson {
4798355f576SJeff Roberson 	uma_slab_t slab;
4808355f576SJeff Roberson 	int hval;
4818355f576SJeff Roberson 	int i;
4828355f576SJeff Roberson 
4830aef6126SJeff Roberson 	if (!newhash->uh_slab_hash)
4840aef6126SJeff Roberson 		return (0);
4858355f576SJeff Roberson 
4860aef6126SJeff Roberson 	if (oldhash->uh_hashsize >= newhash->uh_hashsize)
4870aef6126SJeff Roberson 		return (0);
4888355f576SJeff Roberson 
4898355f576SJeff Roberson 	/*
4908355f576SJeff Roberson 	 * I need to investigate hash algorithms for resizing without a
4918355f576SJeff Roberson 	 * full rehash.
4928355f576SJeff Roberson 	 */
4938355f576SJeff Roberson 
4940aef6126SJeff Roberson 	for (i = 0; i < oldhash->uh_hashsize; i++)
4950aef6126SJeff Roberson 		while (!SLIST_EMPTY(&oldhash->uh_slab_hash[i])) {
4960aef6126SJeff Roberson 			slab = SLIST_FIRST(&oldhash->uh_slab_hash[i]);
4970aef6126SJeff Roberson 			SLIST_REMOVE_HEAD(&oldhash->uh_slab_hash[i], us_hlink);
4980aef6126SJeff Roberson 			hval = UMA_HASH(newhash, slab->us_data);
4990aef6126SJeff Roberson 			SLIST_INSERT_HEAD(&newhash->uh_slab_hash[hval],
5000aef6126SJeff Roberson 			    slab, us_hlink);
5018355f576SJeff Roberson 		}
5028355f576SJeff Roberson 
5030aef6126SJeff Roberson 	return (1);
5049c2cd7e5SJeff Roberson }
5059c2cd7e5SJeff Roberson 
5065300d9ddSJeff Roberson /*
5075300d9ddSJeff Roberson  * Free the hash bucket to the appropriate backing store.
5085300d9ddSJeff Roberson  *
5095300d9ddSJeff Roberson  * Arguments:
5105300d9ddSJeff Roberson  *	slab_hash  The hash bucket we're freeing
5115300d9ddSJeff Roberson  *	hashsize   The number of entries in that hash bucket
5125300d9ddSJeff Roberson  *
5135300d9ddSJeff Roberson  * Returns:
5145300d9ddSJeff Roberson  *	Nothing
5155300d9ddSJeff Roberson  */
5169c2cd7e5SJeff Roberson static void
5170aef6126SJeff Roberson hash_free(struct uma_hash *hash)
5189c2cd7e5SJeff Roberson {
5190aef6126SJeff Roberson 	if (hash->uh_slab_hash == NULL)
5200aef6126SJeff Roberson 		return;
5210aef6126SJeff Roberson 	if (hash->uh_hashsize == UMA_HASH_SIZE_INIT)
5228355f576SJeff Roberson 		uma_zfree_internal(hashzone,
5230aef6126SJeff Roberson 		    hash->uh_slab_hash, NULL, 0);
5248355f576SJeff Roberson 	else
525961647dfSJeff Roberson 		free(hash->uh_slab_hash, M_UMAHASH);
5268355f576SJeff Roberson }
5278355f576SJeff Roberson 
5288355f576SJeff Roberson /*
5298355f576SJeff Roberson  * Frees all outstanding items in a bucket
5308355f576SJeff Roberson  *
5318355f576SJeff Roberson  * Arguments:
5328355f576SJeff Roberson  *	zone   The zone to free to, must be unlocked.
5338355f576SJeff Roberson  *	bucket The free/alloc bucket with items, cpu queue must be locked.
5348355f576SJeff Roberson  *
5358355f576SJeff Roberson  * Returns:
5368355f576SJeff Roberson  *	Nothing
5378355f576SJeff Roberson  */
5388355f576SJeff Roberson 
5398355f576SJeff Roberson static void
5408355f576SJeff Roberson bucket_drain(uma_zone_t zone, uma_bucket_t bucket)
5418355f576SJeff Roberson {
5428355f576SJeff Roberson 	uma_slab_t slab;
5438355f576SJeff Roberson 	int mzone;
5448355f576SJeff Roberson 	void *item;
5458355f576SJeff Roberson 
5468355f576SJeff Roberson 	if (bucket == NULL)
5478355f576SJeff Roberson 		return;
5488355f576SJeff Roberson 
5498355f576SJeff Roberson 	slab = NULL;
5508355f576SJeff Roberson 	mzone = 0;
5518355f576SJeff Roberson 
5528355f576SJeff Roberson 	/* We have to lookup the slab again for malloc.. */
553099a0e58SBosko Milekic 	if (zone->uz_keg->uk_flags & UMA_ZONE_MALLOC)
5548355f576SJeff Roberson 		mzone = 1;
5558355f576SJeff Roberson 
556cae33c14SJeff Roberson 	while (bucket->ub_cnt > 0)  {
557cae33c14SJeff Roberson 		bucket->ub_cnt--;
558cae33c14SJeff Roberson 		item = bucket->ub_bucket[bucket->ub_cnt];
5598355f576SJeff Roberson #ifdef INVARIANTS
560cae33c14SJeff Roberson 		bucket->ub_bucket[bucket->ub_cnt] = NULL;
5618355f576SJeff Roberson 		KASSERT(item != NULL,
5628355f576SJeff Roberson 		    ("bucket_drain: botched ptr, item is NULL"));
5638355f576SJeff Roberson #endif
5648355f576SJeff Roberson 		/*
5658355f576SJeff Roberson 		 * This is extremely inefficient.  The slab pointer was passed
5668355f576SJeff Roberson 		 * to uma_zfree_arg, but we lost it because the buckets don't
5678355f576SJeff Roberson 		 * hold them.  This will go away when free() gets a size passed
5688355f576SJeff Roberson 		 * to it.
5698355f576SJeff Roberson 		 */
57099571dc3SJeff Roberson 		if (mzone)
57199571dc3SJeff Roberson 			slab = vtoslab((vm_offset_t)item & (~UMA_SLAB_MASK));
5728355f576SJeff Roberson 		uma_zfree_internal(zone, item, slab, 1);
5738355f576SJeff Roberson 	}
5748355f576SJeff Roberson }
5758355f576SJeff Roberson 
5768355f576SJeff Roberson /*
5778355f576SJeff Roberson  * Drains the per cpu caches for a zone.
5788355f576SJeff Roberson  *
5798355f576SJeff Roberson  * Arguments:
5808355f576SJeff Roberson  *	zone     The zone to drain, must be unlocked.
5818355f576SJeff Roberson  *
5828355f576SJeff Roberson  * Returns:
5838355f576SJeff Roberson  *	Nothing
5848355f576SJeff Roberson  */
5858355f576SJeff Roberson static void
5869643769aSJeff Roberson cache_drain(uma_zone_t zone)
5878355f576SJeff Roberson {
5888355f576SJeff Roberson 	uma_cache_t cache;
5898355f576SJeff Roberson 	int cpu;
5908355f576SJeff Roberson 
5918355f576SJeff Roberson 	/*
5928355f576SJeff Roberson 	 * We have to lock each cpu cache before locking the zone
5938355f576SJeff Roberson 	 */
594b6c71225SJohn Baldwin 	for (cpu = 0; cpu <= mp_maxid; cpu++) {
5958355f576SJeff Roberson 		if (CPU_ABSENT(cpu))
5968355f576SJeff Roberson 			continue;
597d88797c2SBosko Milekic 		CPU_LOCK(cpu);
5988355f576SJeff Roberson 		cache = &zone->uz_cpu[cpu];
5998355f576SJeff Roberson 		bucket_drain(zone, cache->uc_allocbucket);
6008355f576SJeff Roberson 		bucket_drain(zone, cache->uc_freebucket);
601174ab450SBosko Milekic 		if (cache->uc_allocbucket != NULL)
602cae33c14SJeff Roberson 			bucket_free(cache->uc_allocbucket);
603174ab450SBosko Milekic 		if (cache->uc_freebucket != NULL)
604cae33c14SJeff Roberson 			bucket_free(cache->uc_freebucket);
605d56368d7SBosko Milekic 		cache->uc_allocbucket = cache->uc_freebucket = NULL;
606d56368d7SBosko Milekic 	}
607aaa8bb16SJeff Roberson 	ZONE_LOCK(zone);
608aaa8bb16SJeff Roberson 	bucket_cache_drain(zone);
609aaa8bb16SJeff Roberson 	ZONE_UNLOCK(zone);
610aaa8bb16SJeff Roberson 	for (cpu = 0; cpu <= mp_maxid; cpu++) {
611aaa8bb16SJeff Roberson 		if (CPU_ABSENT(cpu))
612aaa8bb16SJeff Roberson 			continue;
613aaa8bb16SJeff Roberson 		CPU_UNLOCK(cpu);
614aaa8bb16SJeff Roberson 	}
615aaa8bb16SJeff Roberson }
616aaa8bb16SJeff Roberson 
617aaa8bb16SJeff Roberson /*
618aaa8bb16SJeff Roberson  * Drain the cached buckets from a zone.  Expects a locked zone on entry.
619aaa8bb16SJeff Roberson  */
620aaa8bb16SJeff Roberson static void
621aaa8bb16SJeff Roberson bucket_cache_drain(uma_zone_t zone)
622aaa8bb16SJeff Roberson {
623aaa8bb16SJeff Roberson 	uma_bucket_t bucket;
6248355f576SJeff Roberson 
6258355f576SJeff Roberson 	/*
6268355f576SJeff Roberson 	 * Drain the bucket queues and free the buckets, we just keep two per
6278355f576SJeff Roberson 	 * cpu (alloc/free).
6288355f576SJeff Roberson 	 */
6298355f576SJeff Roberson 	while ((bucket = LIST_FIRST(&zone->uz_full_bucket)) != NULL) {
6308355f576SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
6318355f576SJeff Roberson 		ZONE_UNLOCK(zone);
6328355f576SJeff Roberson 		bucket_drain(zone, bucket);
633cae33c14SJeff Roberson 		bucket_free(bucket);
6348355f576SJeff Roberson 		ZONE_LOCK(zone);
6358355f576SJeff Roberson 	}
6368355f576SJeff Roberson 
6378355f576SJeff Roberson 	/* Now we do the free queue.. */
6388355f576SJeff Roberson 	while ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) {
6398355f576SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
640cae33c14SJeff Roberson 		bucket_free(bucket);
6418355f576SJeff Roberson 	}
6428355f576SJeff Roberson }
6438355f576SJeff Roberson 
6448355f576SJeff Roberson /*
6458355f576SJeff Roberson  * Frees pages from a zone back to the system.  This is done on demand from
6468355f576SJeff Roberson  * the pageout daemon.
6478355f576SJeff Roberson  *
6488355f576SJeff Roberson  * Arguments:
6498355f576SJeff Roberson  *	zone  The zone to free pages from
6509c2cd7e5SJeff Roberson  *	 all  Should we drain all items?
6518355f576SJeff Roberson  *
6528355f576SJeff Roberson  * Returns:
6538355f576SJeff Roberson  *	Nothing.
6548355f576SJeff Roberson  */
6558355f576SJeff Roberson static void
6569643769aSJeff Roberson zone_drain(uma_zone_t zone)
6578355f576SJeff Roberson {
658713deb36SJeff Roberson 	struct slabhead freeslabs = {};
659099a0e58SBosko Milekic 	uma_keg_t keg;
6608355f576SJeff Roberson 	uma_slab_t slab;
6618355f576SJeff Roberson 	uma_slab_t n;
6628355f576SJeff Roberson 	u_int8_t flags;
6638355f576SJeff Roberson 	u_int8_t *mem;
6648355f576SJeff Roberson 	int i;
6658355f576SJeff Roberson 
666099a0e58SBosko Milekic 	keg = zone->uz_keg;
667099a0e58SBosko Milekic 
6688355f576SJeff Roberson 	/*
669099a0e58SBosko Milekic 	 * We don't want to take pages from statically allocated zones at this
6708355f576SJeff Roberson 	 * time
6718355f576SJeff Roberson 	 */
672099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL)
6738355f576SJeff Roberson 		return;
6748355f576SJeff Roberson 
6758355f576SJeff Roberson 	ZONE_LOCK(zone);
6768355f576SJeff Roberson 
6778355f576SJeff Roberson #ifdef UMA_DEBUG
678099a0e58SBosko Milekic 	printf("%s free items: %u\n", zone->uz_name, keg->uk_free);
6798355f576SJeff Roberson #endif
680aaa8bb16SJeff Roberson 	bucket_cache_drain(zone);
681099a0e58SBosko Milekic 	if (keg->uk_free == 0)
6828355f576SJeff Roberson 		goto finished;
6838355f576SJeff Roberson 
684099a0e58SBosko Milekic 	slab = LIST_FIRST(&keg->uk_free_slab);
6859643769aSJeff Roberson 	while (slab) {
6868355f576SJeff Roberson 		n = LIST_NEXT(slab, us_link);
6878355f576SJeff Roberson 
6888355f576SJeff Roberson 		/* We have no where to free these to */
6898355f576SJeff Roberson 		if (slab->us_flags & UMA_SLAB_BOOT) {
6908355f576SJeff Roberson 			slab = n;
6918355f576SJeff Roberson 			continue;
6928355f576SJeff Roberson 		}
6938355f576SJeff Roberson 
6948355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
695099a0e58SBosko Milekic 		keg->uk_pages -= keg->uk_ppera;
696099a0e58SBosko Milekic 		keg->uk_free -= keg->uk_ipers;
697713deb36SJeff Roberson 
698099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZONE_HASH)
699099a0e58SBosko Milekic 			UMA_HASH_REMOVE(&keg->uk_hash, slab, slab->us_data);
700713deb36SJeff Roberson 
701713deb36SJeff Roberson 		SLIST_INSERT_HEAD(&freeslabs, slab, us_hlink);
702713deb36SJeff Roberson 
703713deb36SJeff Roberson 		slab = n;
704713deb36SJeff Roberson 	}
705713deb36SJeff Roberson finished:
706713deb36SJeff Roberson 	ZONE_UNLOCK(zone);
707713deb36SJeff Roberson 
708713deb36SJeff Roberson 	while ((slab = SLIST_FIRST(&freeslabs)) != NULL) {
709713deb36SJeff Roberson 		SLIST_REMOVE(&freeslabs, slab, uma_slab, us_hlink);
710099a0e58SBosko Milekic 		if (keg->uk_fini)
711099a0e58SBosko Milekic 			for (i = 0; i < keg->uk_ipers; i++)
712099a0e58SBosko Milekic 				keg->uk_fini(
713099a0e58SBosko Milekic 				    slab->us_data + (keg->uk_rsize * i),
714099a0e58SBosko Milekic 				    keg->uk_size);
7158355f576SJeff Roberson 		flags = slab->us_flags;
7168355f576SJeff Roberson 		mem = slab->us_data;
71799571dc3SJeff Roberson 
718099a0e58SBosko Milekic 		if ((keg->uk_flags & UMA_ZONE_MALLOC) ||
719099a0e58SBosko Milekic 		    (keg->uk_flags & UMA_ZONE_REFCNT)) {
72048eea375SJeff Roberson 			vm_object_t obj;
72148eea375SJeff Roberson 
72248eea375SJeff Roberson 			if (flags & UMA_SLAB_KMEM)
72348eea375SJeff Roberson 				obj = kmem_object;
72448eea375SJeff Roberson 			else
72548eea375SJeff Roberson 				obj = NULL;
726099a0e58SBosko Milekic 			for (i = 0; i < keg->uk_ppera; i++)
72799571dc3SJeff Roberson 				vsetobj((vm_offset_t)mem + (i * PAGE_SIZE),
72848eea375SJeff Roberson 				    obj);
72948eea375SJeff Roberson 		}
730099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZONE_OFFPAGE)
731099a0e58SBosko Milekic 			uma_zfree_internal(keg->uk_slabzone, slab, NULL, 0);
7328355f576SJeff Roberson #ifdef UMA_DEBUG
7338355f576SJeff Roberson 		printf("%s: Returning %d bytes.\n",
734099a0e58SBosko Milekic 		    zone->uz_name, UMA_SLAB_SIZE * keg->uk_ppera);
7358355f576SJeff Roberson #endif
736099a0e58SBosko Milekic 		keg->uk_freef(mem, UMA_SLAB_SIZE * keg->uk_ppera, flags);
7378355f576SJeff Roberson 	}
7388355f576SJeff Roberson }
7398355f576SJeff Roberson 
7408355f576SJeff Roberson /*
7418355f576SJeff Roberson  * Allocate a new slab for a zone.  This does not insert the slab onto a list.
7428355f576SJeff Roberson  *
7438355f576SJeff Roberson  * Arguments:
7448355f576SJeff Roberson  *	zone  The zone to allocate slabs for
7458355f576SJeff Roberson  *	wait  Shall we wait?
7468355f576SJeff Roberson  *
7478355f576SJeff Roberson  * Returns:
7488355f576SJeff Roberson  *	The slab that was allocated or NULL if there is no memory and the
7498355f576SJeff Roberson  *	caller specified M_NOWAIT.
7508355f576SJeff Roberson  */
7518355f576SJeff Roberson static uma_slab_t
7528355f576SJeff Roberson slab_zalloc(uma_zone_t zone, int wait)
7538355f576SJeff Roberson {
754099a0e58SBosko Milekic 	uma_slabrefcnt_t slabref;
755099a0e58SBosko Milekic 	uma_slab_t slab;
756099a0e58SBosko Milekic 	uma_keg_t keg;
7578355f576SJeff Roberson 	u_int8_t *mem;
7588355f576SJeff Roberson 	u_int8_t flags;
7598355f576SJeff Roberson 	int i;
7608355f576SJeff Roberson 
761a553d4b8SJeff Roberson 	slab = NULL;
762099a0e58SBosko Milekic 	keg = zone->uz_keg;
763a553d4b8SJeff Roberson 
7648355f576SJeff Roberson #ifdef UMA_DEBUG
7658355f576SJeff Roberson 	printf("slab_zalloc:  Allocating a new slab for %s\n", zone->uz_name);
7668355f576SJeff Roberson #endif
7678355f576SJeff Roberson 	ZONE_UNLOCK(zone);
768a553d4b8SJeff Roberson 
769099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_OFFPAGE) {
770099a0e58SBosko Milekic 		slab = uma_zalloc_internal(keg->uk_slabzone, NULL, wait);
771a553d4b8SJeff Roberson 		if (slab == NULL) {
772a553d4b8SJeff Roberson 			ZONE_LOCK(zone);
773a553d4b8SJeff Roberson 			return NULL;
774a553d4b8SJeff Roberson 		}
775a553d4b8SJeff Roberson 	}
776a553d4b8SJeff Roberson 
7773370c5bfSJeff Roberson 	/*
7783370c5bfSJeff Roberson 	 * This reproduces the old vm_zone behavior of zero filling pages the
7793370c5bfSJeff Roberson 	 * first time they are added to a zone.
7803370c5bfSJeff Roberson 	 *
7813370c5bfSJeff Roberson 	 * Malloced items are zeroed in uma_zalloc.
7823370c5bfSJeff Roberson 	 */
7833370c5bfSJeff Roberson 
784099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0)
7853370c5bfSJeff Roberson 		wait |= M_ZERO;
7863370c5bfSJeff Roberson 	else
7873370c5bfSJeff Roberson 		wait &= ~M_ZERO;
7883370c5bfSJeff Roberson 
789099a0e58SBosko Milekic 	mem = keg->uk_allocf(zone, keg->uk_ppera * UMA_SLAB_SIZE,
790234c7726SAlan Cox 	    &flags, wait);
791a553d4b8SJeff Roberson 	if (mem == NULL) {
7928355f576SJeff Roberson 		ZONE_LOCK(zone);
7938355f576SJeff Roberson 		return (NULL);
794a553d4b8SJeff Roberson 	}
7958355f576SJeff Roberson 
7965c0e403bSJeff Roberson 	/* Point the slab into the allocated memory */
797099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_OFFPAGE))
798099a0e58SBosko Milekic 		slab = (uma_slab_t )(mem + keg->uk_pgoff);
7995c0e403bSJeff Roberson 
800099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZONE_MALLOC) ||
801099a0e58SBosko Milekic 	    (keg->uk_flags & UMA_ZONE_REFCNT))
802099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ppera; i++)
80399571dc3SJeff Roberson 			vsetslab((vm_offset_t)mem + (i * PAGE_SIZE), slab);
8048355f576SJeff Roberson 
805099a0e58SBosko Milekic 	slab->us_keg = keg;
8068355f576SJeff Roberson 	slab->us_data = mem;
807099a0e58SBosko Milekic 	slab->us_freecount = keg->uk_ipers;
8088355f576SJeff Roberson 	slab->us_firstfree = 0;
8098355f576SJeff Roberson 	slab->us_flags = flags;
810099a0e58SBosko Milekic 	for (i = 0; i < keg->uk_ipers; i++)
811099a0e58SBosko Milekic 		slab->us_freelist[i].us_item = i+1;
8128355f576SJeff Roberson 
813099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_REFCNT) {
814099a0e58SBosko Milekic 		slabref = (uma_slabrefcnt_t)slab;
815099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ipers; i++)
816099a0e58SBosko Milekic 			slabref->us_freelist[i].us_refcnt = 0;
817099a0e58SBosko Milekic 	}
818099a0e58SBosko Milekic 
819099a0e58SBosko Milekic 	if (keg->uk_init)
820099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ipers; i++)
821099a0e58SBosko Milekic 			keg->uk_init(slab->us_data + (keg->uk_rsize * i),
822099a0e58SBosko Milekic 			    keg->uk_size);
8235c0e403bSJeff Roberson 	ZONE_LOCK(zone);
8245c0e403bSJeff Roberson 
825099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH)
826099a0e58SBosko Milekic 		UMA_HASH_INSERT(&keg->uk_hash, slab, mem);
8278355f576SJeff Roberson 
828099a0e58SBosko Milekic 	keg->uk_pages += keg->uk_ppera;
829099a0e58SBosko Milekic 	keg->uk_free += keg->uk_ipers;
8308355f576SJeff Roberson 
8318355f576SJeff Roberson 	return (slab);
8328355f576SJeff Roberson }
8338355f576SJeff Roberson 
8348355f576SJeff Roberson /*
835009b6fcbSJeff Roberson  * This function is intended to be used early on in place of page_alloc() so
836009b6fcbSJeff Roberson  * that we may use the boot time page cache to satisfy allocations before
837009b6fcbSJeff Roberson  * the VM is ready.
838009b6fcbSJeff Roberson  */
839009b6fcbSJeff Roberson static void *
840009b6fcbSJeff Roberson startup_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait)
841009b6fcbSJeff Roberson {
842099a0e58SBosko Milekic 	uma_keg_t keg;
843099a0e58SBosko Milekic 
844099a0e58SBosko Milekic 	keg = zone->uz_keg;
845099a0e58SBosko Milekic 
846009b6fcbSJeff Roberson 	/*
847009b6fcbSJeff Roberson 	 * Check our small startup cache to see if it has pages remaining.
848009b6fcbSJeff Roberson 	 */
849009b6fcbSJeff Roberson 	mtx_lock(&uma_mtx);
850009b6fcbSJeff Roberson 	if (uma_boot_free != 0) {
851009b6fcbSJeff Roberson 		uma_slab_t tmps;
852009b6fcbSJeff Roberson 
853009b6fcbSJeff Roberson 		tmps = LIST_FIRST(&uma_boot_pages);
854009b6fcbSJeff Roberson 		LIST_REMOVE(tmps, us_link);
855009b6fcbSJeff Roberson 		uma_boot_free--;
856009b6fcbSJeff Roberson 		mtx_unlock(&uma_mtx);
857009b6fcbSJeff Roberson 		*pflag = tmps->us_flags;
858009b6fcbSJeff Roberson 		return (tmps->us_data);
859009b6fcbSJeff Roberson 	}
860009b6fcbSJeff Roberson 	mtx_unlock(&uma_mtx);
861009b6fcbSJeff Roberson 	if (booted == 0)
862009b6fcbSJeff Roberson 		panic("UMA: Increase UMA_BOOT_PAGES");
863009b6fcbSJeff Roberson 	/*
864009b6fcbSJeff Roberson 	 * Now that we've booted reset these users to their real allocator.
865009b6fcbSJeff Roberson 	 */
866009b6fcbSJeff Roberson #ifdef UMA_MD_SMALL_ALLOC
867099a0e58SBosko Milekic 	keg->uk_allocf = uma_small_alloc;
868009b6fcbSJeff Roberson #else
869099a0e58SBosko Milekic 	keg->uk_allocf = page_alloc;
870009b6fcbSJeff Roberson #endif
871099a0e58SBosko Milekic 	return keg->uk_allocf(zone, bytes, pflag, wait);
872009b6fcbSJeff Roberson }
873009b6fcbSJeff Roberson 
874009b6fcbSJeff Roberson /*
8758355f576SJeff Roberson  * Allocates a number of pages from the system
8768355f576SJeff Roberson  *
8778355f576SJeff Roberson  * Arguments:
8788355f576SJeff Roberson  *	zone  Unused
8798355f576SJeff Roberson  *	bytes  The number of bytes requested
8808355f576SJeff Roberson  *	wait  Shall we wait?
8818355f576SJeff Roberson  *
8828355f576SJeff Roberson  * Returns:
8838355f576SJeff Roberson  *	A pointer to the alloced memory or possibly
8848355f576SJeff Roberson  *	NULL if M_NOWAIT is set.
8858355f576SJeff Roberson  */
8868355f576SJeff Roberson static void *
8878355f576SJeff Roberson page_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait)
8888355f576SJeff Roberson {
8898355f576SJeff Roberson 	void *p;	/* Returned page */
8908355f576SJeff Roberson 
8918355f576SJeff Roberson 	*pflag = UMA_SLAB_KMEM;
8928355f576SJeff Roberson 	p = (void *) kmem_malloc(kmem_map, bytes, wait);
8938355f576SJeff Roberson 
8948355f576SJeff Roberson 	return (p);
8958355f576SJeff Roberson }
8968355f576SJeff Roberson 
8978355f576SJeff Roberson /*
8988355f576SJeff Roberson  * Allocates a number of pages from within an object
8998355f576SJeff Roberson  *
9008355f576SJeff Roberson  * Arguments:
9018355f576SJeff Roberson  *	zone   Unused
9028355f576SJeff Roberson  *	bytes  The number of bytes requested
9038355f576SJeff Roberson  *	wait   Shall we wait?
9048355f576SJeff Roberson  *
9058355f576SJeff Roberson  * Returns:
9068355f576SJeff Roberson  *	A pointer to the alloced memory or possibly
9078355f576SJeff Roberson  *	NULL if M_NOWAIT is set.
9088355f576SJeff Roberson  */
9098355f576SJeff Roberson static void *
9108355f576SJeff Roberson obj_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int wait)
9118355f576SJeff Roberson {
912b245ac95SAlan Cox 	vm_object_t object;
913b245ac95SAlan Cox 	vm_offset_t retkva, zkva;
9148355f576SJeff Roberson 	vm_page_t p;
915b245ac95SAlan Cox 	int pages, startpages;
9168355f576SJeff Roberson 
917099a0e58SBosko Milekic 	object = zone->uz_keg->uk_obj;
91855f7c614SArchie Cobbs 	retkva = 0;
9198355f576SJeff Roberson 
9208355f576SJeff Roberson 	/*
92164f051e9SJeff Roberson 	 * This looks a little weird since we're getting one page at a time.
9228355f576SJeff Roberson 	 */
923b245ac95SAlan Cox 	VM_OBJECT_LOCK(object);
924b245ac95SAlan Cox 	p = TAILQ_LAST(&object->memq, pglist);
925b245ac95SAlan Cox 	pages = p != NULL ? p->pindex + 1 : 0;
926b245ac95SAlan Cox 	startpages = pages;
927099a0e58SBosko Milekic 	zkva = zone->uz_keg->uk_kva + pages * PAGE_SIZE;
928b245ac95SAlan Cox 	for (; bytes > 0; bytes -= PAGE_SIZE) {
929b245ac95SAlan Cox 		p = vm_page_alloc(object, pages,
930b245ac95SAlan Cox 		    VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED);
931b245ac95SAlan Cox 		if (p == NULL) {
932b245ac95SAlan Cox 			if (pages != startpages)
933b245ac95SAlan Cox 				pmap_qremove(retkva, pages - startpages);
934b245ac95SAlan Cox 			while (pages != startpages) {
935b245ac95SAlan Cox 				pages--;
936b245ac95SAlan Cox 				p = TAILQ_LAST(&object->memq, pglist);
937b245ac95SAlan Cox 				vm_page_lock_queues();
938b245ac95SAlan Cox 				vm_page_unwire(p, 0);
939b245ac95SAlan Cox 				vm_page_free(p);
940b245ac95SAlan Cox 				vm_page_unlock_queues();
941b245ac95SAlan Cox 			}
942b245ac95SAlan Cox 			retkva = 0;
943b245ac95SAlan Cox 			goto done;
944b245ac95SAlan Cox 		}
945b245ac95SAlan Cox 		pmap_qenter(zkva, &p, 1);
94655f7c614SArchie Cobbs 		if (retkva == 0)
9478355f576SJeff Roberson 			retkva = zkva;
948b245ac95SAlan Cox 		zkva += PAGE_SIZE;
9498355f576SJeff Roberson 		pages += 1;
9508355f576SJeff Roberson 	}
951b245ac95SAlan Cox done:
952b245ac95SAlan Cox 	VM_OBJECT_UNLOCK(object);
9538355f576SJeff Roberson 	*flags = UMA_SLAB_PRIV;
9548355f576SJeff Roberson 
9558355f576SJeff Roberson 	return ((void *)retkva);
9568355f576SJeff Roberson }
9578355f576SJeff Roberson 
9588355f576SJeff Roberson /*
9598355f576SJeff Roberson  * Frees a number of pages to the system
9608355f576SJeff Roberson  *
9618355f576SJeff Roberson  * Arguments:
9628355f576SJeff Roberson  *	mem   A pointer to the memory to be freed
9638355f576SJeff Roberson  *	size  The size of the memory being freed
9648355f576SJeff Roberson  *	flags The original p->us_flags field
9658355f576SJeff Roberson  *
9668355f576SJeff Roberson  * Returns:
9678355f576SJeff Roberson  *	Nothing
9688355f576SJeff Roberson  */
9698355f576SJeff Roberson static void
9708355f576SJeff Roberson page_free(void *mem, int size, u_int8_t flags)
9718355f576SJeff Roberson {
9728355f576SJeff Roberson 	vm_map_t map;
9733370c5bfSJeff Roberson 
9748355f576SJeff Roberson 	if (flags & UMA_SLAB_KMEM)
9758355f576SJeff Roberson 		map = kmem_map;
9768355f576SJeff Roberson 	else
9778355f576SJeff Roberson 		panic("UMA: page_free used with invalid flags %d\n", flags);
9788355f576SJeff Roberson 
9798355f576SJeff Roberson 	kmem_free(map, (vm_offset_t)mem, size);
9808355f576SJeff Roberson }
9818355f576SJeff Roberson 
9828355f576SJeff Roberson /*
9838355f576SJeff Roberson  * Zero fill initializer
9848355f576SJeff Roberson  *
9858355f576SJeff Roberson  * Arguments/Returns follow uma_init specifications
9868355f576SJeff Roberson  */
9878355f576SJeff Roberson static void
9888355f576SJeff Roberson zero_init(void *mem, int size)
9898355f576SJeff Roberson {
9908355f576SJeff Roberson 	bzero(mem, size);
9918355f576SJeff Roberson }
9928355f576SJeff Roberson 
9938355f576SJeff Roberson /*
9948355f576SJeff Roberson  * Finish creating a small uma zone.  This calculates ipers, and the zone size.
9958355f576SJeff Roberson  *
9968355f576SJeff Roberson  * Arguments
9978355f576SJeff Roberson  *	zone  The zone we should initialize
9988355f576SJeff Roberson  *
9998355f576SJeff Roberson  * Returns
10008355f576SJeff Roberson  *	Nothing
10018355f576SJeff Roberson  */
10028355f576SJeff Roberson static void
10038355f576SJeff Roberson zone_small_init(uma_zone_t zone)
10048355f576SJeff Roberson {
1005099a0e58SBosko Milekic 	uma_keg_t keg;
10068355f576SJeff Roberson 	int rsize;
10078355f576SJeff Roberson 	int memused;
10088355f576SJeff Roberson 	int ipers;
10098355f576SJeff Roberson 
1010099a0e58SBosko Milekic 	keg = zone->uz_keg;
1011099a0e58SBosko Milekic 	KASSERT(keg != NULL, ("Keg is null in zone_small_init"));
1012099a0e58SBosko Milekic 	rsize = keg->uk_size;
10138355f576SJeff Roberson 
10148355f576SJeff Roberson 	if (rsize < UMA_SMALLEST_UNIT)
10158355f576SJeff Roberson 		rsize = UMA_SMALLEST_UNIT;
10168355f576SJeff Roberson 
1017099a0e58SBosko Milekic 	if (rsize & keg->uk_align)
1018099a0e58SBosko Milekic 		rsize = (rsize & ~keg->uk_align) + (keg->uk_align + 1);
10198355f576SJeff Roberson 
1020099a0e58SBosko Milekic 	keg->uk_rsize = rsize;
10218355f576SJeff Roberson 
10228355f576SJeff Roberson 	rsize += 1;	/* Account for the byte of linkage */
1023099a0e58SBosko Milekic 	keg->uk_ipers = (UMA_SLAB_SIZE - sizeof(struct uma_slab)) / rsize;
1024099a0e58SBosko Milekic 	keg->uk_ppera = 1;
10258355f576SJeff Roberson 
1026099a0e58SBosko Milekic 	KASSERT(keg->uk_ipers != 0, ("zone_small_init: ipers is 0, uh-oh!"));
1027099a0e58SBosko Milekic 	memused = keg->uk_ipers * keg->uk_rsize;
10288355f576SJeff Roberson 
10298355f576SJeff Roberson 	/* Can we do any better? */
1030099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZONE_REFCNT) ||
1031099a0e58SBosko Milekic 	    ((UMA_SLAB_SIZE - memused) >= UMA_MAX_WASTE)) {
103220e8e865SBosko Milekic 		/*
103320e8e865SBosko Milekic 		 * We can't do this if we're internal or if we've been
103420e8e865SBosko Milekic 		 * asked to not go to the VM for buckets.  If we do this we
103520e8e865SBosko Milekic 		 * may end up going to the VM (kmem_map) for slabs which we
103620e8e865SBosko Milekic 		 * do not want to do if we're UMA_ZFLAG_CACHEONLY as a
103720e8e865SBosko Milekic 		 * result of UMA_ZONE_VM, which clearly forbids it.
103820e8e865SBosko Milekic 		 */
1039099a0e58SBosko Milekic 		if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) ||
1040099a0e58SBosko Milekic 		    (keg->uk_flags & UMA_ZFLAG_CACHEONLY))
10418355f576SJeff Roberson 			return;
1042099a0e58SBosko Milekic 		ipers = UMA_SLAB_SIZE / keg->uk_rsize;
1043099a0e58SBosko Milekic 		if ((keg->uk_flags & UMA_ZONE_REFCNT) ||
1044099a0e58SBosko Milekic 		    (ipers > keg->uk_ipers)) {
1045099a0e58SBosko Milekic 			keg->uk_flags |= UMA_ZONE_OFFPAGE;
1046099a0e58SBosko Milekic 			if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0)
1047099a0e58SBosko Milekic 				keg->uk_flags |= UMA_ZONE_HASH;
1048099a0e58SBosko Milekic 			keg->uk_ipers = ipers;
10498355f576SJeff Roberson 		}
10508355f576SJeff Roberson 	}
10518355f576SJeff Roberson }
10528355f576SJeff Roberson 
10538355f576SJeff Roberson /*
10548355f576SJeff Roberson  * Finish creating a large (> UMA_SLAB_SIZE) uma zone.  Just give in and do
10558355f576SJeff Roberson  * OFFPAGE for now.  When I can allow for more dynamic slab sizes this will be
10568355f576SJeff Roberson  * more complicated.
10578355f576SJeff Roberson  *
10588355f576SJeff Roberson  * Arguments
10598355f576SJeff Roberson  *	zone  The zone we should initialize
10608355f576SJeff Roberson  *
10618355f576SJeff Roberson  * Returns
10628355f576SJeff Roberson  *	Nothing
10638355f576SJeff Roberson  */
10648355f576SJeff Roberson static void
10658355f576SJeff Roberson zone_large_init(uma_zone_t zone)
10668355f576SJeff Roberson {
1067099a0e58SBosko Milekic 	uma_keg_t keg;
10688355f576SJeff Roberson 	int pages;
10698355f576SJeff Roberson 
1070099a0e58SBosko Milekic 	keg = zone->uz_keg;
1071099a0e58SBosko Milekic 
1072099a0e58SBosko Milekic 	KASSERT(keg != NULL, ("Keg is null in zone_large_init"));
1073099a0e58SBosko Milekic 	KASSERT((keg->uk_flags & UMA_ZFLAG_CACHEONLY) == 0,
107420e8e865SBosko Milekic 	    ("zone_large_init: Cannot large-init a UMA_ZFLAG_CACHEONLY zone"));
107520e8e865SBosko Milekic 
1076099a0e58SBosko Milekic 	pages = keg->uk_size / UMA_SLAB_SIZE;
10778355f576SJeff Roberson 
10788355f576SJeff Roberson 	/* Account for remainder */
1079099a0e58SBosko Milekic 	if ((pages * UMA_SLAB_SIZE) < keg->uk_size)
10808355f576SJeff Roberson 		pages++;
10818355f576SJeff Roberson 
1082099a0e58SBosko Milekic 	keg->uk_ppera = pages;
1083099a0e58SBosko Milekic 	keg->uk_ipers = 1;
10848355f576SJeff Roberson 
1085099a0e58SBosko Milekic 	keg->uk_flags |= UMA_ZONE_OFFPAGE;
1086099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0)
1087099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZONE_HASH;
108899571dc3SJeff Roberson 
1089099a0e58SBosko Milekic 	keg->uk_rsize = keg->uk_size;
10908355f576SJeff Roberson }
10918355f576SJeff Roberson 
10928355f576SJeff Roberson /*
1093099a0e58SBosko Milekic  * Keg header ctor.  This initializes all fields, locks, etc.  And inserts
1094099a0e58SBosko Milekic  * the keg onto the global keg list.
10958355f576SJeff Roberson  *
10968355f576SJeff Roberson  * Arguments/Returns follow uma_ctor specifications
1097099a0e58SBosko Milekic  *	udata  Actually uma_kctor_args
1098099a0e58SBosko Milekic  */
1099099a0e58SBosko Milekic static void
1100099a0e58SBosko Milekic keg_ctor(void *mem, int size, void *udata)
1101099a0e58SBosko Milekic {
1102099a0e58SBosko Milekic 	struct uma_kctor_args *arg = udata;
1103099a0e58SBosko Milekic 	uma_keg_t keg = mem;
1104099a0e58SBosko Milekic 	uma_zone_t zone;
1105099a0e58SBosko Milekic 
1106099a0e58SBosko Milekic 	bzero(keg, size);
1107099a0e58SBosko Milekic 	keg->uk_size = arg->size;
1108099a0e58SBosko Milekic 	keg->uk_init = arg->uminit;
1109099a0e58SBosko Milekic 	keg->uk_fini = arg->fini;
1110099a0e58SBosko Milekic 	keg->uk_align = arg->align;
1111099a0e58SBosko Milekic 	keg->uk_free = 0;
1112099a0e58SBosko Milekic 	keg->uk_pages = 0;
1113099a0e58SBosko Milekic 	keg->uk_flags = arg->flags;
1114099a0e58SBosko Milekic 	keg->uk_allocf = page_alloc;
1115099a0e58SBosko Milekic 	keg->uk_freef = page_free;
1116099a0e58SBosko Milekic 	keg->uk_recurse = 0;
1117099a0e58SBosko Milekic 	keg->uk_slabzone = NULL;
1118099a0e58SBosko Milekic 
1119099a0e58SBosko Milekic 	/*
1120099a0e58SBosko Milekic 	 * The master zone is passed to us at keg-creation time.
1121099a0e58SBosko Milekic 	 */
1122099a0e58SBosko Milekic 	zone = arg->zone;
1123099a0e58SBosko Milekic 	zone->uz_keg = keg;
1124099a0e58SBosko Milekic 
1125099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_VM)
1126099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZFLAG_CACHEONLY;
1127099a0e58SBosko Milekic 
1128099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_ZINIT)
1129099a0e58SBosko Milekic 		keg->uk_init = zero_init;
1130099a0e58SBosko Milekic 
1131099a0e58SBosko Milekic 	/*
1132099a0e58SBosko Milekic 	 * The +1 byte added to uk_size is to account for the byte of
1133099a0e58SBosko Milekic 	 * linkage that is added to the size in zone_small_init().  If
1134099a0e58SBosko Milekic 	 * we don't account for this here then we may end up in
1135099a0e58SBosko Milekic 	 * zone_small_init() with a calculated 'ipers' of 0.
1136099a0e58SBosko Milekic 	 */
1137099a0e58SBosko Milekic 	if ((keg->uk_size+1) > (UMA_SLAB_SIZE - sizeof(struct uma_slab)))
1138099a0e58SBosko Milekic 		zone_large_init(zone);
1139099a0e58SBosko Milekic 	else
1140099a0e58SBosko Milekic 		zone_small_init(zone);
1141099a0e58SBosko Milekic 
1142099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_REFCNT)
1143099a0e58SBosko Milekic 		keg->uk_slabzone = slabrefzone;
1144099a0e58SBosko Milekic 	else if (keg->uk_flags & UMA_ZONE_OFFPAGE)
1145099a0e58SBosko Milekic 		keg->uk_slabzone = slabzone;
1146099a0e58SBosko Milekic 
1147099a0e58SBosko Milekic 	/*
1148099a0e58SBosko Milekic 	 * If we haven't booted yet we need allocations to go through the
1149099a0e58SBosko Milekic 	 * startup cache until the vm is ready.
1150099a0e58SBosko Milekic 	 */
1151099a0e58SBosko Milekic 	if (keg->uk_ppera == 1) {
1152099a0e58SBosko Milekic #ifdef UMA_MD_SMALL_ALLOC
1153099a0e58SBosko Milekic 		keg->uk_allocf = uma_small_alloc;
1154099a0e58SBosko Milekic 		keg->uk_freef = uma_small_free;
1155099a0e58SBosko Milekic #endif
1156099a0e58SBosko Milekic 		if (booted == 0)
1157099a0e58SBosko Milekic 			keg->uk_allocf = startup_alloc;
1158099a0e58SBosko Milekic 	}
1159099a0e58SBosko Milekic 
1160099a0e58SBosko Milekic 	/*
1161099a0e58SBosko Milekic 	 * Initialize keg's lock (shared among zones) through
1162099a0e58SBosko Milekic 	 * Master zone
1163099a0e58SBosko Milekic 	 */
1164099a0e58SBosko Milekic 	zone->uz_lock = &keg->uk_lock;
1165099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_MTXCLASS)
1166099a0e58SBosko Milekic 		ZONE_LOCK_INIT(zone, 1);
1167099a0e58SBosko Milekic 	else
1168099a0e58SBosko Milekic 		ZONE_LOCK_INIT(zone, 0);
1169099a0e58SBosko Milekic 
1170099a0e58SBosko Milekic 	/*
1171099a0e58SBosko Milekic 	 * If we're putting the slab header in the actual page we need to
1172099a0e58SBosko Milekic 	 * figure out where in each page it goes.  This calculates a right
1173099a0e58SBosko Milekic 	 * justified offset into the memory on an ALIGN_PTR boundary.
1174099a0e58SBosko Milekic 	 */
1175099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) {
1176099a0e58SBosko Milekic 		int totsize;
1177099a0e58SBosko Milekic 
1178099a0e58SBosko Milekic 		/* Size of the slab struct and free list */
1179099a0e58SBosko Milekic 		totsize = sizeof(struct uma_slab) + keg->uk_ipers;
1180099a0e58SBosko Milekic 		if (totsize & UMA_ALIGN_PTR)
1181099a0e58SBosko Milekic 			totsize = (totsize & ~UMA_ALIGN_PTR) +
1182099a0e58SBosko Milekic 			    (UMA_ALIGN_PTR + 1);
1183099a0e58SBosko Milekic 		keg->uk_pgoff = UMA_SLAB_SIZE - totsize;
1184099a0e58SBosko Milekic 		totsize = keg->uk_pgoff + sizeof(struct uma_slab)
1185099a0e58SBosko Milekic 		    + keg->uk_ipers;
1186099a0e58SBosko Milekic 		/* I don't think it's possible, but I'll make sure anyway */
1187099a0e58SBosko Milekic 		if (totsize > UMA_SLAB_SIZE) {
1188099a0e58SBosko Milekic 			printf("zone %s ipers %d rsize %d size %d\n",
1189099a0e58SBosko Milekic 			    zone->uz_name, keg->uk_ipers, keg->uk_rsize,
1190099a0e58SBosko Milekic 			    keg->uk_size);
1191099a0e58SBosko Milekic 			panic("UMA slab won't fit.\n");
1192099a0e58SBosko Milekic 		}
1193099a0e58SBosko Milekic 	}
1194099a0e58SBosko Milekic 
1195099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH)
1196099a0e58SBosko Milekic 		hash_alloc(&keg->uk_hash);
1197099a0e58SBosko Milekic 
1198099a0e58SBosko Milekic #ifdef UMA_DEBUG
1199099a0e58SBosko Milekic 	printf("%s(%p) size = %d ipers = %d ppera = %d pgoff = %d\n",
1200099a0e58SBosko Milekic 	    zone->uz_name, zone,
1201099a0e58SBosko Milekic 	    keg->uk_size, keg->uk_ipers,
1202099a0e58SBosko Milekic 	    keg->uk_ppera, keg->uk_pgoff);
1203099a0e58SBosko Milekic #endif
1204099a0e58SBosko Milekic 
1205099a0e58SBosko Milekic 	LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link);
1206099a0e58SBosko Milekic 
1207099a0e58SBosko Milekic 	mtx_lock(&uma_mtx);
1208099a0e58SBosko Milekic 	LIST_INSERT_HEAD(&uma_kegs, keg, uk_link);
1209099a0e58SBosko Milekic 	mtx_unlock(&uma_mtx);
1210099a0e58SBosko Milekic }
1211099a0e58SBosko Milekic 
1212099a0e58SBosko Milekic /*
1213099a0e58SBosko Milekic  * Zone header ctor.  This initializes all fields, locks, etc.
1214099a0e58SBosko Milekic  *
1215099a0e58SBosko Milekic  * Arguments/Returns follow uma_ctor specifications
1216099a0e58SBosko Milekic  *	udata  Actually uma_zctor_args
12178355f576SJeff Roberson  */
12188355f576SJeff Roberson 
12198355f576SJeff Roberson static void
12208355f576SJeff Roberson zone_ctor(void *mem, int size, void *udata)
12218355f576SJeff Roberson {
12228355f576SJeff Roberson 	struct uma_zctor_args *arg = udata;
12238355f576SJeff Roberson 	uma_zone_t zone = mem;
1224099a0e58SBosko Milekic 	uma_zone_t z;
1225099a0e58SBosko Milekic 	uma_keg_t keg;
12268355f576SJeff Roberson 
12278355f576SJeff Roberson 	bzero(zone, size);
12288355f576SJeff Roberson 	zone->uz_name = arg->name;
12298355f576SJeff Roberson 	zone->uz_ctor = arg->ctor;
12308355f576SJeff Roberson 	zone->uz_dtor = arg->dtor;
1231099a0e58SBosko Milekic 	zone->uz_init = NULL;
1232099a0e58SBosko Milekic 	zone->uz_fini = NULL;
1233099a0e58SBosko Milekic 	zone->uz_allocs = 0;
1234099a0e58SBosko Milekic 	zone->uz_fills = zone->uz_count = 0;
1235099a0e58SBosko Milekic 
1236099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_SECONDARY) {
1237099a0e58SBosko Milekic 		KASSERT(arg->keg != NULL, ("Secondary zone on zero'd keg"));
1238099a0e58SBosko Milekic 		keg = arg->keg;
1239099a0e58SBosko Milekic 		zone->uz_keg = keg;
12408355f576SJeff Roberson 		zone->uz_init = arg->uminit;
1241e221e841SJeff Roberson 		zone->uz_fini = arg->fini;
1242099a0e58SBosko Milekic 		zone->uz_lock = &keg->uk_lock;
12438355f576SJeff Roberson 		mtx_lock(&uma_mtx);
1244099a0e58SBosko Milekic 		ZONE_LOCK(zone);
1245099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZONE_SECONDARY;
1246099a0e58SBosko Milekic 		LIST_FOREACH(z, &keg->uk_zones, uz_link) {
1247099a0e58SBosko Milekic 			if (LIST_NEXT(z, uz_link) == NULL) {
1248099a0e58SBosko Milekic 				LIST_INSERT_AFTER(z, zone, uz_link);
1249099a0e58SBosko Milekic 				break;
1250099a0e58SBosko Milekic 			}
1251099a0e58SBosko Milekic 		}
1252099a0e58SBosko Milekic 		ZONE_UNLOCK(zone);
12538355f576SJeff Roberson 		mtx_unlock(&uma_mtx);
1254099a0e58SBosko Milekic 	} else if (arg->keg == NULL) {
1255099a0e58SBosko Milekic 		uma_kcreate(zone, arg->size, arg->uminit, arg->fini,
1256099a0e58SBosko Milekic 		    arg->align, arg->flags);
1257099a0e58SBosko Milekic 	} else {
1258099a0e58SBosko Milekic 		struct uma_kctor_args karg;
1259099a0e58SBosko Milekic 
1260099a0e58SBosko Milekic 		/* We should only be here from uma_startup() */
1261099a0e58SBosko Milekic 		karg.size = arg->size;
1262099a0e58SBosko Milekic 		karg.uminit = arg->uminit;
1263099a0e58SBosko Milekic 		karg.fini = arg->fini;
1264099a0e58SBosko Milekic 		karg.align = arg->align;
1265099a0e58SBosko Milekic 		karg.flags = arg->flags;
1266099a0e58SBosko Milekic 		karg.zone = zone;
1267099a0e58SBosko Milekic 		keg_ctor(arg->keg, sizeof(struct uma_keg), &karg);
1268099a0e58SBosko Milekic 	}
1269099a0e58SBosko Milekic 	keg = zone->uz_keg;
1270099a0e58SBosko Milekic 	zone->uz_lock = &keg->uk_lock;
12718355f576SJeff Roberson 
12728355f576SJeff Roberson 	/*
12738355f576SJeff Roberson 	 * Some internal zones don't have room allocated for the per cpu
12748355f576SJeff Roberson 	 * caches.  If we're internal, bail out here.
12758355f576SJeff Roberson 	 */
1276099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_INTERNAL) {
1277099a0e58SBosko Milekic 		KASSERT((keg->uk_flags & UMA_ZONE_SECONDARY) == 0,
1278099a0e58SBosko Milekic 		    ("Secondary zone requested UMA_ZFLAG_INTERNAL"));
12798355f576SJeff Roberson 		return;
1280099a0e58SBosko Milekic 	}
12818355f576SJeff Roberson 
1282099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_MAXBUCKET)
1283099a0e58SBosko Milekic 		zone->uz_count = BUCKET_MAX;
1284099a0e58SBosko Milekic 	else if (keg->uk_ipers <= BUCKET_MAX)
1285099a0e58SBosko Milekic 		zone->uz_count = keg->uk_ipers;
12868355f576SJeff Roberson 	else
1287cae33c14SJeff Roberson 		zone->uz_count = BUCKET_MAX;
12888355f576SJeff Roberson }
12898355f576SJeff Roberson 
12908355f576SJeff Roberson /*
1291099a0e58SBosko Milekic  * Keg header dtor.  This frees all data, destroys locks, frees the hash
1292099a0e58SBosko Milekic  * table and removes the keg from the global list.
12939c2cd7e5SJeff Roberson  *
12949c2cd7e5SJeff Roberson  * Arguments/Returns follow uma_dtor specifications
12959c2cd7e5SJeff Roberson  *	udata  unused
12969c2cd7e5SJeff Roberson  */
1297099a0e58SBosko Milekic static void
1298099a0e58SBosko Milekic keg_dtor(void *arg, int size, void *udata)
1299099a0e58SBosko Milekic {
1300099a0e58SBosko Milekic 	uma_keg_t keg;
13019c2cd7e5SJeff Roberson 
1302099a0e58SBosko Milekic 	keg = (uma_keg_t)arg;
1303099a0e58SBosko Milekic 	mtx_lock(&keg->uk_lock);
1304099a0e58SBosko Milekic 	if (keg->uk_free != 0) {
1305099a0e58SBosko Milekic 		printf("Freed UMA keg was not empty (%d items). "
1306099a0e58SBosko Milekic 		    " Lost %d pages of memory.\n",
1307099a0e58SBosko Milekic 		    keg->uk_free, keg->uk_pages);
1308099a0e58SBosko Milekic 	}
1309099a0e58SBosko Milekic 	mtx_unlock(&keg->uk_lock);
1310099a0e58SBosko Milekic 
1311099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH)
1312099a0e58SBosko Milekic 		hash_free(&keg->uk_hash);
1313099a0e58SBosko Milekic 
1314099a0e58SBosko Milekic 	mtx_destroy(&keg->uk_lock);
1315099a0e58SBosko Milekic }
1316099a0e58SBosko Milekic 
1317099a0e58SBosko Milekic /*
1318099a0e58SBosko Milekic  * Zone header dtor.
1319099a0e58SBosko Milekic  *
1320099a0e58SBosko Milekic  * Arguments/Returns follow uma_dtor specifications
1321099a0e58SBosko Milekic  *	udata  unused
1322099a0e58SBosko Milekic  */
13239c2cd7e5SJeff Roberson static void
13249c2cd7e5SJeff Roberson zone_dtor(void *arg, int size, void *udata)
13259c2cd7e5SJeff Roberson {
13269c2cd7e5SJeff Roberson 	uma_zone_t zone;
1327099a0e58SBosko Milekic 	uma_keg_t keg;
13289c2cd7e5SJeff Roberson 
13299c2cd7e5SJeff Roberson 	zone = (uma_zone_t)arg;
1330099a0e58SBosko Milekic 	keg = zone->uz_keg;
13319643769aSJeff Roberson 
1332099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZFLAG_INTERNAL))
13339643769aSJeff Roberson 		cache_drain(zone);
1334099a0e58SBosko Milekic 
133517b9cc49SJeff Roberson 	mtx_lock(&uma_mtx);
13369643769aSJeff Roberson 	zone_drain(zone);
1337099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_SECONDARY) {
1338099a0e58SBosko Milekic 		LIST_REMOVE(zone, uz_link);
1339099a0e58SBosko Milekic 		/*
1340099a0e58SBosko Milekic 		 * XXX there are some races here where
1341099a0e58SBosko Milekic 		 * the zone can be drained but zone lock
1342099a0e58SBosko Milekic 		 * released and then refilled before we
1343099a0e58SBosko Milekic 		 * remove it... we dont care for now
1344099a0e58SBosko Milekic 		 */
13459c2cd7e5SJeff Roberson 		ZONE_LOCK(zone);
1346099a0e58SBosko Milekic 		if (LIST_EMPTY(&keg->uk_zones))
1347099a0e58SBosko Milekic 			keg->uk_flags &= ~UMA_ZONE_SECONDARY;
13489c2cd7e5SJeff Roberson 		ZONE_UNLOCK(zone);
1349099a0e58SBosko Milekic 		mtx_unlock(&uma_mtx);
1350099a0e58SBosko Milekic 	} else {
1351099a0e58SBosko Milekic 		LIST_REMOVE(keg, uk_link);
1352099a0e58SBosko Milekic 		LIST_REMOVE(zone, uz_link);
1353099a0e58SBosko Milekic 		mtx_unlock(&uma_mtx);
1354099a0e58SBosko Milekic 		uma_zfree_internal(kegs, keg, NULL, 0);
13559c2cd7e5SJeff Roberson 	}
1356099a0e58SBosko Milekic 	zone->uz_keg = NULL;
1357099a0e58SBosko Milekic }
1358099a0e58SBosko Milekic 
13599c2cd7e5SJeff Roberson /*
13608355f576SJeff Roberson  * Traverses every zone in the system and calls a callback
13618355f576SJeff Roberson  *
13628355f576SJeff Roberson  * Arguments:
13638355f576SJeff Roberson  *	zfunc  A pointer to a function which accepts a zone
13648355f576SJeff Roberson  *		as an argument.
13658355f576SJeff Roberson  *
13668355f576SJeff Roberson  * Returns:
13678355f576SJeff Roberson  *	Nothing
13688355f576SJeff Roberson  */
13698355f576SJeff Roberson static void
13708355f576SJeff Roberson zone_foreach(void (*zfunc)(uma_zone_t))
13718355f576SJeff Roberson {
1372099a0e58SBosko Milekic 	uma_keg_t keg;
13738355f576SJeff Roberson 	uma_zone_t zone;
13748355f576SJeff Roberson 
13758355f576SJeff Roberson 	mtx_lock(&uma_mtx);
1376099a0e58SBosko Milekic 	LIST_FOREACH(keg, &uma_kegs, uk_link) {
1377099a0e58SBosko Milekic 		LIST_FOREACH(zone, &keg->uk_zones, uz_link)
13788355f576SJeff Roberson 			zfunc(zone);
1379099a0e58SBosko Milekic 	}
13808355f576SJeff Roberson 	mtx_unlock(&uma_mtx);
13818355f576SJeff Roberson }
13828355f576SJeff Roberson 
13838355f576SJeff Roberson /* Public functions */
13848355f576SJeff Roberson /* See uma.h */
13858355f576SJeff Roberson void
13868355f576SJeff Roberson uma_startup(void *bootmem)
13878355f576SJeff Roberson {
13888355f576SJeff Roberson 	struct uma_zctor_args args;
13898355f576SJeff Roberson 	uma_slab_t slab;
13908355f576SJeff Roberson 	int slabsize;
13918355f576SJeff Roberson 	int i;
13928355f576SJeff Roberson 
13938355f576SJeff Roberson #ifdef UMA_DEBUG
1394099a0e58SBosko Milekic 	printf("Creating uma keg headers zone and keg.\n");
13958355f576SJeff Roberson #endif
13966008862bSJohn Baldwin 	mtx_init(&uma_mtx, "UMA lock", NULL, MTX_DEF);
1397099a0e58SBosko Milekic 
1398099a0e58SBosko Milekic 	/* "manually" create the initial zone */
1399099a0e58SBosko Milekic 	args.name = "UMA Kegs";
1400099a0e58SBosko Milekic 	args.size = sizeof(struct uma_keg);
1401099a0e58SBosko Milekic 	args.ctor = keg_ctor;
1402099a0e58SBosko Milekic 	args.dtor = keg_dtor;
14038355f576SJeff Roberson 	args.uminit = zero_init;
14048355f576SJeff Roberson 	args.fini = NULL;
1405099a0e58SBosko Milekic 	args.keg = &masterkeg;
14068355f576SJeff Roberson 	args.align = 32 - 1;
1407b60f5b79SJeff Roberson 	args.flags = UMA_ZFLAG_INTERNAL;
14088355f576SJeff Roberson 	/* The initial zone has no Per cpu queues so it's smaller */
1409099a0e58SBosko Milekic 	zone_ctor(kegs, sizeof(struct uma_zone), &args);
14108355f576SJeff Roberson 
14118355f576SJeff Roberson #ifdef UMA_DEBUG
14128355f576SJeff Roberson 	printf("Filling boot free list.\n");
14138355f576SJeff Roberson #endif
14148355f576SJeff Roberson 	for (i = 0; i < UMA_BOOT_PAGES; i++) {
14158355f576SJeff Roberson 		slab = (uma_slab_t)((u_int8_t *)bootmem + (i * UMA_SLAB_SIZE));
14168355f576SJeff Roberson 		slab->us_data = (u_int8_t *)slab;
14178355f576SJeff Roberson 		slab->us_flags = UMA_SLAB_BOOT;
14188355f576SJeff Roberson 		LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link);
14198355f576SJeff Roberson 		uma_boot_free++;
14208355f576SJeff Roberson 	}
14218355f576SJeff Roberson 
14228355f576SJeff Roberson #ifdef UMA_DEBUG
1423099a0e58SBosko Milekic 	printf("Creating uma zone headers zone and keg.\n");
1424099a0e58SBosko Milekic #endif
1425099a0e58SBosko Milekic 	args.name = "UMA Zones";
1426099a0e58SBosko Milekic 	args.size = sizeof(struct uma_zone) +
1427099a0e58SBosko Milekic 	    (sizeof(struct uma_cache) * (mp_maxid + 1));
1428099a0e58SBosko Milekic 	args.ctor = zone_ctor;
1429099a0e58SBosko Milekic 	args.dtor = zone_dtor;
1430099a0e58SBosko Milekic 	args.uminit = zero_init;
1431099a0e58SBosko Milekic 	args.fini = NULL;
1432099a0e58SBosko Milekic 	args.keg = NULL;
1433099a0e58SBosko Milekic 	args.align = 32 - 1;
1434099a0e58SBosko Milekic 	args.flags = UMA_ZFLAG_INTERNAL;
1435099a0e58SBosko Milekic 	/* The initial zone has no Per cpu queues so it's smaller */
1436099a0e58SBosko Milekic 	zone_ctor(zones, sizeof(struct uma_zone), &args);
1437099a0e58SBosko Milekic 
1438099a0e58SBosko Milekic #ifdef UMA_DEBUG
1439099a0e58SBosko Milekic 	printf("Initializing pcpu cache locks.\n");
1440099a0e58SBosko Milekic #endif
1441099a0e58SBosko Milekic 	/* Initialize the pcpu cache lock set once and for all */
1442099a0e58SBosko Milekic 	for (i = 0; i <= mp_maxid; i++)
1443099a0e58SBosko Milekic 		CPU_LOCK_INIT(i);
1444099a0e58SBosko Milekic 
1445099a0e58SBosko Milekic #ifdef UMA_DEBUG
1446099a0e58SBosko Milekic 	printf("Creating slab and hash zones.\n");
14478355f576SJeff Roberson #endif
14488355f576SJeff Roberson 
14498355f576SJeff Roberson 	/*
14508355f576SJeff Roberson 	 * This is the max number of free list items we'll have with
14518355f576SJeff Roberson 	 * offpage slabs.
14528355f576SJeff Roberson 	 */
14538355f576SJeff Roberson 	slabsize = UMA_SLAB_SIZE - sizeof(struct uma_slab);
14548355f576SJeff Roberson 	slabsize /= UMA_MAX_WASTE;
14558355f576SJeff Roberson 	slabsize++;			/* In case there it's rounded */
14568355f576SJeff Roberson 	slabsize += sizeof(struct uma_slab);
14578355f576SJeff Roberson 
14588355f576SJeff Roberson 	/* Now make a zone for slab headers */
14598355f576SJeff Roberson 	slabzone = uma_zcreate("UMA Slabs",
14608355f576SJeff Roberson 				slabsize,
14618355f576SJeff Roberson 				NULL, NULL, NULL, NULL,
1462b60f5b79SJeff Roberson 				UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
14638355f576SJeff Roberson 
1464099a0e58SBosko Milekic 	/*
1465099a0e58SBosko Milekic 	 * We also create a zone for the bigger slabs with reference
1466099a0e58SBosko Milekic 	 * counts in them, to accomodate UMA_ZONE_REFCNT zones.
1467099a0e58SBosko Milekic 	 */
1468099a0e58SBosko Milekic 	slabsize = UMA_SLAB_SIZE - sizeof(struct uma_slab_refcnt);
1469099a0e58SBosko Milekic 	slabsize /= UMA_MAX_WASTE;
1470099a0e58SBosko Milekic 	slabsize++;
1471099a0e58SBosko Milekic 	slabsize += 4 * slabsize;
1472099a0e58SBosko Milekic 	slabsize += sizeof(struct uma_slab_refcnt);
1473099a0e58SBosko Milekic 	slabrefzone = uma_zcreate("UMA RCntSlabs",
1474099a0e58SBosko Milekic 				  slabsize,
1475099a0e58SBosko Milekic 				  NULL, NULL, NULL, NULL,
1476099a0e58SBosko Milekic 				  UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
1477099a0e58SBosko Milekic 
14788355f576SJeff Roberson 	hashzone = uma_zcreate("UMA Hash",
14798355f576SJeff Roberson 	    sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT,
14808355f576SJeff Roberson 	    NULL, NULL, NULL, NULL,
1481b60f5b79SJeff Roberson 	    UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
14828355f576SJeff Roberson 
1483cae33c14SJeff Roberson 	bucket_init();
14848355f576SJeff Roberson 
1485009b6fcbSJeff Roberson #ifdef UMA_MD_SMALL_ALLOC
148648eea375SJeff Roberson 	booted = 1;
148748eea375SJeff Roberson #endif
14888355f576SJeff Roberson 
14898355f576SJeff Roberson #ifdef UMA_DEBUG
14908355f576SJeff Roberson 	printf("UMA startup complete.\n");
14918355f576SJeff Roberson #endif
14928355f576SJeff Roberson }
14938355f576SJeff Roberson 
14948355f576SJeff Roberson /* see uma.h */
14958355f576SJeff Roberson void
149699571dc3SJeff Roberson uma_startup2(void)
14978355f576SJeff Roberson {
14988355f576SJeff Roberson 	booted = 1;
149986bbae32SJeff Roberson 	bucket_enable();
15008355f576SJeff Roberson #ifdef UMA_DEBUG
15018355f576SJeff Roberson 	printf("UMA startup2 complete.\n");
15028355f576SJeff Roberson #endif
15038355f576SJeff Roberson }
15048355f576SJeff Roberson 
15058355f576SJeff Roberson /*
15068355f576SJeff Roberson  * Initialize our callout handle
15078355f576SJeff Roberson  *
15088355f576SJeff Roberson  */
15098355f576SJeff Roberson 
15108355f576SJeff Roberson static void
15118355f576SJeff Roberson uma_startup3(void)
15128355f576SJeff Roberson {
15138355f576SJeff Roberson #ifdef UMA_DEBUG
15148355f576SJeff Roberson 	printf("Starting callout.\n");
15158355f576SJeff Roberson #endif
1516a3c07611SRobert Watson 	callout_init(&uma_callout, CALLOUT_MPSAFE);
15179643769aSJeff Roberson 	callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
15188355f576SJeff Roberson #ifdef UMA_DEBUG
15198355f576SJeff Roberson 	printf("UMA startup3 complete.\n");
15208355f576SJeff Roberson #endif
15218355f576SJeff Roberson }
15228355f576SJeff Roberson 
1523099a0e58SBosko Milekic static void
1524099a0e58SBosko Milekic uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini,
1525099a0e58SBosko Milekic 		int align, u_int16_t flags)
1526099a0e58SBosko Milekic {
1527099a0e58SBosko Milekic 	struct uma_kctor_args args;
1528099a0e58SBosko Milekic 
1529099a0e58SBosko Milekic 	args.size = size;
1530099a0e58SBosko Milekic 	args.uminit = uminit;
1531099a0e58SBosko Milekic 	args.fini = fini;
1532099a0e58SBosko Milekic 	args.align = align;
1533099a0e58SBosko Milekic 	args.flags = flags;
1534099a0e58SBosko Milekic 	args.zone = zone;
1535099a0e58SBosko Milekic 	zone = uma_zalloc_internal(kegs, &args, M_WAITOK);
1536099a0e58SBosko Milekic }
1537099a0e58SBosko Milekic 
15388355f576SJeff Roberson /* See uma.h */
15398355f576SJeff Roberson uma_zone_t
1540c3bdc05fSAndrew R. Reiter uma_zcreate(char *name, size_t size, uma_ctor ctor, uma_dtor dtor,
1541c3bdc05fSAndrew R. Reiter 		uma_init uminit, uma_fini fini, int align, u_int16_t flags)
15428355f576SJeff Roberson 
15438355f576SJeff Roberson {
15448355f576SJeff Roberson 	struct uma_zctor_args args;
15458355f576SJeff Roberson 
15468355f576SJeff Roberson 	/* This stuff is essential for the zone ctor */
15478355f576SJeff Roberson 	args.name = name;
15488355f576SJeff Roberson 	args.size = size;
15498355f576SJeff Roberson 	args.ctor = ctor;
15508355f576SJeff Roberson 	args.dtor = dtor;
15518355f576SJeff Roberson 	args.uminit = uminit;
15528355f576SJeff Roberson 	args.fini = fini;
15538355f576SJeff Roberson 	args.align = align;
15548355f576SJeff Roberson 	args.flags = flags;
1555099a0e58SBosko Milekic 	args.keg = NULL;
1556099a0e58SBosko Milekic 
1557099a0e58SBosko Milekic 	return (uma_zalloc_internal(zones, &args, M_WAITOK));
1558099a0e58SBosko Milekic }
1559099a0e58SBosko Milekic 
1560099a0e58SBosko Milekic /* See uma.h */
1561099a0e58SBosko Milekic uma_zone_t
1562099a0e58SBosko Milekic uma_zsecond_create(char *name, uma_ctor ctor, uma_dtor dtor,
1563099a0e58SBosko Milekic 		    uma_init zinit, uma_fini zfini, uma_zone_t master)
1564099a0e58SBosko Milekic {
1565099a0e58SBosko Milekic 	struct uma_zctor_args args;
1566099a0e58SBosko Milekic 
1567099a0e58SBosko Milekic 	args.name = name;
1568099a0e58SBosko Milekic 	args.size = master->uz_keg->uk_size;
1569099a0e58SBosko Milekic 	args.ctor = ctor;
1570099a0e58SBosko Milekic 	args.dtor = dtor;
1571099a0e58SBosko Milekic 	args.uminit = zinit;
1572099a0e58SBosko Milekic 	args.fini = zfini;
1573099a0e58SBosko Milekic 	args.align = master->uz_keg->uk_align;
1574099a0e58SBosko Milekic 	args.flags = master->uz_keg->uk_flags | UMA_ZONE_SECONDARY;
1575099a0e58SBosko Milekic 	args.keg = master->uz_keg;
15768355f576SJeff Roberson 
1577a163d034SWarner Losh 	return (uma_zalloc_internal(zones, &args, M_WAITOK));
15788355f576SJeff Roberson }
15798355f576SJeff Roberson 
15808355f576SJeff Roberson /* See uma.h */
15819c2cd7e5SJeff Roberson void
15829c2cd7e5SJeff Roberson uma_zdestroy(uma_zone_t zone)
15839c2cd7e5SJeff Roberson {
15849c2cd7e5SJeff Roberson 	uma_zfree_internal(zones, zone, NULL, 0);
15859c2cd7e5SJeff Roberson }
15869c2cd7e5SJeff Roberson 
15879c2cd7e5SJeff Roberson /* See uma.h */
15888355f576SJeff Roberson void *
15892cc35ff9SJeff Roberson uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
15908355f576SJeff Roberson {
15918355f576SJeff Roberson 	void *item;
15928355f576SJeff Roberson 	uma_cache_t cache;
15938355f576SJeff Roberson 	uma_bucket_t bucket;
15948355f576SJeff Roberson 	int cpu;
1595099a0e58SBosko Milekic 	int badness = 1;
15968355f576SJeff Roberson 
15978355f576SJeff Roberson 	/* This is the fast path allocation */
15988355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC_1
15998355f576SJeff Roberson 	printf("Allocating one item from %s(%p)\n", zone->uz_name, zone);
16008355f576SJeff Roberson #endif
1601a553d4b8SJeff Roberson 
16024c1cc01cSJohn Baldwin 	if (!(flags & M_NOWAIT)) {
16034c1cc01cSJohn Baldwin 		KASSERT(curthread->td_intr_nesting_level == 0,
1604a163d034SWarner Losh 		   ("malloc(M_WAITOK) in interrupt context"));
1605099a0e58SBosko Milekic #ifdef WITNESS
1606099a0e58SBosko Milekic 		badness = WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
1607099a0e58SBosko Milekic 		    "malloc(M_WAITOK) of \"%s\", forcing M_NOWAIT",
1608099a0e58SBosko Milekic 		    zone->uz_name);
1609099a0e58SBosko Milekic #endif
1610099a0e58SBosko Milekic 		if (badness) {
1611099a0e58SBosko Milekic 			flags &= ~M_WAITOK;
1612099a0e58SBosko Milekic 			flags |= M_NOWAIT;
1613099a0e58SBosko Milekic 		}
16144c1cc01cSJohn Baldwin 	}
16154c1cc01cSJohn Baldwin 
1616a553d4b8SJeff Roberson zalloc_restart:
16178355f576SJeff Roberson 	cpu = PCPU_GET(cpuid);
1618d88797c2SBosko Milekic 	CPU_LOCK(cpu);
16198355f576SJeff Roberson 	cache = &zone->uz_cpu[cpu];
16208355f576SJeff Roberson 
16218355f576SJeff Roberson zalloc_start:
16228355f576SJeff Roberson 	bucket = cache->uc_allocbucket;
16238355f576SJeff Roberson 
16248355f576SJeff Roberson 	if (bucket) {
1625cae33c14SJeff Roberson 		if (bucket->ub_cnt > 0) {
1626cae33c14SJeff Roberson 			bucket->ub_cnt--;
1627cae33c14SJeff Roberson 			item = bucket->ub_bucket[bucket->ub_cnt];
16288355f576SJeff Roberson #ifdef INVARIANTS
1629cae33c14SJeff Roberson 			bucket->ub_bucket[bucket->ub_cnt] = NULL;
16308355f576SJeff Roberson #endif
16318355f576SJeff Roberson 			KASSERT(item != NULL,
16328355f576SJeff Roberson 			    ("uma_zalloc: Bucket pointer mangled."));
16338355f576SJeff Roberson 			cache->uc_allocs++;
1634639c9550SJeff Roberson #ifdef INVARIANTS
163581f71edaSMatt Jacob 			ZONE_LOCK(zone);
1636639c9550SJeff Roberson 			uma_dbg_alloc(zone, NULL, item);
163781f71edaSMatt Jacob 			ZONE_UNLOCK(zone);
1638639c9550SJeff Roberson #endif
1639d88797c2SBosko Milekic 			CPU_UNLOCK(cpu);
16408355f576SJeff Roberson 			if (zone->uz_ctor)
1641099a0e58SBosko Milekic 				zone->uz_ctor(item,zone->uz_keg->uk_size,udata);
16422cc35ff9SJeff Roberson 			if (flags & M_ZERO)
1643099a0e58SBosko Milekic 				bzero(item, zone->uz_keg->uk_size);
16448355f576SJeff Roberson 			return (item);
16458355f576SJeff Roberson 		} else if (cache->uc_freebucket) {
16468355f576SJeff Roberson 			/*
16478355f576SJeff Roberson 			 * We have run out of items in our allocbucket.
16488355f576SJeff Roberson 			 * See if we can switch with our free bucket.
16498355f576SJeff Roberson 			 */
1650cae33c14SJeff Roberson 			if (cache->uc_freebucket->ub_cnt > 0) {
16518355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC
165264f051e9SJeff Roberson 				printf("uma_zalloc: Swapping empty with"
165364f051e9SJeff Roberson 				    " alloc.\n");
16548355f576SJeff Roberson #endif
1655b983089aSJeff Roberson 				bucket = cache->uc_freebucket;
16568355f576SJeff Roberson 				cache->uc_freebucket = cache->uc_allocbucket;
1657b983089aSJeff Roberson 				cache->uc_allocbucket = bucket;
16588355f576SJeff Roberson 
16598355f576SJeff Roberson 				goto zalloc_start;
16608355f576SJeff Roberson 			}
16618355f576SJeff Roberson 		}
16628355f576SJeff Roberson 	}
1663a553d4b8SJeff Roberson 	ZONE_LOCK(zone);
1664a553d4b8SJeff Roberson 	/* Since we have locked the zone we may as well send back our stats */
1665a553d4b8SJeff Roberson 	zone->uz_allocs += cache->uc_allocs;
1666a553d4b8SJeff Roberson 	cache->uc_allocs = 0;
16678355f576SJeff Roberson 
1668a553d4b8SJeff Roberson 	/* Our old one is now a free bucket */
1669a553d4b8SJeff Roberson 	if (cache->uc_allocbucket) {
1670cae33c14SJeff Roberson 		KASSERT(cache->uc_allocbucket->ub_cnt == 0,
1671a553d4b8SJeff Roberson 		    ("uma_zalloc_arg: Freeing a non free bucket."));
1672a553d4b8SJeff Roberson 		LIST_INSERT_HEAD(&zone->uz_free_bucket,
1673a553d4b8SJeff Roberson 		    cache->uc_allocbucket, ub_link);
1674a553d4b8SJeff Roberson 		cache->uc_allocbucket = NULL;
1675a553d4b8SJeff Roberson 	}
16768355f576SJeff Roberson 
1677a553d4b8SJeff Roberson 	/* Check the free list for a new alloc bucket */
1678a553d4b8SJeff Roberson 	if ((bucket = LIST_FIRST(&zone->uz_full_bucket)) != NULL) {
1679cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt != 0,
1680a553d4b8SJeff Roberson 		    ("uma_zalloc_arg: Returning an empty bucket."));
16818355f576SJeff Roberson 
1682a553d4b8SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
1683a553d4b8SJeff Roberson 		cache->uc_allocbucket = bucket;
1684a553d4b8SJeff Roberson 		ZONE_UNLOCK(zone);
16858355f576SJeff Roberson 		goto zalloc_start;
1686a553d4b8SJeff Roberson 	}
1687bbee39c6SJeff Roberson 	/* We are no longer associated with this cpu!!! */
1688d88797c2SBosko Milekic 	CPU_UNLOCK(cpu);
1689bbee39c6SJeff Roberson 
1690a553d4b8SJeff Roberson 	/* Bump up our uz_count so we get here less */
1691cae33c14SJeff Roberson 	if (zone->uz_count < BUCKET_MAX)
1692a553d4b8SJeff Roberson 		zone->uz_count++;
1693099a0e58SBosko Milekic 
16948355f576SJeff Roberson 	/*
1695a553d4b8SJeff Roberson 	 * Now lets just fill a bucket and put it on the free list.  If that
1696a553d4b8SJeff Roberson 	 * works we'll restart the allocation from the begining.
1697bbee39c6SJeff Roberson 	 */
1698bbee39c6SJeff Roberson 	if (uma_zalloc_bucket(zone, flags)) {
1699bbee39c6SJeff Roberson 		ZONE_UNLOCK(zone);
1700bbee39c6SJeff Roberson 		goto zalloc_restart;
1701bbee39c6SJeff Roberson 	}
1702bbee39c6SJeff Roberson 	ZONE_UNLOCK(zone);
1703bbee39c6SJeff Roberson 	/*
1704bbee39c6SJeff Roberson 	 * We may not be able to get a bucket so return an actual item.
1705bbee39c6SJeff Roberson 	 */
1706bbee39c6SJeff Roberson #ifdef UMA_DEBUG
1707bbee39c6SJeff Roberson 	printf("uma_zalloc_arg: Bucketzone returned NULL\n");
1708bbee39c6SJeff Roberson #endif
1709bbee39c6SJeff Roberson 
1710bbee39c6SJeff Roberson 	return (uma_zalloc_internal(zone, udata, flags));
1711bbee39c6SJeff Roberson }
1712bbee39c6SJeff Roberson 
1713bbee39c6SJeff Roberson static uma_slab_t
1714bbee39c6SJeff Roberson uma_zone_slab(uma_zone_t zone, int flags)
1715bbee39c6SJeff Roberson {
1716bbee39c6SJeff Roberson 	uma_slab_t slab;
1717099a0e58SBosko Milekic 	uma_keg_t keg;
1718099a0e58SBosko Milekic 
1719099a0e58SBosko Milekic 	keg = zone->uz_keg;
1720bbee39c6SJeff Roberson 
1721bbee39c6SJeff Roberson 	/*
1722bbee39c6SJeff Roberson 	 * This is to prevent us from recursively trying to allocate
1723bbee39c6SJeff Roberson 	 * buckets.  The problem is that if an allocation forces us to
1724bbee39c6SJeff Roberson 	 * grab a new bucket we will call page_alloc, which will go off
1725bbee39c6SJeff Roberson 	 * and cause the vm to allocate vm_map_entries.  If we need new
1726bbee39c6SJeff Roberson 	 * buckets there too we will recurse in kmem_alloc and bad
1727bbee39c6SJeff Roberson 	 * things happen.  So instead we return a NULL bucket, and make
1728bbee39c6SJeff Roberson 	 * the code that allocates buckets smart enough to deal with it
1729bbee39c6SJeff Roberson 	 */
1730099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_INTERNAL && keg->uk_recurse != 0)
1731bbee39c6SJeff Roberson 		return (NULL);
1732bbee39c6SJeff Roberson 
1733bbee39c6SJeff Roberson 	slab = NULL;
1734bbee39c6SJeff Roberson 
1735bbee39c6SJeff Roberson 	for (;;) {
1736bbee39c6SJeff Roberson 		/*
1737bbee39c6SJeff Roberson 		 * Find a slab with some space.  Prefer slabs that are partially
1738bbee39c6SJeff Roberson 		 * used over those that are totally full.  This helps to reduce
1739bbee39c6SJeff Roberson 		 * fragmentation.
1740bbee39c6SJeff Roberson 		 */
1741099a0e58SBosko Milekic 		if (keg->uk_free != 0) {
1742099a0e58SBosko Milekic 			if (!LIST_EMPTY(&keg->uk_part_slab)) {
1743099a0e58SBosko Milekic 				slab = LIST_FIRST(&keg->uk_part_slab);
1744bbee39c6SJeff Roberson 			} else {
1745099a0e58SBosko Milekic 				slab = LIST_FIRST(&keg->uk_free_slab);
1746bbee39c6SJeff Roberson 				LIST_REMOVE(slab, us_link);
1747099a0e58SBosko Milekic 				LIST_INSERT_HEAD(&keg->uk_part_slab, slab,
1748bbee39c6SJeff Roberson 				    us_link);
1749bbee39c6SJeff Roberson 			}
1750bbee39c6SJeff Roberson 			return (slab);
1751bbee39c6SJeff Roberson 		}
1752bbee39c6SJeff Roberson 
1753bbee39c6SJeff Roberson 		/*
1754bbee39c6SJeff Roberson 		 * M_NOVM means don't ask at all!
1755bbee39c6SJeff Roberson 		 */
1756bbee39c6SJeff Roberson 		if (flags & M_NOVM)
1757bbee39c6SJeff Roberson 			break;
1758bbee39c6SJeff Roberson 
1759099a0e58SBosko Milekic 		if (keg->uk_maxpages &&
1760099a0e58SBosko Milekic 		    keg->uk_pages >= keg->uk_maxpages) {
1761099a0e58SBosko Milekic 			keg->uk_flags |= UMA_ZFLAG_FULL;
1762bbee39c6SJeff Roberson 
1763ebc85edfSJeff Roberson 			if (flags & M_NOWAIT)
1764bbee39c6SJeff Roberson 				break;
1765ebc85edfSJeff Roberson 			else
1766099a0e58SBosko Milekic 				msleep(keg, &keg->uk_lock, PVM,
176764f051e9SJeff Roberson 				    "zonelimit", 0);
1768bbee39c6SJeff Roberson 			continue;
1769bbee39c6SJeff Roberson 		}
1770099a0e58SBosko Milekic 		keg->uk_recurse++;
1771bbee39c6SJeff Roberson 		slab = slab_zalloc(zone, flags);
1772099a0e58SBosko Milekic 		keg->uk_recurse--;
1773099a0e58SBosko Milekic 
1774bbee39c6SJeff Roberson 		/*
1775bbee39c6SJeff Roberson 		 * If we got a slab here it's safe to mark it partially used
1776bbee39c6SJeff Roberson 		 * and return.  We assume that the caller is going to remove
1777bbee39c6SJeff Roberson 		 * at least one item.
1778bbee39c6SJeff Roberson 		 */
1779bbee39c6SJeff Roberson 		if (slab) {
1780099a0e58SBosko Milekic 			LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link);
1781bbee39c6SJeff Roberson 			return (slab);
1782bbee39c6SJeff Roberson 		}
1783bbee39c6SJeff Roberson 		/*
1784bbee39c6SJeff Roberson 		 * We might not have been able to get a slab but another cpu
1785bbee39c6SJeff Roberson 		 * could have while we were unlocked.  Check again before we
1786bbee39c6SJeff Roberson 		 * fail.
1787bbee39c6SJeff Roberson 		 */
1788ebc85edfSJeff Roberson 		if (flags & M_NOWAIT)
1789bbee39c6SJeff Roberson 			flags |= M_NOVM;
1790bbee39c6SJeff Roberson 	}
1791bbee39c6SJeff Roberson 	return (slab);
1792bbee39c6SJeff Roberson }
1793bbee39c6SJeff Roberson 
1794d56368d7SBosko Milekic static void *
1795bbee39c6SJeff Roberson uma_slab_alloc(uma_zone_t zone, uma_slab_t slab)
1796bbee39c6SJeff Roberson {
1797099a0e58SBosko Milekic 	uma_keg_t keg;
1798bbee39c6SJeff Roberson 	void *item;
1799bbee39c6SJeff Roberson 	u_int8_t freei;
1800bbee39c6SJeff Roberson 
1801099a0e58SBosko Milekic 	keg = zone->uz_keg;
1802099a0e58SBosko Milekic 
1803bbee39c6SJeff Roberson 	freei = slab->us_firstfree;
1804099a0e58SBosko Milekic 	slab->us_firstfree = slab->us_freelist[freei].us_item;
1805099a0e58SBosko Milekic 	item = slab->us_data + (keg->uk_rsize * freei);
1806bbee39c6SJeff Roberson 
1807bbee39c6SJeff Roberson 	slab->us_freecount--;
1808099a0e58SBosko Milekic 	keg->uk_free--;
1809bbee39c6SJeff Roberson #ifdef INVARIANTS
1810bbee39c6SJeff Roberson 	uma_dbg_alloc(zone, slab, item);
1811bbee39c6SJeff Roberson #endif
1812bbee39c6SJeff Roberson 	/* Move this slab to the full list */
1813bbee39c6SJeff Roberson 	if (slab->us_freecount == 0) {
1814bbee39c6SJeff Roberson 		LIST_REMOVE(slab, us_link);
1815099a0e58SBosko Milekic 		LIST_INSERT_HEAD(&keg->uk_full_slab, slab, us_link);
1816bbee39c6SJeff Roberson 	}
1817bbee39c6SJeff Roberson 
1818bbee39c6SJeff Roberson 	return (item);
1819bbee39c6SJeff Roberson }
1820bbee39c6SJeff Roberson 
1821bbee39c6SJeff Roberson static int
1822bbee39c6SJeff Roberson uma_zalloc_bucket(uma_zone_t zone, int flags)
1823bbee39c6SJeff Roberson {
1824bbee39c6SJeff Roberson 	uma_bucket_t bucket;
1825bbee39c6SJeff Roberson 	uma_slab_t slab;
1826099a0e58SBosko Milekic 	int16_t saved;
182744eca34aSJeff Roberson 	int max;
1828bbee39c6SJeff Roberson 
1829bbee39c6SJeff Roberson 	/*
1830a553d4b8SJeff Roberson 	 * Try this zone's free list first so we don't allocate extra buckets.
18318355f576SJeff Roberson 	 */
1832bbee39c6SJeff Roberson 	if ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) {
1833cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt == 0,
1834bbee39c6SJeff Roberson 		    ("uma_zalloc_bucket: Bucket on free list is not empty."));
1835a553d4b8SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
1836bbee39c6SJeff Roberson 	} else {
183718aa2de5SJeff Roberson 		int bflags;
183818aa2de5SJeff Roberson 
1839cae33c14SJeff Roberson 		bflags = (flags & ~M_ZERO);
1840099a0e58SBosko Milekic 		if (zone->uz_keg->uk_flags & UMA_ZFLAG_CACHEONLY)
184118aa2de5SJeff Roberson 			bflags |= M_NOVM;
184218aa2de5SJeff Roberson 
1843bbee39c6SJeff Roberson 		ZONE_UNLOCK(zone);
1844cae33c14SJeff Roberson 		bucket = bucket_alloc(zone->uz_count, bflags);
1845bbee39c6SJeff Roberson 		ZONE_LOCK(zone);
1846bbee39c6SJeff Roberson 	}
1847bbee39c6SJeff Roberson 
1848bbee39c6SJeff Roberson 	if (bucket == NULL)
1849bbee39c6SJeff Roberson 		return (0);
1850bbee39c6SJeff Roberson 
1851bbee39c6SJeff Roberson #ifdef SMP
1852a553d4b8SJeff Roberson 	/*
1853bbee39c6SJeff Roberson 	 * This code is here to limit the number of simultaneous bucket fills
1854bbee39c6SJeff Roberson 	 * for any given zone to the number of per cpu caches in this zone. This
1855bbee39c6SJeff Roberson 	 * is done so that we don't allocate more memory than we really need.
1856a553d4b8SJeff Roberson 	 */
1857bbee39c6SJeff Roberson 	if (zone->uz_fills >= mp_ncpus)
1858bbee39c6SJeff Roberson 		goto done;
1859a553d4b8SJeff Roberson 
1860bbee39c6SJeff Roberson #endif
1861bbee39c6SJeff Roberson 	zone->uz_fills++;
1862bbee39c6SJeff Roberson 
186344eca34aSJeff Roberson 	max = MIN(bucket->ub_entries, zone->uz_count);
1864bbee39c6SJeff Roberson 	/* Try to keep the buckets totally full */
1865099a0e58SBosko Milekic 	saved = bucket->ub_cnt;
186644eca34aSJeff Roberson 	while (bucket->ub_cnt < max &&
1867d11e0ba5SJeff Roberson 	    (slab = uma_zone_slab(zone, flags)) != NULL) {
186844eca34aSJeff Roberson 		while (slab->us_freecount && bucket->ub_cnt < max) {
1869cae33c14SJeff Roberson 			bucket->ub_bucket[bucket->ub_cnt++] =
1870bbee39c6SJeff Roberson 			    uma_slab_alloc(zone, slab);
1871bbee39c6SJeff Roberson 		}
1872099a0e58SBosko Milekic 
1873bbee39c6SJeff Roberson 		/* Don't block on the next fill */
1874bbee39c6SJeff Roberson 		flags |= M_NOWAIT;
18758355f576SJeff Roberson 	}
18768355f576SJeff Roberson 
1877099a0e58SBosko Milekic 	/*
1878099a0e58SBosko Milekic 	 * We unlock here because we need to call the zone's init.
1879099a0e58SBosko Milekic 	 * It should be safe to unlock because the slab dealt with
1880099a0e58SBosko Milekic 	 * above is already on the appropriate list within the keg
1881099a0e58SBosko Milekic 	 * and the bucket we filled is not yet on any list, so we
1882099a0e58SBosko Milekic 	 * own it.
1883099a0e58SBosko Milekic 	 */
1884099a0e58SBosko Milekic 	if (zone->uz_init != NULL) {
1885099a0e58SBosko Milekic 		int i;
1886bbee39c6SJeff Roberson 
1887099a0e58SBosko Milekic 		ZONE_UNLOCK(zone);
1888099a0e58SBosko Milekic 		for (i = saved; i < bucket->ub_cnt; i++)
1889099a0e58SBosko Milekic 			zone->uz_init(bucket->ub_bucket[i],
1890099a0e58SBosko Milekic 			    zone->uz_keg->uk_size);
1891099a0e58SBosko Milekic 		ZONE_LOCK(zone);
1892099a0e58SBosko Milekic 	}
1893099a0e58SBosko Milekic 
1894099a0e58SBosko Milekic 	zone->uz_fills--;
1895cae33c14SJeff Roberson 	if (bucket->ub_cnt != 0) {
1896bbee39c6SJeff Roberson 		LIST_INSERT_HEAD(&zone->uz_full_bucket,
1897bbee39c6SJeff Roberson 		    bucket, ub_link);
1898bbee39c6SJeff Roberson 		return (1);
1899bbee39c6SJeff Roberson 	}
1900bbee39c6SJeff Roberson #ifdef SMP
1901bbee39c6SJeff Roberson done:
1902bbee39c6SJeff Roberson #endif
1903cae33c14SJeff Roberson 	bucket_free(bucket);
1904bbee39c6SJeff Roberson 
1905bbee39c6SJeff Roberson 	return (0);
1906bbee39c6SJeff Roberson }
19078355f576SJeff Roberson /*
1908bbee39c6SJeff Roberson  * Allocates an item for an internal zone
19098355f576SJeff Roberson  *
19108355f576SJeff Roberson  * Arguments
19118355f576SJeff Roberson  *	zone   The zone to alloc for.
19128355f576SJeff Roberson  *	udata  The data to be passed to the constructor.
1913a163d034SWarner Losh  *	flags  M_WAITOK, M_NOWAIT, M_ZERO.
19148355f576SJeff Roberson  *
19158355f576SJeff Roberson  * Returns
19168355f576SJeff Roberson  *	NULL if there is no memory and M_NOWAIT is set
1917bbee39c6SJeff Roberson  *	An item if successful
19188355f576SJeff Roberson  */
19198355f576SJeff Roberson 
19208355f576SJeff Roberson static void *
1921bbee39c6SJeff Roberson uma_zalloc_internal(uma_zone_t zone, void *udata, int flags)
19228355f576SJeff Roberson {
1923099a0e58SBosko Milekic 	uma_keg_t keg;
19248355f576SJeff Roberson 	uma_slab_t slab;
19258355f576SJeff Roberson 	void *item;
19268355f576SJeff Roberson 
19278355f576SJeff Roberson 	item = NULL;
1928099a0e58SBosko Milekic 	keg = zone->uz_keg;
19298355f576SJeff Roberson 
19308355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC
19318355f576SJeff Roberson 	printf("INTERNAL: Allocating one item from %s(%p)\n", zone->uz_name, zone);
19328355f576SJeff Roberson #endif
19338355f576SJeff Roberson 	ZONE_LOCK(zone);
19348355f576SJeff Roberson 
1935bbee39c6SJeff Roberson 	slab = uma_zone_slab(zone, flags);
1936bbee39c6SJeff Roberson 	if (slab == NULL) {
1937bce97791SJeff Roberson 		ZONE_UNLOCK(zone);
1938a553d4b8SJeff Roberson 		return (NULL);
1939bce97791SJeff Roberson 	}
1940a553d4b8SJeff Roberson 
1941bbee39c6SJeff Roberson 	item = uma_slab_alloc(zone, slab);
19428355f576SJeff Roberson 
19438355f576SJeff Roberson 	ZONE_UNLOCK(zone);
19448355f576SJeff Roberson 
1945099a0e58SBosko Milekic 	/*
1946099a0e58SBosko Milekic 	 * We have to call both the zone's init (not the keg's init)
1947099a0e58SBosko Milekic 	 * and the zone's ctor.  This is because the item is going from
1948099a0e58SBosko Milekic 	 * a keg slab directly to the user, and the user is expecting it
1949099a0e58SBosko Milekic 	 * to be both zone-init'd as well as zone-ctor'd.
1950099a0e58SBosko Milekic 	 */
1951099a0e58SBosko Milekic 	if (zone->uz_init != NULL)
1952099a0e58SBosko Milekic 		zone->uz_init(item, keg->uk_size);
19533370c5bfSJeff Roberson 	if (zone->uz_ctor != NULL)
1954099a0e58SBosko Milekic 		zone->uz_ctor(item, keg->uk_size, udata);
19552cc35ff9SJeff Roberson 	if (flags & M_ZERO)
1956099a0e58SBosko Milekic 		bzero(item, keg->uk_size);
19578355f576SJeff Roberson 
19588355f576SJeff Roberson 	return (item);
19598355f576SJeff Roberson }
19608355f576SJeff Roberson 
19618355f576SJeff Roberson /* See uma.h */
19628355f576SJeff Roberson void
19638355f576SJeff Roberson uma_zfree_arg(uma_zone_t zone, void *item, void *udata)
19648355f576SJeff Roberson {
1965099a0e58SBosko Milekic 	uma_keg_t keg;
19668355f576SJeff Roberson 	uma_cache_t cache;
19678355f576SJeff Roberson 	uma_bucket_t bucket;
19684741dcbfSJeff Roberson 	int bflags;
19698355f576SJeff Roberson 	int cpu;
19705c133dfaSBosko Milekic 	int skip;
19718355f576SJeff Roberson 
19728355f576SJeff Roberson 	/* This is the fast path free */
19735c133dfaSBosko Milekic 	skip = 0;
1974099a0e58SBosko Milekic 	keg = zone->uz_keg;
1975099a0e58SBosko Milekic 
19768355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC_1
19778355f576SJeff Roberson 	printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone);
19788355f576SJeff Roberson #endif
1979af7f9b97SJeff Roberson 	/*
1980af7f9b97SJeff Roberson 	 * The race here is acceptable.  If we miss it we'll just have to wait
1981af7f9b97SJeff Roberson 	 * a little longer for the limits to be reset.
1982af7f9b97SJeff Roberson 	 */
1983af7f9b97SJeff Roberson 
1984099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_FULL)
1985af7f9b97SJeff Roberson 		goto zfree_internal;
1986af7f9b97SJeff Roberson 
19875c133dfaSBosko Milekic 	if (zone->uz_dtor) {
1988099a0e58SBosko Milekic 		zone->uz_dtor(item, keg->uk_size, udata);
19895c133dfaSBosko Milekic 		skip = 1;
19905c133dfaSBosko Milekic 	}
1991bba739abSJeff Roberson 
1992a553d4b8SJeff Roberson zfree_restart:
19938355f576SJeff Roberson 	cpu = PCPU_GET(cpuid);
1994d88797c2SBosko Milekic 	CPU_LOCK(cpu);
19958355f576SJeff Roberson 	cache = &zone->uz_cpu[cpu];
19968355f576SJeff Roberson 
19978355f576SJeff Roberson zfree_start:
19988355f576SJeff Roberson 	bucket = cache->uc_freebucket;
19998355f576SJeff Roberson 
20008355f576SJeff Roberson 	if (bucket) {
2001a553d4b8SJeff Roberson 		/*
2002a553d4b8SJeff Roberson 		 * Do we have room in our bucket? It is OK for this uz count
2003a553d4b8SJeff Roberson 		 * check to be slightly out of sync.
2004a553d4b8SJeff Roberson 		 */
2005a553d4b8SJeff Roberson 
2006cae33c14SJeff Roberson 		if (bucket->ub_cnt < bucket->ub_entries) {
2007cae33c14SJeff Roberson 			KASSERT(bucket->ub_bucket[bucket->ub_cnt] == NULL,
20088355f576SJeff Roberson 			    ("uma_zfree: Freeing to non free bucket index."));
2009cae33c14SJeff Roberson 			bucket->ub_bucket[bucket->ub_cnt] = item;
2010cae33c14SJeff Roberson 			bucket->ub_cnt++;
2011b9ba8931SJeff Roberson #ifdef INVARIANTS
201281f71edaSMatt Jacob 			ZONE_LOCK(zone);
2013099a0e58SBosko Milekic 			if (keg->uk_flags & UMA_ZONE_MALLOC)
2014b9ba8931SJeff Roberson 				uma_dbg_free(zone, udata, item);
2015b9ba8931SJeff Roberson 			else
2016b9ba8931SJeff Roberson 				uma_dbg_free(zone, NULL, item);
201781f71edaSMatt Jacob 			ZONE_UNLOCK(zone);
2018b9ba8931SJeff Roberson #endif
2019d88797c2SBosko Milekic 			CPU_UNLOCK(cpu);
20208355f576SJeff Roberson 			return;
20218355f576SJeff Roberson 		} else if (cache->uc_allocbucket) {
20228355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC
20238355f576SJeff Roberson 			printf("uma_zfree: Swapping buckets.\n");
20248355f576SJeff Roberson #endif
20258355f576SJeff Roberson 			/*
20268355f576SJeff Roberson 			 * We have run out of space in our freebucket.
20278355f576SJeff Roberson 			 * See if we can switch with our alloc bucket.
20288355f576SJeff Roberson 			 */
2029cae33c14SJeff Roberson 			if (cache->uc_allocbucket->ub_cnt <
2030cae33c14SJeff Roberson 			    cache->uc_freebucket->ub_cnt) {
2031b983089aSJeff Roberson 				bucket = cache->uc_freebucket;
20328355f576SJeff Roberson 				cache->uc_freebucket = cache->uc_allocbucket;
2033b983089aSJeff Roberson 				cache->uc_allocbucket = bucket;
20348355f576SJeff Roberson 				goto zfree_start;
20358355f576SJeff Roberson 			}
20368355f576SJeff Roberson 		}
20378355f576SJeff Roberson 	}
20388355f576SJeff Roberson 	/*
2039a553d4b8SJeff Roberson 	 * We can get here for two reasons:
20408355f576SJeff Roberson 	 *
20418355f576SJeff Roberson 	 * 1) The buckets are NULL
2042a553d4b8SJeff Roberson 	 * 2) The alloc and free buckets are both somewhat full.
20438355f576SJeff Roberson 	 */
20448355f576SJeff Roberson 
20458355f576SJeff Roberson 	ZONE_LOCK(zone);
20468355f576SJeff Roberson 
20478355f576SJeff Roberson 	bucket = cache->uc_freebucket;
20488355f576SJeff Roberson 	cache->uc_freebucket = NULL;
20498355f576SJeff Roberson 
20508355f576SJeff Roberson 	/* Can we throw this on the zone full list? */
20518355f576SJeff Roberson 	if (bucket != NULL) {
20528355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC
20538355f576SJeff Roberson 		printf("uma_zfree: Putting old bucket on the free list.\n");
20548355f576SJeff Roberson #endif
2055cae33c14SJeff Roberson 		/* ub_cnt is pointing to the last free item */
2056cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt != 0,
20578355f576SJeff Roberson 		    ("uma_zfree: Attempting to insert an empty bucket onto the full list.\n"));
20588355f576SJeff Roberson 		LIST_INSERT_HEAD(&zone->uz_full_bucket,
20598355f576SJeff Roberson 		    bucket, ub_link);
20608355f576SJeff Roberson 	}
2061a553d4b8SJeff Roberson 	if ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) {
2062a553d4b8SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
2063a553d4b8SJeff Roberson 		ZONE_UNLOCK(zone);
2064a553d4b8SJeff Roberson 		cache->uc_freebucket = bucket;
2065a553d4b8SJeff Roberson 		goto zfree_start;
2066a553d4b8SJeff Roberson 	}
2067a553d4b8SJeff Roberson 	/* We're done with this CPU now */
2068d88797c2SBosko Milekic 	CPU_UNLOCK(cpu);
2069a553d4b8SJeff Roberson 
2070a553d4b8SJeff Roberson 	/* And the zone.. */
2071a553d4b8SJeff Roberson 	ZONE_UNLOCK(zone);
2072a553d4b8SJeff Roberson 
20738355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC
20748355f576SJeff Roberson 	printf("uma_zfree: Allocating new free bucket.\n");
20758355f576SJeff Roberson #endif
20764741dcbfSJeff Roberson 	bflags = M_NOWAIT;
20774741dcbfSJeff Roberson 
2078099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_CACHEONLY)
20794741dcbfSJeff Roberson 		bflags |= M_NOVM;
2080cae33c14SJeff Roberson 	bucket = bucket_alloc(zone->uz_count, bflags);
20814741dcbfSJeff Roberson 	if (bucket) {
2082a553d4b8SJeff Roberson 		ZONE_LOCK(zone);
2083a553d4b8SJeff Roberson 		LIST_INSERT_HEAD(&zone->uz_free_bucket,
2084a553d4b8SJeff Roberson 		    bucket, ub_link);
20858355f576SJeff Roberson 		ZONE_UNLOCK(zone);
2086a553d4b8SJeff Roberson 		goto zfree_restart;
20878355f576SJeff Roberson 	}
20888355f576SJeff Roberson 
2089a553d4b8SJeff Roberson 	/*
2090a553d4b8SJeff Roberson 	 * If nothing else caught this, we'll just do an internal free.
2091a553d4b8SJeff Roberson 	 */
20928355f576SJeff Roberson 
2093af7f9b97SJeff Roberson zfree_internal:
2094af7f9b97SJeff Roberson 
209548bf8725SBosko Milekic #ifdef INVARIANTS
209648bf8725SBosko Milekic 	/*
209764f051e9SJeff Roberson 	 * If we need to skip the dtor and the uma_dbg_free in
209864f051e9SJeff Roberson 	 * uma_zfree_internal because we've already called the dtor
209964f051e9SJeff Roberson 	 * above, but we ended up here, then we need to make sure
210064f051e9SJeff Roberson 	 * that we take care of the uma_dbg_free immediately.
210148bf8725SBosko Milekic 	 */
210248bf8725SBosko Milekic 	if (skip) {
210348bf8725SBosko Milekic 		ZONE_LOCK(zone);
2104099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZONE_MALLOC)
210548bf8725SBosko Milekic 			uma_dbg_free(zone, udata, item);
210648bf8725SBosko Milekic 		else
210748bf8725SBosko Milekic 			uma_dbg_free(zone, NULL, item);
210848bf8725SBosko Milekic 		ZONE_UNLOCK(zone);
210948bf8725SBosko Milekic 	}
211048bf8725SBosko Milekic #endif
21115c133dfaSBosko Milekic 	uma_zfree_internal(zone, item, udata, skip);
21128355f576SJeff Roberson 
21138355f576SJeff Roberson 	return;
21148355f576SJeff Roberson }
21158355f576SJeff Roberson 
21168355f576SJeff Roberson /*
21178355f576SJeff Roberson  * Frees an item to an INTERNAL zone or allocates a free bucket
21188355f576SJeff Roberson  *
21198355f576SJeff Roberson  * Arguments:
21208355f576SJeff Roberson  *	zone   The zone to free to
21218355f576SJeff Roberson  *	item   The item we're freeing
21228355f576SJeff Roberson  *	udata  User supplied data for the dtor
21238355f576SJeff Roberson  *	skip   Skip the dtor, it was done in uma_zfree_arg
21248355f576SJeff Roberson  */
21258355f576SJeff Roberson static void
21268355f576SJeff Roberson uma_zfree_internal(uma_zone_t zone, void *item, void *udata, int skip)
21278355f576SJeff Roberson {
21288355f576SJeff Roberson 	uma_slab_t slab;
2129099a0e58SBosko Milekic 	uma_keg_t keg;
21308355f576SJeff Roberson 	u_int8_t *mem;
21318355f576SJeff Roberson 	u_int8_t freei;
21328355f576SJeff Roberson 
2133099a0e58SBosko Milekic 	keg = zone->uz_keg;
2134099a0e58SBosko Milekic 
2135bba739abSJeff Roberson 	if (!skip && zone->uz_dtor)
2136099a0e58SBosko Milekic 		zone->uz_dtor(item, keg->uk_size, udata);
2137099a0e58SBosko Milekic 	if (zone->uz_fini)
2138099a0e58SBosko Milekic 		zone->uz_fini(item, keg->uk_size);
2139bba739abSJeff Roberson 
21408355f576SJeff Roberson 	ZONE_LOCK(zone);
21418355f576SJeff Roberson 
2142099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_MALLOC)) {
21438355f576SJeff Roberson 		mem = (u_int8_t *)((unsigned long)item & (~UMA_SLAB_MASK));
2144099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZONE_HASH)
2145099a0e58SBosko Milekic 			slab = hash_sfind(&keg->uk_hash, mem);
21468355f576SJeff Roberson 		else {
2147099a0e58SBosko Milekic 			mem += keg->uk_pgoff;
21488355f576SJeff Roberson 			slab = (uma_slab_t)mem;
21498355f576SJeff Roberson 		}
21508355f576SJeff Roberson 	} else {
21518355f576SJeff Roberson 		slab = (uma_slab_t)udata;
21528355f576SJeff Roberson 	}
21538355f576SJeff Roberson 
21548355f576SJeff Roberson 	/* Do we need to remove from any lists? */
2155099a0e58SBosko Milekic 	if (slab->us_freecount+1 == keg->uk_ipers) {
21568355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
2157099a0e58SBosko Milekic 		LIST_INSERT_HEAD(&keg->uk_free_slab, slab, us_link);
21588355f576SJeff Roberson 	} else if (slab->us_freecount == 0) {
21598355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
2160099a0e58SBosko Milekic 		LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link);
21618355f576SJeff Roberson 	}
21628355f576SJeff Roberson 
21638355f576SJeff Roberson 	/* Slab management stuff */
21648355f576SJeff Roberson 	freei = ((unsigned long)item - (unsigned long)slab->us_data)
2165099a0e58SBosko Milekic 		/ keg->uk_rsize;
21668355f576SJeff Roberson 
2167639c9550SJeff Roberson #ifdef INVARIANTS
2168639c9550SJeff Roberson 	if (!skip)
2169639c9550SJeff Roberson 		uma_dbg_free(zone, slab, item);
21708355f576SJeff Roberson #endif
2171639c9550SJeff Roberson 
2172099a0e58SBosko Milekic 	slab->us_freelist[freei].us_item = slab->us_firstfree;
21738355f576SJeff Roberson 	slab->us_firstfree = freei;
21748355f576SJeff Roberson 	slab->us_freecount++;
21758355f576SJeff Roberson 
21768355f576SJeff Roberson 	/* Zone statistics */
2177099a0e58SBosko Milekic 	keg->uk_free++;
21788355f576SJeff Roberson 
2179099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_FULL) {
2180099a0e58SBosko Milekic 		if (keg->uk_pages < keg->uk_maxpages)
2181099a0e58SBosko Milekic 			keg->uk_flags &= ~UMA_ZFLAG_FULL;
2182af7f9b97SJeff Roberson 
2183af7f9b97SJeff Roberson 		/* We can handle one more allocation */
2184099a0e58SBosko Milekic 		wakeup_one(keg);
2185af7f9b97SJeff Roberson 	}
2186af7f9b97SJeff Roberson 
2187605cbd6aSJeff Roberson 	ZONE_UNLOCK(zone);
21888355f576SJeff Roberson }
21898355f576SJeff Roberson 
21908355f576SJeff Roberson /* See uma.h */
21918355f576SJeff Roberson void
2192736ee590SJeff Roberson uma_zone_set_max(uma_zone_t zone, int nitems)
2193736ee590SJeff Roberson {
2194099a0e58SBosko Milekic 	uma_keg_t keg;
2195099a0e58SBosko Milekic 
2196099a0e58SBosko Milekic 	keg = zone->uz_keg;
2197736ee590SJeff Roberson 	ZONE_LOCK(zone);
2198099a0e58SBosko Milekic 	if (keg->uk_ppera > 1)
2199099a0e58SBosko Milekic 		keg->uk_maxpages = nitems * keg->uk_ppera;
2200736ee590SJeff Roberson 	else
2201099a0e58SBosko Milekic 		keg->uk_maxpages = nitems / keg->uk_ipers;
220228bc4419SJeff Roberson 
2203099a0e58SBosko Milekic 	if (keg->uk_maxpages * keg->uk_ipers < nitems)
2204099a0e58SBosko Milekic 		keg->uk_maxpages++;
220528bc4419SJeff Roberson 
2206736ee590SJeff Roberson 	ZONE_UNLOCK(zone);
2207736ee590SJeff Roberson }
2208736ee590SJeff Roberson 
2209736ee590SJeff Roberson /* See uma.h */
2210736ee590SJeff Roberson void
2211099a0e58SBosko Milekic uma_zone_set_init(uma_zone_t zone, uma_init uminit)
2212099a0e58SBosko Milekic {
2213099a0e58SBosko Milekic 	ZONE_LOCK(zone);
2214099a0e58SBosko Milekic 	KASSERT(zone->uz_keg->uk_pages == 0,
2215099a0e58SBosko Milekic 	    ("uma_zone_set_init on non-empty keg"));
2216099a0e58SBosko Milekic 	zone->uz_keg->uk_init = uminit;
2217099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
2218099a0e58SBosko Milekic }
2219099a0e58SBosko Milekic 
2220099a0e58SBosko Milekic /* See uma.h */
2221099a0e58SBosko Milekic void
2222099a0e58SBosko Milekic uma_zone_set_fini(uma_zone_t zone, uma_fini fini)
2223099a0e58SBosko Milekic {
2224099a0e58SBosko Milekic 	ZONE_LOCK(zone);
2225099a0e58SBosko Milekic 	KASSERT(zone->uz_keg->uk_pages == 0,
2226099a0e58SBosko Milekic 	    ("uma_zone_set_fini on non-empty keg"));
2227099a0e58SBosko Milekic 	zone->uz_keg->uk_fini = fini;
2228099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
2229099a0e58SBosko Milekic }
2230099a0e58SBosko Milekic 
2231099a0e58SBosko Milekic /* See uma.h */
2232099a0e58SBosko Milekic void
2233099a0e58SBosko Milekic uma_zone_set_zinit(uma_zone_t zone, uma_init zinit)
2234099a0e58SBosko Milekic {
2235099a0e58SBosko Milekic 	ZONE_LOCK(zone);
2236099a0e58SBosko Milekic 	KASSERT(zone->uz_keg->uk_pages == 0,
2237099a0e58SBosko Milekic 	    ("uma_zone_set_zinit on non-empty keg"));
2238099a0e58SBosko Milekic 	zone->uz_init = zinit;
2239099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
2240099a0e58SBosko Milekic }
2241099a0e58SBosko Milekic 
2242099a0e58SBosko Milekic /* See uma.h */
2243099a0e58SBosko Milekic void
2244099a0e58SBosko Milekic uma_zone_set_zfini(uma_zone_t zone, uma_fini zfini)
2245099a0e58SBosko Milekic {
2246099a0e58SBosko Milekic 	ZONE_LOCK(zone);
2247099a0e58SBosko Milekic 	KASSERT(zone->uz_keg->uk_pages == 0,
2248099a0e58SBosko Milekic 	    ("uma_zone_set_zfini on non-empty keg"));
2249099a0e58SBosko Milekic 	zone->uz_fini = zfini;
2250099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
2251099a0e58SBosko Milekic }
2252099a0e58SBosko Milekic 
2253099a0e58SBosko Milekic /* See uma.h */
2254099a0e58SBosko Milekic void
22558355f576SJeff Roberson uma_zone_set_freef(uma_zone_t zone, uma_free freef)
22568355f576SJeff Roberson {
22578355f576SJeff Roberson 	ZONE_LOCK(zone);
2258099a0e58SBosko Milekic 	zone->uz_keg->uk_freef = freef;
22598355f576SJeff Roberson 	ZONE_UNLOCK(zone);
22608355f576SJeff Roberson }
22618355f576SJeff Roberson 
22628355f576SJeff Roberson /* See uma.h */
22638355f576SJeff Roberson void
22648355f576SJeff Roberson uma_zone_set_allocf(uma_zone_t zone, uma_alloc allocf)
22658355f576SJeff Roberson {
22668355f576SJeff Roberson 	ZONE_LOCK(zone);
2267099a0e58SBosko Milekic 	zone->uz_keg->uk_flags |= UMA_ZFLAG_PRIVALLOC;
2268099a0e58SBosko Milekic 	zone->uz_keg->uk_allocf = allocf;
22698355f576SJeff Roberson 	ZONE_UNLOCK(zone);
22708355f576SJeff Roberson }
22718355f576SJeff Roberson 
22728355f576SJeff Roberson /* See uma.h */
22738355f576SJeff Roberson int
22748355f576SJeff Roberson uma_zone_set_obj(uma_zone_t zone, struct vm_object *obj, int count)
22758355f576SJeff Roberson {
2276099a0e58SBosko Milekic 	uma_keg_t keg;
22778355f576SJeff Roberson 	vm_offset_t kva;
2278099a0e58SBosko Milekic 	int pages;
22798355f576SJeff Roberson 
2280099a0e58SBosko Milekic 	keg = zone->uz_keg;
2281099a0e58SBosko Milekic 	pages = count / keg->uk_ipers;
22828355f576SJeff Roberson 
2283099a0e58SBosko Milekic 	if (pages * keg->uk_ipers < count)
22848355f576SJeff Roberson 		pages++;
2285a553d4b8SJeff Roberson 
22868355f576SJeff Roberson 	kva = kmem_alloc_pageable(kernel_map, pages * UMA_SLAB_SIZE);
22878355f576SJeff Roberson 
2288d1f42ac2SAlan Cox 	if (kva == 0)
22898355f576SJeff Roberson 		return (0);
229064f051e9SJeff Roberson 	if (obj == NULL) {
2291a553d4b8SJeff Roberson 		obj = vm_object_allocate(OBJT_DEFAULT,
2292c7173f58SJeff Roberson 		    pages);
229364f051e9SJeff Roberson 	} else {
229482774d80SAlan Cox 		VM_OBJECT_LOCK_INIT(obj);
22958355f576SJeff Roberson 		_vm_object_allocate(OBJT_DEFAULT,
2296c7173f58SJeff Roberson 		    pages, obj);
229782774d80SAlan Cox 	}
2298a553d4b8SJeff Roberson 	ZONE_LOCK(zone);
2299099a0e58SBosko Milekic 	keg->uk_kva = kva;
2300099a0e58SBosko Milekic 	keg->uk_obj = obj;
2301099a0e58SBosko Milekic 	keg->uk_maxpages = pages;
2302099a0e58SBosko Milekic 	keg->uk_allocf = obj_alloc;
2303099a0e58SBosko Milekic 	keg->uk_flags |= UMA_ZONE_NOFREE | UMA_ZFLAG_PRIVALLOC;
23048355f576SJeff Roberson 	ZONE_UNLOCK(zone);
23058355f576SJeff Roberson 	return (1);
23068355f576SJeff Roberson }
23078355f576SJeff Roberson 
23088355f576SJeff Roberson /* See uma.h */
23098355f576SJeff Roberson void
23108355f576SJeff Roberson uma_prealloc(uma_zone_t zone, int items)
23118355f576SJeff Roberson {
23128355f576SJeff Roberson 	int slabs;
23138355f576SJeff Roberson 	uma_slab_t slab;
2314099a0e58SBosko Milekic 	uma_keg_t keg;
23158355f576SJeff Roberson 
2316099a0e58SBosko Milekic 	keg = zone->uz_keg;
23178355f576SJeff Roberson 	ZONE_LOCK(zone);
2318099a0e58SBosko Milekic 	slabs = items / keg->uk_ipers;
2319099a0e58SBosko Milekic 	if (slabs * keg->uk_ipers < items)
23208355f576SJeff Roberson 		slabs++;
23218355f576SJeff Roberson 	while (slabs > 0) {
2322a163d034SWarner Losh 		slab = slab_zalloc(zone, M_WAITOK);
2323099a0e58SBosko Milekic 		LIST_INSERT_HEAD(&keg->uk_free_slab, slab, us_link);
23248355f576SJeff Roberson 		slabs--;
23258355f576SJeff Roberson 	}
23268355f576SJeff Roberson 	ZONE_UNLOCK(zone);
23278355f576SJeff Roberson }
23288355f576SJeff Roberson 
23298355f576SJeff Roberson /* See uma.h */
2330099a0e58SBosko Milekic u_int32_t *
2331099a0e58SBosko Milekic uma_find_refcnt(uma_zone_t zone, void *item)
2332099a0e58SBosko Milekic {
2333099a0e58SBosko Milekic 	uma_slabrefcnt_t slab;
2334099a0e58SBosko Milekic 	uma_keg_t keg;
2335099a0e58SBosko Milekic 	u_int32_t *refcnt;
2336099a0e58SBosko Milekic 	int idx;
2337099a0e58SBosko Milekic 
2338099a0e58SBosko Milekic 	keg = zone->uz_keg;
2339099a0e58SBosko Milekic 	slab = (uma_slabrefcnt_t)vtoslab((vm_offset_t)item & (~UMA_SLAB_MASK));
2340099a0e58SBosko Milekic 	KASSERT(slab != NULL,
2341099a0e58SBosko Milekic 	    ("uma_find_refcnt(): zone possibly not UMA_ZONE_REFCNT"));
2342099a0e58SBosko Milekic 	idx = ((unsigned long)item - (unsigned long)slab->us_data)
2343099a0e58SBosko Milekic 	    / keg->uk_rsize;
2344099a0e58SBosko Milekic 	refcnt = &(slab->us_freelist[idx].us_refcnt);
2345099a0e58SBosko Milekic 	return refcnt;
2346099a0e58SBosko Milekic }
2347099a0e58SBosko Milekic 
2348099a0e58SBosko Milekic /* See uma.h */
23498355f576SJeff Roberson void
23508355f576SJeff Roberson uma_reclaim(void)
23518355f576SJeff Roberson {
23528355f576SJeff Roberson #ifdef UMA_DEBUG
23538355f576SJeff Roberson 	printf("UMA: vm asked us to release pages!\n");
23548355f576SJeff Roberson #endif
235586bbae32SJeff Roberson 	bucket_enable();
23568355f576SJeff Roberson 	zone_foreach(zone_drain);
23578355f576SJeff Roberson 	/*
23588355f576SJeff Roberson 	 * Some slabs may have been freed but this zone will be visited early
23598355f576SJeff Roberson 	 * we visit again so that we can free pages that are empty once other
23608355f576SJeff Roberson 	 * zones are drained.  We have to do the same for buckets.
23618355f576SJeff Roberson 	 */
23629643769aSJeff Roberson 	zone_drain(slabzone);
2363099a0e58SBosko Milekic 	zone_drain(slabrefzone);
2364cae33c14SJeff Roberson 	bucket_zone_drain();
23658355f576SJeff Roberson }
23668355f576SJeff Roberson 
23678355f576SJeff Roberson void *
23688355f576SJeff Roberson uma_large_malloc(int size, int wait)
23698355f576SJeff Roberson {
23708355f576SJeff Roberson 	void *mem;
23718355f576SJeff Roberson 	uma_slab_t slab;
23728355f576SJeff Roberson 	u_int8_t flags;
23738355f576SJeff Roberson 
2374bbee39c6SJeff Roberson 	slab = uma_zalloc_internal(slabzone, NULL, wait);
23758355f576SJeff Roberson 	if (slab == NULL)
23768355f576SJeff Roberson 		return (NULL);
23778355f576SJeff Roberson 	mem = page_alloc(NULL, size, &flags, wait);
23788355f576SJeff Roberson 	if (mem) {
237999571dc3SJeff Roberson 		vsetslab((vm_offset_t)mem, slab);
23808355f576SJeff Roberson 		slab->us_data = mem;
23818355f576SJeff Roberson 		slab->us_flags = flags | UMA_SLAB_MALLOC;
23828355f576SJeff Roberson 		slab->us_size = size;
23838355f576SJeff Roberson 	} else {
23848355f576SJeff Roberson 		uma_zfree_internal(slabzone, slab, NULL, 0);
23858355f576SJeff Roberson 	}
23868355f576SJeff Roberson 
23878355f576SJeff Roberson 	return (mem);
23888355f576SJeff Roberson }
23898355f576SJeff Roberson 
23908355f576SJeff Roberson void
23918355f576SJeff Roberson uma_large_free(uma_slab_t slab)
23928355f576SJeff Roberson {
239399571dc3SJeff Roberson 	vsetobj((vm_offset_t)slab->us_data, kmem_object);
23948355f576SJeff Roberson 	page_free(slab->us_data, slab->us_size, slab->us_flags);
23958355f576SJeff Roberson 	uma_zfree_internal(slabzone, slab, NULL, 0);
23968355f576SJeff Roberson }
23978355f576SJeff Roberson 
23988355f576SJeff Roberson void
23998355f576SJeff Roberson uma_print_stats(void)
24008355f576SJeff Roberson {
24018355f576SJeff Roberson 	zone_foreach(uma_print_zone);
24028355f576SJeff Roberson }
24038355f576SJeff Roberson 
2404504d5de3SJeff Roberson static void
2405504d5de3SJeff Roberson slab_print(uma_slab_t slab)
2406504d5de3SJeff Roberson {
2407099a0e58SBosko Milekic 	printf("slab: keg %p, data %p, freecount %d, firstfree %d\n",
2408099a0e58SBosko Milekic 		slab->us_keg, slab->us_data, slab->us_freecount,
2409504d5de3SJeff Roberson 		slab->us_firstfree);
2410504d5de3SJeff Roberson }
2411504d5de3SJeff Roberson 
2412504d5de3SJeff Roberson static void
2413504d5de3SJeff Roberson cache_print(uma_cache_t cache)
2414504d5de3SJeff Roberson {
2415504d5de3SJeff Roberson 	printf("alloc: %p(%d), free: %p(%d)\n",
2416504d5de3SJeff Roberson 		cache->uc_allocbucket,
2417504d5de3SJeff Roberson 		cache->uc_allocbucket?cache->uc_allocbucket->ub_cnt:0,
2418504d5de3SJeff Roberson 		cache->uc_freebucket,
2419504d5de3SJeff Roberson 		cache->uc_freebucket?cache->uc_freebucket->ub_cnt:0);
2420504d5de3SJeff Roberson }
2421504d5de3SJeff Roberson 
24228355f576SJeff Roberson void
24238355f576SJeff Roberson uma_print_zone(uma_zone_t zone)
24248355f576SJeff Roberson {
2425504d5de3SJeff Roberson 	uma_cache_t cache;
2426099a0e58SBosko Milekic 	uma_keg_t keg;
2427504d5de3SJeff Roberson 	uma_slab_t slab;
2428504d5de3SJeff Roberson 	int i;
2429504d5de3SJeff Roberson 
2430099a0e58SBosko Milekic 	keg = zone->uz_keg;
24318355f576SJeff Roberson 	printf("%s(%p) size %d(%d) flags %d ipers %d ppera %d out %d free %d\n",
2432099a0e58SBosko Milekic 	    zone->uz_name, zone, keg->uk_size, keg->uk_rsize, keg->uk_flags,
2433099a0e58SBosko Milekic 	    keg->uk_ipers, keg->uk_ppera,
2434099a0e58SBosko Milekic 	    (keg->uk_ipers * keg->uk_pages) - keg->uk_free, keg->uk_free);
2435504d5de3SJeff Roberson 	printf("Part slabs:\n");
2436099a0e58SBosko Milekic 	LIST_FOREACH(slab, &keg->uk_part_slab, us_link)
2437504d5de3SJeff Roberson 		slab_print(slab);
2438504d5de3SJeff Roberson 	printf("Free slabs:\n");
2439099a0e58SBosko Milekic 	LIST_FOREACH(slab, &keg->uk_free_slab, us_link)
2440504d5de3SJeff Roberson 		slab_print(slab);
2441504d5de3SJeff Roberson 	printf("Full slabs:\n");
2442099a0e58SBosko Milekic 	LIST_FOREACH(slab, &keg->uk_full_slab, us_link)
2443504d5de3SJeff Roberson 		slab_print(slab);
2444b6c71225SJohn Baldwin 	for (i = 0; i <= mp_maxid; i++) {
2445504d5de3SJeff Roberson 		if (CPU_ABSENT(i))
2446504d5de3SJeff Roberson 			continue;
2447504d5de3SJeff Roberson 		cache = &zone->uz_cpu[i];
2448504d5de3SJeff Roberson 		printf("CPU %d Cache:\n", i);
2449504d5de3SJeff Roberson 		cache_print(cache);
2450504d5de3SJeff Roberson 	}
24518355f576SJeff Roberson }
24528355f576SJeff Roberson 
24538355f576SJeff Roberson /*
24548355f576SJeff Roberson  * Sysctl handler for vm.zone
24558355f576SJeff Roberson  *
24568355f576SJeff Roberson  * stolen from vm_zone.c
24578355f576SJeff Roberson  */
24588355f576SJeff Roberson static int
24598355f576SJeff Roberson sysctl_vm_zone(SYSCTL_HANDLER_ARGS)
24608355f576SJeff Roberson {
24618355f576SJeff Roberson 	int error, len, cnt;
24628355f576SJeff Roberson 	const int linesize = 128;	/* conservative */
24638355f576SJeff Roberson 	int totalfree;
24648355f576SJeff Roberson 	char *tmpbuf, *offset;
24658355f576SJeff Roberson 	uma_zone_t z;
2466099a0e58SBosko Milekic 	uma_keg_t zk;
24678355f576SJeff Roberson 	char *p;
2468f828e5beSJeff Roberson 	int cpu;
2469f828e5beSJeff Roberson 	int cachefree;
2470f828e5beSJeff Roberson 	uma_bucket_t bucket;
2471f828e5beSJeff Roberson 	uma_cache_t cache;
24728355f576SJeff Roberson 
24738355f576SJeff Roberson 	cnt = 0;
24740da47b2fSJeff Roberson 	mtx_lock(&uma_mtx);
2475099a0e58SBosko Milekic 	LIST_FOREACH(zk, &uma_kegs, uk_link) {
2476099a0e58SBosko Milekic 		LIST_FOREACH(z, &zk->uk_zones, uz_link)
24778355f576SJeff Roberson 			cnt++;
2478099a0e58SBosko Milekic 	}
24790da47b2fSJeff Roberson 	mtx_unlock(&uma_mtx);
24808355f576SJeff Roberson 	MALLOC(tmpbuf, char *, (cnt == 0 ? 1 : cnt) * linesize,
2481a163d034SWarner Losh 			M_TEMP, M_WAITOK);
24828355f576SJeff Roberson 	len = snprintf(tmpbuf, linesize,
24838355f576SJeff Roberson 	    "\nITEM            SIZE     LIMIT     USED    FREE  REQUESTS\n\n");
24848355f576SJeff Roberson 	if (cnt == 0)
24858355f576SJeff Roberson 		tmpbuf[len - 1] = '\0';
24868355f576SJeff Roberson 	error = SYSCTL_OUT(req, tmpbuf, cnt == 0 ? len-1 : len);
24878355f576SJeff Roberson 	if (error || cnt == 0)
24888355f576SJeff Roberson 		goto out;
24898355f576SJeff Roberson 	offset = tmpbuf;
2490f4af24d5SJeff Roberson 	mtx_lock(&uma_mtx);
2491099a0e58SBosko Milekic 	LIST_FOREACH(zk, &uma_kegs, uk_link) {
2492099a0e58SBosko Milekic 	  LIST_FOREACH(z, &zk->uk_zones, uz_link) {
24938355f576SJeff Roberson 		if (cnt == 0)	/* list may have changed size */
24948355f576SJeff Roberson 			break;
2495099a0e58SBosko Milekic 		if (!(zk->uk_flags & UMA_ZFLAG_INTERNAL)) {
2496b6c71225SJohn Baldwin 			for (cpu = 0; cpu <= mp_maxid; cpu++) {
2497f828e5beSJeff Roberson 				if (CPU_ABSENT(cpu))
2498f828e5beSJeff Roberson 					continue;
2499f828e5beSJeff Roberson 				CPU_LOCK(cpu);
2500f828e5beSJeff Roberson 			}
25011c35e213SBosko Milekic 		}
25028355f576SJeff Roberson 		ZONE_LOCK(z);
2503f828e5beSJeff Roberson 		cachefree = 0;
2504099a0e58SBosko Milekic 		if (!(zk->uk_flags & UMA_ZFLAG_INTERNAL)) {
2505b6c71225SJohn Baldwin 			for (cpu = 0; cpu <= mp_maxid; cpu++) {
2506f828e5beSJeff Roberson 				if (CPU_ABSENT(cpu))
2507f828e5beSJeff Roberson 					continue;
2508f828e5beSJeff Roberson 				cache = &z->uz_cpu[cpu];
2509f828e5beSJeff Roberson 				if (cache->uc_allocbucket != NULL)
2510cae33c14SJeff Roberson 					cachefree += cache->uc_allocbucket->ub_cnt;
2511f828e5beSJeff Roberson 				if (cache->uc_freebucket != NULL)
2512cae33c14SJeff Roberson 					cachefree += cache->uc_freebucket->ub_cnt;
2513f828e5beSJeff Roberson 				CPU_UNLOCK(cpu);
2514f828e5beSJeff Roberson 			}
25151c35e213SBosko Milekic 		}
2516f828e5beSJeff Roberson 		LIST_FOREACH(bucket, &z->uz_full_bucket, ub_link) {
2517cae33c14SJeff Roberson 			cachefree += bucket->ub_cnt;
2518f828e5beSJeff Roberson 		}
2519099a0e58SBosko Milekic 		totalfree = zk->uk_free + cachefree;
25208355f576SJeff Roberson 		len = snprintf(offset, linesize,
25218355f576SJeff Roberson 		    "%-12.12s  %6.6u, %8.8u, %6.6u, %6.6u, %8.8llu\n",
2522099a0e58SBosko Milekic 		    z->uz_name, zk->uk_size,
2523099a0e58SBosko Milekic 		    zk->uk_maxpages * zk->uk_ipers,
2524099a0e58SBosko Milekic 		    (zk->uk_ipers * (zk->uk_pages / zk->uk_ppera)) - totalfree,
25258355f576SJeff Roberson 		    totalfree,
25268355f576SJeff Roberson 		    (unsigned long long)z->uz_allocs);
25278355f576SJeff Roberson 		ZONE_UNLOCK(z);
25288355f576SJeff Roberson 		for (p = offset + 12; p > offset && *p == ' '; --p)
25298355f576SJeff Roberson 			/* nothing */ ;
25308355f576SJeff Roberson 		p[1] = ':';
25318355f576SJeff Roberson 		cnt--;
25328355f576SJeff Roberson 		offset += len;
25338355f576SJeff Roberson 	  }
2534099a0e58SBosko Milekic 	}
2535f4af24d5SJeff Roberson 	mtx_unlock(&uma_mtx);
25368355f576SJeff Roberson 	*offset++ = '\0';
25378355f576SJeff Roberson 	error = SYSCTL_OUT(req, tmpbuf, offset - tmpbuf);
25388355f576SJeff Roberson out:
25398355f576SJeff Roberson 	FREE(tmpbuf, M_TEMP);
25408355f576SJeff Roberson 	return (error);
25418355f576SJeff Roberson }
2542