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