#ifndef STANDALONE_DEBUG
#include "libc_private.h"
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/queue.h>
#include <sys/ktrace.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <limits.h>
#include <machine/atomic.h>
#include <machine/cpufunc.h>
#ifdef STANDALONE_DEBUG
void _nmalloc_thr_init(void);
#else
#include "spinlock.h"
#include "un-namespace.h"
#endif
#ifndef MAP_SIZEALIGN
#define MAP_SIZEALIGN 0
#endif
#if SSIZE_MAX == 0x7FFFFFFF
#define ADDRBITS 32
#define UVM_BITS 32
#else
#define ADDRBITS 64
#define UVM_BITS 48
#endif
#if LONG_MAX == 0x7FFFFFFF
#define LONG_BITS 32
#define LONG_BITS_SHIFT 5
#else
#define LONG_BITS 64
#define LONG_BITS_SHIFT 6
#endif
#define LOCKEDPTR ((void *)(intptr_t)-1)
#define NREGIONS_BITS 10
#define NREGIONS (1 << NREGIONS_BITS)
#define NREGIONS_MASK (NREGIONS - 1)
#define NREGIONS_SHIFT (UVM_BITS - NREGIONS_BITS)
#define NREGIONS_SIZE (1LU << NREGIONS_SHIFT)
typedef struct region *region_t;
typedef struct slglobaldata *slglobaldata_t;
typedef struct slab *slab_t;
struct region {
uintptr_t mask;
slab_t slab;
};
static struct region Regions[NREGIONS];
#define CHUNKFACTOR 8
#if ADDRBITS == 32
#define NZONES (16 + 9 * CHUNKFACTOR + 16 * CHUNKFACTOR)
#else
#define NZONES (16 + 9 * CHUNKFACTOR + 48 * CHUNKFACTOR)
#endif
static int MaxChunks[NZONES];
#define NDEPOTS 8
#define MAXCHUNKS (LONG_BITS * LONG_BITS)
#define MAXCHUNKS_BITS (LONG_BITS_SHIFT * LONG_BITS_SHIFT)
#define LITSLABSIZE (32 * 1024)
#define NOMSLABSIZE (2 * 1024 * 1024)
#define BIGSLABSIZE (128 * 1024 * 1024)
#define ZALLOC_SLAB_MAGIC 0x736c6162
TAILQ_HEAD(slab_list, slab);
struct slab {
struct slab *next;
TAILQ_ENTRY(slab) entry;
int32_t magic;
u_int navail;
u_int nmax;
u_int free_bit;
u_int free_index;
u_long bitmap[LONG_BITS];
size_t slab_size;
size_t chunk_size;
int zone_index;
enum { UNKNOWN, AVAIL, EMPTY, FULL } state;
int flags;
region_t region;
char *chunks;
slglobaldata_t slgd;
};
#define NMAGSHORTCUT 16
struct slglobaldata {
spinlock_t lock;
struct zoneinfo {
slab_t avail_base;
slab_t empty_base;
int best_region;
int mag_index;
int avail_count;
int empty_count;
void *mag_shortcut[NMAGSHORTCUT];
} zone[NZONES];
struct slab_list full_zones;
int masked;
int biggest_index;
size_t nslabs;
};
#define SLAB_ZEROD 0x0001
#define MIN_CHUNK_SIZE 8
#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1)
#define SAFLAG_ZERO 0x00000001
#ifdef INVARIANTS
#define WEIRD_ADDR 0xdeadc0de
#endif
#define MASSERT(exp) do { if (__predict_false(!(exp))) \
_mpanic("assertion: %s in %s", \
#exp, __func__); \
} while (0)
#if defined(__LIBC_RTLD)
#define TLS_ATTRIBUTE
#else
#define TLS_ATTRIBUTE __attribute__ ((tls_model ("initial-exec")));
#endif
static __thread struct slglobaldata slglobal TLS_ATTRIBUTE;
static pthread_key_t thread_malloc_key;
static pthread_once_t thread_malloc_once = PTHREAD_ONCE_INIT;
static struct slglobaldata slglobaldepot;
static int opt_madvise = 0;
static int opt_free = 0;
static int opt_cache = 4;
static int opt_utrace = 0;
static int g_malloc_flags = 0;
static int malloc_panic;
#ifdef INVARIANTS
static const int32_t weirdary[16] = {
WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR,
WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR,
WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR,
WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR, WEIRD_ADDR
};
#endif
static void *memalloc(size_t size, int flags);
static void *memrealloc(void *ptr, size_t size);
static void memfree(void *ptr, int);
static int memalign(void **memptr, size_t alignment, size_t size);
static slab_t slaballoc(int zi, size_t chunking, size_t chunk_size);
static void slabfree(slab_t slab);
static void slabterm(slglobaldata_t slgd, slab_t slab);
static void *_vmem_alloc(int ri, size_t slab_size);
static void _vmem_free(void *ptr, size_t slab_size);
static void _mpanic(const char *ctl, ...) __printflike(1, 2);
#ifndef STANDALONE_DEBUG
static void malloc_init(void) __constructor(101);
#else
static void malloc_init(void) __constructor(101);
#endif
struct nmalloc_utrace {
void *p;
size_t s;
void *r;
};
#define UTRACE(a, b, c) \
if (opt_utrace) { \
struct nmalloc_utrace ut = { \
.p = (a), \
.s = (b), \
.r = (c) \
}; \
utrace(&ut, sizeof(ut)); \
}
#ifdef INVARIANTS
static int use_malloc_pattern;
#endif
static void
malloc_init(void)
{
const char *p = NULL;
TAILQ_INIT(&slglobal.full_zones);
Regions[0].mask = -1;
if (issetugid() == 0)
p = getenv("MALLOC_OPTIONS");
for (; p != NULL && *p != '\0'; p++) {
switch(*p) {
case 'u':
opt_utrace = 0;
break;
case 'U':
opt_utrace = 1;
break;
case 'h':
opt_madvise = 0;
break;
case 'H':
opt_madvise = 1;
break;
case 'c':
if (opt_cache > 0)
--opt_cache;
break;
case 'C':
++opt_cache;
break;
case 'f':
opt_free = 0;
break;
case 'F':
opt_free = 1;
break;
case 'z':
g_malloc_flags = 0;
break;
case 'Z':
g_malloc_flags = SAFLAG_ZERO;
break;
default:
break;
}
}
UTRACE((void *) -1, 0, NULL);
}
static void _nmalloc_thr_init_once(void);
static void _nmalloc_thr_destructor(void *thrp);
void
_nmalloc_thr_init(void)
{
static int did_init;
TAILQ_INIT(&slglobal.full_zones);
if (slglobal.masked)
return;
slglobal.masked = 1;
if (did_init == 0) {
did_init = 1;
pthread_once(&thread_malloc_once, _nmalloc_thr_init_once);
}
pthread_setspecific(thread_malloc_key, &slglobal);
slglobal.masked = 0;
}
void
_nmalloc_thr_prepfork(void)
{
if (__isthreaded)
_SPINLOCK(&slglobaldepot.lock);
}
void
_nmalloc_thr_parentfork(void)
{
if (__isthreaded)
_SPINUNLOCK(&slglobaldepot.lock);
}
void
_nmalloc_thr_childfork(void)
{
if (__isthreaded)
_SPINUNLOCK(&slglobaldepot.lock);
}
static void
_nmalloc_thr_init_once(void)
{
pthread_key_create(&thread_malloc_key, _nmalloc_thr_destructor);
}
static void
_nmalloc_thr_destructor(void *thrp)
{
slglobaldata_t slgd = thrp;
struct zoneinfo *zinfo;
slab_t slab;
void *ptr;
int i;
int j;
slgd->masked = 1;
for (i = 0; i <= slgd->biggest_index; i++) {
zinfo = &slgd->zone[i];
while ((j = zinfo->mag_index) > 0) {
--j;
ptr = zinfo->mag_shortcut[j];
zinfo->mag_shortcut[j] = NULL;
zinfo->mag_index = j;
memfree(ptr, 0);
}
while ((slab = zinfo->empty_base) != NULL) {
zinfo->empty_base = slab->next;
--zinfo->empty_count;
slabterm(slgd, slab);
}
while ((slab = zinfo->avail_base) != NULL) {
zinfo->avail_base = slab->next;
--zinfo->avail_count;
slabterm(slgd, slab);
}
while ((slab = TAILQ_FIRST(&slgd->full_zones)) != NULL) {
TAILQ_REMOVE(&slgd->full_zones, slab, entry);
slabterm(slgd, slab);
}
}
}
static __inline int
zoneindex(size_t *bytes, size_t *chunking)
{
size_t n = (size_t)*bytes;
size_t x;
size_t c;
int i;
if (n < 128) {
if (n < 16) {
*bytes = n = (n + 7) & ~7;
*chunking = 8;
return(n / 8 - 1);
} else {
*bytes = n = (n + 15) & ~15;
*chunking = 16;
return(n / 16 + 2);
}
}
if (n < 4096) {
x = 256;
c = x / (CHUNKFACTOR * 2);
i = 16;
} else {
x = 8192;
c = x / (CHUNKFACTOR * 2);
i = 16 + CHUNKFACTOR * 5;
}
while (n >= x) {
x <<= 1;
c <<= 1;
i += CHUNKFACTOR;
if (x == 0)
_mpanic("slaballoc: byte value too high");
}
*bytes = n = roundup2(n, c);
*chunking = c;
return (i + n / c - CHUNKFACTOR);
#if 0
*bytes = n = (n + c - 1) & ~(c - 1);
*chunking = c;
return (n / c + i);
if (n < 256) {
*bytes = n = (n + 15) & ~15;
*chunking = 16;
return(n / (CHUNKINGLO*2) + CHUNKINGLO*1 - 1);
}
if (n < 8192) {
if (n < 512) {
*bytes = n = (n + 31) & ~31;
*chunking = 32;
return(n / (CHUNKINGLO*4) + CHUNKINGLO*2 - 1);
}
if (n < 1024) {
*bytes = n = (n + 63) & ~63;
*chunking = 64;
return(n / (CHUNKINGLO*8) + CHUNKINGLO*3 - 1);
}
if (n < 2048) {
*bytes = n = (n + 127) & ~127;
*chunking = 128;
return(n / (CHUNKINGLO*16) + CHUNKINGLO*4 - 1);
}
if (n < 4096) {
*bytes = n = (n + 255) & ~255;
*chunking = 256;
return(n / (CHUNKINGLO*32) + CHUNKINGLO*5 - 1);
}
*bytes = n = (n + 511) & ~511;
*chunking = 512;
return(n / (CHUNKINGLO*64) + CHUNKINGLO*6 - 1);
}
if (n < 16384) {
*bytes = n = (n + 1023) & ~1023;
*chunking = 1024;
return(n / (CHUNKINGLO*128) + CHUNKINGLO*7 - 1);
}
if (n < 32768) {
*bytes = n = (n + 2047) & ~2047;
*chunking = 2048;
return(n / (CHUNKINGLO*256) + CHUNKINGLO*8 - 1);
}
if (n < 65536) {
*bytes = n = (n + 4095) & ~4095;
*chunking = 4096;
return(n / (CHUNKINGLO*512) + CHUNKINGLO*9 - 1);
}
x = 131072;
c = 8192;
i = CHUNKINGLO*10 - 1;
while (n >= x) {
x <<= 1;
c <<= 1;
i += CHUNKINGHI;
if (x == 0)
_mpanic("slaballoc: byte value too high");
}
*bytes = n = (n + c - 1) & ~(c - 1);
*chunking = c;
return (n / c + i);
#endif
}
void *
__malloc(size_t size)
{
void *ptr;
ptr = memalloc(size, 0);
if (ptr == NULL)
errno = ENOMEM;
else
UTRACE(0, size, ptr);
return(ptr);
}
void *
__calloc(size_t number, size_t size)
{
void *ptr;
ptr = memalloc(number * size, SAFLAG_ZERO);
if (ptr == NULL)
errno = ENOMEM;
else
UTRACE(0, number * size, ptr);
return(ptr);
}
void *
__realloc(void *ptr, size_t size)
{
void *ret;
if (ptr == NULL)
ret = memalloc(size, 0);
else
ret = memrealloc(ptr, size);
if (ret == NULL)
errno = ENOMEM;
else
UTRACE(ptr, size, ret);
return(ret);
}
void *
__aligned_alloc(size_t alignment, size_t size)
{
void *ptr;
int rc;
ptr = NULL;
rc = memalign(&ptr, alignment, size);
if (rc)
errno = rc;
return (ptr);
}
int
__posix_memalign(void **memptr, size_t alignment, size_t size)
{
int rc;
if (alignment < sizeof(void *)) {
*memptr = NULL;
return(EINVAL);
}
rc = memalign(memptr, alignment, size);
return (rc);
}
static int
memalign(void **memptr, size_t alignment, size_t size)
{
if (alignment < 1) {
*memptr = NULL;
return(EINVAL);
}
if ((alignment | (alignment - 1)) + 1 != (alignment << 1)) {
*memptr = NULL;
return(EINVAL);
}
while (alignment < size) {
alignment <<= 1;
if (alignment == 0)
_mpanic("posix_memalign: byte value too high");
}
*memptr = memalloc(alignment, 0);
return(*memptr ? 0 : ENOMEM);
}
void
__free(void *ptr)
{
if (ptr) {
UTRACE(ptr, 0, 0);
memfree(ptr, 0);
}
}
static void *
memalloc(size_t size, int flags)
{
slglobaldata_t slgd;
struct zoneinfo *zinfo;
slab_t slab;
size_t chunking;
int bmi;
int bno;
u_long *bmp;
int zi;
#ifdef INVARIANTS
int i;
#endif
int j;
char *obj;
if (size == 0)
size = 1;
flags |= g_malloc_flags;
zi = zoneindex(&size, &chunking);
MASSERT(zi < NZONES);
if (size == 0)
return(NULL);
slgd = &slglobal;
zinfo = &slgd->zone[zi];
if ((j = zinfo->mag_index) != 0) {
zinfo->mag_index = --j;
obj = zinfo->mag_shortcut[j];
zinfo->mag_shortcut[j] = NULL;
if (flags & SAFLAG_ZERO)
bzero(obj, size);
return obj;
}
retry:
if ((slab = zinfo->avail_base) == NULL) {
if ((slab = zinfo->empty_base) == NULL) {
slab = slaballoc(zi, chunking, size);
if (slab == NULL)
return(NULL);
slab->next = zinfo->avail_base;
zinfo->avail_base = slab;
++zinfo->avail_count;
slab->state = AVAIL;
if (slgd->biggest_index < zi)
slgd->biggest_index = zi;
++slgd->nslabs;
} else {
zinfo->empty_base = slab->next;
slab->next = zinfo->avail_base;
zinfo->avail_base = slab;
++zinfo->avail_count;
slab->state = AVAIL;
--zinfo->empty_count;
}
}
bno = slab->free_bit;
bmi = slab->free_index;
bmp = &slab->bitmap[bmi];
if (*bmp & (1LU << bno)) {
atomic_clear_long(bmp, 1LU << bno);
obj = slab->chunks + ((bmi << LONG_BITS_SHIFT) + bno) * size;
slab->free_bit = (bno + 1) & (LONG_BITS - 1);
atomic_add_int(&slab->navail, -1);
if (flags & SAFLAG_ZERO)
bzero(obj, size);
return (obj);
}
if (slab->navail == 0) {
zinfo->avail_base = slab->next;
--zinfo->avail_count;
slab->state = FULL;
TAILQ_INSERT_TAIL(&slgd->full_zones, slab, entry);
goto retry;
}
while (bmi < LONG_BITS) {
bmp = &slab->bitmap[bmi];
if (*bmp) {
bno = bsflong(*bmp);
atomic_clear_long(bmp, 1LU << bno);
obj = slab->chunks + ((bmi << LONG_BITS_SHIFT) + bno) *
size;
slab->free_index = bmi;
slab->free_bit = (bno + 1) & (LONG_BITS - 1);
atomic_add_int(&slab->navail, -1);
if (flags & SAFLAG_ZERO)
bzero(obj, size);
return (obj);
}
++bmi;
}
bmi = 0;
while (bmi < LONG_BITS) {
bmp = &slab->bitmap[bmi];
if (*bmp) {
bno = bsflong(*bmp);
atomic_clear_long(bmp, 1LU << bno);
obj = slab->chunks + ((bmi << LONG_BITS_SHIFT) + bno) *
size;
slab->free_index = bmi;
slab->free_bit = (bno + 1) & (LONG_BITS - 1);
atomic_add_int(&slab->navail, -1);
if (flags & SAFLAG_ZERO)
bzero(obj, size);
return (obj);
}
++bmi;
}
_mpanic("slaballoc: corrupted zone: navail %d", slab->navail);
return NULL;
}
static void *
memrealloc(void *ptr, size_t nsize)
{
region_t region;
slab_t slab;
size_t osize;
char *obj;
int flags = 0;
if (nsize == 0)
nsize = 1;
flags |= g_malloc_flags;
region = &Regions[((uintptr_t)ptr >> NREGIONS_SHIFT) & NREGIONS_MASK];
if ((slab = region->slab) == NULL)
slab = (void *)((uintptr_t)ptr & region->mask);
MASSERT(slab->magic == ZALLOC_SLAB_MAGIC);
osize = slab->chunk_size;
if (nsize <= osize) {
if (osize < 32 || nsize >= osize / 2) {
obj = ptr;
if ((flags & SAFLAG_ZERO) && nsize < osize)
bzero(obj + nsize, osize - nsize);
return(obj);
}
}
obj = memalloc(nsize, 0);
if (obj) {
if (nsize > osize)
nsize = osize;
bcopy(ptr, obj, nsize);
memfree(ptr, 0);
}
return (obj);
}
static void
memfree(void *ptr, int flags)
{
region_t region;
slglobaldata_t slgd;
slab_t slab;
slab_t stmp;
slab_t *slabp;
int bmi;
int bno;
int j;
u_long *bmp;
region = &Regions[((uintptr_t)ptr >> NREGIONS_SHIFT) & NREGIONS_MASK];
if ((slab = region->slab) == NULL)
slab = (void *)((uintptr_t)ptr & region->mask);
MASSERT(slab != NULL);
MASSERT(slab->magic == ZALLOC_SLAB_MAGIC);
#ifdef INVARIANTS
if (slab->chunk_size < sizeof(weirdary))
bcopy(weirdary, ptr, slab->chunk_size);
else
bcopy(weirdary, ptr, sizeof(weirdary));
#endif
slgd = &slglobal;
if (slgd->masked == 0 && slab->chunk_size <= NOMSLABSIZE) {
struct zoneinfo *zinfo;
zinfo = &slgd->zone[slab->zone_index];
j = zinfo->mag_index;
if (j < NMAGSHORTCUT) {
zinfo->mag_shortcut[j] = ptr;
zinfo->mag_index = j + 1;
return;
}
}
bno = ((uintptr_t)ptr - (uintptr_t)slab->chunks) / slab->chunk_size;
bmi = bno >> LONG_BITS_SHIFT;
bno &= (LONG_BITS - 1);
bmp = &slab->bitmap[bmi];
MASSERT(bmi >= 0 && bmi < slab->nmax);
MASSERT((*bmp & (1LU << bno)) == 0);
atomic_set_long(bmp, 1LU << bno);
if (slab->slgd == slgd) {
struct zoneinfo *zinfo;
atomic_add_int(&slab->navail, 1);
if (slab->free_index > bmi) {
slab->free_index = bmi;
slab->free_bit = bno;
} else if (slab->free_index == bmi &&
slab->free_bit > bno) {
slab->free_bit = bno;
}
zinfo = &slgd->zone[slab->zone_index];
if (slab->state == FULL) {
TAILQ_REMOVE(&slgd->full_zones, slab, entry);
slab->state = AVAIL;
stmp = zinfo->avail_base;
slab->next = stmp;
zinfo->avail_base = slab;
++zinfo->avail_count;
while (zinfo->avail_count > opt_cache) {
slab = zinfo->avail_base;
zinfo->avail_base = slab->next;
--zinfo->avail_count;
slabterm(slgd, slab);
}
goto done;
}
if (slab->navail == slab->nmax && slab->state == AVAIL) {
slabp = &zinfo->avail_base;
while ((stmp = *slabp) != slab)
slabp = &stmp->next;
*slabp = slab->next;
--zinfo->avail_count;
if (opt_free || opt_cache == 0) {
slabterm(slgd, slab);
} else if (slab->slab_size > BIGSLABSIZE) {
slabterm(slgd, slab);
} else if (zinfo->empty_count > opt_cache) {
stmp = zinfo->empty_base;
slab->state = EMPTY;
slab->next = stmp->next;
zinfo->empty_base = slab;
slabterm(slgd, stmp);
} else {
++zinfo->empty_count;
slab->state = EMPTY;
slab->next = zinfo->empty_base;
zinfo->empty_base = slab;
}
}
} else if (slab->slgd == NULL && slab->navail + 1 == slab->nmax) {
slglobaldata_t sldepot;
sldepot = &slglobaldepot;
if (__isthreaded)
_SPINLOCK(&sldepot->lock);
if (slab->slgd == NULL && slab->navail + 1 == slab->nmax) {
struct zoneinfo *zinfo;
MASSERT(slab->state == AVAIL);
atomic_add_int(&slab->navail, 1);
zinfo = &sldepot->zone[slab->zone_index];
slabp = &zinfo->avail_base;
while (slab != *slabp)
slabp = &(*slabp)->next;
*slabp = slab->next;
--zinfo->avail_count;
slab->state = EMPTY;
slab->next = zinfo->empty_base;
zinfo->empty_base = slab;
++zinfo->empty_count;
while (zinfo->empty_count > opt_cache) {
slab = zinfo->empty_base;
zinfo->empty_base = slab->next;
--zinfo->empty_count;
slab->state = UNKNOWN;
if (__isthreaded)
_SPINUNLOCK(&sldepot->lock);
slabfree(slab);
if (__isthreaded)
_SPINLOCK(&sldepot->lock);
}
} else {
atomic_add_int(&slab->navail, 1);
}
if (__isthreaded)
_SPINUNLOCK(&sldepot->lock);
} else {
atomic_add_int(&slab->navail, 1);
}
done:
;
}
static slab_t
slaballoc(int zi, size_t chunking, size_t chunk_size)
{
slglobaldata_t slgd;
slglobaldata_t sldepot;
struct zoneinfo *zinfo;
region_t region;
void *save;
slab_t slab;
size_t slab_desire;
size_t slab_size;
size_t region_mask;
uintptr_t chunk_offset;
ssize_t maxchunks;
ssize_t tmpchunks;
int ispower2;
int power;
int ri;
int rx;
int nswath;
int j;
slgd = &slglobal;
sldepot = &slglobaldepot;
zinfo = &sldepot->zone[zi];
if (zinfo->avail_base) {
if (__isthreaded)
_SPINLOCK(&sldepot->lock);
slab = zinfo->avail_base;
if (slab) {
MASSERT(slab->slgd == NULL);
slab->slgd = slgd;
zinfo->avail_base = slab->next;
--zinfo->avail_count;
if (__isthreaded)
_SPINUNLOCK(&sldepot->lock);
return slab;
}
if (__isthreaded)
_SPINUNLOCK(&sldepot->lock);
}
slab = NULL;
if ((chunk_size ^ (chunk_size - 1)) == (chunk_size << 1) - 1) {
ispower2 = 1;
chunk_offset = roundup2(sizeof(*slab), chunk_size);
} else {
ispower2 = 0;
chunk_offset = sizeof(*slab) + chunking - 1;
chunk_offset -= chunk_offset % chunking;
}
maxchunks = MaxChunks[zi];
if (maxchunks == 0) {
maxchunks = (ssize_t)(NREGIONS_SIZE - chunk_offset) /
(ssize_t)chunk_size;
if (maxchunks <= 0)
maxchunks = 1;
if (maxchunks > MAXCHUNKS / 2)
maxchunks = MAXCHUNKS / 2;
if (chunk_offset + chunk_size * maxchunks > BIGSLABSIZE) {
tmpchunks = (ssize_t)(BIGSLABSIZE - chunk_offset) /
(ssize_t)chunk_size;
if (tmpchunks <= 0)
tmpchunks = 1;
if (maxchunks > tmpchunks)
maxchunks = tmpchunks;
}
if (chunk_offset + chunk_size * maxchunks > NOMSLABSIZE) {
tmpchunks = (ssize_t)(NOMSLABSIZE - chunk_offset) /
(ssize_t)chunk_size;
if (tmpchunks < 1)
tmpchunks = 1;
if (maxchunks > tmpchunks)
maxchunks = tmpchunks;
}
if (chunk_offset + chunk_size * maxchunks > LITSLABSIZE) {
tmpchunks = (ssize_t)(LITSLABSIZE - chunk_offset) /
(ssize_t)chunk_size;
if (tmpchunks < 32)
tmpchunks = 32;
if (maxchunks > tmpchunks)
maxchunks = tmpchunks;
}
MaxChunks[zi] = maxchunks;
}
MASSERT(maxchunks > 0 && maxchunks <= MAXCHUNKS);
slab_desire = chunk_offset + chunk_size * maxchunks;
slab_size = 8 * MAXCHUNKS;
power = 3 + MAXCHUNKS_BITS;
while (slab_size < slab_desire) {
slab_size <<= 1;
++power;
}
if ((power & 1) && slab_size > 32768) {
maxchunks = (slab_size - chunk_offset) / chunk_size;
if (maxchunks >= MAXCHUNKS / 8) {
slab_size >>= 1;
--power;
}
}
if ((power & 2) && slab_size > 32768 * 4) {
maxchunks = (slab_size - chunk_offset) / chunk_size;
if (maxchunks >= MAXCHUNKS / 4) {
slab_size >>= 2;
power -= 2;
}
}
nswath = slab_size / NREGIONS_SIZE;
if (nswath > NREGIONS)
return (NULL);
region_mask = ~(slab_size - 1);
ri = slgd->zone[zi].best_region;
if (Regions[ri].mask == region_mask) {
if ((slab = _vmem_alloc(ri, slab_size)) != NULL)
goto found;
}
while (slab_size <= NREGIONS_SIZE) {
rx = -1;
for (ri = 0; ri < NREGIONS; ++ri) {
if (rx < 0 && Regions[ri].mask == 0)
rx = ri;
if (Regions[ri].mask == region_mask) {
slab = _vmem_alloc(ri, slab_size);
if (slab) {
slgd->zone[zi].best_region = ri;
goto found;
}
}
}
if (rx < 0)
return(NULL);
atomic_cmpset_ptr((void **)&Regions[rx].mask,
NULL,
(void *)region_mask);
}
for (;;) {
rx = -1;
for (ri = 0; ri < NREGIONS; ri += nswath) {
if (Regions[ri].mask == region_mask) {
slab = _vmem_alloc(ri, slab_size);
if (slab) {
slgd->zone[zi].best_region = ri;
goto found;
}
}
if (rx < 0) {
for (j = nswath - 1; j >= 0; --j) {
if (Regions[ri+j].mask != 0)
break;
}
if (j < 0)
rx = ri;
}
}
if (rx < 0)
return(NULL);
for (j = nswath - 1; j >= 0; --j) {
if (!atomic_cmpset_ptr((void **)&Regions[rx+j].mask,
NULL, (void *)region_mask)) {
while (++j < nswath)
Regions[rx+j].mask = 0;
break;
}
}
}
found:
region = &Regions[ri];
MASSERT(region->slab == NULL);
if (slab_size >= NREGIONS_SIZE) {
save = slab;
slab = memalloc(sizeof(*slab), 0);
bzero(slab, sizeof(*slab));
slab->chunks = save;
for (j = 0; j < nswath; ++j)
region[j].slab = slab;
chunk_offset = 0;
} else {
bzero(slab, sizeof(*slab));
slab->chunks = (char *)slab + chunk_offset;
}
maxchunks = (slab_size - chunk_offset) / chunk_size;
if (maxchunks > MAXCHUNKS)
maxchunks = MAXCHUNKS;
slab->magic = ZALLOC_SLAB_MAGIC;
slab->navail = maxchunks;
slab->nmax = maxchunks;
slab->slab_size = slab_size;
slab->chunk_size = chunk_size;
slab->zone_index = zi;
slab->slgd = slgd;
slab->state = UNKNOWN;
slab->region = region;
for (ri = 0; ri < maxchunks; ri += LONG_BITS) {
if (ri + LONG_BITS <= maxchunks)
slab->bitmap[ri >> LONG_BITS_SHIFT] = ULONG_MAX;
else
slab->bitmap[ri >> LONG_BITS_SHIFT] =
(1LU << (maxchunks - ri)) - 1;
}
return (slab);
}
static void
slabfree(slab_t slab)
{
int nswath;
int j;
if (slab->region->slab == slab) {
nswath = slab->slab_size / NREGIONS_SIZE;
for (j = 0; j < nswath; ++j)
slab->region[j].slab = NULL;
slab->magic = 0;
_vmem_free(slab->chunks, slab->slab_size);
memfree(slab, 0);
} else {
slab->magic = 0;
_vmem_free(slab, slab->slab_size);
}
}
static void
slabterm(slglobaldata_t slgd, slab_t slab)
{
slglobaldata_t sldepot;
struct zoneinfo *zinfo;
int zi = slab->zone_index;
slab->slgd = NULL;
--slgd->nslabs;
sldepot = &slglobaldepot;
zinfo = &sldepot->zone[zi];
if (__isthreaded)
_SPINLOCK(&sldepot->lock);
if (slab->navail == slab->nmax) {
slab->state = EMPTY;
slab->next = zinfo->empty_base;
zinfo->empty_base = slab;
++zinfo->empty_count;
} else {
slab->state = AVAIL;
slab->next = zinfo->avail_base;
zinfo->avail_base = slab;
++zinfo->avail_count;
}
while (zinfo->empty_count > opt_cache) {
slab = zinfo->empty_base;
zinfo->empty_base = slab->next;
--zinfo->empty_count;
slab->state = UNKNOWN;
if (__isthreaded)
_SPINUNLOCK(&sldepot->lock);
slabfree(slab);
if (__isthreaded)
_SPINLOCK(&sldepot->lock);
}
if (__isthreaded)
_SPINUNLOCK(&sldepot->lock);
}
static void *
_vmem_alloc(int ri, size_t slab_size)
{
char *baddr = (void *)((uintptr_t)ri << NREGIONS_SHIFT);
char *eaddr;
char *addr;
char *save;
uintptr_t excess;
if (slab_size < NREGIONS_SIZE)
eaddr = baddr + NREGIONS_SIZE;
else
eaddr = baddr + slab_size;
addr = mmap(baddr, slab_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_SIZEALIGN, -1, 0);
if (addr == MAP_FAILED) {
return (NULL);
}
if (addr < baddr || addr + slab_size > eaddr) {
munmap(addr, slab_size);
return (NULL);
}
excess = (uintptr_t)addr & (slab_size - 1);
while (excess) {
excess = slab_size - excess;
save = addr;
munmap(save + excess, slab_size - excess);
addr = _vmem_alloc(ri, slab_size);
munmap(save, excess);
if (addr == NULL)
return (NULL);
if (addr < baddr || addr + slab_size > eaddr) {
munmap(addr, slab_size);
return (NULL);
}
excess = (uintptr_t)addr & (slab_size - 1);
}
return (addr);
}
static void
_vmem_free(void *ptr, size_t size)
{
munmap(ptr, size);
}
static void
_mpanic(const char *ctl, ...)
{
va_list va;
if (malloc_panic == 0) {
malloc_panic = 1;
va_start(va, ctl);
vfprintf(stderr, ctl, va);
fprintf(stderr, "\n");
fflush(stderr);
va_end(va);
}
abort();
}
__weak_reference(__aligned_alloc, aligned_alloc);
__weak_reference(__malloc, malloc);
__weak_reference(__calloc, calloc);
__weak_reference(__posix_memalign, posix_memalign);
__weak_reference(__realloc, realloc);
__weak_reference(__free, free);