xref: /freebsd/sys/vm/uma_core.c (revision 63b5d112b6d1fe36adf2f1c12e98df2c35117086)
160727d8bSWarner Losh /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3fe267a55SPedro F. Giffuni  *
4ef72505eSJeff Roberson  * Copyright (c) 2002-2005, 2009, 2013 Jeffrey Roberson <jeff@FreeBSD.org>
508ecce74SRobert Watson  * Copyright (c) 2004, 2005 Bosko Milekic <bmilekic@FreeBSD.org>
6ae4e9636SRobert Watson  * Copyright (c) 2004-2006 Robert N. M. Watson
708ecce74SRobert Watson  * All rights reserved.
88355f576SJeff Roberson  *
98355f576SJeff Roberson  * Redistribution and use in source and binary forms, with or without
108355f576SJeff Roberson  * modification, are permitted provided that the following conditions
118355f576SJeff Roberson  * are met:
128355f576SJeff Roberson  * 1. Redistributions of source code must retain the above copyright
138355f576SJeff Roberson  *    notice unmodified, this list of conditions, and the following
148355f576SJeff Roberson  *    disclaimer.
158355f576SJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
168355f576SJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
178355f576SJeff Roberson  *    documentation and/or other materials provided with the distribution.
188355f576SJeff Roberson  *
198355f576SJeff Roberson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
208355f576SJeff Roberson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
218355f576SJeff Roberson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
228355f576SJeff Roberson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
238355f576SJeff Roberson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
248355f576SJeff Roberson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
258355f576SJeff Roberson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
268355f576SJeff Roberson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
278355f576SJeff Roberson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
288355f576SJeff Roberson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
298355f576SJeff Roberson  */
308355f576SJeff Roberson 
318355f576SJeff Roberson /*
328355f576SJeff Roberson  * uma_core.c  Implementation of the Universal Memory allocator
338355f576SJeff Roberson  *
348355f576SJeff Roberson  * This allocator is intended to replace the multitude of similar object caches
358355f576SJeff Roberson  * in the standard FreeBSD kernel.  The intent is to be flexible as well as
36763df3ecSPedro F. Giffuni  * efficient.  A primary design goal is to return unused memory to the rest of
378355f576SJeff Roberson  * the system.  This will make the system as a whole more flexible due to the
388355f576SJeff Roberson  * ability to move memory to subsystems which most need it instead of leaving
398355f576SJeff Roberson  * pools of reserved memory unused.
408355f576SJeff Roberson  *
418355f576SJeff Roberson  * The basic ideas stem from similar slab/zone based allocators whose algorithms
428355f576SJeff Roberson  * are well known.
438355f576SJeff Roberson  *
448355f576SJeff Roberson  */
458355f576SJeff Roberson 
468355f576SJeff Roberson /*
478355f576SJeff Roberson  * TODO:
488355f576SJeff Roberson  *	- Improve memory usage for large allocations
498355f576SJeff Roberson  *	- Investigate cache size adjustments
508355f576SJeff Roberson  */
518355f576SJeff Roberson 
52874651b1SDavid E. O'Brien #include <sys/cdefs.h>
53874651b1SDavid E. O'Brien __FBSDID("$FreeBSD$");
54874651b1SDavid E. O'Brien 
5548c5777eSRobert Watson #include "opt_ddb.h"
568355f576SJeff Roberson #include "opt_param.h"
578d689e04SGleb Smirnoff #include "opt_vm.h"
5848c5777eSRobert Watson 
598355f576SJeff Roberson #include <sys/param.h>
608355f576SJeff Roberson #include <sys/systm.h>
61ef72505eSJeff Roberson #include <sys/bitset.h>
629b43bc27SAndriy Gapon #include <sys/eventhandler.h>
638355f576SJeff Roberson #include <sys/kernel.h>
648355f576SJeff Roberson #include <sys/types.h>
65ad5b0f5bSJeff Roberson #include <sys/limits.h>
668355f576SJeff Roberson #include <sys/queue.h>
678355f576SJeff Roberson #include <sys/malloc.h>
683659f747SRobert Watson #include <sys/ktr.h>
698355f576SJeff Roberson #include <sys/lock.h>
708355f576SJeff Roberson #include <sys/sysctl.h>
718355f576SJeff Roberson #include <sys/mutex.h>
724c1cc01cSJohn Baldwin #include <sys/proc.h>
7310cb2424SMark Murray #include <sys/random.h>
7489f6b863SAttilio Rao #include <sys/rwlock.h>
757a52a97eSRobert Watson #include <sys/sbuf.h>
76a2de44abSAlexander Motin #include <sys/sched.h>
778355f576SJeff Roberson #include <sys/smp.h>
78e60b2fcbSGleb Smirnoff #include <sys/taskqueue.h>
7986bbae32SJeff Roberson #include <sys/vmmeter.h>
8086bbae32SJeff Roberson 
818355f576SJeff Roberson #include <vm/vm.h>
828355f576SJeff Roberson #include <vm/vm_object.h>
838355f576SJeff Roberson #include <vm/vm_page.h>
84a4915c21SAttilio Rao #include <vm/vm_pageout.h>
858355f576SJeff Roberson #include <vm/vm_param.h>
86ab3185d1SJeff Roberson #include <vm/vm_phys.h>
878355f576SJeff Roberson #include <vm/vm_map.h>
888355f576SJeff Roberson #include <vm/vm_kern.h>
898355f576SJeff Roberson #include <vm/vm_extern.h>
908355f576SJeff Roberson #include <vm/uma.h>
918355f576SJeff Roberson #include <vm/uma_int.h>
92639c9550SJeff Roberson #include <vm/uma_dbg.h>
938355f576SJeff Roberson 
9448c5777eSRobert Watson #include <ddb/ddb.h>
9548c5777eSRobert Watson 
968d689e04SGleb Smirnoff #ifdef DEBUG_MEMGUARD
978d689e04SGleb Smirnoff #include <vm/memguard.h>
988d689e04SGleb Smirnoff #endif
998d689e04SGleb Smirnoff 
1008355f576SJeff Roberson /*
101ab3185d1SJeff Roberson  * This is the zone and keg from which all zones are spawned.
1028355f576SJeff Roberson  */
103ab3185d1SJeff Roberson static uma_zone_t kegs;
104ab3185d1SJeff Roberson static uma_zone_t zones;
1058355f576SJeff Roberson 
106ab3185d1SJeff Roberson /* This is the zone from which all offpage uma_slab_ts are allocated. */
1078355f576SJeff Roberson static uma_zone_t slabzone;
1088355f576SJeff Roberson 
1098355f576SJeff Roberson /*
1108355f576SJeff Roberson  * The initial hash tables come out of this zone so they can be allocated
1118355f576SJeff Roberson  * prior to malloc coming up.
1128355f576SJeff Roberson  */
1138355f576SJeff Roberson static uma_zone_t hashzone;
1148355f576SJeff Roberson 
1151e319f6dSRobert Watson /* The boot-time adjusted value for cache line alignment. */
116e4cd31ddSJeff Roberson int uma_align_cache = 64 - 1;
1171e319f6dSRobert Watson 
118961647dfSJeff Roberson static MALLOC_DEFINE(M_UMAHASH, "UMAHash", "UMA Hash Buckets");
119961647dfSJeff Roberson 
1208355f576SJeff Roberson /*
12186bbae32SJeff Roberson  * Are we allowed to allocate buckets?
12286bbae32SJeff Roberson  */
12386bbae32SJeff Roberson static int bucketdisable = 1;
12486bbae32SJeff Roberson 
125099a0e58SBosko Milekic /* Linked list of all kegs in the system */
12613e403fdSAntoine Brodin static LIST_HEAD(,uma_keg) uma_kegs = LIST_HEAD_INITIALIZER(uma_kegs);
1278355f576SJeff Roberson 
12803175483SAlexander Motin /* Linked list of all cache-only zones in the system */
12903175483SAlexander Motin static LIST_HEAD(,uma_zone) uma_cachezones =
13003175483SAlexander Motin     LIST_HEAD_INITIALIZER(uma_cachezones);
13103175483SAlexander Motin 
132111fbcd5SBryan Venteicher /* This RW lock protects the keg list */
133fe933c1dSMateusz Guzik static struct rwlock_padalign __exclusive_cache_line uma_rwlock;
1348355f576SJeff Roberson 
135ac0a6fd0SGleb Smirnoff /*
136ac0a6fd0SGleb Smirnoff  * Pointer and counter to pool of pages, that is preallocated at
137f7d35785SGleb Smirnoff  * startup to bootstrap UMA.
138ac0a6fd0SGleb Smirnoff  */
139ac0a6fd0SGleb Smirnoff static char *bootmem;
140ac0a6fd0SGleb Smirnoff static int boot_pages;
1418355f576SJeff Roberson 
14295c4bf75SKonstantin Belousov static struct sx uma_drain_lock;
14395c4bf75SKonstantin Belousov 
1442e47807cSJeff Roberson /* kmem soft limit. */
145ad5b0f5bSJeff Roberson static unsigned long uma_kmem_limit = LONG_MAX;
1462e47807cSJeff Roberson static volatile unsigned long uma_kmem_total;
1472e47807cSJeff Roberson 
1488355f576SJeff Roberson /* Is the VM done starting up? */
149f4bef67cSGleb Smirnoff static enum { BOOT_COLD = 0, BOOT_STRAPPED, BOOT_PAGEALLOC, BOOT_BUCKETS,
150f4bef67cSGleb Smirnoff     BOOT_RUNNING } booted = BOOT_COLD;
1518355f576SJeff Roberson 
152ef72505eSJeff Roberson /*
1539643769aSJeff Roberson  * This is the handle used to schedule events that need to happen
1549643769aSJeff Roberson  * outside of the allocation fast path.
1559643769aSJeff Roberson  */
1568355f576SJeff Roberson static struct callout uma_callout;
1579643769aSJeff Roberson #define	UMA_TIMEOUT	20		/* Seconds for callout interval. */
1588355f576SJeff Roberson 
1598355f576SJeff Roberson /*
1608355f576SJeff Roberson  * This structure is passed as the zone ctor arg so that I don't have to create
1618355f576SJeff Roberson  * a special allocation function just for zones.
1628355f576SJeff Roberson  */
1638355f576SJeff Roberson struct uma_zctor_args {
164bb196eb4SMatthew D Fleming 	const char *name;
165c3bdc05fSAndrew R. Reiter 	size_t size;
1668355f576SJeff Roberson 	uma_ctor ctor;
1678355f576SJeff Roberson 	uma_dtor dtor;
1688355f576SJeff Roberson 	uma_init uminit;
1698355f576SJeff Roberson 	uma_fini fini;
1700095a784SJeff Roberson 	uma_import import;
1710095a784SJeff Roberson 	uma_release release;
1720095a784SJeff Roberson 	void *arg;
173099a0e58SBosko Milekic 	uma_keg_t keg;
174099a0e58SBosko Milekic 	int align;
17585dcf349SGleb Smirnoff 	uint32_t flags;
176099a0e58SBosko Milekic };
177099a0e58SBosko Milekic 
178099a0e58SBosko Milekic struct uma_kctor_args {
179099a0e58SBosko Milekic 	uma_zone_t zone;
180099a0e58SBosko Milekic 	size_t size;
181099a0e58SBosko Milekic 	uma_init uminit;
182099a0e58SBosko Milekic 	uma_fini fini;
1838355f576SJeff Roberson 	int align;
18485dcf349SGleb Smirnoff 	uint32_t flags;
1858355f576SJeff Roberson };
1868355f576SJeff Roberson 
187cae33c14SJeff Roberson struct uma_bucket_zone {
188cae33c14SJeff Roberson 	uma_zone_t	ubz_zone;
189cae33c14SJeff Roberson 	char		*ubz_name;
190fc03d22bSJeff Roberson 	int		ubz_entries;	/* Number of items it can hold. */
191fc03d22bSJeff Roberson 	int		ubz_maxsize;	/* Maximum allocation size per-item. */
192cae33c14SJeff Roberson };
193cae33c14SJeff Roberson 
194f9d27e75SRobert Watson /*
195fc03d22bSJeff Roberson  * Compute the actual number of bucket entries to pack them in power
196fc03d22bSJeff Roberson  * of two sizes for more efficient space utilization.
197f9d27e75SRobert Watson  */
198fc03d22bSJeff Roberson #define	BUCKET_SIZE(n)						\
199fc03d22bSJeff Roberson     (((sizeof(void *) * (n)) - sizeof(struct uma_bucket)) / sizeof(void *))
200fc03d22bSJeff Roberson 
2011aa6c758SAlexander Motin #define	BUCKET_MAX	BUCKET_SIZE(256)
202fc03d22bSJeff Roberson 
203fc03d22bSJeff Roberson struct uma_bucket_zone bucket_zones[] = {
2046fd34d6fSJeff Roberson 	{ NULL, "4 Bucket", BUCKET_SIZE(4), 4096 },
205f3932e90SAlexander Motin 	{ NULL, "6 Bucket", BUCKET_SIZE(6), 3072 },
2066fd34d6fSJeff Roberson 	{ NULL, "8 Bucket", BUCKET_SIZE(8), 2048 },
207f3932e90SAlexander Motin 	{ NULL, "12 Bucket", BUCKET_SIZE(12), 1536 },
2086fd34d6fSJeff Roberson 	{ NULL, "16 Bucket", BUCKET_SIZE(16), 1024 },
209fc03d22bSJeff Roberson 	{ NULL, "32 Bucket", BUCKET_SIZE(32), 512 },
210fc03d22bSJeff Roberson 	{ NULL, "64 Bucket", BUCKET_SIZE(64), 256 },
211fc03d22bSJeff Roberson 	{ NULL, "128 Bucket", BUCKET_SIZE(128), 128 },
2121aa6c758SAlexander Motin 	{ NULL, "256 Bucket", BUCKET_SIZE(256), 64 },
213fc03d22bSJeff Roberson 	{ NULL, NULL, 0}
214fc03d22bSJeff Roberson };
215cae33c14SJeff Roberson 
2162019094aSRobert Watson /*
2172019094aSRobert Watson  * Flags and enumerations to be passed to internal functions.
2182019094aSRobert Watson  */
219ef72505eSJeff Roberson enum zfreeskip { SKIP_NONE = 0, SKIP_DTOR, SKIP_FINI };
220b23f72e9SBrian Feldman 
221ab3185d1SJeff Roberson #define	UMA_ANYDOMAIN	-1	/* Special value for domain search. */
222ab3185d1SJeff Roberson 
2238355f576SJeff Roberson /* Prototypes.. */
2248355f576SJeff Roberson 
225f4bef67cSGleb Smirnoff int	uma_startup_count(int);
226f4bef67cSGleb Smirnoff void	uma_startup(void *, int);
227f4bef67cSGleb Smirnoff void	uma_startup1(void);
228f4bef67cSGleb Smirnoff void	uma_startup2(void);
229f4bef67cSGleb Smirnoff 
230ab3185d1SJeff Roberson static void *noobj_alloc(uma_zone_t, vm_size_t, int, uint8_t *, int);
231ab3185d1SJeff Roberson static void *page_alloc(uma_zone_t, vm_size_t, int, uint8_t *, int);
232ab3185d1SJeff Roberson static void *startup_alloc(uma_zone_t, vm_size_t, int, uint8_t *, int);
233f2c2231eSRyan Stone static void page_free(void *, vm_size_t, uint8_t);
234ab3185d1SJeff Roberson static uma_slab_t keg_alloc_slab(uma_keg_t, uma_zone_t, int, int);
2359643769aSJeff Roberson static void cache_drain(uma_zone_t);
2368355f576SJeff Roberson static void bucket_drain(uma_zone_t, uma_bucket_t);
237aaa8bb16SJeff Roberson static void bucket_cache_drain(uma_zone_t zone);
238b23f72e9SBrian Feldman static int keg_ctor(void *, int, void *, int);
239099a0e58SBosko Milekic static void keg_dtor(void *, int, void *);
240b23f72e9SBrian Feldman static int zone_ctor(void *, int, void *, int);
2419c2cd7e5SJeff Roberson static void zone_dtor(void *, int, void *);
242b23f72e9SBrian Feldman static int zero_init(void *, int, int);
243e20a199fSJeff Roberson static void keg_small_init(uma_keg_t keg);
244e20a199fSJeff Roberson static void keg_large_init(uma_keg_t keg);
2458355f576SJeff Roberson static void zone_foreach(void (*zfunc)(uma_zone_t));
2468355f576SJeff Roberson static void zone_timeout(uma_zone_t zone);
2470aef6126SJeff Roberson static int hash_alloc(struct uma_hash *);
2480aef6126SJeff Roberson static int hash_expand(struct uma_hash *, struct uma_hash *);
2490aef6126SJeff Roberson static void hash_free(struct uma_hash *hash);
2508355f576SJeff Roberson static void uma_timeout(void *);
2518355f576SJeff Roberson static void uma_startup3(void);
252ab3185d1SJeff Roberson static void *zone_alloc_item(uma_zone_t, void *, int, int);
2530095a784SJeff Roberson static void zone_free_item(uma_zone_t, void *, void *, enum zfreeskip);
25486bbae32SJeff Roberson static void bucket_enable(void);
255cae33c14SJeff Roberson static void bucket_init(void);
2566fd34d6fSJeff Roberson static uma_bucket_t bucket_alloc(uma_zone_t zone, void *, int);
2576fd34d6fSJeff Roberson static void bucket_free(uma_zone_t zone, uma_bucket_t, void *);
258cae33c14SJeff Roberson static void bucket_zone_drain(void);
259ab3185d1SJeff Roberson static uma_bucket_t zone_alloc_bucket(uma_zone_t, void *, int, int);
260ab3185d1SJeff Roberson static uma_slab_t zone_fetch_slab(uma_zone_t, uma_keg_t, int, int);
261ab3185d1SJeff Roberson static uma_slab_t zone_fetch_slab_multi(uma_zone_t, uma_keg_t, int, int);
2620095a784SJeff Roberson static void *slab_alloc_item(uma_keg_t keg, uma_slab_t slab);
2630095a784SJeff Roberson static void slab_free_item(uma_keg_t keg, uma_slab_t slab, void *item);
264e20a199fSJeff Roberson static uma_keg_t uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit,
26585dcf349SGleb Smirnoff     uma_fini fini, int align, uint32_t flags);
266ab3185d1SJeff Roberson static int zone_import(uma_zone_t, void **, int, int, int);
267ab3185d1SJeff Roberson static void zone_release(uma_zone_t, void **, int);
268ab3185d1SJeff Roberson static void uma_zero_item(void *, uma_zone_t);
269bbee39c6SJeff Roberson 
2708355f576SJeff Roberson void uma_print_zone(uma_zone_t);
2718355f576SJeff Roberson void uma_print_stats(void);
2727a52a97eSRobert Watson static int sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS);
2737a52a97eSRobert Watson static int sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS);
2748355f576SJeff Roberson 
2759542ea7bSGleb Smirnoff #ifdef INVARIANTS
2769542ea7bSGleb Smirnoff static void uma_dbg_free(uma_zone_t zone, uma_slab_t slab, void *item);
2779542ea7bSGleb Smirnoff static void uma_dbg_alloc(uma_zone_t zone, uma_slab_t slab, void *item);
2789542ea7bSGleb Smirnoff #endif
2799542ea7bSGleb Smirnoff 
2808355f576SJeff Roberson SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL);
2818355f576SJeff Roberson 
2827a52a97eSRobert Watson SYSCTL_PROC(_vm, OID_AUTO, zone_count, CTLFLAG_RD|CTLTYPE_INT,
2837a52a97eSRobert Watson     0, 0, sysctl_vm_zone_count, "I", "Number of UMA zones");
2847a52a97eSRobert Watson 
2857a52a97eSRobert Watson SYSCTL_PROC(_vm, OID_AUTO, zone_stats, CTLFLAG_RD|CTLTYPE_STRUCT,
2867a52a97eSRobert Watson     0, 0, sysctl_vm_zone_stats, "s,struct uma_type_header", "Zone Stats");
2877a52a97eSRobert Watson 
2882f891cd5SPawel Jakub Dawidek static int zone_warnings = 1;
289af3b2549SHans Petter Selasky SYSCTL_INT(_vm, OID_AUTO, zone_warnings, CTLFLAG_RWTUN, &zone_warnings, 0,
2902f891cd5SPawel Jakub Dawidek     "Warn when UMA zones becomes full");
2912f891cd5SPawel Jakub Dawidek 
2922e47807cSJeff Roberson /* Adjust bytes under management by UMA. */
2932e47807cSJeff Roberson static inline void
2942e47807cSJeff Roberson uma_total_dec(unsigned long size)
2952e47807cSJeff Roberson {
2962e47807cSJeff Roberson 
2972e47807cSJeff Roberson 	atomic_subtract_long(&uma_kmem_total, size);
2982e47807cSJeff Roberson }
2992e47807cSJeff Roberson 
3002e47807cSJeff Roberson static inline void
3012e47807cSJeff Roberson uma_total_inc(unsigned long size)
3022e47807cSJeff Roberson {
3032e47807cSJeff Roberson 
3042e47807cSJeff Roberson 	if (atomic_fetchadd_long(&uma_kmem_total, size) > uma_kmem_limit)
3052e47807cSJeff Roberson 		uma_reclaim_wakeup();
3062e47807cSJeff Roberson }
3072e47807cSJeff Roberson 
30886bbae32SJeff Roberson /*
30986bbae32SJeff Roberson  * This routine checks to see whether or not it's safe to enable buckets.
31086bbae32SJeff Roberson  */
31186bbae32SJeff Roberson static void
31286bbae32SJeff Roberson bucket_enable(void)
31386bbae32SJeff Roberson {
314251386b4SMaksim Yevmenkin 	bucketdisable = vm_page_count_min();
31586bbae32SJeff Roberson }
31686bbae32SJeff Roberson 
317dc2c7965SRobert Watson /*
318dc2c7965SRobert Watson  * Initialize bucket_zones, the array of zones of buckets of various sizes.
319dc2c7965SRobert Watson  *
320dc2c7965SRobert Watson  * For each zone, calculate the memory required for each bucket, consisting
321fc03d22bSJeff Roberson  * of the header and an array of pointers.
322dc2c7965SRobert Watson  */
323cae33c14SJeff Roberson static void
324cae33c14SJeff Roberson bucket_init(void)
325cae33c14SJeff Roberson {
326cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
327cae33c14SJeff Roberson 	int size;
328cae33c14SJeff Roberson 
329d74e6a1dSAlan Cox 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++) {
330cae33c14SJeff Roberson 		size = roundup(sizeof(struct uma_bucket), sizeof(void *));
331cae33c14SJeff Roberson 		size += sizeof(void *) * ubz->ubz_entries;
332cae33c14SJeff Roberson 		ubz->ubz_zone = uma_zcreate(ubz->ubz_name, size,
333e20a199fSJeff Roberson 		    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,
334ab3185d1SJeff Roberson 		    UMA_ZONE_MTXCLASS | UMA_ZFLAG_BUCKET | UMA_ZONE_NUMA);
335cae33c14SJeff Roberson 	}
336cae33c14SJeff Roberson }
337cae33c14SJeff Roberson 
338dc2c7965SRobert Watson /*
339dc2c7965SRobert Watson  * Given a desired number of entries for a bucket, return the zone from which
340dc2c7965SRobert Watson  * to allocate the bucket.
341dc2c7965SRobert Watson  */
342dc2c7965SRobert Watson static struct uma_bucket_zone *
343dc2c7965SRobert Watson bucket_zone_lookup(int entries)
344dc2c7965SRobert Watson {
345fc03d22bSJeff Roberson 	struct uma_bucket_zone *ubz;
346dc2c7965SRobert Watson 
347fc03d22bSJeff Roberson 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++)
348fc03d22bSJeff Roberson 		if (ubz->ubz_entries >= entries)
349fc03d22bSJeff Roberson 			return (ubz);
350fc03d22bSJeff Roberson 	ubz--;
351fc03d22bSJeff Roberson 	return (ubz);
352fc03d22bSJeff Roberson }
353fc03d22bSJeff Roberson 
354fc03d22bSJeff Roberson static int
355fc03d22bSJeff Roberson bucket_select(int size)
356fc03d22bSJeff Roberson {
357fc03d22bSJeff Roberson 	struct uma_bucket_zone *ubz;
358fc03d22bSJeff Roberson 
359fc03d22bSJeff Roberson 	ubz = &bucket_zones[0];
360fc03d22bSJeff Roberson 	if (size > ubz->ubz_maxsize)
361fc03d22bSJeff Roberson 		return MAX((ubz->ubz_maxsize * ubz->ubz_entries) / size, 1);
362fc03d22bSJeff Roberson 
363fc03d22bSJeff Roberson 	for (; ubz->ubz_entries != 0; ubz++)
364fc03d22bSJeff Roberson 		if (ubz->ubz_maxsize < size)
365fc03d22bSJeff Roberson 			break;
366fc03d22bSJeff Roberson 	ubz--;
367fc03d22bSJeff Roberson 	return (ubz->ubz_entries);
368dc2c7965SRobert Watson }
369dc2c7965SRobert Watson 
370cae33c14SJeff Roberson static uma_bucket_t
3716fd34d6fSJeff Roberson bucket_alloc(uma_zone_t zone, void *udata, int flags)
372cae33c14SJeff Roberson {
373cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
374cae33c14SJeff Roberson 	uma_bucket_t bucket;
375cae33c14SJeff Roberson 
376cae33c14SJeff Roberson 	/*
377cae33c14SJeff Roberson 	 * This is to stop us from allocating per cpu buckets while we're
3783803b26bSDag-Erling Smørgrav 	 * running out of vm.boot_pages.  Otherwise, we would exhaust the
379cae33c14SJeff Roberson 	 * boot pages.  This also prevents us from allocating buckets in
380cae33c14SJeff Roberson 	 * low memory situations.
381cae33c14SJeff Roberson 	 */
382cae33c14SJeff Roberson 	if (bucketdisable)
383cae33c14SJeff Roberson 		return (NULL);
3846fd34d6fSJeff Roberson 	/*
3856fd34d6fSJeff Roberson 	 * To limit bucket recursion we store the original zone flags
3866fd34d6fSJeff Roberson 	 * in a cookie passed via zalloc_arg/zfree_arg.  This allows the
3876fd34d6fSJeff Roberson 	 * NOVM flag to persist even through deep recursions.  We also
3886fd34d6fSJeff Roberson 	 * store ZFLAG_BUCKET once we have recursed attempting to allocate
3896fd34d6fSJeff Roberson 	 * a bucket for a bucket zone so we do not allow infinite bucket
3906fd34d6fSJeff Roberson 	 * recursion.  This cookie will even persist to frees of unused
3916fd34d6fSJeff Roberson 	 * buckets via the allocation path or bucket allocations in the
3926fd34d6fSJeff Roberson 	 * free path.
3936fd34d6fSJeff Roberson 	 */
3946fd34d6fSJeff Roberson 	if ((zone->uz_flags & UMA_ZFLAG_BUCKET) == 0)
3956fd34d6fSJeff Roberson 		udata = (void *)(uintptr_t)zone->uz_flags;
396e8a720feSAlexander Motin 	else {
397e8a720feSAlexander Motin 		if ((uintptr_t)udata & UMA_ZFLAG_BUCKET)
398e8a720feSAlexander Motin 			return (NULL);
3996fd34d6fSJeff Roberson 		udata = (void *)((uintptr_t)udata | UMA_ZFLAG_BUCKET);
400e8a720feSAlexander Motin 	}
4016fd34d6fSJeff Roberson 	if ((uintptr_t)udata & UMA_ZFLAG_CACHEONLY)
402af526374SJeff Roberson 		flags |= M_NOVM;
403af526374SJeff Roberson 	ubz = bucket_zone_lookup(zone->uz_count);
40420d3ab87SAlexander Motin 	if (ubz->ubz_zone == zone && (ubz + 1)->ubz_entries != 0)
40520d3ab87SAlexander Motin 		ubz++;
4066fd34d6fSJeff Roberson 	bucket = uma_zalloc_arg(ubz->ubz_zone, udata, flags);
407cae33c14SJeff Roberson 	if (bucket) {
408cae33c14SJeff Roberson #ifdef INVARIANTS
409cae33c14SJeff Roberson 		bzero(bucket->ub_bucket, sizeof(void *) * ubz->ubz_entries);
410cae33c14SJeff Roberson #endif
411cae33c14SJeff Roberson 		bucket->ub_cnt = 0;
412cae33c14SJeff Roberson 		bucket->ub_entries = ubz->ubz_entries;
413cae33c14SJeff Roberson 	}
414cae33c14SJeff Roberson 
415cae33c14SJeff Roberson 	return (bucket);
416cae33c14SJeff Roberson }
417cae33c14SJeff Roberson 
418cae33c14SJeff Roberson static void
4196fd34d6fSJeff Roberson bucket_free(uma_zone_t zone, uma_bucket_t bucket, void *udata)
420cae33c14SJeff Roberson {
421cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
422cae33c14SJeff Roberson 
423fc03d22bSJeff Roberson 	KASSERT(bucket->ub_cnt == 0,
424fc03d22bSJeff Roberson 	    ("bucket_free: Freeing a non free bucket."));
4256fd34d6fSJeff Roberson 	if ((zone->uz_flags & UMA_ZFLAG_BUCKET) == 0)
4266fd34d6fSJeff Roberson 		udata = (void *)(uintptr_t)zone->uz_flags;
427dc2c7965SRobert Watson 	ubz = bucket_zone_lookup(bucket->ub_entries);
4286fd34d6fSJeff Roberson 	uma_zfree_arg(ubz->ubz_zone, bucket, udata);
429cae33c14SJeff Roberson }
430cae33c14SJeff Roberson 
431cae33c14SJeff Roberson static void
432cae33c14SJeff Roberson bucket_zone_drain(void)
433cae33c14SJeff Roberson {
434cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
435cae33c14SJeff Roberson 
436cae33c14SJeff Roberson 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++)
437cae33c14SJeff Roberson 		zone_drain(ubz->ubz_zone);
438cae33c14SJeff Roberson }
439cae33c14SJeff Roberson 
4402f891cd5SPawel Jakub Dawidek static void
4412f891cd5SPawel Jakub Dawidek zone_log_warning(uma_zone_t zone)
4422f891cd5SPawel Jakub Dawidek {
4432f891cd5SPawel Jakub Dawidek 	static const struct timeval warninterval = { 300, 0 };
4442f891cd5SPawel Jakub Dawidek 
4452f891cd5SPawel Jakub Dawidek 	if (!zone_warnings || zone->uz_warning == NULL)
4462f891cd5SPawel Jakub Dawidek 		return;
4472f891cd5SPawel Jakub Dawidek 
4482f891cd5SPawel Jakub Dawidek 	if (ratecheck(&zone->uz_ratecheck, &warninterval))
4492f891cd5SPawel Jakub Dawidek 		printf("[zone: %s] %s\n", zone->uz_name, zone->uz_warning);
4502f891cd5SPawel Jakub Dawidek }
4512f891cd5SPawel Jakub Dawidek 
45254503a13SJonathan T. Looney static inline void
45354503a13SJonathan T. Looney zone_maxaction(uma_zone_t zone)
45454503a13SJonathan T. Looney {
455e60b2fcbSGleb Smirnoff 
456e60b2fcbSGleb Smirnoff 	if (zone->uz_maxaction.ta_func != NULL)
457e60b2fcbSGleb Smirnoff 		taskqueue_enqueue(taskqueue_thread, &zone->uz_maxaction);
45854503a13SJonathan T. Looney }
45954503a13SJonathan T. Looney 
460e20a199fSJeff Roberson static void
461e20a199fSJeff Roberson zone_foreach_keg(uma_zone_t zone, void (*kegfn)(uma_keg_t))
462e20a199fSJeff Roberson {
463e20a199fSJeff Roberson 	uma_klink_t klink;
464e20a199fSJeff Roberson 
465e20a199fSJeff Roberson 	LIST_FOREACH(klink, &zone->uz_kegs, kl_link)
466e20a199fSJeff Roberson 		kegfn(klink->kl_keg);
467e20a199fSJeff Roberson }
4688355f576SJeff Roberson 
4698355f576SJeff Roberson /*
4708355f576SJeff Roberson  * Routine called by timeout which is used to fire off some time interval
4719643769aSJeff Roberson  * based calculations.  (stats, hash size, etc.)
4728355f576SJeff Roberson  *
4738355f576SJeff Roberson  * Arguments:
4748355f576SJeff Roberson  *	arg   Unused
4758355f576SJeff Roberson  *
4768355f576SJeff Roberson  * Returns:
4778355f576SJeff Roberson  *	Nothing
4788355f576SJeff Roberson  */
4798355f576SJeff Roberson static void
4808355f576SJeff Roberson uma_timeout(void *unused)
4818355f576SJeff Roberson {
48286bbae32SJeff Roberson 	bucket_enable();
4838355f576SJeff Roberson 	zone_foreach(zone_timeout);
4848355f576SJeff Roberson 
4858355f576SJeff Roberson 	/* Reschedule this event */
4869643769aSJeff Roberson 	callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
4878355f576SJeff Roberson }
4888355f576SJeff Roberson 
4898355f576SJeff Roberson /*
4909643769aSJeff Roberson  * Routine to perform timeout driven calculations.  This expands the
4919643769aSJeff Roberson  * hashes and does per cpu statistics aggregation.
4928355f576SJeff Roberson  *
493e20a199fSJeff Roberson  *  Returns nothing.
4948355f576SJeff Roberson  */
4958355f576SJeff Roberson static void
496e20a199fSJeff Roberson keg_timeout(uma_keg_t keg)
4978355f576SJeff Roberson {
4988355f576SJeff Roberson 
499e20a199fSJeff Roberson 	KEG_LOCK(keg);
5008355f576SJeff Roberson 	/*
501e20a199fSJeff Roberson 	 * Expand the keg hash table.
5028355f576SJeff Roberson 	 *
5038355f576SJeff Roberson 	 * This is done if the number of slabs is larger than the hash size.
5048355f576SJeff Roberson 	 * What I'm trying to do here is completely reduce collisions.  This
5058355f576SJeff Roberson 	 * may be a little aggressive.  Should I allow for two collisions max?
5068355f576SJeff Roberson 	 */
507099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH &&
508099a0e58SBosko Milekic 	    keg->uk_pages / keg->uk_ppera >= keg->uk_hash.uh_hashsize) {
5090aef6126SJeff Roberson 		struct uma_hash newhash;
5100aef6126SJeff Roberson 		struct uma_hash oldhash;
5110aef6126SJeff Roberson 		int ret;
5125300d9ddSJeff Roberson 
5130aef6126SJeff Roberson 		/*
5140aef6126SJeff Roberson 		 * This is so involved because allocating and freeing
515e20a199fSJeff Roberson 		 * while the keg lock is held will lead to deadlock.
5160aef6126SJeff Roberson 		 * I have to do everything in stages and check for
5170aef6126SJeff Roberson 		 * races.
5180aef6126SJeff Roberson 		 */
519099a0e58SBosko Milekic 		newhash = keg->uk_hash;
520e20a199fSJeff Roberson 		KEG_UNLOCK(keg);
5210aef6126SJeff Roberson 		ret = hash_alloc(&newhash);
522e20a199fSJeff Roberson 		KEG_LOCK(keg);
5230aef6126SJeff Roberson 		if (ret) {
524099a0e58SBosko Milekic 			if (hash_expand(&keg->uk_hash, &newhash)) {
525099a0e58SBosko Milekic 				oldhash = keg->uk_hash;
526099a0e58SBosko Milekic 				keg->uk_hash = newhash;
5270aef6126SJeff Roberson 			} else
5280aef6126SJeff Roberson 				oldhash = newhash;
5290aef6126SJeff Roberson 
530e20a199fSJeff Roberson 			KEG_UNLOCK(keg);
5310aef6126SJeff Roberson 			hash_free(&oldhash);
532a1dff920SDavide Italiano 			return;
5330aef6126SJeff Roberson 		}
5345300d9ddSJeff Roberson 	}
535e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
536e20a199fSJeff Roberson }
537e20a199fSJeff Roberson 
538e20a199fSJeff Roberson static void
539e20a199fSJeff Roberson zone_timeout(uma_zone_t zone)
540e20a199fSJeff Roberson {
541e20a199fSJeff Roberson 
542e20a199fSJeff Roberson 	zone_foreach_keg(zone, &keg_timeout);
5438355f576SJeff Roberson }
5448355f576SJeff Roberson 
5458355f576SJeff Roberson /*
5465300d9ddSJeff Roberson  * Allocate and zero fill the next sized hash table from the appropriate
5475300d9ddSJeff Roberson  * backing store.
5485300d9ddSJeff Roberson  *
5495300d9ddSJeff Roberson  * Arguments:
5500aef6126SJeff Roberson  *	hash  A new hash structure with the old hash size in uh_hashsize
5515300d9ddSJeff Roberson  *
5525300d9ddSJeff Roberson  * Returns:
553763df3ecSPedro F. Giffuni  *	1 on success and 0 on failure.
5545300d9ddSJeff Roberson  */
55537c84183SPoul-Henning Kamp static int
5560aef6126SJeff Roberson hash_alloc(struct uma_hash *hash)
5575300d9ddSJeff Roberson {
5580aef6126SJeff Roberson 	int oldsize;
5595300d9ddSJeff Roberson 	int alloc;
5605300d9ddSJeff Roberson 
5610aef6126SJeff Roberson 	oldsize = hash->uh_hashsize;
5620aef6126SJeff Roberson 
5635300d9ddSJeff Roberson 	/* We're just going to go to a power of two greater */
5640aef6126SJeff Roberson 	if (oldsize)  {
5650aef6126SJeff Roberson 		hash->uh_hashsize = oldsize * 2;
5660aef6126SJeff Roberson 		alloc = sizeof(hash->uh_slab_hash[0]) * hash->uh_hashsize;
5670aef6126SJeff Roberson 		hash->uh_slab_hash = (struct slabhead *)malloc(alloc,
568961647dfSJeff Roberson 		    M_UMAHASH, M_NOWAIT);
5695300d9ddSJeff Roberson 	} else {
5700aef6126SJeff Roberson 		alloc = sizeof(hash->uh_slab_hash[0]) * UMA_HASH_SIZE_INIT;
571e20a199fSJeff Roberson 		hash->uh_slab_hash = zone_alloc_item(hashzone, NULL,
572ab3185d1SJeff Roberson 		    UMA_ANYDOMAIN, M_WAITOK);
5730aef6126SJeff Roberson 		hash->uh_hashsize = UMA_HASH_SIZE_INIT;
5745300d9ddSJeff Roberson 	}
5750aef6126SJeff Roberson 	if (hash->uh_slab_hash) {
5760aef6126SJeff Roberson 		bzero(hash->uh_slab_hash, alloc);
5770aef6126SJeff Roberson 		hash->uh_hashmask = hash->uh_hashsize - 1;
5780aef6126SJeff Roberson 		return (1);
5790aef6126SJeff Roberson 	}
5805300d9ddSJeff Roberson 
5810aef6126SJeff Roberson 	return (0);
5825300d9ddSJeff Roberson }
5835300d9ddSJeff Roberson 
5845300d9ddSJeff Roberson /*
58564f051e9SJeff Roberson  * Expands the hash table for HASH zones.  This is done from zone_timeout
58664f051e9SJeff Roberson  * to reduce collisions.  This must not be done in the regular allocation
58764f051e9SJeff Roberson  * path, otherwise, we can recurse on the vm while allocating pages.
5888355f576SJeff Roberson  *
5898355f576SJeff Roberson  * Arguments:
5900aef6126SJeff Roberson  *	oldhash  The hash you want to expand
5910aef6126SJeff Roberson  *	newhash  The hash structure for the new table
5928355f576SJeff Roberson  *
5938355f576SJeff Roberson  * Returns:
5948355f576SJeff Roberson  *	Nothing
5958355f576SJeff Roberson  *
5968355f576SJeff Roberson  * Discussion:
5978355f576SJeff Roberson  */
5980aef6126SJeff Roberson static int
5990aef6126SJeff Roberson hash_expand(struct uma_hash *oldhash, struct uma_hash *newhash)
6008355f576SJeff Roberson {
6018355f576SJeff Roberson 	uma_slab_t slab;
6028355f576SJeff Roberson 	int hval;
6038355f576SJeff Roberson 	int i;
6048355f576SJeff Roberson 
6050aef6126SJeff Roberson 	if (!newhash->uh_slab_hash)
6060aef6126SJeff Roberson 		return (0);
6078355f576SJeff Roberson 
6080aef6126SJeff Roberson 	if (oldhash->uh_hashsize >= newhash->uh_hashsize)
6090aef6126SJeff Roberson 		return (0);
6108355f576SJeff Roberson 
6118355f576SJeff Roberson 	/*
6128355f576SJeff Roberson 	 * I need to investigate hash algorithms for resizing without a
6138355f576SJeff Roberson 	 * full rehash.
6148355f576SJeff Roberson 	 */
6158355f576SJeff Roberson 
6160aef6126SJeff Roberson 	for (i = 0; i < oldhash->uh_hashsize; i++)
6170aef6126SJeff Roberson 		while (!SLIST_EMPTY(&oldhash->uh_slab_hash[i])) {
6180aef6126SJeff Roberson 			slab = SLIST_FIRST(&oldhash->uh_slab_hash[i]);
6190aef6126SJeff Roberson 			SLIST_REMOVE_HEAD(&oldhash->uh_slab_hash[i], us_hlink);
6200aef6126SJeff Roberson 			hval = UMA_HASH(newhash, slab->us_data);
6210aef6126SJeff Roberson 			SLIST_INSERT_HEAD(&newhash->uh_slab_hash[hval],
6220aef6126SJeff Roberson 			    slab, us_hlink);
6238355f576SJeff Roberson 		}
6248355f576SJeff Roberson 
6250aef6126SJeff Roberson 	return (1);
6269c2cd7e5SJeff Roberson }
6279c2cd7e5SJeff Roberson 
6285300d9ddSJeff Roberson /*
6295300d9ddSJeff Roberson  * Free the hash bucket to the appropriate backing store.
6305300d9ddSJeff Roberson  *
6315300d9ddSJeff Roberson  * Arguments:
6325300d9ddSJeff Roberson  *	slab_hash  The hash bucket we're freeing
6335300d9ddSJeff Roberson  *	hashsize   The number of entries in that hash bucket
6345300d9ddSJeff Roberson  *
6355300d9ddSJeff Roberson  * Returns:
6365300d9ddSJeff Roberson  *	Nothing
6375300d9ddSJeff Roberson  */
6389c2cd7e5SJeff Roberson static void
6390aef6126SJeff Roberson hash_free(struct uma_hash *hash)
6409c2cd7e5SJeff Roberson {
6410aef6126SJeff Roberson 	if (hash->uh_slab_hash == NULL)
6420aef6126SJeff Roberson 		return;
6430aef6126SJeff Roberson 	if (hash->uh_hashsize == UMA_HASH_SIZE_INIT)
6440095a784SJeff Roberson 		zone_free_item(hashzone, hash->uh_slab_hash, NULL, SKIP_NONE);
6458355f576SJeff Roberson 	else
646961647dfSJeff Roberson 		free(hash->uh_slab_hash, M_UMAHASH);
6478355f576SJeff Roberson }
6488355f576SJeff Roberson 
6498355f576SJeff Roberson /*
6508355f576SJeff Roberson  * Frees all outstanding items in a bucket
6518355f576SJeff Roberson  *
6528355f576SJeff Roberson  * Arguments:
6538355f576SJeff Roberson  *	zone   The zone to free to, must be unlocked.
6548355f576SJeff Roberson  *	bucket The free/alloc bucket with items, cpu queue must be locked.
6558355f576SJeff Roberson  *
6568355f576SJeff Roberson  * Returns:
6578355f576SJeff Roberson  *	Nothing
6588355f576SJeff Roberson  */
6598355f576SJeff Roberson 
6608355f576SJeff Roberson static void
6618355f576SJeff Roberson bucket_drain(uma_zone_t zone, uma_bucket_t bucket)
6628355f576SJeff Roberson {
6630095a784SJeff Roberson 	int i;
6648355f576SJeff Roberson 
6658355f576SJeff Roberson 	if (bucket == NULL)
6668355f576SJeff Roberson 		return;
6678355f576SJeff Roberson 
6680095a784SJeff Roberson 	if (zone->uz_fini)
6690095a784SJeff Roberson 		for (i = 0; i < bucket->ub_cnt; i++)
6700095a784SJeff Roberson 			zone->uz_fini(bucket->ub_bucket[i], zone->uz_size);
6710095a784SJeff Roberson 	zone->uz_release(zone->uz_arg, bucket->ub_bucket, bucket->ub_cnt);
6720095a784SJeff Roberson 	bucket->ub_cnt = 0;
6738355f576SJeff Roberson }
6748355f576SJeff Roberson 
6758355f576SJeff Roberson /*
6768355f576SJeff Roberson  * Drains the per cpu caches for a zone.
6778355f576SJeff Roberson  *
6785d1ae027SRobert Watson  * NOTE: This may only be called while the zone is being turn down, and not
6795d1ae027SRobert Watson  * during normal operation.  This is necessary in order that we do not have
6805d1ae027SRobert Watson  * to migrate CPUs to drain the per-CPU caches.
6815d1ae027SRobert Watson  *
6828355f576SJeff Roberson  * Arguments:
6838355f576SJeff Roberson  *	zone     The zone to drain, must be unlocked.
6848355f576SJeff Roberson  *
6858355f576SJeff Roberson  * Returns:
6868355f576SJeff Roberson  *	Nothing
6878355f576SJeff Roberson  */
6888355f576SJeff Roberson static void
6899643769aSJeff Roberson cache_drain(uma_zone_t zone)
6908355f576SJeff Roberson {
6918355f576SJeff Roberson 	uma_cache_t cache;
6928355f576SJeff Roberson 	int cpu;
6938355f576SJeff Roberson 
6948355f576SJeff Roberson 	/*
6955d1ae027SRobert Watson 	 * XXX: It is safe to not lock the per-CPU caches, because we're
6965d1ae027SRobert Watson 	 * tearing down the zone anyway.  I.e., there will be no further use
6975d1ae027SRobert Watson 	 * of the caches at this point.
6985d1ae027SRobert Watson 	 *
6995d1ae027SRobert Watson 	 * XXX: It would good to be able to assert that the zone is being
7005d1ae027SRobert Watson 	 * torn down to prevent improper use of cache_drain().
7015d1ae027SRobert Watson 	 *
7025d1ae027SRobert Watson 	 * XXX: We lock the zone before passing into bucket_cache_drain() as
7035d1ae027SRobert Watson 	 * it is used elsewhere.  Should the tear-down path be made special
7045d1ae027SRobert Watson 	 * there in some form?
7058355f576SJeff Roberson 	 */
7063aa6d94eSJohn Baldwin 	CPU_FOREACH(cpu) {
7078355f576SJeff Roberson 		cache = &zone->uz_cpu[cpu];
7088355f576SJeff Roberson 		bucket_drain(zone, cache->uc_allocbucket);
7098355f576SJeff Roberson 		bucket_drain(zone, cache->uc_freebucket);
710174ab450SBosko Milekic 		if (cache->uc_allocbucket != NULL)
7116fd34d6fSJeff Roberson 			bucket_free(zone, cache->uc_allocbucket, NULL);
712174ab450SBosko Milekic 		if (cache->uc_freebucket != NULL)
7136fd34d6fSJeff Roberson 			bucket_free(zone, cache->uc_freebucket, NULL);
714d56368d7SBosko Milekic 		cache->uc_allocbucket = cache->uc_freebucket = NULL;
715d56368d7SBosko Milekic 	}
716aaa8bb16SJeff Roberson 	ZONE_LOCK(zone);
717aaa8bb16SJeff Roberson 	bucket_cache_drain(zone);
718aaa8bb16SJeff Roberson 	ZONE_UNLOCK(zone);
719aaa8bb16SJeff Roberson }
720aaa8bb16SJeff Roberson 
721a2de44abSAlexander Motin static void
722a2de44abSAlexander Motin cache_shrink(uma_zone_t zone)
723a2de44abSAlexander Motin {
724a2de44abSAlexander Motin 
725a2de44abSAlexander Motin 	if (zone->uz_flags & UMA_ZFLAG_INTERNAL)
726a2de44abSAlexander Motin 		return;
727a2de44abSAlexander Motin 
728a2de44abSAlexander Motin 	ZONE_LOCK(zone);
729a2de44abSAlexander Motin 	zone->uz_count = (zone->uz_count_min + zone->uz_count) / 2;
730a2de44abSAlexander Motin 	ZONE_UNLOCK(zone);
731a2de44abSAlexander Motin }
732a2de44abSAlexander Motin 
733a2de44abSAlexander Motin static void
734a2de44abSAlexander Motin cache_drain_safe_cpu(uma_zone_t zone)
735a2de44abSAlexander Motin {
736a2de44abSAlexander Motin 	uma_cache_t cache;
7378a8d9d14SAlexander Motin 	uma_bucket_t b1, b2;
738ab3185d1SJeff Roberson 	int domain;
739a2de44abSAlexander Motin 
740a2de44abSAlexander Motin 	if (zone->uz_flags & UMA_ZFLAG_INTERNAL)
741a2de44abSAlexander Motin 		return;
742a2de44abSAlexander Motin 
7438a8d9d14SAlexander Motin 	b1 = b2 = NULL;
744a2de44abSAlexander Motin 	ZONE_LOCK(zone);
745a2de44abSAlexander Motin 	critical_enter();
746ab3185d1SJeff Roberson 	if (zone->uz_flags & UMA_ZONE_NUMA)
747ab3185d1SJeff Roberson 		domain = PCPU_GET(domain);
748ab3185d1SJeff Roberson 	else
749ab3185d1SJeff Roberson 		domain = 0;
750a2de44abSAlexander Motin 	cache = &zone->uz_cpu[curcpu];
751a2de44abSAlexander Motin 	if (cache->uc_allocbucket) {
7528a8d9d14SAlexander Motin 		if (cache->uc_allocbucket->ub_cnt != 0)
753ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zone->uz_domain[domain].uzd_buckets,
7548a8d9d14SAlexander Motin 			    cache->uc_allocbucket, ub_link);
7558a8d9d14SAlexander Motin 		else
7568a8d9d14SAlexander Motin 			b1 = cache->uc_allocbucket;
757a2de44abSAlexander Motin 		cache->uc_allocbucket = NULL;
758a2de44abSAlexander Motin 	}
759a2de44abSAlexander Motin 	if (cache->uc_freebucket) {
7608a8d9d14SAlexander Motin 		if (cache->uc_freebucket->ub_cnt != 0)
761ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zone->uz_domain[domain].uzd_buckets,
7628a8d9d14SAlexander Motin 			    cache->uc_freebucket, ub_link);
7638a8d9d14SAlexander Motin 		else
7648a8d9d14SAlexander Motin 			b2 = cache->uc_freebucket;
765a2de44abSAlexander Motin 		cache->uc_freebucket = NULL;
766a2de44abSAlexander Motin 	}
767a2de44abSAlexander Motin 	critical_exit();
768a2de44abSAlexander Motin 	ZONE_UNLOCK(zone);
7698a8d9d14SAlexander Motin 	if (b1)
7708a8d9d14SAlexander Motin 		bucket_free(zone, b1, NULL);
7718a8d9d14SAlexander Motin 	if (b2)
7728a8d9d14SAlexander Motin 		bucket_free(zone, b2, NULL);
773a2de44abSAlexander Motin }
774a2de44abSAlexander Motin 
775a2de44abSAlexander Motin /*
776a2de44abSAlexander Motin  * Safely drain per-CPU caches of a zone(s) to alloc bucket.
777a2de44abSAlexander Motin  * This is an expensive call because it needs to bind to all CPUs
778a2de44abSAlexander Motin  * one by one and enter a critical section on each of them in order
779a2de44abSAlexander Motin  * to safely access their cache buckets.
780a2de44abSAlexander Motin  * Zone lock must not be held on call this function.
781a2de44abSAlexander Motin  */
782a2de44abSAlexander Motin static void
783a2de44abSAlexander Motin cache_drain_safe(uma_zone_t zone)
784a2de44abSAlexander Motin {
785a2de44abSAlexander Motin 	int cpu;
786a2de44abSAlexander Motin 
787a2de44abSAlexander Motin 	/*
788a2de44abSAlexander Motin 	 * Polite bucket sizes shrinking was not enouth, shrink aggressively.
789a2de44abSAlexander Motin 	 */
790a2de44abSAlexander Motin 	if (zone)
791a2de44abSAlexander Motin 		cache_shrink(zone);
792a2de44abSAlexander Motin 	else
793a2de44abSAlexander Motin 		zone_foreach(cache_shrink);
794a2de44abSAlexander Motin 
795a2de44abSAlexander Motin 	CPU_FOREACH(cpu) {
796a2de44abSAlexander Motin 		thread_lock(curthread);
797a2de44abSAlexander Motin 		sched_bind(curthread, cpu);
798a2de44abSAlexander Motin 		thread_unlock(curthread);
799a2de44abSAlexander Motin 
800a2de44abSAlexander Motin 		if (zone)
801a2de44abSAlexander Motin 			cache_drain_safe_cpu(zone);
802a2de44abSAlexander Motin 		else
803a2de44abSAlexander Motin 			zone_foreach(cache_drain_safe_cpu);
804a2de44abSAlexander Motin 	}
805a2de44abSAlexander Motin 	thread_lock(curthread);
806a2de44abSAlexander Motin 	sched_unbind(curthread);
807a2de44abSAlexander Motin 	thread_unlock(curthread);
808a2de44abSAlexander Motin }
809a2de44abSAlexander Motin 
810aaa8bb16SJeff Roberson /*
811aaa8bb16SJeff Roberson  * Drain the cached buckets from a zone.  Expects a locked zone on entry.
812aaa8bb16SJeff Roberson  */
813aaa8bb16SJeff Roberson static void
814aaa8bb16SJeff Roberson bucket_cache_drain(uma_zone_t zone)
815aaa8bb16SJeff Roberson {
816ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
817aaa8bb16SJeff Roberson 	uma_bucket_t bucket;
818ab3185d1SJeff Roberson 	int i;
8198355f576SJeff Roberson 
8208355f576SJeff Roberson 	/*
821ab3185d1SJeff Roberson 	 * Drain the bucket queues and free the buckets.
8228355f576SJeff Roberson 	 */
823ab3185d1SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
824ab3185d1SJeff Roberson 		zdom = &zone->uz_domain[i];
825ab3185d1SJeff Roberson 		while ((bucket = LIST_FIRST(&zdom->uzd_buckets)) != NULL) {
8268355f576SJeff Roberson 			LIST_REMOVE(bucket, ub_link);
8278355f576SJeff Roberson 			ZONE_UNLOCK(zone);
8288355f576SJeff Roberson 			bucket_drain(zone, bucket);
8296fd34d6fSJeff Roberson 			bucket_free(zone, bucket, NULL);
8308355f576SJeff Roberson 			ZONE_LOCK(zone);
8318355f576SJeff Roberson 		}
832ab3185d1SJeff Roberson 	}
833ace66b56SAlexander Motin 
834ace66b56SAlexander Motin 	/*
835ace66b56SAlexander Motin 	 * Shrink further bucket sizes.  Price of single zone lock collision
836ace66b56SAlexander Motin 	 * is probably lower then price of global cache drain.
837ace66b56SAlexander Motin 	 */
838ace66b56SAlexander Motin 	if (zone->uz_count > zone->uz_count_min)
839ace66b56SAlexander Motin 		zone->uz_count--;
8408355f576SJeff Roberson }
841fc03d22bSJeff Roberson 
842fc03d22bSJeff Roberson static void
843fc03d22bSJeff Roberson keg_free_slab(uma_keg_t keg, uma_slab_t slab, int start)
844fc03d22bSJeff Roberson {
845fc03d22bSJeff Roberson 	uint8_t *mem;
846fc03d22bSJeff Roberson 	int i;
847fc03d22bSJeff Roberson 	uint8_t flags;
848fc03d22bSJeff Roberson 
8491431a748SGleb Smirnoff 	CTR4(KTR_UMA, "keg_free_slab keg %s(%p) slab %p, returning %d bytes",
8501431a748SGleb Smirnoff 	    keg->uk_name, keg, slab, PAGE_SIZE * keg->uk_ppera);
8511431a748SGleb Smirnoff 
852fc03d22bSJeff Roberson 	mem = slab->us_data;
853fc03d22bSJeff Roberson 	flags = slab->us_flags;
854fc03d22bSJeff Roberson 	i = start;
855fc03d22bSJeff Roberson 	if (keg->uk_fini != NULL) {
856fc03d22bSJeff Roberson 		for (i--; i > -1; i--)
857fc03d22bSJeff Roberson 			keg->uk_fini(slab->us_data + (keg->uk_rsize * i),
858fc03d22bSJeff Roberson 			    keg->uk_size);
859fc03d22bSJeff Roberson 	}
860fc03d22bSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_OFFPAGE)
861fc03d22bSJeff Roberson 		zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE);
862fc03d22bSJeff Roberson 	keg->uk_freef(mem, PAGE_SIZE * keg->uk_ppera, flags);
8632e47807cSJeff Roberson 	uma_total_dec(PAGE_SIZE * keg->uk_ppera);
8648355f576SJeff Roberson }
8658355f576SJeff Roberson 
8668355f576SJeff Roberson /*
867e20a199fSJeff Roberson  * Frees pages from a keg back to the system.  This is done on demand from
8688355f576SJeff Roberson  * the pageout daemon.
8698355f576SJeff Roberson  *
870e20a199fSJeff Roberson  * Returns nothing.
8718355f576SJeff Roberson  */
872e20a199fSJeff Roberson static void
873e20a199fSJeff Roberson keg_drain(uma_keg_t keg)
8748355f576SJeff Roberson {
8751e183df2SStefan Farfeleder 	struct slabhead freeslabs = { 0 };
876ab3185d1SJeff Roberson 	uma_domain_t dom;
877829be516SMark Johnston 	uma_slab_t slab, tmp;
878ab3185d1SJeff Roberson 	int i;
8798355f576SJeff Roberson 
8808355f576SJeff Roberson 	/*
881e20a199fSJeff Roberson 	 * We don't want to take pages from statically allocated kegs at this
8828355f576SJeff Roberson 	 * time
8838355f576SJeff Roberson 	 */
884099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL)
8858355f576SJeff Roberson 		return;
8868355f576SJeff Roberson 
8871431a748SGleb Smirnoff 	CTR3(KTR_UMA, "keg_drain %s(%p) free items: %u",
8881431a748SGleb Smirnoff 	    keg->uk_name, keg, keg->uk_free);
889e20a199fSJeff Roberson 	KEG_LOCK(keg);
890099a0e58SBosko Milekic 	if (keg->uk_free == 0)
8918355f576SJeff Roberson 		goto finished;
8928355f576SJeff Roberson 
893ab3185d1SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
894ab3185d1SJeff Roberson 		dom = &keg->uk_domain[i];
895ab3185d1SJeff Roberson 		LIST_FOREACH_SAFE(slab, &dom->ud_free_slab, us_link, tmp) {
896829be516SMark Johnston 			/* We have nowhere to free these to. */
897829be516SMark Johnston 			if (slab->us_flags & UMA_SLAB_BOOT)
8988355f576SJeff Roberson 				continue;
8998355f576SJeff Roberson 
9008355f576SJeff Roberson 			LIST_REMOVE(slab, us_link);
901099a0e58SBosko Milekic 			keg->uk_pages -= keg->uk_ppera;
902099a0e58SBosko Milekic 			keg->uk_free -= keg->uk_ipers;
903713deb36SJeff Roberson 
904099a0e58SBosko Milekic 			if (keg->uk_flags & UMA_ZONE_HASH)
905ab3185d1SJeff Roberson 				UMA_HASH_REMOVE(&keg->uk_hash, slab,
906ab3185d1SJeff Roberson 				    slab->us_data);
907713deb36SJeff Roberson 
908713deb36SJeff Roberson 			SLIST_INSERT_HEAD(&freeslabs, slab, us_hlink);
909713deb36SJeff Roberson 		}
910ab3185d1SJeff Roberson 	}
911ab3185d1SJeff Roberson 
912713deb36SJeff Roberson finished:
913e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
914713deb36SJeff Roberson 
915713deb36SJeff Roberson 	while ((slab = SLIST_FIRST(&freeslabs)) != NULL) {
916713deb36SJeff Roberson 		SLIST_REMOVE(&freeslabs, slab, uma_slab, us_hlink);
9171645995bSKirk McKusick 		keg_free_slab(keg, slab, keg->uk_ipers);
9188355f576SJeff Roberson 	}
9198355f576SJeff Roberson }
9208355f576SJeff Roberson 
921e20a199fSJeff Roberson static void
922e20a199fSJeff Roberson zone_drain_wait(uma_zone_t zone, int waitok)
923e20a199fSJeff Roberson {
924e20a199fSJeff Roberson 
9258355f576SJeff Roberson 	/*
926e20a199fSJeff Roberson 	 * Set draining to interlock with zone_dtor() so we can release our
927e20a199fSJeff Roberson 	 * locks as we go.  Only dtor() should do a WAITOK call since it
928e20a199fSJeff Roberson 	 * is the only call that knows the structure will still be available
929e20a199fSJeff Roberson 	 * when it wakes up.
930e20a199fSJeff Roberson 	 */
931e20a199fSJeff Roberson 	ZONE_LOCK(zone);
932e20a199fSJeff Roberson 	while (zone->uz_flags & UMA_ZFLAG_DRAINING) {
933e20a199fSJeff Roberson 		if (waitok == M_NOWAIT)
934e20a199fSJeff Roberson 			goto out;
935af526374SJeff Roberson 		msleep(zone, zone->uz_lockptr, PVM, "zonedrain", 1);
936e20a199fSJeff Roberson 	}
937e20a199fSJeff Roberson 	zone->uz_flags |= UMA_ZFLAG_DRAINING;
938e20a199fSJeff Roberson 	bucket_cache_drain(zone);
939e20a199fSJeff Roberson 	ZONE_UNLOCK(zone);
940e20a199fSJeff Roberson 	/*
941e20a199fSJeff Roberson 	 * The DRAINING flag protects us from being freed while
942111fbcd5SBryan Venteicher 	 * we're running.  Normally the uma_rwlock would protect us but we
943e20a199fSJeff Roberson 	 * must be able to release and acquire the right lock for each keg.
944e20a199fSJeff Roberson 	 */
945e20a199fSJeff Roberson 	zone_foreach_keg(zone, &keg_drain);
946e20a199fSJeff Roberson 	ZONE_LOCK(zone);
947e20a199fSJeff Roberson 	zone->uz_flags &= ~UMA_ZFLAG_DRAINING;
948e20a199fSJeff Roberson 	wakeup(zone);
949e20a199fSJeff Roberson out:
950e20a199fSJeff Roberson 	ZONE_UNLOCK(zone);
951e20a199fSJeff Roberson }
952e20a199fSJeff Roberson 
953e20a199fSJeff Roberson void
954e20a199fSJeff Roberson zone_drain(uma_zone_t zone)
955e20a199fSJeff Roberson {
956e20a199fSJeff Roberson 
957e20a199fSJeff Roberson 	zone_drain_wait(zone, M_NOWAIT);
958e20a199fSJeff Roberson }
959e20a199fSJeff Roberson 
960e20a199fSJeff Roberson /*
961e20a199fSJeff Roberson  * Allocate a new slab for a keg.  This does not insert the slab onto a list.
9628355f576SJeff Roberson  *
9638355f576SJeff Roberson  * Arguments:
9648355f576SJeff Roberson  *	wait  Shall we wait?
9658355f576SJeff Roberson  *
9668355f576SJeff Roberson  * Returns:
9678355f576SJeff Roberson  *	The slab that was allocated or NULL if there is no memory and the
9688355f576SJeff Roberson  *	caller specified M_NOWAIT.
9698355f576SJeff Roberson  */
9708355f576SJeff Roberson static uma_slab_t
971ab3185d1SJeff Roberson keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int domain, int wait)
9728355f576SJeff Roberson {
973e20a199fSJeff Roberson 	uma_alloc allocf;
974099a0e58SBosko Milekic 	uma_slab_t slab;
9752e47807cSJeff Roberson 	unsigned long size;
97685dcf349SGleb Smirnoff 	uint8_t *mem;
97785dcf349SGleb Smirnoff 	uint8_t flags;
9788355f576SJeff Roberson 	int i;
9798355f576SJeff Roberson 
980ab3185d1SJeff Roberson 	KASSERT(domain >= 0 && domain < vm_ndomains,
981ab3185d1SJeff Roberson 	    ("keg_alloc_slab: domain %d out of range", domain));
982e20a199fSJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
983a553d4b8SJeff Roberson 	slab = NULL;
984fc03d22bSJeff Roberson 	mem = NULL;
985a553d4b8SJeff Roberson 
986e20a199fSJeff Roberson 	allocf = keg->uk_allocf;
987e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
9882e47807cSJeff Roberson 	size = keg->uk_ppera * PAGE_SIZE;
989a553d4b8SJeff Roberson 
990099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_OFFPAGE) {
991ab3185d1SJeff Roberson 		slab = zone_alloc_item(keg->uk_slabzone, NULL, domain, wait);
992fc03d22bSJeff Roberson 		if (slab == NULL)
993fc03d22bSJeff Roberson 			goto out;
994a553d4b8SJeff Roberson 	}
995a553d4b8SJeff Roberson 
9963370c5bfSJeff Roberson 	/*
9973370c5bfSJeff Roberson 	 * This reproduces the old vm_zone behavior of zero filling pages the
9983370c5bfSJeff Roberson 	 * first time they are added to a zone.
9993370c5bfSJeff Roberson 	 *
10003370c5bfSJeff Roberson 	 * Malloced items are zeroed in uma_zalloc.
10013370c5bfSJeff Roberson 	 */
10023370c5bfSJeff Roberson 
1003099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0)
10043370c5bfSJeff Roberson 		wait |= M_ZERO;
10053370c5bfSJeff Roberson 	else
10063370c5bfSJeff Roberson 		wait &= ~M_ZERO;
10073370c5bfSJeff Roberson 
1008263811f7SKip Macy 	if (keg->uk_flags & UMA_ZONE_NODUMP)
1009263811f7SKip Macy 		wait |= M_NODUMP;
1010263811f7SKip Macy 
1011e20a199fSJeff Roberson 	/* zone is passed for legacy reasons. */
1012ab3185d1SJeff Roberson 	mem = allocf(zone, size, domain, &flags, wait);
1013a553d4b8SJeff Roberson 	if (mem == NULL) {
1014b23f72e9SBrian Feldman 		if (keg->uk_flags & UMA_ZONE_OFFPAGE)
10150095a784SJeff Roberson 			zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE);
1016fc03d22bSJeff Roberson 		slab = NULL;
1017fc03d22bSJeff Roberson 		goto out;
1018a553d4b8SJeff Roberson 	}
10192e47807cSJeff Roberson 	uma_total_inc(size);
10208355f576SJeff Roberson 
10215c0e403bSJeff Roberson 	/* Point the slab into the allocated memory */
1022099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_OFFPAGE))
1023099a0e58SBosko Milekic 		slab = (uma_slab_t )(mem + keg->uk_pgoff);
10245c0e403bSJeff Roberson 
1025e20a199fSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_VTOSLAB)
1026099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ppera; i++)
102799571dc3SJeff Roberson 			vsetslab((vm_offset_t)mem + (i * PAGE_SIZE), slab);
10288355f576SJeff Roberson 
1029099a0e58SBosko Milekic 	slab->us_keg = keg;
10308355f576SJeff Roberson 	slab->us_data = mem;
1031099a0e58SBosko Milekic 	slab->us_freecount = keg->uk_ipers;
10328355f576SJeff Roberson 	slab->us_flags = flags;
1033ab3185d1SJeff Roberson 	slab->us_domain = domain;
1034ef72505eSJeff Roberson 	BIT_FILL(SLAB_SETSIZE, &slab->us_free);
1035ef72505eSJeff Roberson #ifdef INVARIANTS
1036ef72505eSJeff Roberson 	BIT_ZERO(SLAB_SETSIZE, &slab->us_debugfree);
1037ef72505eSJeff Roberson #endif
1038099a0e58SBosko Milekic 
1039b23f72e9SBrian Feldman 	if (keg->uk_init != NULL) {
1040099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ipers; i++)
1041b23f72e9SBrian Feldman 			if (keg->uk_init(slab->us_data + (keg->uk_rsize * i),
1042b23f72e9SBrian Feldman 			    keg->uk_size, wait) != 0)
1043b23f72e9SBrian Feldman 				break;
1044b23f72e9SBrian Feldman 		if (i != keg->uk_ipers) {
1045fc03d22bSJeff Roberson 			keg_free_slab(keg, slab, i);
1046fc03d22bSJeff Roberson 			slab = NULL;
1047fc03d22bSJeff Roberson 			goto out;
1048b23f72e9SBrian Feldman 		}
1049b23f72e9SBrian Feldman 	}
1050fc03d22bSJeff Roberson out:
1051e20a199fSJeff Roberson 	KEG_LOCK(keg);
10525c0e403bSJeff Roberson 
10531431a748SGleb Smirnoff 	CTR3(KTR_UMA, "keg_alloc_slab: allocated slab %p for %s(%p)",
10541431a748SGleb Smirnoff 	    slab, keg->uk_name, keg);
10551431a748SGleb Smirnoff 
1056fc03d22bSJeff Roberson 	if (slab != NULL) {
1057099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZONE_HASH)
1058099a0e58SBosko Milekic 			UMA_HASH_INSERT(&keg->uk_hash, slab, mem);
10598355f576SJeff Roberson 
1060099a0e58SBosko Milekic 		keg->uk_pages += keg->uk_ppera;
1061099a0e58SBosko Milekic 		keg->uk_free += keg->uk_ipers;
1062fc03d22bSJeff Roberson 	}
10638355f576SJeff Roberson 
10648355f576SJeff Roberson 	return (slab);
10658355f576SJeff Roberson }
10668355f576SJeff Roberson 
10678355f576SJeff Roberson /*
1068009b6fcbSJeff Roberson  * This function is intended to be used early on in place of page_alloc() so
1069009b6fcbSJeff Roberson  * that we may use the boot time page cache to satisfy allocations before
1070009b6fcbSJeff Roberson  * the VM is ready.
1071009b6fcbSJeff Roberson  */
1072009b6fcbSJeff Roberson static void *
1073ab3185d1SJeff Roberson startup_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *pflag,
1074ab3185d1SJeff Roberson     int wait)
1075009b6fcbSJeff Roberson {
1076099a0e58SBosko Milekic 	uma_keg_t keg;
1077ac0a6fd0SGleb Smirnoff 	void *mem;
1078ac0a6fd0SGleb Smirnoff 	int pages;
1079099a0e58SBosko Milekic 
1080e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
1081099a0e58SBosko Milekic 
1082009b6fcbSJeff Roberson 	/*
1083f7d35785SGleb Smirnoff 	 * If we are in BOOT_BUCKETS or higher, than switch to real
1084f7d35785SGleb Smirnoff 	 * allocator.  Zones with page sized slabs switch at BOOT_PAGEALLOC.
1085009b6fcbSJeff Roberson 	 */
1086f7d35785SGleb Smirnoff 	switch (booted) {
1087f7d35785SGleb Smirnoff 		case BOOT_COLD:
1088f7d35785SGleb Smirnoff 		case BOOT_STRAPPED:
1089f7d35785SGleb Smirnoff 			break;
1090f7d35785SGleb Smirnoff 		case BOOT_PAGEALLOC:
1091f7d35785SGleb Smirnoff 			if (keg->uk_ppera > 1)
1092f7d35785SGleb Smirnoff 				break;
1093f7d35785SGleb Smirnoff 		case BOOT_BUCKETS:
1094f7d35785SGleb Smirnoff 		case BOOT_RUNNING:
1095009b6fcbSJeff Roberson #ifdef UMA_MD_SMALL_ALLOC
1096f7d35785SGleb Smirnoff 			keg->uk_allocf = (keg->uk_ppera > 1) ?
1097f7d35785SGleb Smirnoff 			    page_alloc : uma_small_alloc;
1098009b6fcbSJeff Roberson #else
1099099a0e58SBosko Milekic 			keg->uk_allocf = page_alloc;
1100009b6fcbSJeff Roberson #endif
1101ab3185d1SJeff Roberson 			return keg->uk_allocf(zone, bytes, domain, pflag, wait);
1102009b6fcbSJeff Roberson 	}
1103009b6fcbSJeff Roberson 
1104009b6fcbSJeff Roberson 	/*
1105f7d35785SGleb Smirnoff 	 * Check our small startup cache to see if it has pages remaining.
1106f7d35785SGleb Smirnoff 	 */
1107f7d35785SGleb Smirnoff 	pages = howmany(bytes, PAGE_SIZE);
1108f7d35785SGleb Smirnoff 	KASSERT(pages > 0, ("%s can't reserve 0 pages", __func__));
1109f7d35785SGleb Smirnoff 	if (pages > boot_pages)
1110f7d35785SGleb Smirnoff 		panic("UMA zone \"%s\": Increase vm.boot_pages", zone->uz_name);
1111f7d35785SGleb Smirnoff #ifdef DIAGNOSTIC
1112f7d35785SGleb Smirnoff 	printf("%s from \"%s\", %d boot pages left\n", __func__, zone->uz_name,
1113f7d35785SGleb Smirnoff 	    boot_pages);
1114f7d35785SGleb Smirnoff #endif
1115f7d35785SGleb Smirnoff 	mem = bootmem;
1116f7d35785SGleb Smirnoff 	boot_pages -= pages;
1117f7d35785SGleb Smirnoff 	bootmem += pages * PAGE_SIZE;
1118f7d35785SGleb Smirnoff 	*pflag = UMA_SLAB_BOOT;
1119f7d35785SGleb Smirnoff 
1120f7d35785SGleb Smirnoff 	return (mem);
1121f7d35785SGleb Smirnoff }
1122f7d35785SGleb Smirnoff 
1123f7d35785SGleb Smirnoff /*
11248355f576SJeff Roberson  * Allocates a number of pages from the system
11258355f576SJeff Roberson  *
11268355f576SJeff Roberson  * Arguments:
11278355f576SJeff Roberson  *	bytes  The number of bytes requested
11288355f576SJeff Roberson  *	wait  Shall we wait?
11298355f576SJeff Roberson  *
11308355f576SJeff Roberson  * Returns:
11318355f576SJeff Roberson  *	A pointer to the alloced memory or possibly
11328355f576SJeff Roberson  *	NULL if M_NOWAIT is set.
11338355f576SJeff Roberson  */
11348355f576SJeff Roberson static void *
1135ab3185d1SJeff Roberson page_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *pflag,
1136ab3185d1SJeff Roberson     int wait)
11378355f576SJeff Roberson {
11388355f576SJeff Roberson 	void *p;	/* Returned page */
11398355f576SJeff Roberson 
11402e47807cSJeff Roberson 	*pflag = UMA_SLAB_KERNEL;
1141ab3185d1SJeff Roberson 	p = (void *) kmem_malloc_domain(domain, bytes, wait);
11428355f576SJeff Roberson 
11438355f576SJeff Roberson 	return (p);
11448355f576SJeff Roberson }
11458355f576SJeff Roberson 
11468355f576SJeff Roberson /*
11478355f576SJeff Roberson  * Allocates a number of pages from within an object
11488355f576SJeff Roberson  *
11498355f576SJeff Roberson  * Arguments:
11508355f576SJeff Roberson  *	bytes  The number of bytes requested
11518355f576SJeff Roberson  *	wait   Shall we wait?
11528355f576SJeff Roberson  *
11538355f576SJeff Roberson  * Returns:
11548355f576SJeff Roberson  *	A pointer to the alloced memory or possibly
11558355f576SJeff Roberson  *	NULL if M_NOWAIT is set.
11568355f576SJeff Roberson  */
11578355f576SJeff Roberson static void *
1158ab3185d1SJeff Roberson noobj_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *flags,
1159ab3185d1SJeff Roberson     int wait)
11608355f576SJeff Roberson {
1161a4915c21SAttilio Rao 	TAILQ_HEAD(, vm_page) alloctail;
1162a4915c21SAttilio Rao 	u_long npages;
1163b245ac95SAlan Cox 	vm_offset_t retkva, zkva;
1164a4915c21SAttilio Rao 	vm_page_t p, p_next;
1165e20a199fSJeff Roberson 	uma_keg_t keg;
11668355f576SJeff Roberson 
1167a4915c21SAttilio Rao 	TAILQ_INIT(&alloctail);
1168e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
1169a4915c21SAttilio Rao 
1170a4915c21SAttilio Rao 	npages = howmany(bytes, PAGE_SIZE);
1171a4915c21SAttilio Rao 	while (npages > 0) {
1172ab3185d1SJeff Roberson 		p = vm_page_alloc_domain(NULL, 0, domain, VM_ALLOC_INTERRUPT |
11738d6fbbb8SJeff Roberson 		    VM_ALLOC_WIRED | VM_ALLOC_NOOBJ |
1174772c8b67SKonstantin Belousov 		    ((wait & M_WAITOK) != 0 ? VM_ALLOC_WAITOK :
1175772c8b67SKonstantin Belousov 		    VM_ALLOC_NOWAIT));
1176a4915c21SAttilio Rao 		if (p != NULL) {
1177a4915c21SAttilio Rao 			/*
1178a4915c21SAttilio Rao 			 * Since the page does not belong to an object, its
1179a4915c21SAttilio Rao 			 * listq is unused.
1180a4915c21SAttilio Rao 			 */
1181a4915c21SAttilio Rao 			TAILQ_INSERT_TAIL(&alloctail, p, listq);
1182a4915c21SAttilio Rao 			npages--;
1183a4915c21SAttilio Rao 			continue;
1184a4915c21SAttilio Rao 		}
11858355f576SJeff Roberson 		/*
1186a4915c21SAttilio Rao 		 * Page allocation failed, free intermediate pages and
1187a4915c21SAttilio Rao 		 * exit.
11888355f576SJeff Roberson 		 */
1189a4915c21SAttilio Rao 		TAILQ_FOREACH_SAFE(p, &alloctail, listq, p_next) {
1190087a6132SAlan Cox 			vm_page_unwire(p, PQ_NONE);
1191b245ac95SAlan Cox 			vm_page_free(p);
1192b245ac95SAlan Cox 		}
1193a4915c21SAttilio Rao 		return (NULL);
1194b245ac95SAlan Cox 	}
11958355f576SJeff Roberson 	*flags = UMA_SLAB_PRIV;
1196a4915c21SAttilio Rao 	zkva = keg->uk_kva +
1197a4915c21SAttilio Rao 	    atomic_fetchadd_long(&keg->uk_offset, round_page(bytes));
1198a4915c21SAttilio Rao 	retkva = zkva;
1199a4915c21SAttilio Rao 	TAILQ_FOREACH(p, &alloctail, listq) {
1200a4915c21SAttilio Rao 		pmap_qenter(zkva, &p, 1);
1201a4915c21SAttilio Rao 		zkva += PAGE_SIZE;
1202a4915c21SAttilio Rao 	}
12038355f576SJeff Roberson 
12048355f576SJeff Roberson 	return ((void *)retkva);
12058355f576SJeff Roberson }
12068355f576SJeff Roberson 
12078355f576SJeff Roberson /*
12088355f576SJeff Roberson  * Frees a number of pages to the system
12098355f576SJeff Roberson  *
12108355f576SJeff Roberson  * Arguments:
12118355f576SJeff Roberson  *	mem   A pointer to the memory to be freed
12128355f576SJeff Roberson  *	size  The size of the memory being freed
12138355f576SJeff Roberson  *	flags The original p->us_flags field
12148355f576SJeff Roberson  *
12158355f576SJeff Roberson  * Returns:
12168355f576SJeff Roberson  *	Nothing
12178355f576SJeff Roberson  */
12188355f576SJeff Roberson static void
1219f2c2231eSRyan Stone page_free(void *mem, vm_size_t size, uint8_t flags)
12208355f576SJeff Roberson {
12215df87b21SJeff Roberson 	struct vmem *vmem;
12223370c5bfSJeff Roberson 
12232e47807cSJeff Roberson 	if (flags & UMA_SLAB_KERNEL)
12245df87b21SJeff Roberson 		vmem = kernel_arena;
12258355f576SJeff Roberson 	else
1226b5345ef1SJustin Hibbits 		panic("UMA: page_free used with invalid flags %x", flags);
12278355f576SJeff Roberson 
12285df87b21SJeff Roberson 	kmem_free(vmem, (vm_offset_t)mem, size);
12298355f576SJeff Roberson }
12308355f576SJeff Roberson 
12318355f576SJeff Roberson /*
12328355f576SJeff Roberson  * Zero fill initializer
12338355f576SJeff Roberson  *
12348355f576SJeff Roberson  * Arguments/Returns follow uma_init specifications
12358355f576SJeff Roberson  */
1236b23f72e9SBrian Feldman static int
1237b23f72e9SBrian Feldman zero_init(void *mem, int size, int flags)
12388355f576SJeff Roberson {
12398355f576SJeff Roberson 	bzero(mem, size);
1240b23f72e9SBrian Feldman 	return (0);
12418355f576SJeff Roberson }
12428355f576SJeff Roberson 
12438355f576SJeff Roberson /*
1244e20a199fSJeff Roberson  * Finish creating a small uma keg.  This calculates ipers, and the keg size.
12458355f576SJeff Roberson  *
12468355f576SJeff Roberson  * Arguments
1247e20a199fSJeff Roberson  *	keg  The zone we should initialize
12488355f576SJeff Roberson  *
12498355f576SJeff Roberson  * Returns
12508355f576SJeff Roberson  *	Nothing
12518355f576SJeff Roberson  */
12528355f576SJeff Roberson static void
1253e20a199fSJeff Roberson keg_small_init(uma_keg_t keg)
12548355f576SJeff Roberson {
1255244f4554SBosko Milekic 	u_int rsize;
1256244f4554SBosko Milekic 	u_int memused;
1257244f4554SBosko Milekic 	u_int wastedspace;
1258244f4554SBosko Milekic 	u_int shsize;
1259a55ebb7cSAndriy Gapon 	u_int slabsize;
12608355f576SJeff Roberson 
1261ad97af7eSGleb Smirnoff 	if (keg->uk_flags & UMA_ZONE_PCPU) {
126296c85efbSNathan Whitehorn 		u_int ncpus = (mp_maxid + 1) ? (mp_maxid + 1) : MAXCPU;
1263e28a647dSGleb Smirnoff 
1264a55ebb7cSAndriy Gapon 		slabsize = sizeof(struct pcpu);
1265e28a647dSGleb Smirnoff 		keg->uk_ppera = howmany(ncpus * sizeof(struct pcpu),
1266ad97af7eSGleb Smirnoff 		    PAGE_SIZE);
1267ad97af7eSGleb Smirnoff 	} else {
1268a55ebb7cSAndriy Gapon 		slabsize = UMA_SLAB_SIZE;
1269ad97af7eSGleb Smirnoff 		keg->uk_ppera = 1;
1270ad97af7eSGleb Smirnoff 	}
1271ad97af7eSGleb Smirnoff 
1272ef72505eSJeff Roberson 	/*
1273ef72505eSJeff Roberson 	 * Calculate the size of each allocation (rsize) according to
1274ef72505eSJeff Roberson 	 * alignment.  If the requested size is smaller than we have
1275ef72505eSJeff Roberson 	 * allocation bits for we round it up.
1276ef72505eSJeff Roberson 	 */
1277099a0e58SBosko Milekic 	rsize = keg->uk_size;
1278a55ebb7cSAndriy Gapon 	if (rsize < slabsize / SLAB_SETSIZE)
1279a55ebb7cSAndriy Gapon 		rsize = slabsize / SLAB_SETSIZE;
1280099a0e58SBosko Milekic 	if (rsize & keg->uk_align)
1281099a0e58SBosko Milekic 		rsize = (rsize & ~keg->uk_align) + (keg->uk_align + 1);
1282099a0e58SBosko Milekic 	keg->uk_rsize = rsize;
1283ad97af7eSGleb Smirnoff 
1284ad97af7eSGleb Smirnoff 	KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0 ||
1285ad97af7eSGleb Smirnoff 	    keg->uk_rsize < sizeof(struct pcpu),
1286ad97af7eSGleb Smirnoff 	    ("%s: size %u too large", __func__, keg->uk_rsize));
12878355f576SJeff Roberson 
1288ef72505eSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_OFFPAGE)
12892864dbbfSGleb Smirnoff 		shsize = 0;
1290ef72505eSJeff Roberson 	else
1291244f4554SBosko Milekic 		shsize = sizeof(struct uma_slab);
12928355f576SJeff Roberson 
1293a55ebb7cSAndriy Gapon 	keg->uk_ipers = (slabsize - shsize) / rsize;
1294ef72505eSJeff Roberson 	KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE,
1295ad97af7eSGleb Smirnoff 	    ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers));
1296ad97af7eSGleb Smirnoff 
1297244f4554SBosko Milekic 	memused = keg->uk_ipers * rsize + shsize;
1298a55ebb7cSAndriy Gapon 	wastedspace = slabsize - memused;
1299244f4554SBosko Milekic 
130020e8e865SBosko Milekic 	/*
1301244f4554SBosko Milekic 	 * We can't do OFFPAGE if we're internal or if we've been
130220e8e865SBosko Milekic 	 * asked to not go to the VM for buckets.  If we do this we
13036fd34d6fSJeff Roberson 	 * may end up going to the VM  for slabs which we do not
13046fd34d6fSJeff Roberson 	 * want to do if we're UMA_ZFLAG_CACHEONLY as a result
13056fd34d6fSJeff Roberson 	 * of UMA_ZONE_VM, which clearly forbids it.
130620e8e865SBosko Milekic 	 */
1307099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) ||
1308099a0e58SBosko Milekic 	    (keg->uk_flags & UMA_ZFLAG_CACHEONLY))
13098355f576SJeff Roberson 		return;
1310244f4554SBosko Milekic 
1311ef72505eSJeff Roberson 	/*
1312ef72505eSJeff Roberson 	 * See if using an OFFPAGE slab will limit our waste.  Only do
1313ef72505eSJeff Roberson 	 * this if it permits more items per-slab.
1314ef72505eSJeff Roberson 	 *
1315ef72505eSJeff Roberson 	 * XXX We could try growing slabsize to limit max waste as well.
1316ef72505eSJeff Roberson 	 * Historically this was not done because the VM could not
1317ef72505eSJeff Roberson 	 * efficiently handle contiguous allocations.
1318ef72505eSJeff Roberson 	 */
1319a55ebb7cSAndriy Gapon 	if ((wastedspace >= slabsize / UMA_MAX_WASTE) &&
1320a55ebb7cSAndriy Gapon 	    (keg->uk_ipers < (slabsize / keg->uk_rsize))) {
1321a55ebb7cSAndriy Gapon 		keg->uk_ipers = slabsize / keg->uk_rsize;
1322ef72505eSJeff Roberson 		KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE,
1323ad97af7eSGleb Smirnoff 		    ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers));
13241431a748SGleb Smirnoff 		CTR6(KTR_UMA, "UMA decided we need offpage slab headers for "
13251431a748SGleb Smirnoff 		    "keg: %s(%p), calculated wastedspace = %d, "
1326244f4554SBosko Milekic 		    "maximum wasted space allowed = %d, "
1327244f4554SBosko Milekic 		    "calculated ipers = %d, "
13281431a748SGleb Smirnoff 		    "new wasted space = %d\n", keg->uk_name, keg, wastedspace,
1329a55ebb7cSAndriy Gapon 		    slabsize / UMA_MAX_WASTE, keg->uk_ipers,
1330a55ebb7cSAndriy Gapon 		    slabsize - keg->uk_ipers * keg->uk_rsize);
1331099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZONE_OFFPAGE;
13328355f576SJeff Roberson 	}
1333ad97af7eSGleb Smirnoff 
1334ad97af7eSGleb Smirnoff 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) &&
1335ad97af7eSGleb Smirnoff 	    (keg->uk_flags & UMA_ZONE_VTOSLAB) == 0)
1336ad97af7eSGleb Smirnoff 		keg->uk_flags |= UMA_ZONE_HASH;
13378355f576SJeff Roberson }
13388355f576SJeff Roberson 
13398355f576SJeff Roberson /*
1340e20a199fSJeff Roberson  * Finish creating a large (> UMA_SLAB_SIZE) uma kegs.  Just give in and do
13418355f576SJeff Roberson  * OFFPAGE for now.  When I can allow for more dynamic slab sizes this will be
13428355f576SJeff Roberson  * more complicated.
13438355f576SJeff Roberson  *
13448355f576SJeff Roberson  * Arguments
1345e20a199fSJeff Roberson  *	keg  The keg we should initialize
13468355f576SJeff Roberson  *
13478355f576SJeff Roberson  * Returns
13488355f576SJeff Roberson  *	Nothing
13498355f576SJeff Roberson  */
13508355f576SJeff Roberson static void
1351e20a199fSJeff Roberson keg_large_init(uma_keg_t keg)
13528355f576SJeff Roberson {
1353cec48e00SAlexander Motin 	u_int shsize;
13548355f576SJeff Roberson 
1355e20a199fSJeff Roberson 	KASSERT(keg != NULL, ("Keg is null in keg_large_init"));
1356099a0e58SBosko Milekic 	KASSERT((keg->uk_flags & UMA_ZFLAG_CACHEONLY) == 0,
1357e20a199fSJeff Roberson 	    ("keg_large_init: Cannot large-init a UMA_ZFLAG_CACHEONLY keg"));
1358ad97af7eSGleb Smirnoff 	KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0,
1359ad97af7eSGleb Smirnoff 	    ("%s: Cannot large-init a UMA_ZONE_PCPU keg", __func__));
136020e8e865SBosko Milekic 
1361ad97af7eSGleb Smirnoff 	keg->uk_ppera = howmany(keg->uk_size, PAGE_SIZE);
1362099a0e58SBosko Milekic 	keg->uk_ipers = 1;
1363e9a069d8SJohn Baldwin 	keg->uk_rsize = keg->uk_size;
1364e9a069d8SJohn Baldwin 
1365cec48e00SAlexander Motin 	/* Check whether we have enough space to not do OFFPAGE. */
1366cec48e00SAlexander Motin 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) == 0) {
1367cec48e00SAlexander Motin 		shsize = sizeof(struct uma_slab);
1368cec48e00SAlexander Motin 		if (shsize & UMA_ALIGN_PTR)
1369cec48e00SAlexander Motin 			shsize = (shsize & ~UMA_ALIGN_PTR) +
1370cec48e00SAlexander Motin 			    (UMA_ALIGN_PTR + 1);
1371cec48e00SAlexander Motin 
13722934eb8aSMark Johnston 		if (PAGE_SIZE * keg->uk_ppera - keg->uk_rsize < shsize) {
13732934eb8aSMark Johnston 			/*
13742934eb8aSMark Johnston 			 * We can't do OFFPAGE if we're internal, in which case
13752934eb8aSMark Johnston 			 * we need an extra page per allocation to contain the
13762934eb8aSMark Johnston 			 * slab header.
13772934eb8aSMark Johnston 			 */
13782934eb8aSMark Johnston 			if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) == 0)
1379099a0e58SBosko Milekic 				keg->uk_flags |= UMA_ZONE_OFFPAGE;
13802934eb8aSMark Johnston 			else
13812934eb8aSMark Johnston 				keg->uk_ppera++;
13822934eb8aSMark Johnston 		}
1383cec48e00SAlexander Motin 	}
1384cec48e00SAlexander Motin 
1385cec48e00SAlexander Motin 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) &&
1386cec48e00SAlexander Motin 	    (keg->uk_flags & UMA_ZONE_VTOSLAB) == 0)
1387099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZONE_HASH;
13888355f576SJeff Roberson }
13898355f576SJeff Roberson 
1390e20a199fSJeff Roberson static void
1391e20a199fSJeff Roberson keg_cachespread_init(uma_keg_t keg)
1392e20a199fSJeff Roberson {
1393e20a199fSJeff Roberson 	int alignsize;
1394e20a199fSJeff Roberson 	int trailer;
1395e20a199fSJeff Roberson 	int pages;
1396e20a199fSJeff Roberson 	int rsize;
1397e20a199fSJeff Roberson 
1398ad97af7eSGleb Smirnoff 	KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0,
1399ad97af7eSGleb Smirnoff 	    ("%s: Cannot cachespread-init a UMA_ZONE_PCPU keg", __func__));
1400ad97af7eSGleb Smirnoff 
1401e20a199fSJeff Roberson 	alignsize = keg->uk_align + 1;
1402e20a199fSJeff Roberson 	rsize = keg->uk_size;
1403e20a199fSJeff Roberson 	/*
1404e20a199fSJeff Roberson 	 * We want one item to start on every align boundary in a page.  To
1405e20a199fSJeff Roberson 	 * do this we will span pages.  We will also extend the item by the
1406e20a199fSJeff Roberson 	 * size of align if it is an even multiple of align.  Otherwise, it
1407e20a199fSJeff Roberson 	 * would fall on the same boundary every time.
1408e20a199fSJeff Roberson 	 */
1409e20a199fSJeff Roberson 	if (rsize & keg->uk_align)
1410e20a199fSJeff Roberson 		rsize = (rsize & ~keg->uk_align) + alignsize;
1411e20a199fSJeff Roberson 	if ((rsize & alignsize) == 0)
1412e20a199fSJeff Roberson 		rsize += alignsize;
1413e20a199fSJeff Roberson 	trailer = rsize - keg->uk_size;
1414e20a199fSJeff Roberson 	pages = (rsize * (PAGE_SIZE / alignsize)) / PAGE_SIZE;
1415e20a199fSJeff Roberson 	pages = MIN(pages, (128 * 1024) / PAGE_SIZE);
1416e20a199fSJeff Roberson 	keg->uk_rsize = rsize;
1417e20a199fSJeff Roberson 	keg->uk_ppera = pages;
1418e20a199fSJeff Roberson 	keg->uk_ipers = ((pages * PAGE_SIZE) + trailer) / rsize;
1419e20a199fSJeff Roberson 	keg->uk_flags |= UMA_ZONE_OFFPAGE | UMA_ZONE_VTOSLAB;
14202367b4ddSDimitry Andric 	KASSERT(keg->uk_ipers <= SLAB_SETSIZE,
142142321809SGleb Smirnoff 	    ("%s: keg->uk_ipers too high(%d) increase max_ipers", __func__,
1422e20a199fSJeff Roberson 	    keg->uk_ipers));
1423e20a199fSJeff Roberson }
1424e20a199fSJeff Roberson 
14258355f576SJeff Roberson /*
1426099a0e58SBosko Milekic  * Keg header ctor.  This initializes all fields, locks, etc.  And inserts
1427099a0e58SBosko Milekic  * the keg onto the global keg list.
14288355f576SJeff Roberson  *
14298355f576SJeff Roberson  * Arguments/Returns follow uma_ctor specifications
1430099a0e58SBosko Milekic  *	udata  Actually uma_kctor_args
1431099a0e58SBosko Milekic  */
1432b23f72e9SBrian Feldman static int
1433b23f72e9SBrian Feldman keg_ctor(void *mem, int size, void *udata, int flags)
1434099a0e58SBosko Milekic {
1435099a0e58SBosko Milekic 	struct uma_kctor_args *arg = udata;
1436099a0e58SBosko Milekic 	uma_keg_t keg = mem;
1437099a0e58SBosko Milekic 	uma_zone_t zone;
1438099a0e58SBosko Milekic 
1439099a0e58SBosko Milekic 	bzero(keg, size);
1440099a0e58SBosko Milekic 	keg->uk_size = arg->size;
1441099a0e58SBosko Milekic 	keg->uk_init = arg->uminit;
1442099a0e58SBosko Milekic 	keg->uk_fini = arg->fini;
1443099a0e58SBosko Milekic 	keg->uk_align = arg->align;
1444ab3185d1SJeff Roberson 	keg->uk_cursor = 0;
1445099a0e58SBosko Milekic 	keg->uk_free = 0;
14466fd34d6fSJeff Roberson 	keg->uk_reserve = 0;
1447099a0e58SBosko Milekic 	keg->uk_pages = 0;
1448099a0e58SBosko Milekic 	keg->uk_flags = arg->flags;
1449099a0e58SBosko Milekic 	keg->uk_slabzone = NULL;
1450099a0e58SBosko Milekic 
1451099a0e58SBosko Milekic 	/*
1452099a0e58SBosko Milekic 	 * The master zone is passed to us at keg-creation time.
1453099a0e58SBosko Milekic 	 */
1454099a0e58SBosko Milekic 	zone = arg->zone;
1455e20a199fSJeff Roberson 	keg->uk_name = zone->uz_name;
1456099a0e58SBosko Milekic 
1457099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_VM)
1458099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZFLAG_CACHEONLY;
1459099a0e58SBosko Milekic 
1460099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_ZINIT)
1461099a0e58SBosko Milekic 		keg->uk_init = zero_init;
1462099a0e58SBosko Milekic 
1463cfcae3f8SGleb Smirnoff 	if (arg->flags & UMA_ZONE_MALLOC)
1464e20a199fSJeff Roberson 		keg->uk_flags |= UMA_ZONE_VTOSLAB;
1465e20a199fSJeff Roberson 
1466ad97af7eSGleb Smirnoff 	if (arg->flags & UMA_ZONE_PCPU)
1467ad97af7eSGleb Smirnoff #ifdef SMP
1468ad97af7eSGleb Smirnoff 		keg->uk_flags |= UMA_ZONE_OFFPAGE;
1469ad97af7eSGleb Smirnoff #else
1470ad97af7eSGleb Smirnoff 		keg->uk_flags &= ~UMA_ZONE_PCPU;
1471ad97af7eSGleb Smirnoff #endif
1472ad97af7eSGleb Smirnoff 
1473ef72505eSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_CACHESPREAD) {
1474e20a199fSJeff Roberson 		keg_cachespread_init(keg);
1475244f4554SBosko Milekic 	} else {
1476ef72505eSJeff Roberson 		if (keg->uk_size > (UMA_SLAB_SIZE - sizeof(struct uma_slab)))
1477e20a199fSJeff Roberson 			keg_large_init(keg);
1478244f4554SBosko Milekic 		else
1479e20a199fSJeff Roberson 			keg_small_init(keg);
1480244f4554SBosko Milekic 	}
1481099a0e58SBosko Milekic 
1482cfcae3f8SGleb Smirnoff 	if (keg->uk_flags & UMA_ZONE_OFFPAGE)
1483099a0e58SBosko Milekic 		keg->uk_slabzone = slabzone;
1484099a0e58SBosko Milekic 
1485099a0e58SBosko Milekic 	/*
1486099a0e58SBosko Milekic 	 * If we haven't booted yet we need allocations to go through the
1487099a0e58SBosko Milekic 	 * startup cache until the vm is ready.
1488099a0e58SBosko Milekic 	 */
1489f4bef67cSGleb Smirnoff 	if (booted < BOOT_PAGEALLOC)
14908cd02d00SAlan Cox 		keg->uk_allocf = startup_alloc;
149177e19437SGleb Smirnoff #ifdef UMA_MD_SMALL_ALLOC
149277e19437SGleb Smirnoff 	else if (keg->uk_ppera == 1)
149377e19437SGleb Smirnoff 		keg->uk_allocf = uma_small_alloc;
14948cd02d00SAlan Cox #endif
149577e19437SGleb Smirnoff 	else
149677e19437SGleb Smirnoff 		keg->uk_allocf = page_alloc;
149777e19437SGleb Smirnoff #ifdef UMA_MD_SMALL_ALLOC
149877e19437SGleb Smirnoff 	if (keg->uk_ppera == 1)
149977e19437SGleb Smirnoff 		keg->uk_freef = uma_small_free;
150077e19437SGleb Smirnoff 	else
150177e19437SGleb Smirnoff #endif
150277e19437SGleb Smirnoff 		keg->uk_freef = page_free;
1503099a0e58SBosko Milekic 
1504099a0e58SBosko Milekic 	/*
1505af526374SJeff Roberson 	 * Initialize keg's lock
1506099a0e58SBosko Milekic 	 */
1507af526374SJeff Roberson 	KEG_LOCK_INIT(keg, (arg->flags & UMA_ZONE_MTXCLASS));
1508099a0e58SBosko Milekic 
1509099a0e58SBosko Milekic 	/*
1510099a0e58SBosko Milekic 	 * If we're putting the slab header in the actual page we need to
1511099a0e58SBosko Milekic 	 * figure out where in each page it goes.  This calculates a right
1512099a0e58SBosko Milekic 	 * justified offset into the memory on an ALIGN_PTR boundary.
1513099a0e58SBosko Milekic 	 */
1514099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) {
1515244f4554SBosko Milekic 		u_int totsize;
1516099a0e58SBosko Milekic 
1517099a0e58SBosko Milekic 		/* Size of the slab struct and free list */
1518ef72505eSJeff Roberson 		totsize = sizeof(struct uma_slab);
1519ef72505eSJeff Roberson 
1520099a0e58SBosko Milekic 		if (totsize & UMA_ALIGN_PTR)
1521099a0e58SBosko Milekic 			totsize = (totsize & ~UMA_ALIGN_PTR) +
1522099a0e58SBosko Milekic 			    (UMA_ALIGN_PTR + 1);
1523ad97af7eSGleb Smirnoff 		keg->uk_pgoff = (PAGE_SIZE * keg->uk_ppera) - totsize;
1524244f4554SBosko Milekic 
1525244f4554SBosko Milekic 		/*
1526244f4554SBosko Milekic 		 * The only way the following is possible is if with our
1527244f4554SBosko Milekic 		 * UMA_ALIGN_PTR adjustments we are now bigger than
1528244f4554SBosko Milekic 		 * UMA_SLAB_SIZE.  I haven't checked whether this is
1529244f4554SBosko Milekic 		 * mathematically possible for all cases, so we make
1530244f4554SBosko Milekic 		 * sure here anyway.
1531244f4554SBosko Milekic 		 */
1532ef72505eSJeff Roberson 		totsize = keg->uk_pgoff + sizeof(struct uma_slab);
1533ad97af7eSGleb Smirnoff 		if (totsize > PAGE_SIZE * keg->uk_ppera) {
1534099a0e58SBosko Milekic 			printf("zone %s ipers %d rsize %d size %d\n",
1535099a0e58SBosko Milekic 			    zone->uz_name, keg->uk_ipers, keg->uk_rsize,
1536099a0e58SBosko Milekic 			    keg->uk_size);
1537aea6e893SAlan Cox 			panic("UMA slab won't fit.");
1538099a0e58SBosko Milekic 		}
1539099a0e58SBosko Milekic 	}
1540099a0e58SBosko Milekic 
1541099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH)
1542099a0e58SBosko Milekic 		hash_alloc(&keg->uk_hash);
1543099a0e58SBosko Milekic 
15441431a748SGleb Smirnoff 	CTR5(KTR_UMA, "keg_ctor %p zone %s(%p) out %d free %d\n",
15451431a748SGleb Smirnoff 	    keg, zone->uz_name, zone,
154657223e99SAndriy Gapon 	    (keg->uk_pages / keg->uk_ppera) * keg->uk_ipers - keg->uk_free,
154757223e99SAndriy Gapon 	    keg->uk_free);
1548099a0e58SBosko Milekic 
1549099a0e58SBosko Milekic 	LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link);
1550099a0e58SBosko Milekic 
1551111fbcd5SBryan Venteicher 	rw_wlock(&uma_rwlock);
1552099a0e58SBosko Milekic 	LIST_INSERT_HEAD(&uma_kegs, keg, uk_link);
1553111fbcd5SBryan Venteicher 	rw_wunlock(&uma_rwlock);
1554b23f72e9SBrian Feldman 	return (0);
1555099a0e58SBosko Milekic }
1556099a0e58SBosko Milekic 
1557099a0e58SBosko Milekic /*
1558099a0e58SBosko Milekic  * Zone header ctor.  This initializes all fields, locks, etc.
1559099a0e58SBosko Milekic  *
1560099a0e58SBosko Milekic  * Arguments/Returns follow uma_ctor specifications
1561099a0e58SBosko Milekic  *	udata  Actually uma_zctor_args
15628355f576SJeff Roberson  */
1563b23f72e9SBrian Feldman static int
1564b23f72e9SBrian Feldman zone_ctor(void *mem, int size, void *udata, int flags)
15658355f576SJeff Roberson {
15668355f576SJeff Roberson 	struct uma_zctor_args *arg = udata;
15678355f576SJeff Roberson 	uma_zone_t zone = mem;
1568099a0e58SBosko Milekic 	uma_zone_t z;
1569099a0e58SBosko Milekic 	uma_keg_t keg;
15708355f576SJeff Roberson 
15718355f576SJeff Roberson 	bzero(zone, size);
15728355f576SJeff Roberson 	zone->uz_name = arg->name;
15738355f576SJeff Roberson 	zone->uz_ctor = arg->ctor;
15748355f576SJeff Roberson 	zone->uz_dtor = arg->dtor;
1575e20a199fSJeff Roberson 	zone->uz_slab = zone_fetch_slab;
1576099a0e58SBosko Milekic 	zone->uz_init = NULL;
1577099a0e58SBosko Milekic 	zone->uz_fini = NULL;
1578099a0e58SBosko Milekic 	zone->uz_allocs = 0;
1579773df9abSRobert Watson 	zone->uz_frees = 0;
15802019094aSRobert Watson 	zone->uz_fails = 0;
1581bf965959SSean Bruno 	zone->uz_sleeps = 0;
1582fc03d22bSJeff Roberson 	zone->uz_count = 0;
1583ace66b56SAlexander Motin 	zone->uz_count_min = 0;
1584e20a199fSJeff Roberson 	zone->uz_flags = 0;
15852f891cd5SPawel Jakub Dawidek 	zone->uz_warning = NULL;
1586ab3185d1SJeff Roberson 	/* The domain structures follow the cpu structures. */
1587ab3185d1SJeff Roberson 	zone->uz_domain = (struct uma_zone_domain *)&zone->uz_cpu[mp_ncpus];
15882f891cd5SPawel Jakub Dawidek 	timevalclear(&zone->uz_ratecheck);
1589e20a199fSJeff Roberson 	keg = arg->keg;
1590099a0e58SBosko Milekic 
1591af526374SJeff Roberson 	ZONE_LOCK_INIT(zone, (arg->flags & UMA_ZONE_MTXCLASS));
1592af526374SJeff Roberson 
15930095a784SJeff Roberson 	/*
15940095a784SJeff Roberson 	 * This is a pure cache zone, no kegs.
15950095a784SJeff Roberson 	 */
15960095a784SJeff Roberson 	if (arg->import) {
15976fd34d6fSJeff Roberson 		if (arg->flags & UMA_ZONE_VM)
15986fd34d6fSJeff Roberson 			arg->flags |= UMA_ZFLAG_CACHEONLY;
15996fd34d6fSJeff Roberson 		zone->uz_flags = arg->flags;
1600af526374SJeff Roberson 		zone->uz_size = arg->size;
16010095a784SJeff Roberson 		zone->uz_import = arg->import;
16020095a784SJeff Roberson 		zone->uz_release = arg->release;
16030095a784SJeff Roberson 		zone->uz_arg = arg->arg;
1604af526374SJeff Roberson 		zone->uz_lockptr = &zone->uz_lock;
1605111fbcd5SBryan Venteicher 		rw_wlock(&uma_rwlock);
160603175483SAlexander Motin 		LIST_INSERT_HEAD(&uma_cachezones, zone, uz_link);
1607111fbcd5SBryan Venteicher 		rw_wunlock(&uma_rwlock);
1608af526374SJeff Roberson 		goto out;
16090095a784SJeff Roberson 	}
16100095a784SJeff Roberson 
16110095a784SJeff Roberson 	/*
16120095a784SJeff Roberson 	 * Use the regular zone/keg/slab allocator.
16130095a784SJeff Roberson 	 */
16140095a784SJeff Roberson 	zone->uz_import = (uma_import)zone_import;
16150095a784SJeff Roberson 	zone->uz_release = (uma_release)zone_release;
16160095a784SJeff Roberson 	zone->uz_arg = zone;
16170095a784SJeff Roberson 
1618099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_SECONDARY) {
1619099a0e58SBosko Milekic 		KASSERT(arg->keg != NULL, ("Secondary zone on zero'd keg"));
16208355f576SJeff Roberson 		zone->uz_init = arg->uminit;
1621e221e841SJeff Roberson 		zone->uz_fini = arg->fini;
1622af526374SJeff Roberson 		zone->uz_lockptr = &keg->uk_lock;
1623e20a199fSJeff Roberson 		zone->uz_flags |= UMA_ZONE_SECONDARY;
1624111fbcd5SBryan Venteicher 		rw_wlock(&uma_rwlock);
1625099a0e58SBosko Milekic 		ZONE_LOCK(zone);
1626099a0e58SBosko Milekic 		LIST_FOREACH(z, &keg->uk_zones, uz_link) {
1627099a0e58SBosko Milekic 			if (LIST_NEXT(z, uz_link) == NULL) {
1628099a0e58SBosko Milekic 				LIST_INSERT_AFTER(z, zone, uz_link);
1629099a0e58SBosko Milekic 				break;
1630099a0e58SBosko Milekic 			}
1631099a0e58SBosko Milekic 		}
1632099a0e58SBosko Milekic 		ZONE_UNLOCK(zone);
1633111fbcd5SBryan Venteicher 		rw_wunlock(&uma_rwlock);
1634e20a199fSJeff Roberson 	} else if (keg == NULL) {
1635e20a199fSJeff Roberson 		if ((keg = uma_kcreate(zone, arg->size, arg->uminit, arg->fini,
1636e20a199fSJeff Roberson 		    arg->align, arg->flags)) == NULL)
1637b23f72e9SBrian Feldman 			return (ENOMEM);
1638099a0e58SBosko Milekic 	} else {
1639099a0e58SBosko Milekic 		struct uma_kctor_args karg;
1640b23f72e9SBrian Feldman 		int error;
1641099a0e58SBosko Milekic 
1642099a0e58SBosko Milekic 		/* We should only be here from uma_startup() */
1643099a0e58SBosko Milekic 		karg.size = arg->size;
1644099a0e58SBosko Milekic 		karg.uminit = arg->uminit;
1645099a0e58SBosko Milekic 		karg.fini = arg->fini;
1646099a0e58SBosko Milekic 		karg.align = arg->align;
1647099a0e58SBosko Milekic 		karg.flags = arg->flags;
1648099a0e58SBosko Milekic 		karg.zone = zone;
1649b23f72e9SBrian Feldman 		error = keg_ctor(arg->keg, sizeof(struct uma_keg), &karg,
1650b23f72e9SBrian Feldman 		    flags);
1651b23f72e9SBrian Feldman 		if (error)
1652b23f72e9SBrian Feldman 			return (error);
1653099a0e58SBosko Milekic 	}
16540095a784SJeff Roberson 
1655e20a199fSJeff Roberson 	/*
1656e20a199fSJeff Roberson 	 * Link in the first keg.
1657e20a199fSJeff Roberson 	 */
1658e20a199fSJeff Roberson 	zone->uz_klink.kl_keg = keg;
1659e20a199fSJeff Roberson 	LIST_INSERT_HEAD(&zone->uz_kegs, &zone->uz_klink, kl_link);
1660af526374SJeff Roberson 	zone->uz_lockptr = &keg->uk_lock;
1661e20a199fSJeff Roberson 	zone->uz_size = keg->uk_size;
1662e20a199fSJeff Roberson 	zone->uz_flags |= (keg->uk_flags &
1663e20a199fSJeff Roberson 	    (UMA_ZONE_INHERIT | UMA_ZFLAG_INHERIT));
16648355f576SJeff Roberson 
16658355f576SJeff Roberson 	/*
16668355f576SJeff Roberson 	 * Some internal zones don't have room allocated for the per cpu
16678355f576SJeff Roberson 	 * caches.  If we're internal, bail out here.
16688355f576SJeff Roberson 	 */
1669099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_INTERNAL) {
1670e20a199fSJeff Roberson 		KASSERT((zone->uz_flags & UMA_ZONE_SECONDARY) == 0,
1671099a0e58SBosko Milekic 		    ("Secondary zone requested UMA_ZFLAG_INTERNAL"));
1672b23f72e9SBrian Feldman 		return (0);
1673099a0e58SBosko Milekic 	}
16748355f576SJeff Roberson 
1675af526374SJeff Roberson out:
1676af526374SJeff Roberson 	if ((arg->flags & UMA_ZONE_MAXBUCKET) == 0)
1677af526374SJeff Roberson 		zone->uz_count = bucket_select(zone->uz_size);
16788355f576SJeff Roberson 	else
1679cae33c14SJeff Roberson 		zone->uz_count = BUCKET_MAX;
1680ace66b56SAlexander Motin 	zone->uz_count_min = zone->uz_count;
1681fc03d22bSJeff Roberson 
1682b23f72e9SBrian Feldman 	return (0);
16838355f576SJeff Roberson }
16848355f576SJeff Roberson 
16858355f576SJeff Roberson /*
1686099a0e58SBosko Milekic  * Keg header dtor.  This frees all data, destroys locks, frees the hash
1687099a0e58SBosko Milekic  * table and removes the keg from the global list.
16889c2cd7e5SJeff Roberson  *
16899c2cd7e5SJeff Roberson  * Arguments/Returns follow uma_dtor specifications
16909c2cd7e5SJeff Roberson  *	udata  unused
16919c2cd7e5SJeff Roberson  */
1692099a0e58SBosko Milekic static void
1693099a0e58SBosko Milekic keg_dtor(void *arg, int size, void *udata)
1694099a0e58SBosko Milekic {
1695099a0e58SBosko Milekic 	uma_keg_t keg;
16969c2cd7e5SJeff Roberson 
1697099a0e58SBosko Milekic 	keg = (uma_keg_t)arg;
1698e20a199fSJeff Roberson 	KEG_LOCK(keg);
1699099a0e58SBosko Milekic 	if (keg->uk_free != 0) {
1700a3845534SCraig Rodrigues 		printf("Freed UMA keg (%s) was not empty (%d items). "
1701099a0e58SBosko Milekic 		    " Lost %d pages of memory.\n",
1702a3845534SCraig Rodrigues 		    keg->uk_name ? keg->uk_name : "",
1703099a0e58SBosko Milekic 		    keg->uk_free, keg->uk_pages);
1704099a0e58SBosko Milekic 	}
1705e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
1706099a0e58SBosko Milekic 
1707099a0e58SBosko Milekic 	hash_free(&keg->uk_hash);
1708099a0e58SBosko Milekic 
1709e20a199fSJeff Roberson 	KEG_LOCK_FINI(keg);
1710099a0e58SBosko Milekic }
1711099a0e58SBosko Milekic 
1712099a0e58SBosko Milekic /*
1713099a0e58SBosko Milekic  * Zone header dtor.
1714099a0e58SBosko Milekic  *
1715099a0e58SBosko Milekic  * Arguments/Returns follow uma_dtor specifications
1716099a0e58SBosko Milekic  *	udata  unused
1717099a0e58SBosko Milekic  */
17189c2cd7e5SJeff Roberson static void
17199c2cd7e5SJeff Roberson zone_dtor(void *arg, int size, void *udata)
17209c2cd7e5SJeff Roberson {
1721e20a199fSJeff Roberson 	uma_klink_t klink;
17229c2cd7e5SJeff Roberson 	uma_zone_t zone;
1723099a0e58SBosko Milekic 	uma_keg_t keg;
17249c2cd7e5SJeff Roberson 
17259c2cd7e5SJeff Roberson 	zone = (uma_zone_t)arg;
1726e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
17279643769aSJeff Roberson 
1728e20a199fSJeff Roberson 	if (!(zone->uz_flags & UMA_ZFLAG_INTERNAL))
17299643769aSJeff Roberson 		cache_drain(zone);
1730099a0e58SBosko Milekic 
1731111fbcd5SBryan Venteicher 	rw_wlock(&uma_rwlock);
1732099a0e58SBosko Milekic 	LIST_REMOVE(zone, uz_link);
1733111fbcd5SBryan Venteicher 	rw_wunlock(&uma_rwlock);
1734099a0e58SBosko Milekic 	/*
1735099a0e58SBosko Milekic 	 * XXX there are some races here where
1736099a0e58SBosko Milekic 	 * the zone can be drained but zone lock
1737099a0e58SBosko Milekic 	 * released and then refilled before we
1738099a0e58SBosko Milekic 	 * remove it... we dont care for now
1739099a0e58SBosko Milekic 	 */
1740e20a199fSJeff Roberson 	zone_drain_wait(zone, M_WAITOK);
1741e20a199fSJeff Roberson 	/*
1742e20a199fSJeff Roberson 	 * Unlink all of our kegs.
1743e20a199fSJeff Roberson 	 */
1744e20a199fSJeff Roberson 	while ((klink = LIST_FIRST(&zone->uz_kegs)) != NULL) {
1745e20a199fSJeff Roberson 		klink->kl_keg = NULL;
1746e20a199fSJeff Roberson 		LIST_REMOVE(klink, kl_link);
1747e20a199fSJeff Roberson 		if (klink == &zone->uz_klink)
1748e20a199fSJeff Roberson 			continue;
1749e20a199fSJeff Roberson 		free(klink, M_TEMP);
1750e20a199fSJeff Roberson 	}
1751e20a199fSJeff Roberson 	/*
1752e20a199fSJeff Roberson 	 * We only destroy kegs from non secondary zones.
1753e20a199fSJeff Roberson 	 */
17540095a784SJeff Roberson 	if (keg != NULL && (zone->uz_flags & UMA_ZONE_SECONDARY) == 0)  {
1755111fbcd5SBryan Venteicher 		rw_wlock(&uma_rwlock);
1756099a0e58SBosko Milekic 		LIST_REMOVE(keg, uk_link);
1757111fbcd5SBryan Venteicher 		rw_wunlock(&uma_rwlock);
17580095a784SJeff Roberson 		zone_free_item(kegs, keg, NULL, SKIP_NONE);
17599c2cd7e5SJeff Roberson 	}
1760af526374SJeff Roberson 	ZONE_LOCK_FINI(zone);
1761099a0e58SBosko Milekic }
1762099a0e58SBosko Milekic 
17639c2cd7e5SJeff Roberson /*
17648355f576SJeff Roberson  * Traverses every zone in the system and calls a callback
17658355f576SJeff Roberson  *
17668355f576SJeff Roberson  * Arguments:
17678355f576SJeff Roberson  *	zfunc  A pointer to a function which accepts a zone
17688355f576SJeff Roberson  *		as an argument.
17698355f576SJeff Roberson  *
17708355f576SJeff Roberson  * Returns:
17718355f576SJeff Roberson  *	Nothing
17728355f576SJeff Roberson  */
17738355f576SJeff Roberson static void
17748355f576SJeff Roberson zone_foreach(void (*zfunc)(uma_zone_t))
17758355f576SJeff Roberson {
1776099a0e58SBosko Milekic 	uma_keg_t keg;
17778355f576SJeff Roberson 	uma_zone_t zone;
17788355f576SJeff Roberson 
1779111fbcd5SBryan Venteicher 	rw_rlock(&uma_rwlock);
1780099a0e58SBosko Milekic 	LIST_FOREACH(keg, &uma_kegs, uk_link) {
1781099a0e58SBosko Milekic 		LIST_FOREACH(zone, &keg->uk_zones, uz_link)
17828355f576SJeff Roberson 			zfunc(zone);
1783099a0e58SBosko Milekic 	}
1784111fbcd5SBryan Venteicher 	rw_runlock(&uma_rwlock);
17858355f576SJeff Roberson }
17868355f576SJeff Roberson 
1787f4bef67cSGleb Smirnoff /*
1788f4bef67cSGleb Smirnoff  * Count how many pages do we need to bootstrap.  VM supplies
1789f4bef67cSGleb Smirnoff  * its need in early zones in the argument, we add up our zones,
1790f4bef67cSGleb Smirnoff  * which consist of: UMA Slabs, UMA Hash and 9 Bucket zones. The
1791f4bef67cSGleb Smirnoff  * zone of zones and zone of kegs are accounted separately.
1792f4bef67cSGleb Smirnoff  */
1793f4bef67cSGleb Smirnoff #define	UMA_BOOT_ZONES	11
17945073a083SGleb Smirnoff /* Zone of zones and zone of kegs have arbitrary alignment. */
17955073a083SGleb Smirnoff #define	UMA_BOOT_ALIGN	32
1796f4bef67cSGleb Smirnoff static int zsize, ksize;
1797f4bef67cSGleb Smirnoff int
1798f7d35785SGleb Smirnoff uma_startup_count(int vm_zones)
1799f4bef67cSGleb Smirnoff {
1800f7d35785SGleb Smirnoff 	int zones, pages;
1801f4bef67cSGleb Smirnoff 
1802f4bef67cSGleb Smirnoff 	ksize = sizeof(struct uma_keg) +
1803f4bef67cSGleb Smirnoff 	    (sizeof(struct uma_domain) * vm_ndomains);
1804f4bef67cSGleb Smirnoff 	zsize = sizeof(struct uma_zone) +
1805f4bef67cSGleb Smirnoff 	    (sizeof(struct uma_cache) * (mp_maxid + 1)) +
1806f4bef67cSGleb Smirnoff 	    (sizeof(struct uma_zone_domain) * vm_ndomains);
1807f4bef67cSGleb Smirnoff 
18085073a083SGleb Smirnoff 	/*
18095073a083SGleb Smirnoff 	 * Memory for the zone of kegs and its keg,
18105073a083SGleb Smirnoff 	 * and for zone of zones.
18115073a083SGleb Smirnoff 	 */
1812f4bef67cSGleb Smirnoff 	pages = howmany(roundup(zsize, CACHE_LINE_SIZE) * 2 +
1813f4bef67cSGleb Smirnoff 	    roundup(ksize, CACHE_LINE_SIZE), PAGE_SIZE);
1814f4bef67cSGleb Smirnoff 
1815f7d35785SGleb Smirnoff #ifdef	UMA_MD_SMALL_ALLOC
1816f7d35785SGleb Smirnoff 	zones = UMA_BOOT_ZONES;
1817f7d35785SGleb Smirnoff #else
1818f7d35785SGleb Smirnoff 	zones = UMA_BOOT_ZONES + vm_zones;
1819f7d35785SGleb Smirnoff 	vm_zones = 0;
1820f7d35785SGleb Smirnoff #endif
1821f4bef67cSGleb Smirnoff 
18225073a083SGleb Smirnoff 	/* Memory for the rest of startup zones, UMA and VM, ... */
1823f4bef67cSGleb Smirnoff 	if (zsize > UMA_SLAB_SIZE)
1824f7d35785SGleb Smirnoff 		pages += (zones + vm_zones) *
1825f7d35785SGleb Smirnoff 		    howmany(roundup2(zsize, UMA_BOOT_ALIGN), UMA_SLAB_SIZE);
1826f4bef67cSGleb Smirnoff 	else
18275073a083SGleb Smirnoff 		pages += howmany(zones,
18285073a083SGleb Smirnoff 		    UMA_SLAB_SPACE / roundup2(zsize, UMA_BOOT_ALIGN));
1829f4bef67cSGleb Smirnoff 
18305073a083SGleb Smirnoff 	/* ... and their kegs. Note that zone of zones allocates a keg! */
18315073a083SGleb Smirnoff 	pages += howmany(zones + 1,
18325073a083SGleb Smirnoff 	    UMA_SLAB_SPACE / roundup2(ksize, UMA_BOOT_ALIGN));
1833f4bef67cSGleb Smirnoff 
1834f4bef67cSGleb Smirnoff 	/*
18355073a083SGleb Smirnoff 	 * Most of startup zones are not going to be offpages, that's
18365073a083SGleb Smirnoff 	 * why we use UMA_SLAB_SPACE instead of UMA_SLAB_SIZE in all
18375073a083SGleb Smirnoff 	 * calculations.  Some large bucket zones will be offpage, and
18385073a083SGleb Smirnoff 	 * thus will allocate hashes.  We take conservative approach
18395073a083SGleb Smirnoff 	 * and assume that all zones may allocate hash.  This may give
18405073a083SGleb Smirnoff 	 * us some positive inaccuracy, usually an extra single page.
1841f4bef67cSGleb Smirnoff 	 */
18425073a083SGleb Smirnoff 	pages += howmany(zones, UMA_SLAB_SPACE /
1843d2be4a1eSGleb Smirnoff 	    (sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT));
1844f4bef67cSGleb Smirnoff 
1845f4bef67cSGleb Smirnoff 	return (pages);
1846f4bef67cSGleb Smirnoff }
1847f4bef67cSGleb Smirnoff 
18488355f576SJeff Roberson void
1849ac0a6fd0SGleb Smirnoff uma_startup(void *mem, int npages)
18508355f576SJeff Roberson {
18518355f576SJeff Roberson 	struct uma_zctor_args args;
1852ab3185d1SJeff Roberson 	uma_keg_t masterkeg;
1853ab3185d1SJeff Roberson 	uintptr_t m;
1854f4bef67cSGleb Smirnoff 
1855f4bef67cSGleb Smirnoff #ifdef DIAGNOSTIC
1856f4bef67cSGleb Smirnoff 	printf("Entering %s with %d boot pages configured\n", __func__, npages);
1857f4bef67cSGleb Smirnoff #endif
18588355f576SJeff Roberson 
1859111fbcd5SBryan Venteicher 	rw_init(&uma_rwlock, "UMA lock");
1860099a0e58SBosko Milekic 
1861ab3185d1SJeff Roberson 	/* Use bootpages memory for the zone of zones and zone of kegs. */
1862ab3185d1SJeff Roberson 	m = (uintptr_t)mem;
1863ab3185d1SJeff Roberson 	zones = (uma_zone_t)m;
1864ab3185d1SJeff Roberson 	m += roundup(zsize, CACHE_LINE_SIZE);
1865ab3185d1SJeff Roberson 	kegs = (uma_zone_t)m;
1866ab3185d1SJeff Roberson 	m += roundup(zsize, CACHE_LINE_SIZE);
1867ab3185d1SJeff Roberson 	masterkeg = (uma_keg_t)m;
1868ab3185d1SJeff Roberson 	m += roundup(ksize, CACHE_LINE_SIZE);
1869ab3185d1SJeff Roberson 	m = roundup(m, PAGE_SIZE);
1870ab3185d1SJeff Roberson 	npages -= (m - (uintptr_t)mem) / PAGE_SIZE;
1871ab3185d1SJeff Roberson 	mem = (void *)m;
1872ab3185d1SJeff Roberson 
1873099a0e58SBosko Milekic 	/* "manually" create the initial zone */
18740095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
1875099a0e58SBosko Milekic 	args.name = "UMA Kegs";
1876ab3185d1SJeff Roberson 	args.size = ksize;
1877099a0e58SBosko Milekic 	args.ctor = keg_ctor;
1878099a0e58SBosko Milekic 	args.dtor = keg_dtor;
18798355f576SJeff Roberson 	args.uminit = zero_init;
18808355f576SJeff Roberson 	args.fini = NULL;
1881ab3185d1SJeff Roberson 	args.keg = masterkeg;
18825073a083SGleb Smirnoff 	args.align = UMA_BOOT_ALIGN - 1;
1883b60f5b79SJeff Roberson 	args.flags = UMA_ZFLAG_INTERNAL;
1884ab3185d1SJeff Roberson 	zone_ctor(kegs, zsize, &args, M_WAITOK);
18858355f576SJeff Roberson 
1886ac0a6fd0SGleb Smirnoff 	bootmem = mem;
1887ac0a6fd0SGleb Smirnoff 	boot_pages = npages;
18888355f576SJeff Roberson 
1889099a0e58SBosko Milekic 	args.name = "UMA Zones";
1890f4bef67cSGleb Smirnoff 	args.size = zsize;
1891099a0e58SBosko Milekic 	args.ctor = zone_ctor;
1892099a0e58SBosko Milekic 	args.dtor = zone_dtor;
1893099a0e58SBosko Milekic 	args.uminit = zero_init;
1894099a0e58SBosko Milekic 	args.fini = NULL;
1895099a0e58SBosko Milekic 	args.keg = NULL;
18965073a083SGleb Smirnoff 	args.align = UMA_BOOT_ALIGN - 1;
1897099a0e58SBosko Milekic 	args.flags = UMA_ZFLAG_INTERNAL;
1898ab3185d1SJeff Roberson 	zone_ctor(zones, zsize, &args, M_WAITOK);
1899099a0e58SBosko Milekic 
19008355f576SJeff Roberson 	/* Now make a zone for slab headers */
19018355f576SJeff Roberson 	slabzone = uma_zcreate("UMA Slabs",
1902ef72505eSJeff Roberson 				sizeof(struct uma_slab),
19038355f576SJeff Roberson 				NULL, NULL, NULL, NULL,
1904b60f5b79SJeff Roberson 				UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
19058355f576SJeff Roberson 
19068355f576SJeff Roberson 	hashzone = uma_zcreate("UMA Hash",
19078355f576SJeff Roberson 	    sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT,
19088355f576SJeff Roberson 	    NULL, NULL, NULL, NULL,
1909b60f5b79SJeff Roberson 	    UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
19108355f576SJeff Roberson 
1911cae33c14SJeff Roberson 	bucket_init();
19128355f576SJeff Roberson 
1913f4bef67cSGleb Smirnoff 	booted = BOOT_STRAPPED;
19148355f576SJeff Roberson }
19158355f576SJeff Roberson 
1916f4bef67cSGleb Smirnoff void
1917f4bef67cSGleb Smirnoff uma_startup1(void)
1918f4bef67cSGleb Smirnoff {
1919f4bef67cSGleb Smirnoff 
1920f4bef67cSGleb Smirnoff #ifdef DIAGNOSTIC
1921f4bef67cSGleb Smirnoff 	printf("Entering %s with %d boot pages left\n", __func__, boot_pages);
1922f4bef67cSGleb Smirnoff #endif
1923f4bef67cSGleb Smirnoff 	booted = BOOT_PAGEALLOC;
1924f4bef67cSGleb Smirnoff }
1925f4bef67cSGleb Smirnoff 
19268355f576SJeff Roberson void
192799571dc3SJeff Roberson uma_startup2(void)
19288355f576SJeff Roberson {
1929f4bef67cSGleb Smirnoff 
1930f7d35785SGleb Smirnoff #ifdef DIAGNOSTIC
1931f7d35785SGleb Smirnoff 	printf("Entering %s with %d boot pages left\n", __func__, boot_pages);
1932f7d35785SGleb Smirnoff #endif
1933f4bef67cSGleb Smirnoff 	booted = BOOT_BUCKETS;
193495c4bf75SKonstantin Belousov 	sx_init(&uma_drain_lock, "umadrain");
1935f4bef67cSGleb Smirnoff 	bucket_enable();
19368355f576SJeff Roberson }
19378355f576SJeff Roberson 
19388355f576SJeff Roberson /*
19398355f576SJeff Roberson  * Initialize our callout handle
19408355f576SJeff Roberson  *
19418355f576SJeff Roberson  */
19428355f576SJeff Roberson static void
19438355f576SJeff Roberson uma_startup3(void)
19448355f576SJeff Roberson {
19451431a748SGleb Smirnoff 
1946f4bef67cSGleb Smirnoff 	booted = BOOT_RUNNING;
1947fd90e2edSJung-uk Kim 	callout_init(&uma_callout, 1);
19489643769aSJeff Roberson 	callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
19498355f576SJeff Roberson }
19508355f576SJeff Roberson 
1951e20a199fSJeff Roberson static uma_keg_t
1952099a0e58SBosko Milekic uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini,
195385dcf349SGleb Smirnoff 		int align, uint32_t flags)
1954099a0e58SBosko Milekic {
1955099a0e58SBosko Milekic 	struct uma_kctor_args args;
1956099a0e58SBosko Milekic 
1957099a0e58SBosko Milekic 	args.size = size;
1958099a0e58SBosko Milekic 	args.uminit = uminit;
1959099a0e58SBosko Milekic 	args.fini = fini;
19601e319f6dSRobert Watson 	args.align = (align == UMA_ALIGN_CACHE) ? uma_align_cache : align;
1961099a0e58SBosko Milekic 	args.flags = flags;
1962099a0e58SBosko Milekic 	args.zone = zone;
1963ab3185d1SJeff Roberson 	return (zone_alloc_item(kegs, &args, UMA_ANYDOMAIN, M_WAITOK));
1964099a0e58SBosko Milekic }
1965099a0e58SBosko Milekic 
1966f4bef67cSGleb Smirnoff /* Public functions */
19678355f576SJeff Roberson /* See uma.h */
19681e319f6dSRobert Watson void
19691e319f6dSRobert Watson uma_set_align(int align)
19701e319f6dSRobert Watson {
19711e319f6dSRobert Watson 
19721e319f6dSRobert Watson 	if (align != UMA_ALIGN_CACHE)
19731e319f6dSRobert Watson 		uma_align_cache = align;
19741e319f6dSRobert Watson }
19751e319f6dSRobert Watson 
19761e319f6dSRobert Watson /* See uma.h */
19778355f576SJeff Roberson uma_zone_t
1978bb196eb4SMatthew D Fleming uma_zcreate(const char *name, size_t size, uma_ctor ctor, uma_dtor dtor,
197985dcf349SGleb Smirnoff 		uma_init uminit, uma_fini fini, int align, uint32_t flags)
19808355f576SJeff Roberson 
19818355f576SJeff Roberson {
19828355f576SJeff Roberson 	struct uma_zctor_args args;
198395c4bf75SKonstantin Belousov 	uma_zone_t res;
198495c4bf75SKonstantin Belousov 	bool locked;
19858355f576SJeff Roberson 
1986a5a35578SJohn Baldwin 	KASSERT(powerof2(align + 1), ("invalid zone alignment %d for \"%s\"",
1987a5a35578SJohn Baldwin 	    align, name));
1988a5a35578SJohn Baldwin 
19898355f576SJeff Roberson 	/* This stuff is essential for the zone ctor */
19900095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
19918355f576SJeff Roberson 	args.name = name;
19928355f576SJeff Roberson 	args.size = size;
19938355f576SJeff Roberson 	args.ctor = ctor;
19948355f576SJeff Roberson 	args.dtor = dtor;
19958355f576SJeff Roberson 	args.uminit = uminit;
19968355f576SJeff Roberson 	args.fini = fini;
1997afc6dc36SJohn-Mark Gurney #ifdef  INVARIANTS
1998afc6dc36SJohn-Mark Gurney 	/*
1999afc6dc36SJohn-Mark Gurney 	 * If a zone is being created with an empty constructor and
2000afc6dc36SJohn-Mark Gurney 	 * destructor, pass UMA constructor/destructor which checks for
2001afc6dc36SJohn-Mark Gurney 	 * memory use after free.
2002afc6dc36SJohn-Mark Gurney 	 */
200319c591bfSMateusz Guzik 	if ((!(flags & (UMA_ZONE_ZINIT | UMA_ZONE_NOFREE))) &&
200419c591bfSMateusz Guzik 	    ctor == NULL && dtor == NULL && uminit == NULL && fini == NULL) {
2005afc6dc36SJohn-Mark Gurney 		args.ctor = trash_ctor;
2006afc6dc36SJohn-Mark Gurney 		args.dtor = trash_dtor;
2007afc6dc36SJohn-Mark Gurney 		args.uminit = trash_init;
2008afc6dc36SJohn-Mark Gurney 		args.fini = trash_fini;
2009afc6dc36SJohn-Mark Gurney 	}
2010afc6dc36SJohn-Mark Gurney #endif
20118355f576SJeff Roberson 	args.align = align;
20128355f576SJeff Roberson 	args.flags = flags;
2013099a0e58SBosko Milekic 	args.keg = NULL;
2014099a0e58SBosko Milekic 
2015f4bef67cSGleb Smirnoff 	if (booted < BOOT_BUCKETS) {
201695c4bf75SKonstantin Belousov 		locked = false;
201795c4bf75SKonstantin Belousov 	} else {
201895c4bf75SKonstantin Belousov 		sx_slock(&uma_drain_lock);
201995c4bf75SKonstantin Belousov 		locked = true;
202095c4bf75SKonstantin Belousov 	}
2021ab3185d1SJeff Roberson 	res = zone_alloc_item(zones, &args, UMA_ANYDOMAIN, M_WAITOK);
202295c4bf75SKonstantin Belousov 	if (locked)
202395c4bf75SKonstantin Belousov 		sx_sunlock(&uma_drain_lock);
202495c4bf75SKonstantin Belousov 	return (res);
2025099a0e58SBosko Milekic }
2026099a0e58SBosko Milekic 
2027099a0e58SBosko Milekic /* See uma.h */
2028099a0e58SBosko Milekic uma_zone_t
2029099a0e58SBosko Milekic uma_zsecond_create(char *name, uma_ctor ctor, uma_dtor dtor,
2030099a0e58SBosko Milekic 		    uma_init zinit, uma_fini zfini, uma_zone_t master)
2031099a0e58SBosko Milekic {
2032099a0e58SBosko Milekic 	struct uma_zctor_args args;
2033e20a199fSJeff Roberson 	uma_keg_t keg;
203495c4bf75SKonstantin Belousov 	uma_zone_t res;
203595c4bf75SKonstantin Belousov 	bool locked;
2036099a0e58SBosko Milekic 
2037e20a199fSJeff Roberson 	keg = zone_first_keg(master);
20380095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
2039099a0e58SBosko Milekic 	args.name = name;
2040e20a199fSJeff Roberson 	args.size = keg->uk_size;
2041099a0e58SBosko Milekic 	args.ctor = ctor;
2042099a0e58SBosko Milekic 	args.dtor = dtor;
2043099a0e58SBosko Milekic 	args.uminit = zinit;
2044099a0e58SBosko Milekic 	args.fini = zfini;
2045e20a199fSJeff Roberson 	args.align = keg->uk_align;
2046e20a199fSJeff Roberson 	args.flags = keg->uk_flags | UMA_ZONE_SECONDARY;
2047e20a199fSJeff Roberson 	args.keg = keg;
20488355f576SJeff Roberson 
2049f4bef67cSGleb Smirnoff 	if (booted < BOOT_BUCKETS) {
205095c4bf75SKonstantin Belousov 		locked = false;
205195c4bf75SKonstantin Belousov 	} else {
205295c4bf75SKonstantin Belousov 		sx_slock(&uma_drain_lock);
205395c4bf75SKonstantin Belousov 		locked = true;
205495c4bf75SKonstantin Belousov 	}
2055e20a199fSJeff Roberson 	/* XXX Attaches only one keg of potentially many. */
2056ab3185d1SJeff Roberson 	res = zone_alloc_item(zones, &args, UMA_ANYDOMAIN, M_WAITOK);
205795c4bf75SKonstantin Belousov 	if (locked)
205895c4bf75SKonstantin Belousov 		sx_sunlock(&uma_drain_lock);
205995c4bf75SKonstantin Belousov 	return (res);
20608355f576SJeff Roberson }
20618355f576SJeff Roberson 
20620095a784SJeff Roberson /* See uma.h */
20630095a784SJeff Roberson uma_zone_t
2064af526374SJeff Roberson uma_zcache_create(char *name, int size, uma_ctor ctor, uma_dtor dtor,
2065af526374SJeff Roberson 		    uma_init zinit, uma_fini zfini, uma_import zimport,
2066af526374SJeff Roberson 		    uma_release zrelease, void *arg, int flags)
20670095a784SJeff Roberson {
20680095a784SJeff Roberson 	struct uma_zctor_args args;
20690095a784SJeff Roberson 
20700095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
20710095a784SJeff Roberson 	args.name = name;
2072af526374SJeff Roberson 	args.size = size;
20730095a784SJeff Roberson 	args.ctor = ctor;
20740095a784SJeff Roberson 	args.dtor = dtor;
20750095a784SJeff Roberson 	args.uminit = zinit;
20760095a784SJeff Roberson 	args.fini = zfini;
20770095a784SJeff Roberson 	args.import = zimport;
20780095a784SJeff Roberson 	args.release = zrelease;
20790095a784SJeff Roberson 	args.arg = arg;
20800095a784SJeff Roberson 	args.align = 0;
20810095a784SJeff Roberson 	args.flags = flags;
20820095a784SJeff Roberson 
2083ab3185d1SJeff Roberson 	return (zone_alloc_item(zones, &args, UMA_ANYDOMAIN, M_WAITOK));
20840095a784SJeff Roberson }
20850095a784SJeff Roberson 
2086e20a199fSJeff Roberson static void
2087e20a199fSJeff Roberson zone_lock_pair(uma_zone_t a, uma_zone_t b)
2088e20a199fSJeff Roberson {
2089e20a199fSJeff Roberson 	if (a < b) {
2090e20a199fSJeff Roberson 		ZONE_LOCK(a);
2091af526374SJeff Roberson 		mtx_lock_flags(b->uz_lockptr, MTX_DUPOK);
2092e20a199fSJeff Roberson 	} else {
2093e20a199fSJeff Roberson 		ZONE_LOCK(b);
2094af526374SJeff Roberson 		mtx_lock_flags(a->uz_lockptr, MTX_DUPOK);
2095e20a199fSJeff Roberson 	}
2096e20a199fSJeff Roberson }
2097e20a199fSJeff Roberson 
2098e20a199fSJeff Roberson static void
2099e20a199fSJeff Roberson zone_unlock_pair(uma_zone_t a, uma_zone_t b)
2100e20a199fSJeff Roberson {
2101e20a199fSJeff Roberson 
2102e20a199fSJeff Roberson 	ZONE_UNLOCK(a);
2103e20a199fSJeff Roberson 	ZONE_UNLOCK(b);
2104e20a199fSJeff Roberson }
2105e20a199fSJeff Roberson 
2106e20a199fSJeff Roberson int
2107e20a199fSJeff Roberson uma_zsecond_add(uma_zone_t zone, uma_zone_t master)
2108e20a199fSJeff Roberson {
2109e20a199fSJeff Roberson 	uma_klink_t klink;
2110e20a199fSJeff Roberson 	uma_klink_t kl;
2111e20a199fSJeff Roberson 	int error;
2112e20a199fSJeff Roberson 
2113e20a199fSJeff Roberson 	error = 0;
2114e20a199fSJeff Roberson 	klink = malloc(sizeof(*klink), M_TEMP, M_WAITOK | M_ZERO);
2115e20a199fSJeff Roberson 
2116e20a199fSJeff Roberson 	zone_lock_pair(zone, master);
2117e20a199fSJeff Roberson 	/*
2118e20a199fSJeff Roberson 	 * zone must use vtoslab() to resolve objects and must already be
2119e20a199fSJeff Roberson 	 * a secondary.
2120e20a199fSJeff Roberson 	 */
2121e20a199fSJeff Roberson 	if ((zone->uz_flags & (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY))
2122e20a199fSJeff Roberson 	    != (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) {
2123e20a199fSJeff Roberson 		error = EINVAL;
2124e20a199fSJeff Roberson 		goto out;
2125e20a199fSJeff Roberson 	}
2126e20a199fSJeff Roberson 	/*
2127e20a199fSJeff Roberson 	 * The new master must also use vtoslab().
2128e20a199fSJeff Roberson 	 */
2129e20a199fSJeff Roberson 	if ((zone->uz_flags & UMA_ZONE_VTOSLAB) != UMA_ZONE_VTOSLAB) {
2130e20a199fSJeff Roberson 		error = EINVAL;
2131e20a199fSJeff Roberson 		goto out;
2132e20a199fSJeff Roberson 	}
2133cfcae3f8SGleb Smirnoff 
2134e20a199fSJeff Roberson 	/*
2135e20a199fSJeff Roberson 	 * The underlying object must be the same size.  rsize
2136e20a199fSJeff Roberson 	 * may be different.
2137e20a199fSJeff Roberson 	 */
2138e20a199fSJeff Roberson 	if (master->uz_size != zone->uz_size) {
2139e20a199fSJeff Roberson 		error = E2BIG;
2140e20a199fSJeff Roberson 		goto out;
2141e20a199fSJeff Roberson 	}
2142e20a199fSJeff Roberson 	/*
2143e20a199fSJeff Roberson 	 * Put it at the end of the list.
2144e20a199fSJeff Roberson 	 */
2145e20a199fSJeff Roberson 	klink->kl_keg = zone_first_keg(master);
2146e20a199fSJeff Roberson 	LIST_FOREACH(kl, &zone->uz_kegs, kl_link) {
2147e20a199fSJeff Roberson 		if (LIST_NEXT(kl, kl_link) == NULL) {
2148e20a199fSJeff Roberson 			LIST_INSERT_AFTER(kl, klink, kl_link);
2149e20a199fSJeff Roberson 			break;
2150e20a199fSJeff Roberson 		}
2151e20a199fSJeff Roberson 	}
2152e20a199fSJeff Roberson 	klink = NULL;
2153e20a199fSJeff Roberson 	zone->uz_flags |= UMA_ZFLAG_MULTI;
2154e20a199fSJeff Roberson 	zone->uz_slab = zone_fetch_slab_multi;
2155e20a199fSJeff Roberson 
2156e20a199fSJeff Roberson out:
2157e20a199fSJeff Roberson 	zone_unlock_pair(zone, master);
2158e20a199fSJeff Roberson 	if (klink != NULL)
2159e20a199fSJeff Roberson 		free(klink, M_TEMP);
2160e20a199fSJeff Roberson 
2161e20a199fSJeff Roberson 	return (error);
2162e20a199fSJeff Roberson }
2163e20a199fSJeff Roberson 
2164e20a199fSJeff Roberson 
21658355f576SJeff Roberson /* See uma.h */
21669c2cd7e5SJeff Roberson void
21679c2cd7e5SJeff Roberson uma_zdestroy(uma_zone_t zone)
21689c2cd7e5SJeff Roberson {
2169f4ff923bSRobert Watson 
217095c4bf75SKonstantin Belousov 	sx_slock(&uma_drain_lock);
21710095a784SJeff Roberson 	zone_free_item(zones, zone, NULL, SKIP_NONE);
217295c4bf75SKonstantin Belousov 	sx_sunlock(&uma_drain_lock);
21739c2cd7e5SJeff Roberson }
21749c2cd7e5SJeff Roberson 
21758d6fbbb8SJeff Roberson void
21768d6fbbb8SJeff Roberson uma_zwait(uma_zone_t zone)
21778d6fbbb8SJeff Roberson {
21788d6fbbb8SJeff Roberson 	void *item;
21798d6fbbb8SJeff Roberson 
21808d6fbbb8SJeff Roberson 	item = uma_zalloc_arg(zone, NULL, M_WAITOK);
21818d6fbbb8SJeff Roberson 	uma_zfree(zone, item);
21828d6fbbb8SJeff Roberson }
21838d6fbbb8SJeff Roberson 
21849c2cd7e5SJeff Roberson /* See uma.h */
21858355f576SJeff Roberson void *
21862cc35ff9SJeff Roberson uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
21878355f576SJeff Roberson {
2188ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
21898355f576SJeff Roberson 	uma_bucket_t bucket;
2190ab3185d1SJeff Roberson 	uma_cache_t cache;
2191ab3185d1SJeff Roberson 	void *item;
2192ab3185d1SJeff Roberson 	int cpu, domain, lockfail;
21938355f576SJeff Roberson 
2194e866d8f0SMark Murray 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
2195e866d8f0SMark Murray 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
219610cb2424SMark Murray 
21978355f576SJeff Roberson 	/* This is the fast path allocation */
21981431a748SGleb Smirnoff 	CTR4(KTR_UMA, "uma_zalloc_arg thread %x zone %s(%p) flags %d",
21991431a748SGleb Smirnoff 	    curthread, zone->uz_name, zone, flags);
2200a553d4b8SJeff Roberson 
2201635fd505SRobert Watson 	if (flags & M_WAITOK) {
2202b23f72e9SBrian Feldman 		WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
2203635fd505SRobert Watson 		    "uma_zalloc_arg: zone \"%s\"", zone->uz_name);
22044c1cc01cSJohn Baldwin 	}
2205d9e2e68dSMark Johnston 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
22061067a2baSJonathan T. Looney 	    ("uma_zalloc_arg: called with spinlock or critical section held"));
22071067a2baSJonathan T. Looney 
22088d689e04SGleb Smirnoff #ifdef DEBUG_MEMGUARD
22098d689e04SGleb Smirnoff 	if (memguard_cmp_zone(zone)) {
22108d689e04SGleb Smirnoff 		item = memguard_alloc(zone->uz_size, flags);
22118d689e04SGleb Smirnoff 		if (item != NULL) {
22128d689e04SGleb Smirnoff 			if (zone->uz_init != NULL &&
22138d689e04SGleb Smirnoff 			    zone->uz_init(item, zone->uz_size, flags) != 0)
22148d689e04SGleb Smirnoff 				return (NULL);
22158d689e04SGleb Smirnoff 			if (zone->uz_ctor != NULL &&
2216fc03d22bSJeff Roberson 			    zone->uz_ctor(item, zone->uz_size, udata,
2217fc03d22bSJeff Roberson 			    flags) != 0) {
22188d689e04SGleb Smirnoff 			    	zone->uz_fini(item, zone->uz_size);
22198d689e04SGleb Smirnoff 				return (NULL);
22208d689e04SGleb Smirnoff 			}
22218d689e04SGleb Smirnoff 			return (item);
22228d689e04SGleb Smirnoff 		}
22238d689e04SGleb Smirnoff 		/* This is unfortunate but should not be fatal. */
22248d689e04SGleb Smirnoff 	}
22258d689e04SGleb Smirnoff #endif
22265d1ae027SRobert Watson 	/*
22275d1ae027SRobert Watson 	 * If possible, allocate from the per-CPU cache.  There are two
22285d1ae027SRobert Watson 	 * requirements for safe access to the per-CPU cache: (1) the thread
22295d1ae027SRobert Watson 	 * accessing the cache must not be preempted or yield during access,
22305d1ae027SRobert Watson 	 * and (2) the thread must not migrate CPUs without switching which
22315d1ae027SRobert Watson 	 * cache it accesses.  We rely on a critical section to prevent
22325d1ae027SRobert Watson 	 * preemption and migration.  We release the critical section in
22335d1ae027SRobert Watson 	 * order to acquire the zone mutex if we are unable to allocate from
22345d1ae027SRobert Watson 	 * the current cache; when we re-acquire the critical section, we
22355d1ae027SRobert Watson 	 * must detect and handle migration if it has occurred.
22365d1ae027SRobert Watson 	 */
22375d1ae027SRobert Watson 	critical_enter();
22385d1ae027SRobert Watson 	cpu = curcpu;
22398355f576SJeff Roberson 	cache = &zone->uz_cpu[cpu];
22408355f576SJeff Roberson 
22418355f576SJeff Roberson zalloc_start:
22428355f576SJeff Roberson 	bucket = cache->uc_allocbucket;
2243fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt > 0) {
2244cae33c14SJeff Roberson 		bucket->ub_cnt--;
2245cae33c14SJeff Roberson 		item = bucket->ub_bucket[bucket->ub_cnt];
22468355f576SJeff Roberson #ifdef INVARIANTS
2247cae33c14SJeff Roberson 		bucket->ub_bucket[bucket->ub_cnt] = NULL;
22488355f576SJeff Roberson #endif
2249fc03d22bSJeff Roberson 		KASSERT(item != NULL, ("uma_zalloc: Bucket pointer mangled."));
22508355f576SJeff Roberson 		cache->uc_allocs++;
22515d1ae027SRobert Watson 		critical_exit();
2252fc03d22bSJeff Roberson 		if (zone->uz_ctor != NULL &&
2253fc03d22bSJeff Roberson 		    zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) {
22540095a784SJeff Roberson 			atomic_add_long(&zone->uz_fails, 1);
2255fc03d22bSJeff Roberson 			zone_free_item(zone, item, udata, SKIP_DTOR);
2256b23f72e9SBrian Feldman 			return (NULL);
2257b23f72e9SBrian Feldman 		}
2258ef72505eSJeff Roberson #ifdef INVARIANTS
2259ef72505eSJeff Roberson 		uma_dbg_alloc(zone, NULL, item);
2260ef72505eSJeff Roberson #endif
22612cc35ff9SJeff Roberson 		if (flags & M_ZERO)
226248343a2fSGleb Smirnoff 			uma_zero_item(item, zone);
22638355f576SJeff Roberson 		return (item);
2264fc03d22bSJeff Roberson 	}
2265fc03d22bSJeff Roberson 
22668355f576SJeff Roberson 	/*
22678355f576SJeff Roberson 	 * We have run out of items in our alloc bucket.
22688355f576SJeff Roberson 	 * See if we can switch with our free bucket.
22698355f576SJeff Roberson 	 */
2270b983089aSJeff Roberson 	bucket = cache->uc_freebucket;
2271fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt > 0) {
22721431a748SGleb Smirnoff 		CTR2(KTR_UMA,
22731431a748SGleb Smirnoff 		    "uma_zalloc: zone %s(%p) swapping empty with alloc",
22741431a748SGleb Smirnoff 		    zone->uz_name, zone);
22758355f576SJeff Roberson 		cache->uc_freebucket = cache->uc_allocbucket;
2276b983089aSJeff Roberson 		cache->uc_allocbucket = bucket;
22778355f576SJeff Roberson 		goto zalloc_start;
22788355f576SJeff Roberson 	}
2279fc03d22bSJeff Roberson 
2280fc03d22bSJeff Roberson 	/*
2281fc03d22bSJeff Roberson 	 * Discard any empty allocation bucket while we hold no locks.
2282fc03d22bSJeff Roberson 	 */
2283fc03d22bSJeff Roberson 	bucket = cache->uc_allocbucket;
2284fc03d22bSJeff Roberson 	cache->uc_allocbucket = NULL;
2285fc03d22bSJeff Roberson 	critical_exit();
2286fc03d22bSJeff Roberson 	if (bucket != NULL)
22876fd34d6fSJeff Roberson 		bucket_free(zone, bucket, udata);
2288fc03d22bSJeff Roberson 
2289ab3185d1SJeff Roberson 	if (zone->uz_flags & UMA_ZONE_NUMA)
2290ab3185d1SJeff Roberson 		domain = PCPU_GET(domain);
2291ab3185d1SJeff Roberson 	else
2292ab3185d1SJeff Roberson 		domain = UMA_ANYDOMAIN;
2293ab3185d1SJeff Roberson 
2294fc03d22bSJeff Roberson 	/* Short-circuit for zones without buckets and low memory. */
2295fc03d22bSJeff Roberson 	if (zone->uz_count == 0 || bucketdisable)
2296fc03d22bSJeff Roberson 		goto zalloc_item;
2297fc03d22bSJeff Roberson 
22985d1ae027SRobert Watson 	/*
22995d1ae027SRobert Watson 	 * Attempt to retrieve the item from the per-CPU cache has failed, so
23005d1ae027SRobert Watson 	 * we must go back to the zone.  This requires the zone lock, so we
23015d1ae027SRobert Watson 	 * must drop the critical section, then re-acquire it when we go back
23025d1ae027SRobert Watson 	 * to the cache.  Since the critical section is released, we may be
23035d1ae027SRobert Watson 	 * preempted or migrate.  As such, make sure not to maintain any
23045d1ae027SRobert Watson 	 * thread-local state specific to the cache from prior to releasing
23055d1ae027SRobert Watson 	 * the critical section.
23065d1ae027SRobert Watson 	 */
2307fc03d22bSJeff Roberson 	lockfail = 0;
2308fc03d22bSJeff Roberson 	if (ZONE_TRYLOCK(zone) == 0) {
2309fc03d22bSJeff Roberson 		/* Record contention to size the buckets. */
2310a553d4b8SJeff Roberson 		ZONE_LOCK(zone);
2311fc03d22bSJeff Roberson 		lockfail = 1;
2312fc03d22bSJeff Roberson 	}
23135d1ae027SRobert Watson 	critical_enter();
23145d1ae027SRobert Watson 	cpu = curcpu;
23155d1ae027SRobert Watson 	cache = &zone->uz_cpu[cpu];
23165d1ae027SRobert Watson 
2317fc03d22bSJeff Roberson 	/*
2318fc03d22bSJeff Roberson 	 * Since we have locked the zone we may as well send back our stats.
2319fc03d22bSJeff Roberson 	 */
23200095a784SJeff Roberson 	atomic_add_long(&zone->uz_allocs, cache->uc_allocs);
23210095a784SJeff Roberson 	atomic_add_long(&zone->uz_frees, cache->uc_frees);
2322a553d4b8SJeff Roberson 	cache->uc_allocs = 0;
2323773df9abSRobert Watson 	cache->uc_frees = 0;
23248355f576SJeff Roberson 
2325fc03d22bSJeff Roberson 	/* See if we lost the race to fill the cache. */
2326fc03d22bSJeff Roberson 	if (cache->uc_allocbucket != NULL) {
2327fc03d22bSJeff Roberson 		ZONE_UNLOCK(zone);
2328fc03d22bSJeff Roberson 		goto zalloc_start;
2329a553d4b8SJeff Roberson 	}
23308355f576SJeff Roberson 
2331fc03d22bSJeff Roberson 	/*
2332fc03d22bSJeff Roberson 	 * Check the zone's cache of buckets.
2333fc03d22bSJeff Roberson 	 */
2334ab3185d1SJeff Roberson 	if (domain == UMA_ANYDOMAIN)
2335ab3185d1SJeff Roberson 		zdom = &zone->uz_domain[0];
2336ab3185d1SJeff Roberson 	else
2337ab3185d1SJeff Roberson 		zdom = &zone->uz_domain[domain];
2338ab3185d1SJeff Roberson 	if ((bucket = LIST_FIRST(&zdom->uzd_buckets)) != NULL) {
2339cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt != 0,
2340a553d4b8SJeff Roberson 		    ("uma_zalloc_arg: Returning an empty bucket."));
23418355f576SJeff Roberson 
2342a553d4b8SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
2343a553d4b8SJeff Roberson 		cache->uc_allocbucket = bucket;
2344a553d4b8SJeff Roberson 		ZONE_UNLOCK(zone);
23458355f576SJeff Roberson 		goto zalloc_start;
2346a553d4b8SJeff Roberson 	}
23475d1ae027SRobert Watson 	/* We are no longer associated with this CPU. */
23485d1ae027SRobert Watson 	critical_exit();
2349bbee39c6SJeff Roberson 
2350fc03d22bSJeff Roberson 	/*
2351fc03d22bSJeff Roberson 	 * We bump the uz count when the cache size is insufficient to
2352fc03d22bSJeff Roberson 	 * handle the working set.
2353fc03d22bSJeff Roberson 	 */
23546fd34d6fSJeff Roberson 	if (lockfail && zone->uz_count < BUCKET_MAX)
2355a553d4b8SJeff Roberson 		zone->uz_count++;
2356fc03d22bSJeff Roberson 	ZONE_UNLOCK(zone);
2357099a0e58SBosko Milekic 
23588355f576SJeff Roberson 	/*
2359a553d4b8SJeff Roberson 	 * Now lets just fill a bucket and put it on the free list.  If that
2360763df3ecSPedro F. Giffuni 	 * works we'll restart the allocation from the beginning and it
2361fc03d22bSJeff Roberson 	 * will use the just filled bucket.
2362bbee39c6SJeff Roberson 	 */
2363ab3185d1SJeff Roberson 	bucket = zone_alloc_bucket(zone, udata, domain, flags);
23641431a748SGleb Smirnoff 	CTR3(KTR_UMA, "uma_zalloc: zone %s(%p) bucket zone returned %p",
23651431a748SGleb Smirnoff 	    zone->uz_name, zone, bucket);
2366fc03d22bSJeff Roberson 	if (bucket != NULL) {
2367fc03d22bSJeff Roberson 		ZONE_LOCK(zone);
2368fc03d22bSJeff Roberson 		critical_enter();
2369fc03d22bSJeff Roberson 		cpu = curcpu;
2370fc03d22bSJeff Roberson 		cache = &zone->uz_cpu[cpu];
2371fc03d22bSJeff Roberson 		/*
2372fc03d22bSJeff Roberson 		 * See if we lost the race or were migrated.  Cache the
2373fc03d22bSJeff Roberson 		 * initialized bucket to make this less likely or claim
2374fc03d22bSJeff Roberson 		 * the memory directly.
2375fc03d22bSJeff Roberson 		 */
2376ab3185d1SJeff Roberson 		if (cache->uc_allocbucket != NULL ||
2377ab3185d1SJeff Roberson 		    (zone->uz_flags & UMA_ZONE_NUMA &&
2378ab3185d1SJeff Roberson 		    domain != PCPU_GET(domain)))
2379ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zdom->uzd_buckets, bucket, ub_link);
2380fc03d22bSJeff Roberson 		else
2381ab3185d1SJeff Roberson 			cache->uc_allocbucket = bucket;
2382bbee39c6SJeff Roberson 		ZONE_UNLOCK(zone);
2383fc03d22bSJeff Roberson 		goto zalloc_start;
2384bbee39c6SJeff Roberson 	}
2385fc03d22bSJeff Roberson 
2386bbee39c6SJeff Roberson 	/*
2387bbee39c6SJeff Roberson 	 * We may not be able to get a bucket so return an actual item.
2388bbee39c6SJeff Roberson 	 */
2389fc03d22bSJeff Roberson zalloc_item:
2390ab3185d1SJeff Roberson 	item = zone_alloc_item(zone, udata, domain, flags);
2391fc03d22bSJeff Roberson 
2392e20a199fSJeff Roberson 	return (item);
2393bbee39c6SJeff Roberson }
2394bbee39c6SJeff Roberson 
2395ab3185d1SJeff Roberson void *
2396ab3185d1SJeff Roberson uma_zalloc_domain(uma_zone_t zone, void *udata, int domain, int flags)
2397bbee39c6SJeff Roberson {
2398ab3185d1SJeff Roberson 
2399ab3185d1SJeff Roberson 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
2400ab3185d1SJeff Roberson 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
2401ab3185d1SJeff Roberson 
2402ab3185d1SJeff Roberson 	/* This is the fast path allocation */
2403ab3185d1SJeff Roberson 	CTR5(KTR_UMA,
2404ab3185d1SJeff Roberson 	    "uma_zalloc_domain thread %x zone %s(%p) domain %d flags %d",
2405ab3185d1SJeff Roberson 	    curthread, zone->uz_name, zone, domain, flags);
2406ab3185d1SJeff Roberson 
2407ab3185d1SJeff Roberson 	if (flags & M_WAITOK) {
2408ab3185d1SJeff Roberson 		WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
2409ab3185d1SJeff Roberson 		    "uma_zalloc_domain: zone \"%s\"", zone->uz_name);
2410ab3185d1SJeff Roberson 	}
2411ab3185d1SJeff Roberson 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
2412ab3185d1SJeff Roberson 	    ("uma_zalloc_domain: called with spinlock or critical section held"));
2413ab3185d1SJeff Roberson 
2414ab3185d1SJeff Roberson 	return (zone_alloc_item(zone, udata, domain, flags));
2415ab3185d1SJeff Roberson }
2416ab3185d1SJeff Roberson 
2417ab3185d1SJeff Roberson /*
2418ab3185d1SJeff Roberson  * Find a slab with some space.  Prefer slabs that are partially used over those
2419ab3185d1SJeff Roberson  * that are totally full.  This helps to reduce fragmentation.
2420ab3185d1SJeff Roberson  *
2421ab3185d1SJeff Roberson  * If 'rr' is 1, search all domains starting from 'domain'.  Otherwise check
2422ab3185d1SJeff Roberson  * only 'domain'.
2423ab3185d1SJeff Roberson  */
2424ab3185d1SJeff Roberson static uma_slab_t
2425ab3185d1SJeff Roberson keg_first_slab(uma_keg_t keg, int domain, int rr)
2426ab3185d1SJeff Roberson {
2427ab3185d1SJeff Roberson 	uma_domain_t dom;
2428bbee39c6SJeff Roberson 	uma_slab_t slab;
2429ab3185d1SJeff Roberson 	int start;
2430ab3185d1SJeff Roberson 
2431ab3185d1SJeff Roberson 	KASSERT(domain >= 0 && domain < vm_ndomains,
2432ab3185d1SJeff Roberson 	    ("keg_first_slab: domain %d out of range", domain));
2433ab3185d1SJeff Roberson 
2434ab3185d1SJeff Roberson 	slab = NULL;
2435ab3185d1SJeff Roberson 	start = domain;
2436ab3185d1SJeff Roberson 	do {
2437ab3185d1SJeff Roberson 		dom = &keg->uk_domain[domain];
2438ab3185d1SJeff Roberson 		if (!LIST_EMPTY(&dom->ud_part_slab))
2439ab3185d1SJeff Roberson 			return (LIST_FIRST(&dom->ud_part_slab));
2440ab3185d1SJeff Roberson 		if (!LIST_EMPTY(&dom->ud_free_slab)) {
2441ab3185d1SJeff Roberson 			slab = LIST_FIRST(&dom->ud_free_slab);
2442ab3185d1SJeff Roberson 			LIST_REMOVE(slab, us_link);
2443ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link);
2444ab3185d1SJeff Roberson 			return (slab);
2445ab3185d1SJeff Roberson 		}
2446ab3185d1SJeff Roberson 		if (rr)
2447ab3185d1SJeff Roberson 			domain = (domain + 1) % vm_ndomains;
2448ab3185d1SJeff Roberson 	} while (domain != start);
2449ab3185d1SJeff Roberson 
2450ab3185d1SJeff Roberson 	return (NULL);
2451ab3185d1SJeff Roberson }
2452ab3185d1SJeff Roberson 
2453ab3185d1SJeff Roberson static uma_slab_t
2454ab3185d1SJeff Roberson keg_fetch_slab(uma_keg_t keg, uma_zone_t zone, int rdomain, int flags)
2455ab3185d1SJeff Roberson {
2456ab3185d1SJeff Roberson 	uma_domain_t dom;
2457ab3185d1SJeff Roberson 	uma_slab_t slab;
2458ab3185d1SJeff Roberson 	int allocflags, domain, reserve, rr, start;
2459099a0e58SBosko Milekic 
2460e20a199fSJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
2461bbee39c6SJeff Roberson 	slab = NULL;
24626fd34d6fSJeff Roberson 	reserve = 0;
2463ab3185d1SJeff Roberson 	allocflags = flags;
24646fd34d6fSJeff Roberson 	if ((flags & M_USE_RESERVE) == 0)
24656fd34d6fSJeff Roberson 		reserve = keg->uk_reserve;
2466bbee39c6SJeff Roberson 
2467bbee39c6SJeff Roberson 	/*
2468ab3185d1SJeff Roberson 	 * Round-robin for non first-touch zones when there is more than one
2469ab3185d1SJeff Roberson 	 * domain.
2470bbee39c6SJeff Roberson 	 */
2471ab3185d1SJeff Roberson 	if (vm_ndomains == 1)
2472ab3185d1SJeff Roberson 		rdomain = 0;
2473ab3185d1SJeff Roberson 	rr = rdomain == UMA_ANYDOMAIN;
2474ab3185d1SJeff Roberson 	if (rr) {
2475ab3185d1SJeff Roberson 		keg->uk_cursor = (keg->uk_cursor + 1) % vm_ndomains;
2476ab3185d1SJeff Roberson 		domain = start = keg->uk_cursor;
2477ab3185d1SJeff Roberson 		/* Only block on the second pass. */
2478ab3185d1SJeff Roberson 		if ((flags & (M_WAITOK | M_NOVM)) == M_WAITOK)
2479ab3185d1SJeff Roberson 			allocflags = (allocflags & ~M_WAITOK) | M_NOWAIT;
2480ab3185d1SJeff Roberson 	} else
2481ab3185d1SJeff Roberson 		domain = start = rdomain;
2482ab3185d1SJeff Roberson 
2483ab3185d1SJeff Roberson again:
2484ab3185d1SJeff Roberson 	do {
2485ab3185d1SJeff Roberson 		if (keg->uk_free > reserve &&
2486ab3185d1SJeff Roberson 		    (slab = keg_first_slab(keg, domain, rr)) != NULL) {
2487e20a199fSJeff Roberson 			MPASS(slab->us_keg == keg);
2488bbee39c6SJeff Roberson 			return (slab);
2489bbee39c6SJeff Roberson 		}
2490bbee39c6SJeff Roberson 
2491bbee39c6SJeff Roberson 		/*
2492bbee39c6SJeff Roberson 		 * M_NOVM means don't ask at all!
2493bbee39c6SJeff Roberson 		 */
2494bbee39c6SJeff Roberson 		if (flags & M_NOVM)
2495bbee39c6SJeff Roberson 			break;
2496bbee39c6SJeff Roberson 
2497e20a199fSJeff Roberson 		if (keg->uk_maxpages && keg->uk_pages >= keg->uk_maxpages) {
2498099a0e58SBosko Milekic 			keg->uk_flags |= UMA_ZFLAG_FULL;
2499e20a199fSJeff Roberson 			/*
2500e20a199fSJeff Roberson 			 * If this is not a multi-zone, set the FULL bit.
2501e20a199fSJeff Roberson 			 * Otherwise slab_multi() takes care of it.
2502e20a199fSJeff Roberson 			 */
25032f891cd5SPawel Jakub Dawidek 			if ((zone->uz_flags & UMA_ZFLAG_MULTI) == 0) {
2504e20a199fSJeff Roberson 				zone->uz_flags |= UMA_ZFLAG_FULL;
25052f891cd5SPawel Jakub Dawidek 				zone_log_warning(zone);
250654503a13SJonathan T. Looney 				zone_maxaction(zone);
25072f891cd5SPawel Jakub Dawidek 			}
2508ebc85edfSJeff Roberson 			if (flags & M_NOWAIT)
2509ab3185d1SJeff Roberson 				return (NULL);
2510c288b548SEitan Adler 			zone->uz_sleeps++;
2511e20a199fSJeff Roberson 			msleep(keg, &keg->uk_lock, PVM, "keglimit", 0);
2512bbee39c6SJeff Roberson 			continue;
2513bbee39c6SJeff Roberson 		}
2514ab3185d1SJeff Roberson 		slab = keg_alloc_slab(keg, zone, domain, allocflags);
2515bbee39c6SJeff Roberson 		/*
2516bbee39c6SJeff Roberson 		 * If we got a slab here it's safe to mark it partially used
2517bbee39c6SJeff Roberson 		 * and return.  We assume that the caller is going to remove
2518bbee39c6SJeff Roberson 		 * at least one item.
2519bbee39c6SJeff Roberson 		 */
2520bbee39c6SJeff Roberson 		if (slab) {
2521e20a199fSJeff Roberson 			MPASS(slab->us_keg == keg);
2522ab3185d1SJeff Roberson 			dom = &keg->uk_domain[slab->us_domain];
2523ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link);
2524bbee39c6SJeff Roberson 			return (slab);
2525bbee39c6SJeff Roberson 		}
2526ab3185d1SJeff Roberson 		if (rr) {
2527ab3185d1SJeff Roberson 			keg->uk_cursor = (keg->uk_cursor + 1) % vm_ndomains;
2528ab3185d1SJeff Roberson 			domain = keg->uk_cursor;
2529ab3185d1SJeff Roberson 		}
2530ab3185d1SJeff Roberson 	} while (domain != start);
2531ab3185d1SJeff Roberson 
2532ab3185d1SJeff Roberson 	/* Retry domain scan with blocking. */
2533ab3185d1SJeff Roberson 	if (allocflags != flags) {
2534ab3185d1SJeff Roberson 		allocflags = flags;
2535ab3185d1SJeff Roberson 		goto again;
2536ab3185d1SJeff Roberson 	}
2537ab3185d1SJeff Roberson 
2538bbee39c6SJeff Roberson 	/*
2539bbee39c6SJeff Roberson 	 * We might not have been able to get a slab but another cpu
2540bbee39c6SJeff Roberson 	 * could have while we were unlocked.  Check again before we
2541bbee39c6SJeff Roberson 	 * fail.
2542bbee39c6SJeff Roberson 	 */
2543ab3185d1SJeff Roberson 	if (keg->uk_free > reserve &&
2544ab3185d1SJeff Roberson 	    (slab = keg_first_slab(keg, domain, rr)) != NULL) {
2545ab3185d1SJeff Roberson 		MPASS(slab->us_keg == keg);
2546bbee39c6SJeff Roberson 		return (slab);
2547bbee39c6SJeff Roberson 	}
2548ab3185d1SJeff Roberson 	return (NULL);
2549ab3185d1SJeff Roberson }
2550bbee39c6SJeff Roberson 
2551e20a199fSJeff Roberson static uma_slab_t
2552ab3185d1SJeff Roberson zone_fetch_slab(uma_zone_t zone, uma_keg_t keg, int domain, int flags)
2553e20a199fSJeff Roberson {
2554e20a199fSJeff Roberson 	uma_slab_t slab;
2555e20a199fSJeff Roberson 
2556af526374SJeff Roberson 	if (keg == NULL) {
2557e20a199fSJeff Roberson 		keg = zone_first_keg(zone);
2558af526374SJeff Roberson 		KEG_LOCK(keg);
2559af526374SJeff Roberson 	}
2560e20a199fSJeff Roberson 
2561e20a199fSJeff Roberson 	for (;;) {
2562ab3185d1SJeff Roberson 		slab = keg_fetch_slab(keg, zone, domain, flags);
2563e20a199fSJeff Roberson 		if (slab)
2564e20a199fSJeff Roberson 			return (slab);
2565e20a199fSJeff Roberson 		if (flags & (M_NOWAIT | M_NOVM))
2566e20a199fSJeff Roberson 			break;
2567e20a199fSJeff Roberson 	}
2568af526374SJeff Roberson 	KEG_UNLOCK(keg);
2569e20a199fSJeff Roberson 	return (NULL);
2570e20a199fSJeff Roberson }
2571e20a199fSJeff Roberson 
2572e20a199fSJeff Roberson /*
2573e20a199fSJeff Roberson  * uma_zone_fetch_slab_multi:  Fetches a slab from one available keg.  Returns
2574af526374SJeff Roberson  * with the keg locked.  On NULL no lock is held.
2575e20a199fSJeff Roberson  *
2576e20a199fSJeff Roberson  * The last pointer is used to seed the search.  It is not required.
2577e20a199fSJeff Roberson  */
2578e20a199fSJeff Roberson static uma_slab_t
2579ab3185d1SJeff Roberson zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int domain, int rflags)
2580e20a199fSJeff Roberson {
2581e20a199fSJeff Roberson 	uma_klink_t klink;
2582e20a199fSJeff Roberson 	uma_slab_t slab;
2583e20a199fSJeff Roberson 	uma_keg_t keg;
2584e20a199fSJeff Roberson 	int flags;
2585e20a199fSJeff Roberson 	int empty;
2586e20a199fSJeff Roberson 	int full;
2587e20a199fSJeff Roberson 
2588e20a199fSJeff Roberson 	/*
2589e20a199fSJeff Roberson 	 * Don't wait on the first pass.  This will skip limit tests
2590e20a199fSJeff Roberson 	 * as well.  We don't want to block if we can find a provider
2591e20a199fSJeff Roberson 	 * without blocking.
2592e20a199fSJeff Roberson 	 */
2593e20a199fSJeff Roberson 	flags = (rflags & ~M_WAITOK) | M_NOWAIT;
2594e20a199fSJeff Roberson 	/*
2595e20a199fSJeff Roberson 	 * Use the last slab allocated as a hint for where to start
2596e20a199fSJeff Roberson 	 * the search.
2597e20a199fSJeff Roberson 	 */
2598af526374SJeff Roberson 	if (last != NULL) {
2599ab3185d1SJeff Roberson 		slab = keg_fetch_slab(last, zone, domain, flags);
2600e20a199fSJeff Roberson 		if (slab)
2601e20a199fSJeff Roberson 			return (slab);
2602af526374SJeff Roberson 		KEG_UNLOCK(last);
2603e20a199fSJeff Roberson 	}
2604e20a199fSJeff Roberson 	/*
2605e20a199fSJeff Roberson 	 * Loop until we have a slab incase of transient failures
2606e20a199fSJeff Roberson 	 * while M_WAITOK is specified.  I'm not sure this is 100%
2607e20a199fSJeff Roberson 	 * required but we've done it for so long now.
2608e20a199fSJeff Roberson 	 */
2609e20a199fSJeff Roberson 	for (;;) {
2610e20a199fSJeff Roberson 		empty = 0;
2611e20a199fSJeff Roberson 		full = 0;
2612e20a199fSJeff Roberson 		/*
2613e20a199fSJeff Roberson 		 * Search the available kegs for slabs.  Be careful to hold the
2614e20a199fSJeff Roberson 		 * correct lock while calling into the keg layer.
2615e20a199fSJeff Roberson 		 */
2616e20a199fSJeff Roberson 		LIST_FOREACH(klink, &zone->uz_kegs, kl_link) {
2617e20a199fSJeff Roberson 			keg = klink->kl_keg;
2618af526374SJeff Roberson 			KEG_LOCK(keg);
2619e20a199fSJeff Roberson 			if ((keg->uk_flags & UMA_ZFLAG_FULL) == 0) {
2620ab3185d1SJeff Roberson 				slab = keg_fetch_slab(keg, zone, domain, flags);
2621e20a199fSJeff Roberson 				if (slab)
2622e20a199fSJeff Roberson 					return (slab);
2623e20a199fSJeff Roberson 			}
2624e20a199fSJeff Roberson 			if (keg->uk_flags & UMA_ZFLAG_FULL)
2625e20a199fSJeff Roberson 				full++;
2626e20a199fSJeff Roberson 			else
2627e20a199fSJeff Roberson 				empty++;
2628af526374SJeff Roberson 			KEG_UNLOCK(keg);
2629e20a199fSJeff Roberson 		}
2630e20a199fSJeff Roberson 		if (rflags & (M_NOWAIT | M_NOVM))
2631e20a199fSJeff Roberson 			break;
2632e20a199fSJeff Roberson 		flags = rflags;
2633e20a199fSJeff Roberson 		/*
2634e20a199fSJeff Roberson 		 * All kegs are full.  XXX We can't atomically check all kegs
2635e20a199fSJeff Roberson 		 * and sleep so just sleep for a short period and retry.
2636e20a199fSJeff Roberson 		 */
2637e20a199fSJeff Roberson 		if (full && !empty) {
2638af526374SJeff Roberson 			ZONE_LOCK(zone);
2639e20a199fSJeff Roberson 			zone->uz_flags |= UMA_ZFLAG_FULL;
2640bf965959SSean Bruno 			zone->uz_sleeps++;
26412f891cd5SPawel Jakub Dawidek 			zone_log_warning(zone);
264254503a13SJonathan T. Looney 			zone_maxaction(zone);
2643af526374SJeff Roberson 			msleep(zone, zone->uz_lockptr, PVM,
2644af526374SJeff Roberson 			    "zonelimit", hz/100);
2645e20a199fSJeff Roberson 			zone->uz_flags &= ~UMA_ZFLAG_FULL;
2646af526374SJeff Roberson 			ZONE_UNLOCK(zone);
2647e20a199fSJeff Roberson 			continue;
2648e20a199fSJeff Roberson 		}
2649e20a199fSJeff Roberson 	}
2650e20a199fSJeff Roberson 	return (NULL);
2651e20a199fSJeff Roberson }
2652e20a199fSJeff Roberson 
2653d56368d7SBosko Milekic static void *
26540095a784SJeff Roberson slab_alloc_item(uma_keg_t keg, uma_slab_t slab)
2655bbee39c6SJeff Roberson {
2656ab3185d1SJeff Roberson 	uma_domain_t dom;
2657bbee39c6SJeff Roberson 	void *item;
265885dcf349SGleb Smirnoff 	uint8_t freei;
2659bbee39c6SJeff Roberson 
26600095a784SJeff Roberson 	MPASS(keg == slab->us_keg);
2661e20a199fSJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
2662099a0e58SBosko Milekic 
2663ef72505eSJeff Roberson 	freei = BIT_FFS(SLAB_SETSIZE, &slab->us_free) - 1;
2664ef72505eSJeff Roberson 	BIT_CLR(SLAB_SETSIZE, freei, &slab->us_free);
2665099a0e58SBosko Milekic 	item = slab->us_data + (keg->uk_rsize * freei);
2666bbee39c6SJeff Roberson 	slab->us_freecount--;
2667099a0e58SBosko Milekic 	keg->uk_free--;
2668ef72505eSJeff Roberson 
2669bbee39c6SJeff Roberson 	/* Move this slab to the full list */
2670bbee39c6SJeff Roberson 	if (slab->us_freecount == 0) {
2671bbee39c6SJeff Roberson 		LIST_REMOVE(slab, us_link);
2672ab3185d1SJeff Roberson 		dom = &keg->uk_domain[slab->us_domain];
2673ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_full_slab, slab, us_link);
2674bbee39c6SJeff Roberson 	}
2675bbee39c6SJeff Roberson 
2676bbee39c6SJeff Roberson 	return (item);
2677bbee39c6SJeff Roberson }
2678bbee39c6SJeff Roberson 
2679bbee39c6SJeff Roberson static int
2680ab3185d1SJeff Roberson zone_import(uma_zone_t zone, void **bucket, int max, int domain, int flags)
26810095a784SJeff Roberson {
26820095a784SJeff Roberson 	uma_slab_t slab;
26830095a784SJeff Roberson 	uma_keg_t keg;
2684ab3185d1SJeff Roberson 	int stripe;
26850095a784SJeff Roberson 	int i;
26860095a784SJeff Roberson 
26870095a784SJeff Roberson 	slab = NULL;
26880095a784SJeff Roberson 	keg = NULL;
2689af526374SJeff Roberson 	/* Try to keep the buckets totally full */
26900095a784SJeff Roberson 	for (i = 0; i < max; ) {
2691ab3185d1SJeff Roberson 		if ((slab = zone->uz_slab(zone, keg, domain, flags)) == NULL)
26920095a784SJeff Roberson 			break;
26930095a784SJeff Roberson 		keg = slab->us_keg;
2694ab3185d1SJeff Roberson 		stripe = howmany(max, vm_ndomains);
26956fd34d6fSJeff Roberson 		while (slab->us_freecount && i < max) {
26960095a784SJeff Roberson 			bucket[i++] = slab_alloc_item(keg, slab);
26976fd34d6fSJeff Roberson 			if (keg->uk_free <= keg->uk_reserve)
26986fd34d6fSJeff Roberson 				break;
2699b6715dabSJeff Roberson #ifdef NUMA
2700ab3185d1SJeff Roberson 			/*
2701ab3185d1SJeff Roberson 			 * If the zone is striped we pick a new slab for every
2702ab3185d1SJeff Roberson 			 * N allocations.  Eliminating this conditional will
2703ab3185d1SJeff Roberson 			 * instead pick a new domain for each bucket rather
2704ab3185d1SJeff Roberson 			 * than stripe within each bucket.  The current option
2705ab3185d1SJeff Roberson 			 * produces more fragmentation and requires more cpu
2706ab3185d1SJeff Roberson 			 * time but yields better distribution.
2707ab3185d1SJeff Roberson 			 */
2708ab3185d1SJeff Roberson 			if ((zone->uz_flags & UMA_ZONE_NUMA) == 0 &&
2709ab3185d1SJeff Roberson 			    vm_ndomains > 1 && --stripe == 0)
2710ab3185d1SJeff Roberson 				break;
2711ab3185d1SJeff Roberson #endif
27126fd34d6fSJeff Roberson 		}
2713ab3185d1SJeff Roberson 		/* Don't block if we allocated any successfully. */
27140095a784SJeff Roberson 		flags &= ~M_WAITOK;
27150095a784SJeff Roberson 		flags |= M_NOWAIT;
27160095a784SJeff Roberson 	}
27170095a784SJeff Roberson 	if (slab != NULL)
27180095a784SJeff Roberson 		KEG_UNLOCK(keg);
27190095a784SJeff Roberson 
27200095a784SJeff Roberson 	return i;
27210095a784SJeff Roberson }
27220095a784SJeff Roberson 
2723fc03d22bSJeff Roberson static uma_bucket_t
2724ab3185d1SJeff Roberson zone_alloc_bucket(uma_zone_t zone, void *udata, int domain, int flags)
2725bbee39c6SJeff Roberson {
2726bbee39c6SJeff Roberson 	uma_bucket_t bucket;
27270095a784SJeff Roberson 	int max;
2728bbee39c6SJeff Roberson 
27296fd34d6fSJeff Roberson 	/* Don't wait for buckets, preserve caller's NOVM setting. */
27306fd34d6fSJeff Roberson 	bucket = bucket_alloc(zone, udata, M_NOWAIT | (flags & M_NOVM));
27310095a784SJeff Roberson 	if (bucket == NULL)
2732f7104ccdSAlexander Motin 		return (NULL);
27330095a784SJeff Roberson 
2734af526374SJeff Roberson 	max = MIN(bucket->ub_entries, zone->uz_count);
27350095a784SJeff Roberson 	bucket->ub_cnt = zone->uz_import(zone->uz_arg, bucket->ub_bucket,
2736ab3185d1SJeff Roberson 	    max, domain, flags);
27370095a784SJeff Roberson 
27380095a784SJeff Roberson 	/*
27390095a784SJeff Roberson 	 * Initialize the memory if necessary.
27400095a784SJeff Roberson 	 */
27410095a784SJeff Roberson 	if (bucket->ub_cnt != 0 && zone->uz_init != NULL) {
2742099a0e58SBosko Milekic 		int i;
2743bbee39c6SJeff Roberson 
27440095a784SJeff Roberson 		for (i = 0; i < bucket->ub_cnt; i++)
2745e20a199fSJeff Roberson 			if (zone->uz_init(bucket->ub_bucket[i], zone->uz_size,
27460095a784SJeff Roberson 			    flags) != 0)
2747b23f72e9SBrian Feldman 				break;
2748b23f72e9SBrian Feldman 		/*
2749b23f72e9SBrian Feldman 		 * If we couldn't initialize the whole bucket, put the
2750b23f72e9SBrian Feldman 		 * rest back onto the freelist.
2751b23f72e9SBrian Feldman 		 */
2752b23f72e9SBrian Feldman 		if (i != bucket->ub_cnt) {
2753af526374SJeff Roberson 			zone->uz_release(zone->uz_arg, &bucket->ub_bucket[i],
27540095a784SJeff Roberson 			    bucket->ub_cnt - i);
2755a5a262c6SBosko Milekic #ifdef INVARIANTS
27560095a784SJeff Roberson 			bzero(&bucket->ub_bucket[i],
27570095a784SJeff Roberson 			    sizeof(void *) * (bucket->ub_cnt - i));
2758a5a262c6SBosko Milekic #endif
2759b23f72e9SBrian Feldman 			bucket->ub_cnt = i;
2760b23f72e9SBrian Feldman 		}
2761099a0e58SBosko Milekic 	}
2762099a0e58SBosko Milekic 
2763f7104ccdSAlexander Motin 	if (bucket->ub_cnt == 0) {
27646fd34d6fSJeff Roberson 		bucket_free(zone, bucket, udata);
2765fc03d22bSJeff Roberson 		atomic_add_long(&zone->uz_fails, 1);
2766fc03d22bSJeff Roberson 		return (NULL);
2767bbee39c6SJeff Roberson 	}
2768fc03d22bSJeff Roberson 
2769fc03d22bSJeff Roberson 	return (bucket);
2770fc03d22bSJeff Roberson }
2771fc03d22bSJeff Roberson 
27728355f576SJeff Roberson /*
27730095a784SJeff Roberson  * Allocates a single item from a zone.
27748355f576SJeff Roberson  *
27758355f576SJeff Roberson  * Arguments
27768355f576SJeff Roberson  *	zone   The zone to alloc for.
27778355f576SJeff Roberson  *	udata  The data to be passed to the constructor.
2778ab3185d1SJeff Roberson  *	domain The domain to allocate from or UMA_ANYDOMAIN.
2779a163d034SWarner Losh  *	flags  M_WAITOK, M_NOWAIT, M_ZERO.
27808355f576SJeff Roberson  *
27818355f576SJeff Roberson  * Returns
27828355f576SJeff Roberson  *	NULL if there is no memory and M_NOWAIT is set
2783bbee39c6SJeff Roberson  *	An item if successful
27848355f576SJeff Roberson  */
27858355f576SJeff Roberson 
27868355f576SJeff Roberson static void *
2787ab3185d1SJeff Roberson zone_alloc_item(uma_zone_t zone, void *udata, int domain, int flags)
27888355f576SJeff Roberson {
27898355f576SJeff Roberson 	void *item;
27908355f576SJeff Roberson 
27918355f576SJeff Roberson 	item = NULL;
27928355f576SJeff Roberson 
2793ab3185d1SJeff Roberson 	if (zone->uz_import(zone->uz_arg, &item, 1, domain, flags) != 1)
27940095a784SJeff Roberson 		goto fail;
27950095a784SJeff Roberson 	atomic_add_long(&zone->uz_allocs, 1);
27968355f576SJeff Roberson 
2797099a0e58SBosko Milekic 	/*
2798099a0e58SBosko Milekic 	 * We have to call both the zone's init (not the keg's init)
2799099a0e58SBosko Milekic 	 * and the zone's ctor.  This is because the item is going from
2800099a0e58SBosko Milekic 	 * a keg slab directly to the user, and the user is expecting it
2801099a0e58SBosko Milekic 	 * to be both zone-init'd as well as zone-ctor'd.
2802099a0e58SBosko Milekic 	 */
2803b23f72e9SBrian Feldman 	if (zone->uz_init != NULL) {
2804e20a199fSJeff Roberson 		if (zone->uz_init(item, zone->uz_size, flags) != 0) {
28050095a784SJeff Roberson 			zone_free_item(zone, item, udata, SKIP_FINI);
28060095a784SJeff Roberson 			goto fail;
2807b23f72e9SBrian Feldman 		}
2808b23f72e9SBrian Feldman 	}
2809b23f72e9SBrian Feldman 	if (zone->uz_ctor != NULL) {
2810e20a199fSJeff Roberson 		if (zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) {
28110095a784SJeff Roberson 			zone_free_item(zone, item, udata, SKIP_DTOR);
28120095a784SJeff Roberson 			goto fail;
2813b23f72e9SBrian Feldman 		}
2814b23f72e9SBrian Feldman 	}
2815ef72505eSJeff Roberson #ifdef INVARIANTS
28160095a784SJeff Roberson 	uma_dbg_alloc(zone, NULL, item);
2817ef72505eSJeff Roberson #endif
28182cc35ff9SJeff Roberson 	if (flags & M_ZERO)
281948343a2fSGleb Smirnoff 		uma_zero_item(item, zone);
28208355f576SJeff Roberson 
28211431a748SGleb Smirnoff 	CTR3(KTR_UMA, "zone_alloc_item item %p from %s(%p)", item,
28221431a748SGleb Smirnoff 	    zone->uz_name, zone);
28231431a748SGleb Smirnoff 
28248355f576SJeff Roberson 	return (item);
28250095a784SJeff Roberson 
28260095a784SJeff Roberson fail:
28271431a748SGleb Smirnoff 	CTR2(KTR_UMA, "zone_alloc_item failed from %s(%p)",
28281431a748SGleb Smirnoff 	    zone->uz_name, zone);
28290095a784SJeff Roberson 	atomic_add_long(&zone->uz_fails, 1);
28300095a784SJeff Roberson 	return (NULL);
28318355f576SJeff Roberson }
28328355f576SJeff Roberson 
28338355f576SJeff Roberson /* See uma.h */
28348355f576SJeff Roberson void
28358355f576SJeff Roberson uma_zfree_arg(uma_zone_t zone, void *item, void *udata)
28368355f576SJeff Roberson {
28378355f576SJeff Roberson 	uma_cache_t cache;
28388355f576SJeff Roberson 	uma_bucket_t bucket;
2839ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
2840ab3185d1SJeff Roberson 	int cpu, domain, lockfail;
28418355f576SJeff Roberson 
2842e866d8f0SMark Murray 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
2843e866d8f0SMark Murray 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
284410cb2424SMark Murray 
28453659f747SRobert Watson 	CTR2(KTR_UMA, "uma_zfree_arg thread %x zone %s", curthread,
28463659f747SRobert Watson 	    zone->uz_name);
28473659f747SRobert Watson 
2848d9e2e68dSMark Johnston 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
28491067a2baSJonathan T. Looney 	    ("uma_zfree_arg: called with spinlock or critical section held"));
28501067a2baSJonathan T. Looney 
285120ed0cb0SMatthew D Fleming         /* uma_zfree(..., NULL) does nothing, to match free(9). */
285220ed0cb0SMatthew D Fleming         if (item == NULL)
285320ed0cb0SMatthew D Fleming                 return;
28548d689e04SGleb Smirnoff #ifdef DEBUG_MEMGUARD
28558d689e04SGleb Smirnoff 	if (is_memguard_addr(item)) {
2856bc9d08e1SMark Johnston 		if (zone->uz_dtor != NULL)
28578d689e04SGleb Smirnoff 			zone->uz_dtor(item, zone->uz_size, udata);
2858bc9d08e1SMark Johnston 		if (zone->uz_fini != NULL)
28598d689e04SGleb Smirnoff 			zone->uz_fini(item, zone->uz_size);
28608d689e04SGleb Smirnoff 		memguard_free(item);
28618d689e04SGleb Smirnoff 		return;
28628d689e04SGleb Smirnoff 	}
28638d689e04SGleb Smirnoff #endif
28645d1ae027SRobert Watson #ifdef INVARIANTS
2865e20a199fSJeff Roberson 	if (zone->uz_flags & UMA_ZONE_MALLOC)
28665d1ae027SRobert Watson 		uma_dbg_free(zone, udata, item);
28675d1ae027SRobert Watson 	else
28685d1ae027SRobert Watson 		uma_dbg_free(zone, NULL, item);
28695d1ae027SRobert Watson #endif
2870fc03d22bSJeff Roberson 	if (zone->uz_dtor != NULL)
2871ef72505eSJeff Roberson 		zone->uz_dtor(item, zone->uz_size, udata);
2872ef72505eSJeff Roberson 
2873af7f9b97SJeff Roberson 	/*
2874af7f9b97SJeff Roberson 	 * The race here is acceptable.  If we miss it we'll just have to wait
2875af7f9b97SJeff Roberson 	 * a little longer for the limits to be reset.
2876af7f9b97SJeff Roberson 	 */
2877e20a199fSJeff Roberson 	if (zone->uz_flags & UMA_ZFLAG_FULL)
2878fc03d22bSJeff Roberson 		goto zfree_item;
2879af7f9b97SJeff Roberson 
28805d1ae027SRobert Watson 	/*
28815d1ae027SRobert Watson 	 * If possible, free to the per-CPU cache.  There are two
28825d1ae027SRobert Watson 	 * requirements for safe access to the per-CPU cache: (1) the thread
28835d1ae027SRobert Watson 	 * accessing the cache must not be preempted or yield during access,
28845d1ae027SRobert Watson 	 * and (2) the thread must not migrate CPUs without switching which
28855d1ae027SRobert Watson 	 * cache it accesses.  We rely on a critical section to prevent
28865d1ae027SRobert Watson 	 * preemption and migration.  We release the critical section in
28875d1ae027SRobert Watson 	 * order to acquire the zone mutex if we are unable to free to the
28885d1ae027SRobert Watson 	 * current cache; when we re-acquire the critical section, we must
28895d1ae027SRobert Watson 	 * detect and handle migration if it has occurred.
28905d1ae027SRobert Watson 	 */
2891a553d4b8SJeff Roberson zfree_restart:
28925d1ae027SRobert Watson 	critical_enter();
28935d1ae027SRobert Watson 	cpu = curcpu;
28948355f576SJeff Roberson 	cache = &zone->uz_cpu[cpu];
28958355f576SJeff Roberson 
28968355f576SJeff Roberson zfree_start:
2897a553d4b8SJeff Roberson 	/*
2898fc03d22bSJeff Roberson 	 * Try to free into the allocbucket first to give LIFO ordering
2899fc03d22bSJeff Roberson 	 * for cache-hot datastructures.  Spill over into the freebucket
2900fc03d22bSJeff Roberson 	 * if necessary.  Alloc will swap them if one runs dry.
2901a553d4b8SJeff Roberson 	 */
2902fc03d22bSJeff Roberson 	bucket = cache->uc_allocbucket;
2903fc03d22bSJeff Roberson 	if (bucket == NULL || bucket->ub_cnt >= bucket->ub_entries)
2904fc03d22bSJeff Roberson 		bucket = cache->uc_freebucket;
2905fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt < bucket->ub_entries) {
2906cae33c14SJeff Roberson 		KASSERT(bucket->ub_bucket[bucket->ub_cnt] == NULL,
29078355f576SJeff Roberson 		    ("uma_zfree: Freeing to non free bucket index."));
2908cae33c14SJeff Roberson 		bucket->ub_bucket[bucket->ub_cnt] = item;
2909cae33c14SJeff Roberson 		bucket->ub_cnt++;
2910773df9abSRobert Watson 		cache->uc_frees++;
29115d1ae027SRobert Watson 		critical_exit();
29128355f576SJeff Roberson 		return;
2913fc03d22bSJeff Roberson 	}
2914fc03d22bSJeff Roberson 
29158355f576SJeff Roberson 	/*
29165d1ae027SRobert Watson 	 * We must go back the zone, which requires acquiring the zone lock,
29175d1ae027SRobert Watson 	 * which in turn means we must release and re-acquire the critical
29185d1ae027SRobert Watson 	 * section.  Since the critical section is released, we may be
29195d1ae027SRobert Watson 	 * preempted or migrate.  As such, make sure not to maintain any
29205d1ae027SRobert Watson 	 * thread-local state specific to the cache from prior to releasing
29215d1ae027SRobert Watson 	 * the critical section.
29228355f576SJeff Roberson 	 */
29235d1ae027SRobert Watson 	critical_exit();
2924fc03d22bSJeff Roberson 	if (zone->uz_count == 0 || bucketdisable)
2925fc03d22bSJeff Roberson 		goto zfree_item;
2926fc03d22bSJeff Roberson 
29274d104ba0SAlexander Motin 	lockfail = 0;
29284d104ba0SAlexander Motin 	if (ZONE_TRYLOCK(zone) == 0) {
29294d104ba0SAlexander Motin 		/* Record contention to size the buckets. */
29308355f576SJeff Roberson 		ZONE_LOCK(zone);
29314d104ba0SAlexander Motin 		lockfail = 1;
29324d104ba0SAlexander Motin 	}
29335d1ae027SRobert Watson 	critical_enter();
29345d1ae027SRobert Watson 	cpu = curcpu;
29355d1ae027SRobert Watson 	cache = &zone->uz_cpu[cpu];
29368355f576SJeff Roberson 
2937fc03d22bSJeff Roberson 	/*
2938fc03d22bSJeff Roberson 	 * Since we have locked the zone we may as well send back our stats.
2939fc03d22bSJeff Roberson 	 */
29400095a784SJeff Roberson 	atomic_add_long(&zone->uz_allocs, cache->uc_allocs);
29410095a784SJeff Roberson 	atomic_add_long(&zone->uz_frees, cache->uc_frees);
2942f4ff923bSRobert Watson 	cache->uc_allocs = 0;
2943f4ff923bSRobert Watson 	cache->uc_frees = 0;
2944f4ff923bSRobert Watson 
29458355f576SJeff Roberson 	bucket = cache->uc_freebucket;
2946fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt < bucket->ub_entries) {
2947fc03d22bSJeff Roberson 		ZONE_UNLOCK(zone);
2948fc03d22bSJeff Roberson 		goto zfree_start;
2949fc03d22bSJeff Roberson 	}
29508355f576SJeff Roberson 	cache->uc_freebucket = NULL;
2951afa5d703SMark Johnston 	/* We are no longer associated with this CPU. */
2952afa5d703SMark Johnston 	critical_exit();
29538355f576SJeff Roberson 
2954ab3185d1SJeff Roberson 	if ((zone->uz_flags & UMA_ZONE_NUMA) != 0)
2955ab3185d1SJeff Roberson 		domain = PCPU_GET(domain);
2956ab3185d1SJeff Roberson 	else
2957ab3185d1SJeff Roberson 		domain = 0;
2958ab3185d1SJeff Roberson 	zdom = &zone->uz_domain[0];
2959ab3185d1SJeff Roberson 
29608355f576SJeff Roberson 	/* Can we throw this on the zone full list? */
29618355f576SJeff Roberson 	if (bucket != NULL) {
29621431a748SGleb Smirnoff 		CTR3(KTR_UMA,
29631431a748SGleb Smirnoff 		    "uma_zfree: zone %s(%p) putting bucket %p on free list",
29641431a748SGleb Smirnoff 		    zone->uz_name, zone, bucket);
2965cae33c14SJeff Roberson 		/* ub_cnt is pointing to the last free item */
2966cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt != 0,
29678355f576SJeff Roberson 		    ("uma_zfree: Attempting to insert an empty bucket onto the full list.\n"));
2968ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&zdom->uzd_buckets, bucket, ub_link);
29698355f576SJeff Roberson 	}
2970fc03d22bSJeff Roberson 
29714d104ba0SAlexander Motin 	/*
29724d104ba0SAlexander Motin 	 * We bump the uz count when the cache size is insufficient to
29734d104ba0SAlexander Motin 	 * handle the working set.
29744d104ba0SAlexander Motin 	 */
29754d104ba0SAlexander Motin 	if (lockfail && zone->uz_count < BUCKET_MAX)
29764d104ba0SAlexander Motin 		zone->uz_count++;
2977a553d4b8SJeff Roberson 	ZONE_UNLOCK(zone);
2978a553d4b8SJeff Roberson 
29796fd34d6fSJeff Roberson 	bucket = bucket_alloc(zone, udata, M_NOWAIT);
29801431a748SGleb Smirnoff 	CTR3(KTR_UMA, "uma_zfree: zone %s(%p) allocated bucket %p",
29811431a748SGleb Smirnoff 	    zone->uz_name, zone, bucket);
29824741dcbfSJeff Roberson 	if (bucket) {
2983fc03d22bSJeff Roberson 		critical_enter();
2984fc03d22bSJeff Roberson 		cpu = curcpu;
2985fc03d22bSJeff Roberson 		cache = &zone->uz_cpu[cpu];
2986ab3185d1SJeff Roberson 		if (cache->uc_freebucket == NULL &&
2987ab3185d1SJeff Roberson 		    ((zone->uz_flags & UMA_ZONE_NUMA) == 0 ||
2988ab3185d1SJeff Roberson 		    domain == PCPU_GET(domain))) {
2989fc03d22bSJeff Roberson 			cache->uc_freebucket = bucket;
2990fc03d22bSJeff Roberson 			goto zfree_start;
2991fc03d22bSJeff Roberson 		}
2992fc03d22bSJeff Roberson 		/*
2993fc03d22bSJeff Roberson 		 * We lost the race, start over.  We have to drop our
2994fc03d22bSJeff Roberson 		 * critical section to free the bucket.
2995fc03d22bSJeff Roberson 		 */
2996fc03d22bSJeff Roberson 		critical_exit();
29976fd34d6fSJeff Roberson 		bucket_free(zone, bucket, udata);
2998a553d4b8SJeff Roberson 		goto zfree_restart;
29998355f576SJeff Roberson 	}
30008355f576SJeff Roberson 
3001a553d4b8SJeff Roberson 	/*
3002a553d4b8SJeff Roberson 	 * If nothing else caught this, we'll just do an internal free.
3003a553d4b8SJeff Roberson 	 */
3004fc03d22bSJeff Roberson zfree_item:
30050095a784SJeff Roberson 	zone_free_item(zone, item, udata, SKIP_DTOR);
30068355f576SJeff Roberson 
30078355f576SJeff Roberson 	return;
30088355f576SJeff Roberson }
30098355f576SJeff Roberson 
3010ab3185d1SJeff Roberson void
3011ab3185d1SJeff Roberson uma_zfree_domain(uma_zone_t zone, void *item, void *udata)
3012ab3185d1SJeff Roberson {
3013ab3185d1SJeff Roberson 
3014ab3185d1SJeff Roberson 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
3015ab3185d1SJeff Roberson 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
3016ab3185d1SJeff Roberson 
3017ab3185d1SJeff Roberson 	CTR2(KTR_UMA, "uma_zfree_domain thread %x zone %s", curthread,
3018ab3185d1SJeff Roberson 	    zone->uz_name);
3019ab3185d1SJeff Roberson 
3020ab3185d1SJeff Roberson 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
3021ab3185d1SJeff Roberson 	    ("uma_zfree_domain: called with spinlock or critical section held"));
3022ab3185d1SJeff Roberson 
3023ab3185d1SJeff Roberson         /* uma_zfree(..., NULL) does nothing, to match free(9). */
3024ab3185d1SJeff Roberson         if (item == NULL)
3025ab3185d1SJeff Roberson                 return;
3026ab3185d1SJeff Roberson 	zone_free_item(zone, item, udata, SKIP_NONE);
3027ab3185d1SJeff Roberson }
3028ab3185d1SJeff Roberson 
30298355f576SJeff Roberson static void
30300095a784SJeff Roberson slab_free_item(uma_keg_t keg, uma_slab_t slab, void *item)
30318355f576SJeff Roberson {
3032ab3185d1SJeff Roberson 	uma_domain_t dom;
303385dcf349SGleb Smirnoff 	uint8_t freei;
3034099a0e58SBosko Milekic 
30350095a784SJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
3036e20a199fSJeff Roberson 	MPASS(keg == slab->us_keg);
30378355f576SJeff Roberson 
3038ab3185d1SJeff Roberson 	dom = &keg->uk_domain[slab->us_domain];
3039ab3185d1SJeff Roberson 
30408355f576SJeff Roberson 	/* Do we need to remove from any lists? */
3041099a0e58SBosko Milekic 	if (slab->us_freecount+1 == keg->uk_ipers) {
30428355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
3043ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_free_slab, slab, us_link);
30448355f576SJeff Roberson 	} else if (slab->us_freecount == 0) {
30458355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
3046ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link);
30478355f576SJeff Roberson 	}
30488355f576SJeff Roberson 
3049ef72505eSJeff Roberson 	/* Slab management. */
3050ef72505eSJeff Roberson 	freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize;
3051ef72505eSJeff Roberson 	BIT_SET(SLAB_SETSIZE, freei, &slab->us_free);
30528355f576SJeff Roberson 	slab->us_freecount++;
30538355f576SJeff Roberson 
3054ef72505eSJeff Roberson 	/* Keg statistics. */
3055099a0e58SBosko Milekic 	keg->uk_free++;
30560095a784SJeff Roberson }
30570095a784SJeff Roberson 
30580095a784SJeff Roberson static void
30590095a784SJeff Roberson zone_release(uma_zone_t zone, void **bucket, int cnt)
30600095a784SJeff Roberson {
30610095a784SJeff Roberson 	void *item;
30620095a784SJeff Roberson 	uma_slab_t slab;
30630095a784SJeff Roberson 	uma_keg_t keg;
30640095a784SJeff Roberson 	uint8_t *mem;
30650095a784SJeff Roberson 	int clearfull;
30660095a784SJeff Roberson 	int i;
30678355f576SJeff Roberson 
3068e20a199fSJeff Roberson 	clearfull = 0;
30690095a784SJeff Roberson 	keg = zone_first_keg(zone);
3070af526374SJeff Roberson 	KEG_LOCK(keg);
30710095a784SJeff Roberson 	for (i = 0; i < cnt; i++) {
30720095a784SJeff Roberson 		item = bucket[i];
30730095a784SJeff Roberson 		if (!(zone->uz_flags & UMA_ZONE_VTOSLAB)) {
30740095a784SJeff Roberson 			mem = (uint8_t *)((uintptr_t)item & (~UMA_SLAB_MASK));
30750095a784SJeff Roberson 			if (zone->uz_flags & UMA_ZONE_HASH) {
30760095a784SJeff Roberson 				slab = hash_sfind(&keg->uk_hash, mem);
30770095a784SJeff Roberson 			} else {
30780095a784SJeff Roberson 				mem += keg->uk_pgoff;
30790095a784SJeff Roberson 				slab = (uma_slab_t)mem;
30800095a784SJeff Roberson 			}
30810095a784SJeff Roberson 		} else {
30820095a784SJeff Roberson 			slab = vtoslab((vm_offset_t)item);
30830095a784SJeff Roberson 			if (slab->us_keg != keg) {
30840095a784SJeff Roberson 				KEG_UNLOCK(keg);
30850095a784SJeff Roberson 				keg = slab->us_keg;
30860095a784SJeff Roberson 				KEG_LOCK(keg);
30870095a784SJeff Roberson 			}
30880095a784SJeff Roberson 		}
30890095a784SJeff Roberson 		slab_free_item(keg, slab, item);
3090099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZFLAG_FULL) {
3091e20a199fSJeff Roberson 			if (keg->uk_pages < keg->uk_maxpages) {
3092099a0e58SBosko Milekic 				keg->uk_flags &= ~UMA_ZFLAG_FULL;
3093e20a199fSJeff Roberson 				clearfull = 1;
3094e20a199fSJeff Roberson 			}
3095af7f9b97SJeff Roberson 
309677380291SMohan Srinivasan 			/*
3097ef72505eSJeff Roberson 			 * We can handle one more allocation. Since we're
3098ef72505eSJeff Roberson 			 * clearing ZFLAG_FULL, wake up all procs blocked
3099ef72505eSJeff Roberson 			 * on pages. This should be uncommon, so keeping this
3100ef72505eSJeff Roberson 			 * simple for now (rather than adding count of blocked
310177380291SMohan Srinivasan 			 * threads etc).
310277380291SMohan Srinivasan 			 */
310377380291SMohan Srinivasan 			wakeup(keg);
3104af7f9b97SJeff Roberson 		}
31050095a784SJeff Roberson 	}
3106af526374SJeff Roberson 	KEG_UNLOCK(keg);
31070095a784SJeff Roberson 	if (clearfull) {
3108af526374SJeff Roberson 		ZONE_LOCK(zone);
3109e20a199fSJeff Roberson 		zone->uz_flags &= ~UMA_ZFLAG_FULL;
3110e20a199fSJeff Roberson 		wakeup(zone);
3111605cbd6aSJeff Roberson 		ZONE_UNLOCK(zone);
3112af526374SJeff Roberson 	}
3113ef72505eSJeff Roberson 
31148355f576SJeff Roberson }
31158355f576SJeff Roberson 
31160095a784SJeff Roberson /*
31170095a784SJeff Roberson  * Frees a single item to any zone.
31180095a784SJeff Roberson  *
31190095a784SJeff Roberson  * Arguments:
31200095a784SJeff Roberson  *	zone   The zone to free to
31210095a784SJeff Roberson  *	item   The item we're freeing
31220095a784SJeff Roberson  *	udata  User supplied data for the dtor
31230095a784SJeff Roberson  *	skip   Skip dtors and finis
31240095a784SJeff Roberson  */
31250095a784SJeff Roberson static void
31260095a784SJeff Roberson zone_free_item(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip)
31270095a784SJeff Roberson {
31280095a784SJeff Roberson 
31290095a784SJeff Roberson #ifdef INVARIANTS
31300095a784SJeff Roberson 	if (skip == SKIP_NONE) {
31310095a784SJeff Roberson 		if (zone->uz_flags & UMA_ZONE_MALLOC)
31320095a784SJeff Roberson 			uma_dbg_free(zone, udata, item);
31330095a784SJeff Roberson 		else
31340095a784SJeff Roberson 			uma_dbg_free(zone, NULL, item);
31350095a784SJeff Roberson 	}
31360095a784SJeff Roberson #endif
31370095a784SJeff Roberson 	if (skip < SKIP_DTOR && zone->uz_dtor)
31380095a784SJeff Roberson 		zone->uz_dtor(item, zone->uz_size, udata);
31390095a784SJeff Roberson 
31400095a784SJeff Roberson 	if (skip < SKIP_FINI && zone->uz_fini)
31410095a784SJeff Roberson 		zone->uz_fini(item, zone->uz_size);
31420095a784SJeff Roberson 
31430095a784SJeff Roberson 	atomic_add_long(&zone->uz_frees, 1);
31440095a784SJeff Roberson 	zone->uz_release(zone->uz_arg, &item, 1);
31450095a784SJeff Roberson }
31460095a784SJeff Roberson 
31478355f576SJeff Roberson /* See uma.h */
31481c6cae97SLawrence Stewart int
3149736ee590SJeff Roberson uma_zone_set_max(uma_zone_t zone, int nitems)
3150736ee590SJeff Roberson {
3151099a0e58SBosko Milekic 	uma_keg_t keg;
3152099a0e58SBosko Milekic 
3153e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
31540095a784SJeff Roberson 	if (keg == NULL)
31550095a784SJeff Roberson 		return (0);
3156af526374SJeff Roberson 	KEG_LOCK(keg);
3157e20a199fSJeff Roberson 	keg->uk_maxpages = (nitems / keg->uk_ipers) * keg->uk_ppera;
3158099a0e58SBosko Milekic 	if (keg->uk_maxpages * keg->uk_ipers < nitems)
3159e20a199fSJeff Roberson 		keg->uk_maxpages += keg->uk_ppera;
316057223e99SAndriy Gapon 	nitems = (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers;
3161af526374SJeff Roberson 	KEG_UNLOCK(keg);
31621c6cae97SLawrence Stewart 
31631c6cae97SLawrence Stewart 	return (nitems);
3164736ee590SJeff Roberson }
3165736ee590SJeff Roberson 
3166736ee590SJeff Roberson /* See uma.h */
3167e49471b0SAndre Oppermann int
3168e49471b0SAndre Oppermann uma_zone_get_max(uma_zone_t zone)
3169e49471b0SAndre Oppermann {
3170e49471b0SAndre Oppermann 	int nitems;
3171e49471b0SAndre Oppermann 	uma_keg_t keg;
3172e49471b0SAndre Oppermann 
3173e49471b0SAndre Oppermann 	keg = zone_first_keg(zone);
31740095a784SJeff Roberson 	if (keg == NULL)
31750095a784SJeff Roberson 		return (0);
3176af526374SJeff Roberson 	KEG_LOCK(keg);
317757223e99SAndriy Gapon 	nitems = (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers;
3178af526374SJeff Roberson 	KEG_UNLOCK(keg);
3179e49471b0SAndre Oppermann 
3180e49471b0SAndre Oppermann 	return (nitems);
3181e49471b0SAndre Oppermann }
3182e49471b0SAndre Oppermann 
3183e49471b0SAndre Oppermann /* See uma.h */
31842f891cd5SPawel Jakub Dawidek void
31852f891cd5SPawel Jakub Dawidek uma_zone_set_warning(uma_zone_t zone, const char *warning)
31862f891cd5SPawel Jakub Dawidek {
31872f891cd5SPawel Jakub Dawidek 
31882f891cd5SPawel Jakub Dawidek 	ZONE_LOCK(zone);
31892f891cd5SPawel Jakub Dawidek 	zone->uz_warning = warning;
31902f891cd5SPawel Jakub Dawidek 	ZONE_UNLOCK(zone);
31912f891cd5SPawel Jakub Dawidek }
31922f891cd5SPawel Jakub Dawidek 
31932f891cd5SPawel Jakub Dawidek /* See uma.h */
319454503a13SJonathan T. Looney void
319554503a13SJonathan T. Looney uma_zone_set_maxaction(uma_zone_t zone, uma_maxaction_t maxaction)
319654503a13SJonathan T. Looney {
319754503a13SJonathan T. Looney 
319854503a13SJonathan T. Looney 	ZONE_LOCK(zone);
3199e60b2fcbSGleb Smirnoff 	TASK_INIT(&zone->uz_maxaction, 0, (task_fn_t *)maxaction, zone);
320054503a13SJonathan T. Looney 	ZONE_UNLOCK(zone);
320154503a13SJonathan T. Looney }
320254503a13SJonathan T. Looney 
320354503a13SJonathan T. Looney /* See uma.h */
3204c4ae7908SLawrence Stewart int
3205c4ae7908SLawrence Stewart uma_zone_get_cur(uma_zone_t zone)
3206c4ae7908SLawrence Stewart {
3207c4ae7908SLawrence Stewart 	int64_t nitems;
3208c4ae7908SLawrence Stewart 	u_int i;
3209c4ae7908SLawrence Stewart 
3210c4ae7908SLawrence Stewart 	ZONE_LOCK(zone);
3211c4ae7908SLawrence Stewart 	nitems = zone->uz_allocs - zone->uz_frees;
3212c4ae7908SLawrence Stewart 	CPU_FOREACH(i) {
3213c4ae7908SLawrence Stewart 		/*
3214c4ae7908SLawrence Stewart 		 * See the comment in sysctl_vm_zone_stats() regarding the
3215c4ae7908SLawrence Stewart 		 * safety of accessing the per-cpu caches. With the zone lock
3216c4ae7908SLawrence Stewart 		 * held, it is safe, but can potentially result in stale data.
3217c4ae7908SLawrence Stewart 		 */
3218c4ae7908SLawrence Stewart 		nitems += zone->uz_cpu[i].uc_allocs -
3219c4ae7908SLawrence Stewart 		    zone->uz_cpu[i].uc_frees;
3220c4ae7908SLawrence Stewart 	}
3221c4ae7908SLawrence Stewart 	ZONE_UNLOCK(zone);
3222c4ae7908SLawrence Stewart 
3223c4ae7908SLawrence Stewart 	return (nitems < 0 ? 0 : nitems);
3224c4ae7908SLawrence Stewart }
3225c4ae7908SLawrence Stewart 
3226c4ae7908SLawrence Stewart /* See uma.h */
3227736ee590SJeff Roberson void
3228099a0e58SBosko Milekic uma_zone_set_init(uma_zone_t zone, uma_init uminit)
3229099a0e58SBosko Milekic {
3230e20a199fSJeff Roberson 	uma_keg_t keg;
3231e20a199fSJeff Roberson 
3232e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
32330095a784SJeff Roberson 	KASSERT(keg != NULL, ("uma_zone_set_init: Invalid zone type"));
3234af526374SJeff Roberson 	KEG_LOCK(keg);
3235e20a199fSJeff Roberson 	KASSERT(keg->uk_pages == 0,
3236099a0e58SBosko Milekic 	    ("uma_zone_set_init on non-empty keg"));
3237e20a199fSJeff Roberson 	keg->uk_init = uminit;
3238af526374SJeff Roberson 	KEG_UNLOCK(keg);
3239099a0e58SBosko Milekic }
3240099a0e58SBosko Milekic 
3241099a0e58SBosko Milekic /* See uma.h */
3242099a0e58SBosko Milekic void
3243099a0e58SBosko Milekic uma_zone_set_fini(uma_zone_t zone, uma_fini fini)
3244099a0e58SBosko Milekic {
3245e20a199fSJeff Roberson 	uma_keg_t keg;
3246e20a199fSJeff Roberson 
3247e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
32481d2c0c46SDmitry Chagin 	KASSERT(keg != NULL, ("uma_zone_set_fini: Invalid zone type"));
3249af526374SJeff Roberson 	KEG_LOCK(keg);
3250e20a199fSJeff Roberson 	KASSERT(keg->uk_pages == 0,
3251099a0e58SBosko Milekic 	    ("uma_zone_set_fini on non-empty keg"));
3252e20a199fSJeff Roberson 	keg->uk_fini = fini;
3253af526374SJeff Roberson 	KEG_UNLOCK(keg);
3254099a0e58SBosko Milekic }
3255099a0e58SBosko Milekic 
3256099a0e58SBosko Milekic /* See uma.h */
3257099a0e58SBosko Milekic void
3258099a0e58SBosko Milekic uma_zone_set_zinit(uma_zone_t zone, uma_init zinit)
3259099a0e58SBosko Milekic {
3260af526374SJeff Roberson 
3261099a0e58SBosko Milekic 	ZONE_LOCK(zone);
3262e20a199fSJeff Roberson 	KASSERT(zone_first_keg(zone)->uk_pages == 0,
3263099a0e58SBosko Milekic 	    ("uma_zone_set_zinit on non-empty keg"));
3264099a0e58SBosko Milekic 	zone->uz_init = zinit;
3265099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
3266099a0e58SBosko Milekic }
3267099a0e58SBosko Milekic 
3268099a0e58SBosko Milekic /* See uma.h */
3269099a0e58SBosko Milekic void
3270099a0e58SBosko Milekic uma_zone_set_zfini(uma_zone_t zone, uma_fini zfini)
3271099a0e58SBosko Milekic {
3272af526374SJeff Roberson 
3273099a0e58SBosko Milekic 	ZONE_LOCK(zone);
3274e20a199fSJeff Roberson 	KASSERT(zone_first_keg(zone)->uk_pages == 0,
3275099a0e58SBosko Milekic 	    ("uma_zone_set_zfini on non-empty keg"));
3276099a0e58SBosko Milekic 	zone->uz_fini = zfini;
3277099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
3278099a0e58SBosko Milekic }
3279099a0e58SBosko Milekic 
3280099a0e58SBosko Milekic /* See uma.h */
3281b23f72e9SBrian Feldman /* XXX uk_freef is not actually used with the zone locked */
3282099a0e58SBosko Milekic void
32838355f576SJeff Roberson uma_zone_set_freef(uma_zone_t zone, uma_free freef)
32848355f576SJeff Roberson {
32850095a784SJeff Roberson 	uma_keg_t keg;
3286e20a199fSJeff Roberson 
32870095a784SJeff Roberson 	keg = zone_first_keg(zone);
32881d2c0c46SDmitry Chagin 	KASSERT(keg != NULL, ("uma_zone_set_freef: Invalid zone type"));
3289af526374SJeff Roberson 	KEG_LOCK(keg);
32900095a784SJeff Roberson 	keg->uk_freef = freef;
3291af526374SJeff Roberson 	KEG_UNLOCK(keg);
32928355f576SJeff Roberson }
32938355f576SJeff Roberson 
32948355f576SJeff Roberson /* See uma.h */
3295b23f72e9SBrian Feldman /* XXX uk_allocf is not actually used with the zone locked */
32968355f576SJeff Roberson void
32978355f576SJeff Roberson uma_zone_set_allocf(uma_zone_t zone, uma_alloc allocf)
32988355f576SJeff Roberson {
3299e20a199fSJeff Roberson 	uma_keg_t keg;
3300e20a199fSJeff Roberson 
3301e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
3302af526374SJeff Roberson 	KEG_LOCK(keg);
3303e20a199fSJeff Roberson 	keg->uk_allocf = allocf;
3304af526374SJeff Roberson 	KEG_UNLOCK(keg);
33058355f576SJeff Roberson }
33068355f576SJeff Roberson 
33078355f576SJeff Roberson /* See uma.h */
33086fd34d6fSJeff Roberson void
33096fd34d6fSJeff Roberson uma_zone_reserve(uma_zone_t zone, int items)
33106fd34d6fSJeff Roberson {
33116fd34d6fSJeff Roberson 	uma_keg_t keg;
33126fd34d6fSJeff Roberson 
33136fd34d6fSJeff Roberson 	keg = zone_first_keg(zone);
33146fd34d6fSJeff Roberson 	if (keg == NULL)
33156fd34d6fSJeff Roberson 		return;
33166fd34d6fSJeff Roberson 	KEG_LOCK(keg);
33176fd34d6fSJeff Roberson 	keg->uk_reserve = items;
33186fd34d6fSJeff Roberson 	KEG_UNLOCK(keg);
33196fd34d6fSJeff Roberson 
33206fd34d6fSJeff Roberson 	return;
33216fd34d6fSJeff Roberson }
33226fd34d6fSJeff Roberson 
33236fd34d6fSJeff Roberson /* See uma.h */
33248355f576SJeff Roberson int
3325a4915c21SAttilio Rao uma_zone_reserve_kva(uma_zone_t zone, int count)
33268355f576SJeff Roberson {
3327099a0e58SBosko Milekic 	uma_keg_t keg;
33288355f576SJeff Roberson 	vm_offset_t kva;
33299ba30bcbSZbigniew Bodek 	u_int pages;
33308355f576SJeff Roberson 
3331e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
33320095a784SJeff Roberson 	if (keg == NULL)
33330095a784SJeff Roberson 		return (0);
3334099a0e58SBosko Milekic 	pages = count / keg->uk_ipers;
33358355f576SJeff Roberson 
3336099a0e58SBosko Milekic 	if (pages * keg->uk_ipers < count)
33378355f576SJeff Roberson 		pages++;
333857223e99SAndriy Gapon 	pages *= keg->uk_ppera;
3339a553d4b8SJeff Roberson 
3340a4915c21SAttilio Rao #ifdef UMA_MD_SMALL_ALLOC
3341a4915c21SAttilio Rao 	if (keg->uk_ppera > 1) {
3342a4915c21SAttilio Rao #else
3343a4915c21SAttilio Rao 	if (1) {
3344a4915c21SAttilio Rao #endif
334557223e99SAndriy Gapon 		kva = kva_alloc((vm_size_t)pages * PAGE_SIZE);
3346d1f42ac2SAlan Cox 		if (kva == 0)
33478355f576SJeff Roberson 			return (0);
3348a4915c21SAttilio Rao 	} else
3349a4915c21SAttilio Rao 		kva = 0;
3350af526374SJeff Roberson 	KEG_LOCK(keg);
3351099a0e58SBosko Milekic 	keg->uk_kva = kva;
3352a4915c21SAttilio Rao 	keg->uk_offset = 0;
3353099a0e58SBosko Milekic 	keg->uk_maxpages = pages;
3354a4915c21SAttilio Rao #ifdef UMA_MD_SMALL_ALLOC
3355a4915c21SAttilio Rao 	keg->uk_allocf = (keg->uk_ppera > 1) ? noobj_alloc : uma_small_alloc;
3356a4915c21SAttilio Rao #else
3357a4915c21SAttilio Rao 	keg->uk_allocf = noobj_alloc;
3358a4915c21SAttilio Rao #endif
33596fd34d6fSJeff Roberson 	keg->uk_flags |= UMA_ZONE_NOFREE;
3360af526374SJeff Roberson 	KEG_UNLOCK(keg);
3361af526374SJeff Roberson 
33628355f576SJeff Roberson 	return (1);
33638355f576SJeff Roberson }
33648355f576SJeff Roberson 
33658355f576SJeff Roberson /* See uma.h */
33668355f576SJeff Roberson void
33678355f576SJeff Roberson uma_prealloc(uma_zone_t zone, int items)
33688355f576SJeff Roberson {
3369ab3185d1SJeff Roberson 	uma_domain_t dom;
33708355f576SJeff Roberson 	uma_slab_t slab;
3371099a0e58SBosko Milekic 	uma_keg_t keg;
3372ab3185d1SJeff Roberson 	int domain, slabs;
33738355f576SJeff Roberson 
3374e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
33750095a784SJeff Roberson 	if (keg == NULL)
33760095a784SJeff Roberson 		return;
3377af526374SJeff Roberson 	KEG_LOCK(keg);
3378099a0e58SBosko Milekic 	slabs = items / keg->uk_ipers;
3379ab3185d1SJeff Roberson 	domain = 0;
3380099a0e58SBosko Milekic 	if (slabs * keg->uk_ipers < items)
33818355f576SJeff Roberson 		slabs++;
33828355f576SJeff Roberson 	while (slabs > 0) {
3383ab3185d1SJeff Roberson 		slab = keg_alloc_slab(keg, zone, domain, M_WAITOK);
3384e20a199fSJeff Roberson 		if (slab == NULL)
3385e20a199fSJeff Roberson 			break;
3386e20a199fSJeff Roberson 		MPASS(slab->us_keg == keg);
3387ab3185d1SJeff Roberson 		dom = &keg->uk_domain[slab->us_domain];
3388ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_free_slab, slab, us_link);
33898355f576SJeff Roberson 		slabs--;
3390ab3185d1SJeff Roberson 		domain = (domain + 1) % vm_ndomains;
33918355f576SJeff Roberson 	}
3392af526374SJeff Roberson 	KEG_UNLOCK(keg);
33938355f576SJeff Roberson }
33948355f576SJeff Roberson 
33958355f576SJeff Roberson /* See uma.h */
339644ec2b63SKonstantin Belousov static void
339744ec2b63SKonstantin Belousov uma_reclaim_locked(bool kmem_danger)
33988355f576SJeff Roberson {
339944ec2b63SKonstantin Belousov 
34001431a748SGleb Smirnoff 	CTR0(KTR_UMA, "UMA: vm asked us to release pages!");
340144ec2b63SKonstantin Belousov 	sx_assert(&uma_drain_lock, SA_XLOCKED);
340286bbae32SJeff Roberson 	bucket_enable();
34038355f576SJeff Roberson 	zone_foreach(zone_drain);
340444ec2b63SKonstantin Belousov 	if (vm_page_count_min() || kmem_danger) {
3405a2de44abSAlexander Motin 		cache_drain_safe(NULL);
3406a2de44abSAlexander Motin 		zone_foreach(zone_drain);
3407a2de44abSAlexander Motin 	}
34088355f576SJeff Roberson 	/*
34098355f576SJeff Roberson 	 * Some slabs may have been freed but this zone will be visited early
34108355f576SJeff Roberson 	 * we visit again so that we can free pages that are empty once other
34118355f576SJeff Roberson 	 * zones are drained.  We have to do the same for buckets.
34128355f576SJeff Roberson 	 */
34139643769aSJeff Roberson 	zone_drain(slabzone);
3414cae33c14SJeff Roberson 	bucket_zone_drain();
341544ec2b63SKonstantin Belousov }
341644ec2b63SKonstantin Belousov 
341744ec2b63SKonstantin Belousov void
341844ec2b63SKonstantin Belousov uma_reclaim(void)
341944ec2b63SKonstantin Belousov {
342044ec2b63SKonstantin Belousov 
342144ec2b63SKonstantin Belousov 	sx_xlock(&uma_drain_lock);
342244ec2b63SKonstantin Belousov 	uma_reclaim_locked(false);
342395c4bf75SKonstantin Belousov 	sx_xunlock(&uma_drain_lock);
34248355f576SJeff Roberson }
34258355f576SJeff Roberson 
34262e47807cSJeff Roberson static volatile int uma_reclaim_needed;
342744ec2b63SKonstantin Belousov 
342844ec2b63SKonstantin Belousov void
342944ec2b63SKonstantin Belousov uma_reclaim_wakeup(void)
343044ec2b63SKonstantin Belousov {
343144ec2b63SKonstantin Belousov 
34322e47807cSJeff Roberson 	if (atomic_fetchadd_int(&uma_reclaim_needed, 1) == 0)
34332e47807cSJeff Roberson 		wakeup(uma_reclaim);
343444ec2b63SKonstantin Belousov }
343544ec2b63SKonstantin Belousov 
343644ec2b63SKonstantin Belousov void
343744ec2b63SKonstantin Belousov uma_reclaim_worker(void *arg __unused)
343844ec2b63SKonstantin Belousov {
343944ec2b63SKonstantin Belousov 
344044ec2b63SKonstantin Belousov 	for (;;) {
34412e47807cSJeff Roberson 		sx_xlock(&uma_drain_lock);
3442200f8117SKonstantin Belousov 		while (atomic_load_int(&uma_reclaim_needed) == 0)
34432e47807cSJeff Roberson 			sx_sleep(uma_reclaim, &uma_drain_lock, PVM, "umarcl",
34442e47807cSJeff Roberson 			    hz);
34459b43bc27SAndriy Gapon 		sx_xunlock(&uma_drain_lock);
34469b43bc27SAndriy Gapon 		EVENTHANDLER_INVOKE(vm_lowmem, VM_LOW_KMEM);
34479b43bc27SAndriy Gapon 		sx_xlock(&uma_drain_lock);
344844ec2b63SKonstantin Belousov 		uma_reclaim_locked(true);
3449200f8117SKonstantin Belousov 		atomic_store_int(&uma_reclaim_needed, 0);
34502e47807cSJeff Roberson 		sx_xunlock(&uma_drain_lock);
34512e47807cSJeff Roberson 		/* Don't fire more than once per-second. */
34522e47807cSJeff Roberson 		pause("umarclslp", hz);
345344ec2b63SKonstantin Belousov 	}
345444ec2b63SKonstantin Belousov }
345544ec2b63SKonstantin Belousov 
3456663b416fSJohn Baldwin /* See uma.h */
3457663b416fSJohn Baldwin int
3458663b416fSJohn Baldwin uma_zone_exhausted(uma_zone_t zone)
3459663b416fSJohn Baldwin {
3460663b416fSJohn Baldwin 	int full;
3461663b416fSJohn Baldwin 
3462663b416fSJohn Baldwin 	ZONE_LOCK(zone);
3463e20a199fSJeff Roberson 	full = (zone->uz_flags & UMA_ZFLAG_FULL);
3464663b416fSJohn Baldwin 	ZONE_UNLOCK(zone);
3465663b416fSJohn Baldwin 	return (full);
3466663b416fSJohn Baldwin }
3467663b416fSJohn Baldwin 
34686c125b8dSMohan Srinivasan int
34696c125b8dSMohan Srinivasan uma_zone_exhausted_nolock(uma_zone_t zone)
34706c125b8dSMohan Srinivasan {
3471e20a199fSJeff Roberson 	return (zone->uz_flags & UMA_ZFLAG_FULL);
34726c125b8dSMohan Srinivasan }
34736c125b8dSMohan Srinivasan 
34748355f576SJeff Roberson void *
3475ab3185d1SJeff Roberson uma_large_malloc_domain(vm_size_t size, int domain, int wait)
34768355f576SJeff Roberson {
3477ab3185d1SJeff Roberson 	vm_offset_t addr;
34788355f576SJeff Roberson 	uma_slab_t slab;
34798355f576SJeff Roberson 
3480ab3185d1SJeff Roberson 	slab = zone_alloc_item(slabzone, NULL, domain, wait);
34818355f576SJeff Roberson 	if (slab == NULL)
34828355f576SJeff Roberson 		return (NULL);
3483ab3185d1SJeff Roberson 	if (domain == UMA_ANYDOMAIN)
3484ab3185d1SJeff Roberson 		addr = kmem_malloc(kernel_arena, size, wait);
3485ab3185d1SJeff Roberson 	else
3486ab3185d1SJeff Roberson 		addr = kmem_malloc_domain(domain, size, wait);
3487ab3185d1SJeff Roberson 	if (addr != 0) {
3488ab3185d1SJeff Roberson 		vsetslab(addr, slab);
3489ab3185d1SJeff Roberson 		slab->us_data = (void *)addr;
3490ab3185d1SJeff Roberson 		slab->us_flags = UMA_SLAB_KERNEL | UMA_SLAB_MALLOC;
34918355f576SJeff Roberson 		slab->us_size = size;
3492e2068d0bSJeff Roberson 		slab->us_domain = vm_phys_domain(PHYS_TO_VM_PAGE(
3493ab3185d1SJeff Roberson 		    pmap_kextract(addr)));
34942e47807cSJeff Roberson 		uma_total_inc(size);
34958355f576SJeff Roberson 	} else {
34960095a784SJeff Roberson 		zone_free_item(slabzone, slab, NULL, SKIP_NONE);
34978355f576SJeff Roberson 	}
34988355f576SJeff Roberson 
3499ab3185d1SJeff Roberson 	return ((void *)addr);
3500ab3185d1SJeff Roberson }
3501ab3185d1SJeff Roberson 
3502ab3185d1SJeff Roberson void *
3503ab3185d1SJeff Roberson uma_large_malloc(vm_size_t size, int wait)
3504ab3185d1SJeff Roberson {
3505ab3185d1SJeff Roberson 
3506ab3185d1SJeff Roberson 	return uma_large_malloc_domain(size, UMA_ANYDOMAIN, wait);
35078355f576SJeff Roberson }
35088355f576SJeff Roberson 
35098355f576SJeff Roberson void
35108355f576SJeff Roberson uma_large_free(uma_slab_t slab)
35118355f576SJeff Roberson {
3512c325e866SKonstantin Belousov 
3513ab3185d1SJeff Roberson 	KASSERT((slab->us_flags & UMA_SLAB_KERNEL) != 0,
3514ab3185d1SJeff Roberson 	    ("uma_large_free:  Memory not allocated with uma_large_malloc."));
3515ab3185d1SJeff Roberson 	kmem_free(kernel_arena, (vm_offset_t)slab->us_data, slab->us_size);
35162e47807cSJeff Roberson 	uma_total_dec(slab->us_size);
35170095a784SJeff Roberson 	zone_free_item(slabzone, slab, NULL, SKIP_NONE);
35188355f576SJeff Roberson }
35198355f576SJeff Roberson 
352048343a2fSGleb Smirnoff static void
352148343a2fSGleb Smirnoff uma_zero_item(void *item, uma_zone_t zone)
352248343a2fSGleb Smirnoff {
352396c85efbSNathan Whitehorn 	int i;
352448343a2fSGleb Smirnoff 
352548343a2fSGleb Smirnoff 	if (zone->uz_flags & UMA_ZONE_PCPU) {
352696c85efbSNathan Whitehorn 		CPU_FOREACH(i)
352748343a2fSGleb Smirnoff 			bzero(zpcpu_get_cpu(item, i), zone->uz_size);
352848343a2fSGleb Smirnoff 	} else
352948343a2fSGleb Smirnoff 		bzero(item, zone->uz_size);
353048343a2fSGleb Smirnoff }
353148343a2fSGleb Smirnoff 
35322e47807cSJeff Roberson unsigned long
35332e47807cSJeff Roberson uma_limit(void)
35342e47807cSJeff Roberson {
35352e47807cSJeff Roberson 
35362e47807cSJeff Roberson 	return (uma_kmem_limit);
35372e47807cSJeff Roberson }
35382e47807cSJeff Roberson 
35392e47807cSJeff Roberson void
35402e47807cSJeff Roberson uma_set_limit(unsigned long limit)
35412e47807cSJeff Roberson {
35422e47807cSJeff Roberson 
35432e47807cSJeff Roberson 	uma_kmem_limit = limit;
35442e47807cSJeff Roberson }
35452e47807cSJeff Roberson 
35462e47807cSJeff Roberson unsigned long
35472e47807cSJeff Roberson uma_size(void)
35482e47807cSJeff Roberson {
35492e47807cSJeff Roberson 
3550ad5b0f5bSJeff Roberson 	return (uma_kmem_total);
3551ad5b0f5bSJeff Roberson }
3552ad5b0f5bSJeff Roberson 
3553ad5b0f5bSJeff Roberson long
3554ad5b0f5bSJeff Roberson uma_avail(void)
3555ad5b0f5bSJeff Roberson {
3556ad5b0f5bSJeff Roberson 
3557ad5b0f5bSJeff Roberson 	return (uma_kmem_limit - uma_kmem_total);
35582e47807cSJeff Roberson }
35592e47807cSJeff Roberson 
35608355f576SJeff Roberson void
35618355f576SJeff Roberson uma_print_stats(void)
35628355f576SJeff Roberson {
35638355f576SJeff Roberson 	zone_foreach(uma_print_zone);
35648355f576SJeff Roberson }
35658355f576SJeff Roberson 
3566504d5de3SJeff Roberson static void
3567504d5de3SJeff Roberson slab_print(uma_slab_t slab)
3568504d5de3SJeff Roberson {
3569ef72505eSJeff Roberson 	printf("slab: keg %p, data %p, freecount %d\n",
3570ef72505eSJeff Roberson 		slab->us_keg, slab->us_data, slab->us_freecount);
3571504d5de3SJeff Roberson }
3572504d5de3SJeff Roberson 
3573504d5de3SJeff Roberson static void
3574504d5de3SJeff Roberson cache_print(uma_cache_t cache)
3575504d5de3SJeff Roberson {
3576504d5de3SJeff Roberson 	printf("alloc: %p(%d), free: %p(%d)\n",
3577504d5de3SJeff Roberson 		cache->uc_allocbucket,
3578504d5de3SJeff Roberson 		cache->uc_allocbucket?cache->uc_allocbucket->ub_cnt:0,
3579504d5de3SJeff Roberson 		cache->uc_freebucket,
3580504d5de3SJeff Roberson 		cache->uc_freebucket?cache->uc_freebucket->ub_cnt:0);
3581504d5de3SJeff Roberson }
3582504d5de3SJeff Roberson 
3583e20a199fSJeff Roberson static void
3584e20a199fSJeff Roberson uma_print_keg(uma_keg_t keg)
35858355f576SJeff Roberson {
3586ab3185d1SJeff Roberson 	uma_domain_t dom;
3587504d5de3SJeff Roberson 	uma_slab_t slab;
3588ab3185d1SJeff Roberson 	int i;
3589504d5de3SJeff Roberson 
35900b80c1e4SEitan Adler 	printf("keg: %s(%p) size %d(%d) flags %#x ipers %d ppera %d "
3591e20a199fSJeff Roberson 	    "out %d free %d limit %d\n",
3592e20a199fSJeff Roberson 	    keg->uk_name, keg, keg->uk_size, keg->uk_rsize, keg->uk_flags,
3593099a0e58SBosko Milekic 	    keg->uk_ipers, keg->uk_ppera,
359457223e99SAndriy Gapon 	    (keg->uk_pages / keg->uk_ppera) * keg->uk_ipers - keg->uk_free,
359557223e99SAndriy Gapon 	    keg->uk_free, (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers);
3596ab3185d1SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
3597ab3185d1SJeff Roberson 		dom = &keg->uk_domain[i];
3598504d5de3SJeff Roberson 		printf("Part slabs:\n");
3599ab3185d1SJeff Roberson 		LIST_FOREACH(slab, &dom->ud_part_slab, us_link)
3600504d5de3SJeff Roberson 			slab_print(slab);
3601504d5de3SJeff Roberson 		printf("Free slabs:\n");
3602ab3185d1SJeff Roberson 		LIST_FOREACH(slab, &dom->ud_free_slab, us_link)
3603504d5de3SJeff Roberson 			slab_print(slab);
3604504d5de3SJeff Roberson 		printf("Full slabs:\n");
3605ab3185d1SJeff Roberson 		LIST_FOREACH(slab, &dom->ud_full_slab, us_link)
3606504d5de3SJeff Roberson 			slab_print(slab);
3607e20a199fSJeff Roberson 	}
3608ab3185d1SJeff Roberson }
3609e20a199fSJeff Roberson 
3610e20a199fSJeff Roberson void
3611e20a199fSJeff Roberson uma_print_zone(uma_zone_t zone)
3612e20a199fSJeff Roberson {
3613e20a199fSJeff Roberson 	uma_cache_t cache;
3614e20a199fSJeff Roberson 	uma_klink_t kl;
3615e20a199fSJeff Roberson 	int i;
3616e20a199fSJeff Roberson 
36170b80c1e4SEitan Adler 	printf("zone: %s(%p) size %d flags %#x\n",
3618e20a199fSJeff Roberson 	    zone->uz_name, zone, zone->uz_size, zone->uz_flags);
3619e20a199fSJeff Roberson 	LIST_FOREACH(kl, &zone->uz_kegs, kl_link)
3620e20a199fSJeff Roberson 		uma_print_keg(kl->kl_keg);
36213aa6d94eSJohn Baldwin 	CPU_FOREACH(i) {
3622504d5de3SJeff Roberson 		cache = &zone->uz_cpu[i];
3623504d5de3SJeff Roberson 		printf("CPU %d Cache:\n", i);
3624504d5de3SJeff Roberson 		cache_print(cache);
3625504d5de3SJeff Roberson 	}
36268355f576SJeff Roberson }
36278355f576SJeff Roberson 
3628a0d4b0aeSRobert Watson #ifdef DDB
36298355f576SJeff Roberson /*
36307a52a97eSRobert Watson  * Generate statistics across both the zone and its per-cpu cache's.  Return
36317a52a97eSRobert Watson  * desired statistics if the pointer is non-NULL for that statistic.
36327a52a97eSRobert Watson  *
36337a52a97eSRobert Watson  * Note: does not update the zone statistics, as it can't safely clear the
36347a52a97eSRobert Watson  * per-CPU cache statistic.
36357a52a97eSRobert Watson  *
36367a52a97eSRobert Watson  * XXXRW: Following the uc_allocbucket and uc_freebucket pointers here isn't
36377a52a97eSRobert Watson  * safe from off-CPU; we should modify the caches to track this information
36387a52a97eSRobert Watson  * directly so that we don't have to.
36397a52a97eSRobert Watson  */
36407a52a97eSRobert Watson static void
364185dcf349SGleb Smirnoff uma_zone_sumstat(uma_zone_t z, int *cachefreep, uint64_t *allocsp,
364285dcf349SGleb Smirnoff     uint64_t *freesp, uint64_t *sleepsp)
36437a52a97eSRobert Watson {
36447a52a97eSRobert Watson 	uma_cache_t cache;
364585dcf349SGleb Smirnoff 	uint64_t allocs, frees, sleeps;
36467a52a97eSRobert Watson 	int cachefree, cpu;
36477a52a97eSRobert Watson 
3648bf965959SSean Bruno 	allocs = frees = sleeps = 0;
36497a52a97eSRobert Watson 	cachefree = 0;
36503aa6d94eSJohn Baldwin 	CPU_FOREACH(cpu) {
36517a52a97eSRobert Watson 		cache = &z->uz_cpu[cpu];
36527a52a97eSRobert Watson 		if (cache->uc_allocbucket != NULL)
36537a52a97eSRobert Watson 			cachefree += cache->uc_allocbucket->ub_cnt;
36547a52a97eSRobert Watson 		if (cache->uc_freebucket != NULL)
36557a52a97eSRobert Watson 			cachefree += cache->uc_freebucket->ub_cnt;
36567a52a97eSRobert Watson 		allocs += cache->uc_allocs;
36577a52a97eSRobert Watson 		frees += cache->uc_frees;
36587a52a97eSRobert Watson 	}
36597a52a97eSRobert Watson 	allocs += z->uz_allocs;
36607a52a97eSRobert Watson 	frees += z->uz_frees;
3661bf965959SSean Bruno 	sleeps += z->uz_sleeps;
36627a52a97eSRobert Watson 	if (cachefreep != NULL)
36637a52a97eSRobert Watson 		*cachefreep = cachefree;
36647a52a97eSRobert Watson 	if (allocsp != NULL)
36657a52a97eSRobert Watson 		*allocsp = allocs;
36667a52a97eSRobert Watson 	if (freesp != NULL)
36677a52a97eSRobert Watson 		*freesp = frees;
3668bf965959SSean Bruno 	if (sleepsp != NULL)
3669bf965959SSean Bruno 		*sleepsp = sleeps;
36707a52a97eSRobert Watson }
3671a0d4b0aeSRobert Watson #endif /* DDB */
36727a52a97eSRobert Watson 
36737a52a97eSRobert Watson static int
36747a52a97eSRobert Watson sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS)
36757a52a97eSRobert Watson {
36767a52a97eSRobert Watson 	uma_keg_t kz;
36777a52a97eSRobert Watson 	uma_zone_t z;
36787a52a97eSRobert Watson 	int count;
36797a52a97eSRobert Watson 
36807a52a97eSRobert Watson 	count = 0;
3681111fbcd5SBryan Venteicher 	rw_rlock(&uma_rwlock);
36827a52a97eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
36837a52a97eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link)
36847a52a97eSRobert Watson 			count++;
36857a52a97eSRobert Watson 	}
3686111fbcd5SBryan Venteicher 	rw_runlock(&uma_rwlock);
36877a52a97eSRobert Watson 	return (sysctl_handle_int(oidp, &count, 0, req));
36887a52a97eSRobert Watson }
36897a52a97eSRobert Watson 
36907a52a97eSRobert Watson static int
36917a52a97eSRobert Watson sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
36927a52a97eSRobert Watson {
36937a52a97eSRobert Watson 	struct uma_stream_header ush;
36947a52a97eSRobert Watson 	struct uma_type_header uth;
3695*63b5d112SKonstantin Belousov 	struct uma_percpu_stat *ups;
36967a52a97eSRobert Watson 	uma_bucket_t bucket;
3697ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
36987a52a97eSRobert Watson 	struct sbuf sbuf;
36997a52a97eSRobert Watson 	uma_cache_t cache;
3700e20a199fSJeff Roberson 	uma_klink_t kl;
37017a52a97eSRobert Watson 	uma_keg_t kz;
37027a52a97eSRobert Watson 	uma_zone_t z;
3703e20a199fSJeff Roberson 	uma_keg_t k;
37044e657159SMatthew D Fleming 	int count, error, i;
37057a52a97eSRobert Watson 
370600f0e671SMatthew D Fleming 	error = sysctl_wire_old_buffer(req, 0);
370700f0e671SMatthew D Fleming 	if (error != 0)
370800f0e671SMatthew D Fleming 		return (error);
37094e657159SMatthew D Fleming 	sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
37101eafc078SIan Lepore 	sbuf_clear_flags(&sbuf, SBUF_INCLUDENUL);
3711*63b5d112SKonstantin Belousov 	ups = malloc((mp_maxid + 1) * sizeof(*ups), M_TEMP, M_WAITOK);
37124e657159SMatthew D Fleming 
3713404a593eSMatthew D Fleming 	count = 0;
3714111fbcd5SBryan Venteicher 	rw_rlock(&uma_rwlock);
37157a52a97eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
37167a52a97eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link)
37177a52a97eSRobert Watson 			count++;
37187a52a97eSRobert Watson 	}
37197a52a97eSRobert Watson 
37207a52a97eSRobert Watson 	/*
37217a52a97eSRobert Watson 	 * Insert stream header.
37227a52a97eSRobert Watson 	 */
37237a52a97eSRobert Watson 	bzero(&ush, sizeof(ush));
37247a52a97eSRobert Watson 	ush.ush_version = UMA_STREAM_VERSION;
3725ab3a57c0SRobert Watson 	ush.ush_maxcpus = (mp_maxid + 1);
37267a52a97eSRobert Watson 	ush.ush_count = count;
37274e657159SMatthew D Fleming 	(void)sbuf_bcat(&sbuf, &ush, sizeof(ush));
37287a52a97eSRobert Watson 
37297a52a97eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
37307a52a97eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link) {
37317a52a97eSRobert Watson 			bzero(&uth, sizeof(uth));
37327a52a97eSRobert Watson 			ZONE_LOCK(z);
3733cbbb4a00SRobert Watson 			strlcpy(uth.uth_name, z->uz_name, UTH_MAX_NAME);
37347a52a97eSRobert Watson 			uth.uth_align = kz->uk_align;
37357a52a97eSRobert Watson 			uth.uth_size = kz->uk_size;
37367a52a97eSRobert Watson 			uth.uth_rsize = kz->uk_rsize;
3737e20a199fSJeff Roberson 			LIST_FOREACH(kl, &z->uz_kegs, kl_link) {
3738e20a199fSJeff Roberson 				k = kl->kl_keg;
3739e20a199fSJeff Roberson 				uth.uth_maxpages += k->uk_maxpages;
3740e20a199fSJeff Roberson 				uth.uth_pages += k->uk_pages;
3741e20a199fSJeff Roberson 				uth.uth_keg_free += k->uk_free;
3742e20a199fSJeff Roberson 				uth.uth_limit = (k->uk_maxpages / k->uk_ppera)
3743e20a199fSJeff Roberson 				    * k->uk_ipers;
3744e20a199fSJeff Roberson 			}
3745cbbb4a00SRobert Watson 
3746cbbb4a00SRobert Watson 			/*
3747cbbb4a00SRobert Watson 			 * A zone is secondary is it is not the first entry
3748cbbb4a00SRobert Watson 			 * on the keg's zone list.
3749cbbb4a00SRobert Watson 			 */
3750e20a199fSJeff Roberson 			if ((z->uz_flags & UMA_ZONE_SECONDARY) &&
3751cbbb4a00SRobert Watson 			    (LIST_FIRST(&kz->uk_zones) != z))
3752cbbb4a00SRobert Watson 				uth.uth_zone_flags = UTH_ZONE_SECONDARY;
3753cbbb4a00SRobert Watson 
3754ab3185d1SJeff Roberson 			for (i = 0; i < vm_ndomains; i++) {
3755ab3185d1SJeff Roberson 				zdom = &z->uz_domain[i];
3756ab3185d1SJeff Roberson 				LIST_FOREACH(bucket, &zdom->uzd_buckets,
3757ab3185d1SJeff Roberson 				    ub_link)
37587a52a97eSRobert Watson 					uth.uth_zone_free += bucket->ub_cnt;
3759ab3185d1SJeff Roberson 			}
37607a52a97eSRobert Watson 			uth.uth_allocs = z->uz_allocs;
37617a52a97eSRobert Watson 			uth.uth_frees = z->uz_frees;
37622019094aSRobert Watson 			uth.uth_fails = z->uz_fails;
3763bf965959SSean Bruno 			uth.uth_sleeps = z->uz_sleeps;
37647a52a97eSRobert Watson 			/*
37652450bbb8SRobert Watson 			 * While it is not normally safe to access the cache
37662450bbb8SRobert Watson 			 * bucket pointers while not on the CPU that owns the
37672450bbb8SRobert Watson 			 * cache, we only allow the pointers to be exchanged
37682450bbb8SRobert Watson 			 * without the zone lock held, not invalidated, so
37692450bbb8SRobert Watson 			 * accept the possible race associated with bucket
37702450bbb8SRobert Watson 			 * exchange during monitoring.
37717a52a97eSRobert Watson 			 */
3772*63b5d112SKonstantin Belousov 			for (i = 0; i < mp_maxid + 1; i++) {
3773*63b5d112SKonstantin Belousov 				bzero(&ups[i], sizeof(*ups));
3774*63b5d112SKonstantin Belousov 				if (kz->uk_flags & UMA_ZFLAG_INTERNAL ||
3775*63b5d112SKonstantin Belousov 				    CPU_ABSENT(i))
3776*63b5d112SKonstantin Belousov 					continue;
37777a52a97eSRobert Watson 				cache = &z->uz_cpu[i];
37787a52a97eSRobert Watson 				if (cache->uc_allocbucket != NULL)
3779*63b5d112SKonstantin Belousov 					ups[i].ups_cache_free +=
37807a52a97eSRobert Watson 					    cache->uc_allocbucket->ub_cnt;
37817a52a97eSRobert Watson 				if (cache->uc_freebucket != NULL)
3782*63b5d112SKonstantin Belousov 					ups[i].ups_cache_free +=
37837a52a97eSRobert Watson 					    cache->uc_freebucket->ub_cnt;
3784*63b5d112SKonstantin Belousov 				ups[i].ups_allocs = cache->uc_allocs;
3785*63b5d112SKonstantin Belousov 				ups[i].ups_frees = cache->uc_frees;
37867a52a97eSRobert Watson 			}
37872450bbb8SRobert Watson 			ZONE_UNLOCK(z);
3788*63b5d112SKonstantin Belousov 			(void)sbuf_bcat(&sbuf, &uth, sizeof(uth));
3789*63b5d112SKonstantin Belousov 			for (i = 0; i < mp_maxid + 1; i++)
3790*63b5d112SKonstantin Belousov 				(void)sbuf_bcat(&sbuf, &ups[i], sizeof(ups[i]));
37917a52a97eSRobert Watson 		}
37927a52a97eSRobert Watson 	}
3793111fbcd5SBryan Venteicher 	rw_runlock(&uma_rwlock);
37944e657159SMatthew D Fleming 	error = sbuf_finish(&sbuf);
37954e657159SMatthew D Fleming 	sbuf_delete(&sbuf);
3796*63b5d112SKonstantin Belousov 	free(ups, M_TEMP);
37977a52a97eSRobert Watson 	return (error);
37987a52a97eSRobert Watson }
379948c5777eSRobert Watson 
38000a5a3ccbSGleb Smirnoff int
38010a5a3ccbSGleb Smirnoff sysctl_handle_uma_zone_max(SYSCTL_HANDLER_ARGS)
38020a5a3ccbSGleb Smirnoff {
38030a5a3ccbSGleb Smirnoff 	uma_zone_t zone = *(uma_zone_t *)arg1;
380416be9f54SGleb Smirnoff 	int error, max;
38050a5a3ccbSGleb Smirnoff 
380616be9f54SGleb Smirnoff 	max = uma_zone_get_max(zone);
38070a5a3ccbSGleb Smirnoff 	error = sysctl_handle_int(oidp, &max, 0, req);
38080a5a3ccbSGleb Smirnoff 	if (error || !req->newptr)
38090a5a3ccbSGleb Smirnoff 		return (error);
38100a5a3ccbSGleb Smirnoff 
38110a5a3ccbSGleb Smirnoff 	uma_zone_set_max(zone, max);
38120a5a3ccbSGleb Smirnoff 
38130a5a3ccbSGleb Smirnoff 	return (0);
38140a5a3ccbSGleb Smirnoff }
38150a5a3ccbSGleb Smirnoff 
38160a5a3ccbSGleb Smirnoff int
38170a5a3ccbSGleb Smirnoff sysctl_handle_uma_zone_cur(SYSCTL_HANDLER_ARGS)
38180a5a3ccbSGleb Smirnoff {
38190a5a3ccbSGleb Smirnoff 	uma_zone_t zone = *(uma_zone_t *)arg1;
38200a5a3ccbSGleb Smirnoff 	int cur;
38210a5a3ccbSGleb Smirnoff 
38220a5a3ccbSGleb Smirnoff 	cur = uma_zone_get_cur(zone);
38230a5a3ccbSGleb Smirnoff 	return (sysctl_handle_int(oidp, &cur, 0, req));
38240a5a3ccbSGleb Smirnoff }
38250a5a3ccbSGleb Smirnoff 
38269542ea7bSGleb Smirnoff #ifdef INVARIANTS
38279542ea7bSGleb Smirnoff static uma_slab_t
38289542ea7bSGleb Smirnoff uma_dbg_getslab(uma_zone_t zone, void *item)
38299542ea7bSGleb Smirnoff {
38309542ea7bSGleb Smirnoff 	uma_slab_t slab;
38319542ea7bSGleb Smirnoff 	uma_keg_t keg;
38329542ea7bSGleb Smirnoff 	uint8_t *mem;
38339542ea7bSGleb Smirnoff 
38349542ea7bSGleb Smirnoff 	mem = (uint8_t *)((uintptr_t)item & (~UMA_SLAB_MASK));
38359542ea7bSGleb Smirnoff 	if (zone->uz_flags & UMA_ZONE_VTOSLAB) {
38369542ea7bSGleb Smirnoff 		slab = vtoslab((vm_offset_t)mem);
38379542ea7bSGleb Smirnoff 	} else {
38389542ea7bSGleb Smirnoff 		/*
38399542ea7bSGleb Smirnoff 		 * It is safe to return the slab here even though the
38409542ea7bSGleb Smirnoff 		 * zone is unlocked because the item's allocation state
38419542ea7bSGleb Smirnoff 		 * essentially holds a reference.
38429542ea7bSGleb Smirnoff 		 */
38439542ea7bSGleb Smirnoff 		ZONE_LOCK(zone);
38449542ea7bSGleb Smirnoff 		keg = LIST_FIRST(&zone->uz_kegs)->kl_keg;
38459542ea7bSGleb Smirnoff 		if (keg->uk_flags & UMA_ZONE_HASH)
38469542ea7bSGleb Smirnoff 			slab = hash_sfind(&keg->uk_hash, mem);
38479542ea7bSGleb Smirnoff 		else
38489542ea7bSGleb Smirnoff 			slab = (uma_slab_t)(mem + keg->uk_pgoff);
38499542ea7bSGleb Smirnoff 		ZONE_UNLOCK(zone);
38509542ea7bSGleb Smirnoff 	}
38519542ea7bSGleb Smirnoff 
38529542ea7bSGleb Smirnoff 	return (slab);
38539542ea7bSGleb Smirnoff }
38549542ea7bSGleb Smirnoff 
38559542ea7bSGleb Smirnoff /*
38569542ea7bSGleb Smirnoff  * Set up the slab's freei data such that uma_dbg_free can function.
38579542ea7bSGleb Smirnoff  *
38589542ea7bSGleb Smirnoff  */
38599542ea7bSGleb Smirnoff static void
38609542ea7bSGleb Smirnoff uma_dbg_alloc(uma_zone_t zone, uma_slab_t slab, void *item)
38619542ea7bSGleb Smirnoff {
38629542ea7bSGleb Smirnoff 	uma_keg_t keg;
38639542ea7bSGleb Smirnoff 	int freei;
38649542ea7bSGleb Smirnoff 
38659542ea7bSGleb Smirnoff 	if (zone_first_keg(zone) == NULL)
38669542ea7bSGleb Smirnoff 		return;
38679542ea7bSGleb Smirnoff 	if (slab == NULL) {
38689542ea7bSGleb Smirnoff 		slab = uma_dbg_getslab(zone, item);
38699542ea7bSGleb Smirnoff 		if (slab == NULL)
38709542ea7bSGleb Smirnoff 			panic("uma: item %p did not belong to zone %s\n",
38719542ea7bSGleb Smirnoff 			    item, zone->uz_name);
38729542ea7bSGleb Smirnoff 	}
38739542ea7bSGleb Smirnoff 	keg = slab->us_keg;
38749542ea7bSGleb Smirnoff 	freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize;
38759542ea7bSGleb Smirnoff 
38769542ea7bSGleb Smirnoff 	if (BIT_ISSET(SLAB_SETSIZE, freei, &slab->us_debugfree))
38779542ea7bSGleb Smirnoff 		panic("Duplicate alloc of %p from zone %p(%s) slab %p(%d)\n",
38789542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
38799542ea7bSGleb Smirnoff 	BIT_SET_ATOMIC(SLAB_SETSIZE, freei, &slab->us_debugfree);
38809542ea7bSGleb Smirnoff 
38819542ea7bSGleb Smirnoff 	return;
38829542ea7bSGleb Smirnoff }
38839542ea7bSGleb Smirnoff 
38849542ea7bSGleb Smirnoff /*
38859542ea7bSGleb Smirnoff  * Verifies freed addresses.  Checks for alignment, valid slab membership
38869542ea7bSGleb Smirnoff  * and duplicate frees.
38879542ea7bSGleb Smirnoff  *
38889542ea7bSGleb Smirnoff  */
38899542ea7bSGleb Smirnoff static void
38909542ea7bSGleb Smirnoff uma_dbg_free(uma_zone_t zone, uma_slab_t slab, void *item)
38919542ea7bSGleb Smirnoff {
38929542ea7bSGleb Smirnoff 	uma_keg_t keg;
38939542ea7bSGleb Smirnoff 	int freei;
38949542ea7bSGleb Smirnoff 
38959542ea7bSGleb Smirnoff 	if (zone_first_keg(zone) == NULL)
38969542ea7bSGleb Smirnoff 		return;
38979542ea7bSGleb Smirnoff 	if (slab == NULL) {
38989542ea7bSGleb Smirnoff 		slab = uma_dbg_getslab(zone, item);
38999542ea7bSGleb Smirnoff 		if (slab == NULL)
39009542ea7bSGleb Smirnoff 			panic("uma: Freed item %p did not belong to zone %s\n",
39019542ea7bSGleb Smirnoff 			    item, zone->uz_name);
39029542ea7bSGleb Smirnoff 	}
39039542ea7bSGleb Smirnoff 	keg = slab->us_keg;
39049542ea7bSGleb Smirnoff 	freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize;
39059542ea7bSGleb Smirnoff 
39069542ea7bSGleb Smirnoff 	if (freei >= keg->uk_ipers)
39079542ea7bSGleb Smirnoff 		panic("Invalid free of %p from zone %p(%s) slab %p(%d)\n",
39089542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
39099542ea7bSGleb Smirnoff 
39109542ea7bSGleb Smirnoff 	if (((freei * keg->uk_rsize) + slab->us_data) != item)
39119542ea7bSGleb Smirnoff 		panic("Unaligned free of %p from zone %p(%s) slab %p(%d)\n",
39129542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
39139542ea7bSGleb Smirnoff 
39149542ea7bSGleb Smirnoff 	if (!BIT_ISSET(SLAB_SETSIZE, freei, &slab->us_debugfree))
39159542ea7bSGleb Smirnoff 		panic("Duplicate free of %p from zone %p(%s) slab %p(%d)\n",
39169542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
39179542ea7bSGleb Smirnoff 
39189542ea7bSGleb Smirnoff 	BIT_CLR_ATOMIC(SLAB_SETSIZE, freei, &slab->us_debugfree);
39199542ea7bSGleb Smirnoff }
39209542ea7bSGleb Smirnoff #endif /* INVARIANTS */
39219542ea7bSGleb Smirnoff 
392248c5777eSRobert Watson #ifdef DDB
392348c5777eSRobert Watson DB_SHOW_COMMAND(uma, db_show_uma)
392448c5777eSRobert Watson {
392548c5777eSRobert Watson 	uma_bucket_t bucket;
392648c5777eSRobert Watson 	uma_keg_t kz;
392748c5777eSRobert Watson 	uma_zone_t z;
3928ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
3929ab3185d1SJeff Roberson 	uint64_t allocs, frees, sleeps;
3930ab3185d1SJeff Roberson 	int cachefree, i;
393148c5777eSRobert Watson 
393203175483SAlexander Motin 	db_printf("%18s %8s %8s %8s %12s %8s %8s\n", "Zone", "Size", "Used",
393303175483SAlexander Motin 	    "Free", "Requests", "Sleeps", "Bucket");
393448c5777eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
393548c5777eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link) {
393648c5777eSRobert Watson 			if (kz->uk_flags & UMA_ZFLAG_INTERNAL) {
393748c5777eSRobert Watson 				allocs = z->uz_allocs;
393848c5777eSRobert Watson 				frees = z->uz_frees;
3939bf965959SSean Bruno 				sleeps = z->uz_sleeps;
394048c5777eSRobert Watson 				cachefree = 0;
394148c5777eSRobert Watson 			} else
394248c5777eSRobert Watson 				uma_zone_sumstat(z, &cachefree, &allocs,
3943bf965959SSean Bruno 				    &frees, &sleeps);
3944e20a199fSJeff Roberson 			if (!((z->uz_flags & UMA_ZONE_SECONDARY) &&
394548c5777eSRobert Watson 			    (LIST_FIRST(&kz->uk_zones) != z)))
394648c5777eSRobert Watson 				cachefree += kz->uk_free;
3947ab3185d1SJeff Roberson 			for (i = 0; i < vm_ndomains; i++) {
3948ab3185d1SJeff Roberson 				zdom = &z->uz_domain[i];
3949ab3185d1SJeff Roberson 				LIST_FOREACH(bucket, &zdom->uzd_buckets,
3950ab3185d1SJeff Roberson 				    ub_link)
395148c5777eSRobert Watson 					cachefree += bucket->ub_cnt;
3952ab3185d1SJeff Roberson 			}
395303175483SAlexander Motin 			db_printf("%18s %8ju %8jd %8d %12ju %8ju %8u\n",
395403175483SAlexander Motin 			    z->uz_name, (uintmax_t)kz->uk_size,
3955ae4e9636SRobert Watson 			    (intmax_t)(allocs - frees), cachefree,
395603175483SAlexander Motin 			    (uintmax_t)allocs, sleeps, z->uz_count);
3957687c94aaSJohn Baldwin 			if (db_pager_quit)
3958687c94aaSJohn Baldwin 				return;
395948c5777eSRobert Watson 		}
396048c5777eSRobert Watson 	}
396148c5777eSRobert Watson }
396203175483SAlexander Motin 
396303175483SAlexander Motin DB_SHOW_COMMAND(umacache, db_show_umacache)
396403175483SAlexander Motin {
396503175483SAlexander Motin 	uma_bucket_t bucket;
396603175483SAlexander Motin 	uma_zone_t z;
3967ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
3968ab3185d1SJeff Roberson 	uint64_t allocs, frees;
3969ab3185d1SJeff Roberson 	int cachefree, i;
397003175483SAlexander Motin 
397103175483SAlexander Motin 	db_printf("%18s %8s %8s %8s %12s %8s\n", "Zone", "Size", "Used", "Free",
397203175483SAlexander Motin 	    "Requests", "Bucket");
397303175483SAlexander Motin 	LIST_FOREACH(z, &uma_cachezones, uz_link) {
397403175483SAlexander Motin 		uma_zone_sumstat(z, &cachefree, &allocs, &frees, NULL);
3975ab3185d1SJeff Roberson 		for (i = 0; i < vm_ndomains; i++) {
3976ab3185d1SJeff Roberson 			zdom = &z->uz_domain[i];
3977ab3185d1SJeff Roberson 			LIST_FOREACH(bucket, &zdom->uzd_buckets, ub_link)
397803175483SAlexander Motin 				cachefree += bucket->ub_cnt;
3979ab3185d1SJeff Roberson 		}
398003175483SAlexander Motin 		db_printf("%18s %8ju %8jd %8d %12ju %8u\n",
398103175483SAlexander Motin 		    z->uz_name, (uintmax_t)z->uz_size,
398203175483SAlexander Motin 		    (intmax_t)(allocs - frees), cachefree,
398303175483SAlexander Motin 		    (uintmax_t)allocs, z->uz_count);
398403175483SAlexander Motin 		if (db_pager_quit)
398503175483SAlexander Motin 			return;
398603175483SAlexander Motin 	}
398703175483SAlexander Motin }
39889542ea7bSGleb Smirnoff #endif	/* DDB */
3989