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.
This commit is contained in:
Jennifer Averett
2012-04-19 13:25:28 -05:00
parent 148b3fd7e5
commit 74587c37d8
3 changed files with 677 additions and 35 deletions

View File

@@ -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

View File

@@ -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',
]
)

View File

@@ -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/lock.h>
#include <freebsd/sys/mutex.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/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__ */