diff options
author | Jennifer Averett <jennifer.averett@oarcorp.com> | 2012-04-19 13:25:28 -0500 |
---|---|---|
committer | Jennifer Averett <jennifer.averett@oarcorp.com> | 2012-04-19 13:25:28 -0500 |
commit | 74587c37d8f8a723770c45e65301e8a708997b67 (patch) | |
tree | ee55d781b3bd34c794445af5497855aeb623e2df | |
parent | Add SYSINIT for RTEMS SMP variable initialization (diff) | |
download | rtems-libbsd-74587c37d8f8a723770c45e65301e8a708997b67.tar.bz2 |
Merged rtems-bsd-uma.c back into uma_core.c
The majority of this file was the same as the freebsd file uma_core.c.
-rw-r--r-- | Makefile | 2 | ||||
-rwxr-xr-x | freebsd-to-rtems.py | 3 | ||||
-rw-r--r-- | freebsd/vm/uma_core.c (renamed from rtemsbsd/src/rtems-bsd-uma.c) | 705 |
3 files changed, 676 insertions, 34 deletions
@@ -50,7 +50,6 @@ C_FILES += rtemsbsd/src/rtems-bsd-bus-dma-mbuf.c C_FILES += rtemsbsd/src/rtems-bsd-sysctl.c C_FILES += rtemsbsd/src/rtems-bsd-sysctlbyname.c C_FILES += rtemsbsd/src/rtems-bsd-sysctlnametomib.c -C_FILES += rtemsbsd/src/rtems-bsd-uma.c C_FILES += rtemsbsd/src/rtems-bsd-taskqueue.c C_FILES += rtemsbsd/src/rtems-bsd-timeout.c C_FILES += rtemsbsd/src/rtems-bsd-timesupport.c @@ -331,6 +330,7 @@ C_FILES += freebsd/kern/uipc_mbuf2.c C_FILES += freebsd/kern/uipc_socket.c C_FILES += freebsd/kern/uipc_sockbuf.c C_FILES += freebsd/kern/uipc_domain.c +C_FILES += freebsd/vm/uma_core.c C_FILES += freebsd/dev/usb/usb_busdma.c C_FILES += freebsd/dev/usb/usb_core.c C_FILES += freebsd/dev/usb/usb_debug.c diff --git a/freebsd-to-rtems.py b/freebsd-to-rtems.py index e78d2a88..761b4b55 100755 --- a/freebsd-to-rtems.py +++ b/freebsd-to-rtems.py @@ -545,7 +545,6 @@ rtems.addRTEMSSourceFiles( 'src/rtems-bsd-sysctl.c', 'src/rtems-bsd-sysctlbyname.c', 'src/rtems-bsd-sysctlnametomib.c', - 'src/rtems-bsd-uma.c', 'src/rtems-bsd-taskqueue.c', 'src/rtems-bsd-timeout.c', 'src/rtems-bsd-timesupport.c', @@ -1130,7 +1129,7 @@ devUsbBase.addSourceFiles( 'kern/uipc_sockbuf.c', 'kern/uipc_domain.c', #'kern/uipc_syscalls.c', - #'vm/uma_core.c', + 'vm/uma_core.c', ] ) diff --git a/rtemsbsd/src/rtems-bsd-uma.c b/freebsd/vm/uma_core.c index 26748f98..5e2b2cde 100644 --- a/rtemsbsd/src/rtems-bsd-uma.c +++ b/freebsd/vm/uma_core.c @@ -1,39 +1,103 @@ -/** - * @file +#include <freebsd/machine/rtems-bsd-config.h> + +/*- + * Copyright (c) 2002-2005, 2009 Jeffrey Roberson <jeff@FreeBSD.org> + * Copyright (c) 2004, 2005 Bosko Milekic <bmilekic@FreeBSD.org> + * Copyright (c) 2004-2006 Robert N. M. Watson + * All rights reserved. * - * @ingroup rtems_bsd_rtems + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * @brief TODO. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Copyright (c) 2009, 2010 embedded brains GmbH. All rights reserved. + * uma_core.c Implementation of the Universal Memory allocator * - * embedded brains GmbH - * Obere Lagerstr. 30 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * This allocator is intended to replace the multitude of similar object caches + * in the standard FreeBSD kernel. The intent is to be flexible as well as + * effecient. A primary design goal is to return unused memory to the rest of + * the system. This will make the system as a whole more flexible due to the + * ability to move memory to subsystems which most need it instead of leaving + * pools of reserved memory unused. + * + * The basic ideas stem from similar slab/zone based allocators whose algorithms + * are well known. * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rtems.com/license/LICENSE. */ -#include <freebsd/machine/rtems-bsd-config.h> +/* + * TODO: + * - Improve memory usage for large allocations + * - Investigate cache size adjustments + */ + +#include <freebsd/sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* I should really use ktr.. */ +/* +#define UMA_DEBUG 1 +#define UMA_DEBUG_ALLOC 1 +#define UMA_DEBUG_ALLOC_1 1 +*/ + +#include <freebsd/local/opt_ddb.h> +#include <freebsd/local/opt_param.h> #include <freebsd/sys/param.h> -#include <freebsd/sys/types.h> #include <freebsd/sys/systm.h> -#include <freebsd/sys/malloc.h> #include <freebsd/sys/kernel.h> +#include <freebsd/sys/types.h> +#include <freebsd/sys/queue.h> +#include <freebsd/sys/malloc.h> +#include <freebsd/sys/ktr.h> #include <freebsd/sys/lock.h> +#include <freebsd/sys/sysctl.h> #include <freebsd/sys/mutex.h> -#include <freebsd/sys/ktr.h> +#include <freebsd/sys/proc.h> +#include <freebsd/sys/sbuf.h> +#include <freebsd/sys/smp.h> +#ifndef __rtems__ +#include <freebsd/sys/vmmeter.h> +#endif /* __rtems__ */ + +#include <freebsd/vm/vm.h> +#ifndef __rtems__ +#include <freebsd/vm/vm_object.h> +#include <freebsd/vm/vm_page.h> +#include <freebsd/vm/vm_param.h> +#include <freebsd/vm/vm_map.h> +#include <freebsd/vm/vm_kern.h> +#include <freebsd/vm/vm_extern.h> +#endif /* __rtems__ */ #include <freebsd/vm/uma.h> #include <freebsd/vm/uma_int.h> #include <freebsd/vm/uma_dbg.h> +#ifndef __rtems__ +#include <freebsd/machine/vmparam.h> + +#include <freebsd/ddb/ddb.h> +#endif /* __rtems__ */ + /* * This is the zone and keg from which all zones are spawned. The idea is that * even the zone & keg heads are allocated from the allocator, so we use the @@ -49,12 +113,6 @@ static uma_zone_t zones = &masterzone_z; static uma_zone_t slabzone; static uma_zone_t slabrefzone; /* With refcounters (for UMA_ZONE_REFCNT) */ -static u_int mp_maxid = 0; /* simulate 1 CPU. This should really come from RTEMS SMP. AT this time, RTEMS SMP is not functional */ -#define CPU_ABSENT(x_cpu) 0 /* force all cpus to be present. This should really come from RTEMS SMP. */ -#define CPU_FOREACH(i) \ - for ((i) = 0; (i) <= mp_maxid; (i)++) \ - if (!CPU_ABSENT((i))) - /* * The initial hash tables come out of this zone so they can be allocated * prior to malloc coming up. @@ -157,7 +215,9 @@ enum zfreeskip { SKIP_NONE, SKIP_DTOR, SKIP_FINI }; #define ZFREE_STATFREE 0x00000002 /* Update zone free statistic. */ /* Prototypes.. */ - +#ifndef __rtems__ +static void *obj_alloc(uma_zone_t, int, u_int8_t *, int); +#endif /* __rtems__ */ static void *page_alloc(uma_zone_t, int, u_int8_t *, int); static void *startup_alloc(uma_zone_t, int, u_int8_t *, int); static void page_free(void *, int, u_int8_t); @@ -177,9 +237,16 @@ static void zone_timeout(uma_zone_t zone); static int hash_alloc(struct uma_hash *); static int hash_expand(struct uma_hash *, struct uma_hash *); static void hash_free(struct uma_hash *hash); +#ifndef __rtems__ +static void uma_timeout(void *); +static void uma_startup3(void); +#endif /* __rtems__ */ static void *zone_alloc_item(uma_zone_t, void *, int); static void zone_free_item(uma_zone_t, void *, void *, enum zfreeskip, int); +#ifndef __rtems__ +static void bucket_enable(void); +#endif /* __rtems__ */ static void bucket_init(void); static uma_bucket_t bucket_alloc(int, int); static void bucket_free(uma_bucket_t); @@ -195,6 +262,31 @@ static inline void keg_relock(uma_keg_t keg, uma_zone_t zone); void uma_print_zone(uma_zone_t); void uma_print_stats(void); +#ifndef __rtems__ +static int sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS); +static int sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS); + +SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL); + +SYSCTL_PROC(_vm, OID_AUTO, zone_count, CTLFLAG_RD|CTLTYPE_INT, + 0, 0, sysctl_vm_zone_count, "I", "Number of UMA zones"); + +SYSCTL_PROC(_vm, OID_AUTO, zone_stats, CTLFLAG_RD|CTLTYPE_STRUCT, + 0, 0, sysctl_vm_zone_stats, "s,struct uma_type_header", "Zone Stats"); + +/* + * This routine checks to see whether or not it's safe to enable buckets. + */ + +static void +bucket_enable(void) +{ + if (cnt.v_free_count < cnt.v_free_min) + bucketdisable = 1; + else + bucketdisable = 0; +} +#endif /* __rtems__ */ /* * Initialize bucket_zones, the array of zones of buckets of various sizes. @@ -300,6 +392,28 @@ zone_foreach_keg(uma_zone_t zone, void (*kegfn)(uma_keg_t)) kegfn(klink->kl_keg); } +#ifndef __rtems__ +/* + * Routine called by timeout which is used to fire off some time interval + * based calculations. (stats, hash size, etc.) + * + * Arguments: + * arg Unused + * + * Returns: + * Nothing + */ +static void +uma_timeout(void *unused) +{ + bucket_enable(); + zone_foreach(zone_timeout); + + /* Reschedule this event */ + callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); +} +#endif /* __rtems__ */ + /* * Routine to perform timeout driven calculations. This expands the * hashes and does per cpu statistics aggregation. @@ -631,6 +745,21 @@ finished: flags = slab->us_flags; mem = slab->us_data; +#ifndef __rtems__ + if (keg->uk_flags & UMA_ZONE_VTOSLAB) { + vm_object_t obj; + + if (flags & UMA_SLAB_KMEM) + obj = kmem_object; + else if (flags & UMA_SLAB_KERNEL) + obj = kernel_object; + else + obj = NULL; + for (i = 0; i < keg->uk_ppera; i++) + vsetobj((vm_offset_t)mem + (i * PAGE_SIZE), + obj); + } +#endif /* __rtems__ */ if (keg->uk_flags & UMA_ZONE_OFFPAGE) zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE, ZFREE_STATFREE); @@ -746,6 +875,12 @@ keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int wait) if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) slab = (uma_slab_t )(mem + keg->uk_pgoff); +#ifndef __rtems__ + if (keg->uk_flags & UMA_ZONE_VTOSLAB) + for (i = 0; i < keg->uk_ppera; i++) + vsetslab((vm_offset_t)mem + (i * PAGE_SIZE), slab); +#endif /* __rtems__ */ + slab->us_keg = keg; slab->us_data = mem; slab->us_freecount = keg->uk_ipers; @@ -775,6 +910,21 @@ keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int wait) (keg->uk_rsize * i), keg->uk_size); } +#ifndef __rtems__ + if (keg->uk_flags & UMA_ZONE_VTOSLAB) { + vm_object_t obj; + + if (flags & UMA_SLAB_KMEM) + obj = kmem_object; + else if (flags & UMA_SLAB_KERNEL) + obj = kernel_object; + else + obj = NULL; + for (i = 0; i < keg->uk_ppera; i++) + vsetobj((vm_offset_t)mem + + (i * PAGE_SIZE), obj); + } +#endif /* __rtems__ */ if (keg->uk_flags & UMA_ZONE_OFFPAGE) zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE, ZFREE_STATFREE); @@ -866,11 +1016,79 @@ page_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait) void *p; /* Returned page */ *pflag = UMA_SLAB_KMEM; - p = (void *) malloc(bytes, M_TEMP, wait); +#ifndef __rtems__ + p = (void *) kmem_malloc(kmem_map, bytes, wait); +#else /* __rtems__ */ + p = (void *) malloc(bytes, M_TEMP, wait); +#endif /* __rtems__ */ return (p); } +#ifndef __rtems__ +/* + * Allocates a number of pages from within an object + * + * Arguments: + * bytes The number of bytes requested + * wait Shall we wait? + * + * Returns: + * A pointer to the alloced memory or possibly + * NULL if M_NOWAIT is set. + */ +static void * +obj_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int wait) +{ + vm_object_t object; + vm_offset_t retkva, zkva; + vm_page_t p; + int pages, startpages; + uma_keg_t keg; + + keg = zone_first_keg(zone); + object = keg->uk_obj; + retkva = 0; + + /* + * This looks a little weird since we're getting one page at a time. + */ + VM_OBJECT_LOCK(object); + p = TAILQ_LAST(&object->memq, pglist); + pages = p != NULL ? p->pindex + 1 : 0; + startpages = pages; + zkva = keg->uk_kva + pages * PAGE_SIZE; + for (; bytes > 0; bytes -= PAGE_SIZE) { + p = vm_page_alloc(object, pages, + VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED); + if (p == NULL) { + if (pages != startpages) + pmap_qremove(retkva, pages - startpages); + while (pages != startpages) { + pages--; + p = TAILQ_LAST(&object->memq, pglist); + vm_page_lock_queues(); + vm_page_unwire(p, 0); + vm_page_free(p); + vm_page_unlock_queues(); + } + retkva = 0; + goto done; + } + pmap_qenter(zkva, &p, 1); + if (retkva == 0) + retkva = zkva; + zkva += PAGE_SIZE; + pages += 1; + } +done: + VM_OBJECT_UNLOCK(object); + *flags = UMA_SLAB_PRIV; + + return ((void *)retkva); +} +#endif /* __rtems__ */ + /* * Frees a number of pages to the system * @@ -885,7 +1103,20 @@ page_alloc(uma_zone_t zone, int bytes, u_int8_t *pflag, int wait) static void page_free(void *mem, int size, u_int8_t flags) { - free( mem, M_TEMP ); +#ifndef __rtems__ + vm_map_t map; + + if (flags & UMA_SLAB_KMEM) + map = kmem_map; + else if (flags & UMA_SLAB_KERNEL) + map = kernel_map; + else + panic("UMA: page_free used with invalid flags %d", flags); + + kmem_free(map, (vm_offset_t)mem, size); +#else /* __rtems__ */ + free( mem, M_TEMP ); +#endif /* __rtems__ */ } /* @@ -1037,7 +1268,9 @@ keg_cachespread_init(uma_keg_t keg) keg->uk_rsize = rsize; keg->uk_ppera = pages; keg->uk_ipers = ((pages * PAGE_SIZE) + trailer) / rsize; - //keg->uk_flags |= UMA_ZONE_OFFPAGE | UMA_ZONE_VTOSLAB; +#ifndef __rtems__ + keg->uk_flags |= UMA_ZONE_OFFPAGE | UMA_ZONE_VTOSLAB; +#endif /* __rtems__ */ KASSERT(keg->uk_ipers <= uma_max_ipers, ("keg_small_init: keg->uk_ipers too high(%d) increase max_ipers", keg->uk_ipers)); @@ -1082,8 +1315,10 @@ keg_ctor(void *mem, int size, void *udata, int flags) if (arg->flags & UMA_ZONE_ZINIT) keg->uk_init = zero_init; - /*if (arg->flags & UMA_ZONE_REFCNT || arg->flags & UMA_ZONE_MALLOC) - keg->uk_flags |= UMA_ZONE_VTOSLAB;*/ +#ifndef __rtems__ + if (arg->flags & UMA_ZONE_REFCNT || arg->flags & UMA_ZONE_MALLOC) + keg->uk_flags |= UMA_ZONE_VTOSLAB; +#endif /* __rtems__ */ /* * The +UMA_FRITM_SZ added to uk_size is to account for the @@ -1568,6 +1803,37 @@ uma_startup(void *bootmem, int boot_pages) #endif } +#ifndef __rtems__ +/* see uma.h */ +void +uma_startup2(void) +{ + booted = 1; + bucket_enable(); +#ifdef UMA_DEBUG + printf("UMA startup2 complete.\n"); +#endif +} + +/* + * Initialize our callout handle + * + */ + +static void +uma_startup3(void) +{ +#ifdef UMA_DEBUG + printf("Starting callout.\n"); +#endif + callout_init(&uma_callout, CALLOUT_MPSAFE); + callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); +#ifdef UMA_DEBUG + printf("UMA startup3 complete.\n"); +#endif +} +#endif /* __rtems__ */ + static uma_keg_t uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini, int align, u_int32_t flags) @@ -1657,6 +1923,73 @@ zone_unlock_pair(uma_zone_t a, uma_zone_t b) ZONE_UNLOCK(b); } +#ifndef __rtems__ +int +uma_zsecond_add(uma_zone_t zone, uma_zone_t master) +{ + uma_klink_t klink; + uma_klink_t kl; + int error; + + error = 0; + klink = malloc(sizeof(*klink), M_TEMP, M_WAITOK | M_ZERO); + + zone_lock_pair(zone, master); + /* + * zone must use vtoslab() to resolve objects and must already be + * a secondary. + */ + if ((zone->uz_flags & (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) + != (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) { + error = EINVAL; + goto out; + } + /* + * The new master must also use vtoslab(). + */ + if ((zone->uz_flags & UMA_ZONE_VTOSLAB) != UMA_ZONE_VTOSLAB) { + error = EINVAL; + goto out; + } + /* + * Both must either be refcnt, or not be refcnt. + */ + if ((zone->uz_flags & UMA_ZONE_REFCNT) != + (master->uz_flags & UMA_ZONE_REFCNT)) { + error = EINVAL; + goto out; + } + /* + * The underlying object must be the same size. rsize + * may be different. + */ + if (master->uz_size != zone->uz_size) { + error = E2BIG; + goto out; + } + /* + * Put it at the end of the list. + */ + klink->kl_keg = zone_first_keg(master); + LIST_FOREACH(kl, &zone->uz_kegs, kl_link) { + if (LIST_NEXT(kl, kl_link) == NULL) { + LIST_INSERT_AFTER(kl, klink, kl_link); + break; + } + } + klink = NULL; + zone->uz_flags |= UMA_ZFLAG_MULTI; + zone->uz_slab = zone_fetch_slab_multi; + +out: + zone_unlock_pair(zone, master); + if (klink != NULL) + free(klink, M_TEMP); + + return (error); +} +#endif /* __rtems__ */ + /* See uma.h */ void @@ -2442,7 +2775,7 @@ zone_free_item(uma_zone_t zone, void *item, void *udata, if (flags & ZFREE_STATFREE) zone->uz_frees++; - if (!(zone->uz_flags & UMA_ZONE_VTOSLAB)) { + if (!(zone->uz_flags & UMA_ZONE_VTOSLAB)) { mem = (u_int8_t *)((unsigned long)item & (~UMA_SLAB_MASK)); keg = zone_first_keg(zone); /* Must only be one. */ if (zone->uz_flags & UMA_ZONE_HASH) { @@ -2452,7 +2785,17 @@ zone_free_item(uma_zone_t zone, void *item, void *udata, slab = (uma_slab_t)mem; } } else { - panic("uma virtual memory not supported!" ); +#ifndef __rtems__ + /* This prevents redundant lookups via free(). */ + if ((zone->uz_flags & UMA_ZONE_MALLOC) && udata != NULL) + slab = (uma_slab_t)udata; + else + slab = vtoslab((vm_offset_t)item); + keg = slab->us_keg; + keg_relock(keg, zone); +#else /* __rtems__ */ + panic("uma virtual memory not supported!" ); +#endif /* __rtems__ */ } MPASS(keg == slab->us_keg); @@ -2638,6 +2981,44 @@ uma_zone_set_allocf(uma_zone_t zone, uma_alloc allocf) ZONE_UNLOCK(zone); } +#ifndef __rtems__ +/* See uma.h */ +int +uma_zone_set_obj(uma_zone_t zone, struct vm_object *obj, int count) +{ + uma_keg_t keg; + vm_offset_t kva; + int pages; + + keg = zone_first_keg(zone); + pages = count / keg->uk_ipers; + + if (pages * keg->uk_ipers < count) + pages++; + + kva = kmem_alloc_nofault(kernel_map, pages * UMA_SLAB_SIZE); + + if (kva == 0) + return (0); + if (obj == NULL) { + obj = vm_object_allocate(OBJT_DEFAULT, + pages); + } else { + VM_OBJECT_LOCK_INIT(obj, "uma object"); + _vm_object_allocate(OBJT_DEFAULT, + pages, obj); + } + ZONE_LOCK(zone); + keg->uk_kva = kva; + keg->uk_obj = obj; + keg->uk_maxpages = pages; + keg->uk_allocf = obj_alloc; + keg->uk_flags |= UMA_ZONE_NOFREE | UMA_ZFLAG_PRIVALLOC; + ZONE_UNLOCK(zone); + return (1); +} +#endif /* __rtems__ */ + /* See uma.h */ void uma_prealloc(uma_zone_t zone, int items) @@ -2662,6 +3043,28 @@ uma_prealloc(uma_zone_t zone, int items) ZONE_UNLOCK(zone); } +#ifndef __rtems__ +/* See uma.h */ +u_int32_t * +uma_find_refcnt(uma_zone_t zone, void *item) +{ + uma_slabrefcnt_t slabref; + uma_keg_t keg; + u_int32_t *refcnt; + int idx; + + slabref = (uma_slabrefcnt_t)vtoslab((vm_offset_t)item & + (~UMA_SLAB_MASK)); + keg = slabref->us_keg; + KASSERT(slabref != NULL && slabref->us_keg->uk_flags & UMA_ZONE_REFCNT, + ("uma_find_refcnt(): zone possibly not UMA_ZONE_REFCNT")); + idx = ((unsigned long)item - (unsigned long)slabref->us_data) + / keg->uk_rsize; + refcnt = &slabref->us_freelist[idx].us_refcnt; + return refcnt; +} +#endif /* __rtems__ */ + /* See uma.h */ void uma_reclaim(void) @@ -2669,6 +3072,9 @@ uma_reclaim(void) #ifdef UMA_DEBUG printf("UMA: vm asked us to release pages!\n"); #endif +#ifndef __rtems__ + bucket_enable(); +#endif /* __rtems__ */ zone_foreach(zone_drain); /* * Some slabs may have been freed but this zone will be visited early @@ -2710,6 +3116,9 @@ uma_large_malloc(int size, int wait) return (NULL); mem = page_alloc(NULL, size, &flags, wait); if (mem) { +#ifndef __rtems__ + vsetslab((vm_offset_t)mem, slab); +#endif /* __rtems__ */ slab->us_data = mem; slab->us_flags = flags | UMA_SLAB_MALLOC; slab->us_size = size; @@ -2724,6 +3133,9 @@ uma_large_malloc(int size, int wait) void uma_large_free(uma_slab_t slab) { +#ifndef __rtems__ + vsetobj((vm_offset_t)slab->us_data, kmem_object); +#endif /* __rtems__ */ page_free(slab->us_data, slab->us_size, slab->us_flags); zone_free_item(slabzone, slab, NULL, SKIP_NONE, ZFREE_STATFREE); } @@ -2794,3 +3206,234 @@ uma_print_zone(uma_zone_t zone) } } +#ifndef __rtems__ +#ifdef DDB +/* + * Generate statistics across both the zone and its per-cpu cache's. Return + * desired statistics if the pointer is non-NULL for that statistic. + * + * Note: does not update the zone statistics, as it can't safely clear the + * per-CPU cache statistic. + * + * XXXRW: Following the uc_allocbucket and uc_freebucket pointers here isn't + * safe from off-CPU; we should modify the caches to track this information + * directly so that we don't have to. + */ +static void +uma_zone_sumstat(uma_zone_t z, int *cachefreep, u_int64_t *allocsp, + u_int64_t *freesp) +{ + uma_cache_t cache; + u_int64_t allocs, frees; + int cachefree, cpu; + + allocs = frees = 0; + cachefree = 0; + for (cpu = 0; cpu <= mp_maxid; cpu++) { + if (CPU_ABSENT(cpu)) + continue; + cache = &z->uz_cpu[cpu]; + if (cache->uc_allocbucket != NULL) + cachefree += cache->uc_allocbucket->ub_cnt; + if (cache->uc_freebucket != NULL) + cachefree += cache->uc_freebucket->ub_cnt; + allocs += cache->uc_allocs; + frees += cache->uc_frees; + } + allocs += z->uz_allocs; + frees += z->uz_frees; + if (cachefreep != NULL) + *cachefreep = cachefree; + if (allocsp != NULL) + *allocsp = allocs; + if (freesp != NULL) + *freesp = frees; +} +#endif /* DDB */ + +static int +sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS) +{ + uma_keg_t kz; + uma_zone_t z; + int count; + + count = 0; + mtx_lock(&uma_mtx); + LIST_FOREACH(kz, &uma_kegs, uk_link) { + LIST_FOREACH(z, &kz->uk_zones, uz_link) + count++; + } + mtx_unlock(&uma_mtx); + return (sysctl_handle_int(oidp, &count, 0, req)); +} + +static int +sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS) +{ + struct uma_stream_header ush; + struct uma_type_header uth; + struct uma_percpu_stat ups; + uma_bucket_t bucket; + struct sbuf sbuf; + uma_cache_t cache; + uma_klink_t kl; + uma_keg_t kz; + uma_zone_t z; + uma_keg_t k; + char *buffer; + int buflen, count, error, i; + + mtx_lock(&uma_mtx); +restart: + mtx_assert(&uma_mtx, MA_OWNED); + count = 0; + LIST_FOREACH(kz, &uma_kegs, uk_link) { + LIST_FOREACH(z, &kz->uk_zones, uz_link) + count++; + } + mtx_unlock(&uma_mtx); + + buflen = sizeof(ush) + count * (sizeof(uth) + sizeof(ups) * + (mp_maxid + 1)) + 1; + buffer = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); + + mtx_lock(&uma_mtx); + i = 0; + LIST_FOREACH(kz, &uma_kegs, uk_link) { + LIST_FOREACH(z, &kz->uk_zones, uz_link) + i++; + } + if (i > count) { + free(buffer, M_TEMP); + goto restart; + } + count = i; + + sbuf_new(&sbuf, buffer, buflen, SBUF_FIXEDLEN); + + /* + * Insert stream header. + */ + bzero(&ush, sizeof(ush)); + ush.ush_version = UMA_STREAM_VERSION; + ush.ush_maxcpus = (mp_maxid + 1); + ush.ush_count = count; + if (sbuf_bcat(&sbuf, &ush, sizeof(ush)) < 0) { + mtx_unlock(&uma_mtx); + error = ENOMEM; + goto out; + } + + LIST_FOREACH(kz, &uma_kegs, uk_link) { + LIST_FOREACH(z, &kz->uk_zones, uz_link) { + bzero(&uth, sizeof(uth)); + ZONE_LOCK(z); + strlcpy(uth.uth_name, z->uz_name, UTH_MAX_NAME); + uth.uth_align = kz->uk_align; + uth.uth_size = kz->uk_size; + uth.uth_rsize = kz->uk_rsize; + LIST_FOREACH(kl, &z->uz_kegs, kl_link) { + k = kl->kl_keg; + uth.uth_maxpages += k->uk_maxpages; + uth.uth_pages += k->uk_pages; + uth.uth_keg_free += k->uk_free; + uth.uth_limit = (k->uk_maxpages / k->uk_ppera) + * k->uk_ipers; + } + + /* + * A zone is secondary is it is not the first entry + * on the keg's zone list. + */ + if ((z->uz_flags & UMA_ZONE_SECONDARY) && + (LIST_FIRST(&kz->uk_zones) != z)) + uth.uth_zone_flags = UTH_ZONE_SECONDARY; + + LIST_FOREACH(bucket, &z->uz_full_bucket, ub_link) + uth.uth_zone_free += bucket->ub_cnt; + uth.uth_allocs = z->uz_allocs; + uth.uth_frees = z->uz_frees; + uth.uth_fails = z->uz_fails; + if (sbuf_bcat(&sbuf, &uth, sizeof(uth)) < 0) { + ZONE_UNLOCK(z); + mtx_unlock(&uma_mtx); + error = ENOMEM; + goto out; + } + /* + * While it is not normally safe to access the cache + * bucket pointers while not on the CPU that owns the + * cache, we only allow the pointers to be exchanged + * without the zone lock held, not invalidated, so + * accept the possible race associated with bucket + * exchange during monitoring. + */ + for (i = 0; i < (mp_maxid + 1); i++) { + bzero(&ups, sizeof(ups)); + if (kz->uk_flags & UMA_ZFLAG_INTERNAL) + goto skip; + if (CPU_ABSENT(i)) + goto skip; + cache = &z->uz_cpu[i]; + if (cache->uc_allocbucket != NULL) + ups.ups_cache_free += + cache->uc_allocbucket->ub_cnt; + if (cache->uc_freebucket != NULL) + ups.ups_cache_free += + cache->uc_freebucket->ub_cnt; + ups.ups_allocs = cache->uc_allocs; + ups.ups_frees = cache->uc_frees; +skip: + if (sbuf_bcat(&sbuf, &ups, sizeof(ups)) < 0) { + ZONE_UNLOCK(z); + mtx_unlock(&uma_mtx); + error = ENOMEM; + goto out; + } + } + ZONE_UNLOCK(z); + } + } + mtx_unlock(&uma_mtx); + sbuf_finish(&sbuf); + error = SYSCTL_OUT(req, sbuf_data(&sbuf), sbuf_len(&sbuf)); +out: + free(buffer, M_TEMP); + return (error); +} + +#ifdef DDB +DB_SHOW_COMMAND(uma, db_show_uma) +{ + u_int64_t allocs, frees; + uma_bucket_t bucket; + uma_keg_t kz; + uma_zone_t z; + int cachefree; + + db_printf("%18s %8s %8s %8s %12s\n", "Zone", "Size", "Used", "Free", + "Requests"); + LIST_FOREACH(kz, &uma_kegs, uk_link) { + LIST_FOREACH(z, &kz->uk_zones, uz_link) { + if (kz->uk_flags & UMA_ZFLAG_INTERNAL) { + allocs = z->uz_allocs; + frees = z->uz_frees; + cachefree = 0; + } else + uma_zone_sumstat(z, &cachefree, &allocs, + &frees); + if (!((z->uz_flags & UMA_ZONE_SECONDARY) && + (LIST_FIRST(&kz->uk_zones) != z))) + cachefree += kz->uk_free; + LIST_FOREACH(bucket, &z->uz_full_bucket, ub_link) + cachefree += bucket->ub_cnt; + db_printf("%18s %8ju %8jd %8d %12ju\n", z->uz_name, + (uintmax_t)kz->uk_size, + (intmax_t)(allocs - frees), cachefree, + (uintmax_t)allocs); + } + } +} +#endif +#endif /* __rtems__ */ |