summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_malloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_malloc.c')
-rw-r--r--sys/kern/kern_malloc.c421
1 files changed, 122 insertions, 299 deletions
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
index a8eec9c..12cd4d3 100644
--- a/sys/kern/kern_malloc.c
+++ b/sys/kern/kern_malloc.c
@@ -52,6 +52,8 @@
#include <vm/vm_extern.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
+#include <vm/uma.h>
+#include <vm/uma_int.h>
#if defined(INVARIANTS) && defined(__i386__)
#include <machine/cpu.h>
@@ -80,50 +82,42 @@ SYSINIT(kmem, SI_SUB_KMEM, SI_ORDER_FIRST, kmeminit, NULL)
static MALLOC_DEFINE(M_FREE, "free", "should be on free list");
static struct malloc_type *kmemstatistics;
-static struct kmembuckets bucket[MINBUCKET + 16];
-static struct kmemusage *kmemusage;
static char *kmembase;
static char *kmemlimit;
-static struct mtx malloc_mtx;
-
-u_int vm_kmem_size;
-
-#ifdef INVARIANTS
-/*
- * This structure provides a set of masks to catch unaligned frees.
- */
-static long addrmask[] = { 0,
- 0x00000001, 0x00000003, 0x00000007, 0x0000000f,
- 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
- 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
- 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
+#define KMEM_ZSHIFT 4
+#define KMEM_ZBASE 16
+#define KMEM_ZMASK (KMEM_ZBASE - 1)
+
+#define KMEM_ZMAX 65536
+#define KMEM_ZSIZE (KMEM_ZMAX >> KMEM_ZSHIFT)
+static uma_zone_t kmemzones[KMEM_ZSIZE + 1];
+
+
+/* These won't be powers of two for long */
+struct {
+ int size;
+ char *name;
+} kmemsizes[] = {
+ {16, "16"},
+ {32, "32"},
+ {64, "64"},
+ {128, "128"},
+ {256, "256"},
+ {512, "512"},
+ {1024, "1024"},
+ {2048, "2048"},
+ {4096, "4096"},
+ {8192, "8192"},
+ {16384, "16384"},
+ {32768, "32768"},
+ {65536, "65536"},
+ {0, NULL},
};
-/*
- * The WEIRD_ADDR is used as known text to copy into free objects so
- * that modifications after frees can be detected.
- */
-#define WEIRD_ADDR 0xdeadc0de
-#define MAX_COPY 64
+static struct mtx malloc_mtx;
-/*
- * Normally the first word of the structure is used to hold the list
- * pointer for free objects. However, when running with diagnostics,
- * we use the third and fourth fields, so as to catch modifications
- * in the most commonly trashed first two words.
- */
-struct freelist {
- long spare0;
- struct malloc_type *type;
- long spare1;
- caddr_t next;
-};
-#else /* !INVARIANTS */
-struct freelist {
- caddr_t next;
-};
-#endif /* INVARIANTS */
+u_int vm_kmem_size;
/*
* malloc:
@@ -139,17 +133,10 @@ malloc(size, type, flags)
struct malloc_type *type;
int flags;
{
- register struct kmembuckets *kbp;
- register struct kmemusage *kup;
- register struct freelist *freep;
- long indx, npg, allocsize;
int s;
- caddr_t va, cp, savedlist;
-#ifdef INVARIANTS
- long *end, *lp;
- int copysize;
- const char *savedtype;
-#endif
+ long indx;
+ caddr_t va;
+ uma_zone_t zone;
register struct malloc_type *ksp = type;
#if defined(INVARIANTS)
@@ -157,124 +144,52 @@ malloc(size, type, flags)
KASSERT(curthread->td_intr_nesting_level == 0,
("malloc(M_WAITOK) in interrupt context"));
#endif
- indx = BUCKETINDX(size);
- kbp = &bucket[indx];
s = splmem();
- mtx_lock(&malloc_mtx);
+ /* mtx_lock(&malloc_mtx); XXX */
while (ksp->ks_memuse >= ksp->ks_limit) {
if (flags & M_NOWAIT) {
splx(s);
- mtx_unlock(&malloc_mtx);
+ /* mtx_unlock(&malloc_mtx); XXX */
return ((void *) NULL);
}
if (ksp->ks_limblocks < 65535)
ksp->ks_limblocks++;
- msleep((caddr_t)ksp, &malloc_mtx, PSWP+2, type->ks_shortdesc,
+ msleep((caddr_t)ksp, /* &malloc_mtx */ NULL, PSWP+2, type->ks_shortdesc,
0);
}
- ksp->ks_size |= 1 << indx;
-#ifdef INVARIANTS
- copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;
-#endif
- if (kbp->kb_next == NULL) {
- kbp->kb_last = NULL;
- if (size > MAXALLOCSAVE)
- allocsize = roundup(size, PAGE_SIZE);
- else
- allocsize = 1 << indx;
- npg = btoc(allocsize);
-
- mtx_unlock(&malloc_mtx);
- va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg), flags);
-
+ /* mtx_unlock(&malloc_mtx); XXX */
+
+ if (size <= KMEM_ZMAX) {
+ indx = size;
+ if (indx & KMEM_ZMASK)
+ indx = (indx & ~KMEM_ZMASK) + KMEM_ZBASE;
+ zone = kmemzones[indx >> KMEM_ZSHIFT];
+ indx = zone->uz_size;
+ va = uma_zalloc(zone, flags);
if (va == NULL) {
- splx(s);
- return ((void *) NULL);
- }
- /*
- * Enter malloc_mtx after the error check to avoid having to
- * immediately exit it again if there is an error.
- */
- mtx_lock(&malloc_mtx);
-
- kbp->kb_total += kbp->kb_elmpercl;
- kup = btokup(va);
- kup->ku_indx = indx;
- if (allocsize > MAXALLOCSAVE) {
- if (npg > 65535)
- panic("malloc: allocation too large");
- kup->ku_pagecnt = npg;
- ksp->ks_memuse += allocsize;
+ /* mtx_lock(&malloc_mtx); XXX */
goto out;
}
- kup->ku_freecnt = kbp->kb_elmpercl;
- kbp->kb_totalfree += kbp->kb_elmpercl;
- /*
- * Just in case we blocked while allocating memory,
- * and someone else also allocated memory for this
- * bucket, don't assume the list is still empty.
- */
- savedlist = kbp->kb_next;
- kbp->kb_next = cp = va + (npg * PAGE_SIZE) - allocsize;
- for (;;) {
- freep = (struct freelist *)cp;
-#ifdef INVARIANTS
- /*
- * Copy in known text to detect modification
- * after freeing.
- */
- end = (long *)&cp[copysize];
- for (lp = (long *)cp; lp < end; lp++)
- *lp = WEIRD_ADDR;
- freep->type = M_FREE;
-#endif /* INVARIANTS */
- if (cp <= va)
- break;
- cp -= allocsize;
- freep->next = cp;
+ ksp->ks_size |= indx;
+ } else {
+ /* XXX This is not the next power of two so this will break ks_size */
+ indx = roundup(size, PAGE_SIZE);
+ zone = NULL;
+ va = uma_large_malloc(size, flags);
+ if (va == NULL) {
+ /* mtx_lock(&malloc_mtx); XXX */
+ goto out;
}
- freep->next = savedlist;
- if (kbp->kb_last == NULL)
- kbp->kb_last = (caddr_t)freep;
- }
- va = kbp->kb_next;
- kbp->kb_next = ((struct freelist *)va)->next;
-#ifdef INVARIANTS
- freep = (struct freelist *)va;
- savedtype = (const char *) freep->type->ks_shortdesc;
- freep->type = (struct malloc_type *)WEIRD_ADDR;
- if ((intptr_t)(void *)&freep->next & 0x2)
- freep->next = (caddr_t)((WEIRD_ADDR >> 16)|(WEIRD_ADDR << 16));
- else
- freep->next = (caddr_t)WEIRD_ADDR;
- end = (long *)&va[copysize];
- for (lp = (long *)va; lp < end; lp++) {
- if (*lp == WEIRD_ADDR)
- continue;
- printf("%s %ld of object %p size %lu %s %s (0x%lx != 0x%lx)\n",
- "Data modified on freelist: word",
- (long)(lp - (long *)va), (void *)va, size,
- "previous type", savedtype, *lp, (u_long)WEIRD_ADDR);
- break;
}
- freep->spare0 = 0;
-#endif /* INVARIANTS */
- kup = btokup(va);
- if (kup->ku_indx != indx)
- panic("malloc: wrong bucket");
- if (kup->ku_freecnt == 0)
- panic("malloc: lost data");
- kup->ku_freecnt--;
- kbp->kb_totalfree--;
- ksp->ks_memuse += 1 << indx;
-out:
- kbp->kb_calls++;
+ /* mtx_lock(&malloc_mtx); XXX */
+ ksp->ks_memuse += indx;
ksp->ks_inuse++;
+out:
ksp->ks_calls++;
if (ksp->ks_memuse > ksp->ks_maxused)
ksp->ks_maxused = ksp->ks_memuse;
splx(s);
- mtx_unlock(&malloc_mtx);
+ /* mtx_unlock(&malloc_mtx); XXX */
/* XXX: Do idle pre-zeroing. */
if (va != NULL && (flags & M_ZERO))
bzero(va, size);
@@ -293,124 +208,41 @@ free(addr, type)
void *addr;
struct malloc_type *type;
{
- register struct kmembuckets *kbp;
- register struct kmemusage *kup;
- register struct freelist *freep;
- long size;
+ uma_slab_t slab;
+ void *mem;
+ u_long size;
int s;
-#ifdef INVARIANTS
- struct freelist *fp;
- long *end, *lp, alloc, copysize;
-#endif
register struct malloc_type *ksp = type;
/* free(NULL, ...) does nothing */
if (addr == NULL)
return;
- KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit,
- ("free: address %p out of range", (void *)addr));
- kup = btokup(addr);
- size = 1 << kup->ku_indx;
- kbp = &bucket[kup->ku_indx];
+ size = 0;
s = splmem();
- mtx_lock(&malloc_mtx);
-#ifdef INVARIANTS
- /*
- * Check for returns of data that do not point to the
- * beginning of the allocation.
- */
- if (size > PAGE_SIZE)
- alloc = addrmask[BUCKETINDX(PAGE_SIZE)];
- else
- alloc = addrmask[kup->ku_indx];
- if (((uintptr_t)(void *)addr & alloc) != 0)
- panic("free: unaligned addr %p, size %ld, type %s, mask %ld",
- (void *)addr, size, type->ks_shortdesc, alloc);
-#endif /* INVARIANTS */
- if (size > MAXALLOCSAVE) {
- mtx_unlock(&malloc_mtx);
- kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt));
- mtx_lock(&malloc_mtx);
-
- size = kup->ku_pagecnt << PAGE_SHIFT;
- ksp->ks_memuse -= size;
- kup->ku_indx = 0;
- kup->ku_pagecnt = 0;
- if (ksp->ks_memuse + size >= ksp->ks_limit &&
- ksp->ks_memuse < ksp->ks_limit)
- wakeup((caddr_t)ksp);
- ksp->ks_inuse--;
- kbp->kb_total -= 1;
- splx(s);
- mtx_unlock(&malloc_mtx);
- return;
- }
- freep = (struct freelist *)addr;
-#ifdef INVARIANTS
- /*
- * Check for multiple frees. Use a quick check to see if
- * it looks free before laboriously searching the freelist.
- */
- if (freep->spare0 == WEIRD_ADDR) {
- fp = (struct freelist *)kbp->kb_next;
- while (fp) {
- if (fp->spare0 != WEIRD_ADDR)
- panic("free: free item %p modified", fp);
- else if (addr == (caddr_t)fp)
- panic("free: multiple freed item %p", addr);
- fp = (struct freelist *)fp->next;
- }
- }
- /*
- * Copy in known text to detect modification after freeing
- * and to make it look free. Also, save the type being freed
- * so we can list likely culprit if modification is detected
- * when the object is reallocated.
- */
- copysize = size < MAX_COPY ? size : MAX_COPY;
- end = (long *)&((caddr_t)addr)[copysize];
- for (lp = (long *)addr; lp < end; lp++)
- *lp = WEIRD_ADDR;
- freep->type = type;
-#endif /* INVARIANTS */
- kup->ku_freecnt++;
- if (kup->ku_freecnt >= kbp->kb_elmpercl) {
- if (kup->ku_freecnt > kbp->kb_elmpercl)
- panic("free: multiple frees");
- else if (kbp->kb_totalfree > kbp->kb_highwat)
- kbp->kb_couldfree++;
+
+ mem = (void *)((u_long)addr & (~UMA_SLAB_MASK));
+ slab = hash_sfind(mallochash, mem);
+
+ if (slab == NULL)
+ panic("free: address %p(%p) has not been allocated.\n", addr, mem);
+
+ if (!(slab->us_flags & UMA_SLAB_MALLOC)) {
+ size = slab->us_zone->uz_size;
+ uma_zfree_arg(slab->us_zone, addr, slab);
+ } else {
+ size = slab->us_size;
+ uma_large_free(slab);
}
- kbp->kb_totalfree++;
+ /* mtx_lock(&malloc_mtx); XXX */
+
ksp->ks_memuse -= size;
if (ksp->ks_memuse + size >= ksp->ks_limit &&
ksp->ks_memuse < ksp->ks_limit)
wakeup((caddr_t)ksp);
ksp->ks_inuse--;
-#ifdef OLD_MALLOC_MEMORY_POLICY
- if (kbp->kb_next == NULL)
- kbp->kb_next = addr;
- else
- ((struct freelist *)kbp->kb_last)->next = addr;
- freep->next = NULL;
- kbp->kb_last = addr;
-#else
- /*
- * Return memory to the head of the queue for quick reuse. This
- * can improve performance by improving the probability of the
- * item being in the cache when it is reused.
- */
- if (kbp->kb_next == NULL) {
- kbp->kb_next = addr;
- kbp->kb_last = addr;
- freep->next = NULL;
- } else {
- freep->next = kbp->kb_next;
- kbp->kb_next = addr;
- }
-#endif
splx(s);
- mtx_unlock(&malloc_mtx);
+ /* mtx_unlock(&malloc_mtx); XXX */
}
/*
@@ -423,7 +255,7 @@ realloc(addr, size, type, flags)
struct malloc_type *type;
int flags;
{
- struct kmemusage *kup;
+ uma_slab_t slab;
unsigned long alloc;
void *newaddr;
@@ -431,15 +263,18 @@ realloc(addr, size, type, flags)
if (addr == NULL)
return (malloc(size, type, flags));
+ slab = hash_sfind(mallochash,
+ (void *)((u_long)addr & ~(UMA_SLAB_MASK)));
+
/* Sanity check */
- KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit,
+ KASSERT(slab != NULL,
("realloc: address %p out of range", (void *)addr));
/* Get the size of the original block */
- kup = btokup(addr);
- alloc = 1 << kup->ku_indx;
- if (alloc > MAXALLOCSAVE)
- alloc = kup->ku_pagecnt << PAGE_SHIFT;
+ if (slab->us_zone)
+ alloc = slab->us_zone->uz_size;
+ else
+ alloc = slab->us_size;
/* Reuse the original block if appropriate */
if (size <= alloc
@@ -484,16 +319,11 @@ kmeminit(dummy)
register long indx;
u_long npg;
u_long mem_size;
-
-#if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0)
-#error "kmeminit: MAXALLOCSAVE not power of 2"
-#endif
-#if (MAXALLOCSAVE > MINALLOCSIZE * 32768)
-#error "kmeminit: MAXALLOCSAVE too big"
-#endif
-#if (MAXALLOCSAVE < PAGE_SIZE)
-#error "kmeminit: MAXALLOCSAVE too small"
-#endif
+ void *hashmem;
+ u_long hashsize;
+ int highbit;
+ int bits;
+ int i;
mtx_init(&malloc_mtx, "malloc", MTX_DEF);
@@ -544,17 +374,36 @@ kmeminit(dummy)
npg = (nmbufs * MSIZE + nmbclusters * MCLBYTES + nmbcnt *
sizeof(u_int) + vm_kmem_size) / PAGE_SIZE;
- kmemusage = (struct kmemusage *) kmem_alloc(kernel_map,
- (vm_size_t)(npg * sizeof(struct kmemusage)));
kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,
(vm_offset_t *)&kmemlimit, (vm_size_t)(npg * PAGE_SIZE));
kmem_map->system_map = 1;
- for (indx = 0; indx < MINBUCKET + 16; indx++) {
- if (1 << indx >= PAGE_SIZE)
- bucket[indx].kb_elmpercl = 1;
- else
- bucket[indx].kb_elmpercl = PAGE_SIZE / (1 << indx);
- bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
+
+ hashsize = npg * sizeof(void *);
+
+ highbit = 0;
+ bits = 0;
+ /* The hash size must be a power of two */
+ for (i = 0; i < 8 * sizeof(hashsize); i++)
+ if (hashsize & (1 << i)) {
+ highbit = i;
+ bits++;
+ }
+ if (bits > 1)
+ hashsize = 1 << (highbit);
+
+ hashmem = (void *)kmem_alloc(kernel_map, (vm_size_t)hashsize);
+ uma_startup2(hashmem, hashsize / sizeof(void *));
+
+ for (i = 0, indx = 0; kmemsizes[indx].size != 0; indx++) {
+ uma_zone_t zone;
+ int size = kmemsizes[indx].size;
+ char *name = kmemsizes[indx].name;
+
+ zone = uma_zcreate(name, size, NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_MALLOC);
+ for (;i <= size; i+= KMEM_ZBASE)
+ kmemzones[i >> KMEM_ZSHIFT] = zone;
+
}
}
@@ -588,12 +437,6 @@ malloc_uninit(data)
{
struct malloc_type *type = (struct malloc_type *)data;
struct malloc_type *t;
-#ifdef INVARIANTS
- struct kmembuckets *kbp;
- struct freelist *freep;
- long indx;
- int s;
-#endif
if (type->ks_magic != M_MAGIC)
panic("malloc type lacks magic");
@@ -604,26 +447,6 @@ malloc_uninit(data)
if (type->ks_limit == 0)
panic("malloc_uninit on uninitialized type");
-#ifdef INVARIANTS
- s = splmem();
- mtx_lock(&malloc_mtx);
- for (indx = 0; indx < MINBUCKET + 16; indx++) {
- kbp = bucket + indx;
- freep = (struct freelist*)kbp->kb_next;
- while (freep) {
- if (freep->type == type)
- freep->type = M_FREE;
- freep = (struct freelist*)freep->next;
- }
- }
- splx(s);
- mtx_unlock(&malloc_mtx);
-
- if (type->ks_memuse != 0)
- printf("malloc_uninit: %ld bytes of '%s' still allocated\n",
- type->ks_memuse, type->ks_shortdesc);
-#endif
-
if (type == kmemstatistics)
kmemstatistics = type->ks_next;
else {
OpenPOWER on IntegriCloud