xref: /freebsd/sys/vm/uma_core.c (revision a03af34228f36701abf506dfd0103b0b03cfcee0)
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);
232ab3059a8SMatt Macy static void *pcpu_page_alloc(uma_zone_t, vm_size_t, int, uint8_t *, int);
233ab3185d1SJeff Roberson static void *startup_alloc(uma_zone_t, vm_size_t, int, uint8_t *, int);
234f2c2231eSRyan Stone static void page_free(void *, vm_size_t, uint8_t);
235ab3059a8SMatt Macy static void pcpu_page_free(void *, vm_size_t, uint8_t);
236ab3185d1SJeff Roberson static uma_slab_t keg_alloc_slab(uma_keg_t, uma_zone_t, int, int);
2379643769aSJeff Roberson static void cache_drain(uma_zone_t);
2388355f576SJeff Roberson static void bucket_drain(uma_zone_t, uma_bucket_t);
239aaa8bb16SJeff Roberson static void bucket_cache_drain(uma_zone_t zone);
240b23f72e9SBrian Feldman static int keg_ctor(void *, int, void *, int);
241099a0e58SBosko Milekic static void keg_dtor(void *, int, void *);
242b23f72e9SBrian Feldman static int zone_ctor(void *, int, void *, int);
2439c2cd7e5SJeff Roberson static void zone_dtor(void *, int, void *);
244b23f72e9SBrian Feldman static int zero_init(void *, int, int);
245e20a199fSJeff Roberson static void keg_small_init(uma_keg_t keg);
246e20a199fSJeff Roberson static void keg_large_init(uma_keg_t keg);
2478355f576SJeff Roberson static void zone_foreach(void (*zfunc)(uma_zone_t));
2488355f576SJeff Roberson static void zone_timeout(uma_zone_t zone);
2490aef6126SJeff Roberson static int hash_alloc(struct uma_hash *);
2500aef6126SJeff Roberson static int hash_expand(struct uma_hash *, struct uma_hash *);
2510aef6126SJeff Roberson static void hash_free(struct uma_hash *hash);
2528355f576SJeff Roberson static void uma_timeout(void *);
2538355f576SJeff Roberson static void uma_startup3(void);
254ab3185d1SJeff Roberson static void *zone_alloc_item(uma_zone_t, void *, int, int);
2550095a784SJeff Roberson static void zone_free_item(uma_zone_t, void *, void *, enum zfreeskip);
25686bbae32SJeff Roberson static void bucket_enable(void);
257cae33c14SJeff Roberson static void bucket_init(void);
2586fd34d6fSJeff Roberson static uma_bucket_t bucket_alloc(uma_zone_t zone, void *, int);
2596fd34d6fSJeff Roberson static void bucket_free(uma_zone_t zone, uma_bucket_t, void *);
260cae33c14SJeff Roberson static void bucket_zone_drain(void);
261ab3185d1SJeff Roberson static uma_bucket_t zone_alloc_bucket(uma_zone_t, void *, int, int);
262ab3185d1SJeff Roberson static uma_slab_t zone_fetch_slab(uma_zone_t, uma_keg_t, int, int);
263ab3185d1SJeff Roberson static uma_slab_t zone_fetch_slab_multi(uma_zone_t, uma_keg_t, int, int);
2640095a784SJeff Roberson static void *slab_alloc_item(uma_keg_t keg, uma_slab_t slab);
2650095a784SJeff Roberson static void slab_free_item(uma_keg_t keg, uma_slab_t slab, void *item);
266e20a199fSJeff Roberson static uma_keg_t uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit,
26785dcf349SGleb Smirnoff     uma_fini fini, int align, uint32_t flags);
268ab3185d1SJeff Roberson static int zone_import(uma_zone_t, void **, int, int, int);
269ab3185d1SJeff Roberson static void zone_release(uma_zone_t, void **, int);
270ab3185d1SJeff Roberson static void uma_zero_item(void *, uma_zone_t);
271bbee39c6SJeff Roberson 
2728355f576SJeff Roberson void uma_print_zone(uma_zone_t);
2738355f576SJeff Roberson void uma_print_stats(void);
2747a52a97eSRobert Watson static int sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS);
2757a52a97eSRobert Watson static int sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS);
2768355f576SJeff Roberson 
2779542ea7bSGleb Smirnoff #ifdef INVARIANTS
278c5deaf04SGleb Smirnoff static bool uma_dbg_kskip(uma_keg_t keg, void *mem);
279c5deaf04SGleb Smirnoff static bool uma_dbg_zskip(uma_zone_t zone, void *mem);
2809542ea7bSGleb Smirnoff static void uma_dbg_free(uma_zone_t zone, uma_slab_t slab, void *item);
2819542ea7bSGleb Smirnoff static void uma_dbg_alloc(uma_zone_t zone, uma_slab_t slab, void *item);
282c5deaf04SGleb Smirnoff 
283c5deaf04SGleb Smirnoff static SYSCTL_NODE(_vm, OID_AUTO, debug, CTLFLAG_RD, 0,
284c5deaf04SGleb Smirnoff     "Memory allocation debugging");
285c5deaf04SGleb Smirnoff 
286c5deaf04SGleb Smirnoff static u_int dbg_divisor = 1;
287c5deaf04SGleb Smirnoff SYSCTL_UINT(_vm_debug, OID_AUTO, divisor,
288c5deaf04SGleb Smirnoff     CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &dbg_divisor, 0,
289c5deaf04SGleb Smirnoff     "Debug & thrash every this item in memory allocator");
290c5deaf04SGleb Smirnoff 
291c5deaf04SGleb Smirnoff static counter_u64_t uma_dbg_cnt = EARLY_COUNTER;
292c5deaf04SGleb Smirnoff static counter_u64_t uma_skip_cnt = EARLY_COUNTER;
293c5deaf04SGleb Smirnoff SYSCTL_COUNTER_U64(_vm_debug, OID_AUTO, trashed, CTLFLAG_RD,
294c5deaf04SGleb Smirnoff     &uma_dbg_cnt, "memory items debugged");
295c5deaf04SGleb Smirnoff SYSCTL_COUNTER_U64(_vm_debug, OID_AUTO, skipped, CTLFLAG_RD,
296c5deaf04SGleb Smirnoff     &uma_skip_cnt, "memory items skipped, not debugged");
2979542ea7bSGleb Smirnoff #endif
2989542ea7bSGleb Smirnoff 
2998355f576SJeff Roberson SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL);
3008355f576SJeff Roberson 
3017a52a97eSRobert Watson SYSCTL_PROC(_vm, OID_AUTO, zone_count, CTLFLAG_RD|CTLTYPE_INT,
3027a52a97eSRobert Watson     0, 0, sysctl_vm_zone_count, "I", "Number of UMA zones");
3037a52a97eSRobert Watson 
3047a52a97eSRobert Watson SYSCTL_PROC(_vm, OID_AUTO, zone_stats, CTLFLAG_RD|CTLTYPE_STRUCT,
3057a52a97eSRobert Watson     0, 0, sysctl_vm_zone_stats, "s,struct uma_type_header", "Zone Stats");
3067a52a97eSRobert Watson 
3072f891cd5SPawel Jakub Dawidek static int zone_warnings = 1;
308af3b2549SHans Petter Selasky SYSCTL_INT(_vm, OID_AUTO, zone_warnings, CTLFLAG_RWTUN, &zone_warnings, 0,
3092f891cd5SPawel Jakub Dawidek     "Warn when UMA zones becomes full");
3102f891cd5SPawel Jakub Dawidek 
3112e47807cSJeff Roberson /* Adjust bytes under management by UMA. */
3122e47807cSJeff Roberson static inline void
3132e47807cSJeff Roberson uma_total_dec(unsigned long size)
3142e47807cSJeff Roberson {
3152e47807cSJeff Roberson 
3162e47807cSJeff Roberson 	atomic_subtract_long(&uma_kmem_total, size);
3172e47807cSJeff Roberson }
3182e47807cSJeff Roberson 
3192e47807cSJeff Roberson static inline void
3202e47807cSJeff Roberson uma_total_inc(unsigned long size)
3212e47807cSJeff Roberson {
3222e47807cSJeff Roberson 
3232e47807cSJeff Roberson 	if (atomic_fetchadd_long(&uma_kmem_total, size) > uma_kmem_limit)
3242e47807cSJeff Roberson 		uma_reclaim_wakeup();
3252e47807cSJeff Roberson }
3262e47807cSJeff Roberson 
32786bbae32SJeff Roberson /*
32886bbae32SJeff Roberson  * This routine checks to see whether or not it's safe to enable buckets.
32986bbae32SJeff Roberson  */
33086bbae32SJeff Roberson static void
33186bbae32SJeff Roberson bucket_enable(void)
33286bbae32SJeff Roberson {
333251386b4SMaksim Yevmenkin 	bucketdisable = vm_page_count_min();
33486bbae32SJeff Roberson }
33586bbae32SJeff Roberson 
336dc2c7965SRobert Watson /*
337dc2c7965SRobert Watson  * Initialize bucket_zones, the array of zones of buckets of various sizes.
338dc2c7965SRobert Watson  *
339dc2c7965SRobert Watson  * For each zone, calculate the memory required for each bucket, consisting
340fc03d22bSJeff Roberson  * of the header and an array of pointers.
341dc2c7965SRobert Watson  */
342cae33c14SJeff Roberson static void
343cae33c14SJeff Roberson bucket_init(void)
344cae33c14SJeff Roberson {
345cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
346cae33c14SJeff Roberson 	int size;
347cae33c14SJeff Roberson 
348d74e6a1dSAlan Cox 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++) {
349cae33c14SJeff Roberson 		size = roundup(sizeof(struct uma_bucket), sizeof(void *));
350cae33c14SJeff Roberson 		size += sizeof(void *) * ubz->ubz_entries;
351cae33c14SJeff Roberson 		ubz->ubz_zone = uma_zcreate(ubz->ubz_name, size,
352e20a199fSJeff Roberson 		    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,
353ab3185d1SJeff Roberson 		    UMA_ZONE_MTXCLASS | UMA_ZFLAG_BUCKET | UMA_ZONE_NUMA);
354cae33c14SJeff Roberson 	}
355cae33c14SJeff Roberson }
356cae33c14SJeff Roberson 
357dc2c7965SRobert Watson /*
358dc2c7965SRobert Watson  * Given a desired number of entries for a bucket, return the zone from which
359dc2c7965SRobert Watson  * to allocate the bucket.
360dc2c7965SRobert Watson  */
361dc2c7965SRobert Watson static struct uma_bucket_zone *
362dc2c7965SRobert Watson bucket_zone_lookup(int entries)
363dc2c7965SRobert Watson {
364fc03d22bSJeff Roberson 	struct uma_bucket_zone *ubz;
365dc2c7965SRobert Watson 
366fc03d22bSJeff Roberson 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++)
367fc03d22bSJeff Roberson 		if (ubz->ubz_entries >= entries)
368fc03d22bSJeff Roberson 			return (ubz);
369fc03d22bSJeff Roberson 	ubz--;
370fc03d22bSJeff Roberson 	return (ubz);
371fc03d22bSJeff Roberson }
372fc03d22bSJeff Roberson 
373fc03d22bSJeff Roberson static int
374fc03d22bSJeff Roberson bucket_select(int size)
375fc03d22bSJeff Roberson {
376fc03d22bSJeff Roberson 	struct uma_bucket_zone *ubz;
377fc03d22bSJeff Roberson 
378fc03d22bSJeff Roberson 	ubz = &bucket_zones[0];
379fc03d22bSJeff Roberson 	if (size > ubz->ubz_maxsize)
380fc03d22bSJeff Roberson 		return MAX((ubz->ubz_maxsize * ubz->ubz_entries) / size, 1);
381fc03d22bSJeff Roberson 
382fc03d22bSJeff Roberson 	for (; ubz->ubz_entries != 0; ubz++)
383fc03d22bSJeff Roberson 		if (ubz->ubz_maxsize < size)
384fc03d22bSJeff Roberson 			break;
385fc03d22bSJeff Roberson 	ubz--;
386fc03d22bSJeff Roberson 	return (ubz->ubz_entries);
387dc2c7965SRobert Watson }
388dc2c7965SRobert Watson 
389cae33c14SJeff Roberson static uma_bucket_t
3906fd34d6fSJeff Roberson bucket_alloc(uma_zone_t zone, void *udata, int flags)
391cae33c14SJeff Roberson {
392cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
393cae33c14SJeff Roberson 	uma_bucket_t bucket;
394cae33c14SJeff Roberson 
395cae33c14SJeff Roberson 	/*
396cae33c14SJeff Roberson 	 * This is to stop us from allocating per cpu buckets while we're
3973803b26bSDag-Erling Smørgrav 	 * running out of vm.boot_pages.  Otherwise, we would exhaust the
398cae33c14SJeff Roberson 	 * boot pages.  This also prevents us from allocating buckets in
399cae33c14SJeff Roberson 	 * low memory situations.
400cae33c14SJeff Roberson 	 */
401cae33c14SJeff Roberson 	if (bucketdisable)
402cae33c14SJeff Roberson 		return (NULL);
4036fd34d6fSJeff Roberson 	/*
4046fd34d6fSJeff Roberson 	 * To limit bucket recursion we store the original zone flags
4056fd34d6fSJeff Roberson 	 * in a cookie passed via zalloc_arg/zfree_arg.  This allows the
4066fd34d6fSJeff Roberson 	 * NOVM flag to persist even through deep recursions.  We also
4076fd34d6fSJeff Roberson 	 * store ZFLAG_BUCKET once we have recursed attempting to allocate
4086fd34d6fSJeff Roberson 	 * a bucket for a bucket zone so we do not allow infinite bucket
4096fd34d6fSJeff Roberson 	 * recursion.  This cookie will even persist to frees of unused
4106fd34d6fSJeff Roberson 	 * buckets via the allocation path or bucket allocations in the
4116fd34d6fSJeff Roberson 	 * free path.
4126fd34d6fSJeff Roberson 	 */
4136fd34d6fSJeff Roberson 	if ((zone->uz_flags & UMA_ZFLAG_BUCKET) == 0)
4146fd34d6fSJeff Roberson 		udata = (void *)(uintptr_t)zone->uz_flags;
415e8a720feSAlexander Motin 	else {
416e8a720feSAlexander Motin 		if ((uintptr_t)udata & UMA_ZFLAG_BUCKET)
417e8a720feSAlexander Motin 			return (NULL);
4186fd34d6fSJeff Roberson 		udata = (void *)((uintptr_t)udata | UMA_ZFLAG_BUCKET);
419e8a720feSAlexander Motin 	}
4206fd34d6fSJeff Roberson 	if ((uintptr_t)udata & UMA_ZFLAG_CACHEONLY)
421af526374SJeff Roberson 		flags |= M_NOVM;
422af526374SJeff Roberson 	ubz = bucket_zone_lookup(zone->uz_count);
42320d3ab87SAlexander Motin 	if (ubz->ubz_zone == zone && (ubz + 1)->ubz_entries != 0)
42420d3ab87SAlexander Motin 		ubz++;
4256fd34d6fSJeff Roberson 	bucket = uma_zalloc_arg(ubz->ubz_zone, udata, flags);
426cae33c14SJeff Roberson 	if (bucket) {
427cae33c14SJeff Roberson #ifdef INVARIANTS
428cae33c14SJeff Roberson 		bzero(bucket->ub_bucket, sizeof(void *) * ubz->ubz_entries);
429cae33c14SJeff Roberson #endif
430cae33c14SJeff Roberson 		bucket->ub_cnt = 0;
431cae33c14SJeff Roberson 		bucket->ub_entries = ubz->ubz_entries;
432cae33c14SJeff Roberson 	}
433cae33c14SJeff Roberson 
434cae33c14SJeff Roberson 	return (bucket);
435cae33c14SJeff Roberson }
436cae33c14SJeff Roberson 
437cae33c14SJeff Roberson static void
4386fd34d6fSJeff Roberson bucket_free(uma_zone_t zone, uma_bucket_t bucket, void *udata)
439cae33c14SJeff Roberson {
440cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
441cae33c14SJeff Roberson 
442fc03d22bSJeff Roberson 	KASSERT(bucket->ub_cnt == 0,
443fc03d22bSJeff Roberson 	    ("bucket_free: Freeing a non free bucket."));
4446fd34d6fSJeff Roberson 	if ((zone->uz_flags & UMA_ZFLAG_BUCKET) == 0)
4456fd34d6fSJeff Roberson 		udata = (void *)(uintptr_t)zone->uz_flags;
446dc2c7965SRobert Watson 	ubz = bucket_zone_lookup(bucket->ub_entries);
4476fd34d6fSJeff Roberson 	uma_zfree_arg(ubz->ubz_zone, bucket, udata);
448cae33c14SJeff Roberson }
449cae33c14SJeff Roberson 
450cae33c14SJeff Roberson static void
451cae33c14SJeff Roberson bucket_zone_drain(void)
452cae33c14SJeff Roberson {
453cae33c14SJeff Roberson 	struct uma_bucket_zone *ubz;
454cae33c14SJeff Roberson 
455cae33c14SJeff Roberson 	for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++)
456cae33c14SJeff Roberson 		zone_drain(ubz->ubz_zone);
457cae33c14SJeff Roberson }
458cae33c14SJeff Roberson 
4592f891cd5SPawel Jakub Dawidek static void
4602f891cd5SPawel Jakub Dawidek zone_log_warning(uma_zone_t zone)
4612f891cd5SPawel Jakub Dawidek {
4622f891cd5SPawel Jakub Dawidek 	static const struct timeval warninterval = { 300, 0 };
4632f891cd5SPawel Jakub Dawidek 
4642f891cd5SPawel Jakub Dawidek 	if (!zone_warnings || zone->uz_warning == NULL)
4652f891cd5SPawel Jakub Dawidek 		return;
4662f891cd5SPawel Jakub Dawidek 
4672f891cd5SPawel Jakub Dawidek 	if (ratecheck(&zone->uz_ratecheck, &warninterval))
4682f891cd5SPawel Jakub Dawidek 		printf("[zone: %s] %s\n", zone->uz_name, zone->uz_warning);
4692f891cd5SPawel Jakub Dawidek }
4702f891cd5SPawel Jakub Dawidek 
47154503a13SJonathan T. Looney static inline void
47254503a13SJonathan T. Looney zone_maxaction(uma_zone_t zone)
47354503a13SJonathan T. Looney {
474e60b2fcbSGleb Smirnoff 
475e60b2fcbSGleb Smirnoff 	if (zone->uz_maxaction.ta_func != NULL)
476e60b2fcbSGleb Smirnoff 		taskqueue_enqueue(taskqueue_thread, &zone->uz_maxaction);
47754503a13SJonathan T. Looney }
47854503a13SJonathan T. Looney 
479e20a199fSJeff Roberson static void
480e20a199fSJeff Roberson zone_foreach_keg(uma_zone_t zone, void (*kegfn)(uma_keg_t))
481e20a199fSJeff Roberson {
482e20a199fSJeff Roberson 	uma_klink_t klink;
483e20a199fSJeff Roberson 
484e20a199fSJeff Roberson 	LIST_FOREACH(klink, &zone->uz_kegs, kl_link)
485e20a199fSJeff Roberson 		kegfn(klink->kl_keg);
486e20a199fSJeff Roberson }
4878355f576SJeff Roberson 
4888355f576SJeff Roberson /*
4898355f576SJeff Roberson  * Routine called by timeout which is used to fire off some time interval
4909643769aSJeff Roberson  * based calculations.  (stats, hash size, etc.)
4918355f576SJeff Roberson  *
4928355f576SJeff Roberson  * Arguments:
4938355f576SJeff Roberson  *	arg   Unused
4948355f576SJeff Roberson  *
4958355f576SJeff Roberson  * Returns:
4968355f576SJeff Roberson  *	Nothing
4978355f576SJeff Roberson  */
4988355f576SJeff Roberson static void
4998355f576SJeff Roberson uma_timeout(void *unused)
5008355f576SJeff Roberson {
50186bbae32SJeff Roberson 	bucket_enable();
5028355f576SJeff Roberson 	zone_foreach(zone_timeout);
5038355f576SJeff Roberson 
5048355f576SJeff Roberson 	/* Reschedule this event */
5059643769aSJeff Roberson 	callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
5068355f576SJeff Roberson }
5078355f576SJeff Roberson 
5088355f576SJeff Roberson /*
5099643769aSJeff Roberson  * Routine to perform timeout driven calculations.  This expands the
5109643769aSJeff Roberson  * hashes and does per cpu statistics aggregation.
5118355f576SJeff Roberson  *
512e20a199fSJeff Roberson  *  Returns nothing.
5138355f576SJeff Roberson  */
5148355f576SJeff Roberson static void
515e20a199fSJeff Roberson keg_timeout(uma_keg_t keg)
5168355f576SJeff Roberson {
5178355f576SJeff Roberson 
518e20a199fSJeff Roberson 	KEG_LOCK(keg);
5198355f576SJeff Roberson 	/*
520e20a199fSJeff Roberson 	 * Expand the keg hash table.
5218355f576SJeff Roberson 	 *
5228355f576SJeff Roberson 	 * This is done if the number of slabs is larger than the hash size.
5238355f576SJeff Roberson 	 * What I'm trying to do here is completely reduce collisions.  This
5248355f576SJeff Roberson 	 * may be a little aggressive.  Should I allow for two collisions max?
5258355f576SJeff Roberson 	 */
526099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH &&
527099a0e58SBosko Milekic 	    keg->uk_pages / keg->uk_ppera >= keg->uk_hash.uh_hashsize) {
5280aef6126SJeff Roberson 		struct uma_hash newhash;
5290aef6126SJeff Roberson 		struct uma_hash oldhash;
5300aef6126SJeff Roberson 		int ret;
5315300d9ddSJeff Roberson 
5320aef6126SJeff Roberson 		/*
5330aef6126SJeff Roberson 		 * This is so involved because allocating and freeing
534e20a199fSJeff Roberson 		 * while the keg lock is held will lead to deadlock.
5350aef6126SJeff Roberson 		 * I have to do everything in stages and check for
5360aef6126SJeff Roberson 		 * races.
5370aef6126SJeff Roberson 		 */
538099a0e58SBosko Milekic 		newhash = keg->uk_hash;
539e20a199fSJeff Roberson 		KEG_UNLOCK(keg);
5400aef6126SJeff Roberson 		ret = hash_alloc(&newhash);
541e20a199fSJeff Roberson 		KEG_LOCK(keg);
5420aef6126SJeff Roberson 		if (ret) {
543099a0e58SBosko Milekic 			if (hash_expand(&keg->uk_hash, &newhash)) {
544099a0e58SBosko Milekic 				oldhash = keg->uk_hash;
545099a0e58SBosko Milekic 				keg->uk_hash = newhash;
5460aef6126SJeff Roberson 			} else
5470aef6126SJeff Roberson 				oldhash = newhash;
5480aef6126SJeff Roberson 
549e20a199fSJeff Roberson 			KEG_UNLOCK(keg);
5500aef6126SJeff Roberson 			hash_free(&oldhash);
551a1dff920SDavide Italiano 			return;
5520aef6126SJeff Roberson 		}
5535300d9ddSJeff Roberson 	}
554e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
555e20a199fSJeff Roberson }
556e20a199fSJeff Roberson 
557e20a199fSJeff Roberson static void
558e20a199fSJeff Roberson zone_timeout(uma_zone_t zone)
559e20a199fSJeff Roberson {
560e20a199fSJeff Roberson 
561e20a199fSJeff Roberson 	zone_foreach_keg(zone, &keg_timeout);
5628355f576SJeff Roberson }
5638355f576SJeff Roberson 
5648355f576SJeff Roberson /*
5655300d9ddSJeff Roberson  * Allocate and zero fill the next sized hash table from the appropriate
5665300d9ddSJeff Roberson  * backing store.
5675300d9ddSJeff Roberson  *
5685300d9ddSJeff Roberson  * Arguments:
5690aef6126SJeff Roberson  *	hash  A new hash structure with the old hash size in uh_hashsize
5705300d9ddSJeff Roberson  *
5715300d9ddSJeff Roberson  * Returns:
572763df3ecSPedro F. Giffuni  *	1 on success and 0 on failure.
5735300d9ddSJeff Roberson  */
57437c84183SPoul-Henning Kamp static int
5750aef6126SJeff Roberson hash_alloc(struct uma_hash *hash)
5765300d9ddSJeff Roberson {
5770aef6126SJeff Roberson 	int oldsize;
5785300d9ddSJeff Roberson 	int alloc;
5795300d9ddSJeff Roberson 
5800aef6126SJeff Roberson 	oldsize = hash->uh_hashsize;
5810aef6126SJeff Roberson 
5825300d9ddSJeff Roberson 	/* We're just going to go to a power of two greater */
5830aef6126SJeff Roberson 	if (oldsize)  {
5840aef6126SJeff Roberson 		hash->uh_hashsize = oldsize * 2;
5850aef6126SJeff Roberson 		alloc = sizeof(hash->uh_slab_hash[0]) * hash->uh_hashsize;
5860aef6126SJeff Roberson 		hash->uh_slab_hash = (struct slabhead *)malloc(alloc,
587961647dfSJeff Roberson 		    M_UMAHASH, M_NOWAIT);
5885300d9ddSJeff Roberson 	} else {
5890aef6126SJeff Roberson 		alloc = sizeof(hash->uh_slab_hash[0]) * UMA_HASH_SIZE_INIT;
590e20a199fSJeff Roberson 		hash->uh_slab_hash = zone_alloc_item(hashzone, NULL,
591ab3185d1SJeff Roberson 		    UMA_ANYDOMAIN, M_WAITOK);
5920aef6126SJeff Roberson 		hash->uh_hashsize = UMA_HASH_SIZE_INIT;
5935300d9ddSJeff Roberson 	}
5940aef6126SJeff Roberson 	if (hash->uh_slab_hash) {
5950aef6126SJeff Roberson 		bzero(hash->uh_slab_hash, alloc);
5960aef6126SJeff Roberson 		hash->uh_hashmask = hash->uh_hashsize - 1;
5970aef6126SJeff Roberson 		return (1);
5980aef6126SJeff Roberson 	}
5995300d9ddSJeff Roberson 
6000aef6126SJeff Roberson 	return (0);
6015300d9ddSJeff Roberson }
6025300d9ddSJeff Roberson 
6035300d9ddSJeff Roberson /*
60464f051e9SJeff Roberson  * Expands the hash table for HASH zones.  This is done from zone_timeout
60564f051e9SJeff Roberson  * to reduce collisions.  This must not be done in the regular allocation
60664f051e9SJeff Roberson  * path, otherwise, we can recurse on the vm while allocating pages.
6078355f576SJeff Roberson  *
6088355f576SJeff Roberson  * Arguments:
6090aef6126SJeff Roberson  *	oldhash  The hash you want to expand
6100aef6126SJeff Roberson  *	newhash  The hash structure for the new table
6118355f576SJeff Roberson  *
6128355f576SJeff Roberson  * Returns:
6138355f576SJeff Roberson  *	Nothing
6148355f576SJeff Roberson  *
6158355f576SJeff Roberson  * Discussion:
6168355f576SJeff Roberson  */
6170aef6126SJeff Roberson static int
6180aef6126SJeff Roberson hash_expand(struct uma_hash *oldhash, struct uma_hash *newhash)
6198355f576SJeff Roberson {
6208355f576SJeff Roberson 	uma_slab_t slab;
6218355f576SJeff Roberson 	int hval;
6228355f576SJeff Roberson 	int i;
6238355f576SJeff Roberson 
6240aef6126SJeff Roberson 	if (!newhash->uh_slab_hash)
6250aef6126SJeff Roberson 		return (0);
6268355f576SJeff Roberson 
6270aef6126SJeff Roberson 	if (oldhash->uh_hashsize >= newhash->uh_hashsize)
6280aef6126SJeff Roberson 		return (0);
6298355f576SJeff Roberson 
6308355f576SJeff Roberson 	/*
6318355f576SJeff Roberson 	 * I need to investigate hash algorithms for resizing without a
6328355f576SJeff Roberson 	 * full rehash.
6338355f576SJeff Roberson 	 */
6348355f576SJeff Roberson 
6350aef6126SJeff Roberson 	for (i = 0; i < oldhash->uh_hashsize; i++)
6360aef6126SJeff Roberson 		while (!SLIST_EMPTY(&oldhash->uh_slab_hash[i])) {
6370aef6126SJeff Roberson 			slab = SLIST_FIRST(&oldhash->uh_slab_hash[i]);
6380aef6126SJeff Roberson 			SLIST_REMOVE_HEAD(&oldhash->uh_slab_hash[i], us_hlink);
6390aef6126SJeff Roberson 			hval = UMA_HASH(newhash, slab->us_data);
6400aef6126SJeff Roberson 			SLIST_INSERT_HEAD(&newhash->uh_slab_hash[hval],
6410aef6126SJeff Roberson 			    slab, us_hlink);
6428355f576SJeff Roberson 		}
6438355f576SJeff Roberson 
6440aef6126SJeff Roberson 	return (1);
6459c2cd7e5SJeff Roberson }
6469c2cd7e5SJeff Roberson 
6475300d9ddSJeff Roberson /*
6485300d9ddSJeff Roberson  * Free the hash bucket to the appropriate backing store.
6495300d9ddSJeff Roberson  *
6505300d9ddSJeff Roberson  * Arguments:
6515300d9ddSJeff Roberson  *	slab_hash  The hash bucket we're freeing
6525300d9ddSJeff Roberson  *	hashsize   The number of entries in that hash bucket
6535300d9ddSJeff Roberson  *
6545300d9ddSJeff Roberson  * Returns:
6555300d9ddSJeff Roberson  *	Nothing
6565300d9ddSJeff Roberson  */
6579c2cd7e5SJeff Roberson static void
6580aef6126SJeff Roberson hash_free(struct uma_hash *hash)
6599c2cd7e5SJeff Roberson {
6600aef6126SJeff Roberson 	if (hash->uh_slab_hash == NULL)
6610aef6126SJeff Roberson 		return;
6620aef6126SJeff Roberson 	if (hash->uh_hashsize == UMA_HASH_SIZE_INIT)
6630095a784SJeff Roberson 		zone_free_item(hashzone, hash->uh_slab_hash, NULL, SKIP_NONE);
6648355f576SJeff Roberson 	else
665961647dfSJeff Roberson 		free(hash->uh_slab_hash, M_UMAHASH);
6668355f576SJeff Roberson }
6678355f576SJeff Roberson 
6688355f576SJeff Roberson /*
6698355f576SJeff Roberson  * Frees all outstanding items in a bucket
6708355f576SJeff Roberson  *
6718355f576SJeff Roberson  * Arguments:
6728355f576SJeff Roberson  *	zone   The zone to free to, must be unlocked.
6738355f576SJeff Roberson  *	bucket The free/alloc bucket with items, cpu queue must be locked.
6748355f576SJeff Roberson  *
6758355f576SJeff Roberson  * Returns:
6768355f576SJeff Roberson  *	Nothing
6778355f576SJeff Roberson  */
6788355f576SJeff Roberson 
6798355f576SJeff Roberson static void
6808355f576SJeff Roberson bucket_drain(uma_zone_t zone, uma_bucket_t bucket)
6818355f576SJeff Roberson {
6820095a784SJeff Roberson 	int i;
6838355f576SJeff Roberson 
6848355f576SJeff Roberson 	if (bucket == NULL)
6858355f576SJeff Roberson 		return;
6868355f576SJeff Roberson 
6870095a784SJeff Roberson 	if (zone->uz_fini)
6880095a784SJeff Roberson 		for (i = 0; i < bucket->ub_cnt; i++)
6890095a784SJeff Roberson 			zone->uz_fini(bucket->ub_bucket[i], zone->uz_size);
6900095a784SJeff Roberson 	zone->uz_release(zone->uz_arg, bucket->ub_bucket, bucket->ub_cnt);
6910095a784SJeff Roberson 	bucket->ub_cnt = 0;
6928355f576SJeff Roberson }
6938355f576SJeff Roberson 
6948355f576SJeff Roberson /*
6958355f576SJeff Roberson  * Drains the per cpu caches for a zone.
6968355f576SJeff Roberson  *
6975d1ae027SRobert Watson  * NOTE: This may only be called while the zone is being turn down, and not
6985d1ae027SRobert Watson  * during normal operation.  This is necessary in order that we do not have
6995d1ae027SRobert Watson  * to migrate CPUs to drain the per-CPU caches.
7005d1ae027SRobert Watson  *
7018355f576SJeff Roberson  * Arguments:
7028355f576SJeff Roberson  *	zone     The zone to drain, must be unlocked.
7038355f576SJeff Roberson  *
7048355f576SJeff Roberson  * Returns:
7058355f576SJeff Roberson  *	Nothing
7068355f576SJeff Roberson  */
7078355f576SJeff Roberson static void
7089643769aSJeff Roberson cache_drain(uma_zone_t zone)
7098355f576SJeff Roberson {
7108355f576SJeff Roberson 	uma_cache_t cache;
7118355f576SJeff Roberson 	int cpu;
7128355f576SJeff Roberson 
7138355f576SJeff Roberson 	/*
7145d1ae027SRobert Watson 	 * XXX: It is safe to not lock the per-CPU caches, because we're
7155d1ae027SRobert Watson 	 * tearing down the zone anyway.  I.e., there will be no further use
7165d1ae027SRobert Watson 	 * of the caches at this point.
7175d1ae027SRobert Watson 	 *
7185d1ae027SRobert Watson 	 * XXX: It would good to be able to assert that the zone is being
7195d1ae027SRobert Watson 	 * torn down to prevent improper use of cache_drain().
7205d1ae027SRobert Watson 	 *
7215d1ae027SRobert Watson 	 * XXX: We lock the zone before passing into bucket_cache_drain() as
7225d1ae027SRobert Watson 	 * it is used elsewhere.  Should the tear-down path be made special
7235d1ae027SRobert Watson 	 * there in some form?
7248355f576SJeff Roberson 	 */
7253aa6d94eSJohn Baldwin 	CPU_FOREACH(cpu) {
7268355f576SJeff Roberson 		cache = &zone->uz_cpu[cpu];
7278355f576SJeff Roberson 		bucket_drain(zone, cache->uc_allocbucket);
7288355f576SJeff Roberson 		bucket_drain(zone, cache->uc_freebucket);
729174ab450SBosko Milekic 		if (cache->uc_allocbucket != NULL)
7306fd34d6fSJeff Roberson 			bucket_free(zone, cache->uc_allocbucket, NULL);
731174ab450SBosko Milekic 		if (cache->uc_freebucket != NULL)
7326fd34d6fSJeff Roberson 			bucket_free(zone, cache->uc_freebucket, NULL);
733d56368d7SBosko Milekic 		cache->uc_allocbucket = cache->uc_freebucket = NULL;
734d56368d7SBosko Milekic 	}
735aaa8bb16SJeff Roberson 	ZONE_LOCK(zone);
736aaa8bb16SJeff Roberson 	bucket_cache_drain(zone);
737aaa8bb16SJeff Roberson 	ZONE_UNLOCK(zone);
738aaa8bb16SJeff Roberson }
739aaa8bb16SJeff Roberson 
740a2de44abSAlexander Motin static void
741a2de44abSAlexander Motin cache_shrink(uma_zone_t zone)
742a2de44abSAlexander Motin {
743a2de44abSAlexander Motin 
744a2de44abSAlexander Motin 	if (zone->uz_flags & UMA_ZFLAG_INTERNAL)
745a2de44abSAlexander Motin 		return;
746a2de44abSAlexander Motin 
747a2de44abSAlexander Motin 	ZONE_LOCK(zone);
748a2de44abSAlexander Motin 	zone->uz_count = (zone->uz_count_min + zone->uz_count) / 2;
749a2de44abSAlexander Motin 	ZONE_UNLOCK(zone);
750a2de44abSAlexander Motin }
751a2de44abSAlexander Motin 
752a2de44abSAlexander Motin static void
753a2de44abSAlexander Motin cache_drain_safe_cpu(uma_zone_t zone)
754a2de44abSAlexander Motin {
755a2de44abSAlexander Motin 	uma_cache_t cache;
7568a8d9d14SAlexander Motin 	uma_bucket_t b1, b2;
757ab3185d1SJeff Roberson 	int domain;
758a2de44abSAlexander Motin 
759a2de44abSAlexander Motin 	if (zone->uz_flags & UMA_ZFLAG_INTERNAL)
760a2de44abSAlexander Motin 		return;
761a2de44abSAlexander Motin 
7628a8d9d14SAlexander Motin 	b1 = b2 = NULL;
763a2de44abSAlexander Motin 	ZONE_LOCK(zone);
764a2de44abSAlexander Motin 	critical_enter();
765ab3185d1SJeff Roberson 	if (zone->uz_flags & UMA_ZONE_NUMA)
766ab3185d1SJeff Roberson 		domain = PCPU_GET(domain);
767ab3185d1SJeff Roberson 	else
768ab3185d1SJeff Roberson 		domain = 0;
769a2de44abSAlexander Motin 	cache = &zone->uz_cpu[curcpu];
770a2de44abSAlexander Motin 	if (cache->uc_allocbucket) {
7718a8d9d14SAlexander Motin 		if (cache->uc_allocbucket->ub_cnt != 0)
772ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zone->uz_domain[domain].uzd_buckets,
7738a8d9d14SAlexander Motin 			    cache->uc_allocbucket, ub_link);
7748a8d9d14SAlexander Motin 		else
7758a8d9d14SAlexander Motin 			b1 = cache->uc_allocbucket;
776a2de44abSAlexander Motin 		cache->uc_allocbucket = NULL;
777a2de44abSAlexander Motin 	}
778a2de44abSAlexander Motin 	if (cache->uc_freebucket) {
7798a8d9d14SAlexander Motin 		if (cache->uc_freebucket->ub_cnt != 0)
780ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zone->uz_domain[domain].uzd_buckets,
7818a8d9d14SAlexander Motin 			    cache->uc_freebucket, ub_link);
7828a8d9d14SAlexander Motin 		else
7838a8d9d14SAlexander Motin 			b2 = cache->uc_freebucket;
784a2de44abSAlexander Motin 		cache->uc_freebucket = NULL;
785a2de44abSAlexander Motin 	}
786a2de44abSAlexander Motin 	critical_exit();
787a2de44abSAlexander Motin 	ZONE_UNLOCK(zone);
7888a8d9d14SAlexander Motin 	if (b1)
7898a8d9d14SAlexander Motin 		bucket_free(zone, b1, NULL);
7908a8d9d14SAlexander Motin 	if (b2)
7918a8d9d14SAlexander Motin 		bucket_free(zone, b2, NULL);
792a2de44abSAlexander Motin }
793a2de44abSAlexander Motin 
794a2de44abSAlexander Motin /*
795a2de44abSAlexander Motin  * Safely drain per-CPU caches of a zone(s) to alloc bucket.
796a2de44abSAlexander Motin  * This is an expensive call because it needs to bind to all CPUs
797a2de44abSAlexander Motin  * one by one and enter a critical section on each of them in order
798a2de44abSAlexander Motin  * to safely access their cache buckets.
799a2de44abSAlexander Motin  * Zone lock must not be held on call this function.
800a2de44abSAlexander Motin  */
801a2de44abSAlexander Motin static void
802a2de44abSAlexander Motin cache_drain_safe(uma_zone_t zone)
803a2de44abSAlexander Motin {
804a2de44abSAlexander Motin 	int cpu;
805a2de44abSAlexander Motin 
806a2de44abSAlexander Motin 	/*
807a2de44abSAlexander Motin 	 * Polite bucket sizes shrinking was not enouth, shrink aggressively.
808a2de44abSAlexander Motin 	 */
809a2de44abSAlexander Motin 	if (zone)
810a2de44abSAlexander Motin 		cache_shrink(zone);
811a2de44abSAlexander Motin 	else
812a2de44abSAlexander Motin 		zone_foreach(cache_shrink);
813a2de44abSAlexander Motin 
814a2de44abSAlexander Motin 	CPU_FOREACH(cpu) {
815a2de44abSAlexander Motin 		thread_lock(curthread);
816a2de44abSAlexander Motin 		sched_bind(curthread, cpu);
817a2de44abSAlexander Motin 		thread_unlock(curthread);
818a2de44abSAlexander Motin 
819a2de44abSAlexander Motin 		if (zone)
820a2de44abSAlexander Motin 			cache_drain_safe_cpu(zone);
821a2de44abSAlexander Motin 		else
822a2de44abSAlexander Motin 			zone_foreach(cache_drain_safe_cpu);
823a2de44abSAlexander Motin 	}
824a2de44abSAlexander Motin 	thread_lock(curthread);
825a2de44abSAlexander Motin 	sched_unbind(curthread);
826a2de44abSAlexander Motin 	thread_unlock(curthread);
827a2de44abSAlexander Motin }
828a2de44abSAlexander Motin 
829aaa8bb16SJeff Roberson /*
830aaa8bb16SJeff Roberson  * Drain the cached buckets from a zone.  Expects a locked zone on entry.
831aaa8bb16SJeff Roberson  */
832aaa8bb16SJeff Roberson static void
833aaa8bb16SJeff Roberson bucket_cache_drain(uma_zone_t zone)
834aaa8bb16SJeff Roberson {
835ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
836aaa8bb16SJeff Roberson 	uma_bucket_t bucket;
837ab3185d1SJeff Roberson 	int i;
8388355f576SJeff Roberson 
8398355f576SJeff Roberson 	/*
840ab3185d1SJeff Roberson 	 * Drain the bucket queues and free the buckets.
8418355f576SJeff Roberson 	 */
842ab3185d1SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
843ab3185d1SJeff Roberson 		zdom = &zone->uz_domain[i];
844ab3185d1SJeff Roberson 		while ((bucket = LIST_FIRST(&zdom->uzd_buckets)) != NULL) {
8458355f576SJeff Roberson 			LIST_REMOVE(bucket, ub_link);
8468355f576SJeff Roberson 			ZONE_UNLOCK(zone);
8478355f576SJeff Roberson 			bucket_drain(zone, bucket);
8486fd34d6fSJeff Roberson 			bucket_free(zone, bucket, NULL);
8498355f576SJeff Roberson 			ZONE_LOCK(zone);
8508355f576SJeff Roberson 		}
851ab3185d1SJeff Roberson 	}
852ace66b56SAlexander Motin 
853ace66b56SAlexander Motin 	/*
854ace66b56SAlexander Motin 	 * Shrink further bucket sizes.  Price of single zone lock collision
855ace66b56SAlexander Motin 	 * is probably lower then price of global cache drain.
856ace66b56SAlexander Motin 	 */
857ace66b56SAlexander Motin 	if (zone->uz_count > zone->uz_count_min)
858ace66b56SAlexander Motin 		zone->uz_count--;
8598355f576SJeff Roberson }
860fc03d22bSJeff Roberson 
861fc03d22bSJeff Roberson static void
862fc03d22bSJeff Roberson keg_free_slab(uma_keg_t keg, uma_slab_t slab, int start)
863fc03d22bSJeff Roberson {
864fc03d22bSJeff Roberson 	uint8_t *mem;
865fc03d22bSJeff Roberson 	int i;
866fc03d22bSJeff Roberson 	uint8_t flags;
867fc03d22bSJeff Roberson 
8681431a748SGleb Smirnoff 	CTR4(KTR_UMA, "keg_free_slab keg %s(%p) slab %p, returning %d bytes",
8691431a748SGleb Smirnoff 	    keg->uk_name, keg, slab, PAGE_SIZE * keg->uk_ppera);
8701431a748SGleb Smirnoff 
871fc03d22bSJeff Roberson 	mem = slab->us_data;
872fc03d22bSJeff Roberson 	flags = slab->us_flags;
873fc03d22bSJeff Roberson 	i = start;
874fc03d22bSJeff Roberson 	if (keg->uk_fini != NULL) {
875fc03d22bSJeff Roberson 		for (i--; i > -1; i--)
876c5deaf04SGleb Smirnoff #ifdef INVARIANTS
877c5deaf04SGleb Smirnoff 		/*
878c5deaf04SGleb Smirnoff 		 * trash_fini implies that dtor was trash_dtor. trash_fini
879c5deaf04SGleb Smirnoff 		 * would check that memory hasn't been modified since free,
880c5deaf04SGleb Smirnoff 		 * which executed trash_dtor.
881c5deaf04SGleb Smirnoff 		 * That's why we need to run uma_dbg_kskip() check here,
882c5deaf04SGleb Smirnoff 		 * albeit we don't make skip check for other init/fini
883c5deaf04SGleb Smirnoff 		 * invocations.
884c5deaf04SGleb Smirnoff 		 */
885c5deaf04SGleb Smirnoff 		if (!uma_dbg_kskip(keg, slab->us_data + (keg->uk_rsize * i)) ||
886c5deaf04SGleb Smirnoff 		    keg->uk_fini != trash_fini)
887c5deaf04SGleb Smirnoff #endif
888fc03d22bSJeff Roberson 			keg->uk_fini(slab->us_data + (keg->uk_rsize * i),
889fc03d22bSJeff Roberson 			    keg->uk_size);
890fc03d22bSJeff Roberson 	}
891fc03d22bSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_OFFPAGE)
892fc03d22bSJeff Roberson 		zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE);
893fc03d22bSJeff Roberson 	keg->uk_freef(mem, PAGE_SIZE * keg->uk_ppera, flags);
8942e47807cSJeff Roberson 	uma_total_dec(PAGE_SIZE * keg->uk_ppera);
8958355f576SJeff Roberson }
8968355f576SJeff Roberson 
8978355f576SJeff Roberson /*
898e20a199fSJeff Roberson  * Frees pages from a keg back to the system.  This is done on demand from
8998355f576SJeff Roberson  * the pageout daemon.
9008355f576SJeff Roberson  *
901e20a199fSJeff Roberson  * Returns nothing.
9028355f576SJeff Roberson  */
903e20a199fSJeff Roberson static void
904e20a199fSJeff Roberson keg_drain(uma_keg_t keg)
9058355f576SJeff Roberson {
9061e183df2SStefan Farfeleder 	struct slabhead freeslabs = { 0 };
907ab3185d1SJeff Roberson 	uma_domain_t dom;
908829be516SMark Johnston 	uma_slab_t slab, tmp;
909ab3185d1SJeff Roberson 	int i;
9108355f576SJeff Roberson 
9118355f576SJeff Roberson 	/*
912e20a199fSJeff Roberson 	 * We don't want to take pages from statically allocated kegs at this
9138355f576SJeff Roberson 	 * time
9148355f576SJeff Roberson 	 */
915099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL)
9168355f576SJeff Roberson 		return;
9178355f576SJeff Roberson 
9181431a748SGleb Smirnoff 	CTR3(KTR_UMA, "keg_drain %s(%p) free items: %u",
9191431a748SGleb Smirnoff 	    keg->uk_name, keg, keg->uk_free);
920e20a199fSJeff Roberson 	KEG_LOCK(keg);
921099a0e58SBosko Milekic 	if (keg->uk_free == 0)
9228355f576SJeff Roberson 		goto finished;
9238355f576SJeff Roberson 
924ab3185d1SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
925ab3185d1SJeff Roberson 		dom = &keg->uk_domain[i];
926ab3185d1SJeff Roberson 		LIST_FOREACH_SAFE(slab, &dom->ud_free_slab, us_link, tmp) {
927829be516SMark Johnston 			/* We have nowhere to free these to. */
928829be516SMark Johnston 			if (slab->us_flags & UMA_SLAB_BOOT)
9298355f576SJeff Roberson 				continue;
9308355f576SJeff Roberson 
9318355f576SJeff Roberson 			LIST_REMOVE(slab, us_link);
932099a0e58SBosko Milekic 			keg->uk_pages -= keg->uk_ppera;
933099a0e58SBosko Milekic 			keg->uk_free -= keg->uk_ipers;
934713deb36SJeff Roberson 
935099a0e58SBosko Milekic 			if (keg->uk_flags & UMA_ZONE_HASH)
936ab3185d1SJeff Roberson 				UMA_HASH_REMOVE(&keg->uk_hash, slab,
937ab3185d1SJeff Roberson 				    slab->us_data);
938713deb36SJeff Roberson 
939713deb36SJeff Roberson 			SLIST_INSERT_HEAD(&freeslabs, slab, us_hlink);
940713deb36SJeff Roberson 		}
941ab3185d1SJeff Roberson 	}
942ab3185d1SJeff Roberson 
943713deb36SJeff Roberson finished:
944e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
945713deb36SJeff Roberson 
946713deb36SJeff Roberson 	while ((slab = SLIST_FIRST(&freeslabs)) != NULL) {
947713deb36SJeff Roberson 		SLIST_REMOVE(&freeslabs, slab, uma_slab, us_hlink);
9481645995bSKirk McKusick 		keg_free_slab(keg, slab, keg->uk_ipers);
9498355f576SJeff Roberson 	}
9508355f576SJeff Roberson }
9518355f576SJeff Roberson 
952e20a199fSJeff Roberson static void
953e20a199fSJeff Roberson zone_drain_wait(uma_zone_t zone, int waitok)
954e20a199fSJeff Roberson {
955e20a199fSJeff Roberson 
9568355f576SJeff Roberson 	/*
957e20a199fSJeff Roberson 	 * Set draining to interlock with zone_dtor() so we can release our
958e20a199fSJeff Roberson 	 * locks as we go.  Only dtor() should do a WAITOK call since it
959e20a199fSJeff Roberson 	 * is the only call that knows the structure will still be available
960e20a199fSJeff Roberson 	 * when it wakes up.
961e20a199fSJeff Roberson 	 */
962e20a199fSJeff Roberson 	ZONE_LOCK(zone);
963e20a199fSJeff Roberson 	while (zone->uz_flags & UMA_ZFLAG_DRAINING) {
964e20a199fSJeff Roberson 		if (waitok == M_NOWAIT)
965e20a199fSJeff Roberson 			goto out;
966af526374SJeff Roberson 		msleep(zone, zone->uz_lockptr, PVM, "zonedrain", 1);
967e20a199fSJeff Roberson 	}
968e20a199fSJeff Roberson 	zone->uz_flags |= UMA_ZFLAG_DRAINING;
969e20a199fSJeff Roberson 	bucket_cache_drain(zone);
970e20a199fSJeff Roberson 	ZONE_UNLOCK(zone);
971e20a199fSJeff Roberson 	/*
972e20a199fSJeff Roberson 	 * The DRAINING flag protects us from being freed while
973111fbcd5SBryan Venteicher 	 * we're running.  Normally the uma_rwlock would protect us but we
974e20a199fSJeff Roberson 	 * must be able to release and acquire the right lock for each keg.
975e20a199fSJeff Roberson 	 */
976e20a199fSJeff Roberson 	zone_foreach_keg(zone, &keg_drain);
977e20a199fSJeff Roberson 	ZONE_LOCK(zone);
978e20a199fSJeff Roberson 	zone->uz_flags &= ~UMA_ZFLAG_DRAINING;
979e20a199fSJeff Roberson 	wakeup(zone);
980e20a199fSJeff Roberson out:
981e20a199fSJeff Roberson 	ZONE_UNLOCK(zone);
982e20a199fSJeff Roberson }
983e20a199fSJeff Roberson 
984e20a199fSJeff Roberson void
985e20a199fSJeff Roberson zone_drain(uma_zone_t zone)
986e20a199fSJeff Roberson {
987e20a199fSJeff Roberson 
988e20a199fSJeff Roberson 	zone_drain_wait(zone, M_NOWAIT);
989e20a199fSJeff Roberson }
990e20a199fSJeff Roberson 
991e20a199fSJeff Roberson /*
992e20a199fSJeff Roberson  * Allocate a new slab for a keg.  This does not insert the slab onto a list.
9938355f576SJeff Roberson  *
9948355f576SJeff Roberson  * Arguments:
9958355f576SJeff Roberson  *	wait  Shall we wait?
9968355f576SJeff Roberson  *
9978355f576SJeff Roberson  * Returns:
9988355f576SJeff Roberson  *	The slab that was allocated or NULL if there is no memory and the
9998355f576SJeff Roberson  *	caller specified M_NOWAIT.
10008355f576SJeff Roberson  */
10018355f576SJeff Roberson static uma_slab_t
1002ab3185d1SJeff Roberson keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int domain, int wait)
10038355f576SJeff Roberson {
1004e20a199fSJeff Roberson 	uma_alloc allocf;
1005099a0e58SBosko Milekic 	uma_slab_t slab;
10062e47807cSJeff Roberson 	unsigned long size;
100785dcf349SGleb Smirnoff 	uint8_t *mem;
100885dcf349SGleb Smirnoff 	uint8_t flags;
10098355f576SJeff Roberson 	int i;
10108355f576SJeff Roberson 
1011ab3185d1SJeff Roberson 	KASSERT(domain >= 0 && domain < vm_ndomains,
1012ab3185d1SJeff Roberson 	    ("keg_alloc_slab: domain %d out of range", domain));
1013e20a199fSJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
1014a553d4b8SJeff Roberson 	slab = NULL;
1015fc03d22bSJeff Roberson 	mem = NULL;
1016a553d4b8SJeff Roberson 
1017e20a199fSJeff Roberson 	allocf = keg->uk_allocf;
1018e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
10192e47807cSJeff Roberson 	size = keg->uk_ppera * PAGE_SIZE;
1020a553d4b8SJeff Roberson 
1021099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_OFFPAGE) {
1022ab3185d1SJeff Roberson 		slab = zone_alloc_item(keg->uk_slabzone, NULL, domain, wait);
1023fc03d22bSJeff Roberson 		if (slab == NULL)
1024fc03d22bSJeff Roberson 			goto out;
1025a553d4b8SJeff Roberson 	}
1026a553d4b8SJeff Roberson 
10273370c5bfSJeff Roberson 	/*
10283370c5bfSJeff Roberson 	 * This reproduces the old vm_zone behavior of zero filling pages the
10293370c5bfSJeff Roberson 	 * first time they are added to a zone.
10303370c5bfSJeff Roberson 	 *
10313370c5bfSJeff Roberson 	 * Malloced items are zeroed in uma_zalloc.
10323370c5bfSJeff Roberson 	 */
10333370c5bfSJeff Roberson 
1034099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0)
10353370c5bfSJeff Roberson 		wait |= M_ZERO;
10363370c5bfSJeff Roberson 	else
10373370c5bfSJeff Roberson 		wait &= ~M_ZERO;
10383370c5bfSJeff Roberson 
1039263811f7SKip Macy 	if (keg->uk_flags & UMA_ZONE_NODUMP)
1040263811f7SKip Macy 		wait |= M_NODUMP;
1041263811f7SKip Macy 
1042e20a199fSJeff Roberson 	/* zone is passed for legacy reasons. */
1043ab3185d1SJeff Roberson 	mem = allocf(zone, size, domain, &flags, wait);
1044a553d4b8SJeff Roberson 	if (mem == NULL) {
1045b23f72e9SBrian Feldman 		if (keg->uk_flags & UMA_ZONE_OFFPAGE)
10460095a784SJeff Roberson 			zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE);
1047fc03d22bSJeff Roberson 		slab = NULL;
1048fc03d22bSJeff Roberson 		goto out;
1049a553d4b8SJeff Roberson 	}
10502e47807cSJeff Roberson 	uma_total_inc(size);
10518355f576SJeff Roberson 
10525c0e403bSJeff Roberson 	/* Point the slab into the allocated memory */
1053099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_OFFPAGE))
1054099a0e58SBosko Milekic 		slab = (uma_slab_t )(mem + keg->uk_pgoff);
10555c0e403bSJeff Roberson 
1056e20a199fSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_VTOSLAB)
1057099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ppera; i++)
105899571dc3SJeff Roberson 			vsetslab((vm_offset_t)mem + (i * PAGE_SIZE), slab);
10598355f576SJeff Roberson 
1060099a0e58SBosko Milekic 	slab->us_keg = keg;
10618355f576SJeff Roberson 	slab->us_data = mem;
1062099a0e58SBosko Milekic 	slab->us_freecount = keg->uk_ipers;
10638355f576SJeff Roberson 	slab->us_flags = flags;
1064ab3185d1SJeff Roberson 	slab->us_domain = domain;
1065ef72505eSJeff Roberson 	BIT_FILL(SLAB_SETSIZE, &slab->us_free);
1066ef72505eSJeff Roberson #ifdef INVARIANTS
1067ef72505eSJeff Roberson 	BIT_ZERO(SLAB_SETSIZE, &slab->us_debugfree);
1068ef72505eSJeff Roberson #endif
1069099a0e58SBosko Milekic 
1070b23f72e9SBrian Feldman 	if (keg->uk_init != NULL) {
1071099a0e58SBosko Milekic 		for (i = 0; i < keg->uk_ipers; i++)
1072b23f72e9SBrian Feldman 			if (keg->uk_init(slab->us_data + (keg->uk_rsize * i),
1073b23f72e9SBrian Feldman 			    keg->uk_size, wait) != 0)
1074b23f72e9SBrian Feldman 				break;
1075b23f72e9SBrian Feldman 		if (i != keg->uk_ipers) {
1076fc03d22bSJeff Roberson 			keg_free_slab(keg, slab, i);
1077fc03d22bSJeff Roberson 			slab = NULL;
1078fc03d22bSJeff Roberson 			goto out;
1079b23f72e9SBrian Feldman 		}
1080b23f72e9SBrian Feldman 	}
1081fc03d22bSJeff Roberson out:
1082e20a199fSJeff Roberson 	KEG_LOCK(keg);
10835c0e403bSJeff Roberson 
10841431a748SGleb Smirnoff 	CTR3(KTR_UMA, "keg_alloc_slab: allocated slab %p for %s(%p)",
10851431a748SGleb Smirnoff 	    slab, keg->uk_name, keg);
10861431a748SGleb Smirnoff 
1087fc03d22bSJeff Roberson 	if (slab != NULL) {
1088099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZONE_HASH)
1089099a0e58SBosko Milekic 			UMA_HASH_INSERT(&keg->uk_hash, slab, mem);
10908355f576SJeff Roberson 
1091099a0e58SBosko Milekic 		keg->uk_pages += keg->uk_ppera;
1092099a0e58SBosko Milekic 		keg->uk_free += keg->uk_ipers;
1093fc03d22bSJeff Roberson 	}
10948355f576SJeff Roberson 
10958355f576SJeff Roberson 	return (slab);
10968355f576SJeff Roberson }
10978355f576SJeff Roberson 
10988355f576SJeff Roberson /*
1099009b6fcbSJeff Roberson  * This function is intended to be used early on in place of page_alloc() so
1100009b6fcbSJeff Roberson  * that we may use the boot time page cache to satisfy allocations before
1101009b6fcbSJeff Roberson  * the VM is ready.
1102009b6fcbSJeff Roberson  */
1103009b6fcbSJeff Roberson static void *
1104ab3185d1SJeff Roberson startup_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *pflag,
1105ab3185d1SJeff Roberson     int wait)
1106009b6fcbSJeff Roberson {
1107099a0e58SBosko Milekic 	uma_keg_t keg;
1108ac0a6fd0SGleb Smirnoff 	void *mem;
1109ac0a6fd0SGleb Smirnoff 	int pages;
1110099a0e58SBosko Milekic 
1111e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
1112099a0e58SBosko Milekic 
1113009b6fcbSJeff Roberson 	/*
1114f7d35785SGleb Smirnoff 	 * If we are in BOOT_BUCKETS or higher, than switch to real
1115f7d35785SGleb Smirnoff 	 * allocator.  Zones with page sized slabs switch at BOOT_PAGEALLOC.
1116009b6fcbSJeff Roberson 	 */
1117f7d35785SGleb Smirnoff 	switch (booted) {
1118f7d35785SGleb Smirnoff 		case BOOT_COLD:
1119f7d35785SGleb Smirnoff 		case BOOT_STRAPPED:
1120f7d35785SGleb Smirnoff 			break;
1121f7d35785SGleb Smirnoff 		case BOOT_PAGEALLOC:
1122f7d35785SGleb Smirnoff 			if (keg->uk_ppera > 1)
1123f7d35785SGleb Smirnoff 				break;
1124f7d35785SGleb Smirnoff 		case BOOT_BUCKETS:
1125f7d35785SGleb Smirnoff 		case BOOT_RUNNING:
1126009b6fcbSJeff Roberson #ifdef UMA_MD_SMALL_ALLOC
1127f7d35785SGleb Smirnoff 			keg->uk_allocf = (keg->uk_ppera > 1) ?
1128f7d35785SGleb Smirnoff 			    page_alloc : uma_small_alloc;
1129009b6fcbSJeff Roberson #else
1130099a0e58SBosko Milekic 			keg->uk_allocf = page_alloc;
1131009b6fcbSJeff Roberson #endif
1132ab3185d1SJeff Roberson 			return keg->uk_allocf(zone, bytes, domain, pflag, wait);
1133009b6fcbSJeff Roberson 	}
1134009b6fcbSJeff Roberson 
1135009b6fcbSJeff Roberson 	/*
1136f7d35785SGleb Smirnoff 	 * Check our small startup cache to see if it has pages remaining.
1137f7d35785SGleb Smirnoff 	 */
1138f7d35785SGleb Smirnoff 	pages = howmany(bytes, PAGE_SIZE);
1139f7d35785SGleb Smirnoff 	KASSERT(pages > 0, ("%s can't reserve 0 pages", __func__));
1140f7d35785SGleb Smirnoff 	if (pages > boot_pages)
1141f7d35785SGleb Smirnoff 		panic("UMA zone \"%s\": Increase vm.boot_pages", zone->uz_name);
1142f7d35785SGleb Smirnoff #ifdef DIAGNOSTIC
1143f7d35785SGleb Smirnoff 	printf("%s from \"%s\", %d boot pages left\n", __func__, zone->uz_name,
1144f7d35785SGleb Smirnoff 	    boot_pages);
1145f7d35785SGleb Smirnoff #endif
1146f7d35785SGleb Smirnoff 	mem = bootmem;
1147f7d35785SGleb Smirnoff 	boot_pages -= pages;
1148f7d35785SGleb Smirnoff 	bootmem += pages * PAGE_SIZE;
1149f7d35785SGleb Smirnoff 	*pflag = UMA_SLAB_BOOT;
1150f7d35785SGleb Smirnoff 
1151f7d35785SGleb Smirnoff 	return (mem);
1152f7d35785SGleb Smirnoff }
1153f7d35785SGleb Smirnoff 
1154f7d35785SGleb Smirnoff /*
11558355f576SJeff Roberson  * Allocates a number of pages from the system
11568355f576SJeff Roberson  *
11578355f576SJeff Roberson  * Arguments:
11588355f576SJeff Roberson  *	bytes  The number of bytes requested
11598355f576SJeff Roberson  *	wait  Shall we wait?
11608355f576SJeff Roberson  *
11618355f576SJeff Roberson  * Returns:
11628355f576SJeff Roberson  *	A pointer to the alloced memory or possibly
11638355f576SJeff Roberson  *	NULL if M_NOWAIT is set.
11648355f576SJeff Roberson  */
11658355f576SJeff Roberson static void *
1166ab3185d1SJeff Roberson page_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *pflag,
1167ab3185d1SJeff Roberson     int wait)
11688355f576SJeff Roberson {
11698355f576SJeff Roberson 	void *p;	/* Returned page */
11708355f576SJeff Roberson 
11712e47807cSJeff Roberson 	*pflag = UMA_SLAB_KERNEL;
11720766f278SJonathan T. Looney 	p = (void *) kmem_malloc_domain(kernel_arena, domain, bytes, wait);
11738355f576SJeff Roberson 
11748355f576SJeff Roberson 	return (p);
11758355f576SJeff Roberson }
11768355f576SJeff Roberson 
1177ab3059a8SMatt Macy static void *
1178ab3059a8SMatt Macy pcpu_page_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *pflag,
1179ab3059a8SMatt Macy     int wait)
1180ab3059a8SMatt Macy {
1181ab3059a8SMatt Macy 	struct pglist alloctail;
1182ab3059a8SMatt Macy 	vm_offset_t addr, zkva;
1183ab3059a8SMatt Macy 	int cpu, flags;
1184ab3059a8SMatt Macy 	vm_page_t p, p_next;
1185ab3059a8SMatt Macy #ifdef NUMA
1186ab3059a8SMatt Macy 	struct pcpu *pc;
1187ab3059a8SMatt Macy #endif
1188ab3059a8SMatt Macy 
1189ab3059a8SMatt Macy 	TAILQ_INIT(&alloctail);
1190ab3059a8SMatt Macy 	MPASS(bytes == (mp_maxid+1)*PAGE_SIZE);
1191ab3059a8SMatt Macy 	*pflag = UMA_SLAB_KERNEL;
1192ab3059a8SMatt Macy 
1193ab3059a8SMatt Macy 	flags = VM_ALLOC_SYSTEM | VM_ALLOC_WIRED | VM_ALLOC_NOOBJ |
1194ab3059a8SMatt Macy 		((wait & M_WAITOK) != 0 ? VM_ALLOC_WAITOK :
1195ab3059a8SMatt Macy 		 VM_ALLOC_NOWAIT);
1196ab3059a8SMatt Macy 	for (cpu = 0; cpu <= mp_maxid; cpu++) {
1197ab3059a8SMatt Macy 		if (CPU_ABSENT(cpu)) {
1198ab3059a8SMatt Macy 			p = vm_page_alloc(NULL, 0, flags);
1199ab3059a8SMatt Macy 		} else {
1200ab3059a8SMatt Macy #ifndef NUMA
1201ab3059a8SMatt Macy 			p = vm_page_alloc(NULL, 0, flags);
1202ab3059a8SMatt Macy #else
1203ab3059a8SMatt Macy 			pc = pcpu_find(cpu);
1204ab3059a8SMatt Macy 			p = vm_page_alloc_domain(NULL, 0, pc->pc_domain, flags);
1205ab3059a8SMatt Macy 			if (__predict_false(p == NULL))
1206ab3059a8SMatt Macy 				p = vm_page_alloc(NULL, 0, flags);
1207ab3059a8SMatt Macy #endif
1208ab3059a8SMatt Macy 		}
1209ab3059a8SMatt Macy 		if (__predict_false(p == NULL))
1210ab3059a8SMatt Macy 			goto fail;
1211ab3059a8SMatt Macy 		TAILQ_INSERT_TAIL(&alloctail, p, listq);
1212ab3059a8SMatt Macy 	}
1213ab3059a8SMatt Macy 	if ((addr = kva_alloc(bytes)) == 0)
1214ab3059a8SMatt Macy 		goto fail;
1215ab3059a8SMatt Macy 	zkva = addr;
1216ab3059a8SMatt Macy 	TAILQ_FOREACH(p, &alloctail, listq) {
1217ab3059a8SMatt Macy 		pmap_qenter(zkva, &p, 1);
1218ab3059a8SMatt Macy 		zkva += PAGE_SIZE;
1219ab3059a8SMatt Macy 	}
1220ab3059a8SMatt Macy 	return ((void*)addr);
1221ab3059a8SMatt Macy  fail:
1222ab3059a8SMatt Macy 	TAILQ_FOREACH_SAFE(p, &alloctail, listq, p_next) {
1223ab3059a8SMatt Macy 		vm_page_unwire(p, PQ_NONE);
1224ab3059a8SMatt Macy 		vm_page_free(p);
1225ab3059a8SMatt Macy 	}
1226ab3059a8SMatt Macy 	return (NULL);
1227ab3059a8SMatt Macy }
1228ab3059a8SMatt Macy 
12298355f576SJeff Roberson /*
12308355f576SJeff Roberson  * Allocates a number of pages from within an object
12318355f576SJeff Roberson  *
12328355f576SJeff Roberson  * Arguments:
12338355f576SJeff Roberson  *	bytes  The number of bytes requested
12348355f576SJeff Roberson  *	wait   Shall we wait?
12358355f576SJeff Roberson  *
12368355f576SJeff Roberson  * Returns:
12378355f576SJeff Roberson  *	A pointer to the alloced memory or possibly
12388355f576SJeff Roberson  *	NULL if M_NOWAIT is set.
12398355f576SJeff Roberson  */
12408355f576SJeff Roberson static void *
1241ab3185d1SJeff Roberson noobj_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *flags,
1242ab3185d1SJeff Roberson     int wait)
12438355f576SJeff Roberson {
1244a4915c21SAttilio Rao 	TAILQ_HEAD(, vm_page) alloctail;
1245a4915c21SAttilio Rao 	u_long npages;
1246b245ac95SAlan Cox 	vm_offset_t retkva, zkva;
1247a4915c21SAttilio Rao 	vm_page_t p, p_next;
1248e20a199fSJeff Roberson 	uma_keg_t keg;
12498355f576SJeff Roberson 
1250a4915c21SAttilio Rao 	TAILQ_INIT(&alloctail);
1251e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
1252a4915c21SAttilio Rao 
1253a4915c21SAttilio Rao 	npages = howmany(bytes, PAGE_SIZE);
1254a4915c21SAttilio Rao 	while (npages > 0) {
1255ab3185d1SJeff Roberson 		p = vm_page_alloc_domain(NULL, 0, domain, VM_ALLOC_INTERRUPT |
12568d6fbbb8SJeff Roberson 		    VM_ALLOC_WIRED | VM_ALLOC_NOOBJ |
1257772c8b67SKonstantin Belousov 		    ((wait & M_WAITOK) != 0 ? VM_ALLOC_WAITOK :
1258772c8b67SKonstantin Belousov 		    VM_ALLOC_NOWAIT));
1259a4915c21SAttilio Rao 		if (p != NULL) {
1260a4915c21SAttilio Rao 			/*
1261a4915c21SAttilio Rao 			 * Since the page does not belong to an object, its
1262a4915c21SAttilio Rao 			 * listq is unused.
1263a4915c21SAttilio Rao 			 */
1264a4915c21SAttilio Rao 			TAILQ_INSERT_TAIL(&alloctail, p, listq);
1265a4915c21SAttilio Rao 			npages--;
1266a4915c21SAttilio Rao 			continue;
1267a4915c21SAttilio Rao 		}
12688355f576SJeff Roberson 		/*
1269a4915c21SAttilio Rao 		 * Page allocation failed, free intermediate pages and
1270a4915c21SAttilio Rao 		 * exit.
12718355f576SJeff Roberson 		 */
1272a4915c21SAttilio Rao 		TAILQ_FOREACH_SAFE(p, &alloctail, listq, p_next) {
1273087a6132SAlan Cox 			vm_page_unwire(p, PQ_NONE);
1274b245ac95SAlan Cox 			vm_page_free(p);
1275b245ac95SAlan Cox 		}
1276a4915c21SAttilio Rao 		return (NULL);
1277b245ac95SAlan Cox 	}
12788355f576SJeff Roberson 	*flags = UMA_SLAB_PRIV;
1279a4915c21SAttilio Rao 	zkva = keg->uk_kva +
1280a4915c21SAttilio Rao 	    atomic_fetchadd_long(&keg->uk_offset, round_page(bytes));
1281a4915c21SAttilio Rao 	retkva = zkva;
1282a4915c21SAttilio Rao 	TAILQ_FOREACH(p, &alloctail, listq) {
1283a4915c21SAttilio Rao 		pmap_qenter(zkva, &p, 1);
1284a4915c21SAttilio Rao 		zkva += PAGE_SIZE;
1285a4915c21SAttilio Rao 	}
12868355f576SJeff Roberson 
12878355f576SJeff Roberson 	return ((void *)retkva);
12888355f576SJeff Roberson }
12898355f576SJeff Roberson 
12908355f576SJeff Roberson /*
12918355f576SJeff Roberson  * Frees a number of pages to the system
12928355f576SJeff Roberson  *
12938355f576SJeff Roberson  * Arguments:
12948355f576SJeff Roberson  *	mem   A pointer to the memory to be freed
12958355f576SJeff Roberson  *	size  The size of the memory being freed
12968355f576SJeff Roberson  *	flags The original p->us_flags field
12978355f576SJeff Roberson  *
12988355f576SJeff Roberson  * Returns:
12998355f576SJeff Roberson  *	Nothing
13008355f576SJeff Roberson  */
13018355f576SJeff Roberson static void
1302f2c2231eSRyan Stone page_free(void *mem, vm_size_t size, uint8_t flags)
13038355f576SJeff Roberson {
13045df87b21SJeff Roberson 	struct vmem *vmem;
13053370c5bfSJeff Roberson 
13062e47807cSJeff Roberson 	if (flags & UMA_SLAB_KERNEL)
13075df87b21SJeff Roberson 		vmem = kernel_arena;
13088355f576SJeff Roberson 	else
1309b5345ef1SJustin Hibbits 		panic("UMA: page_free used with invalid flags %x", flags);
13108355f576SJeff Roberson 
13115df87b21SJeff Roberson 	kmem_free(vmem, (vm_offset_t)mem, size);
13128355f576SJeff Roberson }
13138355f576SJeff Roberson 
13148355f576SJeff Roberson /*
1315ab3059a8SMatt Macy  * Frees pcpu zone allocations
1316ab3059a8SMatt Macy  *
1317ab3059a8SMatt Macy  * Arguments:
1318ab3059a8SMatt Macy  *	mem   A pointer to the memory to be freed
1319ab3059a8SMatt Macy  *	size  The size of the memory being freed
1320ab3059a8SMatt Macy  *	flags The original p->us_flags field
1321ab3059a8SMatt Macy  *
1322ab3059a8SMatt Macy  * Returns:
1323ab3059a8SMatt Macy  *	Nothing
1324ab3059a8SMatt Macy  */
1325ab3059a8SMatt Macy static void
1326ab3059a8SMatt Macy pcpu_page_free(void *mem, vm_size_t size, uint8_t flags)
1327ab3059a8SMatt Macy {
1328ab3059a8SMatt Macy 	vm_offset_t sva, curva;
1329ab3059a8SMatt Macy 	vm_paddr_t paddr;
1330ab3059a8SMatt Macy 	vm_page_t m;
1331ab3059a8SMatt Macy 
1332ab3059a8SMatt Macy 	MPASS(size == (mp_maxid+1)*PAGE_SIZE);
1333ab3059a8SMatt Macy 	sva = (vm_offset_t)mem;
1334ab3059a8SMatt Macy 	for (curva = sva; curva < sva + size; curva += PAGE_SIZE) {
1335ab3059a8SMatt Macy 		paddr = pmap_kextract(curva);
1336ab3059a8SMatt Macy 		m = PHYS_TO_VM_PAGE(paddr);
1337ab3059a8SMatt Macy 		vm_page_unwire(m, PQ_NONE);
1338ab3059a8SMatt Macy 		vm_page_free(m);
1339ab3059a8SMatt Macy 	}
1340ab3059a8SMatt Macy 	pmap_qremove(sva, size >> PAGE_SHIFT);
1341ab3059a8SMatt Macy 	kva_free(sva, size);
1342ab3059a8SMatt Macy }
1343ab3059a8SMatt Macy 
1344ab3059a8SMatt Macy 
1345ab3059a8SMatt Macy /*
13468355f576SJeff Roberson  * Zero fill initializer
13478355f576SJeff Roberson  *
13488355f576SJeff Roberson  * Arguments/Returns follow uma_init specifications
13498355f576SJeff Roberson  */
1350b23f72e9SBrian Feldman static int
1351b23f72e9SBrian Feldman zero_init(void *mem, int size, int flags)
13528355f576SJeff Roberson {
13538355f576SJeff Roberson 	bzero(mem, size);
1354b23f72e9SBrian Feldman 	return (0);
13558355f576SJeff Roberson }
13568355f576SJeff Roberson 
13578355f576SJeff Roberson /*
1358e20a199fSJeff Roberson  * Finish creating a small uma keg.  This calculates ipers, and the keg size.
13598355f576SJeff Roberson  *
13608355f576SJeff Roberson  * Arguments
1361e20a199fSJeff Roberson  *	keg  The zone we should initialize
13628355f576SJeff Roberson  *
13638355f576SJeff Roberson  * Returns
13648355f576SJeff Roberson  *	Nothing
13658355f576SJeff Roberson  */
13668355f576SJeff Roberson static void
1367e20a199fSJeff Roberson keg_small_init(uma_keg_t keg)
13688355f576SJeff Roberson {
1369244f4554SBosko Milekic 	u_int rsize;
1370244f4554SBosko Milekic 	u_int memused;
1371244f4554SBosko Milekic 	u_int wastedspace;
1372244f4554SBosko Milekic 	u_int shsize;
1373a55ebb7cSAndriy Gapon 	u_int slabsize;
13748355f576SJeff Roberson 
1375ad97af7eSGleb Smirnoff 	if (keg->uk_flags & UMA_ZONE_PCPU) {
137696c85efbSNathan Whitehorn 		u_int ncpus = (mp_maxid + 1) ? (mp_maxid + 1) : MAXCPU;
1377e28a647dSGleb Smirnoff 
1378ab3059a8SMatt Macy 		slabsize = UMA_PCPU_ALLOC_SIZE;
1379ab3059a8SMatt Macy 		keg->uk_ppera = ncpus;
1380ad97af7eSGleb Smirnoff 	} else {
1381a55ebb7cSAndriy Gapon 		slabsize = UMA_SLAB_SIZE;
1382ad97af7eSGleb Smirnoff 		keg->uk_ppera = 1;
1383ad97af7eSGleb Smirnoff 	}
1384ad97af7eSGleb Smirnoff 
1385ef72505eSJeff Roberson 	/*
1386ef72505eSJeff Roberson 	 * Calculate the size of each allocation (rsize) according to
1387ef72505eSJeff Roberson 	 * alignment.  If the requested size is smaller than we have
1388ef72505eSJeff Roberson 	 * allocation bits for we round it up.
1389ef72505eSJeff Roberson 	 */
1390099a0e58SBosko Milekic 	rsize = keg->uk_size;
1391a55ebb7cSAndriy Gapon 	if (rsize < slabsize / SLAB_SETSIZE)
1392a55ebb7cSAndriy Gapon 		rsize = slabsize / SLAB_SETSIZE;
1393099a0e58SBosko Milekic 	if (rsize & keg->uk_align)
1394099a0e58SBosko Milekic 		rsize = (rsize & ~keg->uk_align) + (keg->uk_align + 1);
1395099a0e58SBosko Milekic 	keg->uk_rsize = rsize;
1396ad97af7eSGleb Smirnoff 
1397ad97af7eSGleb Smirnoff 	KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0 ||
1398ab3059a8SMatt Macy 	    keg->uk_rsize < UMA_PCPU_ALLOC_SIZE,
1399ad97af7eSGleb Smirnoff 	    ("%s: size %u too large", __func__, keg->uk_rsize));
14008355f576SJeff Roberson 
1401ef72505eSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_OFFPAGE)
14022864dbbfSGleb Smirnoff 		shsize = 0;
1403ef72505eSJeff Roberson 	else
1404244f4554SBosko Milekic 		shsize = sizeof(struct uma_slab);
14058355f576SJeff Roberson 
14061ca6ed45SGleb Smirnoff 	if (rsize <= slabsize - shsize)
1407a55ebb7cSAndriy Gapon 		keg->uk_ipers = (slabsize - shsize) / rsize;
14081ca6ed45SGleb Smirnoff 	else {
14091ca6ed45SGleb Smirnoff 		/* Handle special case when we have 1 item per slab, so
14101ca6ed45SGleb Smirnoff 		 * alignment requirement can be relaxed. */
14111ca6ed45SGleb Smirnoff 		KASSERT(keg->uk_size <= slabsize - shsize,
14121ca6ed45SGleb Smirnoff 		    ("%s: size %u greater than slab", __func__, keg->uk_size));
14131ca6ed45SGleb Smirnoff 		keg->uk_ipers = 1;
14141ca6ed45SGleb Smirnoff 	}
1415ef72505eSJeff Roberson 	KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE,
1416ad97af7eSGleb Smirnoff 	    ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers));
1417ad97af7eSGleb Smirnoff 
1418244f4554SBosko Milekic 	memused = keg->uk_ipers * rsize + shsize;
1419a55ebb7cSAndriy Gapon 	wastedspace = slabsize - memused;
1420244f4554SBosko Milekic 
142120e8e865SBosko Milekic 	/*
1422244f4554SBosko Milekic 	 * We can't do OFFPAGE if we're internal or if we've been
142320e8e865SBosko Milekic 	 * asked to not go to the VM for buckets.  If we do this we
14246fd34d6fSJeff Roberson 	 * may end up going to the VM  for slabs which we do not
14256fd34d6fSJeff Roberson 	 * want to do if we're UMA_ZFLAG_CACHEONLY as a result
14266fd34d6fSJeff Roberson 	 * of UMA_ZONE_VM, which clearly forbids it.
142720e8e865SBosko Milekic 	 */
1428099a0e58SBosko Milekic 	if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) ||
1429099a0e58SBosko Milekic 	    (keg->uk_flags & UMA_ZFLAG_CACHEONLY))
14308355f576SJeff Roberson 		return;
1431244f4554SBosko Milekic 
1432ef72505eSJeff Roberson 	/*
1433ef72505eSJeff Roberson 	 * See if using an OFFPAGE slab will limit our waste.  Only do
1434ef72505eSJeff Roberson 	 * this if it permits more items per-slab.
1435ef72505eSJeff Roberson 	 *
1436ef72505eSJeff Roberson 	 * XXX We could try growing slabsize to limit max waste as well.
1437ef72505eSJeff Roberson 	 * Historically this was not done because the VM could not
1438ef72505eSJeff Roberson 	 * efficiently handle contiguous allocations.
1439ef72505eSJeff Roberson 	 */
1440a55ebb7cSAndriy Gapon 	if ((wastedspace >= slabsize / UMA_MAX_WASTE) &&
1441a55ebb7cSAndriy Gapon 	    (keg->uk_ipers < (slabsize / keg->uk_rsize))) {
1442a55ebb7cSAndriy Gapon 		keg->uk_ipers = slabsize / keg->uk_rsize;
1443ef72505eSJeff Roberson 		KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE,
1444ad97af7eSGleb Smirnoff 		    ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers));
14451431a748SGleb Smirnoff 		CTR6(KTR_UMA, "UMA decided we need offpage slab headers for "
14461431a748SGleb Smirnoff 		    "keg: %s(%p), calculated wastedspace = %d, "
1447244f4554SBosko Milekic 		    "maximum wasted space allowed = %d, "
1448244f4554SBosko Milekic 		    "calculated ipers = %d, "
14491431a748SGleb Smirnoff 		    "new wasted space = %d\n", keg->uk_name, keg, wastedspace,
1450a55ebb7cSAndriy Gapon 		    slabsize / UMA_MAX_WASTE, keg->uk_ipers,
1451a55ebb7cSAndriy Gapon 		    slabsize - keg->uk_ipers * keg->uk_rsize);
1452099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZONE_OFFPAGE;
14538355f576SJeff Roberson 	}
1454ad97af7eSGleb Smirnoff 
1455ad97af7eSGleb Smirnoff 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) &&
1456ad97af7eSGleb Smirnoff 	    (keg->uk_flags & UMA_ZONE_VTOSLAB) == 0)
1457ad97af7eSGleb Smirnoff 		keg->uk_flags |= UMA_ZONE_HASH;
14588355f576SJeff Roberson }
14598355f576SJeff Roberson 
14608355f576SJeff Roberson /*
1461e20a199fSJeff Roberson  * Finish creating a large (> UMA_SLAB_SIZE) uma kegs.  Just give in and do
14628355f576SJeff Roberson  * OFFPAGE for now.  When I can allow for more dynamic slab sizes this will be
14638355f576SJeff Roberson  * more complicated.
14648355f576SJeff Roberson  *
14658355f576SJeff Roberson  * Arguments
1466e20a199fSJeff Roberson  *	keg  The keg we should initialize
14678355f576SJeff Roberson  *
14688355f576SJeff Roberson  * Returns
14698355f576SJeff Roberson  *	Nothing
14708355f576SJeff Roberson  */
14718355f576SJeff Roberson static void
1472e20a199fSJeff Roberson keg_large_init(uma_keg_t keg)
14738355f576SJeff Roberson {
1474cec48e00SAlexander Motin 	u_int shsize;
14758355f576SJeff Roberson 
1476e20a199fSJeff Roberson 	KASSERT(keg != NULL, ("Keg is null in keg_large_init"));
1477099a0e58SBosko Milekic 	KASSERT((keg->uk_flags & UMA_ZFLAG_CACHEONLY) == 0,
1478e20a199fSJeff Roberson 	    ("keg_large_init: Cannot large-init a UMA_ZFLAG_CACHEONLY keg"));
1479ad97af7eSGleb Smirnoff 	KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0,
1480ad97af7eSGleb Smirnoff 	    ("%s: Cannot large-init a UMA_ZONE_PCPU keg", __func__));
148120e8e865SBosko Milekic 
1482ad97af7eSGleb Smirnoff 	keg->uk_ppera = howmany(keg->uk_size, PAGE_SIZE);
1483099a0e58SBosko Milekic 	keg->uk_ipers = 1;
1484e9a069d8SJohn Baldwin 	keg->uk_rsize = keg->uk_size;
1485e9a069d8SJohn Baldwin 
1486cec48e00SAlexander Motin 	/* Check whether we have enough space to not do OFFPAGE. */
1487cec48e00SAlexander Motin 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) == 0) {
1488cec48e00SAlexander Motin 		shsize = sizeof(struct uma_slab);
1489cec48e00SAlexander Motin 		if (shsize & UMA_ALIGN_PTR)
1490cec48e00SAlexander Motin 			shsize = (shsize & ~UMA_ALIGN_PTR) +
1491cec48e00SAlexander Motin 			    (UMA_ALIGN_PTR + 1);
1492cec48e00SAlexander Motin 
14932934eb8aSMark Johnston 		if (PAGE_SIZE * keg->uk_ppera - keg->uk_rsize < shsize) {
14942934eb8aSMark Johnston 			/*
14952934eb8aSMark Johnston 			 * We can't do OFFPAGE if we're internal, in which case
14962934eb8aSMark Johnston 			 * we need an extra page per allocation to contain the
14972934eb8aSMark Johnston 			 * slab header.
14982934eb8aSMark Johnston 			 */
14992934eb8aSMark Johnston 			if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) == 0)
1500099a0e58SBosko Milekic 				keg->uk_flags |= UMA_ZONE_OFFPAGE;
15012934eb8aSMark Johnston 			else
15022934eb8aSMark Johnston 				keg->uk_ppera++;
15032934eb8aSMark Johnston 		}
1504cec48e00SAlexander Motin 	}
1505cec48e00SAlexander Motin 
1506cec48e00SAlexander Motin 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) &&
1507cec48e00SAlexander Motin 	    (keg->uk_flags & UMA_ZONE_VTOSLAB) == 0)
1508099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZONE_HASH;
15098355f576SJeff Roberson }
15108355f576SJeff Roberson 
1511e20a199fSJeff Roberson static void
1512e20a199fSJeff Roberson keg_cachespread_init(uma_keg_t keg)
1513e20a199fSJeff Roberson {
1514e20a199fSJeff Roberson 	int alignsize;
1515e20a199fSJeff Roberson 	int trailer;
1516e20a199fSJeff Roberson 	int pages;
1517e20a199fSJeff Roberson 	int rsize;
1518e20a199fSJeff Roberson 
1519ad97af7eSGleb Smirnoff 	KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0,
1520ad97af7eSGleb Smirnoff 	    ("%s: Cannot cachespread-init a UMA_ZONE_PCPU keg", __func__));
1521ad97af7eSGleb Smirnoff 
1522e20a199fSJeff Roberson 	alignsize = keg->uk_align + 1;
1523e20a199fSJeff Roberson 	rsize = keg->uk_size;
1524e20a199fSJeff Roberson 	/*
1525e20a199fSJeff Roberson 	 * We want one item to start on every align boundary in a page.  To
1526e20a199fSJeff Roberson 	 * do this we will span pages.  We will also extend the item by the
1527e20a199fSJeff Roberson 	 * size of align if it is an even multiple of align.  Otherwise, it
1528e20a199fSJeff Roberson 	 * would fall on the same boundary every time.
1529e20a199fSJeff Roberson 	 */
1530e20a199fSJeff Roberson 	if (rsize & keg->uk_align)
1531e20a199fSJeff Roberson 		rsize = (rsize & ~keg->uk_align) + alignsize;
1532e20a199fSJeff Roberson 	if ((rsize & alignsize) == 0)
1533e20a199fSJeff Roberson 		rsize += alignsize;
1534e20a199fSJeff Roberson 	trailer = rsize - keg->uk_size;
1535e20a199fSJeff Roberson 	pages = (rsize * (PAGE_SIZE / alignsize)) / PAGE_SIZE;
1536e20a199fSJeff Roberson 	pages = MIN(pages, (128 * 1024) / PAGE_SIZE);
1537e20a199fSJeff Roberson 	keg->uk_rsize = rsize;
1538e20a199fSJeff Roberson 	keg->uk_ppera = pages;
1539e20a199fSJeff Roberson 	keg->uk_ipers = ((pages * PAGE_SIZE) + trailer) / rsize;
1540e20a199fSJeff Roberson 	keg->uk_flags |= UMA_ZONE_OFFPAGE | UMA_ZONE_VTOSLAB;
15412367b4ddSDimitry Andric 	KASSERT(keg->uk_ipers <= SLAB_SETSIZE,
154242321809SGleb Smirnoff 	    ("%s: keg->uk_ipers too high(%d) increase max_ipers", __func__,
1543e20a199fSJeff Roberson 	    keg->uk_ipers));
1544e20a199fSJeff Roberson }
1545e20a199fSJeff Roberson 
15468355f576SJeff Roberson /*
1547099a0e58SBosko Milekic  * Keg header ctor.  This initializes all fields, locks, etc.  And inserts
1548099a0e58SBosko Milekic  * the keg onto the global keg list.
15498355f576SJeff Roberson  *
15508355f576SJeff Roberson  * Arguments/Returns follow uma_ctor specifications
1551099a0e58SBosko Milekic  *	udata  Actually uma_kctor_args
1552099a0e58SBosko Milekic  */
1553b23f72e9SBrian Feldman static int
1554b23f72e9SBrian Feldman keg_ctor(void *mem, int size, void *udata, int flags)
1555099a0e58SBosko Milekic {
1556099a0e58SBosko Milekic 	struct uma_kctor_args *arg = udata;
1557099a0e58SBosko Milekic 	uma_keg_t keg = mem;
1558099a0e58SBosko Milekic 	uma_zone_t zone;
1559099a0e58SBosko Milekic 
1560099a0e58SBosko Milekic 	bzero(keg, size);
1561099a0e58SBosko Milekic 	keg->uk_size = arg->size;
1562099a0e58SBosko Milekic 	keg->uk_init = arg->uminit;
1563099a0e58SBosko Milekic 	keg->uk_fini = arg->fini;
1564099a0e58SBosko Milekic 	keg->uk_align = arg->align;
1565ab3185d1SJeff Roberson 	keg->uk_cursor = 0;
1566099a0e58SBosko Milekic 	keg->uk_free = 0;
15676fd34d6fSJeff Roberson 	keg->uk_reserve = 0;
1568099a0e58SBosko Milekic 	keg->uk_pages = 0;
1569099a0e58SBosko Milekic 	keg->uk_flags = arg->flags;
1570099a0e58SBosko Milekic 	keg->uk_slabzone = NULL;
1571099a0e58SBosko Milekic 
1572099a0e58SBosko Milekic 	/*
1573099a0e58SBosko Milekic 	 * The master zone is passed to us at keg-creation time.
1574099a0e58SBosko Milekic 	 */
1575099a0e58SBosko Milekic 	zone = arg->zone;
1576e20a199fSJeff Roberson 	keg->uk_name = zone->uz_name;
1577099a0e58SBosko Milekic 
1578099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_VM)
1579099a0e58SBosko Milekic 		keg->uk_flags |= UMA_ZFLAG_CACHEONLY;
1580099a0e58SBosko Milekic 
1581099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_ZINIT)
1582099a0e58SBosko Milekic 		keg->uk_init = zero_init;
1583099a0e58SBosko Milekic 
1584cfcae3f8SGleb Smirnoff 	if (arg->flags & UMA_ZONE_MALLOC)
1585e20a199fSJeff Roberson 		keg->uk_flags |= UMA_ZONE_VTOSLAB;
1586e20a199fSJeff Roberson 
1587ad97af7eSGleb Smirnoff 	if (arg->flags & UMA_ZONE_PCPU)
1588ad97af7eSGleb Smirnoff #ifdef SMP
1589ad97af7eSGleb Smirnoff 		keg->uk_flags |= UMA_ZONE_OFFPAGE;
1590ad97af7eSGleb Smirnoff #else
1591ad97af7eSGleb Smirnoff 		keg->uk_flags &= ~UMA_ZONE_PCPU;
1592ad97af7eSGleb Smirnoff #endif
1593ad97af7eSGleb Smirnoff 
1594ef72505eSJeff Roberson 	if (keg->uk_flags & UMA_ZONE_CACHESPREAD) {
1595e20a199fSJeff Roberson 		keg_cachespread_init(keg);
1596244f4554SBosko Milekic 	} else {
1597b92b26adSGleb Smirnoff 		if (keg->uk_size > UMA_SLAB_SPACE)
1598e20a199fSJeff Roberson 			keg_large_init(keg);
1599244f4554SBosko Milekic 		else
1600e20a199fSJeff Roberson 			keg_small_init(keg);
1601244f4554SBosko Milekic 	}
1602099a0e58SBosko Milekic 
1603cfcae3f8SGleb Smirnoff 	if (keg->uk_flags & UMA_ZONE_OFFPAGE)
1604099a0e58SBosko Milekic 		keg->uk_slabzone = slabzone;
1605099a0e58SBosko Milekic 
1606099a0e58SBosko Milekic 	/*
1607099a0e58SBosko Milekic 	 * If we haven't booted yet we need allocations to go through the
1608099a0e58SBosko Milekic 	 * startup cache until the vm is ready.
1609099a0e58SBosko Milekic 	 */
1610f4bef67cSGleb Smirnoff 	if (booted < BOOT_PAGEALLOC)
16118cd02d00SAlan Cox 		keg->uk_allocf = startup_alloc;
161277e19437SGleb Smirnoff #ifdef UMA_MD_SMALL_ALLOC
161377e19437SGleb Smirnoff 	else if (keg->uk_ppera == 1)
161477e19437SGleb Smirnoff 		keg->uk_allocf = uma_small_alloc;
16158cd02d00SAlan Cox #endif
1616ab3059a8SMatt Macy 	else if (keg->uk_flags & UMA_ZONE_PCPU)
1617ab3059a8SMatt Macy 		keg->uk_allocf = pcpu_page_alloc;
161877e19437SGleb Smirnoff 	else
161977e19437SGleb Smirnoff 		keg->uk_allocf = page_alloc;
162077e19437SGleb Smirnoff #ifdef UMA_MD_SMALL_ALLOC
162177e19437SGleb Smirnoff 	if (keg->uk_ppera == 1)
162277e19437SGleb Smirnoff 		keg->uk_freef = uma_small_free;
162377e19437SGleb Smirnoff 	else
162477e19437SGleb Smirnoff #endif
1625ab3059a8SMatt Macy 	if (keg->uk_flags & UMA_ZONE_PCPU)
1626ab3059a8SMatt Macy 		keg->uk_freef = pcpu_page_free;
1627ab3059a8SMatt Macy 	else
162877e19437SGleb Smirnoff 		keg->uk_freef = page_free;
1629099a0e58SBosko Milekic 
1630099a0e58SBosko Milekic 	/*
1631af526374SJeff Roberson 	 * Initialize keg's lock
1632099a0e58SBosko Milekic 	 */
1633af526374SJeff Roberson 	KEG_LOCK_INIT(keg, (arg->flags & UMA_ZONE_MTXCLASS));
1634099a0e58SBosko Milekic 
1635099a0e58SBosko Milekic 	/*
1636099a0e58SBosko Milekic 	 * If we're putting the slab header in the actual page we need to
1637099a0e58SBosko Milekic 	 * figure out where in each page it goes.  This calculates a right
1638099a0e58SBosko Milekic 	 * justified offset into the memory on an ALIGN_PTR boundary.
1639099a0e58SBosko Milekic 	 */
1640099a0e58SBosko Milekic 	if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) {
1641244f4554SBosko Milekic 		u_int totsize;
1642099a0e58SBosko Milekic 
1643099a0e58SBosko Milekic 		/* Size of the slab struct and free list */
1644ef72505eSJeff Roberson 		totsize = sizeof(struct uma_slab);
1645ef72505eSJeff Roberson 
1646099a0e58SBosko Milekic 		if (totsize & UMA_ALIGN_PTR)
1647099a0e58SBosko Milekic 			totsize = (totsize & ~UMA_ALIGN_PTR) +
1648099a0e58SBosko Milekic 			    (UMA_ALIGN_PTR + 1);
1649ad97af7eSGleb Smirnoff 		keg->uk_pgoff = (PAGE_SIZE * keg->uk_ppera) - totsize;
1650244f4554SBosko Milekic 
1651244f4554SBosko Milekic 		/*
1652244f4554SBosko Milekic 		 * The only way the following is possible is if with our
1653244f4554SBosko Milekic 		 * UMA_ALIGN_PTR adjustments we are now bigger than
1654244f4554SBosko Milekic 		 * UMA_SLAB_SIZE.  I haven't checked whether this is
1655244f4554SBosko Milekic 		 * mathematically possible for all cases, so we make
1656244f4554SBosko Milekic 		 * sure here anyway.
1657244f4554SBosko Milekic 		 */
1658ef72505eSJeff Roberson 		totsize = keg->uk_pgoff + sizeof(struct uma_slab);
1659ad97af7eSGleb Smirnoff 		if (totsize > PAGE_SIZE * keg->uk_ppera) {
1660099a0e58SBosko Milekic 			printf("zone %s ipers %d rsize %d size %d\n",
1661099a0e58SBosko Milekic 			    zone->uz_name, keg->uk_ipers, keg->uk_rsize,
1662099a0e58SBosko Milekic 			    keg->uk_size);
1663aea6e893SAlan Cox 			panic("UMA slab won't fit.");
1664099a0e58SBosko Milekic 		}
1665099a0e58SBosko Milekic 	}
1666099a0e58SBosko Milekic 
1667099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZONE_HASH)
1668099a0e58SBosko Milekic 		hash_alloc(&keg->uk_hash);
1669099a0e58SBosko Milekic 
16701431a748SGleb Smirnoff 	CTR5(KTR_UMA, "keg_ctor %p zone %s(%p) out %d free %d\n",
16711431a748SGleb Smirnoff 	    keg, zone->uz_name, zone,
167257223e99SAndriy Gapon 	    (keg->uk_pages / keg->uk_ppera) * keg->uk_ipers - keg->uk_free,
167357223e99SAndriy Gapon 	    keg->uk_free);
1674099a0e58SBosko Milekic 
1675099a0e58SBosko Milekic 	LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link);
1676099a0e58SBosko Milekic 
1677111fbcd5SBryan Venteicher 	rw_wlock(&uma_rwlock);
1678099a0e58SBosko Milekic 	LIST_INSERT_HEAD(&uma_kegs, keg, uk_link);
1679111fbcd5SBryan Venteicher 	rw_wunlock(&uma_rwlock);
1680b23f72e9SBrian Feldman 	return (0);
1681099a0e58SBosko Milekic }
1682099a0e58SBosko Milekic 
1683099a0e58SBosko Milekic /*
1684099a0e58SBosko Milekic  * Zone header ctor.  This initializes all fields, locks, etc.
1685099a0e58SBosko Milekic  *
1686099a0e58SBosko Milekic  * Arguments/Returns follow uma_ctor specifications
1687099a0e58SBosko Milekic  *	udata  Actually uma_zctor_args
16888355f576SJeff Roberson  */
1689b23f72e9SBrian Feldman static int
1690b23f72e9SBrian Feldman zone_ctor(void *mem, int size, void *udata, int flags)
16918355f576SJeff Roberson {
16928355f576SJeff Roberson 	struct uma_zctor_args *arg = udata;
16938355f576SJeff Roberson 	uma_zone_t zone = mem;
1694099a0e58SBosko Milekic 	uma_zone_t z;
1695099a0e58SBosko Milekic 	uma_keg_t keg;
16968355f576SJeff Roberson 
16978355f576SJeff Roberson 	bzero(zone, size);
16988355f576SJeff Roberson 	zone->uz_name = arg->name;
16998355f576SJeff Roberson 	zone->uz_ctor = arg->ctor;
17008355f576SJeff Roberson 	zone->uz_dtor = arg->dtor;
1701e20a199fSJeff Roberson 	zone->uz_slab = zone_fetch_slab;
1702099a0e58SBosko Milekic 	zone->uz_init = NULL;
1703099a0e58SBosko Milekic 	zone->uz_fini = NULL;
1704099a0e58SBosko Milekic 	zone->uz_allocs = 0;
1705773df9abSRobert Watson 	zone->uz_frees = 0;
17062019094aSRobert Watson 	zone->uz_fails = 0;
1707bf965959SSean Bruno 	zone->uz_sleeps = 0;
1708fc03d22bSJeff Roberson 	zone->uz_count = 0;
1709ace66b56SAlexander Motin 	zone->uz_count_min = 0;
1710e20a199fSJeff Roberson 	zone->uz_flags = 0;
17112f891cd5SPawel Jakub Dawidek 	zone->uz_warning = NULL;
1712ab3185d1SJeff Roberson 	/* The domain structures follow the cpu structures. */
1713ab3185d1SJeff Roberson 	zone->uz_domain = (struct uma_zone_domain *)&zone->uz_cpu[mp_ncpus];
17142f891cd5SPawel Jakub Dawidek 	timevalclear(&zone->uz_ratecheck);
1715e20a199fSJeff Roberson 	keg = arg->keg;
1716099a0e58SBosko Milekic 
1717af526374SJeff Roberson 	ZONE_LOCK_INIT(zone, (arg->flags & UMA_ZONE_MTXCLASS));
1718af526374SJeff Roberson 
17190095a784SJeff Roberson 	/*
17200095a784SJeff Roberson 	 * This is a pure cache zone, no kegs.
17210095a784SJeff Roberson 	 */
17220095a784SJeff Roberson 	if (arg->import) {
17236fd34d6fSJeff Roberson 		if (arg->flags & UMA_ZONE_VM)
17246fd34d6fSJeff Roberson 			arg->flags |= UMA_ZFLAG_CACHEONLY;
17256fd34d6fSJeff Roberson 		zone->uz_flags = arg->flags;
1726af526374SJeff Roberson 		zone->uz_size = arg->size;
17270095a784SJeff Roberson 		zone->uz_import = arg->import;
17280095a784SJeff Roberson 		zone->uz_release = arg->release;
17290095a784SJeff Roberson 		zone->uz_arg = arg->arg;
1730af526374SJeff Roberson 		zone->uz_lockptr = &zone->uz_lock;
1731111fbcd5SBryan Venteicher 		rw_wlock(&uma_rwlock);
173203175483SAlexander Motin 		LIST_INSERT_HEAD(&uma_cachezones, zone, uz_link);
1733111fbcd5SBryan Venteicher 		rw_wunlock(&uma_rwlock);
1734af526374SJeff Roberson 		goto out;
17350095a784SJeff Roberson 	}
17360095a784SJeff Roberson 
17370095a784SJeff Roberson 	/*
17380095a784SJeff Roberson 	 * Use the regular zone/keg/slab allocator.
17390095a784SJeff Roberson 	 */
17400095a784SJeff Roberson 	zone->uz_import = (uma_import)zone_import;
17410095a784SJeff Roberson 	zone->uz_release = (uma_release)zone_release;
17420095a784SJeff Roberson 	zone->uz_arg = zone;
17430095a784SJeff Roberson 
1744099a0e58SBosko Milekic 	if (arg->flags & UMA_ZONE_SECONDARY) {
1745099a0e58SBosko Milekic 		KASSERT(arg->keg != NULL, ("Secondary zone on zero'd keg"));
17468355f576SJeff Roberson 		zone->uz_init = arg->uminit;
1747e221e841SJeff Roberson 		zone->uz_fini = arg->fini;
1748af526374SJeff Roberson 		zone->uz_lockptr = &keg->uk_lock;
1749e20a199fSJeff Roberson 		zone->uz_flags |= UMA_ZONE_SECONDARY;
1750111fbcd5SBryan Venteicher 		rw_wlock(&uma_rwlock);
1751099a0e58SBosko Milekic 		ZONE_LOCK(zone);
1752099a0e58SBosko Milekic 		LIST_FOREACH(z, &keg->uk_zones, uz_link) {
1753099a0e58SBosko Milekic 			if (LIST_NEXT(z, uz_link) == NULL) {
1754099a0e58SBosko Milekic 				LIST_INSERT_AFTER(z, zone, uz_link);
1755099a0e58SBosko Milekic 				break;
1756099a0e58SBosko Milekic 			}
1757099a0e58SBosko Milekic 		}
1758099a0e58SBosko Milekic 		ZONE_UNLOCK(zone);
1759111fbcd5SBryan Venteicher 		rw_wunlock(&uma_rwlock);
1760e20a199fSJeff Roberson 	} else if (keg == NULL) {
1761e20a199fSJeff Roberson 		if ((keg = uma_kcreate(zone, arg->size, arg->uminit, arg->fini,
1762e20a199fSJeff Roberson 		    arg->align, arg->flags)) == NULL)
1763b23f72e9SBrian Feldman 			return (ENOMEM);
1764099a0e58SBosko Milekic 	} else {
1765099a0e58SBosko Milekic 		struct uma_kctor_args karg;
1766b23f72e9SBrian Feldman 		int error;
1767099a0e58SBosko Milekic 
1768099a0e58SBosko Milekic 		/* We should only be here from uma_startup() */
1769099a0e58SBosko Milekic 		karg.size = arg->size;
1770099a0e58SBosko Milekic 		karg.uminit = arg->uminit;
1771099a0e58SBosko Milekic 		karg.fini = arg->fini;
1772099a0e58SBosko Milekic 		karg.align = arg->align;
1773099a0e58SBosko Milekic 		karg.flags = arg->flags;
1774099a0e58SBosko Milekic 		karg.zone = zone;
1775b23f72e9SBrian Feldman 		error = keg_ctor(arg->keg, sizeof(struct uma_keg), &karg,
1776b23f72e9SBrian Feldman 		    flags);
1777b23f72e9SBrian Feldman 		if (error)
1778b23f72e9SBrian Feldman 			return (error);
1779099a0e58SBosko Milekic 	}
17800095a784SJeff Roberson 
1781e20a199fSJeff Roberson 	/*
1782e20a199fSJeff Roberson 	 * Link in the first keg.
1783e20a199fSJeff Roberson 	 */
1784e20a199fSJeff Roberson 	zone->uz_klink.kl_keg = keg;
1785e20a199fSJeff Roberson 	LIST_INSERT_HEAD(&zone->uz_kegs, &zone->uz_klink, kl_link);
1786af526374SJeff Roberson 	zone->uz_lockptr = &keg->uk_lock;
1787e20a199fSJeff Roberson 	zone->uz_size = keg->uk_size;
1788e20a199fSJeff Roberson 	zone->uz_flags |= (keg->uk_flags &
1789e20a199fSJeff Roberson 	    (UMA_ZONE_INHERIT | UMA_ZFLAG_INHERIT));
17908355f576SJeff Roberson 
17918355f576SJeff Roberson 	/*
17928355f576SJeff Roberson 	 * Some internal zones don't have room allocated for the per cpu
17938355f576SJeff Roberson 	 * caches.  If we're internal, bail out here.
17948355f576SJeff Roberson 	 */
1795099a0e58SBosko Milekic 	if (keg->uk_flags & UMA_ZFLAG_INTERNAL) {
1796e20a199fSJeff Roberson 		KASSERT((zone->uz_flags & UMA_ZONE_SECONDARY) == 0,
1797099a0e58SBosko Milekic 		    ("Secondary zone requested UMA_ZFLAG_INTERNAL"));
1798b23f72e9SBrian Feldman 		return (0);
1799099a0e58SBosko Milekic 	}
18008355f576SJeff Roberson 
1801af526374SJeff Roberson out:
18027e28037aSMark Johnston 	KASSERT((arg->flags & (UMA_ZONE_MAXBUCKET | UMA_ZONE_NOBUCKET)) !=
18037e28037aSMark Johnston 	    (UMA_ZONE_MAXBUCKET | UMA_ZONE_NOBUCKET),
18047e28037aSMark Johnston 	    ("Invalid zone flag combination"));
18057e28037aSMark Johnston 	if ((arg->flags & UMA_ZONE_MAXBUCKET) != 0)
1806cae33c14SJeff Roberson 		zone->uz_count = BUCKET_MAX;
18077e28037aSMark Johnston 	else if ((arg->flags & UMA_ZONE_NOBUCKET) != 0)
18087e28037aSMark Johnston 		zone->uz_count = 0;
18097e28037aSMark Johnston 	else
18107e28037aSMark Johnston 		zone->uz_count = bucket_select(zone->uz_size);
1811ace66b56SAlexander Motin 	zone->uz_count_min = zone->uz_count;
1812fc03d22bSJeff Roberson 
1813b23f72e9SBrian Feldman 	return (0);
18148355f576SJeff Roberson }
18158355f576SJeff Roberson 
18168355f576SJeff Roberson /*
1817099a0e58SBosko Milekic  * Keg header dtor.  This frees all data, destroys locks, frees the hash
1818099a0e58SBosko Milekic  * table and removes the keg from the global list.
18199c2cd7e5SJeff Roberson  *
18209c2cd7e5SJeff Roberson  * Arguments/Returns follow uma_dtor specifications
18219c2cd7e5SJeff Roberson  *	udata  unused
18229c2cd7e5SJeff Roberson  */
1823099a0e58SBosko Milekic static void
1824099a0e58SBosko Milekic keg_dtor(void *arg, int size, void *udata)
1825099a0e58SBosko Milekic {
1826099a0e58SBosko Milekic 	uma_keg_t keg;
18279c2cd7e5SJeff Roberson 
1828099a0e58SBosko Milekic 	keg = (uma_keg_t)arg;
1829e20a199fSJeff Roberson 	KEG_LOCK(keg);
1830099a0e58SBosko Milekic 	if (keg->uk_free != 0) {
1831a3845534SCraig Rodrigues 		printf("Freed UMA keg (%s) was not empty (%d items). "
1832099a0e58SBosko Milekic 		    " Lost %d pages of memory.\n",
1833a3845534SCraig Rodrigues 		    keg->uk_name ? keg->uk_name : "",
1834099a0e58SBosko Milekic 		    keg->uk_free, keg->uk_pages);
1835099a0e58SBosko Milekic 	}
1836e20a199fSJeff Roberson 	KEG_UNLOCK(keg);
1837099a0e58SBosko Milekic 
1838099a0e58SBosko Milekic 	hash_free(&keg->uk_hash);
1839099a0e58SBosko Milekic 
1840e20a199fSJeff Roberson 	KEG_LOCK_FINI(keg);
1841099a0e58SBosko Milekic }
1842099a0e58SBosko Milekic 
1843099a0e58SBosko Milekic /*
1844099a0e58SBosko Milekic  * Zone header dtor.
1845099a0e58SBosko Milekic  *
1846099a0e58SBosko Milekic  * Arguments/Returns follow uma_dtor specifications
1847099a0e58SBosko Milekic  *	udata  unused
1848099a0e58SBosko Milekic  */
18499c2cd7e5SJeff Roberson static void
18509c2cd7e5SJeff Roberson zone_dtor(void *arg, int size, void *udata)
18519c2cd7e5SJeff Roberson {
1852e20a199fSJeff Roberson 	uma_klink_t klink;
18539c2cd7e5SJeff Roberson 	uma_zone_t zone;
1854099a0e58SBosko Milekic 	uma_keg_t keg;
18559c2cd7e5SJeff Roberson 
18569c2cd7e5SJeff Roberson 	zone = (uma_zone_t)arg;
1857e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
18589643769aSJeff Roberson 
1859e20a199fSJeff Roberson 	if (!(zone->uz_flags & UMA_ZFLAG_INTERNAL))
18609643769aSJeff Roberson 		cache_drain(zone);
1861099a0e58SBosko Milekic 
1862111fbcd5SBryan Venteicher 	rw_wlock(&uma_rwlock);
1863099a0e58SBosko Milekic 	LIST_REMOVE(zone, uz_link);
1864111fbcd5SBryan Venteicher 	rw_wunlock(&uma_rwlock);
1865099a0e58SBosko Milekic 	/*
1866099a0e58SBosko Milekic 	 * XXX there are some races here where
1867099a0e58SBosko Milekic 	 * the zone can be drained but zone lock
1868099a0e58SBosko Milekic 	 * released and then refilled before we
1869099a0e58SBosko Milekic 	 * remove it... we dont care for now
1870099a0e58SBosko Milekic 	 */
1871e20a199fSJeff Roberson 	zone_drain_wait(zone, M_WAITOK);
1872e20a199fSJeff Roberson 	/*
1873e20a199fSJeff Roberson 	 * Unlink all of our kegs.
1874e20a199fSJeff Roberson 	 */
1875e20a199fSJeff Roberson 	while ((klink = LIST_FIRST(&zone->uz_kegs)) != NULL) {
1876e20a199fSJeff Roberson 		klink->kl_keg = NULL;
1877e20a199fSJeff Roberson 		LIST_REMOVE(klink, kl_link);
1878e20a199fSJeff Roberson 		if (klink == &zone->uz_klink)
1879e20a199fSJeff Roberson 			continue;
1880e20a199fSJeff Roberson 		free(klink, M_TEMP);
1881e20a199fSJeff Roberson 	}
1882e20a199fSJeff Roberson 	/*
1883e20a199fSJeff Roberson 	 * We only destroy kegs from non secondary zones.
1884e20a199fSJeff Roberson 	 */
18850095a784SJeff Roberson 	if (keg != NULL && (zone->uz_flags & UMA_ZONE_SECONDARY) == 0)  {
1886111fbcd5SBryan Venteicher 		rw_wlock(&uma_rwlock);
1887099a0e58SBosko Milekic 		LIST_REMOVE(keg, uk_link);
1888111fbcd5SBryan Venteicher 		rw_wunlock(&uma_rwlock);
18890095a784SJeff Roberson 		zone_free_item(kegs, keg, NULL, SKIP_NONE);
18909c2cd7e5SJeff Roberson 	}
1891af526374SJeff Roberson 	ZONE_LOCK_FINI(zone);
1892099a0e58SBosko Milekic }
1893099a0e58SBosko Milekic 
18949c2cd7e5SJeff Roberson /*
18958355f576SJeff Roberson  * Traverses every zone in the system and calls a callback
18968355f576SJeff Roberson  *
18978355f576SJeff Roberson  * Arguments:
18988355f576SJeff Roberson  *	zfunc  A pointer to a function which accepts a zone
18998355f576SJeff Roberson  *		as an argument.
19008355f576SJeff Roberson  *
19018355f576SJeff Roberson  * Returns:
19028355f576SJeff Roberson  *	Nothing
19038355f576SJeff Roberson  */
19048355f576SJeff Roberson static void
19058355f576SJeff Roberson zone_foreach(void (*zfunc)(uma_zone_t))
19068355f576SJeff Roberson {
1907099a0e58SBosko Milekic 	uma_keg_t keg;
19088355f576SJeff Roberson 	uma_zone_t zone;
19098355f576SJeff Roberson 
1910111fbcd5SBryan Venteicher 	rw_rlock(&uma_rwlock);
1911099a0e58SBosko Milekic 	LIST_FOREACH(keg, &uma_kegs, uk_link) {
1912099a0e58SBosko Milekic 		LIST_FOREACH(zone, &keg->uk_zones, uz_link)
19138355f576SJeff Roberson 			zfunc(zone);
1914099a0e58SBosko Milekic 	}
1915111fbcd5SBryan Venteicher 	rw_runlock(&uma_rwlock);
19168355f576SJeff Roberson }
19178355f576SJeff Roberson 
1918f4bef67cSGleb Smirnoff /*
1919f4bef67cSGleb Smirnoff  * Count how many pages do we need to bootstrap.  VM supplies
1920f4bef67cSGleb Smirnoff  * its need in early zones in the argument, we add up our zones,
1921f4bef67cSGleb Smirnoff  * which consist of: UMA Slabs, UMA Hash and 9 Bucket zones. The
1922f4bef67cSGleb Smirnoff  * zone of zones and zone of kegs are accounted separately.
1923f4bef67cSGleb Smirnoff  */
1924f4bef67cSGleb Smirnoff #define	UMA_BOOT_ZONES	11
19255073a083SGleb Smirnoff /* Zone of zones and zone of kegs have arbitrary alignment. */
19265073a083SGleb Smirnoff #define	UMA_BOOT_ALIGN	32
1927f4bef67cSGleb Smirnoff static int zsize, ksize;
1928f4bef67cSGleb Smirnoff int
1929f7d35785SGleb Smirnoff uma_startup_count(int vm_zones)
1930f4bef67cSGleb Smirnoff {
1931f7d35785SGleb Smirnoff 	int zones, pages;
1932f4bef67cSGleb Smirnoff 
1933f4bef67cSGleb Smirnoff 	ksize = sizeof(struct uma_keg) +
1934f4bef67cSGleb Smirnoff 	    (sizeof(struct uma_domain) * vm_ndomains);
1935f4bef67cSGleb Smirnoff 	zsize = sizeof(struct uma_zone) +
1936f4bef67cSGleb Smirnoff 	    (sizeof(struct uma_cache) * (mp_maxid + 1)) +
1937f4bef67cSGleb Smirnoff 	    (sizeof(struct uma_zone_domain) * vm_ndomains);
1938f4bef67cSGleb Smirnoff 
19395073a083SGleb Smirnoff 	/*
19405073a083SGleb Smirnoff 	 * Memory for the zone of kegs and its keg,
19415073a083SGleb Smirnoff 	 * and for zone of zones.
19425073a083SGleb Smirnoff 	 */
1943f4bef67cSGleb Smirnoff 	pages = howmany(roundup(zsize, CACHE_LINE_SIZE) * 2 +
1944f4bef67cSGleb Smirnoff 	    roundup(ksize, CACHE_LINE_SIZE), PAGE_SIZE);
1945f4bef67cSGleb Smirnoff 
1946f7d35785SGleb Smirnoff #ifdef	UMA_MD_SMALL_ALLOC
1947f7d35785SGleb Smirnoff 	zones = UMA_BOOT_ZONES;
1948f7d35785SGleb Smirnoff #else
1949f7d35785SGleb Smirnoff 	zones = UMA_BOOT_ZONES + vm_zones;
1950f7d35785SGleb Smirnoff 	vm_zones = 0;
1951f7d35785SGleb Smirnoff #endif
1952f4bef67cSGleb Smirnoff 
19535073a083SGleb Smirnoff 	/* Memory for the rest of startup zones, UMA and VM, ... */
195496a10340SGleb Smirnoff 	if (zsize > UMA_SLAB_SPACE)
1955f7d35785SGleb Smirnoff 		pages += (zones + vm_zones) *
1956f7d35785SGleb Smirnoff 		    howmany(roundup2(zsize, UMA_BOOT_ALIGN), UMA_SLAB_SIZE);
195796a10340SGleb Smirnoff 	else if (roundup2(zsize, UMA_BOOT_ALIGN) > UMA_SLAB_SPACE)
195896a10340SGleb Smirnoff 		pages += zones;
1959f4bef67cSGleb Smirnoff 	else
19605073a083SGleb Smirnoff 		pages += howmany(zones,
19615073a083SGleb Smirnoff 		    UMA_SLAB_SPACE / roundup2(zsize, UMA_BOOT_ALIGN));
1962f4bef67cSGleb Smirnoff 
19635073a083SGleb Smirnoff 	/* ... and their kegs. Note that zone of zones allocates a keg! */
19645073a083SGleb Smirnoff 	pages += howmany(zones + 1,
19655073a083SGleb Smirnoff 	    UMA_SLAB_SPACE / roundup2(ksize, UMA_BOOT_ALIGN));
1966f4bef67cSGleb Smirnoff 
1967f4bef67cSGleb Smirnoff 	/*
19685073a083SGleb Smirnoff 	 * Most of startup zones are not going to be offpages, that's
19695073a083SGleb Smirnoff 	 * why we use UMA_SLAB_SPACE instead of UMA_SLAB_SIZE in all
19705073a083SGleb Smirnoff 	 * calculations.  Some large bucket zones will be offpage, and
19715073a083SGleb Smirnoff 	 * thus will allocate hashes.  We take conservative approach
19725073a083SGleb Smirnoff 	 * and assume that all zones may allocate hash.  This may give
19735073a083SGleb Smirnoff 	 * us some positive inaccuracy, usually an extra single page.
1974f4bef67cSGleb Smirnoff 	 */
19755073a083SGleb Smirnoff 	pages += howmany(zones, UMA_SLAB_SPACE /
1976d2be4a1eSGleb Smirnoff 	    (sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT));
1977f4bef67cSGleb Smirnoff 
1978f4bef67cSGleb Smirnoff 	return (pages);
1979f4bef67cSGleb Smirnoff }
1980f4bef67cSGleb Smirnoff 
19818355f576SJeff Roberson void
1982ac0a6fd0SGleb Smirnoff uma_startup(void *mem, int npages)
19838355f576SJeff Roberson {
19848355f576SJeff Roberson 	struct uma_zctor_args args;
1985ab3185d1SJeff Roberson 	uma_keg_t masterkeg;
1986ab3185d1SJeff Roberson 	uintptr_t m;
1987f4bef67cSGleb Smirnoff 
1988f4bef67cSGleb Smirnoff #ifdef DIAGNOSTIC
1989f4bef67cSGleb Smirnoff 	printf("Entering %s with %d boot pages configured\n", __func__, npages);
1990f4bef67cSGleb Smirnoff #endif
19918355f576SJeff Roberson 
1992111fbcd5SBryan Venteicher 	rw_init(&uma_rwlock, "UMA lock");
1993099a0e58SBosko Milekic 
1994ab3185d1SJeff Roberson 	/* Use bootpages memory for the zone of zones and zone of kegs. */
1995ab3185d1SJeff Roberson 	m = (uintptr_t)mem;
1996ab3185d1SJeff Roberson 	zones = (uma_zone_t)m;
1997ab3185d1SJeff Roberson 	m += roundup(zsize, CACHE_LINE_SIZE);
1998ab3185d1SJeff Roberson 	kegs = (uma_zone_t)m;
1999ab3185d1SJeff Roberson 	m += roundup(zsize, CACHE_LINE_SIZE);
2000ab3185d1SJeff Roberson 	masterkeg = (uma_keg_t)m;
2001ab3185d1SJeff Roberson 	m += roundup(ksize, CACHE_LINE_SIZE);
2002ab3185d1SJeff Roberson 	m = roundup(m, PAGE_SIZE);
2003ab3185d1SJeff Roberson 	npages -= (m - (uintptr_t)mem) / PAGE_SIZE;
2004ab3185d1SJeff Roberson 	mem = (void *)m;
2005ab3185d1SJeff Roberson 
2006099a0e58SBosko Milekic 	/* "manually" create the initial zone */
20070095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
2008099a0e58SBosko Milekic 	args.name = "UMA Kegs";
2009ab3185d1SJeff Roberson 	args.size = ksize;
2010099a0e58SBosko Milekic 	args.ctor = keg_ctor;
2011099a0e58SBosko Milekic 	args.dtor = keg_dtor;
20128355f576SJeff Roberson 	args.uminit = zero_init;
20138355f576SJeff Roberson 	args.fini = NULL;
2014ab3185d1SJeff Roberson 	args.keg = masterkeg;
20155073a083SGleb Smirnoff 	args.align = UMA_BOOT_ALIGN - 1;
2016b60f5b79SJeff Roberson 	args.flags = UMA_ZFLAG_INTERNAL;
2017ab3185d1SJeff Roberson 	zone_ctor(kegs, zsize, &args, M_WAITOK);
20188355f576SJeff Roberson 
2019ac0a6fd0SGleb Smirnoff 	bootmem = mem;
2020ac0a6fd0SGleb Smirnoff 	boot_pages = npages;
20218355f576SJeff Roberson 
2022099a0e58SBosko Milekic 	args.name = "UMA Zones";
2023f4bef67cSGleb Smirnoff 	args.size = zsize;
2024099a0e58SBosko Milekic 	args.ctor = zone_ctor;
2025099a0e58SBosko Milekic 	args.dtor = zone_dtor;
2026099a0e58SBosko Milekic 	args.uminit = zero_init;
2027099a0e58SBosko Milekic 	args.fini = NULL;
2028099a0e58SBosko Milekic 	args.keg = NULL;
20295073a083SGleb Smirnoff 	args.align = UMA_BOOT_ALIGN - 1;
2030099a0e58SBosko Milekic 	args.flags = UMA_ZFLAG_INTERNAL;
2031ab3185d1SJeff Roberson 	zone_ctor(zones, zsize, &args, M_WAITOK);
2032099a0e58SBosko Milekic 
20338355f576SJeff Roberson 	/* Now make a zone for slab headers */
20348355f576SJeff Roberson 	slabzone = uma_zcreate("UMA Slabs",
2035ef72505eSJeff Roberson 				sizeof(struct uma_slab),
20368355f576SJeff Roberson 				NULL, NULL, NULL, NULL,
2037b60f5b79SJeff Roberson 				UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
20388355f576SJeff Roberson 
20398355f576SJeff Roberson 	hashzone = uma_zcreate("UMA Hash",
20408355f576SJeff Roberson 	    sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT,
20418355f576SJeff Roberson 	    NULL, NULL, NULL, NULL,
2042b60f5b79SJeff Roberson 	    UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL);
20438355f576SJeff Roberson 
2044cae33c14SJeff Roberson 	bucket_init();
20458355f576SJeff Roberson 
2046f4bef67cSGleb Smirnoff 	booted = BOOT_STRAPPED;
20478355f576SJeff Roberson }
20488355f576SJeff Roberson 
2049f4bef67cSGleb Smirnoff void
2050f4bef67cSGleb Smirnoff uma_startup1(void)
2051f4bef67cSGleb Smirnoff {
2052f4bef67cSGleb Smirnoff 
2053f4bef67cSGleb Smirnoff #ifdef DIAGNOSTIC
2054f4bef67cSGleb Smirnoff 	printf("Entering %s with %d boot pages left\n", __func__, boot_pages);
2055f4bef67cSGleb Smirnoff #endif
2056f4bef67cSGleb Smirnoff 	booted = BOOT_PAGEALLOC;
2057f4bef67cSGleb Smirnoff }
2058f4bef67cSGleb Smirnoff 
20598355f576SJeff Roberson void
206099571dc3SJeff Roberson uma_startup2(void)
20618355f576SJeff Roberson {
2062f4bef67cSGleb Smirnoff 
2063f7d35785SGleb Smirnoff #ifdef DIAGNOSTIC
2064f7d35785SGleb Smirnoff 	printf("Entering %s with %d boot pages left\n", __func__, boot_pages);
2065f7d35785SGleb Smirnoff #endif
2066f4bef67cSGleb Smirnoff 	booted = BOOT_BUCKETS;
206795c4bf75SKonstantin Belousov 	sx_init(&uma_drain_lock, "umadrain");
2068f4bef67cSGleb Smirnoff 	bucket_enable();
20698355f576SJeff Roberson }
20708355f576SJeff Roberson 
20718355f576SJeff Roberson /*
20728355f576SJeff Roberson  * Initialize our callout handle
20738355f576SJeff Roberson  *
20748355f576SJeff Roberson  */
20758355f576SJeff Roberson static void
20768355f576SJeff Roberson uma_startup3(void)
20778355f576SJeff Roberson {
20781431a748SGleb Smirnoff 
2079c5deaf04SGleb Smirnoff #ifdef INVARIANTS
2080c5deaf04SGleb Smirnoff 	TUNABLE_INT_FETCH("vm.debug.divisor", &dbg_divisor);
2081c5deaf04SGleb Smirnoff 	uma_dbg_cnt = counter_u64_alloc(M_WAITOK);
2082c5deaf04SGleb Smirnoff 	uma_skip_cnt = counter_u64_alloc(M_WAITOK);
2083c5deaf04SGleb Smirnoff #endif
2084fd90e2edSJung-uk Kim 	callout_init(&uma_callout, 1);
20859643769aSJeff Roberson 	callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL);
2086c5deaf04SGleb Smirnoff 	booted = BOOT_RUNNING;
20878355f576SJeff Roberson }
20888355f576SJeff Roberson 
2089e20a199fSJeff Roberson static uma_keg_t
2090099a0e58SBosko Milekic uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini,
209185dcf349SGleb Smirnoff 		int align, uint32_t flags)
2092099a0e58SBosko Milekic {
2093099a0e58SBosko Milekic 	struct uma_kctor_args args;
2094099a0e58SBosko Milekic 
2095099a0e58SBosko Milekic 	args.size = size;
2096099a0e58SBosko Milekic 	args.uminit = uminit;
2097099a0e58SBosko Milekic 	args.fini = fini;
20981e319f6dSRobert Watson 	args.align = (align == UMA_ALIGN_CACHE) ? uma_align_cache : align;
2099099a0e58SBosko Milekic 	args.flags = flags;
2100099a0e58SBosko Milekic 	args.zone = zone;
2101ab3185d1SJeff Roberson 	return (zone_alloc_item(kegs, &args, UMA_ANYDOMAIN, M_WAITOK));
2102099a0e58SBosko Milekic }
2103099a0e58SBosko Milekic 
2104f4bef67cSGleb Smirnoff /* Public functions */
21058355f576SJeff Roberson /* See uma.h */
21061e319f6dSRobert Watson void
21071e319f6dSRobert Watson uma_set_align(int align)
21081e319f6dSRobert Watson {
21091e319f6dSRobert Watson 
21101e319f6dSRobert Watson 	if (align != UMA_ALIGN_CACHE)
21111e319f6dSRobert Watson 		uma_align_cache = align;
21121e319f6dSRobert Watson }
21131e319f6dSRobert Watson 
21141e319f6dSRobert Watson /* See uma.h */
21158355f576SJeff Roberson uma_zone_t
2116bb196eb4SMatthew D Fleming uma_zcreate(const char *name, size_t size, uma_ctor ctor, uma_dtor dtor,
211785dcf349SGleb Smirnoff 		uma_init uminit, uma_fini fini, int align, uint32_t flags)
21188355f576SJeff Roberson 
21198355f576SJeff Roberson {
21208355f576SJeff Roberson 	struct uma_zctor_args args;
212195c4bf75SKonstantin Belousov 	uma_zone_t res;
212295c4bf75SKonstantin Belousov 	bool locked;
21238355f576SJeff Roberson 
2124a5a35578SJohn Baldwin 	KASSERT(powerof2(align + 1), ("invalid zone alignment %d for \"%s\"",
2125a5a35578SJohn Baldwin 	    align, name));
2126a5a35578SJohn Baldwin 
21278355f576SJeff Roberson 	/* This stuff is essential for the zone ctor */
21280095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
21298355f576SJeff Roberson 	args.name = name;
21308355f576SJeff Roberson 	args.size = size;
21318355f576SJeff Roberson 	args.ctor = ctor;
21328355f576SJeff Roberson 	args.dtor = dtor;
21338355f576SJeff Roberson 	args.uminit = uminit;
21348355f576SJeff Roberson 	args.fini = fini;
2135afc6dc36SJohn-Mark Gurney #ifdef  INVARIANTS
2136afc6dc36SJohn-Mark Gurney 	/*
2137afc6dc36SJohn-Mark Gurney 	 * If a zone is being created with an empty constructor and
2138afc6dc36SJohn-Mark Gurney 	 * destructor, pass UMA constructor/destructor which checks for
2139afc6dc36SJohn-Mark Gurney 	 * memory use after free.
2140afc6dc36SJohn-Mark Gurney 	 */
214119c591bfSMateusz Guzik 	if ((!(flags & (UMA_ZONE_ZINIT | UMA_ZONE_NOFREE))) &&
214219c591bfSMateusz Guzik 	    ctor == NULL && dtor == NULL && uminit == NULL && fini == NULL) {
2143afc6dc36SJohn-Mark Gurney 		args.ctor = trash_ctor;
2144afc6dc36SJohn-Mark Gurney 		args.dtor = trash_dtor;
2145afc6dc36SJohn-Mark Gurney 		args.uminit = trash_init;
2146afc6dc36SJohn-Mark Gurney 		args.fini = trash_fini;
2147afc6dc36SJohn-Mark Gurney 	}
2148afc6dc36SJohn-Mark Gurney #endif
21498355f576SJeff Roberson 	args.align = align;
21508355f576SJeff Roberson 	args.flags = flags;
2151099a0e58SBosko Milekic 	args.keg = NULL;
2152099a0e58SBosko Milekic 
2153f4bef67cSGleb Smirnoff 	if (booted < BOOT_BUCKETS) {
215495c4bf75SKonstantin Belousov 		locked = false;
215595c4bf75SKonstantin Belousov 	} else {
215695c4bf75SKonstantin Belousov 		sx_slock(&uma_drain_lock);
215795c4bf75SKonstantin Belousov 		locked = true;
215895c4bf75SKonstantin Belousov 	}
2159ab3185d1SJeff Roberson 	res = zone_alloc_item(zones, &args, UMA_ANYDOMAIN, M_WAITOK);
216095c4bf75SKonstantin Belousov 	if (locked)
216195c4bf75SKonstantin Belousov 		sx_sunlock(&uma_drain_lock);
216295c4bf75SKonstantin Belousov 	return (res);
2163099a0e58SBosko Milekic }
2164099a0e58SBosko Milekic 
2165099a0e58SBosko Milekic /* See uma.h */
2166099a0e58SBosko Milekic uma_zone_t
2167099a0e58SBosko Milekic uma_zsecond_create(char *name, uma_ctor ctor, uma_dtor dtor,
2168099a0e58SBosko Milekic 		    uma_init zinit, uma_fini zfini, uma_zone_t master)
2169099a0e58SBosko Milekic {
2170099a0e58SBosko Milekic 	struct uma_zctor_args args;
2171e20a199fSJeff Roberson 	uma_keg_t keg;
217295c4bf75SKonstantin Belousov 	uma_zone_t res;
217395c4bf75SKonstantin Belousov 	bool locked;
2174099a0e58SBosko Milekic 
2175e20a199fSJeff Roberson 	keg = zone_first_keg(master);
21760095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
2177099a0e58SBosko Milekic 	args.name = name;
2178e20a199fSJeff Roberson 	args.size = keg->uk_size;
2179099a0e58SBosko Milekic 	args.ctor = ctor;
2180099a0e58SBosko Milekic 	args.dtor = dtor;
2181099a0e58SBosko Milekic 	args.uminit = zinit;
2182099a0e58SBosko Milekic 	args.fini = zfini;
2183e20a199fSJeff Roberson 	args.align = keg->uk_align;
2184e20a199fSJeff Roberson 	args.flags = keg->uk_flags | UMA_ZONE_SECONDARY;
2185e20a199fSJeff Roberson 	args.keg = keg;
21868355f576SJeff Roberson 
2187f4bef67cSGleb Smirnoff 	if (booted < BOOT_BUCKETS) {
218895c4bf75SKonstantin Belousov 		locked = false;
218995c4bf75SKonstantin Belousov 	} else {
219095c4bf75SKonstantin Belousov 		sx_slock(&uma_drain_lock);
219195c4bf75SKonstantin Belousov 		locked = true;
219295c4bf75SKonstantin Belousov 	}
2193e20a199fSJeff Roberson 	/* XXX Attaches only one keg of potentially many. */
2194ab3185d1SJeff Roberson 	res = zone_alloc_item(zones, &args, UMA_ANYDOMAIN, M_WAITOK);
219595c4bf75SKonstantin Belousov 	if (locked)
219695c4bf75SKonstantin Belousov 		sx_sunlock(&uma_drain_lock);
219795c4bf75SKonstantin Belousov 	return (res);
21988355f576SJeff Roberson }
21998355f576SJeff Roberson 
22000095a784SJeff Roberson /* See uma.h */
22010095a784SJeff Roberson uma_zone_t
2202af526374SJeff Roberson uma_zcache_create(char *name, int size, uma_ctor ctor, uma_dtor dtor,
2203af526374SJeff Roberson 		    uma_init zinit, uma_fini zfini, uma_import zimport,
2204af526374SJeff Roberson 		    uma_release zrelease, void *arg, int flags)
22050095a784SJeff Roberson {
22060095a784SJeff Roberson 	struct uma_zctor_args args;
22070095a784SJeff Roberson 
22080095a784SJeff Roberson 	memset(&args, 0, sizeof(args));
22090095a784SJeff Roberson 	args.name = name;
2210af526374SJeff Roberson 	args.size = size;
22110095a784SJeff Roberson 	args.ctor = ctor;
22120095a784SJeff Roberson 	args.dtor = dtor;
22130095a784SJeff Roberson 	args.uminit = zinit;
22140095a784SJeff Roberson 	args.fini = zfini;
22150095a784SJeff Roberson 	args.import = zimport;
22160095a784SJeff Roberson 	args.release = zrelease;
22170095a784SJeff Roberson 	args.arg = arg;
22180095a784SJeff Roberson 	args.align = 0;
22190095a784SJeff Roberson 	args.flags = flags;
22200095a784SJeff Roberson 
2221ab3185d1SJeff Roberson 	return (zone_alloc_item(zones, &args, UMA_ANYDOMAIN, M_WAITOK));
22220095a784SJeff Roberson }
22230095a784SJeff Roberson 
2224e20a199fSJeff Roberson static void
2225e20a199fSJeff Roberson zone_lock_pair(uma_zone_t a, uma_zone_t b)
2226e20a199fSJeff Roberson {
2227e20a199fSJeff Roberson 	if (a < b) {
2228e20a199fSJeff Roberson 		ZONE_LOCK(a);
2229af526374SJeff Roberson 		mtx_lock_flags(b->uz_lockptr, MTX_DUPOK);
2230e20a199fSJeff Roberson 	} else {
2231e20a199fSJeff Roberson 		ZONE_LOCK(b);
2232af526374SJeff Roberson 		mtx_lock_flags(a->uz_lockptr, MTX_DUPOK);
2233e20a199fSJeff Roberson 	}
2234e20a199fSJeff Roberson }
2235e20a199fSJeff Roberson 
2236e20a199fSJeff Roberson static void
2237e20a199fSJeff Roberson zone_unlock_pair(uma_zone_t a, uma_zone_t b)
2238e20a199fSJeff Roberson {
2239e20a199fSJeff Roberson 
2240e20a199fSJeff Roberson 	ZONE_UNLOCK(a);
2241e20a199fSJeff Roberson 	ZONE_UNLOCK(b);
2242e20a199fSJeff Roberson }
2243e20a199fSJeff Roberson 
2244e20a199fSJeff Roberson int
2245e20a199fSJeff Roberson uma_zsecond_add(uma_zone_t zone, uma_zone_t master)
2246e20a199fSJeff Roberson {
2247e20a199fSJeff Roberson 	uma_klink_t klink;
2248e20a199fSJeff Roberson 	uma_klink_t kl;
2249e20a199fSJeff Roberson 	int error;
2250e20a199fSJeff Roberson 
2251e20a199fSJeff Roberson 	error = 0;
2252e20a199fSJeff Roberson 	klink = malloc(sizeof(*klink), M_TEMP, M_WAITOK | M_ZERO);
2253e20a199fSJeff Roberson 
2254e20a199fSJeff Roberson 	zone_lock_pair(zone, master);
2255e20a199fSJeff Roberson 	/*
2256e20a199fSJeff Roberson 	 * zone must use vtoslab() to resolve objects and must already be
2257e20a199fSJeff Roberson 	 * a secondary.
2258e20a199fSJeff Roberson 	 */
2259e20a199fSJeff Roberson 	if ((zone->uz_flags & (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY))
2260e20a199fSJeff Roberson 	    != (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) {
2261e20a199fSJeff Roberson 		error = EINVAL;
2262e20a199fSJeff Roberson 		goto out;
2263e20a199fSJeff Roberson 	}
2264e20a199fSJeff Roberson 	/*
2265e20a199fSJeff Roberson 	 * The new master must also use vtoslab().
2266e20a199fSJeff Roberson 	 */
2267e20a199fSJeff Roberson 	if ((zone->uz_flags & UMA_ZONE_VTOSLAB) != UMA_ZONE_VTOSLAB) {
2268e20a199fSJeff Roberson 		error = EINVAL;
2269e20a199fSJeff Roberson 		goto out;
2270e20a199fSJeff Roberson 	}
2271cfcae3f8SGleb Smirnoff 
2272e20a199fSJeff Roberson 	/*
2273e20a199fSJeff Roberson 	 * The underlying object must be the same size.  rsize
2274e20a199fSJeff Roberson 	 * may be different.
2275e20a199fSJeff Roberson 	 */
2276e20a199fSJeff Roberson 	if (master->uz_size != zone->uz_size) {
2277e20a199fSJeff Roberson 		error = E2BIG;
2278e20a199fSJeff Roberson 		goto out;
2279e20a199fSJeff Roberson 	}
2280e20a199fSJeff Roberson 	/*
2281e20a199fSJeff Roberson 	 * Put it at the end of the list.
2282e20a199fSJeff Roberson 	 */
2283e20a199fSJeff Roberson 	klink->kl_keg = zone_first_keg(master);
2284e20a199fSJeff Roberson 	LIST_FOREACH(kl, &zone->uz_kegs, kl_link) {
2285e20a199fSJeff Roberson 		if (LIST_NEXT(kl, kl_link) == NULL) {
2286e20a199fSJeff Roberson 			LIST_INSERT_AFTER(kl, klink, kl_link);
2287e20a199fSJeff Roberson 			break;
2288e20a199fSJeff Roberson 		}
2289e20a199fSJeff Roberson 	}
2290e20a199fSJeff Roberson 	klink = NULL;
2291e20a199fSJeff Roberson 	zone->uz_flags |= UMA_ZFLAG_MULTI;
2292e20a199fSJeff Roberson 	zone->uz_slab = zone_fetch_slab_multi;
2293e20a199fSJeff Roberson 
2294e20a199fSJeff Roberson out:
2295e20a199fSJeff Roberson 	zone_unlock_pair(zone, master);
2296e20a199fSJeff Roberson 	if (klink != NULL)
2297e20a199fSJeff Roberson 		free(klink, M_TEMP);
2298e20a199fSJeff Roberson 
2299e20a199fSJeff Roberson 	return (error);
2300e20a199fSJeff Roberson }
2301e20a199fSJeff Roberson 
2302e20a199fSJeff Roberson 
23038355f576SJeff Roberson /* See uma.h */
23049c2cd7e5SJeff Roberson void
23059c2cd7e5SJeff Roberson uma_zdestroy(uma_zone_t zone)
23069c2cd7e5SJeff Roberson {
2307f4ff923bSRobert Watson 
230895c4bf75SKonstantin Belousov 	sx_slock(&uma_drain_lock);
23090095a784SJeff Roberson 	zone_free_item(zones, zone, NULL, SKIP_NONE);
231095c4bf75SKonstantin Belousov 	sx_sunlock(&uma_drain_lock);
23119c2cd7e5SJeff Roberson }
23129c2cd7e5SJeff Roberson 
23138d6fbbb8SJeff Roberson void
23148d6fbbb8SJeff Roberson uma_zwait(uma_zone_t zone)
23158d6fbbb8SJeff Roberson {
23168d6fbbb8SJeff Roberson 	void *item;
23178d6fbbb8SJeff Roberson 
23188d6fbbb8SJeff Roberson 	item = uma_zalloc_arg(zone, NULL, M_WAITOK);
23198d6fbbb8SJeff Roberson 	uma_zfree(zone, item);
23208d6fbbb8SJeff Roberson }
23218d6fbbb8SJeff Roberson 
23224e180881SMateusz Guzik void *
23234e180881SMateusz Guzik uma_zalloc_pcpu_arg(uma_zone_t zone, void *udata, int flags)
23244e180881SMateusz Guzik {
23254e180881SMateusz Guzik 	void *item;
2326b4799947SRuslan Bukin #ifdef SMP
23274e180881SMateusz Guzik 	int i;
23284e180881SMateusz Guzik 
23294e180881SMateusz Guzik 	MPASS(zone->uz_flags & UMA_ZONE_PCPU);
2330b4799947SRuslan Bukin #endif
23314e180881SMateusz Guzik 	item = uma_zalloc_arg(zone, udata, flags &~ M_ZERO);
23324e180881SMateusz Guzik 	if (item != NULL && (flags & M_ZERO)) {
2333b4799947SRuslan Bukin #ifdef SMP
23344e180881SMateusz Guzik 		CPU_FOREACH(i)
23354e180881SMateusz Guzik 			bzero(zpcpu_get_cpu(item, i), zone->uz_size);
2336b4799947SRuslan Bukin #else
2337b4799947SRuslan Bukin 		bzero(item, zone->uz_size);
2338b4799947SRuslan Bukin #endif
23394e180881SMateusz Guzik 	}
23404e180881SMateusz Guzik 	return (item);
23414e180881SMateusz Guzik }
23424e180881SMateusz Guzik 
23434e180881SMateusz Guzik /*
23444e180881SMateusz Guzik  * A stub while both regular and pcpu cases are identical.
23454e180881SMateusz Guzik  */
23464e180881SMateusz Guzik void
23474e180881SMateusz Guzik uma_zfree_pcpu_arg(uma_zone_t zone, void *item, void *udata)
23484e180881SMateusz Guzik {
23494e180881SMateusz Guzik 
2350c5b7751fSIan Lepore #ifdef SMP
23514e180881SMateusz Guzik 	MPASS(zone->uz_flags & UMA_ZONE_PCPU);
2352c5b7751fSIan Lepore #endif
23534e180881SMateusz Guzik 	uma_zfree_arg(zone, item, udata);
23544e180881SMateusz Guzik }
23554e180881SMateusz Guzik 
23569c2cd7e5SJeff Roberson /* See uma.h */
23578355f576SJeff Roberson void *
23582cc35ff9SJeff Roberson uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
23598355f576SJeff Roberson {
2360ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
23618355f576SJeff Roberson 	uma_bucket_t bucket;
2362ab3185d1SJeff Roberson 	uma_cache_t cache;
2363ab3185d1SJeff Roberson 	void *item;
2364ab3185d1SJeff Roberson 	int cpu, domain, lockfail;
2365c5deaf04SGleb Smirnoff #ifdef INVARIANTS
2366c5deaf04SGleb Smirnoff 	bool skipdbg;
2367c5deaf04SGleb Smirnoff #endif
23688355f576SJeff Roberson 
2369e866d8f0SMark Murray 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
2370e866d8f0SMark Murray 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
237110cb2424SMark Murray 
23728355f576SJeff Roberson 	/* This is the fast path allocation */
23731431a748SGleb Smirnoff 	CTR4(KTR_UMA, "uma_zalloc_arg thread %x zone %s(%p) flags %d",
23741431a748SGleb Smirnoff 	    curthread, zone->uz_name, zone, flags);
2375a553d4b8SJeff Roberson 
2376635fd505SRobert Watson 	if (flags & M_WAITOK) {
2377b23f72e9SBrian Feldman 		WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
2378635fd505SRobert Watson 		    "uma_zalloc_arg: zone \"%s\"", zone->uz_name);
23794c1cc01cSJohn Baldwin 	}
23800766f278SJonathan T. Looney 	KASSERT((flags & M_EXEC) == 0, ("uma_zalloc_arg: called with M_EXEC"));
2381d9e2e68dSMark Johnston 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
23821067a2baSJonathan T. Looney 	    ("uma_zalloc_arg: called with spinlock or critical section held"));
2383ea99223eSMateusz Guzik 	if (zone->uz_flags & UMA_ZONE_PCPU)
2384b8af2820SMateusz Guzik 		KASSERT((flags & M_ZERO) == 0, ("allocating from a pcpu zone "
2385b8af2820SMateusz Guzik 		    "with M_ZERO passed"));
23861067a2baSJonathan T. Looney 
23878d689e04SGleb Smirnoff #ifdef DEBUG_MEMGUARD
23888d689e04SGleb Smirnoff 	if (memguard_cmp_zone(zone)) {
23898d689e04SGleb Smirnoff 		item = memguard_alloc(zone->uz_size, flags);
23908d689e04SGleb Smirnoff 		if (item != NULL) {
23918d689e04SGleb Smirnoff 			if (zone->uz_init != NULL &&
23928d689e04SGleb Smirnoff 			    zone->uz_init(item, zone->uz_size, flags) != 0)
23938d689e04SGleb Smirnoff 				return (NULL);
23948d689e04SGleb Smirnoff 			if (zone->uz_ctor != NULL &&
2395fc03d22bSJeff Roberson 			    zone->uz_ctor(item, zone->uz_size, udata,
2396fc03d22bSJeff Roberson 			    flags) != 0) {
23978d689e04SGleb Smirnoff 			    	zone->uz_fini(item, zone->uz_size);
23988d689e04SGleb Smirnoff 				return (NULL);
23998d689e04SGleb Smirnoff 			}
24008d689e04SGleb Smirnoff 			return (item);
24018d689e04SGleb Smirnoff 		}
24028d689e04SGleb Smirnoff 		/* This is unfortunate but should not be fatal. */
24038d689e04SGleb Smirnoff 	}
24048d689e04SGleb Smirnoff #endif
24055d1ae027SRobert Watson 	/*
24065d1ae027SRobert Watson 	 * If possible, allocate from the per-CPU cache.  There are two
24075d1ae027SRobert Watson 	 * requirements for safe access to the per-CPU cache: (1) the thread
24085d1ae027SRobert Watson 	 * accessing the cache must not be preempted or yield during access,
24095d1ae027SRobert Watson 	 * and (2) the thread must not migrate CPUs without switching which
24105d1ae027SRobert Watson 	 * cache it accesses.  We rely on a critical section to prevent
24115d1ae027SRobert Watson 	 * preemption and migration.  We release the critical section in
24125d1ae027SRobert Watson 	 * order to acquire the zone mutex if we are unable to allocate from
24135d1ae027SRobert Watson 	 * the current cache; when we re-acquire the critical section, we
24145d1ae027SRobert Watson 	 * must detect and handle migration if it has occurred.
24155d1ae027SRobert Watson 	 */
24165d1ae027SRobert Watson 	critical_enter();
24175d1ae027SRobert Watson 	cpu = curcpu;
24188355f576SJeff Roberson 	cache = &zone->uz_cpu[cpu];
24198355f576SJeff Roberson 
24208355f576SJeff Roberson zalloc_start:
24218355f576SJeff Roberson 	bucket = cache->uc_allocbucket;
2422fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt > 0) {
2423cae33c14SJeff Roberson 		bucket->ub_cnt--;
2424cae33c14SJeff Roberson 		item = bucket->ub_bucket[bucket->ub_cnt];
24258355f576SJeff Roberson #ifdef INVARIANTS
2426cae33c14SJeff Roberson 		bucket->ub_bucket[bucket->ub_cnt] = NULL;
24278355f576SJeff Roberson #endif
2428fc03d22bSJeff Roberson 		KASSERT(item != NULL, ("uma_zalloc: Bucket pointer mangled."));
24298355f576SJeff Roberson 		cache->uc_allocs++;
24305d1ae027SRobert Watson 		critical_exit();
2431c5deaf04SGleb Smirnoff #ifdef INVARIANTS
2432c5deaf04SGleb Smirnoff 		skipdbg = uma_dbg_zskip(zone, item);
2433c5deaf04SGleb Smirnoff #endif
2434fc03d22bSJeff Roberson 		if (zone->uz_ctor != NULL &&
2435c5deaf04SGleb Smirnoff #ifdef INVARIANTS
2436c5deaf04SGleb Smirnoff 		    (!skipdbg || zone->uz_ctor != trash_ctor ||
2437c5deaf04SGleb Smirnoff 		    zone->uz_dtor != trash_dtor) &&
2438c5deaf04SGleb Smirnoff #endif
2439fc03d22bSJeff Roberson 		    zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) {
24400095a784SJeff Roberson 			atomic_add_long(&zone->uz_fails, 1);
2441fc03d22bSJeff Roberson 			zone_free_item(zone, item, udata, SKIP_DTOR);
2442b23f72e9SBrian Feldman 			return (NULL);
2443b23f72e9SBrian Feldman 		}
2444ef72505eSJeff Roberson #ifdef INVARIANTS
2445c5deaf04SGleb Smirnoff 		if (!skipdbg)
2446ef72505eSJeff Roberson 			uma_dbg_alloc(zone, NULL, item);
2447ef72505eSJeff Roberson #endif
24482cc35ff9SJeff Roberson 		if (flags & M_ZERO)
244948343a2fSGleb Smirnoff 			uma_zero_item(item, zone);
24508355f576SJeff Roberson 		return (item);
2451fc03d22bSJeff Roberson 	}
2452fc03d22bSJeff Roberson 
24538355f576SJeff Roberson 	/*
24548355f576SJeff Roberson 	 * We have run out of items in our alloc bucket.
24558355f576SJeff Roberson 	 * See if we can switch with our free bucket.
24568355f576SJeff Roberson 	 */
2457b983089aSJeff Roberson 	bucket = cache->uc_freebucket;
2458fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt > 0) {
24591431a748SGleb Smirnoff 		CTR2(KTR_UMA,
24601431a748SGleb Smirnoff 		    "uma_zalloc: zone %s(%p) swapping empty with alloc",
24611431a748SGleb Smirnoff 		    zone->uz_name, zone);
24628355f576SJeff Roberson 		cache->uc_freebucket = cache->uc_allocbucket;
2463b983089aSJeff Roberson 		cache->uc_allocbucket = bucket;
24648355f576SJeff Roberson 		goto zalloc_start;
24658355f576SJeff Roberson 	}
2466fc03d22bSJeff Roberson 
2467fc03d22bSJeff Roberson 	/*
2468fc03d22bSJeff Roberson 	 * Discard any empty allocation bucket while we hold no locks.
2469fc03d22bSJeff Roberson 	 */
2470fc03d22bSJeff Roberson 	bucket = cache->uc_allocbucket;
2471fc03d22bSJeff Roberson 	cache->uc_allocbucket = NULL;
2472fc03d22bSJeff Roberson 	critical_exit();
2473fc03d22bSJeff Roberson 	if (bucket != NULL)
24746fd34d6fSJeff Roberson 		bucket_free(zone, bucket, udata);
2475fc03d22bSJeff Roberson 
2476ab3185d1SJeff Roberson 	if (zone->uz_flags & UMA_ZONE_NUMA)
2477ab3185d1SJeff Roberson 		domain = PCPU_GET(domain);
2478ab3185d1SJeff Roberson 	else
2479ab3185d1SJeff Roberson 		domain = UMA_ANYDOMAIN;
2480ab3185d1SJeff Roberson 
2481fc03d22bSJeff Roberson 	/* Short-circuit for zones without buckets and low memory. */
2482fc03d22bSJeff Roberson 	if (zone->uz_count == 0 || bucketdisable)
2483fc03d22bSJeff Roberson 		goto zalloc_item;
2484fc03d22bSJeff Roberson 
24855d1ae027SRobert Watson 	/*
24865d1ae027SRobert Watson 	 * Attempt to retrieve the item from the per-CPU cache has failed, so
24875d1ae027SRobert Watson 	 * we must go back to the zone.  This requires the zone lock, so we
24885d1ae027SRobert Watson 	 * must drop the critical section, then re-acquire it when we go back
24895d1ae027SRobert Watson 	 * to the cache.  Since the critical section is released, we may be
24905d1ae027SRobert Watson 	 * preempted or migrate.  As such, make sure not to maintain any
24915d1ae027SRobert Watson 	 * thread-local state specific to the cache from prior to releasing
24925d1ae027SRobert Watson 	 * the critical section.
24935d1ae027SRobert Watson 	 */
2494fc03d22bSJeff Roberson 	lockfail = 0;
2495fc03d22bSJeff Roberson 	if (ZONE_TRYLOCK(zone) == 0) {
2496fc03d22bSJeff Roberson 		/* Record contention to size the buckets. */
2497a553d4b8SJeff Roberson 		ZONE_LOCK(zone);
2498fc03d22bSJeff Roberson 		lockfail = 1;
2499fc03d22bSJeff Roberson 	}
25005d1ae027SRobert Watson 	critical_enter();
25015d1ae027SRobert Watson 	cpu = curcpu;
25025d1ae027SRobert Watson 	cache = &zone->uz_cpu[cpu];
25035d1ae027SRobert Watson 
2504fc03d22bSJeff Roberson 	/* See if we lost the race to fill the cache. */
2505fc03d22bSJeff Roberson 	if (cache->uc_allocbucket != NULL) {
2506fc03d22bSJeff Roberson 		ZONE_UNLOCK(zone);
2507fc03d22bSJeff Roberson 		goto zalloc_start;
2508a553d4b8SJeff Roberson 	}
25098355f576SJeff Roberson 
2510fc03d22bSJeff Roberson 	/*
2511fc03d22bSJeff Roberson 	 * Check the zone's cache of buckets.
2512fc03d22bSJeff Roberson 	 */
2513ab3185d1SJeff Roberson 	if (domain == UMA_ANYDOMAIN)
2514ab3185d1SJeff Roberson 		zdom = &zone->uz_domain[0];
2515ab3185d1SJeff Roberson 	else
2516ab3185d1SJeff Roberson 		zdom = &zone->uz_domain[domain];
2517ab3185d1SJeff Roberson 	if ((bucket = LIST_FIRST(&zdom->uzd_buckets)) != NULL) {
2518cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt != 0,
2519a553d4b8SJeff Roberson 		    ("uma_zalloc_arg: Returning an empty bucket."));
25208355f576SJeff Roberson 
2521a553d4b8SJeff Roberson 		LIST_REMOVE(bucket, ub_link);
2522a553d4b8SJeff Roberson 		cache->uc_allocbucket = bucket;
2523a553d4b8SJeff Roberson 		ZONE_UNLOCK(zone);
25248355f576SJeff Roberson 		goto zalloc_start;
2525a553d4b8SJeff Roberson 	}
25265d1ae027SRobert Watson 	/* We are no longer associated with this CPU. */
25275d1ae027SRobert Watson 	critical_exit();
2528bbee39c6SJeff Roberson 
2529fc03d22bSJeff Roberson 	/*
2530fc03d22bSJeff Roberson 	 * We bump the uz count when the cache size is insufficient to
2531fc03d22bSJeff Roberson 	 * handle the working set.
2532fc03d22bSJeff Roberson 	 */
25336fd34d6fSJeff Roberson 	if (lockfail && zone->uz_count < BUCKET_MAX)
2534a553d4b8SJeff Roberson 		zone->uz_count++;
2535fc03d22bSJeff Roberson 	ZONE_UNLOCK(zone);
2536099a0e58SBosko Milekic 
25378355f576SJeff Roberson 	/*
2538a553d4b8SJeff Roberson 	 * Now lets just fill a bucket and put it on the free list.  If that
2539763df3ecSPedro F. Giffuni 	 * works we'll restart the allocation from the beginning and it
2540fc03d22bSJeff Roberson 	 * will use the just filled bucket.
2541bbee39c6SJeff Roberson 	 */
2542ab3185d1SJeff Roberson 	bucket = zone_alloc_bucket(zone, udata, domain, flags);
25431431a748SGleb Smirnoff 	CTR3(KTR_UMA, "uma_zalloc: zone %s(%p) bucket zone returned %p",
25441431a748SGleb Smirnoff 	    zone->uz_name, zone, bucket);
2545fc03d22bSJeff Roberson 	if (bucket != NULL) {
2546fc03d22bSJeff Roberson 		ZONE_LOCK(zone);
2547fc03d22bSJeff Roberson 		critical_enter();
2548fc03d22bSJeff Roberson 		cpu = curcpu;
2549fc03d22bSJeff Roberson 		cache = &zone->uz_cpu[cpu];
2550fc03d22bSJeff Roberson 		/*
2551fc03d22bSJeff Roberson 		 * See if we lost the race or were migrated.  Cache the
2552fc03d22bSJeff Roberson 		 * initialized bucket to make this less likely or claim
2553fc03d22bSJeff Roberson 		 * the memory directly.
2554fc03d22bSJeff Roberson 		 */
2555ab3185d1SJeff Roberson 		if (cache->uc_allocbucket != NULL ||
2556ab3185d1SJeff Roberson 		    (zone->uz_flags & UMA_ZONE_NUMA &&
2557ab3185d1SJeff Roberson 		    domain != PCPU_GET(domain)))
2558ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zdom->uzd_buckets, bucket, ub_link);
2559fc03d22bSJeff Roberson 		else
2560ab3185d1SJeff Roberson 			cache->uc_allocbucket = bucket;
2561bbee39c6SJeff Roberson 		ZONE_UNLOCK(zone);
2562fc03d22bSJeff Roberson 		goto zalloc_start;
2563bbee39c6SJeff Roberson 	}
2564fc03d22bSJeff Roberson 
2565bbee39c6SJeff Roberson 	/*
2566bbee39c6SJeff Roberson 	 * We may not be able to get a bucket so return an actual item.
2567bbee39c6SJeff Roberson 	 */
2568fc03d22bSJeff Roberson zalloc_item:
2569ab3185d1SJeff Roberson 	item = zone_alloc_item(zone, udata, domain, flags);
2570fc03d22bSJeff Roberson 
2571e20a199fSJeff Roberson 	return (item);
2572bbee39c6SJeff Roberson }
2573bbee39c6SJeff Roberson 
2574ab3185d1SJeff Roberson void *
2575ab3185d1SJeff Roberson uma_zalloc_domain(uma_zone_t zone, void *udata, int domain, int flags)
2576bbee39c6SJeff Roberson {
2577ab3185d1SJeff Roberson 
2578ab3185d1SJeff Roberson 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
2579ab3185d1SJeff Roberson 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
2580ab3185d1SJeff Roberson 
2581ab3185d1SJeff Roberson 	/* This is the fast path allocation */
2582ab3185d1SJeff Roberson 	CTR5(KTR_UMA,
2583ab3185d1SJeff Roberson 	    "uma_zalloc_domain thread %x zone %s(%p) domain %d flags %d",
2584ab3185d1SJeff Roberson 	    curthread, zone->uz_name, zone, domain, flags);
2585ab3185d1SJeff Roberson 
2586ab3185d1SJeff Roberson 	if (flags & M_WAITOK) {
2587ab3185d1SJeff Roberson 		WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
2588ab3185d1SJeff Roberson 		    "uma_zalloc_domain: zone \"%s\"", zone->uz_name);
2589ab3185d1SJeff Roberson 	}
2590ab3185d1SJeff Roberson 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
2591ab3185d1SJeff Roberson 	    ("uma_zalloc_domain: called with spinlock or critical section held"));
2592ab3185d1SJeff Roberson 
2593ab3185d1SJeff Roberson 	return (zone_alloc_item(zone, udata, domain, flags));
2594ab3185d1SJeff Roberson }
2595ab3185d1SJeff Roberson 
2596ab3185d1SJeff Roberson /*
2597ab3185d1SJeff Roberson  * Find a slab with some space.  Prefer slabs that are partially used over those
2598ab3185d1SJeff Roberson  * that are totally full.  This helps to reduce fragmentation.
2599ab3185d1SJeff Roberson  *
2600ab3185d1SJeff Roberson  * If 'rr' is 1, search all domains starting from 'domain'.  Otherwise check
2601ab3185d1SJeff Roberson  * only 'domain'.
2602ab3185d1SJeff Roberson  */
2603ab3185d1SJeff Roberson static uma_slab_t
2604ab3185d1SJeff Roberson keg_first_slab(uma_keg_t keg, int domain, int rr)
2605ab3185d1SJeff Roberson {
2606ab3185d1SJeff Roberson 	uma_domain_t dom;
2607bbee39c6SJeff Roberson 	uma_slab_t slab;
2608ab3185d1SJeff Roberson 	int start;
2609ab3185d1SJeff Roberson 
2610ab3185d1SJeff Roberson 	KASSERT(domain >= 0 && domain < vm_ndomains,
2611ab3185d1SJeff Roberson 	    ("keg_first_slab: domain %d out of range", domain));
2612ab3185d1SJeff Roberson 
2613ab3185d1SJeff Roberson 	slab = NULL;
2614ab3185d1SJeff Roberson 	start = domain;
2615ab3185d1SJeff Roberson 	do {
2616ab3185d1SJeff Roberson 		dom = &keg->uk_domain[domain];
2617ab3185d1SJeff Roberson 		if (!LIST_EMPTY(&dom->ud_part_slab))
2618ab3185d1SJeff Roberson 			return (LIST_FIRST(&dom->ud_part_slab));
2619ab3185d1SJeff Roberson 		if (!LIST_EMPTY(&dom->ud_free_slab)) {
2620ab3185d1SJeff Roberson 			slab = LIST_FIRST(&dom->ud_free_slab);
2621ab3185d1SJeff Roberson 			LIST_REMOVE(slab, us_link);
2622ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link);
2623ab3185d1SJeff Roberson 			return (slab);
2624ab3185d1SJeff Roberson 		}
2625ab3185d1SJeff Roberson 		if (rr)
2626ab3185d1SJeff Roberson 			domain = (domain + 1) % vm_ndomains;
2627ab3185d1SJeff Roberson 	} while (domain != start);
2628ab3185d1SJeff Roberson 
2629ab3185d1SJeff Roberson 	return (NULL);
2630ab3185d1SJeff Roberson }
2631ab3185d1SJeff Roberson 
2632ab3185d1SJeff Roberson static uma_slab_t
2633ab3185d1SJeff Roberson keg_fetch_slab(uma_keg_t keg, uma_zone_t zone, int rdomain, int flags)
2634ab3185d1SJeff Roberson {
2635ab3185d1SJeff Roberson 	uma_domain_t dom;
2636ab3185d1SJeff Roberson 	uma_slab_t slab;
2637ab3185d1SJeff Roberson 	int allocflags, domain, reserve, rr, start;
2638099a0e58SBosko Milekic 
2639e20a199fSJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
2640bbee39c6SJeff Roberson 	slab = NULL;
26416fd34d6fSJeff Roberson 	reserve = 0;
2642ab3185d1SJeff Roberson 	allocflags = flags;
26436fd34d6fSJeff Roberson 	if ((flags & M_USE_RESERVE) == 0)
26446fd34d6fSJeff Roberson 		reserve = keg->uk_reserve;
2645bbee39c6SJeff Roberson 
2646bbee39c6SJeff Roberson 	/*
2647ab3185d1SJeff Roberson 	 * Round-robin for non first-touch zones when there is more than one
2648ab3185d1SJeff Roberson 	 * domain.
2649bbee39c6SJeff Roberson 	 */
2650ab3185d1SJeff Roberson 	if (vm_ndomains == 1)
2651ab3185d1SJeff Roberson 		rdomain = 0;
2652ab3185d1SJeff Roberson 	rr = rdomain == UMA_ANYDOMAIN;
2653ab3185d1SJeff Roberson 	if (rr) {
2654ab3185d1SJeff Roberson 		keg->uk_cursor = (keg->uk_cursor + 1) % vm_ndomains;
2655ab3185d1SJeff Roberson 		domain = start = keg->uk_cursor;
2656ab3185d1SJeff Roberson 		/* Only block on the second pass. */
2657ab3185d1SJeff Roberson 		if ((flags & (M_WAITOK | M_NOVM)) == M_WAITOK)
2658ab3185d1SJeff Roberson 			allocflags = (allocflags & ~M_WAITOK) | M_NOWAIT;
2659ab3185d1SJeff Roberson 	} else
2660ab3185d1SJeff Roberson 		domain = start = rdomain;
2661ab3185d1SJeff Roberson 
2662ab3185d1SJeff Roberson again:
2663ab3185d1SJeff Roberson 	do {
2664ab3185d1SJeff Roberson 		if (keg->uk_free > reserve &&
2665ab3185d1SJeff Roberson 		    (slab = keg_first_slab(keg, domain, rr)) != NULL) {
2666e20a199fSJeff Roberson 			MPASS(slab->us_keg == keg);
2667bbee39c6SJeff Roberson 			return (slab);
2668bbee39c6SJeff Roberson 		}
2669bbee39c6SJeff Roberson 
2670bbee39c6SJeff Roberson 		/*
2671bbee39c6SJeff Roberson 		 * M_NOVM means don't ask at all!
2672bbee39c6SJeff Roberson 		 */
2673bbee39c6SJeff Roberson 		if (flags & M_NOVM)
2674bbee39c6SJeff Roberson 			break;
2675bbee39c6SJeff Roberson 
2676e20a199fSJeff Roberson 		if (keg->uk_maxpages && keg->uk_pages >= keg->uk_maxpages) {
2677099a0e58SBosko Milekic 			keg->uk_flags |= UMA_ZFLAG_FULL;
2678e20a199fSJeff Roberson 			/*
2679e20a199fSJeff Roberson 			 * If this is not a multi-zone, set the FULL bit.
2680e20a199fSJeff Roberson 			 * Otherwise slab_multi() takes care of it.
2681e20a199fSJeff Roberson 			 */
26822f891cd5SPawel Jakub Dawidek 			if ((zone->uz_flags & UMA_ZFLAG_MULTI) == 0) {
2683e20a199fSJeff Roberson 				zone->uz_flags |= UMA_ZFLAG_FULL;
26842f891cd5SPawel Jakub Dawidek 				zone_log_warning(zone);
268554503a13SJonathan T. Looney 				zone_maxaction(zone);
26862f891cd5SPawel Jakub Dawidek 			}
2687ebc85edfSJeff Roberson 			if (flags & M_NOWAIT)
2688ab3185d1SJeff Roberson 				return (NULL);
2689c288b548SEitan Adler 			zone->uz_sleeps++;
2690e20a199fSJeff Roberson 			msleep(keg, &keg->uk_lock, PVM, "keglimit", 0);
2691bbee39c6SJeff Roberson 			continue;
2692bbee39c6SJeff Roberson 		}
2693ab3185d1SJeff Roberson 		slab = keg_alloc_slab(keg, zone, domain, allocflags);
2694bbee39c6SJeff Roberson 		/*
2695bbee39c6SJeff Roberson 		 * If we got a slab here it's safe to mark it partially used
2696bbee39c6SJeff Roberson 		 * and return.  We assume that the caller is going to remove
2697bbee39c6SJeff Roberson 		 * at least one item.
2698bbee39c6SJeff Roberson 		 */
2699bbee39c6SJeff Roberson 		if (slab) {
2700e20a199fSJeff Roberson 			MPASS(slab->us_keg == keg);
2701ab3185d1SJeff Roberson 			dom = &keg->uk_domain[slab->us_domain];
2702ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link);
2703bbee39c6SJeff Roberson 			return (slab);
2704bbee39c6SJeff Roberson 		}
2705ab3185d1SJeff Roberson 		if (rr) {
2706ab3185d1SJeff Roberson 			keg->uk_cursor = (keg->uk_cursor + 1) % vm_ndomains;
2707ab3185d1SJeff Roberson 			domain = keg->uk_cursor;
2708ab3185d1SJeff Roberson 		}
2709ab3185d1SJeff Roberson 	} while (domain != start);
2710ab3185d1SJeff Roberson 
2711ab3185d1SJeff Roberson 	/* Retry domain scan with blocking. */
2712ab3185d1SJeff Roberson 	if (allocflags != flags) {
2713ab3185d1SJeff Roberson 		allocflags = flags;
2714ab3185d1SJeff Roberson 		goto again;
2715ab3185d1SJeff Roberson 	}
2716ab3185d1SJeff Roberson 
2717bbee39c6SJeff Roberson 	/*
2718bbee39c6SJeff Roberson 	 * We might not have been able to get a slab but another cpu
2719bbee39c6SJeff Roberson 	 * could have while we were unlocked.  Check again before we
2720bbee39c6SJeff Roberson 	 * fail.
2721bbee39c6SJeff Roberson 	 */
2722ab3185d1SJeff Roberson 	if (keg->uk_free > reserve &&
2723ab3185d1SJeff Roberson 	    (slab = keg_first_slab(keg, domain, rr)) != NULL) {
2724ab3185d1SJeff Roberson 		MPASS(slab->us_keg == keg);
2725bbee39c6SJeff Roberson 		return (slab);
2726bbee39c6SJeff Roberson 	}
2727ab3185d1SJeff Roberson 	return (NULL);
2728ab3185d1SJeff Roberson }
2729bbee39c6SJeff Roberson 
2730e20a199fSJeff Roberson static uma_slab_t
2731ab3185d1SJeff Roberson zone_fetch_slab(uma_zone_t zone, uma_keg_t keg, int domain, int flags)
2732e20a199fSJeff Roberson {
2733e20a199fSJeff Roberson 	uma_slab_t slab;
2734e20a199fSJeff Roberson 
2735af526374SJeff Roberson 	if (keg == NULL) {
2736e20a199fSJeff Roberson 		keg = zone_first_keg(zone);
2737af526374SJeff Roberson 		KEG_LOCK(keg);
2738af526374SJeff Roberson 	}
2739e20a199fSJeff Roberson 
2740e20a199fSJeff Roberson 	for (;;) {
2741ab3185d1SJeff Roberson 		slab = keg_fetch_slab(keg, zone, domain, flags);
2742e20a199fSJeff Roberson 		if (slab)
2743e20a199fSJeff Roberson 			return (slab);
2744e20a199fSJeff Roberson 		if (flags & (M_NOWAIT | M_NOVM))
2745e20a199fSJeff Roberson 			break;
2746e20a199fSJeff Roberson 	}
2747af526374SJeff Roberson 	KEG_UNLOCK(keg);
2748e20a199fSJeff Roberson 	return (NULL);
2749e20a199fSJeff Roberson }
2750e20a199fSJeff Roberson 
2751e20a199fSJeff Roberson /*
2752e20a199fSJeff Roberson  * uma_zone_fetch_slab_multi:  Fetches a slab from one available keg.  Returns
2753af526374SJeff Roberson  * with the keg locked.  On NULL no lock is held.
2754e20a199fSJeff Roberson  *
2755e20a199fSJeff Roberson  * The last pointer is used to seed the search.  It is not required.
2756e20a199fSJeff Roberson  */
2757e20a199fSJeff Roberson static uma_slab_t
2758ab3185d1SJeff Roberson zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int domain, int rflags)
2759e20a199fSJeff Roberson {
2760e20a199fSJeff Roberson 	uma_klink_t klink;
2761e20a199fSJeff Roberson 	uma_slab_t slab;
2762e20a199fSJeff Roberson 	uma_keg_t keg;
2763e20a199fSJeff Roberson 	int flags;
2764e20a199fSJeff Roberson 	int empty;
2765e20a199fSJeff Roberson 	int full;
2766e20a199fSJeff Roberson 
2767e20a199fSJeff Roberson 	/*
2768e20a199fSJeff Roberson 	 * Don't wait on the first pass.  This will skip limit tests
2769e20a199fSJeff Roberson 	 * as well.  We don't want to block if we can find a provider
2770e20a199fSJeff Roberson 	 * without blocking.
2771e20a199fSJeff Roberson 	 */
2772e20a199fSJeff Roberson 	flags = (rflags & ~M_WAITOK) | M_NOWAIT;
2773e20a199fSJeff Roberson 	/*
2774e20a199fSJeff Roberson 	 * Use the last slab allocated as a hint for where to start
2775e20a199fSJeff Roberson 	 * the search.
2776e20a199fSJeff Roberson 	 */
2777af526374SJeff Roberson 	if (last != NULL) {
2778ab3185d1SJeff Roberson 		slab = keg_fetch_slab(last, zone, domain, flags);
2779e20a199fSJeff Roberson 		if (slab)
2780e20a199fSJeff Roberson 			return (slab);
2781af526374SJeff Roberson 		KEG_UNLOCK(last);
2782e20a199fSJeff Roberson 	}
2783e20a199fSJeff Roberson 	/*
2784e20a199fSJeff Roberson 	 * Loop until we have a slab incase of transient failures
2785e20a199fSJeff Roberson 	 * while M_WAITOK is specified.  I'm not sure this is 100%
2786e20a199fSJeff Roberson 	 * required but we've done it for so long now.
2787e20a199fSJeff Roberson 	 */
2788e20a199fSJeff Roberson 	for (;;) {
2789e20a199fSJeff Roberson 		empty = 0;
2790e20a199fSJeff Roberson 		full = 0;
2791e20a199fSJeff Roberson 		/*
2792e20a199fSJeff Roberson 		 * Search the available kegs for slabs.  Be careful to hold the
2793e20a199fSJeff Roberson 		 * correct lock while calling into the keg layer.
2794e20a199fSJeff Roberson 		 */
2795e20a199fSJeff Roberson 		LIST_FOREACH(klink, &zone->uz_kegs, kl_link) {
2796e20a199fSJeff Roberson 			keg = klink->kl_keg;
2797af526374SJeff Roberson 			KEG_LOCK(keg);
2798e20a199fSJeff Roberson 			if ((keg->uk_flags & UMA_ZFLAG_FULL) == 0) {
2799ab3185d1SJeff Roberson 				slab = keg_fetch_slab(keg, zone, domain, flags);
2800e20a199fSJeff Roberson 				if (slab)
2801e20a199fSJeff Roberson 					return (slab);
2802e20a199fSJeff Roberson 			}
2803e20a199fSJeff Roberson 			if (keg->uk_flags & UMA_ZFLAG_FULL)
2804e20a199fSJeff Roberson 				full++;
2805e20a199fSJeff Roberson 			else
2806e20a199fSJeff Roberson 				empty++;
2807af526374SJeff Roberson 			KEG_UNLOCK(keg);
2808e20a199fSJeff Roberson 		}
2809e20a199fSJeff Roberson 		if (rflags & (M_NOWAIT | M_NOVM))
2810e20a199fSJeff Roberson 			break;
2811e20a199fSJeff Roberson 		flags = rflags;
2812e20a199fSJeff Roberson 		/*
2813e20a199fSJeff Roberson 		 * All kegs are full.  XXX We can't atomically check all kegs
2814e20a199fSJeff Roberson 		 * and sleep so just sleep for a short period and retry.
2815e20a199fSJeff Roberson 		 */
2816e20a199fSJeff Roberson 		if (full && !empty) {
2817af526374SJeff Roberson 			ZONE_LOCK(zone);
2818e20a199fSJeff Roberson 			zone->uz_flags |= UMA_ZFLAG_FULL;
2819bf965959SSean Bruno 			zone->uz_sleeps++;
28202f891cd5SPawel Jakub Dawidek 			zone_log_warning(zone);
282154503a13SJonathan T. Looney 			zone_maxaction(zone);
2822af526374SJeff Roberson 			msleep(zone, zone->uz_lockptr, PVM,
2823af526374SJeff Roberson 			    "zonelimit", hz/100);
2824e20a199fSJeff Roberson 			zone->uz_flags &= ~UMA_ZFLAG_FULL;
2825af526374SJeff Roberson 			ZONE_UNLOCK(zone);
2826e20a199fSJeff Roberson 			continue;
2827e20a199fSJeff Roberson 		}
2828e20a199fSJeff Roberson 	}
2829e20a199fSJeff Roberson 	return (NULL);
2830e20a199fSJeff Roberson }
2831e20a199fSJeff Roberson 
2832d56368d7SBosko Milekic static void *
28330095a784SJeff Roberson slab_alloc_item(uma_keg_t keg, uma_slab_t slab)
2834bbee39c6SJeff Roberson {
2835ab3185d1SJeff Roberson 	uma_domain_t dom;
2836bbee39c6SJeff Roberson 	void *item;
283785dcf349SGleb Smirnoff 	uint8_t freei;
2838bbee39c6SJeff Roberson 
28390095a784SJeff Roberson 	MPASS(keg == slab->us_keg);
2840e20a199fSJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
2841099a0e58SBosko Milekic 
2842ef72505eSJeff Roberson 	freei = BIT_FFS(SLAB_SETSIZE, &slab->us_free) - 1;
2843ef72505eSJeff Roberson 	BIT_CLR(SLAB_SETSIZE, freei, &slab->us_free);
2844099a0e58SBosko Milekic 	item = slab->us_data + (keg->uk_rsize * freei);
2845bbee39c6SJeff Roberson 	slab->us_freecount--;
2846099a0e58SBosko Milekic 	keg->uk_free--;
2847ef72505eSJeff Roberson 
2848bbee39c6SJeff Roberson 	/* Move this slab to the full list */
2849bbee39c6SJeff Roberson 	if (slab->us_freecount == 0) {
2850bbee39c6SJeff Roberson 		LIST_REMOVE(slab, us_link);
2851ab3185d1SJeff Roberson 		dom = &keg->uk_domain[slab->us_domain];
2852ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_full_slab, slab, us_link);
2853bbee39c6SJeff Roberson 	}
2854bbee39c6SJeff Roberson 
2855bbee39c6SJeff Roberson 	return (item);
2856bbee39c6SJeff Roberson }
2857bbee39c6SJeff Roberson 
2858bbee39c6SJeff Roberson static int
2859ab3185d1SJeff Roberson zone_import(uma_zone_t zone, void **bucket, int max, int domain, int flags)
28600095a784SJeff Roberson {
28610095a784SJeff Roberson 	uma_slab_t slab;
28620095a784SJeff Roberson 	uma_keg_t keg;
2863*a03af342SSean Bruno #ifdef NUMA
2864ab3185d1SJeff Roberson 	int stripe;
2865*a03af342SSean Bruno #endif
28660095a784SJeff Roberson 	int i;
28670095a784SJeff Roberson 
28680095a784SJeff Roberson 	slab = NULL;
28690095a784SJeff Roberson 	keg = NULL;
2870af526374SJeff Roberson 	/* Try to keep the buckets totally full */
28710095a784SJeff Roberson 	for (i = 0; i < max; ) {
2872ab3185d1SJeff Roberson 		if ((slab = zone->uz_slab(zone, keg, domain, flags)) == NULL)
28730095a784SJeff Roberson 			break;
28740095a784SJeff Roberson 		keg = slab->us_keg;
2875*a03af342SSean Bruno #ifdef NUMA
2876ab3185d1SJeff Roberson 		stripe = howmany(max, vm_ndomains);
2877*a03af342SSean Bruno #endif
28786fd34d6fSJeff Roberson 		while (slab->us_freecount && i < max) {
28790095a784SJeff Roberson 			bucket[i++] = slab_alloc_item(keg, slab);
28806fd34d6fSJeff Roberson 			if (keg->uk_free <= keg->uk_reserve)
28816fd34d6fSJeff Roberson 				break;
2882b6715dabSJeff Roberson #ifdef NUMA
2883ab3185d1SJeff Roberson 			/*
2884ab3185d1SJeff Roberson 			 * If the zone is striped we pick a new slab for every
2885ab3185d1SJeff Roberson 			 * N allocations.  Eliminating this conditional will
2886ab3185d1SJeff Roberson 			 * instead pick a new domain for each bucket rather
2887ab3185d1SJeff Roberson 			 * than stripe within each bucket.  The current option
2888ab3185d1SJeff Roberson 			 * produces more fragmentation and requires more cpu
2889ab3185d1SJeff Roberson 			 * time but yields better distribution.
2890ab3185d1SJeff Roberson 			 */
2891ab3185d1SJeff Roberson 			if ((zone->uz_flags & UMA_ZONE_NUMA) == 0 &&
2892ab3185d1SJeff Roberson 			    vm_ndomains > 1 && --stripe == 0)
2893ab3185d1SJeff Roberson 				break;
2894ab3185d1SJeff Roberson #endif
28956fd34d6fSJeff Roberson 		}
2896ab3185d1SJeff Roberson 		/* Don't block if we allocated any successfully. */
28970095a784SJeff Roberson 		flags &= ~M_WAITOK;
28980095a784SJeff Roberson 		flags |= M_NOWAIT;
28990095a784SJeff Roberson 	}
29000095a784SJeff Roberson 	if (slab != NULL)
29010095a784SJeff Roberson 		KEG_UNLOCK(keg);
29020095a784SJeff Roberson 
29030095a784SJeff Roberson 	return i;
29040095a784SJeff Roberson }
29050095a784SJeff Roberson 
2906fc03d22bSJeff Roberson static uma_bucket_t
2907ab3185d1SJeff Roberson zone_alloc_bucket(uma_zone_t zone, void *udata, int domain, int flags)
2908bbee39c6SJeff Roberson {
2909bbee39c6SJeff Roberson 	uma_bucket_t bucket;
29100095a784SJeff Roberson 	int max;
2911bbee39c6SJeff Roberson 
29126fd34d6fSJeff Roberson 	/* Don't wait for buckets, preserve caller's NOVM setting. */
29136fd34d6fSJeff Roberson 	bucket = bucket_alloc(zone, udata, M_NOWAIT | (flags & M_NOVM));
29140095a784SJeff Roberson 	if (bucket == NULL)
2915f7104ccdSAlexander Motin 		return (NULL);
29160095a784SJeff Roberson 
2917af526374SJeff Roberson 	max = MIN(bucket->ub_entries, zone->uz_count);
29180095a784SJeff Roberson 	bucket->ub_cnt = zone->uz_import(zone->uz_arg, bucket->ub_bucket,
2919ab3185d1SJeff Roberson 	    max, domain, flags);
29200095a784SJeff Roberson 
29210095a784SJeff Roberson 	/*
29220095a784SJeff Roberson 	 * Initialize the memory if necessary.
29230095a784SJeff Roberson 	 */
29240095a784SJeff Roberson 	if (bucket->ub_cnt != 0 && zone->uz_init != NULL) {
2925099a0e58SBosko Milekic 		int i;
2926bbee39c6SJeff Roberson 
29270095a784SJeff Roberson 		for (i = 0; i < bucket->ub_cnt; i++)
2928e20a199fSJeff Roberson 			if (zone->uz_init(bucket->ub_bucket[i], zone->uz_size,
29290095a784SJeff Roberson 			    flags) != 0)
2930b23f72e9SBrian Feldman 				break;
2931b23f72e9SBrian Feldman 		/*
2932b23f72e9SBrian Feldman 		 * If we couldn't initialize the whole bucket, put the
2933b23f72e9SBrian Feldman 		 * rest back onto the freelist.
2934b23f72e9SBrian Feldman 		 */
2935b23f72e9SBrian Feldman 		if (i != bucket->ub_cnt) {
2936af526374SJeff Roberson 			zone->uz_release(zone->uz_arg, &bucket->ub_bucket[i],
29370095a784SJeff Roberson 			    bucket->ub_cnt - i);
2938a5a262c6SBosko Milekic #ifdef INVARIANTS
29390095a784SJeff Roberson 			bzero(&bucket->ub_bucket[i],
29400095a784SJeff Roberson 			    sizeof(void *) * (bucket->ub_cnt - i));
2941a5a262c6SBosko Milekic #endif
2942b23f72e9SBrian Feldman 			bucket->ub_cnt = i;
2943b23f72e9SBrian Feldman 		}
2944099a0e58SBosko Milekic 	}
2945099a0e58SBosko Milekic 
2946f7104ccdSAlexander Motin 	if (bucket->ub_cnt == 0) {
29476fd34d6fSJeff Roberson 		bucket_free(zone, bucket, udata);
2948fc03d22bSJeff Roberson 		atomic_add_long(&zone->uz_fails, 1);
2949fc03d22bSJeff Roberson 		return (NULL);
2950bbee39c6SJeff Roberson 	}
2951fc03d22bSJeff Roberson 
2952fc03d22bSJeff Roberson 	return (bucket);
2953fc03d22bSJeff Roberson }
2954fc03d22bSJeff Roberson 
29558355f576SJeff Roberson /*
29560095a784SJeff Roberson  * Allocates a single item from a zone.
29578355f576SJeff Roberson  *
29588355f576SJeff Roberson  * Arguments
29598355f576SJeff Roberson  *	zone   The zone to alloc for.
29608355f576SJeff Roberson  *	udata  The data to be passed to the constructor.
2961ab3185d1SJeff Roberson  *	domain The domain to allocate from or UMA_ANYDOMAIN.
2962a163d034SWarner Losh  *	flags  M_WAITOK, M_NOWAIT, M_ZERO.
29638355f576SJeff Roberson  *
29648355f576SJeff Roberson  * Returns
29658355f576SJeff Roberson  *	NULL if there is no memory and M_NOWAIT is set
2966bbee39c6SJeff Roberson  *	An item if successful
29678355f576SJeff Roberson  */
29688355f576SJeff Roberson 
29698355f576SJeff Roberson static void *
2970ab3185d1SJeff Roberson zone_alloc_item(uma_zone_t zone, void *udata, int domain, int flags)
29718355f576SJeff Roberson {
29728355f576SJeff Roberson 	void *item;
2973c5deaf04SGleb Smirnoff #ifdef INVARIANTS
2974c5deaf04SGleb Smirnoff 	bool skipdbg;
2975c5deaf04SGleb Smirnoff #endif
29768355f576SJeff Roberson 
29778355f576SJeff Roberson 	item = NULL;
29788355f576SJeff Roberson 
2979ab3185d1SJeff Roberson 	if (zone->uz_import(zone->uz_arg, &item, 1, domain, flags) != 1)
29800095a784SJeff Roberson 		goto fail;
29810095a784SJeff Roberson 	atomic_add_long(&zone->uz_allocs, 1);
29828355f576SJeff Roberson 
2983c5deaf04SGleb Smirnoff #ifdef INVARIANTS
2984c5deaf04SGleb Smirnoff 	skipdbg = uma_dbg_zskip(zone, item);
2985c5deaf04SGleb Smirnoff #endif
2986099a0e58SBosko Milekic 	/*
2987099a0e58SBosko Milekic 	 * We have to call both the zone's init (not the keg's init)
2988099a0e58SBosko Milekic 	 * and the zone's ctor.  This is because the item is going from
2989099a0e58SBosko Milekic 	 * a keg slab directly to the user, and the user is expecting it
2990099a0e58SBosko Milekic 	 * to be both zone-init'd as well as zone-ctor'd.
2991099a0e58SBosko Milekic 	 */
2992b23f72e9SBrian Feldman 	if (zone->uz_init != NULL) {
2993e20a199fSJeff Roberson 		if (zone->uz_init(item, zone->uz_size, flags) != 0) {
29940095a784SJeff Roberson 			zone_free_item(zone, item, udata, SKIP_FINI);
29950095a784SJeff Roberson 			goto fail;
2996b23f72e9SBrian Feldman 		}
2997b23f72e9SBrian Feldman 	}
2998c5deaf04SGleb Smirnoff 	if (zone->uz_ctor != NULL &&
2999c5deaf04SGleb Smirnoff #ifdef INVARIANTS
3000c5deaf04SGleb Smirnoff 	    (!skipdbg || zone->uz_ctor != trash_ctor ||
3001c5deaf04SGleb Smirnoff 	    zone->uz_dtor != trash_dtor) &&
3002c5deaf04SGleb Smirnoff #endif
3003c5deaf04SGleb Smirnoff 	    zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) {
30040095a784SJeff Roberson 		zone_free_item(zone, item, udata, SKIP_DTOR);
30050095a784SJeff Roberson 		goto fail;
3006b23f72e9SBrian Feldman 	}
3007ef72505eSJeff Roberson #ifdef INVARIANTS
3008c5deaf04SGleb Smirnoff 	if (!skipdbg)
30090095a784SJeff Roberson 		uma_dbg_alloc(zone, NULL, item);
3010ef72505eSJeff Roberson #endif
30112cc35ff9SJeff Roberson 	if (flags & M_ZERO)
301248343a2fSGleb Smirnoff 		uma_zero_item(item, zone);
30138355f576SJeff Roberson 
30141431a748SGleb Smirnoff 	CTR3(KTR_UMA, "zone_alloc_item item %p from %s(%p)", item,
30151431a748SGleb Smirnoff 	    zone->uz_name, zone);
30161431a748SGleb Smirnoff 
30178355f576SJeff Roberson 	return (item);
30180095a784SJeff Roberson 
30190095a784SJeff Roberson fail:
30201431a748SGleb Smirnoff 	CTR2(KTR_UMA, "zone_alloc_item failed from %s(%p)",
30211431a748SGleb Smirnoff 	    zone->uz_name, zone);
30220095a784SJeff Roberson 	atomic_add_long(&zone->uz_fails, 1);
30230095a784SJeff Roberson 	return (NULL);
30248355f576SJeff Roberson }
30258355f576SJeff Roberson 
30268355f576SJeff Roberson /* See uma.h */
30278355f576SJeff Roberson void
30288355f576SJeff Roberson uma_zfree_arg(uma_zone_t zone, void *item, void *udata)
30298355f576SJeff Roberson {
30308355f576SJeff Roberson 	uma_cache_t cache;
30318355f576SJeff Roberson 	uma_bucket_t bucket;
3032ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
3033ab3185d1SJeff Roberson 	int cpu, domain, lockfail;
3034c5deaf04SGleb Smirnoff #ifdef INVARIANTS
3035c5deaf04SGleb Smirnoff 	bool skipdbg;
3036c5deaf04SGleb Smirnoff #endif
30378355f576SJeff Roberson 
3038e866d8f0SMark Murray 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
3039e866d8f0SMark Murray 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
304010cb2424SMark Murray 
30413659f747SRobert Watson 	CTR2(KTR_UMA, "uma_zfree_arg thread %x zone %s", curthread,
30423659f747SRobert Watson 	    zone->uz_name);
30433659f747SRobert Watson 
3044d9e2e68dSMark Johnston 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
30451067a2baSJonathan T. Looney 	    ("uma_zfree_arg: called with spinlock or critical section held"));
30461067a2baSJonathan T. Looney 
304720ed0cb0SMatthew D Fleming         /* uma_zfree(..., NULL) does nothing, to match free(9). */
304820ed0cb0SMatthew D Fleming         if (item == NULL)
304920ed0cb0SMatthew D Fleming                 return;
30508d689e04SGleb Smirnoff #ifdef DEBUG_MEMGUARD
30518d689e04SGleb Smirnoff 	if (is_memguard_addr(item)) {
3052bc9d08e1SMark Johnston 		if (zone->uz_dtor != NULL)
30538d689e04SGleb Smirnoff 			zone->uz_dtor(item, zone->uz_size, udata);
3054bc9d08e1SMark Johnston 		if (zone->uz_fini != NULL)
30558d689e04SGleb Smirnoff 			zone->uz_fini(item, zone->uz_size);
30568d689e04SGleb Smirnoff 		memguard_free(item);
30578d689e04SGleb Smirnoff 		return;
30588d689e04SGleb Smirnoff 	}
30598d689e04SGleb Smirnoff #endif
30605d1ae027SRobert Watson #ifdef INVARIANTS
3061c5deaf04SGleb Smirnoff 	skipdbg = uma_dbg_zskip(zone, item);
3062c5deaf04SGleb Smirnoff 	if (skipdbg == false) {
3063e20a199fSJeff Roberson 		if (zone->uz_flags & UMA_ZONE_MALLOC)
30645d1ae027SRobert Watson 			uma_dbg_free(zone, udata, item);
30655d1ae027SRobert Watson 		else
30665d1ae027SRobert Watson 			uma_dbg_free(zone, NULL, item);
3067c5deaf04SGleb Smirnoff 	}
3068c5deaf04SGleb Smirnoff 	if (zone->uz_dtor != NULL && (!skipdbg ||
3069c5deaf04SGleb Smirnoff 	    zone->uz_dtor != trash_dtor || zone->uz_ctor != trash_ctor))
3070c5deaf04SGleb Smirnoff #else
3071fc03d22bSJeff Roberson 	if (zone->uz_dtor != NULL)
3072c5deaf04SGleb Smirnoff #endif
3073ef72505eSJeff Roberson 		zone->uz_dtor(item, zone->uz_size, udata);
3074ef72505eSJeff Roberson 
3075af7f9b97SJeff Roberson 	/*
3076af7f9b97SJeff Roberson 	 * The race here is acceptable.  If we miss it we'll just have to wait
3077af7f9b97SJeff Roberson 	 * a little longer for the limits to be reset.
3078af7f9b97SJeff Roberson 	 */
3079e20a199fSJeff Roberson 	if (zone->uz_flags & UMA_ZFLAG_FULL)
3080fc03d22bSJeff Roberson 		goto zfree_item;
3081af7f9b97SJeff Roberson 
30825d1ae027SRobert Watson 	/*
30835d1ae027SRobert Watson 	 * If possible, free to the per-CPU cache.  There are two
30845d1ae027SRobert Watson 	 * requirements for safe access to the per-CPU cache: (1) the thread
30855d1ae027SRobert Watson 	 * accessing the cache must not be preempted or yield during access,
30865d1ae027SRobert Watson 	 * and (2) the thread must not migrate CPUs without switching which
30875d1ae027SRobert Watson 	 * cache it accesses.  We rely on a critical section to prevent
30885d1ae027SRobert Watson 	 * preemption and migration.  We release the critical section in
30895d1ae027SRobert Watson 	 * order to acquire the zone mutex if we are unable to free to the
30905d1ae027SRobert Watson 	 * current cache; when we re-acquire the critical section, we must
30915d1ae027SRobert Watson 	 * detect and handle migration if it has occurred.
30925d1ae027SRobert Watson 	 */
3093a553d4b8SJeff Roberson zfree_restart:
30945d1ae027SRobert Watson 	critical_enter();
30955d1ae027SRobert Watson 	cpu = curcpu;
30968355f576SJeff Roberson 	cache = &zone->uz_cpu[cpu];
30978355f576SJeff Roberson 
30988355f576SJeff Roberson zfree_start:
3099a553d4b8SJeff Roberson 	/*
3100fc03d22bSJeff Roberson 	 * Try to free into the allocbucket first to give LIFO ordering
3101fc03d22bSJeff Roberson 	 * for cache-hot datastructures.  Spill over into the freebucket
3102fc03d22bSJeff Roberson 	 * if necessary.  Alloc will swap them if one runs dry.
3103a553d4b8SJeff Roberson 	 */
3104fc03d22bSJeff Roberson 	bucket = cache->uc_allocbucket;
3105fc03d22bSJeff Roberson 	if (bucket == NULL || bucket->ub_cnt >= bucket->ub_entries)
3106fc03d22bSJeff Roberson 		bucket = cache->uc_freebucket;
3107fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt < bucket->ub_entries) {
3108cae33c14SJeff Roberson 		KASSERT(bucket->ub_bucket[bucket->ub_cnt] == NULL,
31098355f576SJeff Roberson 		    ("uma_zfree: Freeing to non free bucket index."));
3110cae33c14SJeff Roberson 		bucket->ub_bucket[bucket->ub_cnt] = item;
3111cae33c14SJeff Roberson 		bucket->ub_cnt++;
3112773df9abSRobert Watson 		cache->uc_frees++;
31135d1ae027SRobert Watson 		critical_exit();
31148355f576SJeff Roberson 		return;
3115fc03d22bSJeff Roberson 	}
3116fc03d22bSJeff Roberson 
31178355f576SJeff Roberson 	/*
31185d1ae027SRobert Watson 	 * We must go back the zone, which requires acquiring the zone lock,
31195d1ae027SRobert Watson 	 * which in turn means we must release and re-acquire the critical
31205d1ae027SRobert Watson 	 * section.  Since the critical section is released, we may be
31215d1ae027SRobert Watson 	 * preempted or migrate.  As such, make sure not to maintain any
31225d1ae027SRobert Watson 	 * thread-local state specific to the cache from prior to releasing
31235d1ae027SRobert Watson 	 * the critical section.
31248355f576SJeff Roberson 	 */
31255d1ae027SRobert Watson 	critical_exit();
3126fc03d22bSJeff Roberson 	if (zone->uz_count == 0 || bucketdisable)
3127fc03d22bSJeff Roberson 		goto zfree_item;
3128fc03d22bSJeff Roberson 
31294d104ba0SAlexander Motin 	lockfail = 0;
31304d104ba0SAlexander Motin 	if (ZONE_TRYLOCK(zone) == 0) {
31314d104ba0SAlexander Motin 		/* Record contention to size the buckets. */
31328355f576SJeff Roberson 		ZONE_LOCK(zone);
31334d104ba0SAlexander Motin 		lockfail = 1;
31344d104ba0SAlexander Motin 	}
31355d1ae027SRobert Watson 	critical_enter();
31365d1ae027SRobert Watson 	cpu = curcpu;
31375d1ae027SRobert Watson 	cache = &zone->uz_cpu[cpu];
31388355f576SJeff Roberson 
3139fc03d22bSJeff Roberson 	/*
3140fc03d22bSJeff Roberson 	 * Since we have locked the zone we may as well send back our stats.
3141fc03d22bSJeff Roberson 	 */
31420095a784SJeff Roberson 	atomic_add_long(&zone->uz_allocs, cache->uc_allocs);
31430095a784SJeff Roberson 	atomic_add_long(&zone->uz_frees, cache->uc_frees);
3144f4ff923bSRobert Watson 	cache->uc_allocs = 0;
3145f4ff923bSRobert Watson 	cache->uc_frees = 0;
3146f4ff923bSRobert Watson 
31478355f576SJeff Roberson 	bucket = cache->uc_freebucket;
3148fc03d22bSJeff Roberson 	if (bucket != NULL && bucket->ub_cnt < bucket->ub_entries) {
3149fc03d22bSJeff Roberson 		ZONE_UNLOCK(zone);
3150fc03d22bSJeff Roberson 		goto zfree_start;
3151fc03d22bSJeff Roberson 	}
31528355f576SJeff Roberson 	cache->uc_freebucket = NULL;
3153afa5d703SMark Johnston 	/* We are no longer associated with this CPU. */
3154afa5d703SMark Johnston 	critical_exit();
31558355f576SJeff Roberson 
3156ab3185d1SJeff Roberson 	if ((zone->uz_flags & UMA_ZONE_NUMA) != 0)
3157ab3185d1SJeff Roberson 		domain = PCPU_GET(domain);
3158ab3185d1SJeff Roberson 	else
3159ab3185d1SJeff Roberson 		domain = 0;
3160ab3185d1SJeff Roberson 	zdom = &zone->uz_domain[0];
3161ab3185d1SJeff Roberson 
31628355f576SJeff Roberson 	/* Can we throw this on the zone full list? */
31638355f576SJeff Roberson 	if (bucket != NULL) {
31641431a748SGleb Smirnoff 		CTR3(KTR_UMA,
31651431a748SGleb Smirnoff 		    "uma_zfree: zone %s(%p) putting bucket %p on free list",
31661431a748SGleb Smirnoff 		    zone->uz_name, zone, bucket);
3167cae33c14SJeff Roberson 		/* ub_cnt is pointing to the last free item */
3168cae33c14SJeff Roberson 		KASSERT(bucket->ub_cnt != 0,
31698355f576SJeff Roberson 		    ("uma_zfree: Attempting to insert an empty bucket onto the full list.\n"));
3170e8bb2dc7SJeff Roberson 		if ((zone->uz_flags & UMA_ZONE_NOBUCKETCACHE) != 0) {
3171e8bb2dc7SJeff Roberson 			ZONE_UNLOCK(zone);
3172e8bb2dc7SJeff Roberson 			bucket_drain(zone, bucket);
3173e8bb2dc7SJeff Roberson 			bucket_free(zone, bucket, udata);
3174e8bb2dc7SJeff Roberson 			goto zfree_restart;
3175e8bb2dc7SJeff Roberson 		} else
3176ab3185d1SJeff Roberson 			LIST_INSERT_HEAD(&zdom->uzd_buckets, bucket, ub_link);
31778355f576SJeff Roberson 	}
3178fc03d22bSJeff Roberson 
31794d104ba0SAlexander Motin 	/*
31804d104ba0SAlexander Motin 	 * We bump the uz count when the cache size is insufficient to
31814d104ba0SAlexander Motin 	 * handle the working set.
31824d104ba0SAlexander Motin 	 */
31834d104ba0SAlexander Motin 	if (lockfail && zone->uz_count < BUCKET_MAX)
31844d104ba0SAlexander Motin 		zone->uz_count++;
3185a553d4b8SJeff Roberson 	ZONE_UNLOCK(zone);
3186a553d4b8SJeff Roberson 
31876fd34d6fSJeff Roberson 	bucket = bucket_alloc(zone, udata, M_NOWAIT);
31881431a748SGleb Smirnoff 	CTR3(KTR_UMA, "uma_zfree: zone %s(%p) allocated bucket %p",
31891431a748SGleb Smirnoff 	    zone->uz_name, zone, bucket);
31904741dcbfSJeff Roberson 	if (bucket) {
3191fc03d22bSJeff Roberson 		critical_enter();
3192fc03d22bSJeff Roberson 		cpu = curcpu;
3193fc03d22bSJeff Roberson 		cache = &zone->uz_cpu[cpu];
3194ab3185d1SJeff Roberson 		if (cache->uc_freebucket == NULL &&
3195ab3185d1SJeff Roberson 		    ((zone->uz_flags & UMA_ZONE_NUMA) == 0 ||
3196ab3185d1SJeff Roberson 		    domain == PCPU_GET(domain))) {
3197fc03d22bSJeff Roberson 			cache->uc_freebucket = bucket;
3198fc03d22bSJeff Roberson 			goto zfree_start;
3199fc03d22bSJeff Roberson 		}
3200fc03d22bSJeff Roberson 		/*
3201fc03d22bSJeff Roberson 		 * We lost the race, start over.  We have to drop our
3202fc03d22bSJeff Roberson 		 * critical section to free the bucket.
3203fc03d22bSJeff Roberson 		 */
3204fc03d22bSJeff Roberson 		critical_exit();
32056fd34d6fSJeff Roberson 		bucket_free(zone, bucket, udata);
3206a553d4b8SJeff Roberson 		goto zfree_restart;
32078355f576SJeff Roberson 	}
32088355f576SJeff Roberson 
3209a553d4b8SJeff Roberson 	/*
3210a553d4b8SJeff Roberson 	 * If nothing else caught this, we'll just do an internal free.
3211a553d4b8SJeff Roberson 	 */
3212fc03d22bSJeff Roberson zfree_item:
32130095a784SJeff Roberson 	zone_free_item(zone, item, udata, SKIP_DTOR);
32148355f576SJeff Roberson 
32158355f576SJeff Roberson 	return;
32168355f576SJeff Roberson }
32178355f576SJeff Roberson 
3218ab3185d1SJeff Roberson void
3219ab3185d1SJeff Roberson uma_zfree_domain(uma_zone_t zone, void *item, void *udata)
3220ab3185d1SJeff Roberson {
3221ab3185d1SJeff Roberson 
3222ab3185d1SJeff Roberson 	/* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */
3223ab3185d1SJeff Roberson 	random_harvest_fast_uma(&zone, sizeof(zone), 1, RANDOM_UMA);
3224ab3185d1SJeff Roberson 
3225ab3185d1SJeff Roberson 	CTR2(KTR_UMA, "uma_zfree_domain thread %x zone %s", curthread,
3226ab3185d1SJeff Roberson 	    zone->uz_name);
3227ab3185d1SJeff Roberson 
3228ab3185d1SJeff Roberson 	KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(),
3229ab3185d1SJeff Roberson 	    ("uma_zfree_domain: called with spinlock or critical section held"));
3230ab3185d1SJeff Roberson 
3231ab3185d1SJeff Roberson         /* uma_zfree(..., NULL) does nothing, to match free(9). */
3232ab3185d1SJeff Roberson         if (item == NULL)
3233ab3185d1SJeff Roberson                 return;
3234ab3185d1SJeff Roberson 	zone_free_item(zone, item, udata, SKIP_NONE);
3235ab3185d1SJeff Roberson }
3236ab3185d1SJeff Roberson 
32378355f576SJeff Roberson static void
32380095a784SJeff Roberson slab_free_item(uma_keg_t keg, uma_slab_t slab, void *item)
32398355f576SJeff Roberson {
3240ab3185d1SJeff Roberson 	uma_domain_t dom;
324185dcf349SGleb Smirnoff 	uint8_t freei;
3242099a0e58SBosko Milekic 
32430095a784SJeff Roberson 	mtx_assert(&keg->uk_lock, MA_OWNED);
3244e20a199fSJeff Roberson 	MPASS(keg == slab->us_keg);
32458355f576SJeff Roberson 
3246ab3185d1SJeff Roberson 	dom = &keg->uk_domain[slab->us_domain];
3247ab3185d1SJeff Roberson 
32488355f576SJeff Roberson 	/* Do we need to remove from any lists? */
3249099a0e58SBosko Milekic 	if (slab->us_freecount+1 == keg->uk_ipers) {
32508355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
3251ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_free_slab, slab, us_link);
32528355f576SJeff Roberson 	} else if (slab->us_freecount == 0) {
32538355f576SJeff Roberson 		LIST_REMOVE(slab, us_link);
3254ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link);
32558355f576SJeff Roberson 	}
32568355f576SJeff Roberson 
3257ef72505eSJeff Roberson 	/* Slab management. */
3258ef72505eSJeff Roberson 	freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize;
3259ef72505eSJeff Roberson 	BIT_SET(SLAB_SETSIZE, freei, &slab->us_free);
32608355f576SJeff Roberson 	slab->us_freecount++;
32618355f576SJeff Roberson 
3262ef72505eSJeff Roberson 	/* Keg statistics. */
3263099a0e58SBosko Milekic 	keg->uk_free++;
32640095a784SJeff Roberson }
32650095a784SJeff Roberson 
32660095a784SJeff Roberson static void
32670095a784SJeff Roberson zone_release(uma_zone_t zone, void **bucket, int cnt)
32680095a784SJeff Roberson {
32690095a784SJeff Roberson 	void *item;
32700095a784SJeff Roberson 	uma_slab_t slab;
32710095a784SJeff Roberson 	uma_keg_t keg;
32720095a784SJeff Roberson 	uint8_t *mem;
32730095a784SJeff Roberson 	int clearfull;
32740095a784SJeff Roberson 	int i;
32758355f576SJeff Roberson 
3276e20a199fSJeff Roberson 	clearfull = 0;
32770095a784SJeff Roberson 	keg = zone_first_keg(zone);
3278af526374SJeff Roberson 	KEG_LOCK(keg);
32790095a784SJeff Roberson 	for (i = 0; i < cnt; i++) {
32800095a784SJeff Roberson 		item = bucket[i];
32810095a784SJeff Roberson 		if (!(zone->uz_flags & UMA_ZONE_VTOSLAB)) {
32820095a784SJeff Roberson 			mem = (uint8_t *)((uintptr_t)item & (~UMA_SLAB_MASK));
32830095a784SJeff Roberson 			if (zone->uz_flags & UMA_ZONE_HASH) {
32840095a784SJeff Roberson 				slab = hash_sfind(&keg->uk_hash, mem);
32850095a784SJeff Roberson 			} else {
32860095a784SJeff Roberson 				mem += keg->uk_pgoff;
32870095a784SJeff Roberson 				slab = (uma_slab_t)mem;
32880095a784SJeff Roberson 			}
32890095a784SJeff Roberson 		} else {
32900095a784SJeff Roberson 			slab = vtoslab((vm_offset_t)item);
32910095a784SJeff Roberson 			if (slab->us_keg != keg) {
32920095a784SJeff Roberson 				KEG_UNLOCK(keg);
32930095a784SJeff Roberson 				keg = slab->us_keg;
32940095a784SJeff Roberson 				KEG_LOCK(keg);
32950095a784SJeff Roberson 			}
32960095a784SJeff Roberson 		}
32970095a784SJeff Roberson 		slab_free_item(keg, slab, item);
3298099a0e58SBosko Milekic 		if (keg->uk_flags & UMA_ZFLAG_FULL) {
3299e20a199fSJeff Roberson 			if (keg->uk_pages < keg->uk_maxpages) {
3300099a0e58SBosko Milekic 				keg->uk_flags &= ~UMA_ZFLAG_FULL;
3301e20a199fSJeff Roberson 				clearfull = 1;
3302e20a199fSJeff Roberson 			}
3303af7f9b97SJeff Roberson 
330477380291SMohan Srinivasan 			/*
3305ef72505eSJeff Roberson 			 * We can handle one more allocation. Since we're
3306ef72505eSJeff Roberson 			 * clearing ZFLAG_FULL, wake up all procs blocked
3307ef72505eSJeff Roberson 			 * on pages. This should be uncommon, so keeping this
3308ef72505eSJeff Roberson 			 * simple for now (rather than adding count of blocked
330977380291SMohan Srinivasan 			 * threads etc).
331077380291SMohan Srinivasan 			 */
331177380291SMohan Srinivasan 			wakeup(keg);
3312af7f9b97SJeff Roberson 		}
33130095a784SJeff Roberson 	}
3314af526374SJeff Roberson 	KEG_UNLOCK(keg);
33150095a784SJeff Roberson 	if (clearfull) {
3316af526374SJeff Roberson 		ZONE_LOCK(zone);
3317e20a199fSJeff Roberson 		zone->uz_flags &= ~UMA_ZFLAG_FULL;
3318e20a199fSJeff Roberson 		wakeup(zone);
3319605cbd6aSJeff Roberson 		ZONE_UNLOCK(zone);
3320af526374SJeff Roberson 	}
3321ef72505eSJeff Roberson 
33228355f576SJeff Roberson }
33238355f576SJeff Roberson 
33240095a784SJeff Roberson /*
33250095a784SJeff Roberson  * Frees a single item to any zone.
33260095a784SJeff Roberson  *
33270095a784SJeff Roberson  * Arguments:
33280095a784SJeff Roberson  *	zone   The zone to free to
33290095a784SJeff Roberson  *	item   The item we're freeing
33300095a784SJeff Roberson  *	udata  User supplied data for the dtor
33310095a784SJeff Roberson  *	skip   Skip dtors and finis
33320095a784SJeff Roberson  */
33330095a784SJeff Roberson static void
33340095a784SJeff Roberson zone_free_item(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip)
33350095a784SJeff Roberson {
33360095a784SJeff Roberson #ifdef INVARIANTS
3337c5deaf04SGleb Smirnoff 	bool skipdbg;
3338c5deaf04SGleb Smirnoff 
3339c5deaf04SGleb Smirnoff 	skipdbg = uma_dbg_zskip(zone, item);
3340c5deaf04SGleb Smirnoff 	if (skip == SKIP_NONE && !skipdbg) {
33410095a784SJeff Roberson 		if (zone->uz_flags & UMA_ZONE_MALLOC)
33420095a784SJeff Roberson 			uma_dbg_free(zone, udata, item);
33430095a784SJeff Roberson 		else
33440095a784SJeff Roberson 			uma_dbg_free(zone, NULL, item);
33450095a784SJeff Roberson 	}
3346c5deaf04SGleb Smirnoff 
3347c5deaf04SGleb Smirnoff 	if (skip < SKIP_DTOR && zone->uz_dtor != NULL &&
3348c5deaf04SGleb Smirnoff 	    (!skipdbg || zone->uz_dtor != trash_dtor ||
3349c5deaf04SGleb Smirnoff 	    zone->uz_ctor != trash_ctor))
3350c5deaf04SGleb Smirnoff #else
3351c5deaf04SGleb Smirnoff 	if (skip < SKIP_DTOR && zone->uz_dtor != NULL)
33520095a784SJeff Roberson #endif
33530095a784SJeff Roberson 		zone->uz_dtor(item, zone->uz_size, udata);
33540095a784SJeff Roberson 
33550095a784SJeff Roberson 	if (skip < SKIP_FINI && zone->uz_fini)
33560095a784SJeff Roberson 		zone->uz_fini(item, zone->uz_size);
33570095a784SJeff Roberson 
33580095a784SJeff Roberson 	atomic_add_long(&zone->uz_frees, 1);
33590095a784SJeff Roberson 	zone->uz_release(zone->uz_arg, &item, 1);
33600095a784SJeff Roberson }
33610095a784SJeff Roberson 
33628355f576SJeff Roberson /* See uma.h */
33631c6cae97SLawrence Stewart int
3364736ee590SJeff Roberson uma_zone_set_max(uma_zone_t zone, int nitems)
3365736ee590SJeff Roberson {
3366099a0e58SBosko Milekic 	uma_keg_t keg;
3367099a0e58SBosko Milekic 
3368e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
33690095a784SJeff Roberson 	if (keg == NULL)
33700095a784SJeff Roberson 		return (0);
3371af526374SJeff Roberson 	KEG_LOCK(keg);
3372e20a199fSJeff Roberson 	keg->uk_maxpages = (nitems / keg->uk_ipers) * keg->uk_ppera;
3373099a0e58SBosko Milekic 	if (keg->uk_maxpages * keg->uk_ipers < nitems)
3374e20a199fSJeff Roberson 		keg->uk_maxpages += keg->uk_ppera;
337557223e99SAndriy Gapon 	nitems = (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers;
3376af526374SJeff Roberson 	KEG_UNLOCK(keg);
33771c6cae97SLawrence Stewart 
33781c6cae97SLawrence Stewart 	return (nitems);
3379736ee590SJeff Roberson }
3380736ee590SJeff Roberson 
3381736ee590SJeff Roberson /* See uma.h */
3382e49471b0SAndre Oppermann int
3383e49471b0SAndre Oppermann uma_zone_get_max(uma_zone_t zone)
3384e49471b0SAndre Oppermann {
3385e49471b0SAndre Oppermann 	int nitems;
3386e49471b0SAndre Oppermann 	uma_keg_t keg;
3387e49471b0SAndre Oppermann 
3388e49471b0SAndre Oppermann 	keg = zone_first_keg(zone);
33890095a784SJeff Roberson 	if (keg == NULL)
33900095a784SJeff Roberson 		return (0);
3391af526374SJeff Roberson 	KEG_LOCK(keg);
339257223e99SAndriy Gapon 	nitems = (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers;
3393af526374SJeff Roberson 	KEG_UNLOCK(keg);
3394e49471b0SAndre Oppermann 
3395e49471b0SAndre Oppermann 	return (nitems);
3396e49471b0SAndre Oppermann }
3397e49471b0SAndre Oppermann 
3398e49471b0SAndre Oppermann /* See uma.h */
33992f891cd5SPawel Jakub Dawidek void
34002f891cd5SPawel Jakub Dawidek uma_zone_set_warning(uma_zone_t zone, const char *warning)
34012f891cd5SPawel Jakub Dawidek {
34022f891cd5SPawel Jakub Dawidek 
34032f891cd5SPawel Jakub Dawidek 	ZONE_LOCK(zone);
34042f891cd5SPawel Jakub Dawidek 	zone->uz_warning = warning;
34052f891cd5SPawel Jakub Dawidek 	ZONE_UNLOCK(zone);
34062f891cd5SPawel Jakub Dawidek }
34072f891cd5SPawel Jakub Dawidek 
34082f891cd5SPawel Jakub Dawidek /* See uma.h */
340954503a13SJonathan T. Looney void
341054503a13SJonathan T. Looney uma_zone_set_maxaction(uma_zone_t zone, uma_maxaction_t maxaction)
341154503a13SJonathan T. Looney {
341254503a13SJonathan T. Looney 
341354503a13SJonathan T. Looney 	ZONE_LOCK(zone);
3414e60b2fcbSGleb Smirnoff 	TASK_INIT(&zone->uz_maxaction, 0, (task_fn_t *)maxaction, zone);
341554503a13SJonathan T. Looney 	ZONE_UNLOCK(zone);
341654503a13SJonathan T. Looney }
341754503a13SJonathan T. Looney 
341854503a13SJonathan T. Looney /* See uma.h */
3419c4ae7908SLawrence Stewart int
3420c4ae7908SLawrence Stewart uma_zone_get_cur(uma_zone_t zone)
3421c4ae7908SLawrence Stewart {
3422c4ae7908SLawrence Stewart 	int64_t nitems;
3423c4ae7908SLawrence Stewart 	u_int i;
3424c4ae7908SLawrence Stewart 
3425c4ae7908SLawrence Stewart 	ZONE_LOCK(zone);
3426c4ae7908SLawrence Stewart 	nitems = zone->uz_allocs - zone->uz_frees;
3427c4ae7908SLawrence Stewart 	CPU_FOREACH(i) {
3428c4ae7908SLawrence Stewart 		/*
3429c4ae7908SLawrence Stewart 		 * See the comment in sysctl_vm_zone_stats() regarding the
3430c4ae7908SLawrence Stewart 		 * safety of accessing the per-cpu caches. With the zone lock
3431c4ae7908SLawrence Stewart 		 * held, it is safe, but can potentially result in stale data.
3432c4ae7908SLawrence Stewart 		 */
3433c4ae7908SLawrence Stewart 		nitems += zone->uz_cpu[i].uc_allocs -
3434c4ae7908SLawrence Stewart 		    zone->uz_cpu[i].uc_frees;
3435c4ae7908SLawrence Stewart 	}
3436c4ae7908SLawrence Stewart 	ZONE_UNLOCK(zone);
3437c4ae7908SLawrence Stewart 
3438c4ae7908SLawrence Stewart 	return (nitems < 0 ? 0 : nitems);
3439c4ae7908SLawrence Stewart }
3440c4ae7908SLawrence Stewart 
3441c4ae7908SLawrence Stewart /* See uma.h */
3442736ee590SJeff Roberson void
3443099a0e58SBosko Milekic uma_zone_set_init(uma_zone_t zone, uma_init uminit)
3444099a0e58SBosko Milekic {
3445e20a199fSJeff Roberson 	uma_keg_t keg;
3446e20a199fSJeff Roberson 
3447e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
34480095a784SJeff Roberson 	KASSERT(keg != NULL, ("uma_zone_set_init: Invalid zone type"));
3449af526374SJeff Roberson 	KEG_LOCK(keg);
3450e20a199fSJeff Roberson 	KASSERT(keg->uk_pages == 0,
3451099a0e58SBosko Milekic 	    ("uma_zone_set_init on non-empty keg"));
3452e20a199fSJeff Roberson 	keg->uk_init = uminit;
3453af526374SJeff Roberson 	KEG_UNLOCK(keg);
3454099a0e58SBosko Milekic }
3455099a0e58SBosko Milekic 
3456099a0e58SBosko Milekic /* See uma.h */
3457099a0e58SBosko Milekic void
3458099a0e58SBosko Milekic uma_zone_set_fini(uma_zone_t zone, uma_fini fini)
3459099a0e58SBosko Milekic {
3460e20a199fSJeff Roberson 	uma_keg_t keg;
3461e20a199fSJeff Roberson 
3462e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
34631d2c0c46SDmitry Chagin 	KASSERT(keg != NULL, ("uma_zone_set_fini: Invalid zone type"));
3464af526374SJeff Roberson 	KEG_LOCK(keg);
3465e20a199fSJeff Roberson 	KASSERT(keg->uk_pages == 0,
3466099a0e58SBosko Milekic 	    ("uma_zone_set_fini on non-empty keg"));
3467e20a199fSJeff Roberson 	keg->uk_fini = fini;
3468af526374SJeff Roberson 	KEG_UNLOCK(keg);
3469099a0e58SBosko Milekic }
3470099a0e58SBosko Milekic 
3471099a0e58SBosko Milekic /* See uma.h */
3472099a0e58SBosko Milekic void
3473099a0e58SBosko Milekic uma_zone_set_zinit(uma_zone_t zone, uma_init zinit)
3474099a0e58SBosko Milekic {
3475af526374SJeff Roberson 
3476099a0e58SBosko Milekic 	ZONE_LOCK(zone);
3477e20a199fSJeff Roberson 	KASSERT(zone_first_keg(zone)->uk_pages == 0,
3478099a0e58SBosko Milekic 	    ("uma_zone_set_zinit on non-empty keg"));
3479099a0e58SBosko Milekic 	zone->uz_init = zinit;
3480099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
3481099a0e58SBosko Milekic }
3482099a0e58SBosko Milekic 
3483099a0e58SBosko Milekic /* See uma.h */
3484099a0e58SBosko Milekic void
3485099a0e58SBosko Milekic uma_zone_set_zfini(uma_zone_t zone, uma_fini zfini)
3486099a0e58SBosko Milekic {
3487af526374SJeff Roberson 
3488099a0e58SBosko Milekic 	ZONE_LOCK(zone);
3489e20a199fSJeff Roberson 	KASSERT(zone_first_keg(zone)->uk_pages == 0,
3490099a0e58SBosko Milekic 	    ("uma_zone_set_zfini on non-empty keg"));
3491099a0e58SBosko Milekic 	zone->uz_fini = zfini;
3492099a0e58SBosko Milekic 	ZONE_UNLOCK(zone);
3493099a0e58SBosko Milekic }
3494099a0e58SBosko Milekic 
3495099a0e58SBosko Milekic /* See uma.h */
3496b23f72e9SBrian Feldman /* XXX uk_freef is not actually used with the zone locked */
3497099a0e58SBosko Milekic void
34988355f576SJeff Roberson uma_zone_set_freef(uma_zone_t zone, uma_free freef)
34998355f576SJeff Roberson {
35000095a784SJeff Roberson 	uma_keg_t keg;
3501e20a199fSJeff Roberson 
35020095a784SJeff Roberson 	keg = zone_first_keg(zone);
35031d2c0c46SDmitry Chagin 	KASSERT(keg != NULL, ("uma_zone_set_freef: Invalid zone type"));
3504af526374SJeff Roberson 	KEG_LOCK(keg);
35050095a784SJeff Roberson 	keg->uk_freef = freef;
3506af526374SJeff Roberson 	KEG_UNLOCK(keg);
35078355f576SJeff Roberson }
35088355f576SJeff Roberson 
35098355f576SJeff Roberson /* See uma.h */
3510b23f72e9SBrian Feldman /* XXX uk_allocf is not actually used with the zone locked */
35118355f576SJeff Roberson void
35128355f576SJeff Roberson uma_zone_set_allocf(uma_zone_t zone, uma_alloc allocf)
35138355f576SJeff Roberson {
3514e20a199fSJeff Roberson 	uma_keg_t keg;
3515e20a199fSJeff Roberson 
3516e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
3517af526374SJeff Roberson 	KEG_LOCK(keg);
3518e20a199fSJeff Roberson 	keg->uk_allocf = allocf;
3519af526374SJeff Roberson 	KEG_UNLOCK(keg);
35208355f576SJeff Roberson }
35218355f576SJeff Roberson 
35228355f576SJeff Roberson /* See uma.h */
35236fd34d6fSJeff Roberson void
35246fd34d6fSJeff Roberson uma_zone_reserve(uma_zone_t zone, int items)
35256fd34d6fSJeff Roberson {
35266fd34d6fSJeff Roberson 	uma_keg_t keg;
35276fd34d6fSJeff Roberson 
35286fd34d6fSJeff Roberson 	keg = zone_first_keg(zone);
35296fd34d6fSJeff Roberson 	if (keg == NULL)
35306fd34d6fSJeff Roberson 		return;
35316fd34d6fSJeff Roberson 	KEG_LOCK(keg);
35326fd34d6fSJeff Roberson 	keg->uk_reserve = items;
35336fd34d6fSJeff Roberson 	KEG_UNLOCK(keg);
35346fd34d6fSJeff Roberson 
35356fd34d6fSJeff Roberson 	return;
35366fd34d6fSJeff Roberson }
35376fd34d6fSJeff Roberson 
35386fd34d6fSJeff Roberson /* See uma.h */
35398355f576SJeff Roberson int
3540a4915c21SAttilio Rao uma_zone_reserve_kva(uma_zone_t zone, int count)
35418355f576SJeff Roberson {
3542099a0e58SBosko Milekic 	uma_keg_t keg;
35438355f576SJeff Roberson 	vm_offset_t kva;
35449ba30bcbSZbigniew Bodek 	u_int pages;
35458355f576SJeff Roberson 
3546e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
35470095a784SJeff Roberson 	if (keg == NULL)
35480095a784SJeff Roberson 		return (0);
3549099a0e58SBosko Milekic 	pages = count / keg->uk_ipers;
35508355f576SJeff Roberson 
3551099a0e58SBosko Milekic 	if (pages * keg->uk_ipers < count)
35528355f576SJeff Roberson 		pages++;
355357223e99SAndriy Gapon 	pages *= keg->uk_ppera;
3554a553d4b8SJeff Roberson 
3555a4915c21SAttilio Rao #ifdef UMA_MD_SMALL_ALLOC
3556a4915c21SAttilio Rao 	if (keg->uk_ppera > 1) {
3557a4915c21SAttilio Rao #else
3558a4915c21SAttilio Rao 	if (1) {
3559a4915c21SAttilio Rao #endif
356057223e99SAndriy Gapon 		kva = kva_alloc((vm_size_t)pages * PAGE_SIZE);
3561d1f42ac2SAlan Cox 		if (kva == 0)
35628355f576SJeff Roberson 			return (0);
3563a4915c21SAttilio Rao 	} else
3564a4915c21SAttilio Rao 		kva = 0;
3565af526374SJeff Roberson 	KEG_LOCK(keg);
3566099a0e58SBosko Milekic 	keg->uk_kva = kva;
3567a4915c21SAttilio Rao 	keg->uk_offset = 0;
3568099a0e58SBosko Milekic 	keg->uk_maxpages = pages;
3569a4915c21SAttilio Rao #ifdef UMA_MD_SMALL_ALLOC
3570a4915c21SAttilio Rao 	keg->uk_allocf = (keg->uk_ppera > 1) ? noobj_alloc : uma_small_alloc;
3571a4915c21SAttilio Rao #else
3572a4915c21SAttilio Rao 	keg->uk_allocf = noobj_alloc;
3573a4915c21SAttilio Rao #endif
35746fd34d6fSJeff Roberson 	keg->uk_flags |= UMA_ZONE_NOFREE;
3575af526374SJeff Roberson 	KEG_UNLOCK(keg);
3576af526374SJeff Roberson 
35778355f576SJeff Roberson 	return (1);
35788355f576SJeff Roberson }
35798355f576SJeff Roberson 
35808355f576SJeff Roberson /* See uma.h */
35818355f576SJeff Roberson void
35828355f576SJeff Roberson uma_prealloc(uma_zone_t zone, int items)
35838355f576SJeff Roberson {
3584ab3185d1SJeff Roberson 	uma_domain_t dom;
35858355f576SJeff Roberson 	uma_slab_t slab;
3586099a0e58SBosko Milekic 	uma_keg_t keg;
3587ab3185d1SJeff Roberson 	int domain, slabs;
35888355f576SJeff Roberson 
3589e20a199fSJeff Roberson 	keg = zone_first_keg(zone);
35900095a784SJeff Roberson 	if (keg == NULL)
35910095a784SJeff Roberson 		return;
3592af526374SJeff Roberson 	KEG_LOCK(keg);
3593099a0e58SBosko Milekic 	slabs = items / keg->uk_ipers;
3594ab3185d1SJeff Roberson 	domain = 0;
3595099a0e58SBosko Milekic 	if (slabs * keg->uk_ipers < items)
35968355f576SJeff Roberson 		slabs++;
35978355f576SJeff Roberson 	while (slabs > 0) {
3598ab3185d1SJeff Roberson 		slab = keg_alloc_slab(keg, zone, domain, M_WAITOK);
3599e20a199fSJeff Roberson 		if (slab == NULL)
3600e20a199fSJeff Roberson 			break;
3601e20a199fSJeff Roberson 		MPASS(slab->us_keg == keg);
3602ab3185d1SJeff Roberson 		dom = &keg->uk_domain[slab->us_domain];
3603ab3185d1SJeff Roberson 		LIST_INSERT_HEAD(&dom->ud_free_slab, slab, us_link);
36048355f576SJeff Roberson 		slabs--;
3605ab3185d1SJeff Roberson 		domain = (domain + 1) % vm_ndomains;
36068355f576SJeff Roberson 	}
3607af526374SJeff Roberson 	KEG_UNLOCK(keg);
36088355f576SJeff Roberson }
36098355f576SJeff Roberson 
36108355f576SJeff Roberson /* See uma.h */
361144ec2b63SKonstantin Belousov static void
361244ec2b63SKonstantin Belousov uma_reclaim_locked(bool kmem_danger)
36138355f576SJeff Roberson {
361444ec2b63SKonstantin Belousov 
36151431a748SGleb Smirnoff 	CTR0(KTR_UMA, "UMA: vm asked us to release pages!");
361644ec2b63SKonstantin Belousov 	sx_assert(&uma_drain_lock, SA_XLOCKED);
361786bbae32SJeff Roberson 	bucket_enable();
36188355f576SJeff Roberson 	zone_foreach(zone_drain);
361944ec2b63SKonstantin Belousov 	if (vm_page_count_min() || kmem_danger) {
3620a2de44abSAlexander Motin 		cache_drain_safe(NULL);
3621a2de44abSAlexander Motin 		zone_foreach(zone_drain);
3622a2de44abSAlexander Motin 	}
36238355f576SJeff Roberson 	/*
36248355f576SJeff Roberson 	 * Some slabs may have been freed but this zone will be visited early
36258355f576SJeff Roberson 	 * we visit again so that we can free pages that are empty once other
36268355f576SJeff Roberson 	 * zones are drained.  We have to do the same for buckets.
36278355f576SJeff Roberson 	 */
36289643769aSJeff Roberson 	zone_drain(slabzone);
3629cae33c14SJeff Roberson 	bucket_zone_drain();
363044ec2b63SKonstantin Belousov }
363144ec2b63SKonstantin Belousov 
363244ec2b63SKonstantin Belousov void
363344ec2b63SKonstantin Belousov uma_reclaim(void)
363444ec2b63SKonstantin Belousov {
363544ec2b63SKonstantin Belousov 
363644ec2b63SKonstantin Belousov 	sx_xlock(&uma_drain_lock);
363744ec2b63SKonstantin Belousov 	uma_reclaim_locked(false);
363895c4bf75SKonstantin Belousov 	sx_xunlock(&uma_drain_lock);
36398355f576SJeff Roberson }
36408355f576SJeff Roberson 
36412e47807cSJeff Roberson static volatile int uma_reclaim_needed;
364244ec2b63SKonstantin Belousov 
364344ec2b63SKonstantin Belousov void
364444ec2b63SKonstantin Belousov uma_reclaim_wakeup(void)
364544ec2b63SKonstantin Belousov {
364644ec2b63SKonstantin Belousov 
36472e47807cSJeff Roberson 	if (atomic_fetchadd_int(&uma_reclaim_needed, 1) == 0)
36482e47807cSJeff Roberson 		wakeup(uma_reclaim);
364944ec2b63SKonstantin Belousov }
365044ec2b63SKonstantin Belousov 
365144ec2b63SKonstantin Belousov void
365244ec2b63SKonstantin Belousov uma_reclaim_worker(void *arg __unused)
365344ec2b63SKonstantin Belousov {
365444ec2b63SKonstantin Belousov 
365544ec2b63SKonstantin Belousov 	for (;;) {
36562e47807cSJeff Roberson 		sx_xlock(&uma_drain_lock);
3657200f8117SKonstantin Belousov 		while (atomic_load_int(&uma_reclaim_needed) == 0)
36582e47807cSJeff Roberson 			sx_sleep(uma_reclaim, &uma_drain_lock, PVM, "umarcl",
36592e47807cSJeff Roberson 			    hz);
36609b43bc27SAndriy Gapon 		sx_xunlock(&uma_drain_lock);
36619b43bc27SAndriy Gapon 		EVENTHANDLER_INVOKE(vm_lowmem, VM_LOW_KMEM);
36629b43bc27SAndriy Gapon 		sx_xlock(&uma_drain_lock);
366344ec2b63SKonstantin Belousov 		uma_reclaim_locked(true);
3664200f8117SKonstantin Belousov 		atomic_store_int(&uma_reclaim_needed, 0);
36652e47807cSJeff Roberson 		sx_xunlock(&uma_drain_lock);
36662e47807cSJeff Roberson 		/* Don't fire more than once per-second. */
36672e47807cSJeff Roberson 		pause("umarclslp", hz);
366844ec2b63SKonstantin Belousov 	}
366944ec2b63SKonstantin Belousov }
367044ec2b63SKonstantin Belousov 
3671663b416fSJohn Baldwin /* See uma.h */
3672663b416fSJohn Baldwin int
3673663b416fSJohn Baldwin uma_zone_exhausted(uma_zone_t zone)
3674663b416fSJohn Baldwin {
3675663b416fSJohn Baldwin 	int full;
3676663b416fSJohn Baldwin 
3677663b416fSJohn Baldwin 	ZONE_LOCK(zone);
3678e20a199fSJeff Roberson 	full = (zone->uz_flags & UMA_ZFLAG_FULL);
3679663b416fSJohn Baldwin 	ZONE_UNLOCK(zone);
3680663b416fSJohn Baldwin 	return (full);
3681663b416fSJohn Baldwin }
3682663b416fSJohn Baldwin 
36836c125b8dSMohan Srinivasan int
36846c125b8dSMohan Srinivasan uma_zone_exhausted_nolock(uma_zone_t zone)
36856c125b8dSMohan Srinivasan {
3686e20a199fSJeff Roberson 	return (zone->uz_flags & UMA_ZFLAG_FULL);
36876c125b8dSMohan Srinivasan }
36886c125b8dSMohan Srinivasan 
36898355f576SJeff Roberson void *
3690ab3185d1SJeff Roberson uma_large_malloc_domain(vm_size_t size, int domain, int wait)
36918355f576SJeff Roberson {
36920766f278SJonathan T. Looney 	struct vmem *arena;
3693ab3185d1SJeff Roberson 	vm_offset_t addr;
36948355f576SJeff Roberson 	uma_slab_t slab;
36958355f576SJeff Roberson 
36960766f278SJonathan T. Looney #if VM_NRESERVLEVEL > 0
36970766f278SJonathan T. Looney 	if (__predict_true((wait & M_EXEC) == 0))
36980766f278SJonathan T. Looney 		arena = kernel_arena;
36990766f278SJonathan T. Looney 	else
37000766f278SJonathan T. Looney 		arena = kernel_rwx_arena;
37010766f278SJonathan T. Looney #else
37020766f278SJonathan T. Looney 	arena = kernel_arena;
37030766f278SJonathan T. Looney #endif
37040766f278SJonathan T. Looney 
3705ab3185d1SJeff Roberson 	slab = zone_alloc_item(slabzone, NULL, domain, wait);
37068355f576SJeff Roberson 	if (slab == NULL)
37078355f576SJeff Roberson 		return (NULL);
3708ab3185d1SJeff Roberson 	if (domain == UMA_ANYDOMAIN)
37090766f278SJonathan T. Looney 		addr = kmem_malloc(arena, size, wait);
3710ab3185d1SJeff Roberson 	else
37110766f278SJonathan T. Looney 		addr = kmem_malloc_domain(arena, domain, size, wait);
3712ab3185d1SJeff Roberson 	if (addr != 0) {
3713ab3185d1SJeff Roberson 		vsetslab(addr, slab);
3714ab3185d1SJeff Roberson 		slab->us_data = (void *)addr;
3715ab3185d1SJeff Roberson 		slab->us_flags = UMA_SLAB_KERNEL | UMA_SLAB_MALLOC;
37160766f278SJonathan T. Looney #if VM_NRESERVLEVEL > 0
37170766f278SJonathan T. Looney 		if (__predict_false(arena == kernel_rwx_arena))
37180766f278SJonathan T. Looney 			slab->us_flags |= UMA_SLAB_KRWX;
37190766f278SJonathan T. Looney #endif
37208355f576SJeff Roberson 		slab->us_size = size;
3721e2068d0bSJeff Roberson 		slab->us_domain = vm_phys_domain(PHYS_TO_VM_PAGE(
3722ab3185d1SJeff Roberson 		    pmap_kextract(addr)));
37232e47807cSJeff Roberson 		uma_total_inc(size);
37248355f576SJeff Roberson 	} else {
37250095a784SJeff Roberson 		zone_free_item(slabzone, slab, NULL, SKIP_NONE);
37268355f576SJeff Roberson 	}
37278355f576SJeff Roberson 
3728ab3185d1SJeff Roberson 	return ((void *)addr);
3729ab3185d1SJeff Roberson }
3730ab3185d1SJeff Roberson 
3731ab3185d1SJeff Roberson void *
3732ab3185d1SJeff Roberson uma_large_malloc(vm_size_t size, int wait)
3733ab3185d1SJeff Roberson {
3734ab3185d1SJeff Roberson 
3735ab3185d1SJeff Roberson 	return uma_large_malloc_domain(size, UMA_ANYDOMAIN, wait);
37368355f576SJeff Roberson }
37378355f576SJeff Roberson 
37388355f576SJeff Roberson void
37398355f576SJeff Roberson uma_large_free(uma_slab_t slab)
37408355f576SJeff Roberson {
37410766f278SJonathan T. Looney 	struct vmem *arena;
3742c325e866SKonstantin Belousov 
3743ab3185d1SJeff Roberson 	KASSERT((slab->us_flags & UMA_SLAB_KERNEL) != 0,
3744ab3185d1SJeff Roberson 	    ("uma_large_free:  Memory not allocated with uma_large_malloc."));
37450766f278SJonathan T. Looney #if VM_NRESERVLEVEL > 0
37460766f278SJonathan T. Looney 	if (__predict_true((slab->us_flags & UMA_SLAB_KRWX) == 0))
37470766f278SJonathan T. Looney 		arena = kernel_arena;
37480766f278SJonathan T. Looney 	else
37490766f278SJonathan T. Looney 		arena = kernel_rwx_arena;
37500766f278SJonathan T. Looney #else
37510766f278SJonathan T. Looney 	arena = kernel_arena;
37520766f278SJonathan T. Looney #endif
37530766f278SJonathan T. Looney 	kmem_free(arena, (vm_offset_t)slab->us_data, slab->us_size);
37542e47807cSJeff Roberson 	uma_total_dec(slab->us_size);
37550095a784SJeff Roberson 	zone_free_item(slabzone, slab, NULL, SKIP_NONE);
37568355f576SJeff Roberson }
37578355f576SJeff Roberson 
375848343a2fSGleb Smirnoff static void
375948343a2fSGleb Smirnoff uma_zero_item(void *item, uma_zone_t zone)
376048343a2fSGleb Smirnoff {
376148343a2fSGleb Smirnoff 
376248343a2fSGleb Smirnoff 	bzero(item, zone->uz_size);
376348343a2fSGleb Smirnoff }
376448343a2fSGleb Smirnoff 
37652e47807cSJeff Roberson unsigned long
37662e47807cSJeff Roberson uma_limit(void)
37672e47807cSJeff Roberson {
37682e47807cSJeff Roberson 
37692e47807cSJeff Roberson 	return (uma_kmem_limit);
37702e47807cSJeff Roberson }
37712e47807cSJeff Roberson 
37722e47807cSJeff Roberson void
37732e47807cSJeff Roberson uma_set_limit(unsigned long limit)
37742e47807cSJeff Roberson {
37752e47807cSJeff Roberson 
37762e47807cSJeff Roberson 	uma_kmem_limit = limit;
37772e47807cSJeff Roberson }
37782e47807cSJeff Roberson 
37792e47807cSJeff Roberson unsigned long
37802e47807cSJeff Roberson uma_size(void)
37812e47807cSJeff Roberson {
37822e47807cSJeff Roberson 
3783ad5b0f5bSJeff Roberson 	return (uma_kmem_total);
3784ad5b0f5bSJeff Roberson }
3785ad5b0f5bSJeff Roberson 
3786ad5b0f5bSJeff Roberson long
3787ad5b0f5bSJeff Roberson uma_avail(void)
3788ad5b0f5bSJeff Roberson {
3789ad5b0f5bSJeff Roberson 
3790ad5b0f5bSJeff Roberson 	return (uma_kmem_limit - uma_kmem_total);
37912e47807cSJeff Roberson }
37922e47807cSJeff Roberson 
37938355f576SJeff Roberson void
37948355f576SJeff Roberson uma_print_stats(void)
37958355f576SJeff Roberson {
37968355f576SJeff Roberson 	zone_foreach(uma_print_zone);
37978355f576SJeff Roberson }
37988355f576SJeff Roberson 
3799504d5de3SJeff Roberson static void
3800504d5de3SJeff Roberson slab_print(uma_slab_t slab)
3801504d5de3SJeff Roberson {
3802ef72505eSJeff Roberson 	printf("slab: keg %p, data %p, freecount %d\n",
3803ef72505eSJeff Roberson 		slab->us_keg, slab->us_data, slab->us_freecount);
3804504d5de3SJeff Roberson }
3805504d5de3SJeff Roberson 
3806504d5de3SJeff Roberson static void
3807504d5de3SJeff Roberson cache_print(uma_cache_t cache)
3808504d5de3SJeff Roberson {
3809504d5de3SJeff Roberson 	printf("alloc: %p(%d), free: %p(%d)\n",
3810504d5de3SJeff Roberson 		cache->uc_allocbucket,
3811504d5de3SJeff Roberson 		cache->uc_allocbucket?cache->uc_allocbucket->ub_cnt:0,
3812504d5de3SJeff Roberson 		cache->uc_freebucket,
3813504d5de3SJeff Roberson 		cache->uc_freebucket?cache->uc_freebucket->ub_cnt:0);
3814504d5de3SJeff Roberson }
3815504d5de3SJeff Roberson 
3816e20a199fSJeff Roberson static void
3817e20a199fSJeff Roberson uma_print_keg(uma_keg_t keg)
38188355f576SJeff Roberson {
3819ab3185d1SJeff Roberson 	uma_domain_t dom;
3820504d5de3SJeff Roberson 	uma_slab_t slab;
3821ab3185d1SJeff Roberson 	int i;
3822504d5de3SJeff Roberson 
38230b80c1e4SEitan Adler 	printf("keg: %s(%p) size %d(%d) flags %#x ipers %d ppera %d "
3824e20a199fSJeff Roberson 	    "out %d free %d limit %d\n",
3825e20a199fSJeff Roberson 	    keg->uk_name, keg, keg->uk_size, keg->uk_rsize, keg->uk_flags,
3826099a0e58SBosko Milekic 	    keg->uk_ipers, keg->uk_ppera,
382757223e99SAndriy Gapon 	    (keg->uk_pages / keg->uk_ppera) * keg->uk_ipers - keg->uk_free,
382857223e99SAndriy Gapon 	    keg->uk_free, (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers);
3829ab3185d1SJeff Roberson 	for (i = 0; i < vm_ndomains; i++) {
3830ab3185d1SJeff Roberson 		dom = &keg->uk_domain[i];
3831504d5de3SJeff Roberson 		printf("Part slabs:\n");
3832ab3185d1SJeff Roberson 		LIST_FOREACH(slab, &dom->ud_part_slab, us_link)
3833504d5de3SJeff Roberson 			slab_print(slab);
3834504d5de3SJeff Roberson 		printf("Free slabs:\n");
3835ab3185d1SJeff Roberson 		LIST_FOREACH(slab, &dom->ud_free_slab, us_link)
3836504d5de3SJeff Roberson 			slab_print(slab);
3837504d5de3SJeff Roberson 		printf("Full slabs:\n");
3838ab3185d1SJeff Roberson 		LIST_FOREACH(slab, &dom->ud_full_slab, us_link)
3839504d5de3SJeff Roberson 			slab_print(slab);
3840e20a199fSJeff Roberson 	}
3841ab3185d1SJeff Roberson }
3842e20a199fSJeff Roberson 
3843e20a199fSJeff Roberson void
3844e20a199fSJeff Roberson uma_print_zone(uma_zone_t zone)
3845e20a199fSJeff Roberson {
3846e20a199fSJeff Roberson 	uma_cache_t cache;
3847e20a199fSJeff Roberson 	uma_klink_t kl;
3848e20a199fSJeff Roberson 	int i;
3849e20a199fSJeff Roberson 
38500b80c1e4SEitan Adler 	printf("zone: %s(%p) size %d flags %#x\n",
3851e20a199fSJeff Roberson 	    zone->uz_name, zone, zone->uz_size, zone->uz_flags);
3852e20a199fSJeff Roberson 	LIST_FOREACH(kl, &zone->uz_kegs, kl_link)
3853e20a199fSJeff Roberson 		uma_print_keg(kl->kl_keg);
38543aa6d94eSJohn Baldwin 	CPU_FOREACH(i) {
3855504d5de3SJeff Roberson 		cache = &zone->uz_cpu[i];
3856504d5de3SJeff Roberson 		printf("CPU %d Cache:\n", i);
3857504d5de3SJeff Roberson 		cache_print(cache);
3858504d5de3SJeff Roberson 	}
38598355f576SJeff Roberson }
38608355f576SJeff Roberson 
3861a0d4b0aeSRobert Watson #ifdef DDB
38628355f576SJeff Roberson /*
38637a52a97eSRobert Watson  * Generate statistics across both the zone and its per-cpu cache's.  Return
38647a52a97eSRobert Watson  * desired statistics if the pointer is non-NULL for that statistic.
38657a52a97eSRobert Watson  *
38667a52a97eSRobert Watson  * Note: does not update the zone statistics, as it can't safely clear the
38677a52a97eSRobert Watson  * per-CPU cache statistic.
38687a52a97eSRobert Watson  *
38697a52a97eSRobert Watson  * XXXRW: Following the uc_allocbucket and uc_freebucket pointers here isn't
38707a52a97eSRobert Watson  * safe from off-CPU; we should modify the caches to track this information
38717a52a97eSRobert Watson  * directly so that we don't have to.
38727a52a97eSRobert Watson  */
38737a52a97eSRobert Watson static void
387485dcf349SGleb Smirnoff uma_zone_sumstat(uma_zone_t z, int *cachefreep, uint64_t *allocsp,
387585dcf349SGleb Smirnoff     uint64_t *freesp, uint64_t *sleepsp)
38767a52a97eSRobert Watson {
38777a52a97eSRobert Watson 	uma_cache_t cache;
387885dcf349SGleb Smirnoff 	uint64_t allocs, frees, sleeps;
38797a52a97eSRobert Watson 	int cachefree, cpu;
38807a52a97eSRobert Watson 
3881bf965959SSean Bruno 	allocs = frees = sleeps = 0;
38827a52a97eSRobert Watson 	cachefree = 0;
38833aa6d94eSJohn Baldwin 	CPU_FOREACH(cpu) {
38847a52a97eSRobert Watson 		cache = &z->uz_cpu[cpu];
38857a52a97eSRobert Watson 		if (cache->uc_allocbucket != NULL)
38867a52a97eSRobert Watson 			cachefree += cache->uc_allocbucket->ub_cnt;
38877a52a97eSRobert Watson 		if (cache->uc_freebucket != NULL)
38887a52a97eSRobert Watson 			cachefree += cache->uc_freebucket->ub_cnt;
38897a52a97eSRobert Watson 		allocs += cache->uc_allocs;
38907a52a97eSRobert Watson 		frees += cache->uc_frees;
38917a52a97eSRobert Watson 	}
38927a52a97eSRobert Watson 	allocs += z->uz_allocs;
38937a52a97eSRobert Watson 	frees += z->uz_frees;
3894bf965959SSean Bruno 	sleeps += z->uz_sleeps;
38957a52a97eSRobert Watson 	if (cachefreep != NULL)
38967a52a97eSRobert Watson 		*cachefreep = cachefree;
38977a52a97eSRobert Watson 	if (allocsp != NULL)
38987a52a97eSRobert Watson 		*allocsp = allocs;
38997a52a97eSRobert Watson 	if (freesp != NULL)
39007a52a97eSRobert Watson 		*freesp = frees;
3901bf965959SSean Bruno 	if (sleepsp != NULL)
3902bf965959SSean Bruno 		*sleepsp = sleeps;
39037a52a97eSRobert Watson }
3904a0d4b0aeSRobert Watson #endif /* DDB */
39057a52a97eSRobert Watson 
39067a52a97eSRobert Watson static int
39077a52a97eSRobert Watson sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS)
39087a52a97eSRobert Watson {
39097a52a97eSRobert Watson 	uma_keg_t kz;
39107a52a97eSRobert Watson 	uma_zone_t z;
39117a52a97eSRobert Watson 	int count;
39127a52a97eSRobert Watson 
39137a52a97eSRobert Watson 	count = 0;
3914111fbcd5SBryan Venteicher 	rw_rlock(&uma_rwlock);
39157a52a97eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
39167a52a97eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link)
39177a52a97eSRobert Watson 			count++;
39187a52a97eSRobert Watson 	}
3919111fbcd5SBryan Venteicher 	rw_runlock(&uma_rwlock);
39207a52a97eSRobert Watson 	return (sysctl_handle_int(oidp, &count, 0, req));
39217a52a97eSRobert Watson }
39227a52a97eSRobert Watson 
39237a52a97eSRobert Watson static int
39247a52a97eSRobert Watson sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
39257a52a97eSRobert Watson {
39267a52a97eSRobert Watson 	struct uma_stream_header ush;
39277a52a97eSRobert Watson 	struct uma_type_header uth;
392863b5d112SKonstantin Belousov 	struct uma_percpu_stat *ups;
39297a52a97eSRobert Watson 	uma_bucket_t bucket;
3930ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
39317a52a97eSRobert Watson 	struct sbuf sbuf;
39327a52a97eSRobert Watson 	uma_cache_t cache;
3933e20a199fSJeff Roberson 	uma_klink_t kl;
39347a52a97eSRobert Watson 	uma_keg_t kz;
39357a52a97eSRobert Watson 	uma_zone_t z;
3936e20a199fSJeff Roberson 	uma_keg_t k;
39374e657159SMatthew D Fleming 	int count, error, i;
39387a52a97eSRobert Watson 
393900f0e671SMatthew D Fleming 	error = sysctl_wire_old_buffer(req, 0);
394000f0e671SMatthew D Fleming 	if (error != 0)
394100f0e671SMatthew D Fleming 		return (error);
39424e657159SMatthew D Fleming 	sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
39431eafc078SIan Lepore 	sbuf_clear_flags(&sbuf, SBUF_INCLUDENUL);
394463b5d112SKonstantin Belousov 	ups = malloc((mp_maxid + 1) * sizeof(*ups), M_TEMP, M_WAITOK);
39454e657159SMatthew D Fleming 
3946404a593eSMatthew D Fleming 	count = 0;
3947111fbcd5SBryan Venteicher 	rw_rlock(&uma_rwlock);
39487a52a97eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
39497a52a97eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link)
39507a52a97eSRobert Watson 			count++;
39517a52a97eSRobert Watson 	}
39527a52a97eSRobert Watson 
39537a52a97eSRobert Watson 	/*
39547a52a97eSRobert Watson 	 * Insert stream header.
39557a52a97eSRobert Watson 	 */
39567a52a97eSRobert Watson 	bzero(&ush, sizeof(ush));
39577a52a97eSRobert Watson 	ush.ush_version = UMA_STREAM_VERSION;
3958ab3a57c0SRobert Watson 	ush.ush_maxcpus = (mp_maxid + 1);
39597a52a97eSRobert Watson 	ush.ush_count = count;
39604e657159SMatthew D Fleming 	(void)sbuf_bcat(&sbuf, &ush, sizeof(ush));
39617a52a97eSRobert Watson 
39627a52a97eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
39637a52a97eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link) {
39647a52a97eSRobert Watson 			bzero(&uth, sizeof(uth));
39657a52a97eSRobert Watson 			ZONE_LOCK(z);
3966cbbb4a00SRobert Watson 			strlcpy(uth.uth_name, z->uz_name, UTH_MAX_NAME);
39677a52a97eSRobert Watson 			uth.uth_align = kz->uk_align;
39687a52a97eSRobert Watson 			uth.uth_size = kz->uk_size;
39697a52a97eSRobert Watson 			uth.uth_rsize = kz->uk_rsize;
3970e20a199fSJeff Roberson 			LIST_FOREACH(kl, &z->uz_kegs, kl_link) {
3971e20a199fSJeff Roberson 				k = kl->kl_keg;
3972e20a199fSJeff Roberson 				uth.uth_maxpages += k->uk_maxpages;
3973e20a199fSJeff Roberson 				uth.uth_pages += k->uk_pages;
3974e20a199fSJeff Roberson 				uth.uth_keg_free += k->uk_free;
3975e20a199fSJeff Roberson 				uth.uth_limit = (k->uk_maxpages / k->uk_ppera)
3976e20a199fSJeff Roberson 				    * k->uk_ipers;
3977e20a199fSJeff Roberson 			}
3978cbbb4a00SRobert Watson 
3979cbbb4a00SRobert Watson 			/*
3980cbbb4a00SRobert Watson 			 * A zone is secondary is it is not the first entry
3981cbbb4a00SRobert Watson 			 * on the keg's zone list.
3982cbbb4a00SRobert Watson 			 */
3983e20a199fSJeff Roberson 			if ((z->uz_flags & UMA_ZONE_SECONDARY) &&
3984cbbb4a00SRobert Watson 			    (LIST_FIRST(&kz->uk_zones) != z))
3985cbbb4a00SRobert Watson 				uth.uth_zone_flags = UTH_ZONE_SECONDARY;
3986cbbb4a00SRobert Watson 
3987ab3185d1SJeff Roberson 			for (i = 0; i < vm_ndomains; i++) {
3988ab3185d1SJeff Roberson 				zdom = &z->uz_domain[i];
3989ab3185d1SJeff Roberson 				LIST_FOREACH(bucket, &zdom->uzd_buckets,
3990ab3185d1SJeff Roberson 				    ub_link)
39917a52a97eSRobert Watson 					uth.uth_zone_free += bucket->ub_cnt;
3992ab3185d1SJeff Roberson 			}
39937a52a97eSRobert Watson 			uth.uth_allocs = z->uz_allocs;
39947a52a97eSRobert Watson 			uth.uth_frees = z->uz_frees;
39952019094aSRobert Watson 			uth.uth_fails = z->uz_fails;
3996bf965959SSean Bruno 			uth.uth_sleeps = z->uz_sleeps;
39977a52a97eSRobert Watson 			/*
39982450bbb8SRobert Watson 			 * While it is not normally safe to access the cache
39992450bbb8SRobert Watson 			 * bucket pointers while not on the CPU that owns the
40002450bbb8SRobert Watson 			 * cache, we only allow the pointers to be exchanged
40012450bbb8SRobert Watson 			 * without the zone lock held, not invalidated, so
40022450bbb8SRobert Watson 			 * accept the possible race associated with bucket
40032450bbb8SRobert Watson 			 * exchange during monitoring.
40047a52a97eSRobert Watson 			 */
400563b5d112SKonstantin Belousov 			for (i = 0; i < mp_maxid + 1; i++) {
400663b5d112SKonstantin Belousov 				bzero(&ups[i], sizeof(*ups));
400763b5d112SKonstantin Belousov 				if (kz->uk_flags & UMA_ZFLAG_INTERNAL ||
400863b5d112SKonstantin Belousov 				    CPU_ABSENT(i))
400963b5d112SKonstantin Belousov 					continue;
40107a52a97eSRobert Watson 				cache = &z->uz_cpu[i];
40117a52a97eSRobert Watson 				if (cache->uc_allocbucket != NULL)
401263b5d112SKonstantin Belousov 					ups[i].ups_cache_free +=
40137a52a97eSRobert Watson 					    cache->uc_allocbucket->ub_cnt;
40147a52a97eSRobert Watson 				if (cache->uc_freebucket != NULL)
401563b5d112SKonstantin Belousov 					ups[i].ups_cache_free +=
40167a52a97eSRobert Watson 					    cache->uc_freebucket->ub_cnt;
401763b5d112SKonstantin Belousov 				ups[i].ups_allocs = cache->uc_allocs;
401863b5d112SKonstantin Belousov 				ups[i].ups_frees = cache->uc_frees;
40197a52a97eSRobert Watson 			}
40202450bbb8SRobert Watson 			ZONE_UNLOCK(z);
402163b5d112SKonstantin Belousov 			(void)sbuf_bcat(&sbuf, &uth, sizeof(uth));
402263b5d112SKonstantin Belousov 			for (i = 0; i < mp_maxid + 1; i++)
402363b5d112SKonstantin Belousov 				(void)sbuf_bcat(&sbuf, &ups[i], sizeof(ups[i]));
40247a52a97eSRobert Watson 		}
40257a52a97eSRobert Watson 	}
4026111fbcd5SBryan Venteicher 	rw_runlock(&uma_rwlock);
40274e657159SMatthew D Fleming 	error = sbuf_finish(&sbuf);
40284e657159SMatthew D Fleming 	sbuf_delete(&sbuf);
402963b5d112SKonstantin Belousov 	free(ups, M_TEMP);
40307a52a97eSRobert Watson 	return (error);
40317a52a97eSRobert Watson }
403248c5777eSRobert Watson 
40330a5a3ccbSGleb Smirnoff int
40340a5a3ccbSGleb Smirnoff sysctl_handle_uma_zone_max(SYSCTL_HANDLER_ARGS)
40350a5a3ccbSGleb Smirnoff {
40360a5a3ccbSGleb Smirnoff 	uma_zone_t zone = *(uma_zone_t *)arg1;
403716be9f54SGleb Smirnoff 	int error, max;
40380a5a3ccbSGleb Smirnoff 
403916be9f54SGleb Smirnoff 	max = uma_zone_get_max(zone);
40400a5a3ccbSGleb Smirnoff 	error = sysctl_handle_int(oidp, &max, 0, req);
40410a5a3ccbSGleb Smirnoff 	if (error || !req->newptr)
40420a5a3ccbSGleb Smirnoff 		return (error);
40430a5a3ccbSGleb Smirnoff 
40440a5a3ccbSGleb Smirnoff 	uma_zone_set_max(zone, max);
40450a5a3ccbSGleb Smirnoff 
40460a5a3ccbSGleb Smirnoff 	return (0);
40470a5a3ccbSGleb Smirnoff }
40480a5a3ccbSGleb Smirnoff 
40490a5a3ccbSGleb Smirnoff int
40500a5a3ccbSGleb Smirnoff sysctl_handle_uma_zone_cur(SYSCTL_HANDLER_ARGS)
40510a5a3ccbSGleb Smirnoff {
40520a5a3ccbSGleb Smirnoff 	uma_zone_t zone = *(uma_zone_t *)arg1;
40530a5a3ccbSGleb Smirnoff 	int cur;
40540a5a3ccbSGleb Smirnoff 
40550a5a3ccbSGleb Smirnoff 	cur = uma_zone_get_cur(zone);
40560a5a3ccbSGleb Smirnoff 	return (sysctl_handle_int(oidp, &cur, 0, req));
40570a5a3ccbSGleb Smirnoff }
40580a5a3ccbSGleb Smirnoff 
40599542ea7bSGleb Smirnoff #ifdef INVARIANTS
40609542ea7bSGleb Smirnoff static uma_slab_t
40619542ea7bSGleb Smirnoff uma_dbg_getslab(uma_zone_t zone, void *item)
40629542ea7bSGleb Smirnoff {
40639542ea7bSGleb Smirnoff 	uma_slab_t slab;
40649542ea7bSGleb Smirnoff 	uma_keg_t keg;
40659542ea7bSGleb Smirnoff 	uint8_t *mem;
40669542ea7bSGleb Smirnoff 
40679542ea7bSGleb Smirnoff 	mem = (uint8_t *)((uintptr_t)item & (~UMA_SLAB_MASK));
40689542ea7bSGleb Smirnoff 	if (zone->uz_flags & UMA_ZONE_VTOSLAB) {
40699542ea7bSGleb Smirnoff 		slab = vtoslab((vm_offset_t)mem);
40709542ea7bSGleb Smirnoff 	} else {
40719542ea7bSGleb Smirnoff 		/*
40729542ea7bSGleb Smirnoff 		 * It is safe to return the slab here even though the
40739542ea7bSGleb Smirnoff 		 * zone is unlocked because the item's allocation state
40749542ea7bSGleb Smirnoff 		 * essentially holds a reference.
40759542ea7bSGleb Smirnoff 		 */
40769542ea7bSGleb Smirnoff 		ZONE_LOCK(zone);
40779542ea7bSGleb Smirnoff 		keg = LIST_FIRST(&zone->uz_kegs)->kl_keg;
40789542ea7bSGleb Smirnoff 		if (keg->uk_flags & UMA_ZONE_HASH)
40799542ea7bSGleb Smirnoff 			slab = hash_sfind(&keg->uk_hash, mem);
40809542ea7bSGleb Smirnoff 		else
40819542ea7bSGleb Smirnoff 			slab = (uma_slab_t)(mem + keg->uk_pgoff);
40829542ea7bSGleb Smirnoff 		ZONE_UNLOCK(zone);
40839542ea7bSGleb Smirnoff 	}
40849542ea7bSGleb Smirnoff 
40859542ea7bSGleb Smirnoff 	return (slab);
40869542ea7bSGleb Smirnoff }
40879542ea7bSGleb Smirnoff 
4088c5deaf04SGleb Smirnoff static bool
4089c5deaf04SGleb Smirnoff uma_dbg_zskip(uma_zone_t zone, void *mem)
4090c5deaf04SGleb Smirnoff {
4091c5deaf04SGleb Smirnoff 	uma_keg_t keg;
4092c5deaf04SGleb Smirnoff 
4093c5deaf04SGleb Smirnoff 	if ((keg = zone_first_keg(zone)) == NULL)
4094c5deaf04SGleb Smirnoff 		return (true);
4095c5deaf04SGleb Smirnoff 
4096c5deaf04SGleb Smirnoff 	return (uma_dbg_kskip(keg, mem));
4097c5deaf04SGleb Smirnoff }
4098c5deaf04SGleb Smirnoff 
4099c5deaf04SGleb Smirnoff static bool
4100c5deaf04SGleb Smirnoff uma_dbg_kskip(uma_keg_t keg, void *mem)
4101c5deaf04SGleb Smirnoff {
4102c5deaf04SGleb Smirnoff 	uintptr_t idx;
4103c5deaf04SGleb Smirnoff 
4104c5deaf04SGleb Smirnoff 	if (dbg_divisor == 0)
4105c5deaf04SGleb Smirnoff 		return (true);
4106c5deaf04SGleb Smirnoff 
4107c5deaf04SGleb Smirnoff 	if (dbg_divisor == 1)
4108c5deaf04SGleb Smirnoff 		return (false);
4109c5deaf04SGleb Smirnoff 
4110c5deaf04SGleb Smirnoff 	idx = (uintptr_t)mem >> PAGE_SHIFT;
4111c5deaf04SGleb Smirnoff 	if (keg->uk_ipers > 1) {
4112c5deaf04SGleb Smirnoff 		idx *= keg->uk_ipers;
4113c5deaf04SGleb Smirnoff 		idx += ((uintptr_t)mem & PAGE_MASK) / keg->uk_rsize;
4114c5deaf04SGleb Smirnoff 	}
4115c5deaf04SGleb Smirnoff 
4116c5deaf04SGleb Smirnoff 	if ((idx / dbg_divisor) * dbg_divisor != idx) {
4117c5deaf04SGleb Smirnoff 		counter_u64_add(uma_skip_cnt, 1);
4118c5deaf04SGleb Smirnoff 		return (true);
4119c5deaf04SGleb Smirnoff 	}
4120c5deaf04SGleb Smirnoff 	counter_u64_add(uma_dbg_cnt, 1);
4121c5deaf04SGleb Smirnoff 
4122c5deaf04SGleb Smirnoff 	return (false);
4123c5deaf04SGleb Smirnoff }
4124c5deaf04SGleb Smirnoff 
41259542ea7bSGleb Smirnoff /*
41269542ea7bSGleb Smirnoff  * Set up the slab's freei data such that uma_dbg_free can function.
41279542ea7bSGleb Smirnoff  *
41289542ea7bSGleb Smirnoff  */
41299542ea7bSGleb Smirnoff static void
41309542ea7bSGleb Smirnoff uma_dbg_alloc(uma_zone_t zone, uma_slab_t slab, void *item)
41319542ea7bSGleb Smirnoff {
41329542ea7bSGleb Smirnoff 	uma_keg_t keg;
41339542ea7bSGleb Smirnoff 	int freei;
41349542ea7bSGleb Smirnoff 
41359542ea7bSGleb Smirnoff 	if (slab == NULL) {
41369542ea7bSGleb Smirnoff 		slab = uma_dbg_getslab(zone, item);
41379542ea7bSGleb Smirnoff 		if (slab == NULL)
41389542ea7bSGleb Smirnoff 			panic("uma: item %p did not belong to zone %s\n",
41399542ea7bSGleb Smirnoff 			    item, zone->uz_name);
41409542ea7bSGleb Smirnoff 	}
41419542ea7bSGleb Smirnoff 	keg = slab->us_keg;
41429542ea7bSGleb Smirnoff 	freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize;
41439542ea7bSGleb Smirnoff 
41449542ea7bSGleb Smirnoff 	if (BIT_ISSET(SLAB_SETSIZE, freei, &slab->us_debugfree))
41459542ea7bSGleb Smirnoff 		panic("Duplicate alloc of %p from zone %p(%s) slab %p(%d)\n",
41469542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
41479542ea7bSGleb Smirnoff 	BIT_SET_ATOMIC(SLAB_SETSIZE, freei, &slab->us_debugfree);
41489542ea7bSGleb Smirnoff 
41499542ea7bSGleb Smirnoff 	return;
41509542ea7bSGleb Smirnoff }
41519542ea7bSGleb Smirnoff 
41529542ea7bSGleb Smirnoff /*
41539542ea7bSGleb Smirnoff  * Verifies freed addresses.  Checks for alignment, valid slab membership
41549542ea7bSGleb Smirnoff  * and duplicate frees.
41559542ea7bSGleb Smirnoff  *
41569542ea7bSGleb Smirnoff  */
41579542ea7bSGleb Smirnoff static void
41589542ea7bSGleb Smirnoff uma_dbg_free(uma_zone_t zone, uma_slab_t slab, void *item)
41599542ea7bSGleb Smirnoff {
41609542ea7bSGleb Smirnoff 	uma_keg_t keg;
41619542ea7bSGleb Smirnoff 	int freei;
41629542ea7bSGleb Smirnoff 
41639542ea7bSGleb Smirnoff 	if (slab == NULL) {
41649542ea7bSGleb Smirnoff 		slab = uma_dbg_getslab(zone, item);
41659542ea7bSGleb Smirnoff 		if (slab == NULL)
41669542ea7bSGleb Smirnoff 			panic("uma: Freed item %p did not belong to zone %s\n",
41679542ea7bSGleb Smirnoff 			    item, zone->uz_name);
41689542ea7bSGleb Smirnoff 	}
41699542ea7bSGleb Smirnoff 	keg = slab->us_keg;
41709542ea7bSGleb Smirnoff 	freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize;
41719542ea7bSGleb Smirnoff 
41729542ea7bSGleb Smirnoff 	if (freei >= keg->uk_ipers)
41739542ea7bSGleb Smirnoff 		panic("Invalid free of %p from zone %p(%s) slab %p(%d)\n",
41749542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
41759542ea7bSGleb Smirnoff 
41769542ea7bSGleb Smirnoff 	if (((freei * keg->uk_rsize) + slab->us_data) != item)
41779542ea7bSGleb Smirnoff 		panic("Unaligned free of %p from zone %p(%s) slab %p(%d)\n",
41789542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
41799542ea7bSGleb Smirnoff 
41809542ea7bSGleb Smirnoff 	if (!BIT_ISSET(SLAB_SETSIZE, freei, &slab->us_debugfree))
41819542ea7bSGleb Smirnoff 		panic("Duplicate free of %p from zone %p(%s) slab %p(%d)\n",
41829542ea7bSGleb Smirnoff 		    item, zone, zone->uz_name, slab, freei);
41839542ea7bSGleb Smirnoff 
41849542ea7bSGleb Smirnoff 	BIT_CLR_ATOMIC(SLAB_SETSIZE, freei, &slab->us_debugfree);
41859542ea7bSGleb Smirnoff }
41869542ea7bSGleb Smirnoff #endif /* INVARIANTS */
41879542ea7bSGleb Smirnoff 
418848c5777eSRobert Watson #ifdef DDB
418948c5777eSRobert Watson DB_SHOW_COMMAND(uma, db_show_uma)
419048c5777eSRobert Watson {
419148c5777eSRobert Watson 	uma_bucket_t bucket;
419248c5777eSRobert Watson 	uma_keg_t kz;
419348c5777eSRobert Watson 	uma_zone_t z;
4194ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
4195ab3185d1SJeff Roberson 	uint64_t allocs, frees, sleeps;
4196ab3185d1SJeff Roberson 	int cachefree, i;
419748c5777eSRobert Watson 
419803175483SAlexander Motin 	db_printf("%18s %8s %8s %8s %12s %8s %8s\n", "Zone", "Size", "Used",
419903175483SAlexander Motin 	    "Free", "Requests", "Sleeps", "Bucket");
420048c5777eSRobert Watson 	LIST_FOREACH(kz, &uma_kegs, uk_link) {
420148c5777eSRobert Watson 		LIST_FOREACH(z, &kz->uk_zones, uz_link) {
420248c5777eSRobert Watson 			if (kz->uk_flags & UMA_ZFLAG_INTERNAL) {
420348c5777eSRobert Watson 				allocs = z->uz_allocs;
420448c5777eSRobert Watson 				frees = z->uz_frees;
4205bf965959SSean Bruno 				sleeps = z->uz_sleeps;
420648c5777eSRobert Watson 				cachefree = 0;
420748c5777eSRobert Watson 			} else
420848c5777eSRobert Watson 				uma_zone_sumstat(z, &cachefree, &allocs,
4209bf965959SSean Bruno 				    &frees, &sleeps);
4210e20a199fSJeff Roberson 			if (!((z->uz_flags & UMA_ZONE_SECONDARY) &&
421148c5777eSRobert Watson 			    (LIST_FIRST(&kz->uk_zones) != z)))
421248c5777eSRobert Watson 				cachefree += kz->uk_free;
4213ab3185d1SJeff Roberson 			for (i = 0; i < vm_ndomains; i++) {
4214ab3185d1SJeff Roberson 				zdom = &z->uz_domain[i];
4215ab3185d1SJeff Roberson 				LIST_FOREACH(bucket, &zdom->uzd_buckets,
4216ab3185d1SJeff Roberson 				    ub_link)
421748c5777eSRobert Watson 					cachefree += bucket->ub_cnt;
4218ab3185d1SJeff Roberson 			}
421903175483SAlexander Motin 			db_printf("%18s %8ju %8jd %8d %12ju %8ju %8u\n",
422003175483SAlexander Motin 			    z->uz_name, (uintmax_t)kz->uk_size,
4221ae4e9636SRobert Watson 			    (intmax_t)(allocs - frees), cachefree,
422203175483SAlexander Motin 			    (uintmax_t)allocs, sleeps, z->uz_count);
4223687c94aaSJohn Baldwin 			if (db_pager_quit)
4224687c94aaSJohn Baldwin 				return;
422548c5777eSRobert Watson 		}
422648c5777eSRobert Watson 	}
422748c5777eSRobert Watson }
422803175483SAlexander Motin 
422903175483SAlexander Motin DB_SHOW_COMMAND(umacache, db_show_umacache)
423003175483SAlexander Motin {
423103175483SAlexander Motin 	uma_bucket_t bucket;
423203175483SAlexander Motin 	uma_zone_t z;
4233ab3185d1SJeff Roberson 	uma_zone_domain_t zdom;
4234ab3185d1SJeff Roberson 	uint64_t allocs, frees;
4235ab3185d1SJeff Roberson 	int cachefree, i;
423603175483SAlexander Motin 
423703175483SAlexander Motin 	db_printf("%18s %8s %8s %8s %12s %8s\n", "Zone", "Size", "Used", "Free",
423803175483SAlexander Motin 	    "Requests", "Bucket");
423903175483SAlexander Motin 	LIST_FOREACH(z, &uma_cachezones, uz_link) {
424003175483SAlexander Motin 		uma_zone_sumstat(z, &cachefree, &allocs, &frees, NULL);
4241ab3185d1SJeff Roberson 		for (i = 0; i < vm_ndomains; i++) {
4242ab3185d1SJeff Roberson 			zdom = &z->uz_domain[i];
4243ab3185d1SJeff Roberson 			LIST_FOREACH(bucket, &zdom->uzd_buckets, ub_link)
424403175483SAlexander Motin 				cachefree += bucket->ub_cnt;
4245ab3185d1SJeff Roberson 		}
424603175483SAlexander Motin 		db_printf("%18s %8ju %8jd %8d %12ju %8u\n",
424703175483SAlexander Motin 		    z->uz_name, (uintmax_t)z->uz_size,
424803175483SAlexander Motin 		    (intmax_t)(allocs - frees), cachefree,
424903175483SAlexander Motin 		    (uintmax_t)allocs, z->uz_count);
425003175483SAlexander Motin 		if (db_pager_quit)
425103175483SAlexander Motin 			return;
425203175483SAlexander Motin 	}
425303175483SAlexander Motin }
42549542ea7bSGleb Smirnoff #endif	/* DDB */
4255