160727d8bSWarner Losh /*- 2e20a199fSJeff Roberson * Copyright (c) 2002-2005, 2009 Jeffrey Roberson <jeff@FreeBSD.org> 308ecce74SRobert Watson * Copyright (c) 2004, 2005 Bosko Milekic <bmilekic@FreeBSD.org> 4ae4e9636SRobert Watson * Copyright (c) 2004-2006 Robert N. M. Watson 508ecce74SRobert Watson * All rights reserved. 68355f576SJeff Roberson * 78355f576SJeff Roberson * Redistribution and use in source and binary forms, with or without 88355f576SJeff Roberson * modification, are permitted provided that the following conditions 98355f576SJeff Roberson * are met: 108355f576SJeff Roberson * 1. Redistributions of source code must retain the above copyright 118355f576SJeff Roberson * notice unmodified, this list of conditions, and the following 128355f576SJeff Roberson * disclaimer. 138355f576SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 148355f576SJeff Roberson * notice, this list of conditions and the following disclaimer in the 158355f576SJeff Roberson * documentation and/or other materials provided with the distribution. 168355f576SJeff Roberson * 178355f576SJeff Roberson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 188355f576SJeff Roberson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 198355f576SJeff Roberson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 208355f576SJeff Roberson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 218355f576SJeff Roberson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 228355f576SJeff Roberson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 238355f576SJeff Roberson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 248355f576SJeff Roberson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 258355f576SJeff Roberson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 268355f576SJeff Roberson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 278355f576SJeff Roberson */ 288355f576SJeff Roberson 298355f576SJeff Roberson /* 308355f576SJeff Roberson * uma_core.c Implementation of the Universal Memory allocator 318355f576SJeff Roberson * 328355f576SJeff Roberson * This allocator is intended to replace the multitude of similar object caches 338355f576SJeff Roberson * in the standard FreeBSD kernel. The intent is to be flexible as well as 348355f576SJeff Roberson * effecient. A primary design goal is to return unused memory to the rest of 358355f576SJeff Roberson * the system. This will make the system as a whole more flexible due to the 368355f576SJeff Roberson * ability to move memory to subsystems which most need it instead of leaving 378355f576SJeff Roberson * pools of reserved memory unused. 388355f576SJeff Roberson * 398355f576SJeff Roberson * The basic ideas stem from similar slab/zone based allocators whose algorithms 408355f576SJeff Roberson * are well known. 418355f576SJeff Roberson * 428355f576SJeff Roberson */ 438355f576SJeff Roberson 448355f576SJeff Roberson /* 458355f576SJeff Roberson * TODO: 468355f576SJeff Roberson * - Improve memory usage for large allocations 478355f576SJeff Roberson * - Investigate cache size adjustments 488355f576SJeff Roberson */ 498355f576SJeff Roberson 50874651b1SDavid E. O'Brien #include <sys/cdefs.h> 51874651b1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 52874651b1SDavid E. O'Brien 538355f576SJeff Roberson /* I should really use ktr.. */ 548355f576SJeff Roberson /* 558355f576SJeff Roberson #define UMA_DEBUG 1 568355f576SJeff Roberson #define UMA_DEBUG_ALLOC 1 578355f576SJeff Roberson #define UMA_DEBUG_ALLOC_1 1 588355f576SJeff Roberson */ 598355f576SJeff Roberson 6048c5777eSRobert Watson #include "opt_ddb.h" 618355f576SJeff Roberson #include "opt_param.h" 6248c5777eSRobert Watson 638355f576SJeff Roberson #include <sys/param.h> 648355f576SJeff Roberson #include <sys/systm.h> 658355f576SJeff Roberson #include <sys/kernel.h> 668355f576SJeff Roberson #include <sys/types.h> 678355f576SJeff Roberson #include <sys/queue.h> 688355f576SJeff Roberson #include <sys/malloc.h> 693659f747SRobert Watson #include <sys/ktr.h> 708355f576SJeff Roberson #include <sys/lock.h> 718355f576SJeff Roberson #include <sys/sysctl.h> 728355f576SJeff Roberson #include <sys/mutex.h> 734c1cc01cSJohn Baldwin #include <sys/proc.h> 747a52a97eSRobert Watson #include <sys/sbuf.h> 758355f576SJeff Roberson #include <sys/smp.h> 7686bbae32SJeff Roberson #include <sys/vmmeter.h> 7786bbae32SJeff Roberson 788355f576SJeff Roberson #include <vm/vm.h> 798355f576SJeff Roberson #include <vm/vm_object.h> 808355f576SJeff Roberson #include <vm/vm_page.h> 818355f576SJeff Roberson #include <vm/vm_param.h> 828355f576SJeff Roberson #include <vm/vm_map.h> 838355f576SJeff Roberson #include <vm/vm_kern.h> 848355f576SJeff Roberson #include <vm/vm_extern.h> 858355f576SJeff Roberson #include <vm/uma.h> 868355f576SJeff Roberson #include <vm/uma_int.h> 87639c9550SJeff Roberson #include <vm/uma_dbg.h> 888355f576SJeff Roberson 8948eea375SJeff Roberson #include <machine/vmparam.h> 9048eea375SJeff Roberson 9148c5777eSRobert Watson #include <ddb/ddb.h> 9248c5777eSRobert Watson 938355f576SJeff Roberson /* 94099a0e58SBosko Milekic * This is the zone and keg from which all zones are spawned. The idea is that 95099a0e58SBosko Milekic * even the zone & keg heads are allocated from the allocator, so we use the 96099a0e58SBosko Milekic * bss section to bootstrap us. 978355f576SJeff Roberson */ 98099a0e58SBosko Milekic static struct uma_keg masterkeg; 99099a0e58SBosko Milekic static struct uma_zone masterzone_k; 100099a0e58SBosko Milekic static struct uma_zone masterzone_z; 101099a0e58SBosko Milekic static uma_zone_t kegs = &masterzone_k; 102099a0e58SBosko Milekic static uma_zone_t zones = &masterzone_z; 1038355f576SJeff Roberson 1048355f576SJeff Roberson /* This is the zone from which all of uma_slab_t's are allocated. */ 1058355f576SJeff Roberson static uma_zone_t slabzone; 106099a0e58SBosko Milekic static uma_zone_t slabrefzone; /* With refcounters (for UMA_ZONE_REFCNT) */ 1078355f576SJeff Roberson 1088355f576SJeff Roberson /* 1098355f576SJeff Roberson * The initial hash tables come out of this zone so they can be allocated 1108355f576SJeff Roberson * prior to malloc coming up. 1118355f576SJeff Roberson */ 1128355f576SJeff Roberson static uma_zone_t hashzone; 1138355f576SJeff Roberson 1141e319f6dSRobert Watson /* The boot-time adjusted value for cache line alignment. */ 115e20a199fSJeff Roberson static int uma_align_cache = 64 - 1; 1161e319f6dSRobert Watson 117961647dfSJeff Roberson static MALLOC_DEFINE(M_UMAHASH, "UMAHash", "UMA Hash Buckets"); 118961647dfSJeff Roberson 1198355f576SJeff Roberson /* 12086bbae32SJeff Roberson * Are we allowed to allocate buckets? 12186bbae32SJeff Roberson */ 12286bbae32SJeff Roberson static int bucketdisable = 1; 12386bbae32SJeff Roberson 124099a0e58SBosko Milekic /* Linked list of all kegs in the system */ 12513e403fdSAntoine Brodin static LIST_HEAD(,uma_keg) uma_kegs = LIST_HEAD_INITIALIZER(uma_kegs); 1268355f576SJeff Roberson 127099a0e58SBosko Milekic /* This mutex protects the keg list */ 1288355f576SJeff Roberson static struct mtx uma_mtx; 1298355f576SJeff Roberson 1308355f576SJeff Roberson /* Linked list of boot time pages */ 1318355f576SJeff Roberson static LIST_HEAD(,uma_slab) uma_boot_pages = 13213e403fdSAntoine Brodin LIST_HEAD_INITIALIZER(uma_boot_pages); 1338355f576SJeff Roberson 134f353d338SAlan Cox /* This mutex protects the boot time pages list */ 135f353d338SAlan Cox static struct mtx uma_boot_pages_mtx; 1368355f576SJeff Roberson 1378355f576SJeff Roberson /* Is the VM done starting up? */ 1388355f576SJeff Roberson static int booted = 0; 1398355f576SJeff Roberson 140244f4554SBosko Milekic /* Maximum number of allowed items-per-slab if the slab header is OFFPAGE */ 141244f4554SBosko Milekic static u_int uma_max_ipers; 142244f4554SBosko Milekic static u_int uma_max_ipers_ref; 143244f4554SBosko Milekic 1449643769aSJeff Roberson /* 1459643769aSJeff Roberson * This is the handle used to schedule events that need to happen 1469643769aSJeff Roberson * outside of the allocation fast path. 1479643769aSJeff Roberson */ 1488355f576SJeff Roberson static struct callout uma_callout; 1499643769aSJeff Roberson #define UMA_TIMEOUT 20 /* Seconds for callout interval. */ 1508355f576SJeff Roberson 1518355f576SJeff Roberson /* 1528355f576SJeff Roberson * This structure is passed as the zone ctor arg so that I don't have to create 1538355f576SJeff Roberson * a special allocation function just for zones. 1548355f576SJeff Roberson */ 1558355f576SJeff Roberson struct uma_zctor_args { 1568355f576SJeff Roberson char *name; 157c3bdc05fSAndrew R. Reiter size_t size; 1588355f576SJeff Roberson uma_ctor ctor; 1598355f576SJeff Roberson uma_dtor dtor; 1608355f576SJeff Roberson uma_init uminit; 1618355f576SJeff Roberson uma_fini fini; 162099a0e58SBosko Milekic uma_keg_t keg; 163099a0e58SBosko Milekic int align; 1642018f30cSMike Silbersack u_int32_t flags; 165099a0e58SBosko Milekic }; 166099a0e58SBosko Milekic 167099a0e58SBosko Milekic struct uma_kctor_args { 168099a0e58SBosko Milekic uma_zone_t zone; 169099a0e58SBosko Milekic size_t size; 170099a0e58SBosko Milekic uma_init uminit; 171099a0e58SBosko Milekic uma_fini fini; 1728355f576SJeff Roberson int align; 1732018f30cSMike Silbersack u_int32_t flags; 1748355f576SJeff Roberson }; 1758355f576SJeff Roberson 176cae33c14SJeff Roberson struct uma_bucket_zone { 177cae33c14SJeff Roberson uma_zone_t ubz_zone; 178cae33c14SJeff Roberson char *ubz_name; 179cae33c14SJeff Roberson int ubz_entries; 180cae33c14SJeff Roberson }; 181cae33c14SJeff Roberson 182cae33c14SJeff Roberson #define BUCKET_MAX 128 183cae33c14SJeff Roberson 184cae33c14SJeff Roberson struct uma_bucket_zone bucket_zones[] = { 185cae33c14SJeff Roberson { NULL, "16 Bucket", 16 }, 186cae33c14SJeff Roberson { NULL, "32 Bucket", 32 }, 187cae33c14SJeff Roberson { NULL, "64 Bucket", 64 }, 188cae33c14SJeff Roberson { NULL, "128 Bucket", 128 }, 189cae33c14SJeff Roberson { NULL, NULL, 0} 190cae33c14SJeff Roberson }; 191cae33c14SJeff Roberson 192cae33c14SJeff Roberson #define BUCKET_SHIFT 4 193cae33c14SJeff Roberson #define BUCKET_ZONES ((BUCKET_MAX >> BUCKET_SHIFT) + 1) 194cae33c14SJeff Roberson 195f9d27e75SRobert Watson /* 196f9d27e75SRobert Watson * bucket_size[] maps requested bucket sizes to zones that allocate a bucket 197f9d27e75SRobert Watson * of approximately the right size. 198f9d27e75SRobert Watson */ 199f9d27e75SRobert Watson static uint8_t bucket_size[BUCKET_ZONES]; 200cae33c14SJeff Roberson 2012019094aSRobert Watson /* 2022019094aSRobert Watson * Flags and enumerations to be passed to internal functions. 2032019094aSRobert Watson */ 204b23f72e9SBrian Feldman enum zfreeskip { SKIP_NONE, SKIP_DTOR, SKIP_FINI }; 205b23f72e9SBrian Feldman 2062019094aSRobert Watson #define ZFREE_STATFAIL 0x00000001 /* Update zone failure statistic. */ 207f4ff923bSRobert Watson #define ZFREE_STATFREE 0x00000002 /* Update zone free statistic. */ 2082019094aSRobert Watson 2098355f576SJeff Roberson /* Prototypes.. */ 2108355f576SJeff Roberson 2118355f576SJeff Roberson static void *obj_alloc(uma_zone_t, int, u_int8_t *, int); 2128355f576SJeff Roberson static void *page_alloc(uma_zone_t, int, u_int8_t *, int); 213009b6fcbSJeff Roberson static void *startup_alloc(uma_zone_t, int, u_int8_t *, int); 2148355f576SJeff Roberson static void page_free(void *, int, u_int8_t); 215e20a199fSJeff Roberson static uma_slab_t keg_alloc_slab(uma_keg_t, uma_zone_t, int); 2169643769aSJeff Roberson static void cache_drain(uma_zone_t); 2178355f576SJeff Roberson static void bucket_drain(uma_zone_t, uma_bucket_t); 218aaa8bb16SJeff Roberson static void bucket_cache_drain(uma_zone_t zone); 219b23f72e9SBrian Feldman static int keg_ctor(void *, int, void *, int); 220099a0e58SBosko Milekic static void keg_dtor(void *, int, void *); 221b23f72e9SBrian Feldman static int zone_ctor(void *, int, void *, int); 2229c2cd7e5SJeff Roberson static void zone_dtor(void *, int, void *); 223b23f72e9SBrian Feldman static int zero_init(void *, int, int); 224e20a199fSJeff Roberson static void keg_small_init(uma_keg_t keg); 225e20a199fSJeff Roberson static void keg_large_init(uma_keg_t keg); 2268355f576SJeff Roberson static void zone_foreach(void (*zfunc)(uma_zone_t)); 2278355f576SJeff Roberson static void zone_timeout(uma_zone_t zone); 2280aef6126SJeff Roberson static int hash_alloc(struct uma_hash *); 2290aef6126SJeff Roberson static int hash_expand(struct uma_hash *, struct uma_hash *); 2300aef6126SJeff Roberson static void hash_free(struct uma_hash *hash); 2318355f576SJeff Roberson static void uma_timeout(void *); 2328355f576SJeff Roberson static void uma_startup3(void); 233e20a199fSJeff Roberson static void *zone_alloc_item(uma_zone_t, void *, int); 234e20a199fSJeff Roberson static void zone_free_item(uma_zone_t, void *, void *, enum zfreeskip, 2352019094aSRobert Watson int); 23686bbae32SJeff Roberson static void bucket_enable(void); 237cae33c14SJeff Roberson static void bucket_init(void); 238cae33c14SJeff Roberson static uma_bucket_t bucket_alloc(int, int); 239cae33c14SJeff Roberson static void bucket_free(uma_bucket_t); 240cae33c14SJeff Roberson static void bucket_zone_drain(void); 241e20a199fSJeff Roberson static int zone_alloc_bucket(uma_zone_t zone, int flags); 242e20a199fSJeff Roberson static uma_slab_t zone_fetch_slab(uma_zone_t zone, uma_keg_t last, int flags); 243e20a199fSJeff Roberson static uma_slab_t zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int flags); 244e20a199fSJeff Roberson static void *slab_alloc_item(uma_zone_t zone, uma_slab_t slab); 245e20a199fSJeff Roberson static uma_keg_t uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, 2462018f30cSMike Silbersack uma_fini fini, int align, u_int32_t flags); 247e20a199fSJeff Roberson static inline void zone_relock(uma_zone_t zone, uma_keg_t keg); 248e20a199fSJeff Roberson static inline void keg_relock(uma_keg_t keg, uma_zone_t zone); 249bbee39c6SJeff Roberson 2508355f576SJeff Roberson void uma_print_zone(uma_zone_t); 2518355f576SJeff Roberson void uma_print_stats(void); 2527a52a97eSRobert Watson static int sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS); 2537a52a97eSRobert Watson static int sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS); 2548355f576SJeff Roberson 2558355f576SJeff Roberson SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL); 2568355f576SJeff Roberson 2577a52a97eSRobert Watson SYSCTL_PROC(_vm, OID_AUTO, zone_count, CTLFLAG_RD|CTLTYPE_INT, 2587a52a97eSRobert Watson 0, 0, sysctl_vm_zone_count, "I", "Number of UMA zones"); 2597a52a97eSRobert Watson 2607a52a97eSRobert Watson SYSCTL_PROC(_vm, OID_AUTO, zone_stats, CTLFLAG_RD|CTLTYPE_STRUCT, 2617a52a97eSRobert Watson 0, 0, sysctl_vm_zone_stats, "s,struct uma_type_header", "Zone Stats"); 2627a52a97eSRobert Watson 26386bbae32SJeff Roberson /* 26486bbae32SJeff Roberson * This routine checks to see whether or not it's safe to enable buckets. 26586bbae32SJeff Roberson */ 26686bbae32SJeff Roberson 26786bbae32SJeff Roberson static void 26886bbae32SJeff Roberson bucket_enable(void) 26986bbae32SJeff Roberson { 2702feb50bfSAttilio Rao if (cnt.v_free_count < cnt.v_free_min) 27186bbae32SJeff Roberson bucketdisable = 1; 27286bbae32SJeff Roberson else 27386bbae32SJeff Roberson bucketdisable = 0; 27486bbae32SJeff Roberson } 27586bbae32SJeff Roberson 276dc2c7965SRobert Watson /* 277dc2c7965SRobert Watson * Initialize bucket_zones, the array of zones of buckets of various sizes. 278dc2c7965SRobert Watson * 279dc2c7965SRobert Watson * For each zone, calculate the memory required for each bucket, consisting 280dc2c7965SRobert Watson * of the header and an array of pointers. Initialize bucket_size[] to point 281dc2c7965SRobert Watson * the range of appropriate bucket sizes at the zone. 282dc2c7965SRobert Watson */ 283cae33c14SJeff Roberson static void 284cae33c14SJeff Roberson bucket_init(void) 285cae33c14SJeff Roberson { 286cae33c14SJeff Roberson struct uma_bucket_zone *ubz; 287cae33c14SJeff Roberson int i; 288cae33c14SJeff Roberson int j; 289cae33c14SJeff Roberson 290cae33c14SJeff Roberson for (i = 0, j = 0; bucket_zones[j].ubz_entries != 0; j++) { 291cae33c14SJeff Roberson int size; 292cae33c14SJeff Roberson 293cae33c14SJeff Roberson ubz = &bucket_zones[j]; 294cae33c14SJeff Roberson size = roundup(sizeof(struct uma_bucket), sizeof(void *)); 295cae33c14SJeff Roberson size += sizeof(void *) * ubz->ubz_entries; 296cae33c14SJeff Roberson ubz->ubz_zone = uma_zcreate(ubz->ubz_name, size, 297e20a199fSJeff Roberson NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 298e20a199fSJeff Roberson UMA_ZFLAG_INTERNAL | UMA_ZFLAG_BUCKET); 299cae33c14SJeff Roberson for (; i <= ubz->ubz_entries; i += (1 << BUCKET_SHIFT)) 300cae33c14SJeff Roberson bucket_size[i >> BUCKET_SHIFT] = j; 301cae33c14SJeff Roberson } 302cae33c14SJeff Roberson } 303cae33c14SJeff Roberson 304dc2c7965SRobert Watson /* 305dc2c7965SRobert Watson * Given a desired number of entries for a bucket, return the zone from which 306dc2c7965SRobert Watson * to allocate the bucket. 307dc2c7965SRobert Watson */ 308dc2c7965SRobert Watson static struct uma_bucket_zone * 309dc2c7965SRobert Watson bucket_zone_lookup(int entries) 310dc2c7965SRobert Watson { 311dc2c7965SRobert Watson int idx; 312dc2c7965SRobert Watson 313dc2c7965SRobert Watson idx = howmany(entries, 1 << BUCKET_SHIFT); 314dc2c7965SRobert Watson return (&bucket_zones[bucket_size[idx]]); 315dc2c7965SRobert Watson } 316dc2c7965SRobert Watson 317cae33c14SJeff Roberson static uma_bucket_t 318cae33c14SJeff Roberson bucket_alloc(int entries, int bflags) 319cae33c14SJeff Roberson { 320cae33c14SJeff Roberson struct uma_bucket_zone *ubz; 321cae33c14SJeff Roberson uma_bucket_t bucket; 322cae33c14SJeff Roberson 323cae33c14SJeff Roberson /* 324cae33c14SJeff Roberson * This is to stop us from allocating per cpu buckets while we're 3253803b26bSDag-Erling Smørgrav * running out of vm.boot_pages. Otherwise, we would exhaust the 326cae33c14SJeff Roberson * boot pages. This also prevents us from allocating buckets in 327cae33c14SJeff Roberson * low memory situations. 328cae33c14SJeff Roberson */ 329cae33c14SJeff Roberson if (bucketdisable) 330cae33c14SJeff Roberson return (NULL); 331dc2c7965SRobert Watson 332dc2c7965SRobert Watson ubz = bucket_zone_lookup(entries); 333e20a199fSJeff Roberson bucket = zone_alloc_item(ubz->ubz_zone, NULL, bflags); 334cae33c14SJeff Roberson if (bucket) { 335cae33c14SJeff Roberson #ifdef INVARIANTS 336cae33c14SJeff Roberson bzero(bucket->ub_bucket, sizeof(void *) * ubz->ubz_entries); 337cae33c14SJeff Roberson #endif 338cae33c14SJeff Roberson bucket->ub_cnt = 0; 339cae33c14SJeff Roberson bucket->ub_entries = ubz->ubz_entries; 340cae33c14SJeff Roberson } 341cae33c14SJeff Roberson 342cae33c14SJeff Roberson return (bucket); 343cae33c14SJeff Roberson } 344cae33c14SJeff Roberson 345cae33c14SJeff Roberson static void 346cae33c14SJeff Roberson bucket_free(uma_bucket_t bucket) 347cae33c14SJeff Roberson { 348cae33c14SJeff Roberson struct uma_bucket_zone *ubz; 349cae33c14SJeff Roberson 350dc2c7965SRobert Watson ubz = bucket_zone_lookup(bucket->ub_entries); 351e20a199fSJeff Roberson zone_free_item(ubz->ubz_zone, bucket, NULL, SKIP_NONE, 352f4ff923bSRobert Watson ZFREE_STATFREE); 353cae33c14SJeff Roberson } 354cae33c14SJeff Roberson 355cae33c14SJeff Roberson static void 356cae33c14SJeff Roberson bucket_zone_drain(void) 357cae33c14SJeff Roberson { 358cae33c14SJeff Roberson struct uma_bucket_zone *ubz; 359cae33c14SJeff Roberson 360cae33c14SJeff Roberson for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++) 361cae33c14SJeff Roberson zone_drain(ubz->ubz_zone); 362cae33c14SJeff Roberson } 363cae33c14SJeff Roberson 364e20a199fSJeff Roberson static inline uma_keg_t 365e20a199fSJeff Roberson zone_first_keg(uma_zone_t zone) 366e20a199fSJeff Roberson { 367e20a199fSJeff Roberson 368e20a199fSJeff Roberson return (LIST_FIRST(&zone->uz_kegs)->kl_keg); 369e20a199fSJeff Roberson } 370e20a199fSJeff Roberson 371e20a199fSJeff Roberson static void 372e20a199fSJeff Roberson zone_foreach_keg(uma_zone_t zone, void (*kegfn)(uma_keg_t)) 373e20a199fSJeff Roberson { 374e20a199fSJeff Roberson uma_klink_t klink; 375e20a199fSJeff Roberson 376e20a199fSJeff Roberson LIST_FOREACH(klink, &zone->uz_kegs, kl_link) 377e20a199fSJeff Roberson kegfn(klink->kl_keg); 378e20a199fSJeff Roberson } 3798355f576SJeff Roberson 3808355f576SJeff Roberson /* 3818355f576SJeff Roberson * Routine called by timeout which is used to fire off some time interval 3829643769aSJeff Roberson * based calculations. (stats, hash size, etc.) 3838355f576SJeff Roberson * 3848355f576SJeff Roberson * Arguments: 3858355f576SJeff Roberson * arg Unused 3868355f576SJeff Roberson * 3878355f576SJeff Roberson * Returns: 3888355f576SJeff Roberson * Nothing 3898355f576SJeff Roberson */ 3908355f576SJeff Roberson static void 3918355f576SJeff Roberson uma_timeout(void *unused) 3928355f576SJeff Roberson { 39386bbae32SJeff Roberson bucket_enable(); 3948355f576SJeff Roberson zone_foreach(zone_timeout); 3958355f576SJeff Roberson 3968355f576SJeff Roberson /* Reschedule this event */ 3979643769aSJeff Roberson callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); 3988355f576SJeff Roberson } 3998355f576SJeff Roberson 4008355f576SJeff Roberson /* 4019643769aSJeff Roberson * Routine to perform timeout driven calculations. This expands the 4029643769aSJeff Roberson * hashes and does per cpu statistics aggregation. 4038355f576SJeff Roberson * 404e20a199fSJeff Roberson * Returns nothing. 4058355f576SJeff Roberson */ 4068355f576SJeff Roberson static void 407e20a199fSJeff Roberson keg_timeout(uma_keg_t keg) 4088355f576SJeff Roberson { 4098355f576SJeff Roberson 410e20a199fSJeff Roberson KEG_LOCK(keg); 4118355f576SJeff Roberson /* 412e20a199fSJeff Roberson * Expand the keg hash table. 4138355f576SJeff Roberson * 4148355f576SJeff Roberson * This is done if the number of slabs is larger than the hash size. 4158355f576SJeff Roberson * What I'm trying to do here is completely reduce collisions. This 4168355f576SJeff Roberson * may be a little aggressive. Should I allow for two collisions max? 4178355f576SJeff Roberson */ 418099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_HASH && 419099a0e58SBosko Milekic keg->uk_pages / keg->uk_ppera >= keg->uk_hash.uh_hashsize) { 4200aef6126SJeff Roberson struct uma_hash newhash; 4210aef6126SJeff Roberson struct uma_hash oldhash; 4220aef6126SJeff Roberson int ret; 4235300d9ddSJeff Roberson 4240aef6126SJeff Roberson /* 4250aef6126SJeff Roberson * This is so involved because allocating and freeing 426e20a199fSJeff Roberson * while the keg lock is held will lead to deadlock. 4270aef6126SJeff Roberson * I have to do everything in stages and check for 4280aef6126SJeff Roberson * races. 4290aef6126SJeff Roberson */ 430099a0e58SBosko Milekic newhash = keg->uk_hash; 431e20a199fSJeff Roberson KEG_UNLOCK(keg); 4320aef6126SJeff Roberson ret = hash_alloc(&newhash); 433e20a199fSJeff Roberson KEG_LOCK(keg); 4340aef6126SJeff Roberson if (ret) { 435099a0e58SBosko Milekic if (hash_expand(&keg->uk_hash, &newhash)) { 436099a0e58SBosko Milekic oldhash = keg->uk_hash; 437099a0e58SBosko Milekic keg->uk_hash = newhash; 4380aef6126SJeff Roberson } else 4390aef6126SJeff Roberson oldhash = newhash; 4400aef6126SJeff Roberson 441e20a199fSJeff Roberson KEG_UNLOCK(keg); 4420aef6126SJeff Roberson hash_free(&oldhash); 443e20a199fSJeff Roberson KEG_LOCK(keg); 4440aef6126SJeff Roberson } 4455300d9ddSJeff Roberson } 446e20a199fSJeff Roberson KEG_UNLOCK(keg); 447e20a199fSJeff Roberson } 448e20a199fSJeff Roberson 449e20a199fSJeff Roberson static void 450e20a199fSJeff Roberson zone_timeout(uma_zone_t zone) 451e20a199fSJeff Roberson { 452e20a199fSJeff Roberson 453e20a199fSJeff Roberson zone_foreach_keg(zone, &keg_timeout); 4548355f576SJeff Roberson } 4558355f576SJeff Roberson 4568355f576SJeff Roberson /* 4575300d9ddSJeff Roberson * Allocate and zero fill the next sized hash table from the appropriate 4585300d9ddSJeff Roberson * backing store. 4595300d9ddSJeff Roberson * 4605300d9ddSJeff Roberson * Arguments: 4610aef6126SJeff Roberson * hash A new hash structure with the old hash size in uh_hashsize 4625300d9ddSJeff Roberson * 4635300d9ddSJeff Roberson * Returns: 4640aef6126SJeff Roberson * 1 on sucess and 0 on failure. 4655300d9ddSJeff Roberson */ 46637c84183SPoul-Henning Kamp static int 4670aef6126SJeff Roberson hash_alloc(struct uma_hash *hash) 4685300d9ddSJeff Roberson { 4690aef6126SJeff Roberson int oldsize; 4705300d9ddSJeff Roberson int alloc; 4715300d9ddSJeff Roberson 4720aef6126SJeff Roberson oldsize = hash->uh_hashsize; 4730aef6126SJeff Roberson 4745300d9ddSJeff Roberson /* We're just going to go to a power of two greater */ 4750aef6126SJeff Roberson if (oldsize) { 4760aef6126SJeff Roberson hash->uh_hashsize = oldsize * 2; 4770aef6126SJeff Roberson alloc = sizeof(hash->uh_slab_hash[0]) * hash->uh_hashsize; 4780aef6126SJeff Roberson hash->uh_slab_hash = (struct slabhead *)malloc(alloc, 479961647dfSJeff Roberson M_UMAHASH, M_NOWAIT); 4805300d9ddSJeff Roberson } else { 4810aef6126SJeff Roberson alloc = sizeof(hash->uh_slab_hash[0]) * UMA_HASH_SIZE_INIT; 482e20a199fSJeff Roberson hash->uh_slab_hash = zone_alloc_item(hashzone, NULL, 483a163d034SWarner Losh M_WAITOK); 4840aef6126SJeff Roberson hash->uh_hashsize = UMA_HASH_SIZE_INIT; 4855300d9ddSJeff Roberson } 4860aef6126SJeff Roberson if (hash->uh_slab_hash) { 4870aef6126SJeff Roberson bzero(hash->uh_slab_hash, alloc); 4880aef6126SJeff Roberson hash->uh_hashmask = hash->uh_hashsize - 1; 4890aef6126SJeff Roberson return (1); 4900aef6126SJeff Roberson } 4915300d9ddSJeff Roberson 4920aef6126SJeff Roberson return (0); 4935300d9ddSJeff Roberson } 4945300d9ddSJeff Roberson 4955300d9ddSJeff Roberson /* 49664f051e9SJeff Roberson * Expands the hash table for HASH zones. This is done from zone_timeout 49764f051e9SJeff Roberson * to reduce collisions. This must not be done in the regular allocation 49864f051e9SJeff Roberson * path, otherwise, we can recurse on the vm while allocating pages. 4998355f576SJeff Roberson * 5008355f576SJeff Roberson * Arguments: 5010aef6126SJeff Roberson * oldhash The hash you want to expand 5020aef6126SJeff Roberson * newhash The hash structure for the new table 5038355f576SJeff Roberson * 5048355f576SJeff Roberson * Returns: 5058355f576SJeff Roberson * Nothing 5068355f576SJeff Roberson * 5078355f576SJeff Roberson * Discussion: 5088355f576SJeff Roberson */ 5090aef6126SJeff Roberson static int 5100aef6126SJeff Roberson hash_expand(struct uma_hash *oldhash, struct uma_hash *newhash) 5118355f576SJeff Roberson { 5128355f576SJeff Roberson uma_slab_t slab; 5138355f576SJeff Roberson int hval; 5148355f576SJeff Roberson int i; 5158355f576SJeff Roberson 5160aef6126SJeff Roberson if (!newhash->uh_slab_hash) 5170aef6126SJeff Roberson return (0); 5188355f576SJeff Roberson 5190aef6126SJeff Roberson if (oldhash->uh_hashsize >= newhash->uh_hashsize) 5200aef6126SJeff Roberson return (0); 5218355f576SJeff Roberson 5228355f576SJeff Roberson /* 5238355f576SJeff Roberson * I need to investigate hash algorithms for resizing without a 5248355f576SJeff Roberson * full rehash. 5258355f576SJeff Roberson */ 5268355f576SJeff Roberson 5270aef6126SJeff Roberson for (i = 0; i < oldhash->uh_hashsize; i++) 5280aef6126SJeff Roberson while (!SLIST_EMPTY(&oldhash->uh_slab_hash[i])) { 5290aef6126SJeff Roberson slab = SLIST_FIRST(&oldhash->uh_slab_hash[i]); 5300aef6126SJeff Roberson SLIST_REMOVE_HEAD(&oldhash->uh_slab_hash[i], us_hlink); 5310aef6126SJeff Roberson hval = UMA_HASH(newhash, slab->us_data); 5320aef6126SJeff Roberson SLIST_INSERT_HEAD(&newhash->uh_slab_hash[hval], 5330aef6126SJeff Roberson slab, us_hlink); 5348355f576SJeff Roberson } 5358355f576SJeff Roberson 5360aef6126SJeff Roberson return (1); 5379c2cd7e5SJeff Roberson } 5389c2cd7e5SJeff Roberson 5395300d9ddSJeff Roberson /* 5405300d9ddSJeff Roberson * Free the hash bucket to the appropriate backing store. 5415300d9ddSJeff Roberson * 5425300d9ddSJeff Roberson * Arguments: 5435300d9ddSJeff Roberson * slab_hash The hash bucket we're freeing 5445300d9ddSJeff Roberson * hashsize The number of entries in that hash bucket 5455300d9ddSJeff Roberson * 5465300d9ddSJeff Roberson * Returns: 5475300d9ddSJeff Roberson * Nothing 5485300d9ddSJeff Roberson */ 5499c2cd7e5SJeff Roberson static void 5500aef6126SJeff Roberson hash_free(struct uma_hash *hash) 5519c2cd7e5SJeff Roberson { 5520aef6126SJeff Roberson if (hash->uh_slab_hash == NULL) 5530aef6126SJeff Roberson return; 5540aef6126SJeff Roberson if (hash->uh_hashsize == UMA_HASH_SIZE_INIT) 555e20a199fSJeff Roberson zone_free_item(hashzone, 556f4ff923bSRobert Watson hash->uh_slab_hash, NULL, SKIP_NONE, ZFREE_STATFREE); 5578355f576SJeff Roberson else 558961647dfSJeff Roberson free(hash->uh_slab_hash, M_UMAHASH); 5598355f576SJeff Roberson } 5608355f576SJeff Roberson 5618355f576SJeff Roberson /* 5628355f576SJeff Roberson * Frees all outstanding items in a bucket 5638355f576SJeff Roberson * 5648355f576SJeff Roberson * Arguments: 5658355f576SJeff Roberson * zone The zone to free to, must be unlocked. 5668355f576SJeff Roberson * bucket The free/alloc bucket with items, cpu queue must be locked. 5678355f576SJeff Roberson * 5688355f576SJeff Roberson * Returns: 5698355f576SJeff Roberson * Nothing 5708355f576SJeff Roberson */ 5718355f576SJeff Roberson 5728355f576SJeff Roberson static void 5738355f576SJeff Roberson bucket_drain(uma_zone_t zone, uma_bucket_t bucket) 5748355f576SJeff Roberson { 5758355f576SJeff Roberson void *item; 5768355f576SJeff Roberson 5778355f576SJeff Roberson if (bucket == NULL) 5788355f576SJeff Roberson return; 5798355f576SJeff Roberson 580cae33c14SJeff Roberson while (bucket->ub_cnt > 0) { 581cae33c14SJeff Roberson bucket->ub_cnt--; 582cae33c14SJeff Roberson item = bucket->ub_bucket[bucket->ub_cnt]; 5838355f576SJeff Roberson #ifdef INVARIANTS 584cae33c14SJeff Roberson bucket->ub_bucket[bucket->ub_cnt] = NULL; 5858355f576SJeff Roberson KASSERT(item != NULL, 5868355f576SJeff Roberson ("bucket_drain: botched ptr, item is NULL")); 5878355f576SJeff Roberson #endif 588e20a199fSJeff Roberson zone_free_item(zone, item, NULL, SKIP_DTOR, 0); 5898355f576SJeff Roberson } 5908355f576SJeff Roberson } 5918355f576SJeff Roberson 5928355f576SJeff Roberson /* 5938355f576SJeff Roberson * Drains the per cpu caches for a zone. 5948355f576SJeff Roberson * 5955d1ae027SRobert Watson * NOTE: This may only be called while the zone is being turn down, and not 5965d1ae027SRobert Watson * during normal operation. This is necessary in order that we do not have 5975d1ae027SRobert Watson * to migrate CPUs to drain the per-CPU caches. 5985d1ae027SRobert Watson * 5998355f576SJeff Roberson * Arguments: 6008355f576SJeff Roberson * zone The zone to drain, must be unlocked. 6018355f576SJeff Roberson * 6028355f576SJeff Roberson * Returns: 6038355f576SJeff Roberson * Nothing 6048355f576SJeff Roberson */ 6058355f576SJeff Roberson static void 6069643769aSJeff Roberson cache_drain(uma_zone_t zone) 6078355f576SJeff Roberson { 6088355f576SJeff Roberson uma_cache_t cache; 6098355f576SJeff Roberson int cpu; 6108355f576SJeff Roberson 6118355f576SJeff Roberson /* 6125d1ae027SRobert Watson * XXX: It is safe to not lock the per-CPU caches, because we're 6135d1ae027SRobert Watson * tearing down the zone anyway. I.e., there will be no further use 6145d1ae027SRobert Watson * of the caches at this point. 6155d1ae027SRobert Watson * 6165d1ae027SRobert Watson * XXX: It would good to be able to assert that the zone is being 6175d1ae027SRobert Watson * torn down to prevent improper use of cache_drain(). 6185d1ae027SRobert Watson * 6195d1ae027SRobert Watson * XXX: We lock the zone before passing into bucket_cache_drain() as 6205d1ae027SRobert Watson * it is used elsewhere. Should the tear-down path be made special 6215d1ae027SRobert Watson * there in some form? 6228355f576SJeff Roberson */ 6233aa6d94eSJohn Baldwin CPU_FOREACH(cpu) { 6248355f576SJeff Roberson cache = &zone->uz_cpu[cpu]; 6258355f576SJeff Roberson bucket_drain(zone, cache->uc_allocbucket); 6268355f576SJeff Roberson bucket_drain(zone, cache->uc_freebucket); 627174ab450SBosko Milekic if (cache->uc_allocbucket != NULL) 628cae33c14SJeff Roberson bucket_free(cache->uc_allocbucket); 629174ab450SBosko Milekic if (cache->uc_freebucket != NULL) 630cae33c14SJeff Roberson bucket_free(cache->uc_freebucket); 631d56368d7SBosko Milekic cache->uc_allocbucket = cache->uc_freebucket = NULL; 632d56368d7SBosko Milekic } 633aaa8bb16SJeff Roberson ZONE_LOCK(zone); 634aaa8bb16SJeff Roberson bucket_cache_drain(zone); 635aaa8bb16SJeff Roberson ZONE_UNLOCK(zone); 636aaa8bb16SJeff Roberson } 637aaa8bb16SJeff Roberson 638aaa8bb16SJeff Roberson /* 639aaa8bb16SJeff Roberson * Drain the cached buckets from a zone. Expects a locked zone on entry. 640aaa8bb16SJeff Roberson */ 641aaa8bb16SJeff Roberson static void 642aaa8bb16SJeff Roberson bucket_cache_drain(uma_zone_t zone) 643aaa8bb16SJeff Roberson { 644aaa8bb16SJeff Roberson uma_bucket_t bucket; 6458355f576SJeff Roberson 6468355f576SJeff Roberson /* 6478355f576SJeff Roberson * Drain the bucket queues and free the buckets, we just keep two per 6488355f576SJeff Roberson * cpu (alloc/free). 6498355f576SJeff Roberson */ 6508355f576SJeff Roberson while ((bucket = LIST_FIRST(&zone->uz_full_bucket)) != NULL) { 6518355f576SJeff Roberson LIST_REMOVE(bucket, ub_link); 6528355f576SJeff Roberson ZONE_UNLOCK(zone); 6538355f576SJeff Roberson bucket_drain(zone, bucket); 654cae33c14SJeff Roberson bucket_free(bucket); 6558355f576SJeff Roberson ZONE_LOCK(zone); 6568355f576SJeff Roberson } 6578355f576SJeff Roberson 6588355f576SJeff Roberson /* Now we do the free queue.. */ 6598355f576SJeff Roberson while ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) { 6608355f576SJeff Roberson LIST_REMOVE(bucket, ub_link); 661cae33c14SJeff Roberson bucket_free(bucket); 6628355f576SJeff Roberson } 6638355f576SJeff Roberson } 6648355f576SJeff Roberson 6658355f576SJeff Roberson /* 666e20a199fSJeff Roberson * Frees pages from a keg back to the system. This is done on demand from 6678355f576SJeff Roberson * the pageout daemon. 6688355f576SJeff Roberson * 669e20a199fSJeff Roberson * Returns nothing. 6708355f576SJeff Roberson */ 671e20a199fSJeff Roberson static void 672e20a199fSJeff Roberson keg_drain(uma_keg_t keg) 6738355f576SJeff Roberson { 6741e183df2SStefan Farfeleder struct slabhead freeslabs = { 0 }; 6758355f576SJeff Roberson uma_slab_t slab; 6768355f576SJeff Roberson uma_slab_t n; 6778355f576SJeff Roberson u_int8_t flags; 6788355f576SJeff Roberson u_int8_t *mem; 6798355f576SJeff Roberson int i; 6808355f576SJeff Roberson 6818355f576SJeff Roberson /* 682e20a199fSJeff Roberson * We don't want to take pages from statically allocated kegs at this 6838355f576SJeff Roberson * time 6848355f576SJeff Roberson */ 685099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL) 6868355f576SJeff Roberson return; 6878355f576SJeff Roberson 6888355f576SJeff Roberson #ifdef UMA_DEBUG 689e20a199fSJeff Roberson printf("%s free items: %u\n", keg->uk_name, keg->uk_free); 6908355f576SJeff Roberson #endif 691e20a199fSJeff Roberson KEG_LOCK(keg); 692099a0e58SBosko Milekic if (keg->uk_free == 0) 6938355f576SJeff Roberson goto finished; 6948355f576SJeff Roberson 695099a0e58SBosko Milekic slab = LIST_FIRST(&keg->uk_free_slab); 6969643769aSJeff Roberson while (slab) { 6978355f576SJeff Roberson n = LIST_NEXT(slab, us_link); 6988355f576SJeff Roberson 6998355f576SJeff Roberson /* We have no where to free these to */ 7008355f576SJeff Roberson if (slab->us_flags & UMA_SLAB_BOOT) { 7018355f576SJeff Roberson slab = n; 7028355f576SJeff Roberson continue; 7038355f576SJeff Roberson } 7048355f576SJeff Roberson 7058355f576SJeff Roberson LIST_REMOVE(slab, us_link); 706099a0e58SBosko Milekic keg->uk_pages -= keg->uk_ppera; 707099a0e58SBosko Milekic keg->uk_free -= keg->uk_ipers; 708713deb36SJeff Roberson 709099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_HASH) 710099a0e58SBosko Milekic UMA_HASH_REMOVE(&keg->uk_hash, slab, slab->us_data); 711713deb36SJeff Roberson 712713deb36SJeff Roberson SLIST_INSERT_HEAD(&freeslabs, slab, us_hlink); 713713deb36SJeff Roberson 714713deb36SJeff Roberson slab = n; 715713deb36SJeff Roberson } 716713deb36SJeff Roberson finished: 717e20a199fSJeff Roberson KEG_UNLOCK(keg); 718713deb36SJeff Roberson 719713deb36SJeff Roberson while ((slab = SLIST_FIRST(&freeslabs)) != NULL) { 720713deb36SJeff Roberson SLIST_REMOVE(&freeslabs, slab, uma_slab, us_hlink); 721099a0e58SBosko Milekic if (keg->uk_fini) 722099a0e58SBosko Milekic for (i = 0; i < keg->uk_ipers; i++) 723099a0e58SBosko Milekic keg->uk_fini( 724099a0e58SBosko Milekic slab->us_data + (keg->uk_rsize * i), 725099a0e58SBosko Milekic keg->uk_size); 7268355f576SJeff Roberson flags = slab->us_flags; 7278355f576SJeff Roberson mem = slab->us_data; 72899571dc3SJeff Roberson 729e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZONE_VTOSLAB) { 730b70458aeSAlan Cox vm_object_t obj; 731b70458aeSAlan Cox 732b70458aeSAlan Cox if (flags & UMA_SLAB_KMEM) 733b70458aeSAlan Cox obj = kmem_object; 7347630c265SAlan Cox else if (flags & UMA_SLAB_KERNEL) 7357630c265SAlan Cox obj = kernel_object; 736b70458aeSAlan Cox else 737b70458aeSAlan Cox obj = NULL; 738099a0e58SBosko Milekic for (i = 0; i < keg->uk_ppera; i++) 73999571dc3SJeff Roberson vsetobj((vm_offset_t)mem + (i * PAGE_SIZE), 740b70458aeSAlan Cox obj); 74148eea375SJeff Roberson } 742099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_OFFPAGE) 743e20a199fSJeff Roberson zone_free_item(keg->uk_slabzone, slab, NULL, 744f4ff923bSRobert Watson SKIP_NONE, ZFREE_STATFREE); 7458355f576SJeff Roberson #ifdef UMA_DEBUG 7468355f576SJeff Roberson printf("%s: Returning %d bytes.\n", 747e20a199fSJeff Roberson keg->uk_name, UMA_SLAB_SIZE * keg->uk_ppera); 7488355f576SJeff Roberson #endif 749099a0e58SBosko Milekic keg->uk_freef(mem, UMA_SLAB_SIZE * keg->uk_ppera, flags); 7508355f576SJeff Roberson } 7518355f576SJeff Roberson } 7528355f576SJeff Roberson 753e20a199fSJeff Roberson static void 754e20a199fSJeff Roberson zone_drain_wait(uma_zone_t zone, int waitok) 755e20a199fSJeff Roberson { 756e20a199fSJeff Roberson 7578355f576SJeff Roberson /* 758e20a199fSJeff Roberson * Set draining to interlock with zone_dtor() so we can release our 759e20a199fSJeff Roberson * locks as we go. Only dtor() should do a WAITOK call since it 760e20a199fSJeff Roberson * is the only call that knows the structure will still be available 761e20a199fSJeff Roberson * when it wakes up. 762e20a199fSJeff Roberson */ 763e20a199fSJeff Roberson ZONE_LOCK(zone); 764e20a199fSJeff Roberson while (zone->uz_flags & UMA_ZFLAG_DRAINING) { 765e20a199fSJeff Roberson if (waitok == M_NOWAIT) 766e20a199fSJeff Roberson goto out; 767e20a199fSJeff Roberson mtx_unlock(&uma_mtx); 768e20a199fSJeff Roberson msleep(zone, zone->uz_lock, PVM, "zonedrain", 1); 769e20a199fSJeff Roberson mtx_lock(&uma_mtx); 770e20a199fSJeff Roberson } 771e20a199fSJeff Roberson zone->uz_flags |= UMA_ZFLAG_DRAINING; 772e20a199fSJeff Roberson bucket_cache_drain(zone); 773e20a199fSJeff Roberson ZONE_UNLOCK(zone); 774e20a199fSJeff Roberson /* 775e20a199fSJeff Roberson * The DRAINING flag protects us from being freed while 776e20a199fSJeff Roberson * we're running. Normally the uma_mtx would protect us but we 777e20a199fSJeff Roberson * must be able to release and acquire the right lock for each keg. 778e20a199fSJeff Roberson */ 779e20a199fSJeff Roberson zone_foreach_keg(zone, &keg_drain); 780e20a199fSJeff Roberson ZONE_LOCK(zone); 781e20a199fSJeff Roberson zone->uz_flags &= ~UMA_ZFLAG_DRAINING; 782e20a199fSJeff Roberson wakeup(zone); 783e20a199fSJeff Roberson out: 784e20a199fSJeff Roberson ZONE_UNLOCK(zone); 785e20a199fSJeff Roberson } 786e20a199fSJeff Roberson 787e20a199fSJeff Roberson void 788e20a199fSJeff Roberson zone_drain(uma_zone_t zone) 789e20a199fSJeff Roberson { 790e20a199fSJeff Roberson 791e20a199fSJeff Roberson zone_drain_wait(zone, M_NOWAIT); 792e20a199fSJeff Roberson } 793e20a199fSJeff Roberson 794e20a199fSJeff Roberson /* 795e20a199fSJeff Roberson * Allocate a new slab for a keg. This does not insert the slab onto a list. 7968355f576SJeff Roberson * 7978355f576SJeff Roberson * Arguments: 7988355f576SJeff Roberson * wait Shall we wait? 7998355f576SJeff Roberson * 8008355f576SJeff Roberson * Returns: 8018355f576SJeff Roberson * The slab that was allocated or NULL if there is no memory and the 8028355f576SJeff Roberson * caller specified M_NOWAIT. 8038355f576SJeff Roberson */ 8048355f576SJeff Roberson static uma_slab_t 805e20a199fSJeff Roberson keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int wait) 8068355f576SJeff Roberson { 807099a0e58SBosko Milekic uma_slabrefcnt_t slabref; 808e20a199fSJeff Roberson uma_alloc allocf; 809099a0e58SBosko Milekic uma_slab_t slab; 8108355f576SJeff Roberson u_int8_t *mem; 8118355f576SJeff Roberson u_int8_t flags; 8128355f576SJeff Roberson int i; 8138355f576SJeff Roberson 814e20a199fSJeff Roberson mtx_assert(&keg->uk_lock, MA_OWNED); 815a553d4b8SJeff Roberson slab = NULL; 816a553d4b8SJeff Roberson 8178355f576SJeff Roberson #ifdef UMA_DEBUG 818e20a199fSJeff Roberson printf("slab_zalloc: Allocating a new slab for %s\n", keg->uk_name); 8198355f576SJeff Roberson #endif 820e20a199fSJeff Roberson allocf = keg->uk_allocf; 821e20a199fSJeff Roberson KEG_UNLOCK(keg); 822a553d4b8SJeff Roberson 823099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_OFFPAGE) { 824e20a199fSJeff Roberson slab = zone_alloc_item(keg->uk_slabzone, NULL, wait); 825a553d4b8SJeff Roberson if (slab == NULL) { 826e20a199fSJeff Roberson KEG_LOCK(keg); 827a553d4b8SJeff Roberson return NULL; 828a553d4b8SJeff Roberson } 829a553d4b8SJeff Roberson } 830a553d4b8SJeff Roberson 8313370c5bfSJeff Roberson /* 8323370c5bfSJeff Roberson * This reproduces the old vm_zone behavior of zero filling pages the 8333370c5bfSJeff Roberson * first time they are added to a zone. 8343370c5bfSJeff Roberson * 8353370c5bfSJeff Roberson * Malloced items are zeroed in uma_zalloc. 8363370c5bfSJeff Roberson */ 8373370c5bfSJeff Roberson 838099a0e58SBosko Milekic if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0) 8393370c5bfSJeff Roberson wait |= M_ZERO; 8403370c5bfSJeff Roberson else 8413370c5bfSJeff Roberson wait &= ~M_ZERO; 8423370c5bfSJeff Roberson 843e20a199fSJeff Roberson /* zone is passed for legacy reasons. */ 844e20a199fSJeff Roberson mem = allocf(zone, keg->uk_ppera * UMA_SLAB_SIZE, &flags, wait); 845a553d4b8SJeff Roberson if (mem == NULL) { 846b23f72e9SBrian Feldman if (keg->uk_flags & UMA_ZONE_OFFPAGE) 847e20a199fSJeff Roberson zone_free_item(keg->uk_slabzone, slab, NULL, 848f4ff923bSRobert Watson SKIP_NONE, ZFREE_STATFREE); 849e20a199fSJeff Roberson KEG_LOCK(keg); 8508355f576SJeff Roberson return (NULL); 851a553d4b8SJeff Roberson } 8528355f576SJeff Roberson 8535c0e403bSJeff Roberson /* Point the slab into the allocated memory */ 854099a0e58SBosko Milekic if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) 855099a0e58SBosko Milekic slab = (uma_slab_t )(mem + keg->uk_pgoff); 8565c0e403bSJeff Roberson 857e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZONE_VTOSLAB) 858099a0e58SBosko Milekic for (i = 0; i < keg->uk_ppera; i++) 85999571dc3SJeff Roberson vsetslab((vm_offset_t)mem + (i * PAGE_SIZE), slab); 8608355f576SJeff Roberson 861099a0e58SBosko Milekic slab->us_keg = keg; 8628355f576SJeff Roberson slab->us_data = mem; 863099a0e58SBosko Milekic slab->us_freecount = keg->uk_ipers; 8648355f576SJeff Roberson slab->us_firstfree = 0; 8658355f576SJeff Roberson slab->us_flags = flags; 8668355f576SJeff Roberson 867099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_REFCNT) { 868099a0e58SBosko Milekic slabref = (uma_slabrefcnt_t)slab; 869ab14a3f7SBrian Feldman for (i = 0; i < keg->uk_ipers; i++) { 870099a0e58SBosko Milekic slabref->us_freelist[i].us_refcnt = 0; 871ab14a3f7SBrian Feldman slabref->us_freelist[i].us_item = i+1; 872ab14a3f7SBrian Feldman } 873ab14a3f7SBrian Feldman } else { 874ab14a3f7SBrian Feldman for (i = 0; i < keg->uk_ipers; i++) 875ab14a3f7SBrian Feldman slab->us_freelist[i].us_item = i+1; 876099a0e58SBosko Milekic } 877099a0e58SBosko Milekic 878b23f72e9SBrian Feldman if (keg->uk_init != NULL) { 879099a0e58SBosko Milekic for (i = 0; i < keg->uk_ipers; i++) 880b23f72e9SBrian Feldman if (keg->uk_init(slab->us_data + (keg->uk_rsize * i), 881b23f72e9SBrian Feldman keg->uk_size, wait) != 0) 882b23f72e9SBrian Feldman break; 883b23f72e9SBrian Feldman if (i != keg->uk_ipers) { 884b23f72e9SBrian Feldman if (keg->uk_fini != NULL) { 885b23f72e9SBrian Feldman for (i--; i > -1; i--) 886b23f72e9SBrian Feldman keg->uk_fini(slab->us_data + 887b23f72e9SBrian Feldman (keg->uk_rsize * i), 888099a0e58SBosko Milekic keg->uk_size); 889b23f72e9SBrian Feldman } 890e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZONE_VTOSLAB) { 891b70458aeSAlan Cox vm_object_t obj; 892b70458aeSAlan Cox 893b70458aeSAlan Cox if (flags & UMA_SLAB_KMEM) 894b70458aeSAlan Cox obj = kmem_object; 8957630c265SAlan Cox else if (flags & UMA_SLAB_KERNEL) 8967630c265SAlan Cox obj = kernel_object; 897b70458aeSAlan Cox else 898b70458aeSAlan Cox obj = NULL; 899b23f72e9SBrian Feldman for (i = 0; i < keg->uk_ppera; i++) 900b23f72e9SBrian Feldman vsetobj((vm_offset_t)mem + 901b70458aeSAlan Cox (i * PAGE_SIZE), obj); 902b70458aeSAlan Cox } 903b23f72e9SBrian Feldman if (keg->uk_flags & UMA_ZONE_OFFPAGE) 904e20a199fSJeff Roberson zone_free_item(keg->uk_slabzone, slab, 905f4ff923bSRobert Watson NULL, SKIP_NONE, ZFREE_STATFREE); 906b23f72e9SBrian Feldman keg->uk_freef(mem, UMA_SLAB_SIZE * keg->uk_ppera, 907b23f72e9SBrian Feldman flags); 908e20a199fSJeff Roberson KEG_LOCK(keg); 909b23f72e9SBrian Feldman return (NULL); 910b23f72e9SBrian Feldman } 911b23f72e9SBrian Feldman } 912e20a199fSJeff Roberson KEG_LOCK(keg); 9135c0e403bSJeff Roberson 914099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_HASH) 915099a0e58SBosko Milekic UMA_HASH_INSERT(&keg->uk_hash, slab, mem); 9168355f576SJeff Roberson 917099a0e58SBosko Milekic keg->uk_pages += keg->uk_ppera; 918099a0e58SBosko Milekic keg->uk_free += keg->uk_ipers; 9198355f576SJeff Roberson 9208355f576SJeff Roberson return (slab); 9218355f576SJeff Roberson } 9228355f576SJeff Roberson 9238355f576SJeff Roberson /* 924009b6fcbSJeff Roberson * This function is intended to be used early on in place of page_alloc() so 925009b6fcbSJeff Roberson * that we may use the boot time page cache to satisfy allocations before 926009b6fcbSJeff Roberson * the VM is ready. 927009b6fcbSJeff Roberson */ 928009b6fcbSJeff Roberson static void * 929009b6fcbSJeff Roberson startup_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait) 930009b6fcbSJeff Roberson { 931099a0e58SBosko Milekic uma_keg_t keg; 932f353d338SAlan Cox uma_slab_t tmps; 933099a0e58SBosko Milekic 934e20a199fSJeff Roberson keg = zone_first_keg(zone); 935099a0e58SBosko Milekic 936009b6fcbSJeff Roberson /* 937009b6fcbSJeff Roberson * Check our small startup cache to see if it has pages remaining. 938009b6fcbSJeff Roberson */ 939f353d338SAlan Cox mtx_lock(&uma_boot_pages_mtx); 940f353d338SAlan Cox if ((tmps = LIST_FIRST(&uma_boot_pages)) != NULL) { 941009b6fcbSJeff Roberson LIST_REMOVE(tmps, us_link); 942f353d338SAlan Cox mtx_unlock(&uma_boot_pages_mtx); 943009b6fcbSJeff Roberson *pflag = tmps->us_flags; 944009b6fcbSJeff Roberson return (tmps->us_data); 945009b6fcbSJeff Roberson } 946f353d338SAlan Cox mtx_unlock(&uma_boot_pages_mtx); 947009b6fcbSJeff Roberson if (booted == 0) 9483803b26bSDag-Erling Smørgrav panic("UMA: Increase vm.boot_pages"); 949009b6fcbSJeff Roberson /* 950009b6fcbSJeff Roberson * Now that we've booted reset these users to their real allocator. 951009b6fcbSJeff Roberson */ 952009b6fcbSJeff Roberson #ifdef UMA_MD_SMALL_ALLOC 953099a0e58SBosko Milekic keg->uk_allocf = uma_small_alloc; 954009b6fcbSJeff Roberson #else 955099a0e58SBosko Milekic keg->uk_allocf = page_alloc; 956009b6fcbSJeff Roberson #endif 957099a0e58SBosko Milekic return keg->uk_allocf(zone, bytes, pflag, wait); 958009b6fcbSJeff Roberson } 959009b6fcbSJeff Roberson 960009b6fcbSJeff Roberson /* 9618355f576SJeff Roberson * Allocates a number of pages from the system 9628355f576SJeff Roberson * 9638355f576SJeff Roberson * Arguments: 9648355f576SJeff Roberson * bytes The number of bytes requested 9658355f576SJeff Roberson * wait Shall we wait? 9668355f576SJeff Roberson * 9678355f576SJeff Roberson * Returns: 9688355f576SJeff Roberson * A pointer to the alloced memory or possibly 9698355f576SJeff Roberson * NULL if M_NOWAIT is set. 9708355f576SJeff Roberson */ 9718355f576SJeff Roberson static void * 9728355f576SJeff Roberson page_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait) 9738355f576SJeff Roberson { 9748355f576SJeff Roberson void *p; /* Returned page */ 9758355f576SJeff Roberson 9768355f576SJeff Roberson *pflag = UMA_SLAB_KMEM; 9778355f576SJeff Roberson p = (void *) kmem_malloc(kmem_map, bytes, wait); 9788355f576SJeff Roberson 9798355f576SJeff Roberson return (p); 9808355f576SJeff Roberson } 9818355f576SJeff Roberson 9828355f576SJeff Roberson /* 9838355f576SJeff Roberson * Allocates a number of pages from within an object 9848355f576SJeff Roberson * 9858355f576SJeff Roberson * Arguments: 9868355f576SJeff Roberson * bytes The number of bytes requested 9878355f576SJeff Roberson * wait Shall we wait? 9888355f576SJeff Roberson * 9898355f576SJeff Roberson * Returns: 9908355f576SJeff Roberson * A pointer to the alloced memory or possibly 9918355f576SJeff Roberson * NULL if M_NOWAIT is set. 9928355f576SJeff Roberson */ 9938355f576SJeff Roberson static void * 9948355f576SJeff Roberson obj_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int wait) 9958355f576SJeff Roberson { 996b245ac95SAlan Cox vm_object_t object; 997b245ac95SAlan Cox vm_offset_t retkva, zkva; 9988355f576SJeff Roberson vm_page_t p; 999b245ac95SAlan Cox int pages, startpages; 1000e20a199fSJeff Roberson uma_keg_t keg; 10018355f576SJeff Roberson 1002e20a199fSJeff Roberson keg = zone_first_keg(zone); 1003e20a199fSJeff Roberson object = keg->uk_obj; 100455f7c614SArchie Cobbs retkva = 0; 10058355f576SJeff Roberson 10068355f576SJeff Roberson /* 100764f051e9SJeff Roberson * This looks a little weird since we're getting one page at a time. 10088355f576SJeff Roberson */ 1009b245ac95SAlan Cox VM_OBJECT_LOCK(object); 1010b245ac95SAlan Cox p = TAILQ_LAST(&object->memq, pglist); 1011b245ac95SAlan Cox pages = p != NULL ? p->pindex + 1 : 0; 1012b245ac95SAlan Cox startpages = pages; 1013e20a199fSJeff Roberson zkva = keg->uk_kva + pages * PAGE_SIZE; 1014b245ac95SAlan Cox for (; bytes > 0; bytes -= PAGE_SIZE) { 1015b245ac95SAlan Cox p = vm_page_alloc(object, pages, 1016b245ac95SAlan Cox VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED); 1017b245ac95SAlan Cox if (p == NULL) { 1018b245ac95SAlan Cox if (pages != startpages) 1019b245ac95SAlan Cox pmap_qremove(retkva, pages - startpages); 1020b245ac95SAlan Cox while (pages != startpages) { 1021b245ac95SAlan Cox pages--; 1022b245ac95SAlan Cox p = TAILQ_LAST(&object->memq, pglist); 1023b245ac95SAlan Cox vm_page_unwire(p, 0); 1024b245ac95SAlan Cox vm_page_free(p); 1025b245ac95SAlan Cox } 1026b245ac95SAlan Cox retkva = 0; 1027b245ac95SAlan Cox goto done; 1028b245ac95SAlan Cox } 1029b245ac95SAlan Cox pmap_qenter(zkva, &p, 1); 103055f7c614SArchie Cobbs if (retkva == 0) 10318355f576SJeff Roberson retkva = zkva; 1032b245ac95SAlan Cox zkva += PAGE_SIZE; 10338355f576SJeff Roberson pages += 1; 10348355f576SJeff Roberson } 1035b245ac95SAlan Cox done: 1036b245ac95SAlan Cox VM_OBJECT_UNLOCK(object); 10378355f576SJeff Roberson *flags = UMA_SLAB_PRIV; 10388355f576SJeff Roberson 10398355f576SJeff Roberson return ((void *)retkva); 10408355f576SJeff Roberson } 10418355f576SJeff Roberson 10428355f576SJeff Roberson /* 10438355f576SJeff Roberson * Frees a number of pages to the system 10448355f576SJeff Roberson * 10458355f576SJeff Roberson * Arguments: 10468355f576SJeff Roberson * mem A pointer to the memory to be freed 10478355f576SJeff Roberson * size The size of the memory being freed 10488355f576SJeff Roberson * flags The original p->us_flags field 10498355f576SJeff Roberson * 10508355f576SJeff Roberson * Returns: 10518355f576SJeff Roberson * Nothing 10528355f576SJeff Roberson */ 10538355f576SJeff Roberson static void 10548355f576SJeff Roberson page_free(void *mem, int size, u_int8_t flags) 10558355f576SJeff Roberson { 10568355f576SJeff Roberson vm_map_t map; 10573370c5bfSJeff Roberson 10588355f576SJeff Roberson if (flags & UMA_SLAB_KMEM) 10598355f576SJeff Roberson map = kmem_map; 1060aea6e893SAlan Cox else if (flags & UMA_SLAB_KERNEL) 1061aea6e893SAlan Cox map = kernel_map; 10628355f576SJeff Roberson else 1063aea6e893SAlan Cox panic("UMA: page_free used with invalid flags %d", flags); 10648355f576SJeff Roberson 10658355f576SJeff Roberson kmem_free(map, (vm_offset_t)mem, size); 10668355f576SJeff Roberson } 10678355f576SJeff Roberson 10688355f576SJeff Roberson /* 10698355f576SJeff Roberson * Zero fill initializer 10708355f576SJeff Roberson * 10718355f576SJeff Roberson * Arguments/Returns follow uma_init specifications 10728355f576SJeff Roberson */ 1073b23f72e9SBrian Feldman static int 1074b23f72e9SBrian Feldman zero_init(void *mem, int size, int flags) 10758355f576SJeff Roberson { 10768355f576SJeff Roberson bzero(mem, size); 1077b23f72e9SBrian Feldman return (0); 10788355f576SJeff Roberson } 10798355f576SJeff Roberson 10808355f576SJeff Roberson /* 1081e20a199fSJeff Roberson * Finish creating a small uma keg. This calculates ipers, and the keg size. 10828355f576SJeff Roberson * 10838355f576SJeff Roberson * Arguments 1084e20a199fSJeff Roberson * keg The zone we should initialize 10858355f576SJeff Roberson * 10868355f576SJeff Roberson * Returns 10878355f576SJeff Roberson * Nothing 10888355f576SJeff Roberson */ 10898355f576SJeff Roberson static void 1090e20a199fSJeff Roberson keg_small_init(uma_keg_t keg) 10918355f576SJeff Roberson { 1092244f4554SBosko Milekic u_int rsize; 1093244f4554SBosko Milekic u_int memused; 1094244f4554SBosko Milekic u_int wastedspace; 1095244f4554SBosko Milekic u_int shsize; 10968355f576SJeff Roberson 1097e20a199fSJeff Roberson KASSERT(keg != NULL, ("Keg is null in keg_small_init")); 1098099a0e58SBosko Milekic rsize = keg->uk_size; 10998355f576SJeff Roberson 11008355f576SJeff Roberson if (rsize < UMA_SMALLEST_UNIT) 11018355f576SJeff Roberson rsize = UMA_SMALLEST_UNIT; 1102099a0e58SBosko Milekic if (rsize & keg->uk_align) 1103099a0e58SBosko Milekic rsize = (rsize & ~keg->uk_align) + (keg->uk_align + 1); 11048355f576SJeff Roberson 1105099a0e58SBosko Milekic keg->uk_rsize = rsize; 1106099a0e58SBosko Milekic keg->uk_ppera = 1; 11078355f576SJeff Roberson 1108244f4554SBosko Milekic if (keg->uk_flags & UMA_ZONE_REFCNT) { 1109244f4554SBosko Milekic rsize += UMA_FRITMREF_SZ; /* linkage & refcnt */ 1110244f4554SBosko Milekic shsize = sizeof(struct uma_slab_refcnt); 1111244f4554SBosko Milekic } else { 1112244f4554SBosko Milekic rsize += UMA_FRITM_SZ; /* Account for linkage */ 1113244f4554SBosko Milekic shsize = sizeof(struct uma_slab); 1114244f4554SBosko Milekic } 11158355f576SJeff Roberson 1116244f4554SBosko Milekic keg->uk_ipers = (UMA_SLAB_SIZE - shsize) / rsize; 1117e20a199fSJeff Roberson KASSERT(keg->uk_ipers != 0, ("keg_small_init: ipers is 0")); 1118244f4554SBosko Milekic memused = keg->uk_ipers * rsize + shsize; 1119244f4554SBosko Milekic wastedspace = UMA_SLAB_SIZE - memused; 1120244f4554SBosko Milekic 112120e8e865SBosko Milekic /* 1122244f4554SBosko Milekic * We can't do OFFPAGE if we're internal or if we've been 112320e8e865SBosko Milekic * asked to not go to the VM for buckets. If we do this we 112420e8e865SBosko Milekic * may end up going to the VM (kmem_map) for slabs which we 112520e8e865SBosko Milekic * do not want to do if we're UMA_ZFLAG_CACHEONLY as a 112620e8e865SBosko Milekic * result of UMA_ZONE_VM, which clearly forbids it. 112720e8e865SBosko Milekic */ 1128099a0e58SBosko Milekic if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) || 1129099a0e58SBosko Milekic (keg->uk_flags & UMA_ZFLAG_CACHEONLY)) 11308355f576SJeff Roberson return; 1131244f4554SBosko Milekic 1132244f4554SBosko Milekic if ((wastedspace >= UMA_MAX_WASTE) && 1133244f4554SBosko Milekic (keg->uk_ipers < (UMA_SLAB_SIZE / keg->uk_rsize))) { 1134244f4554SBosko Milekic keg->uk_ipers = UMA_SLAB_SIZE / keg->uk_rsize; 1135244f4554SBosko Milekic KASSERT(keg->uk_ipers <= 255, 1136e20a199fSJeff Roberson ("keg_small_init: keg->uk_ipers too high!")); 1137244f4554SBosko Milekic #ifdef UMA_DEBUG 1138244f4554SBosko Milekic printf("UMA decided we need offpage slab headers for " 1139e20a199fSJeff Roberson "keg: %s, calculated wastedspace = %d, " 1140244f4554SBosko Milekic "maximum wasted space allowed = %d, " 1141244f4554SBosko Milekic "calculated ipers = %d, " 1142e20a199fSJeff Roberson "new wasted space = %d\n", keg->uk_name, wastedspace, 1143244f4554SBosko Milekic UMA_MAX_WASTE, keg->uk_ipers, 1144244f4554SBosko Milekic UMA_SLAB_SIZE - keg->uk_ipers * keg->uk_rsize); 1145244f4554SBosko Milekic #endif 1146099a0e58SBosko Milekic keg->uk_flags |= UMA_ZONE_OFFPAGE; 1147e20a199fSJeff Roberson if ((keg->uk_flags & UMA_ZONE_VTOSLAB) == 0) 1148099a0e58SBosko Milekic keg->uk_flags |= UMA_ZONE_HASH; 11498355f576SJeff Roberson } 11508355f576SJeff Roberson } 11518355f576SJeff Roberson 11528355f576SJeff Roberson /* 1153e20a199fSJeff Roberson * Finish creating a large (> UMA_SLAB_SIZE) uma kegs. Just give in and do 11548355f576SJeff Roberson * OFFPAGE for now. When I can allow for more dynamic slab sizes this will be 11558355f576SJeff Roberson * more complicated. 11568355f576SJeff Roberson * 11578355f576SJeff Roberson * Arguments 1158e20a199fSJeff Roberson * keg The keg we should initialize 11598355f576SJeff Roberson * 11608355f576SJeff Roberson * Returns 11618355f576SJeff Roberson * Nothing 11628355f576SJeff Roberson */ 11638355f576SJeff Roberson static void 1164e20a199fSJeff Roberson keg_large_init(uma_keg_t keg) 11658355f576SJeff Roberson { 11668355f576SJeff Roberson int pages; 11678355f576SJeff Roberson 1168e20a199fSJeff Roberson KASSERT(keg != NULL, ("Keg is null in keg_large_init")); 1169099a0e58SBosko Milekic KASSERT((keg->uk_flags & UMA_ZFLAG_CACHEONLY) == 0, 1170e20a199fSJeff Roberson ("keg_large_init: Cannot large-init a UMA_ZFLAG_CACHEONLY keg")); 117120e8e865SBosko Milekic 1172099a0e58SBosko Milekic pages = keg->uk_size / UMA_SLAB_SIZE; 11738355f576SJeff Roberson 11748355f576SJeff Roberson /* Account for remainder */ 1175099a0e58SBosko Milekic if ((pages * UMA_SLAB_SIZE) < keg->uk_size) 11768355f576SJeff Roberson pages++; 11778355f576SJeff Roberson 1178099a0e58SBosko Milekic keg->uk_ppera = pages; 1179099a0e58SBosko Milekic keg->uk_ipers = 1; 11808355f576SJeff Roberson 1181099a0e58SBosko Milekic keg->uk_flags |= UMA_ZONE_OFFPAGE; 1182e20a199fSJeff Roberson if ((keg->uk_flags & UMA_ZONE_VTOSLAB) == 0) 1183099a0e58SBosko Milekic keg->uk_flags |= UMA_ZONE_HASH; 118499571dc3SJeff Roberson 1185099a0e58SBosko Milekic keg->uk_rsize = keg->uk_size; 11868355f576SJeff Roberson } 11878355f576SJeff Roberson 1188e20a199fSJeff Roberson static void 1189e20a199fSJeff Roberson keg_cachespread_init(uma_keg_t keg) 1190e20a199fSJeff Roberson { 1191e20a199fSJeff Roberson int alignsize; 1192e20a199fSJeff Roberson int trailer; 1193e20a199fSJeff Roberson int pages; 1194e20a199fSJeff Roberson int rsize; 1195e20a199fSJeff Roberson 1196e20a199fSJeff Roberson alignsize = keg->uk_align + 1; 1197e20a199fSJeff Roberson rsize = keg->uk_size; 1198e20a199fSJeff Roberson /* 1199e20a199fSJeff Roberson * We want one item to start on every align boundary in a page. To 1200e20a199fSJeff Roberson * do this we will span pages. We will also extend the item by the 1201e20a199fSJeff Roberson * size of align if it is an even multiple of align. Otherwise, it 1202e20a199fSJeff Roberson * would fall on the same boundary every time. 1203e20a199fSJeff Roberson */ 1204e20a199fSJeff Roberson if (rsize & keg->uk_align) 1205e20a199fSJeff Roberson rsize = (rsize & ~keg->uk_align) + alignsize; 1206e20a199fSJeff Roberson if ((rsize & alignsize) == 0) 1207e20a199fSJeff Roberson rsize += alignsize; 1208e20a199fSJeff Roberson trailer = rsize - keg->uk_size; 1209e20a199fSJeff Roberson pages = (rsize * (PAGE_SIZE / alignsize)) / PAGE_SIZE; 1210e20a199fSJeff Roberson pages = MIN(pages, (128 * 1024) / PAGE_SIZE); 1211e20a199fSJeff Roberson keg->uk_rsize = rsize; 1212e20a199fSJeff Roberson keg->uk_ppera = pages; 1213e20a199fSJeff Roberson keg->uk_ipers = ((pages * PAGE_SIZE) + trailer) / rsize; 1214e20a199fSJeff Roberson keg->uk_flags |= UMA_ZONE_OFFPAGE | UMA_ZONE_VTOSLAB; 1215e20a199fSJeff Roberson KASSERT(keg->uk_ipers <= uma_max_ipers, 1216e20a199fSJeff Roberson ("keg_small_init: keg->uk_ipers too high(%d) increase max_ipers", 1217e20a199fSJeff Roberson keg->uk_ipers)); 1218e20a199fSJeff Roberson } 1219e20a199fSJeff Roberson 12208355f576SJeff Roberson /* 1221099a0e58SBosko Milekic * Keg header ctor. This initializes all fields, locks, etc. And inserts 1222099a0e58SBosko Milekic * the keg onto the global keg list. 12238355f576SJeff Roberson * 12248355f576SJeff Roberson * Arguments/Returns follow uma_ctor specifications 1225099a0e58SBosko Milekic * udata Actually uma_kctor_args 1226099a0e58SBosko Milekic */ 1227b23f72e9SBrian Feldman static int 1228b23f72e9SBrian Feldman keg_ctor(void *mem, int size, void *udata, int flags) 1229099a0e58SBosko Milekic { 1230099a0e58SBosko Milekic struct uma_kctor_args *arg = udata; 1231099a0e58SBosko Milekic uma_keg_t keg = mem; 1232099a0e58SBosko Milekic uma_zone_t zone; 1233099a0e58SBosko Milekic 1234099a0e58SBosko Milekic bzero(keg, size); 1235099a0e58SBosko Milekic keg->uk_size = arg->size; 1236099a0e58SBosko Milekic keg->uk_init = arg->uminit; 1237099a0e58SBosko Milekic keg->uk_fini = arg->fini; 1238099a0e58SBosko Milekic keg->uk_align = arg->align; 1239099a0e58SBosko Milekic keg->uk_free = 0; 1240099a0e58SBosko Milekic keg->uk_pages = 0; 1241099a0e58SBosko Milekic keg->uk_flags = arg->flags; 1242099a0e58SBosko Milekic keg->uk_allocf = page_alloc; 1243099a0e58SBosko Milekic keg->uk_freef = page_free; 1244099a0e58SBosko Milekic keg->uk_recurse = 0; 1245099a0e58SBosko Milekic keg->uk_slabzone = NULL; 1246099a0e58SBosko Milekic 1247099a0e58SBosko Milekic /* 1248099a0e58SBosko Milekic * The master zone is passed to us at keg-creation time. 1249099a0e58SBosko Milekic */ 1250099a0e58SBosko Milekic zone = arg->zone; 1251e20a199fSJeff Roberson keg->uk_name = zone->uz_name; 1252099a0e58SBosko Milekic 1253099a0e58SBosko Milekic if (arg->flags & UMA_ZONE_VM) 1254099a0e58SBosko Milekic keg->uk_flags |= UMA_ZFLAG_CACHEONLY; 1255099a0e58SBosko Milekic 1256099a0e58SBosko Milekic if (arg->flags & UMA_ZONE_ZINIT) 1257099a0e58SBosko Milekic keg->uk_init = zero_init; 1258099a0e58SBosko Milekic 1259e20a199fSJeff Roberson if (arg->flags & UMA_ZONE_REFCNT || arg->flags & UMA_ZONE_MALLOC) 1260e20a199fSJeff Roberson keg->uk_flags |= UMA_ZONE_VTOSLAB; 1261e20a199fSJeff Roberson 1262099a0e58SBosko Milekic /* 1263244f4554SBosko Milekic * The +UMA_FRITM_SZ added to uk_size is to account for the 1264e20a199fSJeff Roberson * linkage that is added to the size in keg_small_init(). If 1265099a0e58SBosko Milekic * we don't account for this here then we may end up in 1266e20a199fSJeff Roberson * keg_small_init() with a calculated 'ipers' of 0. 1267099a0e58SBosko Milekic */ 1268244f4554SBosko Milekic if (keg->uk_flags & UMA_ZONE_REFCNT) { 1269e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZONE_CACHESPREAD) 1270e20a199fSJeff Roberson keg_cachespread_init(keg); 1271e20a199fSJeff Roberson else if ((keg->uk_size+UMA_FRITMREF_SZ) > 1272244f4554SBosko Milekic (UMA_SLAB_SIZE - sizeof(struct uma_slab_refcnt))) 1273e20a199fSJeff Roberson keg_large_init(keg); 1274099a0e58SBosko Milekic else 1275e20a199fSJeff Roberson keg_small_init(keg); 1276244f4554SBosko Milekic } else { 1277e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZONE_CACHESPREAD) 1278e20a199fSJeff Roberson keg_cachespread_init(keg); 1279e20a199fSJeff Roberson else if ((keg->uk_size+UMA_FRITM_SZ) > 1280244f4554SBosko Milekic (UMA_SLAB_SIZE - sizeof(struct uma_slab))) 1281e20a199fSJeff Roberson keg_large_init(keg); 1282244f4554SBosko Milekic else 1283e20a199fSJeff Roberson keg_small_init(keg); 1284244f4554SBosko Milekic } 1285099a0e58SBosko Milekic 1286244f4554SBosko Milekic if (keg->uk_flags & UMA_ZONE_OFFPAGE) { 1287099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_REFCNT) 1288099a0e58SBosko Milekic keg->uk_slabzone = slabrefzone; 1289244f4554SBosko Milekic else 1290099a0e58SBosko Milekic keg->uk_slabzone = slabzone; 1291244f4554SBosko Milekic } 1292099a0e58SBosko Milekic 1293099a0e58SBosko Milekic /* 1294099a0e58SBosko Milekic * If we haven't booted yet we need allocations to go through the 1295099a0e58SBosko Milekic * startup cache until the vm is ready. 1296099a0e58SBosko Milekic */ 1297099a0e58SBosko Milekic if (keg->uk_ppera == 1) { 1298099a0e58SBosko Milekic #ifdef UMA_MD_SMALL_ALLOC 1299099a0e58SBosko Milekic keg->uk_allocf = uma_small_alloc; 1300099a0e58SBosko Milekic keg->uk_freef = uma_small_free; 1301099a0e58SBosko Milekic #endif 1302099a0e58SBosko Milekic if (booted == 0) 1303099a0e58SBosko Milekic keg->uk_allocf = startup_alloc; 1304099a0e58SBosko Milekic } 1305099a0e58SBosko Milekic 1306099a0e58SBosko Milekic /* 1307e20a199fSJeff Roberson * Initialize keg's lock (shared among zones). 1308099a0e58SBosko Milekic */ 1309099a0e58SBosko Milekic if (arg->flags & UMA_ZONE_MTXCLASS) 1310e20a199fSJeff Roberson KEG_LOCK_INIT(keg, 1); 1311099a0e58SBosko Milekic else 1312e20a199fSJeff Roberson KEG_LOCK_INIT(keg, 0); 1313099a0e58SBosko Milekic 1314099a0e58SBosko Milekic /* 1315099a0e58SBosko Milekic * If we're putting the slab header in the actual page we need to 1316099a0e58SBosko Milekic * figure out where in each page it goes. This calculates a right 1317099a0e58SBosko Milekic * justified offset into the memory on an ALIGN_PTR boundary. 1318099a0e58SBosko Milekic */ 1319099a0e58SBosko Milekic if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) { 1320244f4554SBosko Milekic u_int totsize; 1321099a0e58SBosko Milekic 1322099a0e58SBosko Milekic /* Size of the slab struct and free list */ 1323244f4554SBosko Milekic if (keg->uk_flags & UMA_ZONE_REFCNT) 1324244f4554SBosko Milekic totsize = sizeof(struct uma_slab_refcnt) + 1325244f4554SBosko Milekic keg->uk_ipers * UMA_FRITMREF_SZ; 1326244f4554SBosko Milekic else 1327244f4554SBosko Milekic totsize = sizeof(struct uma_slab) + 1328244f4554SBosko Milekic keg->uk_ipers * UMA_FRITM_SZ; 1329244f4554SBosko Milekic 1330099a0e58SBosko Milekic if (totsize & UMA_ALIGN_PTR) 1331099a0e58SBosko Milekic totsize = (totsize & ~UMA_ALIGN_PTR) + 1332099a0e58SBosko Milekic (UMA_ALIGN_PTR + 1); 1333099a0e58SBosko Milekic keg->uk_pgoff = UMA_SLAB_SIZE - totsize; 1334244f4554SBosko Milekic 1335244f4554SBosko Milekic if (keg->uk_flags & UMA_ZONE_REFCNT) 1336244f4554SBosko Milekic totsize = keg->uk_pgoff + sizeof(struct uma_slab_refcnt) 1337244f4554SBosko Milekic + keg->uk_ipers * UMA_FRITMREF_SZ; 1338244f4554SBosko Milekic else 1339099a0e58SBosko Milekic totsize = keg->uk_pgoff + sizeof(struct uma_slab) 1340244f4554SBosko Milekic + keg->uk_ipers * UMA_FRITM_SZ; 1341244f4554SBosko Milekic 1342244f4554SBosko Milekic /* 1343244f4554SBosko Milekic * The only way the following is possible is if with our 1344244f4554SBosko Milekic * UMA_ALIGN_PTR adjustments we are now bigger than 1345244f4554SBosko Milekic * UMA_SLAB_SIZE. I haven't checked whether this is 1346244f4554SBosko Milekic * mathematically possible for all cases, so we make 1347244f4554SBosko Milekic * sure here anyway. 1348244f4554SBosko Milekic */ 1349099a0e58SBosko Milekic if (totsize > UMA_SLAB_SIZE) { 1350099a0e58SBosko Milekic printf("zone %s ipers %d rsize %d size %d\n", 1351099a0e58SBosko Milekic zone->uz_name, keg->uk_ipers, keg->uk_rsize, 1352099a0e58SBosko Milekic keg->uk_size); 1353aea6e893SAlan Cox panic("UMA slab won't fit."); 1354099a0e58SBosko Milekic } 1355099a0e58SBosko Milekic } 1356099a0e58SBosko Milekic 1357099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_HASH) 1358099a0e58SBosko Milekic hash_alloc(&keg->uk_hash); 1359099a0e58SBosko Milekic 1360099a0e58SBosko Milekic #ifdef UMA_DEBUG 1361e20a199fSJeff Roberson printf("UMA: %s(%p) size %d(%d) flags %d ipers %d ppera %d out %d free %d\n", 1362e20a199fSJeff Roberson zone->uz_name, zone, keg->uk_size, keg->uk_rsize, keg->uk_flags, 1363e20a199fSJeff Roberson keg->uk_ipers, keg->uk_ppera, 1364e20a199fSJeff Roberson (keg->uk_ipers * keg->uk_pages) - keg->uk_free, keg->uk_free); 1365099a0e58SBosko Milekic #endif 1366099a0e58SBosko Milekic 1367099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link); 1368099a0e58SBosko Milekic 1369099a0e58SBosko Milekic mtx_lock(&uma_mtx); 1370099a0e58SBosko Milekic LIST_INSERT_HEAD(&uma_kegs, keg, uk_link); 1371099a0e58SBosko Milekic mtx_unlock(&uma_mtx); 1372b23f72e9SBrian Feldman return (0); 1373099a0e58SBosko Milekic } 1374099a0e58SBosko Milekic 1375099a0e58SBosko Milekic /* 1376099a0e58SBosko Milekic * Zone header ctor. This initializes all fields, locks, etc. 1377099a0e58SBosko Milekic * 1378099a0e58SBosko Milekic * Arguments/Returns follow uma_ctor specifications 1379099a0e58SBosko Milekic * udata Actually uma_zctor_args 13808355f576SJeff Roberson */ 1381b23f72e9SBrian Feldman static int 1382b23f72e9SBrian Feldman zone_ctor(void *mem, int size, void *udata, int flags) 13838355f576SJeff Roberson { 13848355f576SJeff Roberson struct uma_zctor_args *arg = udata; 13858355f576SJeff Roberson uma_zone_t zone = mem; 1386099a0e58SBosko Milekic uma_zone_t z; 1387099a0e58SBosko Milekic uma_keg_t keg; 13888355f576SJeff Roberson 13898355f576SJeff Roberson bzero(zone, size); 13908355f576SJeff Roberson zone->uz_name = arg->name; 13918355f576SJeff Roberson zone->uz_ctor = arg->ctor; 13928355f576SJeff Roberson zone->uz_dtor = arg->dtor; 1393e20a199fSJeff Roberson zone->uz_slab = zone_fetch_slab; 1394099a0e58SBosko Milekic zone->uz_init = NULL; 1395099a0e58SBosko Milekic zone->uz_fini = NULL; 1396099a0e58SBosko Milekic zone->uz_allocs = 0; 1397773df9abSRobert Watson zone->uz_frees = 0; 13982019094aSRobert Watson zone->uz_fails = 0; 1399bf965959SSean Bruno zone->uz_sleeps = 0; 1400099a0e58SBosko Milekic zone->uz_fills = zone->uz_count = 0; 1401e20a199fSJeff Roberson zone->uz_flags = 0; 1402e20a199fSJeff Roberson keg = arg->keg; 1403099a0e58SBosko Milekic 1404099a0e58SBosko Milekic if (arg->flags & UMA_ZONE_SECONDARY) { 1405099a0e58SBosko Milekic KASSERT(arg->keg != NULL, ("Secondary zone on zero'd keg")); 14068355f576SJeff Roberson zone->uz_init = arg->uminit; 1407e221e841SJeff Roberson zone->uz_fini = arg->fini; 1408099a0e58SBosko Milekic zone->uz_lock = &keg->uk_lock; 1409e20a199fSJeff Roberson zone->uz_flags |= UMA_ZONE_SECONDARY; 14108355f576SJeff Roberson mtx_lock(&uma_mtx); 1411099a0e58SBosko Milekic ZONE_LOCK(zone); 1412099a0e58SBosko Milekic LIST_FOREACH(z, &keg->uk_zones, uz_link) { 1413099a0e58SBosko Milekic if (LIST_NEXT(z, uz_link) == NULL) { 1414099a0e58SBosko Milekic LIST_INSERT_AFTER(z, zone, uz_link); 1415099a0e58SBosko Milekic break; 1416099a0e58SBosko Milekic } 1417099a0e58SBosko Milekic } 1418099a0e58SBosko Milekic ZONE_UNLOCK(zone); 14198355f576SJeff Roberson mtx_unlock(&uma_mtx); 1420e20a199fSJeff Roberson } else if (keg == NULL) { 1421e20a199fSJeff Roberson if ((keg = uma_kcreate(zone, arg->size, arg->uminit, arg->fini, 1422e20a199fSJeff Roberson arg->align, arg->flags)) == NULL) 1423b23f72e9SBrian Feldman return (ENOMEM); 1424099a0e58SBosko Milekic } else { 1425099a0e58SBosko Milekic struct uma_kctor_args karg; 1426b23f72e9SBrian Feldman int error; 1427099a0e58SBosko Milekic 1428099a0e58SBosko Milekic /* We should only be here from uma_startup() */ 1429099a0e58SBosko Milekic karg.size = arg->size; 1430099a0e58SBosko Milekic karg.uminit = arg->uminit; 1431099a0e58SBosko Milekic karg.fini = arg->fini; 1432099a0e58SBosko Milekic karg.align = arg->align; 1433099a0e58SBosko Milekic karg.flags = arg->flags; 1434099a0e58SBosko Milekic karg.zone = zone; 1435b23f72e9SBrian Feldman error = keg_ctor(arg->keg, sizeof(struct uma_keg), &karg, 1436b23f72e9SBrian Feldman flags); 1437b23f72e9SBrian Feldman if (error) 1438b23f72e9SBrian Feldman return (error); 1439099a0e58SBosko Milekic } 1440e20a199fSJeff Roberson /* 1441e20a199fSJeff Roberson * Link in the first keg. 1442e20a199fSJeff Roberson */ 1443e20a199fSJeff Roberson zone->uz_klink.kl_keg = keg; 1444e20a199fSJeff Roberson LIST_INSERT_HEAD(&zone->uz_kegs, &zone->uz_klink, kl_link); 1445099a0e58SBosko Milekic zone->uz_lock = &keg->uk_lock; 1446e20a199fSJeff Roberson zone->uz_size = keg->uk_size; 1447e20a199fSJeff Roberson zone->uz_flags |= (keg->uk_flags & 1448e20a199fSJeff Roberson (UMA_ZONE_INHERIT | UMA_ZFLAG_INHERIT)); 14498355f576SJeff Roberson 14508355f576SJeff Roberson /* 14518355f576SJeff Roberson * Some internal zones don't have room allocated for the per cpu 14528355f576SJeff Roberson * caches. If we're internal, bail out here. 14538355f576SJeff Roberson */ 1454099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZFLAG_INTERNAL) { 1455e20a199fSJeff Roberson KASSERT((zone->uz_flags & UMA_ZONE_SECONDARY) == 0, 1456099a0e58SBosko Milekic ("Secondary zone requested UMA_ZFLAG_INTERNAL")); 1457b23f72e9SBrian Feldman return (0); 1458099a0e58SBosko Milekic } 14598355f576SJeff Roberson 1460099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZONE_MAXBUCKET) 1461099a0e58SBosko Milekic zone->uz_count = BUCKET_MAX; 1462099a0e58SBosko Milekic else if (keg->uk_ipers <= BUCKET_MAX) 1463099a0e58SBosko Milekic zone->uz_count = keg->uk_ipers; 14648355f576SJeff Roberson else 1465cae33c14SJeff Roberson zone->uz_count = BUCKET_MAX; 1466b23f72e9SBrian Feldman return (0); 14678355f576SJeff Roberson } 14688355f576SJeff Roberson 14698355f576SJeff Roberson /* 1470099a0e58SBosko Milekic * Keg header dtor. This frees all data, destroys locks, frees the hash 1471099a0e58SBosko Milekic * table and removes the keg from the global list. 14729c2cd7e5SJeff Roberson * 14739c2cd7e5SJeff Roberson * Arguments/Returns follow uma_dtor specifications 14749c2cd7e5SJeff Roberson * udata unused 14759c2cd7e5SJeff Roberson */ 1476099a0e58SBosko Milekic static void 1477099a0e58SBosko Milekic keg_dtor(void *arg, int size, void *udata) 1478099a0e58SBosko Milekic { 1479099a0e58SBosko Milekic uma_keg_t keg; 14809c2cd7e5SJeff Roberson 1481099a0e58SBosko Milekic keg = (uma_keg_t)arg; 1482e20a199fSJeff Roberson KEG_LOCK(keg); 1483099a0e58SBosko Milekic if (keg->uk_free != 0) { 1484099a0e58SBosko Milekic printf("Freed UMA keg was not empty (%d items). " 1485099a0e58SBosko Milekic " Lost %d pages of memory.\n", 1486099a0e58SBosko Milekic keg->uk_free, keg->uk_pages); 1487099a0e58SBosko Milekic } 1488e20a199fSJeff Roberson KEG_UNLOCK(keg); 1489099a0e58SBosko Milekic 1490099a0e58SBosko Milekic hash_free(&keg->uk_hash); 1491099a0e58SBosko Milekic 1492e20a199fSJeff Roberson KEG_LOCK_FINI(keg); 1493099a0e58SBosko Milekic } 1494099a0e58SBosko Milekic 1495099a0e58SBosko Milekic /* 1496099a0e58SBosko Milekic * Zone header dtor. 1497099a0e58SBosko Milekic * 1498099a0e58SBosko Milekic * Arguments/Returns follow uma_dtor specifications 1499099a0e58SBosko Milekic * udata unused 1500099a0e58SBosko Milekic */ 15019c2cd7e5SJeff Roberson static void 15029c2cd7e5SJeff Roberson zone_dtor(void *arg, int size, void *udata) 15039c2cd7e5SJeff Roberson { 1504e20a199fSJeff Roberson uma_klink_t klink; 15059c2cd7e5SJeff Roberson uma_zone_t zone; 1506099a0e58SBosko Milekic uma_keg_t keg; 15079c2cd7e5SJeff Roberson 15089c2cd7e5SJeff Roberson zone = (uma_zone_t)arg; 1509e20a199fSJeff Roberson keg = zone_first_keg(zone); 15109643769aSJeff Roberson 1511e20a199fSJeff Roberson if (!(zone->uz_flags & UMA_ZFLAG_INTERNAL)) 15129643769aSJeff Roberson cache_drain(zone); 1513099a0e58SBosko Milekic 151417b9cc49SJeff Roberson mtx_lock(&uma_mtx); 1515099a0e58SBosko Milekic LIST_REMOVE(zone, uz_link); 1516e20a199fSJeff Roberson mtx_unlock(&uma_mtx); 1517099a0e58SBosko Milekic /* 1518099a0e58SBosko Milekic * XXX there are some races here where 1519099a0e58SBosko Milekic * the zone can be drained but zone lock 1520099a0e58SBosko Milekic * released and then refilled before we 1521099a0e58SBosko Milekic * remove it... we dont care for now 1522099a0e58SBosko Milekic */ 1523e20a199fSJeff Roberson zone_drain_wait(zone, M_WAITOK); 1524e20a199fSJeff Roberson /* 1525e20a199fSJeff Roberson * Unlink all of our kegs. 1526e20a199fSJeff Roberson */ 1527e20a199fSJeff Roberson while ((klink = LIST_FIRST(&zone->uz_kegs)) != NULL) { 1528e20a199fSJeff Roberson klink->kl_keg = NULL; 1529e20a199fSJeff Roberson LIST_REMOVE(klink, kl_link); 1530e20a199fSJeff Roberson if (klink == &zone->uz_klink) 1531e20a199fSJeff Roberson continue; 1532e20a199fSJeff Roberson free(klink, M_TEMP); 1533e20a199fSJeff Roberson } 1534e20a199fSJeff Roberson /* 1535e20a199fSJeff Roberson * We only destroy kegs from non secondary zones. 1536e20a199fSJeff Roberson */ 1537e20a199fSJeff Roberson if ((zone->uz_flags & UMA_ZONE_SECONDARY) == 0) { 1538e20a199fSJeff Roberson mtx_lock(&uma_mtx); 1539099a0e58SBosko Milekic LIST_REMOVE(keg, uk_link); 1540099a0e58SBosko Milekic mtx_unlock(&uma_mtx); 1541e20a199fSJeff Roberson zone_free_item(kegs, keg, NULL, SKIP_NONE, 1542f4ff923bSRobert Watson ZFREE_STATFREE); 15439c2cd7e5SJeff Roberson } 1544099a0e58SBosko Milekic } 1545099a0e58SBosko Milekic 15469c2cd7e5SJeff Roberson /* 15478355f576SJeff Roberson * Traverses every zone in the system and calls a callback 15488355f576SJeff Roberson * 15498355f576SJeff Roberson * Arguments: 15508355f576SJeff Roberson * zfunc A pointer to a function which accepts a zone 15518355f576SJeff Roberson * as an argument. 15528355f576SJeff Roberson * 15538355f576SJeff Roberson * Returns: 15548355f576SJeff Roberson * Nothing 15558355f576SJeff Roberson */ 15568355f576SJeff Roberson static void 15578355f576SJeff Roberson zone_foreach(void (*zfunc)(uma_zone_t)) 15588355f576SJeff Roberson { 1559099a0e58SBosko Milekic uma_keg_t keg; 15608355f576SJeff Roberson uma_zone_t zone; 15618355f576SJeff Roberson 15628355f576SJeff Roberson mtx_lock(&uma_mtx); 1563099a0e58SBosko Milekic LIST_FOREACH(keg, &uma_kegs, uk_link) { 1564099a0e58SBosko Milekic LIST_FOREACH(zone, &keg->uk_zones, uz_link) 15658355f576SJeff Roberson zfunc(zone); 1566099a0e58SBosko Milekic } 15678355f576SJeff Roberson mtx_unlock(&uma_mtx); 15688355f576SJeff Roberson } 15698355f576SJeff Roberson 15708355f576SJeff Roberson /* Public functions */ 15718355f576SJeff Roberson /* See uma.h */ 15728355f576SJeff Roberson void 15733803b26bSDag-Erling Smørgrav uma_startup(void *bootmem, int boot_pages) 15748355f576SJeff Roberson { 15758355f576SJeff Roberson struct uma_zctor_args args; 15768355f576SJeff Roberson uma_slab_t slab; 1577244f4554SBosko Milekic u_int slabsize; 1578244f4554SBosko Milekic u_int objsize, totsize, wsize; 15798355f576SJeff Roberson int i; 15808355f576SJeff Roberson 15818355f576SJeff Roberson #ifdef UMA_DEBUG 1582099a0e58SBosko Milekic printf("Creating uma keg headers zone and keg.\n"); 15838355f576SJeff Roberson #endif 1584f353d338SAlan Cox mtx_init(&uma_mtx, "UMA lock", NULL, MTX_DEF); 1585099a0e58SBosko Milekic 1586244f4554SBosko Milekic /* 1587244f4554SBosko Milekic * Figure out the maximum number of items-per-slab we'll have if 1588244f4554SBosko Milekic * we're using the OFFPAGE slab header to track free items, given 1589244f4554SBosko Milekic * all possible object sizes and the maximum desired wastage 1590244f4554SBosko Milekic * (UMA_MAX_WASTE). 1591244f4554SBosko Milekic * 1592244f4554SBosko Milekic * We iterate until we find an object size for 1593e20a199fSJeff Roberson * which the calculated wastage in keg_small_init() will be 1594244f4554SBosko Milekic * enough to warrant OFFPAGE. Since wastedspace versus objsize 1595244f4554SBosko Milekic * is an overall increasing see-saw function, we find the smallest 1596244f4554SBosko Milekic * objsize such that the wastage is always acceptable for objects 1597244f4554SBosko Milekic * with that objsize or smaller. Since a smaller objsize always 1598244f4554SBosko Milekic * generates a larger possible uma_max_ipers, we use this computed 1599244f4554SBosko Milekic * objsize to calculate the largest ipers possible. Since the 1600244f4554SBosko Milekic * ipers calculated for OFFPAGE slab headers is always larger than 1601e20a199fSJeff Roberson * the ipers initially calculated in keg_small_init(), we use 1602244f4554SBosko Milekic * the former's equation (UMA_SLAB_SIZE / keg->uk_rsize) to 1603244f4554SBosko Milekic * obtain the maximum ipers possible for offpage slab headers. 1604244f4554SBosko Milekic * 1605244f4554SBosko Milekic * It should be noted that ipers versus objsize is an inversly 1606244f4554SBosko Milekic * proportional function which drops off rather quickly so as 1607244f4554SBosko Milekic * long as our UMA_MAX_WASTE is such that the objsize we calculate 1608244f4554SBosko Milekic * falls into the portion of the inverse relation AFTER the steep 1609244f4554SBosko Milekic * falloff, then uma_max_ipers shouldn't be too high (~10 on i386). 1610244f4554SBosko Milekic * 1611244f4554SBosko Milekic * Note that we have 8-bits (1 byte) to use as a freelist index 1612244f4554SBosko Milekic * inside the actual slab header itself and this is enough to 1613244f4554SBosko Milekic * accomodate us. In the worst case, a UMA_SMALLEST_UNIT sized 1614244f4554SBosko Milekic * object with offpage slab header would have ipers = 1615244f4554SBosko Milekic * UMA_SLAB_SIZE / UMA_SMALLEST_UNIT (currently = 256), which is 1616244f4554SBosko Milekic * 1 greater than what our byte-integer freelist index can 1617244f4554SBosko Milekic * accomodate, but we know that this situation never occurs as 1618244f4554SBosko Milekic * for UMA_SMALLEST_UNIT-sized objects, we will never calculate 1619244f4554SBosko Milekic * that we need to go to offpage slab headers. Or, if we do, 1620244f4554SBosko Milekic * then we trap that condition below and panic in the INVARIANTS case. 1621244f4554SBosko Milekic */ 1622244f4554SBosko Milekic wsize = UMA_SLAB_SIZE - sizeof(struct uma_slab) - UMA_MAX_WASTE; 1623244f4554SBosko Milekic totsize = wsize; 1624244f4554SBosko Milekic objsize = UMA_SMALLEST_UNIT; 1625244f4554SBosko Milekic while (totsize >= wsize) { 1626244f4554SBosko Milekic totsize = (UMA_SLAB_SIZE - sizeof(struct uma_slab)) / 1627244f4554SBosko Milekic (objsize + UMA_FRITM_SZ); 1628244f4554SBosko Milekic totsize *= (UMA_FRITM_SZ + objsize); 1629244f4554SBosko Milekic objsize++; 1630244f4554SBosko Milekic } 1631244f4554SBosko Milekic if (objsize > UMA_SMALLEST_UNIT) 1632244f4554SBosko Milekic objsize--; 1633e20a199fSJeff Roberson uma_max_ipers = MAX(UMA_SLAB_SIZE / objsize, 64); 1634244f4554SBosko Milekic 1635244f4554SBosko Milekic wsize = UMA_SLAB_SIZE - sizeof(struct uma_slab_refcnt) - UMA_MAX_WASTE; 1636244f4554SBosko Milekic totsize = wsize; 1637244f4554SBosko Milekic objsize = UMA_SMALLEST_UNIT; 1638244f4554SBosko Milekic while (totsize >= wsize) { 1639244f4554SBosko Milekic totsize = (UMA_SLAB_SIZE - sizeof(struct uma_slab_refcnt)) / 1640244f4554SBosko Milekic (objsize + UMA_FRITMREF_SZ); 1641244f4554SBosko Milekic totsize *= (UMA_FRITMREF_SZ + objsize); 1642244f4554SBosko Milekic objsize++; 1643244f4554SBosko Milekic } 1644244f4554SBosko Milekic if (objsize > UMA_SMALLEST_UNIT) 1645244f4554SBosko Milekic objsize--; 1646e20a199fSJeff Roberson uma_max_ipers_ref = MAX(UMA_SLAB_SIZE / objsize, 64); 1647244f4554SBosko Milekic 1648244f4554SBosko Milekic KASSERT((uma_max_ipers_ref <= 255) && (uma_max_ipers <= 255), 1649244f4554SBosko Milekic ("uma_startup: calculated uma_max_ipers values too large!")); 1650244f4554SBosko Milekic 1651244f4554SBosko Milekic #ifdef UMA_DEBUG 1652244f4554SBosko Milekic printf("Calculated uma_max_ipers (for OFFPAGE) is %d\n", uma_max_ipers); 1653244f4554SBosko Milekic printf("Calculated uma_max_ipers_slab (for OFFPAGE) is %d\n", 1654244f4554SBosko Milekic uma_max_ipers_ref); 1655244f4554SBosko Milekic #endif 1656244f4554SBosko Milekic 1657099a0e58SBosko Milekic /* "manually" create the initial zone */ 1658099a0e58SBosko Milekic args.name = "UMA Kegs"; 1659099a0e58SBosko Milekic args.size = sizeof(struct uma_keg); 1660099a0e58SBosko Milekic args.ctor = keg_ctor; 1661099a0e58SBosko Milekic args.dtor = keg_dtor; 16628355f576SJeff Roberson args.uminit = zero_init; 16638355f576SJeff Roberson args.fini = NULL; 1664099a0e58SBosko Milekic args.keg = &masterkeg; 16658355f576SJeff Roberson args.align = 32 - 1; 1666b60f5b79SJeff Roberson args.flags = UMA_ZFLAG_INTERNAL; 16678355f576SJeff Roberson /* The initial zone has no Per cpu queues so it's smaller */ 1668b23f72e9SBrian Feldman zone_ctor(kegs, sizeof(struct uma_zone), &args, M_WAITOK); 16698355f576SJeff Roberson 16708355f576SJeff Roberson #ifdef UMA_DEBUG 16718355f576SJeff Roberson printf("Filling boot free list.\n"); 16728355f576SJeff Roberson #endif 16733803b26bSDag-Erling Smørgrav for (i = 0; i < boot_pages; i++) { 16748355f576SJeff Roberson slab = (uma_slab_t)((u_int8_t *)bootmem + (i * UMA_SLAB_SIZE)); 16758355f576SJeff Roberson slab->us_data = (u_int8_t *)slab; 16768355f576SJeff Roberson slab->us_flags = UMA_SLAB_BOOT; 16778355f576SJeff Roberson LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link); 16788355f576SJeff Roberson } 1679f353d338SAlan Cox mtx_init(&uma_boot_pages_mtx, "UMA boot pages", NULL, MTX_DEF); 16808355f576SJeff Roberson 16818355f576SJeff Roberson #ifdef UMA_DEBUG 1682099a0e58SBosko Milekic printf("Creating uma zone headers zone and keg.\n"); 1683099a0e58SBosko Milekic #endif 1684099a0e58SBosko Milekic args.name = "UMA Zones"; 1685099a0e58SBosko Milekic args.size = sizeof(struct uma_zone) + 1686099a0e58SBosko Milekic (sizeof(struct uma_cache) * (mp_maxid + 1)); 1687099a0e58SBosko Milekic args.ctor = zone_ctor; 1688099a0e58SBosko Milekic args.dtor = zone_dtor; 1689099a0e58SBosko Milekic args.uminit = zero_init; 1690099a0e58SBosko Milekic args.fini = NULL; 1691099a0e58SBosko Milekic args.keg = NULL; 1692099a0e58SBosko Milekic args.align = 32 - 1; 1693099a0e58SBosko Milekic args.flags = UMA_ZFLAG_INTERNAL; 1694099a0e58SBosko Milekic /* The initial zone has no Per cpu queues so it's smaller */ 1695b23f72e9SBrian Feldman zone_ctor(zones, sizeof(struct uma_zone), &args, M_WAITOK); 1696099a0e58SBosko Milekic 1697099a0e58SBosko Milekic #ifdef UMA_DEBUG 1698099a0e58SBosko Milekic printf("Initializing pcpu cache locks.\n"); 1699099a0e58SBosko Milekic #endif 1700099a0e58SBosko Milekic #ifdef UMA_DEBUG 1701099a0e58SBosko Milekic printf("Creating slab and hash zones.\n"); 17028355f576SJeff Roberson #endif 17038355f576SJeff Roberson 17048355f576SJeff Roberson /* 17058355f576SJeff Roberson * This is the max number of free list items we'll have with 17068355f576SJeff Roberson * offpage slabs. 17078355f576SJeff Roberson */ 1708244f4554SBosko Milekic slabsize = uma_max_ipers * UMA_FRITM_SZ; 17098355f576SJeff Roberson slabsize += sizeof(struct uma_slab); 17108355f576SJeff Roberson 17118355f576SJeff Roberson /* Now make a zone for slab headers */ 17128355f576SJeff Roberson slabzone = uma_zcreate("UMA Slabs", 17138355f576SJeff Roberson slabsize, 17148355f576SJeff Roberson NULL, NULL, NULL, NULL, 1715b60f5b79SJeff Roberson UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL); 17168355f576SJeff Roberson 1717099a0e58SBosko Milekic /* 1718099a0e58SBosko Milekic * We also create a zone for the bigger slabs with reference 1719099a0e58SBosko Milekic * counts in them, to accomodate UMA_ZONE_REFCNT zones. 1720099a0e58SBosko Milekic */ 1721244f4554SBosko Milekic slabsize = uma_max_ipers_ref * UMA_FRITMREF_SZ; 1722099a0e58SBosko Milekic slabsize += sizeof(struct uma_slab_refcnt); 1723099a0e58SBosko Milekic slabrefzone = uma_zcreate("UMA RCntSlabs", 1724099a0e58SBosko Milekic slabsize, 1725099a0e58SBosko Milekic NULL, NULL, NULL, NULL, 1726e66468eaSBosko Milekic UMA_ALIGN_PTR, 17277fd87882SBosko Milekic UMA_ZFLAG_INTERNAL); 1728099a0e58SBosko Milekic 17298355f576SJeff Roberson hashzone = uma_zcreate("UMA Hash", 17308355f576SJeff Roberson sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT, 17318355f576SJeff Roberson NULL, NULL, NULL, NULL, 1732b60f5b79SJeff Roberson UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL); 17338355f576SJeff Roberson 1734cae33c14SJeff Roberson bucket_init(); 17358355f576SJeff Roberson 1736f620b5bfSNathan Whitehorn #if defined(UMA_MD_SMALL_ALLOC) && !defined(UMA_MD_SMALL_ALLOC_NEEDS_VM) 173748eea375SJeff Roberson booted = 1; 173848eea375SJeff Roberson #endif 17398355f576SJeff Roberson 17408355f576SJeff Roberson #ifdef UMA_DEBUG 17418355f576SJeff Roberson printf("UMA startup complete.\n"); 17428355f576SJeff Roberson #endif 17438355f576SJeff Roberson } 17448355f576SJeff Roberson 17458355f576SJeff Roberson /* see uma.h */ 17468355f576SJeff Roberson void 174799571dc3SJeff Roberson uma_startup2(void) 17488355f576SJeff Roberson { 17498355f576SJeff Roberson booted = 1; 175086bbae32SJeff Roberson bucket_enable(); 17518355f576SJeff Roberson #ifdef UMA_DEBUG 17528355f576SJeff Roberson printf("UMA startup2 complete.\n"); 17538355f576SJeff Roberson #endif 17548355f576SJeff Roberson } 17558355f576SJeff Roberson 17568355f576SJeff Roberson /* 17578355f576SJeff Roberson * Initialize our callout handle 17588355f576SJeff Roberson * 17598355f576SJeff Roberson */ 17608355f576SJeff Roberson 17618355f576SJeff Roberson static void 17628355f576SJeff Roberson uma_startup3(void) 17638355f576SJeff Roberson { 17648355f576SJeff Roberson #ifdef UMA_DEBUG 17658355f576SJeff Roberson printf("Starting callout.\n"); 17668355f576SJeff Roberson #endif 1767a3c07611SRobert Watson callout_init(&uma_callout, CALLOUT_MPSAFE); 17689643769aSJeff Roberson callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); 17698355f576SJeff Roberson #ifdef UMA_DEBUG 17708355f576SJeff Roberson printf("UMA startup3 complete.\n"); 17718355f576SJeff Roberson #endif 17728355f576SJeff Roberson } 17738355f576SJeff Roberson 1774e20a199fSJeff Roberson static uma_keg_t 1775099a0e58SBosko Milekic uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini, 17762018f30cSMike Silbersack int align, u_int32_t flags) 1777099a0e58SBosko Milekic { 1778099a0e58SBosko Milekic struct uma_kctor_args args; 1779099a0e58SBosko Milekic 1780099a0e58SBosko Milekic args.size = size; 1781099a0e58SBosko Milekic args.uminit = uminit; 1782099a0e58SBosko Milekic args.fini = fini; 17831e319f6dSRobert Watson args.align = (align == UMA_ALIGN_CACHE) ? uma_align_cache : align; 1784099a0e58SBosko Milekic args.flags = flags; 1785099a0e58SBosko Milekic args.zone = zone; 1786e20a199fSJeff Roberson return (zone_alloc_item(kegs, &args, M_WAITOK)); 1787099a0e58SBosko Milekic } 1788099a0e58SBosko Milekic 17898355f576SJeff Roberson /* See uma.h */ 17901e319f6dSRobert Watson void 17911e319f6dSRobert Watson uma_set_align(int align) 17921e319f6dSRobert Watson { 17931e319f6dSRobert Watson 17941e319f6dSRobert Watson if (align != UMA_ALIGN_CACHE) 17951e319f6dSRobert Watson uma_align_cache = align; 17961e319f6dSRobert Watson } 17971e319f6dSRobert Watson 17981e319f6dSRobert Watson /* See uma.h */ 17998355f576SJeff Roberson uma_zone_t 1800c3bdc05fSAndrew R. Reiter uma_zcreate(char *name, size_t size, uma_ctor ctor, uma_dtor dtor, 18012018f30cSMike Silbersack uma_init uminit, uma_fini fini, int align, u_int32_t flags) 18028355f576SJeff Roberson 18038355f576SJeff Roberson { 18048355f576SJeff Roberson struct uma_zctor_args args; 18058355f576SJeff Roberson 18068355f576SJeff Roberson /* This stuff is essential for the zone ctor */ 18078355f576SJeff Roberson args.name = name; 18088355f576SJeff Roberson args.size = size; 18098355f576SJeff Roberson args.ctor = ctor; 18108355f576SJeff Roberson args.dtor = dtor; 18118355f576SJeff Roberson args.uminit = uminit; 18128355f576SJeff Roberson args.fini = fini; 18138355f576SJeff Roberson args.align = align; 18148355f576SJeff Roberson args.flags = flags; 1815099a0e58SBosko Milekic args.keg = NULL; 1816099a0e58SBosko Milekic 1817e20a199fSJeff Roberson return (zone_alloc_item(zones, &args, M_WAITOK)); 1818099a0e58SBosko Milekic } 1819099a0e58SBosko Milekic 1820099a0e58SBosko Milekic /* See uma.h */ 1821099a0e58SBosko Milekic uma_zone_t 1822099a0e58SBosko Milekic uma_zsecond_create(char *name, uma_ctor ctor, uma_dtor dtor, 1823099a0e58SBosko Milekic uma_init zinit, uma_fini zfini, uma_zone_t master) 1824099a0e58SBosko Milekic { 1825099a0e58SBosko Milekic struct uma_zctor_args args; 1826e20a199fSJeff Roberson uma_keg_t keg; 1827099a0e58SBosko Milekic 1828e20a199fSJeff Roberson keg = zone_first_keg(master); 1829099a0e58SBosko Milekic args.name = name; 1830e20a199fSJeff Roberson args.size = keg->uk_size; 1831099a0e58SBosko Milekic args.ctor = ctor; 1832099a0e58SBosko Milekic args.dtor = dtor; 1833099a0e58SBosko Milekic args.uminit = zinit; 1834099a0e58SBosko Milekic args.fini = zfini; 1835e20a199fSJeff Roberson args.align = keg->uk_align; 1836e20a199fSJeff Roberson args.flags = keg->uk_flags | UMA_ZONE_SECONDARY; 1837e20a199fSJeff Roberson args.keg = keg; 18388355f576SJeff Roberson 1839e20a199fSJeff Roberson /* XXX Attaches only one keg of potentially many. */ 1840e20a199fSJeff Roberson return (zone_alloc_item(zones, &args, M_WAITOK)); 18418355f576SJeff Roberson } 18428355f576SJeff Roberson 1843e20a199fSJeff Roberson static void 1844e20a199fSJeff Roberson zone_lock_pair(uma_zone_t a, uma_zone_t b) 1845e20a199fSJeff Roberson { 1846e20a199fSJeff Roberson if (a < b) { 1847e20a199fSJeff Roberson ZONE_LOCK(a); 1848e20a199fSJeff Roberson mtx_lock_flags(b->uz_lock, MTX_DUPOK); 1849e20a199fSJeff Roberson } else { 1850e20a199fSJeff Roberson ZONE_LOCK(b); 1851e20a199fSJeff Roberson mtx_lock_flags(a->uz_lock, MTX_DUPOK); 1852e20a199fSJeff Roberson } 1853e20a199fSJeff Roberson } 1854e20a199fSJeff Roberson 1855e20a199fSJeff Roberson static void 1856e20a199fSJeff Roberson zone_unlock_pair(uma_zone_t a, uma_zone_t b) 1857e20a199fSJeff Roberson { 1858e20a199fSJeff Roberson 1859e20a199fSJeff Roberson ZONE_UNLOCK(a); 1860e20a199fSJeff Roberson ZONE_UNLOCK(b); 1861e20a199fSJeff Roberson } 1862e20a199fSJeff Roberson 1863e20a199fSJeff Roberson int 1864e20a199fSJeff Roberson uma_zsecond_add(uma_zone_t zone, uma_zone_t master) 1865e20a199fSJeff Roberson { 1866e20a199fSJeff Roberson uma_klink_t klink; 1867e20a199fSJeff Roberson uma_klink_t kl; 1868e20a199fSJeff Roberson int error; 1869e20a199fSJeff Roberson 1870e20a199fSJeff Roberson error = 0; 1871e20a199fSJeff Roberson klink = malloc(sizeof(*klink), M_TEMP, M_WAITOK | M_ZERO); 1872e20a199fSJeff Roberson 1873e20a199fSJeff Roberson zone_lock_pair(zone, master); 1874e20a199fSJeff Roberson /* 1875e20a199fSJeff Roberson * zone must use vtoslab() to resolve objects and must already be 1876e20a199fSJeff Roberson * a secondary. 1877e20a199fSJeff Roberson */ 1878e20a199fSJeff Roberson if ((zone->uz_flags & (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) 1879e20a199fSJeff Roberson != (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) { 1880e20a199fSJeff Roberson error = EINVAL; 1881e20a199fSJeff Roberson goto out; 1882e20a199fSJeff Roberson } 1883e20a199fSJeff Roberson /* 1884e20a199fSJeff Roberson * The new master must also use vtoslab(). 1885e20a199fSJeff Roberson */ 1886e20a199fSJeff Roberson if ((zone->uz_flags & UMA_ZONE_VTOSLAB) != UMA_ZONE_VTOSLAB) { 1887e20a199fSJeff Roberson error = EINVAL; 1888e20a199fSJeff Roberson goto out; 1889e20a199fSJeff Roberson } 1890e20a199fSJeff Roberson /* 1891e20a199fSJeff Roberson * Both must either be refcnt, or not be refcnt. 1892e20a199fSJeff Roberson */ 1893e20a199fSJeff Roberson if ((zone->uz_flags & UMA_ZONE_REFCNT) != 1894e20a199fSJeff Roberson (master->uz_flags & UMA_ZONE_REFCNT)) { 1895e20a199fSJeff Roberson error = EINVAL; 1896e20a199fSJeff Roberson goto out; 1897e20a199fSJeff Roberson } 1898e20a199fSJeff Roberson /* 1899e20a199fSJeff Roberson * The underlying object must be the same size. rsize 1900e20a199fSJeff Roberson * may be different. 1901e20a199fSJeff Roberson */ 1902e20a199fSJeff Roberson if (master->uz_size != zone->uz_size) { 1903e20a199fSJeff Roberson error = E2BIG; 1904e20a199fSJeff Roberson goto out; 1905e20a199fSJeff Roberson } 1906e20a199fSJeff Roberson /* 1907e20a199fSJeff Roberson * Put it at the end of the list. 1908e20a199fSJeff Roberson */ 1909e20a199fSJeff Roberson klink->kl_keg = zone_first_keg(master); 1910e20a199fSJeff Roberson LIST_FOREACH(kl, &zone->uz_kegs, kl_link) { 1911e20a199fSJeff Roberson if (LIST_NEXT(kl, kl_link) == NULL) { 1912e20a199fSJeff Roberson LIST_INSERT_AFTER(kl, klink, kl_link); 1913e20a199fSJeff Roberson break; 1914e20a199fSJeff Roberson } 1915e20a199fSJeff Roberson } 1916e20a199fSJeff Roberson klink = NULL; 1917e20a199fSJeff Roberson zone->uz_flags |= UMA_ZFLAG_MULTI; 1918e20a199fSJeff Roberson zone->uz_slab = zone_fetch_slab_multi; 1919e20a199fSJeff Roberson 1920e20a199fSJeff Roberson out: 1921e20a199fSJeff Roberson zone_unlock_pair(zone, master); 1922e20a199fSJeff Roberson if (klink != NULL) 1923e20a199fSJeff Roberson free(klink, M_TEMP); 1924e20a199fSJeff Roberson 1925e20a199fSJeff Roberson return (error); 1926e20a199fSJeff Roberson } 1927e20a199fSJeff Roberson 1928e20a199fSJeff Roberson 19298355f576SJeff Roberson /* See uma.h */ 19309c2cd7e5SJeff Roberson void 19319c2cd7e5SJeff Roberson uma_zdestroy(uma_zone_t zone) 19329c2cd7e5SJeff Roberson { 1933f4ff923bSRobert Watson 1934e20a199fSJeff Roberson zone_free_item(zones, zone, NULL, SKIP_NONE, ZFREE_STATFREE); 19359c2cd7e5SJeff Roberson } 19369c2cd7e5SJeff Roberson 19379c2cd7e5SJeff Roberson /* See uma.h */ 19388355f576SJeff Roberson void * 19392cc35ff9SJeff Roberson uma_zalloc_arg(uma_zone_t zone, void *udata, int flags) 19408355f576SJeff Roberson { 19418355f576SJeff Roberson void *item; 19428355f576SJeff Roberson uma_cache_t cache; 19438355f576SJeff Roberson uma_bucket_t bucket; 19448355f576SJeff Roberson int cpu; 19458355f576SJeff Roberson 19468355f576SJeff Roberson /* This is the fast path allocation */ 19478355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC_1 19488355f576SJeff Roberson printf("Allocating one item from %s(%p)\n", zone->uz_name, zone); 19498355f576SJeff Roberson #endif 19503659f747SRobert Watson CTR3(KTR_UMA, "uma_zalloc_arg thread %x zone %s flags %d", curthread, 19513659f747SRobert Watson zone->uz_name, flags); 1952a553d4b8SJeff Roberson 1953635fd505SRobert Watson if (flags & M_WAITOK) { 1954b23f72e9SBrian Feldman WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, 1955635fd505SRobert Watson "uma_zalloc_arg: zone \"%s\"", zone->uz_name); 19564c1cc01cSJohn Baldwin } 19574c1cc01cSJohn Baldwin 19585d1ae027SRobert Watson /* 19595d1ae027SRobert Watson * If possible, allocate from the per-CPU cache. There are two 19605d1ae027SRobert Watson * requirements for safe access to the per-CPU cache: (1) the thread 19615d1ae027SRobert Watson * accessing the cache must not be preempted or yield during access, 19625d1ae027SRobert Watson * and (2) the thread must not migrate CPUs without switching which 19635d1ae027SRobert Watson * cache it accesses. We rely on a critical section to prevent 19645d1ae027SRobert Watson * preemption and migration. We release the critical section in 19655d1ae027SRobert Watson * order to acquire the zone mutex if we are unable to allocate from 19665d1ae027SRobert Watson * the current cache; when we re-acquire the critical section, we 19675d1ae027SRobert Watson * must detect and handle migration if it has occurred. 19685d1ae027SRobert Watson */ 1969a553d4b8SJeff Roberson zalloc_restart: 19705d1ae027SRobert Watson critical_enter(); 19715d1ae027SRobert Watson cpu = curcpu; 19728355f576SJeff Roberson cache = &zone->uz_cpu[cpu]; 19738355f576SJeff Roberson 19748355f576SJeff Roberson zalloc_start: 19758355f576SJeff Roberson bucket = cache->uc_allocbucket; 19768355f576SJeff Roberson 19778355f576SJeff Roberson if (bucket) { 1978cae33c14SJeff Roberson if (bucket->ub_cnt > 0) { 1979cae33c14SJeff Roberson bucket->ub_cnt--; 1980cae33c14SJeff Roberson item = bucket->ub_bucket[bucket->ub_cnt]; 19818355f576SJeff Roberson #ifdef INVARIANTS 1982cae33c14SJeff Roberson bucket->ub_bucket[bucket->ub_cnt] = NULL; 19838355f576SJeff Roberson #endif 19848355f576SJeff Roberson KASSERT(item != NULL, 19858355f576SJeff Roberson ("uma_zalloc: Bucket pointer mangled.")); 19868355f576SJeff Roberson cache->uc_allocs++; 19875d1ae027SRobert Watson critical_exit(); 1988639c9550SJeff Roberson #ifdef INVARIANTS 198981f71edaSMatt Jacob ZONE_LOCK(zone); 1990639c9550SJeff Roberson uma_dbg_alloc(zone, NULL, item); 199181f71edaSMatt Jacob ZONE_UNLOCK(zone); 1992639c9550SJeff Roberson #endif 1993b23f72e9SBrian Feldman if (zone->uz_ctor != NULL) { 1994e20a199fSJeff Roberson if (zone->uz_ctor(item, zone->uz_size, 1995b23f72e9SBrian Feldman udata, flags) != 0) { 1996e20a199fSJeff Roberson zone_free_item(zone, item, udata, 1997f4ff923bSRobert Watson SKIP_DTOR, ZFREE_STATFAIL | 1998f4ff923bSRobert Watson ZFREE_STATFREE); 1999b23f72e9SBrian Feldman return (NULL); 2000b23f72e9SBrian Feldman } 2001b23f72e9SBrian Feldman } 20022cc35ff9SJeff Roberson if (flags & M_ZERO) 2003e20a199fSJeff Roberson bzero(item, zone->uz_size); 20048355f576SJeff Roberson return (item); 20058355f576SJeff Roberson } else if (cache->uc_freebucket) { 20068355f576SJeff Roberson /* 20078355f576SJeff Roberson * We have run out of items in our allocbucket. 20088355f576SJeff Roberson * See if we can switch with our free bucket. 20098355f576SJeff Roberson */ 2010cae33c14SJeff Roberson if (cache->uc_freebucket->ub_cnt > 0) { 20118355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC 201264f051e9SJeff Roberson printf("uma_zalloc: Swapping empty with" 201364f051e9SJeff Roberson " alloc.\n"); 20148355f576SJeff Roberson #endif 2015b983089aSJeff Roberson bucket = cache->uc_freebucket; 20168355f576SJeff Roberson cache->uc_freebucket = cache->uc_allocbucket; 2017b983089aSJeff Roberson cache->uc_allocbucket = bucket; 20188355f576SJeff Roberson 20198355f576SJeff Roberson goto zalloc_start; 20208355f576SJeff Roberson } 20218355f576SJeff Roberson } 20228355f576SJeff Roberson } 20235d1ae027SRobert Watson /* 20245d1ae027SRobert Watson * Attempt to retrieve the item from the per-CPU cache has failed, so 20255d1ae027SRobert Watson * we must go back to the zone. This requires the zone lock, so we 20265d1ae027SRobert Watson * must drop the critical section, then re-acquire it when we go back 20275d1ae027SRobert Watson * to the cache. Since the critical section is released, we may be 20285d1ae027SRobert Watson * preempted or migrate. As such, make sure not to maintain any 20295d1ae027SRobert Watson * thread-local state specific to the cache from prior to releasing 20305d1ae027SRobert Watson * the critical section. 20315d1ae027SRobert Watson */ 20325d1ae027SRobert Watson critical_exit(); 2033a553d4b8SJeff Roberson ZONE_LOCK(zone); 20345d1ae027SRobert Watson critical_enter(); 20355d1ae027SRobert Watson cpu = curcpu; 20365d1ae027SRobert Watson cache = &zone->uz_cpu[cpu]; 20375d1ae027SRobert Watson bucket = cache->uc_allocbucket; 20385d1ae027SRobert Watson if (bucket != NULL) { 20395d1ae027SRobert Watson if (bucket->ub_cnt > 0) { 20405d1ae027SRobert Watson ZONE_UNLOCK(zone); 20415d1ae027SRobert Watson goto zalloc_start; 20425d1ae027SRobert Watson } 20435d1ae027SRobert Watson bucket = cache->uc_freebucket; 20445d1ae027SRobert Watson if (bucket != NULL && bucket->ub_cnt > 0) { 20455d1ae027SRobert Watson ZONE_UNLOCK(zone); 20465d1ae027SRobert Watson goto zalloc_start; 20475d1ae027SRobert Watson } 20485d1ae027SRobert Watson } 20495d1ae027SRobert Watson 2050a553d4b8SJeff Roberson /* Since we have locked the zone we may as well send back our stats */ 2051a553d4b8SJeff Roberson zone->uz_allocs += cache->uc_allocs; 2052a553d4b8SJeff Roberson cache->uc_allocs = 0; 2053773df9abSRobert Watson zone->uz_frees += cache->uc_frees; 2054773df9abSRobert Watson cache->uc_frees = 0; 20558355f576SJeff Roberson 2056a553d4b8SJeff Roberson /* Our old one is now a free bucket */ 2057a553d4b8SJeff Roberson if (cache->uc_allocbucket) { 2058cae33c14SJeff Roberson KASSERT(cache->uc_allocbucket->ub_cnt == 0, 2059a553d4b8SJeff Roberson ("uma_zalloc_arg: Freeing a non free bucket.")); 2060a553d4b8SJeff Roberson LIST_INSERT_HEAD(&zone->uz_free_bucket, 2061a553d4b8SJeff Roberson cache->uc_allocbucket, ub_link); 2062a553d4b8SJeff Roberson cache->uc_allocbucket = NULL; 2063a553d4b8SJeff Roberson } 20648355f576SJeff Roberson 2065a553d4b8SJeff Roberson /* Check the free list for a new alloc bucket */ 2066a553d4b8SJeff Roberson if ((bucket = LIST_FIRST(&zone->uz_full_bucket)) != NULL) { 2067cae33c14SJeff Roberson KASSERT(bucket->ub_cnt != 0, 2068a553d4b8SJeff Roberson ("uma_zalloc_arg: Returning an empty bucket.")); 20698355f576SJeff Roberson 2070a553d4b8SJeff Roberson LIST_REMOVE(bucket, ub_link); 2071a553d4b8SJeff Roberson cache->uc_allocbucket = bucket; 2072a553d4b8SJeff Roberson ZONE_UNLOCK(zone); 20738355f576SJeff Roberson goto zalloc_start; 2074a553d4b8SJeff Roberson } 20755d1ae027SRobert Watson /* We are no longer associated with this CPU. */ 20765d1ae027SRobert Watson critical_exit(); 2077bbee39c6SJeff Roberson 2078a553d4b8SJeff Roberson /* Bump up our uz_count so we get here less */ 2079cae33c14SJeff Roberson if (zone->uz_count < BUCKET_MAX) 2080a553d4b8SJeff Roberson zone->uz_count++; 2081099a0e58SBosko Milekic 20828355f576SJeff Roberson /* 2083a553d4b8SJeff Roberson * Now lets just fill a bucket and put it on the free list. If that 2084a553d4b8SJeff Roberson * works we'll restart the allocation from the begining. 2085bbee39c6SJeff Roberson */ 2086e20a199fSJeff Roberson if (zone_alloc_bucket(zone, flags)) { 2087bbee39c6SJeff Roberson ZONE_UNLOCK(zone); 2088bbee39c6SJeff Roberson goto zalloc_restart; 2089bbee39c6SJeff Roberson } 2090bbee39c6SJeff Roberson ZONE_UNLOCK(zone); 2091bbee39c6SJeff Roberson /* 2092bbee39c6SJeff Roberson * We may not be able to get a bucket so return an actual item. 2093bbee39c6SJeff Roberson */ 2094bbee39c6SJeff Roberson #ifdef UMA_DEBUG 2095bbee39c6SJeff Roberson printf("uma_zalloc_arg: Bucketzone returned NULL\n"); 2096bbee39c6SJeff Roberson #endif 2097bbee39c6SJeff Roberson 2098e20a199fSJeff Roberson item = zone_alloc_item(zone, udata, flags); 2099e20a199fSJeff Roberson return (item); 2100bbee39c6SJeff Roberson } 2101bbee39c6SJeff Roberson 2102bbee39c6SJeff Roberson static uma_slab_t 2103e20a199fSJeff Roberson keg_fetch_slab(uma_keg_t keg, uma_zone_t zone, int flags) 2104bbee39c6SJeff Roberson { 2105bbee39c6SJeff Roberson uma_slab_t slab; 2106099a0e58SBosko Milekic 2107e20a199fSJeff Roberson mtx_assert(&keg->uk_lock, MA_OWNED); 2108bbee39c6SJeff Roberson slab = NULL; 2109bbee39c6SJeff Roberson 2110bbee39c6SJeff Roberson for (;;) { 2111bbee39c6SJeff Roberson /* 2112bbee39c6SJeff Roberson * Find a slab with some space. Prefer slabs that are partially 2113bbee39c6SJeff Roberson * used over those that are totally full. This helps to reduce 2114bbee39c6SJeff Roberson * fragmentation. 2115bbee39c6SJeff Roberson */ 2116099a0e58SBosko Milekic if (keg->uk_free != 0) { 2117099a0e58SBosko Milekic if (!LIST_EMPTY(&keg->uk_part_slab)) { 2118099a0e58SBosko Milekic slab = LIST_FIRST(&keg->uk_part_slab); 2119bbee39c6SJeff Roberson } else { 2120099a0e58SBosko Milekic slab = LIST_FIRST(&keg->uk_free_slab); 2121bbee39c6SJeff Roberson LIST_REMOVE(slab, us_link); 2122099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_part_slab, slab, 2123bbee39c6SJeff Roberson us_link); 2124bbee39c6SJeff Roberson } 2125e20a199fSJeff Roberson MPASS(slab->us_keg == keg); 2126bbee39c6SJeff Roberson return (slab); 2127bbee39c6SJeff Roberson } 2128bbee39c6SJeff Roberson 2129bbee39c6SJeff Roberson /* 2130bbee39c6SJeff Roberson * M_NOVM means don't ask at all! 2131bbee39c6SJeff Roberson */ 2132bbee39c6SJeff Roberson if (flags & M_NOVM) 2133bbee39c6SJeff Roberson break; 2134bbee39c6SJeff Roberson 2135e20a199fSJeff Roberson if (keg->uk_maxpages && keg->uk_pages >= keg->uk_maxpages) { 2136099a0e58SBosko Milekic keg->uk_flags |= UMA_ZFLAG_FULL; 2137e20a199fSJeff Roberson /* 2138e20a199fSJeff Roberson * If this is not a multi-zone, set the FULL bit. 2139e20a199fSJeff Roberson * Otherwise slab_multi() takes care of it. 2140e20a199fSJeff Roberson */ 2141e20a199fSJeff Roberson if ((zone->uz_flags & UMA_ZFLAG_MULTI) == 0) 2142e20a199fSJeff Roberson zone->uz_flags |= UMA_ZFLAG_FULL; 2143ebc85edfSJeff Roberson if (flags & M_NOWAIT) 2144bbee39c6SJeff Roberson break; 2145e20a199fSJeff Roberson msleep(keg, &keg->uk_lock, PVM, "keglimit", 0); 2146bbee39c6SJeff Roberson continue; 2147bbee39c6SJeff Roberson } 2148099a0e58SBosko Milekic keg->uk_recurse++; 2149e20a199fSJeff Roberson slab = keg_alloc_slab(keg, zone, flags); 2150099a0e58SBosko Milekic keg->uk_recurse--; 2151bbee39c6SJeff Roberson /* 2152bbee39c6SJeff Roberson * If we got a slab here it's safe to mark it partially used 2153bbee39c6SJeff Roberson * and return. We assume that the caller is going to remove 2154bbee39c6SJeff Roberson * at least one item. 2155bbee39c6SJeff Roberson */ 2156bbee39c6SJeff Roberson if (slab) { 2157e20a199fSJeff Roberson MPASS(slab->us_keg == keg); 2158099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link); 2159bbee39c6SJeff Roberson return (slab); 2160bbee39c6SJeff Roberson } 2161bbee39c6SJeff Roberson /* 2162bbee39c6SJeff Roberson * We might not have been able to get a slab but another cpu 2163bbee39c6SJeff Roberson * could have while we were unlocked. Check again before we 2164bbee39c6SJeff Roberson * fail. 2165bbee39c6SJeff Roberson */ 2166bbee39c6SJeff Roberson flags |= M_NOVM; 2167bbee39c6SJeff Roberson } 2168bbee39c6SJeff Roberson return (slab); 2169bbee39c6SJeff Roberson } 2170bbee39c6SJeff Roberson 2171e20a199fSJeff Roberson static inline void 2172e20a199fSJeff Roberson zone_relock(uma_zone_t zone, uma_keg_t keg) 2173e20a199fSJeff Roberson { 2174e20a199fSJeff Roberson if (zone->uz_lock != &keg->uk_lock) { 2175e20a199fSJeff Roberson KEG_UNLOCK(keg); 2176e20a199fSJeff Roberson ZONE_LOCK(zone); 2177e20a199fSJeff Roberson } 2178e20a199fSJeff Roberson } 2179e20a199fSJeff Roberson 2180e20a199fSJeff Roberson static inline void 2181e20a199fSJeff Roberson keg_relock(uma_keg_t keg, uma_zone_t zone) 2182e20a199fSJeff Roberson { 2183e20a199fSJeff Roberson if (zone->uz_lock != &keg->uk_lock) { 2184e20a199fSJeff Roberson ZONE_UNLOCK(zone); 2185e20a199fSJeff Roberson KEG_LOCK(keg); 2186e20a199fSJeff Roberson } 2187e20a199fSJeff Roberson } 2188e20a199fSJeff Roberson 2189e20a199fSJeff Roberson static uma_slab_t 2190e20a199fSJeff Roberson zone_fetch_slab(uma_zone_t zone, uma_keg_t keg, int flags) 2191e20a199fSJeff Roberson { 2192e20a199fSJeff Roberson uma_slab_t slab; 2193e20a199fSJeff Roberson 2194e20a199fSJeff Roberson if (keg == NULL) 2195e20a199fSJeff Roberson keg = zone_first_keg(zone); 2196e20a199fSJeff Roberson /* 2197e20a199fSJeff Roberson * This is to prevent us from recursively trying to allocate 2198e20a199fSJeff Roberson * buckets. The problem is that if an allocation forces us to 2199e20a199fSJeff Roberson * grab a new bucket we will call page_alloc, which will go off 2200e20a199fSJeff Roberson * and cause the vm to allocate vm_map_entries. If we need new 2201e20a199fSJeff Roberson * buckets there too we will recurse in kmem_alloc and bad 2202e20a199fSJeff Roberson * things happen. So instead we return a NULL bucket, and make 2203e20a199fSJeff Roberson * the code that allocates buckets smart enough to deal with it 2204e20a199fSJeff Roberson */ 2205e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZFLAG_BUCKET && keg->uk_recurse != 0) 2206e20a199fSJeff Roberson return (NULL); 2207e20a199fSJeff Roberson 2208e20a199fSJeff Roberson for (;;) { 2209e20a199fSJeff Roberson slab = keg_fetch_slab(keg, zone, flags); 2210e20a199fSJeff Roberson if (slab) 2211e20a199fSJeff Roberson return (slab); 2212e20a199fSJeff Roberson if (flags & (M_NOWAIT | M_NOVM)) 2213e20a199fSJeff Roberson break; 2214e20a199fSJeff Roberson } 2215e20a199fSJeff Roberson return (NULL); 2216e20a199fSJeff Roberson } 2217e20a199fSJeff Roberson 2218e20a199fSJeff Roberson /* 2219e20a199fSJeff Roberson * uma_zone_fetch_slab_multi: Fetches a slab from one available keg. Returns 2220e20a199fSJeff Roberson * with the keg locked. Caller must call zone_relock() afterwards if the 2221e20a199fSJeff Roberson * zone lock is required. On NULL the zone lock is held. 2222e20a199fSJeff Roberson * 2223e20a199fSJeff Roberson * The last pointer is used to seed the search. It is not required. 2224e20a199fSJeff Roberson */ 2225e20a199fSJeff Roberson static uma_slab_t 2226e20a199fSJeff Roberson zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int rflags) 2227e20a199fSJeff Roberson { 2228e20a199fSJeff Roberson uma_klink_t klink; 2229e20a199fSJeff Roberson uma_slab_t slab; 2230e20a199fSJeff Roberson uma_keg_t keg; 2231e20a199fSJeff Roberson int flags; 2232e20a199fSJeff Roberson int empty; 2233e20a199fSJeff Roberson int full; 2234e20a199fSJeff Roberson 2235e20a199fSJeff Roberson /* 2236e20a199fSJeff Roberson * Don't wait on the first pass. This will skip limit tests 2237e20a199fSJeff Roberson * as well. We don't want to block if we can find a provider 2238e20a199fSJeff Roberson * without blocking. 2239e20a199fSJeff Roberson */ 2240e20a199fSJeff Roberson flags = (rflags & ~M_WAITOK) | M_NOWAIT; 2241e20a199fSJeff Roberson /* 2242e20a199fSJeff Roberson * Use the last slab allocated as a hint for where to start 2243e20a199fSJeff Roberson * the search. 2244e20a199fSJeff Roberson */ 2245e20a199fSJeff Roberson if (last) { 2246e20a199fSJeff Roberson slab = keg_fetch_slab(last, zone, flags); 2247e20a199fSJeff Roberson if (slab) 2248e20a199fSJeff Roberson return (slab); 2249e20a199fSJeff Roberson zone_relock(zone, last); 2250e20a199fSJeff Roberson last = NULL; 2251e20a199fSJeff Roberson } 2252e20a199fSJeff Roberson /* 2253e20a199fSJeff Roberson * Loop until we have a slab incase of transient failures 2254e20a199fSJeff Roberson * while M_WAITOK is specified. I'm not sure this is 100% 2255e20a199fSJeff Roberson * required but we've done it for so long now. 2256e20a199fSJeff Roberson */ 2257e20a199fSJeff Roberson for (;;) { 2258e20a199fSJeff Roberson empty = 0; 2259e20a199fSJeff Roberson full = 0; 2260e20a199fSJeff Roberson /* 2261e20a199fSJeff Roberson * Search the available kegs for slabs. Be careful to hold the 2262e20a199fSJeff Roberson * correct lock while calling into the keg layer. 2263e20a199fSJeff Roberson */ 2264e20a199fSJeff Roberson LIST_FOREACH(klink, &zone->uz_kegs, kl_link) { 2265e20a199fSJeff Roberson keg = klink->kl_keg; 2266e20a199fSJeff Roberson keg_relock(keg, zone); 2267e20a199fSJeff Roberson if ((keg->uk_flags & UMA_ZFLAG_FULL) == 0) { 2268e20a199fSJeff Roberson slab = keg_fetch_slab(keg, zone, flags); 2269e20a199fSJeff Roberson if (slab) 2270e20a199fSJeff Roberson return (slab); 2271e20a199fSJeff Roberson } 2272e20a199fSJeff Roberson if (keg->uk_flags & UMA_ZFLAG_FULL) 2273e20a199fSJeff Roberson full++; 2274e20a199fSJeff Roberson else 2275e20a199fSJeff Roberson empty++; 2276e20a199fSJeff Roberson zone_relock(zone, keg); 2277e20a199fSJeff Roberson } 2278e20a199fSJeff Roberson if (rflags & (M_NOWAIT | M_NOVM)) 2279e20a199fSJeff Roberson break; 2280e20a199fSJeff Roberson flags = rflags; 2281e20a199fSJeff Roberson /* 2282e20a199fSJeff Roberson * All kegs are full. XXX We can't atomically check all kegs 2283e20a199fSJeff Roberson * and sleep so just sleep for a short period and retry. 2284e20a199fSJeff Roberson */ 2285e20a199fSJeff Roberson if (full && !empty) { 2286e20a199fSJeff Roberson zone->uz_flags |= UMA_ZFLAG_FULL; 2287bf965959SSean Bruno zone->uz_sleeps++; 2288e20a199fSJeff Roberson msleep(zone, zone->uz_lock, PVM, "zonelimit", hz/100); 2289e20a199fSJeff Roberson zone->uz_flags &= ~UMA_ZFLAG_FULL; 2290e20a199fSJeff Roberson continue; 2291e20a199fSJeff Roberson } 2292e20a199fSJeff Roberson } 2293e20a199fSJeff Roberson return (NULL); 2294e20a199fSJeff Roberson } 2295e20a199fSJeff Roberson 2296d56368d7SBosko Milekic static void * 2297e20a199fSJeff Roberson slab_alloc_item(uma_zone_t zone, uma_slab_t slab) 2298bbee39c6SJeff Roberson { 2299099a0e58SBosko Milekic uma_keg_t keg; 2300ab14a3f7SBrian Feldman uma_slabrefcnt_t slabref; 2301bbee39c6SJeff Roberson void *item; 2302bbee39c6SJeff Roberson u_int8_t freei; 2303bbee39c6SJeff Roberson 2304e20a199fSJeff Roberson keg = slab->us_keg; 2305e20a199fSJeff Roberson mtx_assert(&keg->uk_lock, MA_OWNED); 2306099a0e58SBosko Milekic 2307bbee39c6SJeff Roberson freei = slab->us_firstfree; 2308ab14a3f7SBrian Feldman if (keg->uk_flags & UMA_ZONE_REFCNT) { 2309ab14a3f7SBrian Feldman slabref = (uma_slabrefcnt_t)slab; 2310ab14a3f7SBrian Feldman slab->us_firstfree = slabref->us_freelist[freei].us_item; 2311ab14a3f7SBrian Feldman } else { 2312099a0e58SBosko Milekic slab->us_firstfree = slab->us_freelist[freei].us_item; 2313ab14a3f7SBrian Feldman } 2314099a0e58SBosko Milekic item = slab->us_data + (keg->uk_rsize * freei); 2315bbee39c6SJeff Roberson 2316bbee39c6SJeff Roberson slab->us_freecount--; 2317099a0e58SBosko Milekic keg->uk_free--; 2318bbee39c6SJeff Roberson #ifdef INVARIANTS 2319bbee39c6SJeff Roberson uma_dbg_alloc(zone, slab, item); 2320bbee39c6SJeff Roberson #endif 2321bbee39c6SJeff Roberson /* Move this slab to the full list */ 2322bbee39c6SJeff Roberson if (slab->us_freecount == 0) { 2323bbee39c6SJeff Roberson LIST_REMOVE(slab, us_link); 2324099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_full_slab, slab, us_link); 2325bbee39c6SJeff Roberson } 2326bbee39c6SJeff Roberson 2327bbee39c6SJeff Roberson return (item); 2328bbee39c6SJeff Roberson } 2329bbee39c6SJeff Roberson 2330bbee39c6SJeff Roberson static int 2331e20a199fSJeff Roberson zone_alloc_bucket(uma_zone_t zone, int flags) 2332bbee39c6SJeff Roberson { 2333bbee39c6SJeff Roberson uma_bucket_t bucket; 2334bbee39c6SJeff Roberson uma_slab_t slab; 2335e20a199fSJeff Roberson uma_keg_t keg; 2336099a0e58SBosko Milekic int16_t saved; 2337b23f72e9SBrian Feldman int max, origflags = flags; 2338bbee39c6SJeff Roberson 2339bbee39c6SJeff Roberson /* 2340a553d4b8SJeff Roberson * Try this zone's free list first so we don't allocate extra buckets. 23418355f576SJeff Roberson */ 2342bbee39c6SJeff Roberson if ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) { 2343cae33c14SJeff Roberson KASSERT(bucket->ub_cnt == 0, 2344e20a199fSJeff Roberson ("zone_alloc_bucket: Bucket on free list is not empty.")); 2345a553d4b8SJeff Roberson LIST_REMOVE(bucket, ub_link); 2346bbee39c6SJeff Roberson } else { 234718aa2de5SJeff Roberson int bflags; 234818aa2de5SJeff Roberson 2349cae33c14SJeff Roberson bflags = (flags & ~M_ZERO); 2350e20a199fSJeff Roberson if (zone->uz_flags & UMA_ZFLAG_CACHEONLY) 235118aa2de5SJeff Roberson bflags |= M_NOVM; 235218aa2de5SJeff Roberson 2353bbee39c6SJeff Roberson ZONE_UNLOCK(zone); 2354cae33c14SJeff Roberson bucket = bucket_alloc(zone->uz_count, bflags); 2355bbee39c6SJeff Roberson ZONE_LOCK(zone); 2356bbee39c6SJeff Roberson } 2357bbee39c6SJeff Roberson 2358e20a199fSJeff Roberson if (bucket == NULL) { 2359bbee39c6SJeff Roberson return (0); 2360e20a199fSJeff Roberson } 2361bbee39c6SJeff Roberson 2362bbee39c6SJeff Roberson #ifdef SMP 2363a553d4b8SJeff Roberson /* 2364bbee39c6SJeff Roberson * This code is here to limit the number of simultaneous bucket fills 2365bbee39c6SJeff Roberson * for any given zone to the number of per cpu caches in this zone. This 2366bbee39c6SJeff Roberson * is done so that we don't allocate more memory than we really need. 2367a553d4b8SJeff Roberson */ 2368bbee39c6SJeff Roberson if (zone->uz_fills >= mp_ncpus) 2369bbee39c6SJeff Roberson goto done; 2370a553d4b8SJeff Roberson 2371bbee39c6SJeff Roberson #endif 2372bbee39c6SJeff Roberson zone->uz_fills++; 2373bbee39c6SJeff Roberson 237444eca34aSJeff Roberson max = MIN(bucket->ub_entries, zone->uz_count); 2375bbee39c6SJeff Roberson /* Try to keep the buckets totally full */ 2376099a0e58SBosko Milekic saved = bucket->ub_cnt; 2377e20a199fSJeff Roberson slab = NULL; 2378e20a199fSJeff Roberson keg = NULL; 237944eca34aSJeff Roberson while (bucket->ub_cnt < max && 2380e20a199fSJeff Roberson (slab = zone->uz_slab(zone, keg, flags)) != NULL) { 2381e20a199fSJeff Roberson keg = slab->us_keg; 238244eca34aSJeff Roberson while (slab->us_freecount && bucket->ub_cnt < max) { 2383cae33c14SJeff Roberson bucket->ub_bucket[bucket->ub_cnt++] = 2384e20a199fSJeff Roberson slab_alloc_item(zone, slab); 2385bbee39c6SJeff Roberson } 2386099a0e58SBosko Milekic 2387bbee39c6SJeff Roberson /* Don't block on the next fill */ 2388bbee39c6SJeff Roberson flags |= M_NOWAIT; 23898355f576SJeff Roberson } 2390e20a199fSJeff Roberson if (slab) 2391e20a199fSJeff Roberson zone_relock(zone, keg); 23928355f576SJeff Roberson 2393099a0e58SBosko Milekic /* 2394099a0e58SBosko Milekic * We unlock here because we need to call the zone's init. 2395099a0e58SBosko Milekic * It should be safe to unlock because the slab dealt with 2396099a0e58SBosko Milekic * above is already on the appropriate list within the keg 2397099a0e58SBosko Milekic * and the bucket we filled is not yet on any list, so we 2398099a0e58SBosko Milekic * own it. 2399099a0e58SBosko Milekic */ 2400099a0e58SBosko Milekic if (zone->uz_init != NULL) { 2401099a0e58SBosko Milekic int i; 2402bbee39c6SJeff Roberson 2403099a0e58SBosko Milekic ZONE_UNLOCK(zone); 2404099a0e58SBosko Milekic for (i = saved; i < bucket->ub_cnt; i++) 2405e20a199fSJeff Roberson if (zone->uz_init(bucket->ub_bucket[i], zone->uz_size, 2406e20a199fSJeff Roberson origflags) != 0) 2407b23f72e9SBrian Feldman break; 2408b23f72e9SBrian Feldman /* 2409b23f72e9SBrian Feldman * If we couldn't initialize the whole bucket, put the 2410b23f72e9SBrian Feldman * rest back onto the freelist. 2411b23f72e9SBrian Feldman */ 2412b23f72e9SBrian Feldman if (i != bucket->ub_cnt) { 2413b23f72e9SBrian Feldman int j; 2414b23f72e9SBrian Feldman 2415a5a262c6SBosko Milekic for (j = i; j < bucket->ub_cnt; j++) { 2416e20a199fSJeff Roberson zone_free_item(zone, bucket->ub_bucket[j], 24172019094aSRobert Watson NULL, SKIP_FINI, 0); 2418a5a262c6SBosko Milekic #ifdef INVARIANTS 2419a5a262c6SBosko Milekic bucket->ub_bucket[j] = NULL; 2420a5a262c6SBosko Milekic #endif 2421a5a262c6SBosko Milekic } 2422b23f72e9SBrian Feldman bucket->ub_cnt = i; 2423b23f72e9SBrian Feldman } 2424099a0e58SBosko Milekic ZONE_LOCK(zone); 2425099a0e58SBosko Milekic } 2426099a0e58SBosko Milekic 2427099a0e58SBosko Milekic zone->uz_fills--; 2428cae33c14SJeff Roberson if (bucket->ub_cnt != 0) { 2429bbee39c6SJeff Roberson LIST_INSERT_HEAD(&zone->uz_full_bucket, 2430bbee39c6SJeff Roberson bucket, ub_link); 2431bbee39c6SJeff Roberson return (1); 2432bbee39c6SJeff Roberson } 2433bbee39c6SJeff Roberson #ifdef SMP 2434bbee39c6SJeff Roberson done: 2435bbee39c6SJeff Roberson #endif 2436cae33c14SJeff Roberson bucket_free(bucket); 2437bbee39c6SJeff Roberson 2438bbee39c6SJeff Roberson return (0); 2439bbee39c6SJeff Roberson } 24408355f576SJeff Roberson /* 2441bbee39c6SJeff Roberson * Allocates an item for an internal zone 24428355f576SJeff Roberson * 24438355f576SJeff Roberson * Arguments 24448355f576SJeff Roberson * zone The zone to alloc for. 24458355f576SJeff Roberson * udata The data to be passed to the constructor. 2446a163d034SWarner Losh * flags M_WAITOK, M_NOWAIT, M_ZERO. 24478355f576SJeff Roberson * 24488355f576SJeff Roberson * Returns 24498355f576SJeff Roberson * NULL if there is no memory and M_NOWAIT is set 2450bbee39c6SJeff Roberson * An item if successful 24518355f576SJeff Roberson */ 24528355f576SJeff Roberson 24538355f576SJeff Roberson static void * 2454e20a199fSJeff Roberson zone_alloc_item(uma_zone_t zone, void *udata, int flags) 24558355f576SJeff Roberson { 24568355f576SJeff Roberson uma_slab_t slab; 24578355f576SJeff Roberson void *item; 24588355f576SJeff Roberson 24598355f576SJeff Roberson item = NULL; 24608355f576SJeff Roberson 24618355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC 24628355f576SJeff Roberson printf("INTERNAL: Allocating one item from %s(%p)\n", zone->uz_name, zone); 24638355f576SJeff Roberson #endif 24648355f576SJeff Roberson ZONE_LOCK(zone); 24658355f576SJeff Roberson 2466e20a199fSJeff Roberson slab = zone->uz_slab(zone, NULL, flags); 2467bbee39c6SJeff Roberson if (slab == NULL) { 24682019094aSRobert Watson zone->uz_fails++; 2469bce97791SJeff Roberson ZONE_UNLOCK(zone); 2470a553d4b8SJeff Roberson return (NULL); 2471bce97791SJeff Roberson } 2472a553d4b8SJeff Roberson 2473e20a199fSJeff Roberson item = slab_alloc_item(zone, slab); 24748355f576SJeff Roberson 2475e20a199fSJeff Roberson zone_relock(zone, slab->us_keg); 24762c743d36SRobert Watson zone->uz_allocs++; 24778355f576SJeff Roberson ZONE_UNLOCK(zone); 24788355f576SJeff Roberson 2479099a0e58SBosko Milekic /* 2480099a0e58SBosko Milekic * We have to call both the zone's init (not the keg's init) 2481099a0e58SBosko Milekic * and the zone's ctor. This is because the item is going from 2482099a0e58SBosko Milekic * a keg slab directly to the user, and the user is expecting it 2483099a0e58SBosko Milekic * to be both zone-init'd as well as zone-ctor'd. 2484099a0e58SBosko Milekic */ 2485b23f72e9SBrian Feldman if (zone->uz_init != NULL) { 2486e20a199fSJeff Roberson if (zone->uz_init(item, zone->uz_size, flags) != 0) { 2487e20a199fSJeff Roberson zone_free_item(zone, item, udata, SKIP_FINI, 2488f4ff923bSRobert Watson ZFREE_STATFAIL | ZFREE_STATFREE); 2489b23f72e9SBrian Feldman return (NULL); 2490b23f72e9SBrian Feldman } 2491b23f72e9SBrian Feldman } 2492b23f72e9SBrian Feldman if (zone->uz_ctor != NULL) { 2493e20a199fSJeff Roberson if (zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) { 2494e20a199fSJeff Roberson zone_free_item(zone, item, udata, SKIP_DTOR, 2495f4ff923bSRobert Watson ZFREE_STATFAIL | ZFREE_STATFREE); 2496b23f72e9SBrian Feldman return (NULL); 2497b23f72e9SBrian Feldman } 2498b23f72e9SBrian Feldman } 24992cc35ff9SJeff Roberson if (flags & M_ZERO) 2500e20a199fSJeff Roberson bzero(item, zone->uz_size); 25018355f576SJeff Roberson 25028355f576SJeff Roberson return (item); 25038355f576SJeff Roberson } 25048355f576SJeff Roberson 25058355f576SJeff Roberson /* See uma.h */ 25068355f576SJeff Roberson void 25078355f576SJeff Roberson uma_zfree_arg(uma_zone_t zone, void *item, void *udata) 25088355f576SJeff Roberson { 25098355f576SJeff Roberson uma_cache_t cache; 25108355f576SJeff Roberson uma_bucket_t bucket; 25114741dcbfSJeff Roberson int bflags; 25128355f576SJeff Roberson int cpu; 25138355f576SJeff Roberson 25148355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC_1 25158355f576SJeff Roberson printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone); 25168355f576SJeff Roberson #endif 25173659f747SRobert Watson CTR2(KTR_UMA, "uma_zfree_arg thread %x zone %s", curthread, 25183659f747SRobert Watson zone->uz_name); 25193659f747SRobert Watson 2520*20ed0cb0SMatthew D Fleming /* uma_zfree(..., NULL) does nothing, to match free(9). */ 2521*20ed0cb0SMatthew D Fleming if (item == NULL) 2522*20ed0cb0SMatthew D Fleming return; 2523*20ed0cb0SMatthew D Fleming 25245d1ae027SRobert Watson if (zone->uz_dtor) 2525e20a199fSJeff Roberson zone->uz_dtor(item, zone->uz_size, udata); 2526e20a199fSJeff Roberson 25275d1ae027SRobert Watson #ifdef INVARIANTS 25285d1ae027SRobert Watson ZONE_LOCK(zone); 2529e20a199fSJeff Roberson if (zone->uz_flags & UMA_ZONE_MALLOC) 25305d1ae027SRobert Watson uma_dbg_free(zone, udata, item); 25315d1ae027SRobert Watson else 25325d1ae027SRobert Watson uma_dbg_free(zone, NULL, item); 25335d1ae027SRobert Watson ZONE_UNLOCK(zone); 25345d1ae027SRobert Watson #endif 2535af7f9b97SJeff Roberson /* 2536af7f9b97SJeff Roberson * The race here is acceptable. If we miss it we'll just have to wait 2537af7f9b97SJeff Roberson * a little longer for the limits to be reset. 2538af7f9b97SJeff Roberson */ 2539e20a199fSJeff Roberson if (zone->uz_flags & UMA_ZFLAG_FULL) 2540af7f9b97SJeff Roberson goto zfree_internal; 2541af7f9b97SJeff Roberson 25425d1ae027SRobert Watson /* 25435d1ae027SRobert Watson * If possible, free to the per-CPU cache. There are two 25445d1ae027SRobert Watson * requirements for safe access to the per-CPU cache: (1) the thread 25455d1ae027SRobert Watson * accessing the cache must not be preempted or yield during access, 25465d1ae027SRobert Watson * and (2) the thread must not migrate CPUs without switching which 25475d1ae027SRobert Watson * cache it accesses. We rely on a critical section to prevent 25485d1ae027SRobert Watson * preemption and migration. We release the critical section in 25495d1ae027SRobert Watson * order to acquire the zone mutex if we are unable to free to the 25505d1ae027SRobert Watson * current cache; when we re-acquire the critical section, we must 25515d1ae027SRobert Watson * detect and handle migration if it has occurred. 25525d1ae027SRobert Watson */ 2553a553d4b8SJeff Roberson zfree_restart: 25545d1ae027SRobert Watson critical_enter(); 25555d1ae027SRobert Watson cpu = curcpu; 25568355f576SJeff Roberson cache = &zone->uz_cpu[cpu]; 25578355f576SJeff Roberson 25588355f576SJeff Roberson zfree_start: 25598355f576SJeff Roberson bucket = cache->uc_freebucket; 25608355f576SJeff Roberson 25618355f576SJeff Roberson if (bucket) { 2562a553d4b8SJeff Roberson /* 2563a553d4b8SJeff Roberson * Do we have room in our bucket? It is OK for this uz count 2564a553d4b8SJeff Roberson * check to be slightly out of sync. 2565a553d4b8SJeff Roberson */ 2566a553d4b8SJeff Roberson 2567cae33c14SJeff Roberson if (bucket->ub_cnt < bucket->ub_entries) { 2568cae33c14SJeff Roberson KASSERT(bucket->ub_bucket[bucket->ub_cnt] == NULL, 25698355f576SJeff Roberson ("uma_zfree: Freeing to non free bucket index.")); 2570cae33c14SJeff Roberson bucket->ub_bucket[bucket->ub_cnt] = item; 2571cae33c14SJeff Roberson bucket->ub_cnt++; 2572773df9abSRobert Watson cache->uc_frees++; 25735d1ae027SRobert Watson critical_exit(); 25748355f576SJeff Roberson return; 25758355f576SJeff Roberson } else if (cache->uc_allocbucket) { 25768355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC 25778355f576SJeff Roberson printf("uma_zfree: Swapping buckets.\n"); 25788355f576SJeff Roberson #endif 25798355f576SJeff Roberson /* 25808355f576SJeff Roberson * We have run out of space in our freebucket. 25818355f576SJeff Roberson * See if we can switch with our alloc bucket. 25828355f576SJeff Roberson */ 2583cae33c14SJeff Roberson if (cache->uc_allocbucket->ub_cnt < 2584cae33c14SJeff Roberson cache->uc_freebucket->ub_cnt) { 2585b983089aSJeff Roberson bucket = cache->uc_freebucket; 25868355f576SJeff Roberson cache->uc_freebucket = cache->uc_allocbucket; 2587b983089aSJeff Roberson cache->uc_allocbucket = bucket; 25888355f576SJeff Roberson goto zfree_start; 25898355f576SJeff Roberson } 25908355f576SJeff Roberson } 25918355f576SJeff Roberson } 25928355f576SJeff Roberson /* 2593a553d4b8SJeff Roberson * We can get here for two reasons: 25948355f576SJeff Roberson * 25958355f576SJeff Roberson * 1) The buckets are NULL 2596a553d4b8SJeff Roberson * 2) The alloc and free buckets are both somewhat full. 25975d1ae027SRobert Watson * 25985d1ae027SRobert Watson * We must go back the zone, which requires acquiring the zone lock, 25995d1ae027SRobert Watson * which in turn means we must release and re-acquire the critical 26005d1ae027SRobert Watson * section. Since the critical section is released, we may be 26015d1ae027SRobert Watson * preempted or migrate. As such, make sure not to maintain any 26025d1ae027SRobert Watson * thread-local state specific to the cache from prior to releasing 26035d1ae027SRobert Watson * the critical section. 26048355f576SJeff Roberson */ 26055d1ae027SRobert Watson critical_exit(); 26068355f576SJeff Roberson ZONE_LOCK(zone); 26075d1ae027SRobert Watson critical_enter(); 26085d1ae027SRobert Watson cpu = curcpu; 26095d1ae027SRobert Watson cache = &zone->uz_cpu[cpu]; 26105d1ae027SRobert Watson if (cache->uc_freebucket != NULL) { 26115d1ae027SRobert Watson if (cache->uc_freebucket->ub_cnt < 26125d1ae027SRobert Watson cache->uc_freebucket->ub_entries) { 26135d1ae027SRobert Watson ZONE_UNLOCK(zone); 26145d1ae027SRobert Watson goto zfree_start; 26155d1ae027SRobert Watson } 26165d1ae027SRobert Watson if (cache->uc_allocbucket != NULL && 26175d1ae027SRobert Watson (cache->uc_allocbucket->ub_cnt < 26185d1ae027SRobert Watson cache->uc_freebucket->ub_cnt)) { 26195d1ae027SRobert Watson ZONE_UNLOCK(zone); 26205d1ae027SRobert Watson goto zfree_start; 26215d1ae027SRobert Watson } 26225d1ae027SRobert Watson } 26238355f576SJeff Roberson 2624f4ff923bSRobert Watson /* Since we have locked the zone we may as well send back our stats */ 2625f4ff923bSRobert Watson zone->uz_allocs += cache->uc_allocs; 2626f4ff923bSRobert Watson cache->uc_allocs = 0; 2627f4ff923bSRobert Watson zone->uz_frees += cache->uc_frees; 2628f4ff923bSRobert Watson cache->uc_frees = 0; 2629f4ff923bSRobert Watson 26308355f576SJeff Roberson bucket = cache->uc_freebucket; 26318355f576SJeff Roberson cache->uc_freebucket = NULL; 26328355f576SJeff Roberson 26338355f576SJeff Roberson /* Can we throw this on the zone full list? */ 26348355f576SJeff Roberson if (bucket != NULL) { 26358355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC 26368355f576SJeff Roberson printf("uma_zfree: Putting old bucket on the free list.\n"); 26378355f576SJeff Roberson #endif 2638cae33c14SJeff Roberson /* ub_cnt is pointing to the last free item */ 2639cae33c14SJeff Roberson KASSERT(bucket->ub_cnt != 0, 26408355f576SJeff Roberson ("uma_zfree: Attempting to insert an empty bucket onto the full list.\n")); 26418355f576SJeff Roberson LIST_INSERT_HEAD(&zone->uz_full_bucket, 26428355f576SJeff Roberson bucket, ub_link); 26438355f576SJeff Roberson } 2644a553d4b8SJeff Roberson if ((bucket = LIST_FIRST(&zone->uz_free_bucket)) != NULL) { 2645a553d4b8SJeff Roberson LIST_REMOVE(bucket, ub_link); 2646a553d4b8SJeff Roberson ZONE_UNLOCK(zone); 2647a553d4b8SJeff Roberson cache->uc_freebucket = bucket; 2648a553d4b8SJeff Roberson goto zfree_start; 2649a553d4b8SJeff Roberson } 26505d1ae027SRobert Watson /* We are no longer associated with this CPU. */ 26515d1ae027SRobert Watson critical_exit(); 2652a553d4b8SJeff Roberson 2653a553d4b8SJeff Roberson /* And the zone.. */ 2654a553d4b8SJeff Roberson ZONE_UNLOCK(zone); 2655a553d4b8SJeff Roberson 26568355f576SJeff Roberson #ifdef UMA_DEBUG_ALLOC 26578355f576SJeff Roberson printf("uma_zfree: Allocating new free bucket.\n"); 26588355f576SJeff Roberson #endif 26594741dcbfSJeff Roberson bflags = M_NOWAIT; 26604741dcbfSJeff Roberson 2661e20a199fSJeff Roberson if (zone->uz_flags & UMA_ZFLAG_CACHEONLY) 26624741dcbfSJeff Roberson bflags |= M_NOVM; 2663cae33c14SJeff Roberson bucket = bucket_alloc(zone->uz_count, bflags); 26644741dcbfSJeff Roberson if (bucket) { 2665a553d4b8SJeff Roberson ZONE_LOCK(zone); 2666a553d4b8SJeff Roberson LIST_INSERT_HEAD(&zone->uz_free_bucket, 2667a553d4b8SJeff Roberson bucket, ub_link); 26688355f576SJeff Roberson ZONE_UNLOCK(zone); 2669a553d4b8SJeff Roberson goto zfree_restart; 26708355f576SJeff Roberson } 26718355f576SJeff Roberson 2672a553d4b8SJeff Roberson /* 2673a553d4b8SJeff Roberson * If nothing else caught this, we'll just do an internal free. 2674a553d4b8SJeff Roberson */ 2675af7f9b97SJeff Roberson zfree_internal: 2676e20a199fSJeff Roberson zone_free_item(zone, item, udata, SKIP_DTOR, ZFREE_STATFREE); 26778355f576SJeff Roberson 26788355f576SJeff Roberson return; 26798355f576SJeff Roberson } 26808355f576SJeff Roberson 26818355f576SJeff Roberson /* 26828355f576SJeff Roberson * Frees an item to an INTERNAL zone or allocates a free bucket 26838355f576SJeff Roberson * 26848355f576SJeff Roberson * Arguments: 26858355f576SJeff Roberson * zone The zone to free to 26868355f576SJeff Roberson * item The item we're freeing 26878355f576SJeff Roberson * udata User supplied data for the dtor 2688b23f72e9SBrian Feldman * skip Skip dtors and finis 26898355f576SJeff Roberson */ 26908355f576SJeff Roberson static void 2691e20a199fSJeff Roberson zone_free_item(uma_zone_t zone, void *item, void *udata, 26922019094aSRobert Watson enum zfreeskip skip, int flags) 26938355f576SJeff Roberson { 26948355f576SJeff Roberson uma_slab_t slab; 2695ab14a3f7SBrian Feldman uma_slabrefcnt_t slabref; 2696099a0e58SBosko Milekic uma_keg_t keg; 26978355f576SJeff Roberson u_int8_t *mem; 26988355f576SJeff Roberson u_int8_t freei; 2699e20a199fSJeff Roberson int clearfull; 2700099a0e58SBosko Milekic 2701b23f72e9SBrian Feldman if (skip < SKIP_DTOR && zone->uz_dtor) 2702e20a199fSJeff Roberson zone->uz_dtor(item, zone->uz_size, udata); 2703e20a199fSJeff Roberson 2704b23f72e9SBrian Feldman if (skip < SKIP_FINI && zone->uz_fini) 2705e20a199fSJeff Roberson zone->uz_fini(item, zone->uz_size); 2706bba739abSJeff Roberson 27078355f576SJeff Roberson ZONE_LOCK(zone); 27088355f576SJeff Roberson 27092019094aSRobert Watson if (flags & ZFREE_STATFAIL) 27102019094aSRobert Watson zone->uz_fails++; 2711f4ff923bSRobert Watson if (flags & ZFREE_STATFREE) 2712f4ff923bSRobert Watson zone->uz_frees++; 27132019094aSRobert Watson 2714e20a199fSJeff Roberson if (!(zone->uz_flags & UMA_ZONE_VTOSLAB)) { 27158355f576SJeff Roberson mem = (u_int8_t *)((unsigned long)item & (~UMA_SLAB_MASK)); 2716e20a199fSJeff Roberson keg = zone_first_keg(zone); /* Must only be one. */ 2717e20a199fSJeff Roberson if (zone->uz_flags & UMA_ZONE_HASH) { 2718099a0e58SBosko Milekic slab = hash_sfind(&keg->uk_hash, mem); 2719e20a199fSJeff Roberson } else { 2720099a0e58SBosko Milekic mem += keg->uk_pgoff; 27218355f576SJeff Roberson slab = (uma_slab_t)mem; 27228355f576SJeff Roberson } 27238355f576SJeff Roberson } else { 2724e20a199fSJeff Roberson /* This prevents redundant lookups via free(). */ 2725e20a199fSJeff Roberson if ((zone->uz_flags & UMA_ZONE_MALLOC) && udata != NULL) 27268355f576SJeff Roberson slab = (uma_slab_t)udata; 2727e20a199fSJeff Roberson else 2728e20a199fSJeff Roberson slab = vtoslab((vm_offset_t)item); 2729e20a199fSJeff Roberson keg = slab->us_keg; 2730e20a199fSJeff Roberson keg_relock(keg, zone); 27318355f576SJeff Roberson } 2732e20a199fSJeff Roberson MPASS(keg == slab->us_keg); 27338355f576SJeff Roberson 27348355f576SJeff Roberson /* Do we need to remove from any lists? */ 2735099a0e58SBosko Milekic if (slab->us_freecount+1 == keg->uk_ipers) { 27368355f576SJeff Roberson LIST_REMOVE(slab, us_link); 2737099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_free_slab, slab, us_link); 27388355f576SJeff Roberson } else if (slab->us_freecount == 0) { 27398355f576SJeff Roberson LIST_REMOVE(slab, us_link); 2740099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link); 27418355f576SJeff Roberson } 27428355f576SJeff Roberson 27438355f576SJeff Roberson /* Slab management stuff */ 27448355f576SJeff Roberson freei = ((unsigned long)item - (unsigned long)slab->us_data) 2745099a0e58SBosko Milekic / keg->uk_rsize; 27468355f576SJeff Roberson 2747639c9550SJeff Roberson #ifdef INVARIANTS 2748639c9550SJeff Roberson if (!skip) 2749639c9550SJeff Roberson uma_dbg_free(zone, slab, item); 27508355f576SJeff Roberson #endif 2751639c9550SJeff Roberson 2752ab14a3f7SBrian Feldman if (keg->uk_flags & UMA_ZONE_REFCNT) { 2753ab14a3f7SBrian Feldman slabref = (uma_slabrefcnt_t)slab; 2754ab14a3f7SBrian Feldman slabref->us_freelist[freei].us_item = slab->us_firstfree; 2755ab14a3f7SBrian Feldman } else { 2756099a0e58SBosko Milekic slab->us_freelist[freei].us_item = slab->us_firstfree; 2757ab14a3f7SBrian Feldman } 27588355f576SJeff Roberson slab->us_firstfree = freei; 27598355f576SJeff Roberson slab->us_freecount++; 27608355f576SJeff Roberson 27618355f576SJeff Roberson /* Zone statistics */ 2762099a0e58SBosko Milekic keg->uk_free++; 27638355f576SJeff Roberson 2764e20a199fSJeff Roberson clearfull = 0; 2765099a0e58SBosko Milekic if (keg->uk_flags & UMA_ZFLAG_FULL) { 2766e20a199fSJeff Roberson if (keg->uk_pages < keg->uk_maxpages) { 2767099a0e58SBosko Milekic keg->uk_flags &= ~UMA_ZFLAG_FULL; 2768e20a199fSJeff Roberson clearfull = 1; 2769e20a199fSJeff Roberson } 2770af7f9b97SJeff Roberson 277177380291SMohan Srinivasan /* 277277380291SMohan Srinivasan * We can handle one more allocation. Since we're clearing ZFLAG_FULL, 277377380291SMohan Srinivasan * wake up all procs blocked on pages. This should be uncommon, so 277477380291SMohan Srinivasan * keeping this simple for now (rather than adding count of blocked 277577380291SMohan Srinivasan * threads etc). 277677380291SMohan Srinivasan */ 277777380291SMohan Srinivasan wakeup(keg); 2778af7f9b97SJeff Roberson } 2779e20a199fSJeff Roberson if (clearfull) { 2780e20a199fSJeff Roberson zone_relock(zone, keg); 2781e20a199fSJeff Roberson zone->uz_flags &= ~UMA_ZFLAG_FULL; 2782e20a199fSJeff Roberson wakeup(zone); 2783605cbd6aSJeff Roberson ZONE_UNLOCK(zone); 2784e20a199fSJeff Roberson } else 2785e20a199fSJeff Roberson KEG_UNLOCK(keg); 27868355f576SJeff Roberson } 27878355f576SJeff Roberson 27888355f576SJeff Roberson /* See uma.h */ 27891c6cae97SLawrence Stewart int 2790736ee590SJeff Roberson uma_zone_set_max(uma_zone_t zone, int nitems) 2791736ee590SJeff Roberson { 2792099a0e58SBosko Milekic uma_keg_t keg; 2793099a0e58SBosko Milekic 2794736ee590SJeff Roberson ZONE_LOCK(zone); 2795e20a199fSJeff Roberson keg = zone_first_keg(zone); 2796e20a199fSJeff Roberson keg->uk_maxpages = (nitems / keg->uk_ipers) * keg->uk_ppera; 2797099a0e58SBosko Milekic if (keg->uk_maxpages * keg->uk_ipers < nitems) 2798e20a199fSJeff Roberson keg->uk_maxpages += keg->uk_ppera; 27991c6cae97SLawrence Stewart nitems = keg->uk_maxpages * keg->uk_ipers; 2800736ee590SJeff Roberson ZONE_UNLOCK(zone); 28011c6cae97SLawrence Stewart 28021c6cae97SLawrence Stewart return (nitems); 2803736ee590SJeff Roberson } 2804736ee590SJeff Roberson 2805736ee590SJeff Roberson /* See uma.h */ 2806e49471b0SAndre Oppermann int 2807e49471b0SAndre Oppermann uma_zone_get_max(uma_zone_t zone) 2808e49471b0SAndre Oppermann { 2809e49471b0SAndre Oppermann int nitems; 2810e49471b0SAndre Oppermann uma_keg_t keg; 2811e49471b0SAndre Oppermann 2812e49471b0SAndre Oppermann ZONE_LOCK(zone); 2813e49471b0SAndre Oppermann keg = zone_first_keg(zone); 2814e49471b0SAndre Oppermann nitems = keg->uk_maxpages * keg->uk_ipers; 2815e49471b0SAndre Oppermann ZONE_UNLOCK(zone); 2816e49471b0SAndre Oppermann 2817e49471b0SAndre Oppermann return (nitems); 2818e49471b0SAndre Oppermann } 2819e49471b0SAndre Oppermann 2820e49471b0SAndre Oppermann /* See uma.h */ 2821c4ae7908SLawrence Stewart int 2822c4ae7908SLawrence Stewart uma_zone_get_cur(uma_zone_t zone) 2823c4ae7908SLawrence Stewart { 2824c4ae7908SLawrence Stewart int64_t nitems; 2825c4ae7908SLawrence Stewart u_int i; 2826c4ae7908SLawrence Stewart 2827c4ae7908SLawrence Stewart ZONE_LOCK(zone); 2828c4ae7908SLawrence Stewart nitems = zone->uz_allocs - zone->uz_frees; 2829c4ae7908SLawrence Stewart CPU_FOREACH(i) { 2830c4ae7908SLawrence Stewart /* 2831c4ae7908SLawrence Stewart * See the comment in sysctl_vm_zone_stats() regarding the 2832c4ae7908SLawrence Stewart * safety of accessing the per-cpu caches. With the zone lock 2833c4ae7908SLawrence Stewart * held, it is safe, but can potentially result in stale data. 2834c4ae7908SLawrence Stewart */ 2835c4ae7908SLawrence Stewart nitems += zone->uz_cpu[i].uc_allocs - 2836c4ae7908SLawrence Stewart zone->uz_cpu[i].uc_frees; 2837c4ae7908SLawrence Stewart } 2838c4ae7908SLawrence Stewart ZONE_UNLOCK(zone); 2839c4ae7908SLawrence Stewart 2840c4ae7908SLawrence Stewart return (nitems < 0 ? 0 : nitems); 2841c4ae7908SLawrence Stewart } 2842c4ae7908SLawrence Stewart 2843c4ae7908SLawrence Stewart /* See uma.h */ 2844736ee590SJeff Roberson void 2845099a0e58SBosko Milekic uma_zone_set_init(uma_zone_t zone, uma_init uminit) 2846099a0e58SBosko Milekic { 2847e20a199fSJeff Roberson uma_keg_t keg; 2848e20a199fSJeff Roberson 2849099a0e58SBosko Milekic ZONE_LOCK(zone); 2850e20a199fSJeff Roberson keg = zone_first_keg(zone); 2851e20a199fSJeff Roberson KASSERT(keg->uk_pages == 0, 2852099a0e58SBosko Milekic ("uma_zone_set_init on non-empty keg")); 2853e20a199fSJeff Roberson keg->uk_init = uminit; 2854099a0e58SBosko Milekic ZONE_UNLOCK(zone); 2855099a0e58SBosko Milekic } 2856099a0e58SBosko Milekic 2857099a0e58SBosko Milekic /* See uma.h */ 2858099a0e58SBosko Milekic void 2859099a0e58SBosko Milekic uma_zone_set_fini(uma_zone_t zone, uma_fini fini) 2860099a0e58SBosko Milekic { 2861e20a199fSJeff Roberson uma_keg_t keg; 2862e20a199fSJeff Roberson 2863099a0e58SBosko Milekic ZONE_LOCK(zone); 2864e20a199fSJeff Roberson keg = zone_first_keg(zone); 2865e20a199fSJeff Roberson KASSERT(keg->uk_pages == 0, 2866099a0e58SBosko Milekic ("uma_zone_set_fini on non-empty keg")); 2867e20a199fSJeff Roberson keg->uk_fini = fini; 2868099a0e58SBosko Milekic ZONE_UNLOCK(zone); 2869099a0e58SBosko Milekic } 2870099a0e58SBosko Milekic 2871099a0e58SBosko Milekic /* See uma.h */ 2872099a0e58SBosko Milekic void 2873099a0e58SBosko Milekic uma_zone_set_zinit(uma_zone_t zone, uma_init zinit) 2874099a0e58SBosko Milekic { 2875099a0e58SBosko Milekic ZONE_LOCK(zone); 2876e20a199fSJeff Roberson KASSERT(zone_first_keg(zone)->uk_pages == 0, 2877099a0e58SBosko Milekic ("uma_zone_set_zinit on non-empty keg")); 2878099a0e58SBosko Milekic zone->uz_init = zinit; 2879099a0e58SBosko Milekic ZONE_UNLOCK(zone); 2880099a0e58SBosko Milekic } 2881099a0e58SBosko Milekic 2882099a0e58SBosko Milekic /* See uma.h */ 2883099a0e58SBosko Milekic void 2884099a0e58SBosko Milekic uma_zone_set_zfini(uma_zone_t zone, uma_fini zfini) 2885099a0e58SBosko Milekic { 2886099a0e58SBosko Milekic ZONE_LOCK(zone); 2887e20a199fSJeff Roberson KASSERT(zone_first_keg(zone)->uk_pages == 0, 2888099a0e58SBosko Milekic ("uma_zone_set_zfini on non-empty keg")); 2889099a0e58SBosko Milekic zone->uz_fini = zfini; 2890099a0e58SBosko Milekic ZONE_UNLOCK(zone); 2891099a0e58SBosko Milekic } 2892099a0e58SBosko Milekic 2893099a0e58SBosko Milekic /* See uma.h */ 2894b23f72e9SBrian Feldman /* XXX uk_freef is not actually used with the zone locked */ 2895099a0e58SBosko Milekic void 28968355f576SJeff Roberson uma_zone_set_freef(uma_zone_t zone, uma_free freef) 28978355f576SJeff Roberson { 2898e20a199fSJeff Roberson 28998355f576SJeff Roberson ZONE_LOCK(zone); 2900e20a199fSJeff Roberson zone_first_keg(zone)->uk_freef = freef; 29018355f576SJeff Roberson ZONE_UNLOCK(zone); 29028355f576SJeff Roberson } 29038355f576SJeff Roberson 29048355f576SJeff Roberson /* See uma.h */ 2905b23f72e9SBrian Feldman /* XXX uk_allocf is not actually used with the zone locked */ 29068355f576SJeff Roberson void 29078355f576SJeff Roberson uma_zone_set_allocf(uma_zone_t zone, uma_alloc allocf) 29088355f576SJeff Roberson { 2909e20a199fSJeff Roberson uma_keg_t keg; 2910e20a199fSJeff Roberson 29118355f576SJeff Roberson ZONE_LOCK(zone); 2912e20a199fSJeff Roberson keg = zone_first_keg(zone); 2913e20a199fSJeff Roberson keg->uk_flags |= UMA_ZFLAG_PRIVALLOC; 2914e20a199fSJeff Roberson keg->uk_allocf = allocf; 29158355f576SJeff Roberson ZONE_UNLOCK(zone); 29168355f576SJeff Roberson } 29178355f576SJeff Roberson 29188355f576SJeff Roberson /* See uma.h */ 29198355f576SJeff Roberson int 29208355f576SJeff Roberson uma_zone_set_obj(uma_zone_t zone, struct vm_object *obj, int count) 29218355f576SJeff Roberson { 2922099a0e58SBosko Milekic uma_keg_t keg; 29238355f576SJeff Roberson vm_offset_t kva; 2924099a0e58SBosko Milekic int pages; 29258355f576SJeff Roberson 2926e20a199fSJeff Roberson keg = zone_first_keg(zone); 2927099a0e58SBosko Milekic pages = count / keg->uk_ipers; 29288355f576SJeff Roberson 2929099a0e58SBosko Milekic if (pages * keg->uk_ipers < count) 29308355f576SJeff Roberson pages++; 2931a553d4b8SJeff Roberson 29325285558aSAlan Cox kva = kmem_alloc_nofault(kernel_map, pages * UMA_SLAB_SIZE); 29338355f576SJeff Roberson 2934d1f42ac2SAlan Cox if (kva == 0) 29358355f576SJeff Roberson return (0); 2936451033a4SAlan Cox if (obj == NULL) 2937451033a4SAlan Cox obj = vm_object_allocate(OBJT_PHYS, pages); 2938451033a4SAlan Cox else { 29395285558aSAlan Cox VM_OBJECT_LOCK_INIT(obj, "uma object"); 2940451033a4SAlan Cox _vm_object_allocate(OBJT_PHYS, pages, obj); 294182774d80SAlan Cox } 2942a553d4b8SJeff Roberson ZONE_LOCK(zone); 2943099a0e58SBosko Milekic keg->uk_kva = kva; 2944099a0e58SBosko Milekic keg->uk_obj = obj; 2945099a0e58SBosko Milekic keg->uk_maxpages = pages; 2946099a0e58SBosko Milekic keg->uk_allocf = obj_alloc; 2947099a0e58SBosko Milekic keg->uk_flags |= UMA_ZONE_NOFREE | UMA_ZFLAG_PRIVALLOC; 29488355f576SJeff Roberson ZONE_UNLOCK(zone); 29498355f576SJeff Roberson return (1); 29508355f576SJeff Roberson } 29518355f576SJeff Roberson 29528355f576SJeff Roberson /* See uma.h */ 29538355f576SJeff Roberson void 29548355f576SJeff Roberson uma_prealloc(uma_zone_t zone, int items) 29558355f576SJeff Roberson { 29568355f576SJeff Roberson int slabs; 29578355f576SJeff Roberson uma_slab_t slab; 2958099a0e58SBosko Milekic uma_keg_t keg; 29598355f576SJeff Roberson 2960e20a199fSJeff Roberson keg = zone_first_keg(zone); 29618355f576SJeff Roberson ZONE_LOCK(zone); 2962099a0e58SBosko Milekic slabs = items / keg->uk_ipers; 2963099a0e58SBosko Milekic if (slabs * keg->uk_ipers < items) 29648355f576SJeff Roberson slabs++; 29658355f576SJeff Roberson while (slabs > 0) { 2966e20a199fSJeff Roberson slab = keg_alloc_slab(keg, zone, M_WAITOK); 2967e20a199fSJeff Roberson if (slab == NULL) 2968e20a199fSJeff Roberson break; 2969e20a199fSJeff Roberson MPASS(slab->us_keg == keg); 2970099a0e58SBosko Milekic LIST_INSERT_HEAD(&keg->uk_free_slab, slab, us_link); 29718355f576SJeff Roberson slabs--; 29728355f576SJeff Roberson } 29738355f576SJeff Roberson ZONE_UNLOCK(zone); 29748355f576SJeff Roberson } 29758355f576SJeff Roberson 29768355f576SJeff Roberson /* See uma.h */ 2977099a0e58SBosko Milekic u_int32_t * 2978099a0e58SBosko Milekic uma_find_refcnt(uma_zone_t zone, void *item) 2979099a0e58SBosko Milekic { 2980ab14a3f7SBrian Feldman uma_slabrefcnt_t slabref; 2981099a0e58SBosko Milekic uma_keg_t keg; 2982099a0e58SBosko Milekic u_int32_t *refcnt; 2983099a0e58SBosko Milekic int idx; 2984099a0e58SBosko Milekic 2985ab14a3f7SBrian Feldman slabref = (uma_slabrefcnt_t)vtoslab((vm_offset_t)item & 2986ab14a3f7SBrian Feldman (~UMA_SLAB_MASK)); 2987e20a199fSJeff Roberson keg = slabref->us_keg; 2988ab14a3f7SBrian Feldman KASSERT(slabref != NULL && slabref->us_keg->uk_flags & UMA_ZONE_REFCNT, 2989099a0e58SBosko Milekic ("uma_find_refcnt(): zone possibly not UMA_ZONE_REFCNT")); 2990ab14a3f7SBrian Feldman idx = ((unsigned long)item - (unsigned long)slabref->us_data) 2991099a0e58SBosko Milekic / keg->uk_rsize; 2992ab14a3f7SBrian Feldman refcnt = &slabref->us_freelist[idx].us_refcnt; 2993099a0e58SBosko Milekic return refcnt; 2994099a0e58SBosko Milekic } 2995099a0e58SBosko Milekic 2996099a0e58SBosko Milekic /* See uma.h */ 29978355f576SJeff Roberson void 29988355f576SJeff Roberson uma_reclaim(void) 29998355f576SJeff Roberson { 30008355f576SJeff Roberson #ifdef UMA_DEBUG 30018355f576SJeff Roberson printf("UMA: vm asked us to release pages!\n"); 30028355f576SJeff Roberson #endif 300386bbae32SJeff Roberson bucket_enable(); 30048355f576SJeff Roberson zone_foreach(zone_drain); 30058355f576SJeff Roberson /* 30068355f576SJeff Roberson * Some slabs may have been freed but this zone will be visited early 30078355f576SJeff Roberson * we visit again so that we can free pages that are empty once other 30088355f576SJeff Roberson * zones are drained. We have to do the same for buckets. 30098355f576SJeff Roberson */ 30109643769aSJeff Roberson zone_drain(slabzone); 3011099a0e58SBosko Milekic zone_drain(slabrefzone); 3012cae33c14SJeff Roberson bucket_zone_drain(); 30138355f576SJeff Roberson } 30148355f576SJeff Roberson 3015663b416fSJohn Baldwin /* See uma.h */ 3016663b416fSJohn Baldwin int 3017663b416fSJohn Baldwin uma_zone_exhausted(uma_zone_t zone) 3018663b416fSJohn Baldwin { 3019663b416fSJohn Baldwin int full; 3020663b416fSJohn Baldwin 3021663b416fSJohn Baldwin ZONE_LOCK(zone); 3022e20a199fSJeff Roberson full = (zone->uz_flags & UMA_ZFLAG_FULL); 3023663b416fSJohn Baldwin ZONE_UNLOCK(zone); 3024663b416fSJohn Baldwin return (full); 3025663b416fSJohn Baldwin } 3026663b416fSJohn Baldwin 30276c125b8dSMohan Srinivasan int 30286c125b8dSMohan Srinivasan uma_zone_exhausted_nolock(uma_zone_t zone) 30296c125b8dSMohan Srinivasan { 3030e20a199fSJeff Roberson return (zone->uz_flags & UMA_ZFLAG_FULL); 30316c125b8dSMohan Srinivasan } 30326c125b8dSMohan Srinivasan 30338355f576SJeff Roberson void * 30348355f576SJeff Roberson uma_large_malloc(int size, int wait) 30358355f576SJeff Roberson { 30368355f576SJeff Roberson void *mem; 30378355f576SJeff Roberson uma_slab_t slab; 30388355f576SJeff Roberson u_int8_t flags; 30398355f576SJeff Roberson 3040e20a199fSJeff Roberson slab = zone_alloc_item(slabzone, NULL, wait); 30418355f576SJeff Roberson if (slab == NULL) 30428355f576SJeff Roberson return (NULL); 30438355f576SJeff Roberson mem = page_alloc(NULL, size, &flags, wait); 30448355f576SJeff Roberson if (mem) { 304599571dc3SJeff Roberson vsetslab((vm_offset_t)mem, slab); 30468355f576SJeff Roberson slab->us_data = mem; 30478355f576SJeff Roberson slab->us_flags = flags | UMA_SLAB_MALLOC; 30488355f576SJeff Roberson slab->us_size = size; 30498355f576SJeff Roberson } else { 3050e20a199fSJeff Roberson zone_free_item(slabzone, slab, NULL, SKIP_NONE, 3051f4ff923bSRobert Watson ZFREE_STATFAIL | ZFREE_STATFREE); 30528355f576SJeff Roberson } 30538355f576SJeff Roberson 30548355f576SJeff Roberson return (mem); 30558355f576SJeff Roberson } 30568355f576SJeff Roberson 30578355f576SJeff Roberson void 30588355f576SJeff Roberson uma_large_free(uma_slab_t slab) 30598355f576SJeff Roberson { 306099571dc3SJeff Roberson vsetobj((vm_offset_t)slab->us_data, kmem_object); 30618355f576SJeff Roberson page_free(slab->us_data, slab->us_size, slab->us_flags); 3062e20a199fSJeff Roberson zone_free_item(slabzone, slab, NULL, SKIP_NONE, ZFREE_STATFREE); 30638355f576SJeff Roberson } 30648355f576SJeff Roberson 30658355f576SJeff Roberson void 30668355f576SJeff Roberson uma_print_stats(void) 30678355f576SJeff Roberson { 30688355f576SJeff Roberson zone_foreach(uma_print_zone); 30698355f576SJeff Roberson } 30708355f576SJeff Roberson 3071504d5de3SJeff Roberson static void 3072504d5de3SJeff Roberson slab_print(uma_slab_t slab) 3073504d5de3SJeff Roberson { 3074099a0e58SBosko Milekic printf("slab: keg %p, data %p, freecount %d, firstfree %d\n", 3075099a0e58SBosko Milekic slab->us_keg, slab->us_data, slab->us_freecount, 3076504d5de3SJeff Roberson slab->us_firstfree); 3077504d5de3SJeff Roberson } 3078504d5de3SJeff Roberson 3079504d5de3SJeff Roberson static void 3080504d5de3SJeff Roberson cache_print(uma_cache_t cache) 3081504d5de3SJeff Roberson { 3082504d5de3SJeff Roberson printf("alloc: %p(%d), free: %p(%d)\n", 3083504d5de3SJeff Roberson cache->uc_allocbucket, 3084504d5de3SJeff Roberson cache->uc_allocbucket?cache->uc_allocbucket->ub_cnt:0, 3085504d5de3SJeff Roberson cache->uc_freebucket, 3086504d5de3SJeff Roberson cache->uc_freebucket?cache->uc_freebucket->ub_cnt:0); 3087504d5de3SJeff Roberson } 3088504d5de3SJeff Roberson 3089e20a199fSJeff Roberson static void 3090e20a199fSJeff Roberson uma_print_keg(uma_keg_t keg) 30918355f576SJeff Roberson { 3092504d5de3SJeff Roberson uma_slab_t slab; 3093504d5de3SJeff Roberson 3094e20a199fSJeff Roberson printf("keg: %s(%p) size %d(%d) flags %d ipers %d ppera %d " 3095e20a199fSJeff Roberson "out %d free %d limit %d\n", 3096e20a199fSJeff Roberson keg->uk_name, keg, keg->uk_size, keg->uk_rsize, keg->uk_flags, 3097099a0e58SBosko Milekic keg->uk_ipers, keg->uk_ppera, 3098e20a199fSJeff Roberson (keg->uk_ipers * keg->uk_pages) - keg->uk_free, keg->uk_free, 3099e20a199fSJeff Roberson (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers); 3100504d5de3SJeff Roberson printf("Part slabs:\n"); 3101099a0e58SBosko Milekic LIST_FOREACH(slab, &keg->uk_part_slab, us_link) 3102504d5de3SJeff Roberson slab_print(slab); 3103504d5de3SJeff Roberson printf("Free slabs:\n"); 3104099a0e58SBosko Milekic LIST_FOREACH(slab, &keg->uk_free_slab, us_link) 3105504d5de3SJeff Roberson slab_print(slab); 3106504d5de3SJeff Roberson printf("Full slabs:\n"); 3107099a0e58SBosko Milekic LIST_FOREACH(slab, &keg->uk_full_slab, us_link) 3108504d5de3SJeff Roberson slab_print(slab); 3109e20a199fSJeff Roberson } 3110e20a199fSJeff Roberson 3111e20a199fSJeff Roberson void 3112e20a199fSJeff Roberson uma_print_zone(uma_zone_t zone) 3113e20a199fSJeff Roberson { 3114e20a199fSJeff Roberson uma_cache_t cache; 3115e20a199fSJeff Roberson uma_klink_t kl; 3116e20a199fSJeff Roberson int i; 3117e20a199fSJeff Roberson 3118e20a199fSJeff Roberson printf("zone: %s(%p) size %d flags %d\n", 3119e20a199fSJeff Roberson zone->uz_name, zone, zone->uz_size, zone->uz_flags); 3120e20a199fSJeff Roberson LIST_FOREACH(kl, &zone->uz_kegs, kl_link) 3121e20a199fSJeff Roberson uma_print_keg(kl->kl_keg); 31223aa6d94eSJohn Baldwin CPU_FOREACH(i) { 3123504d5de3SJeff Roberson cache = &zone->uz_cpu[i]; 3124504d5de3SJeff Roberson printf("CPU %d Cache:\n", i); 3125504d5de3SJeff Roberson cache_print(cache); 3126504d5de3SJeff Roberson } 31278355f576SJeff Roberson } 31288355f576SJeff Roberson 3129a0d4b0aeSRobert Watson #ifdef DDB 31308355f576SJeff Roberson /* 31317a52a97eSRobert Watson * Generate statistics across both the zone and its per-cpu cache's. Return 31327a52a97eSRobert Watson * desired statistics if the pointer is non-NULL for that statistic. 31337a52a97eSRobert Watson * 31347a52a97eSRobert Watson * Note: does not update the zone statistics, as it can't safely clear the 31357a52a97eSRobert Watson * per-CPU cache statistic. 31367a52a97eSRobert Watson * 31377a52a97eSRobert Watson * XXXRW: Following the uc_allocbucket and uc_freebucket pointers here isn't 31387a52a97eSRobert Watson * safe from off-CPU; we should modify the caches to track this information 31397a52a97eSRobert Watson * directly so that we don't have to. 31407a52a97eSRobert Watson */ 31417a52a97eSRobert Watson static void 31427a52a97eSRobert Watson uma_zone_sumstat(uma_zone_t z, int *cachefreep, u_int64_t *allocsp, 3143bf965959SSean Bruno u_int64_t *freesp, u_int64_t *sleepsp) 31447a52a97eSRobert Watson { 31457a52a97eSRobert Watson uma_cache_t cache; 3146bf965959SSean Bruno u_int64_t allocs, frees, sleeps; 31477a52a97eSRobert Watson int cachefree, cpu; 31487a52a97eSRobert Watson 3149bf965959SSean Bruno allocs = frees = sleeps = 0; 31507a52a97eSRobert Watson cachefree = 0; 31513aa6d94eSJohn Baldwin CPU_FOREACH(cpu) { 31527a52a97eSRobert Watson cache = &z->uz_cpu[cpu]; 31537a52a97eSRobert Watson if (cache->uc_allocbucket != NULL) 31547a52a97eSRobert Watson cachefree += cache->uc_allocbucket->ub_cnt; 31557a52a97eSRobert Watson if (cache->uc_freebucket != NULL) 31567a52a97eSRobert Watson cachefree += cache->uc_freebucket->ub_cnt; 31577a52a97eSRobert Watson allocs += cache->uc_allocs; 31587a52a97eSRobert Watson frees += cache->uc_frees; 31597a52a97eSRobert Watson } 31607a52a97eSRobert Watson allocs += z->uz_allocs; 31617a52a97eSRobert Watson frees += z->uz_frees; 3162bf965959SSean Bruno sleeps += z->uz_sleeps; 31637a52a97eSRobert Watson if (cachefreep != NULL) 31647a52a97eSRobert Watson *cachefreep = cachefree; 31657a52a97eSRobert Watson if (allocsp != NULL) 31667a52a97eSRobert Watson *allocsp = allocs; 31677a52a97eSRobert Watson if (freesp != NULL) 31687a52a97eSRobert Watson *freesp = frees; 3169bf965959SSean Bruno if (sleepsp != NULL) 3170bf965959SSean Bruno *sleepsp = sleeps; 31717a52a97eSRobert Watson } 3172a0d4b0aeSRobert Watson #endif /* DDB */ 31737a52a97eSRobert Watson 31747a52a97eSRobert Watson static int 31757a52a97eSRobert Watson sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS) 31767a52a97eSRobert Watson { 31777a52a97eSRobert Watson uma_keg_t kz; 31787a52a97eSRobert Watson uma_zone_t z; 31797a52a97eSRobert Watson int count; 31807a52a97eSRobert Watson 31817a52a97eSRobert Watson count = 0; 31827a52a97eSRobert Watson mtx_lock(&uma_mtx); 31837a52a97eSRobert Watson LIST_FOREACH(kz, &uma_kegs, uk_link) { 31847a52a97eSRobert Watson LIST_FOREACH(z, &kz->uk_zones, uz_link) 31857a52a97eSRobert Watson count++; 31867a52a97eSRobert Watson } 31877a52a97eSRobert Watson mtx_unlock(&uma_mtx); 31887a52a97eSRobert Watson return (sysctl_handle_int(oidp, &count, 0, req)); 31897a52a97eSRobert Watson } 31907a52a97eSRobert Watson 31917a52a97eSRobert Watson static int 31927a52a97eSRobert Watson sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS) 31937a52a97eSRobert Watson { 31947a52a97eSRobert Watson struct uma_stream_header ush; 31957a52a97eSRobert Watson struct uma_type_header uth; 31967a52a97eSRobert Watson struct uma_percpu_stat ups; 31977a52a97eSRobert Watson uma_bucket_t bucket; 31987a52a97eSRobert Watson struct sbuf sbuf; 31997a52a97eSRobert Watson uma_cache_t cache; 3200e20a199fSJeff Roberson uma_klink_t kl; 32017a52a97eSRobert Watson uma_keg_t kz; 32027a52a97eSRobert Watson uma_zone_t z; 3203e20a199fSJeff Roberson uma_keg_t k; 32044e657159SMatthew D Fleming int count, error, i; 32057a52a97eSRobert Watson 32064e657159SMatthew D Fleming sbuf_new_for_sysctl(&sbuf, NULL, 128, req); 32074e657159SMatthew D Fleming 3208404a593eSMatthew D Fleming count = 0; 32094e657159SMatthew D Fleming mtx_lock(&uma_mtx); 32107a52a97eSRobert Watson LIST_FOREACH(kz, &uma_kegs, uk_link) { 32117a52a97eSRobert Watson LIST_FOREACH(z, &kz->uk_zones, uz_link) 32127a52a97eSRobert Watson count++; 32137a52a97eSRobert Watson } 32147a52a97eSRobert Watson 32157a52a97eSRobert Watson /* 32167a52a97eSRobert Watson * Insert stream header. 32177a52a97eSRobert Watson */ 32187a52a97eSRobert Watson bzero(&ush, sizeof(ush)); 32197a52a97eSRobert Watson ush.ush_version = UMA_STREAM_VERSION; 3220ab3a57c0SRobert Watson ush.ush_maxcpus = (mp_maxid + 1); 32217a52a97eSRobert Watson ush.ush_count = count; 32224e657159SMatthew D Fleming (void)sbuf_bcat(&sbuf, &ush, sizeof(ush)); 32237a52a97eSRobert Watson 32247a52a97eSRobert Watson LIST_FOREACH(kz, &uma_kegs, uk_link) { 32257a52a97eSRobert Watson LIST_FOREACH(z, &kz->uk_zones, uz_link) { 32267a52a97eSRobert Watson bzero(&uth, sizeof(uth)); 32277a52a97eSRobert Watson ZONE_LOCK(z); 3228cbbb4a00SRobert Watson strlcpy(uth.uth_name, z->uz_name, UTH_MAX_NAME); 32297a52a97eSRobert Watson uth.uth_align = kz->uk_align; 32307a52a97eSRobert Watson uth.uth_size = kz->uk_size; 32317a52a97eSRobert Watson uth.uth_rsize = kz->uk_rsize; 3232e20a199fSJeff Roberson LIST_FOREACH(kl, &z->uz_kegs, kl_link) { 3233e20a199fSJeff Roberson k = kl->kl_keg; 3234e20a199fSJeff Roberson uth.uth_maxpages += k->uk_maxpages; 3235e20a199fSJeff Roberson uth.uth_pages += k->uk_pages; 3236e20a199fSJeff Roberson uth.uth_keg_free += k->uk_free; 3237e20a199fSJeff Roberson uth.uth_limit = (k->uk_maxpages / k->uk_ppera) 3238e20a199fSJeff Roberson * k->uk_ipers; 3239e20a199fSJeff Roberson } 3240cbbb4a00SRobert Watson 3241cbbb4a00SRobert Watson /* 3242cbbb4a00SRobert Watson * A zone is secondary is it is not the first entry 3243cbbb4a00SRobert Watson * on the keg's zone list. 3244cbbb4a00SRobert Watson */ 3245e20a199fSJeff Roberson if ((z->uz_flags & UMA_ZONE_SECONDARY) && 3246cbbb4a00SRobert Watson (LIST_FIRST(&kz->uk_zones) != z)) 3247cbbb4a00SRobert Watson uth.uth_zone_flags = UTH_ZONE_SECONDARY; 3248cbbb4a00SRobert Watson 32497a52a97eSRobert Watson LIST_FOREACH(bucket, &z->uz_full_bucket, ub_link) 32507a52a97eSRobert Watson uth.uth_zone_free += bucket->ub_cnt; 32517a52a97eSRobert Watson uth.uth_allocs = z->uz_allocs; 32527a52a97eSRobert Watson uth.uth_frees = z->uz_frees; 32532019094aSRobert Watson uth.uth_fails = z->uz_fails; 3254bf965959SSean Bruno uth.uth_sleeps = z->uz_sleeps; 32554e657159SMatthew D Fleming (void)sbuf_bcat(&sbuf, &uth, sizeof(uth)); 32567a52a97eSRobert Watson /* 32572450bbb8SRobert Watson * While it is not normally safe to access the cache 32582450bbb8SRobert Watson * bucket pointers while not on the CPU that owns the 32592450bbb8SRobert Watson * cache, we only allow the pointers to be exchanged 32602450bbb8SRobert Watson * without the zone lock held, not invalidated, so 32612450bbb8SRobert Watson * accept the possible race associated with bucket 32622450bbb8SRobert Watson * exchange during monitoring. 32637a52a97eSRobert Watson */ 3264ab3a57c0SRobert Watson for (i = 0; i < (mp_maxid + 1); i++) { 32657a52a97eSRobert Watson bzero(&ups, sizeof(ups)); 32667a52a97eSRobert Watson if (kz->uk_flags & UMA_ZFLAG_INTERNAL) 32677a52a97eSRobert Watson goto skip; 3268082dc776SRobert Watson if (CPU_ABSENT(i)) 3269082dc776SRobert Watson goto skip; 32707a52a97eSRobert Watson cache = &z->uz_cpu[i]; 32717a52a97eSRobert Watson if (cache->uc_allocbucket != NULL) 32727a52a97eSRobert Watson ups.ups_cache_free += 32737a52a97eSRobert Watson cache->uc_allocbucket->ub_cnt; 32747a52a97eSRobert Watson if (cache->uc_freebucket != NULL) 32757a52a97eSRobert Watson ups.ups_cache_free += 32767a52a97eSRobert Watson cache->uc_freebucket->ub_cnt; 32777a52a97eSRobert Watson ups.ups_allocs = cache->uc_allocs; 32787a52a97eSRobert Watson ups.ups_frees = cache->uc_frees; 32797a52a97eSRobert Watson skip: 32804e657159SMatthew D Fleming (void)sbuf_bcat(&sbuf, &ups, sizeof(ups)); 32817a52a97eSRobert Watson } 32822450bbb8SRobert Watson ZONE_UNLOCK(z); 32837a52a97eSRobert Watson } 32847a52a97eSRobert Watson } 32857a52a97eSRobert Watson mtx_unlock(&uma_mtx); 32864e657159SMatthew D Fleming error = sbuf_finish(&sbuf); 32874e657159SMatthew D Fleming sbuf_delete(&sbuf); 32887a52a97eSRobert Watson return (error); 32897a52a97eSRobert Watson } 329048c5777eSRobert Watson 329148c5777eSRobert Watson #ifdef DDB 329248c5777eSRobert Watson DB_SHOW_COMMAND(uma, db_show_uma) 329348c5777eSRobert Watson { 3294bf965959SSean Bruno u_int64_t allocs, frees, sleeps; 329548c5777eSRobert Watson uma_bucket_t bucket; 329648c5777eSRobert Watson uma_keg_t kz; 329748c5777eSRobert Watson uma_zone_t z; 329848c5777eSRobert Watson int cachefree; 329948c5777eSRobert Watson 3300bf965959SSean Bruno db_printf("%18s %8s %8s %8s %12s %8s\n", "Zone", "Size", "Used", "Free", 3301bf965959SSean Bruno "Requests", "Sleeps"); 330248c5777eSRobert Watson LIST_FOREACH(kz, &uma_kegs, uk_link) { 330348c5777eSRobert Watson LIST_FOREACH(z, &kz->uk_zones, uz_link) { 330448c5777eSRobert Watson if (kz->uk_flags & UMA_ZFLAG_INTERNAL) { 330548c5777eSRobert Watson allocs = z->uz_allocs; 330648c5777eSRobert Watson frees = z->uz_frees; 3307bf965959SSean Bruno sleeps = z->uz_sleeps; 330848c5777eSRobert Watson cachefree = 0; 330948c5777eSRobert Watson } else 331048c5777eSRobert Watson uma_zone_sumstat(z, &cachefree, &allocs, 3311bf965959SSean Bruno &frees, &sleeps); 3312e20a199fSJeff Roberson if (!((z->uz_flags & UMA_ZONE_SECONDARY) && 331348c5777eSRobert Watson (LIST_FIRST(&kz->uk_zones) != z))) 331448c5777eSRobert Watson cachefree += kz->uk_free; 331548c5777eSRobert Watson LIST_FOREACH(bucket, &z->uz_full_bucket, ub_link) 331648c5777eSRobert Watson cachefree += bucket->ub_cnt; 3317bf965959SSean Bruno db_printf("%18s %8ju %8jd %8d %12ju %8ju\n", z->uz_name, 3318ae4e9636SRobert Watson (uintmax_t)kz->uk_size, 3319ae4e9636SRobert Watson (intmax_t)(allocs - frees), cachefree, 3320bf965959SSean Bruno (uintmax_t)allocs, sleeps); 332148c5777eSRobert Watson } 332248c5777eSRobert Watson } 332348c5777eSRobert Watson } 332448c5777eSRobert Watson #endif 3325