#include "db_config.h"
#include "db_int.h"
typedef SH_TAILQ_HEAD(__sizeq) SIZEQ_HEAD;
typedef struct __alloc_layout {
SH_TAILQ_HEAD(__addrq) addrq;
#define DB_SIZE_Q_COUNT 11
SIZEQ_HEAD sizeq[DB_SIZE_Q_COUNT];
#ifdef HAVE_STATISTICS
u_int32_t pow2_size[DB_SIZE_Q_COUNT];
#endif
#ifdef HAVE_STATISTICS
u_int32_t success;
u_int32_t failure;
u_int32_t freed;
u_int32_t longest;
#endif
uintmax_t unused;
} ALLOC_LAYOUT;
typedef struct __alloc_element {
SH_TAILQ_ENTRY addrq;
SH_TAILQ_ENTRY sizeq;
uintmax_t len;
uintmax_t ulen;
} ALLOC_ELEMENT;
#define SHALLOC_FRAGMENT (sizeof(ALLOC_ELEMENT) + 64)
#undef SET_QUEUE_FOR_SIZE
#define SET_QUEUE_FOR_SIZE(head, q, i, len) do { \
for (i = 0; i < DB_SIZE_Q_COUNT; ++i) { \
q = &(head)->sizeq[i]; \
if ((len) <= (u_int64_t)1024 << i) \
break; \
} \
} while (0)
static void __env_size_insert __P((ALLOC_LAYOUT *, ALLOC_ELEMENT *));
void
__env_alloc_init(infop, size)
REGINFO *infop;
size_t size;
{
ALLOC_ELEMENT *elp;
ALLOC_LAYOUT *head;
ENV *env;
u_int i;
env = infop->env;
if (F_ISSET(env, ENV_PRIVATE))
return;
head = infop->head;
memset(head, 0, sizeof(*head));
SH_TAILQ_INIT(&head->addrq);
for (i = 0; i < DB_SIZE_Q_COUNT; ++i)
SH_TAILQ_INIT(&head->sizeq[i]);
COMPQUIET(head->unused, 0);
elp = (ALLOC_ELEMENT *)((u_int8_t *)head + sizeof(ALLOC_LAYOUT));
elp->len = size - sizeof(ALLOC_LAYOUT);
elp->ulen = 0;
SH_TAILQ_INSERT_HEAD(&head->addrq, elp, addrq, __alloc_element);
SH_TAILQ_INSERT_HEAD(
&head->sizeq[DB_SIZE_Q_COUNT - 1], elp, sizeq, __alloc_element);
}
#ifdef DIAGNOSTIC
#define DB_ALLOC_SIZE(len) \
(size_t)DB_ALIGN((len) + sizeof(ALLOC_ELEMENT) + 1, sizeof(uintmax_t))
#else
#define DB_ALLOC_SIZE(len) \
(size_t)DB_ALIGN((len) + sizeof(ALLOC_ELEMENT), sizeof(uintmax_t))
#endif
size_t
__env_alloc_overhead()
{
return (sizeof(ALLOC_ELEMENT));
}
size_t
__env_alloc_size(len)
size_t len;
{
return (DB_ALLOC_SIZE(len));
}
int
__env_alloc(infop, len, retp)
REGINFO *infop;
size_t len;
void *retp;
{
SIZEQ_HEAD *q;
ALLOC_ELEMENT *elp, *frag, *elp_tmp;
ALLOC_LAYOUT *head;
ENV *env;
REGION_MEM *mem;
REGINFO *envinfop;
size_t total_len;
u_int8_t *p;
u_int i;
int ret;
#ifdef HAVE_STATISTICS
u_int32_t st_search;
#endif
env = infop->env;
*(void **)retp = NULL;
#ifdef HAVE_MUTEX_SUPPORT
MUTEX_REQUIRED(env, infop->mtx_alloc);
#endif
PERFMON3(env, mpool, env_alloc, len, infop->id, infop->type);
if (F_ISSET(env, ENV_PRIVATE)) {
if (F_ISSET(infop, REGION_SHARED))
envinfop = env->reginfo;
else
envinfop = infop;
len += sizeof(uintmax_t);
if (F_ISSET(infop, REGION_TRACKED))
len += sizeof(REGION_MEM);
#ifdef DIAGNOSTIC
++len;
#endif
if (envinfop->max_alloc != 0 &&
envinfop->allocated + len > envinfop->max_alloc)
return (ENOMEM);
if ((ret = __os_malloc(env, len, &p)) != 0)
return (ret);
infop->allocated += len;
if (infop != envinfop)
envinfop->allocated += len;
*(uintmax_t *)p = len;
#ifdef DIAGNOSTIC
p[len - 1] = GUARD_BYTE;
#endif
if (F_ISSET(infop, REGION_TRACKED)) {
mem = (REGION_MEM *)(p + sizeof(uintmax_t));
mem->next = infop->mem;
infop->mem = mem;
p += sizeof(mem);
}
*(void **)retp = p + sizeof(uintmax_t);
return (0);
}
head = infop->head;
total_len = DB_ALLOC_SIZE(len);
COMPQUIET(q, NULL);
#ifdef HAVE_MMAP_EXTEND
retry:
#endif
SET_QUEUE_FOR_SIZE(head, q, i, total_len);
#ifdef HAVE_STATISTICS
if (i >= DB_SIZE_Q_COUNT)
i = DB_SIZE_Q_COUNT - 1;
++head->pow2_size[i];
#endif
STAT(st_search = 0);
for (elp = NULL;; ++q) {
SH_TAILQ_FOREACH(elp_tmp, q, sizeq, __alloc_element) {
STAT(++st_search);
if (elp_tmp->len < total_len)
break;
elp = elp_tmp;
if (elp_tmp->len - total_len <= SHALLOC_FRAGMENT)
break;
}
if (elp != NULL || ++i >= DB_SIZE_Q_COUNT)
break;
}
#ifdef HAVE_STATISTICS
if (head->longest < st_search) {
head->longest = st_search;
STAT_PERFMON3(env,
mpool, longest_search, len, infop->id, st_search);
}
#endif
if (elp == NULL) {
ret = ENOMEM;
#ifdef HAVE_MMAP_EXTEND
if (infop->rp->size < infop->rp->max &&
(ret = __env_region_extend(env, infop)) == 0)
goto retry;
#endif
STAT_INC_VERB(env, mpool, fail, head->failure, len, infop->id);
return (ret);
}
STAT_INC_VERB(env, mpool, alloc, head->success, len, infop->id);
SH_TAILQ_REMOVE(q, elp, sizeq, __alloc_element);
if (elp->len - total_len > SHALLOC_FRAGMENT) {
frag = (ALLOC_ELEMENT *)((u_int8_t *)elp + total_len);
frag->len = elp->len - total_len;
frag->ulen = 0;
elp->len = total_len;
SH_TAILQ_INSERT_AFTER(
&head->addrq, elp, frag, addrq, __alloc_element);
__env_size_insert(head, frag);
}
p = (u_int8_t *)elp + sizeof(ALLOC_ELEMENT);
elp->ulen = len;
#ifdef DIAGNOSTIC
p[len] = GUARD_BYTE;
#endif
*(void **)retp = p;
return (0);
}
void
__env_alloc_free(infop, ptr)
REGINFO *infop;
void *ptr;
{
ALLOC_ELEMENT *elp, *elp_tmp;
ALLOC_LAYOUT *head;
ENV *env;
SIZEQ_HEAD *q;
size_t len;
u_int8_t i, *p;
env = infop->env;
if (F_ISSET(env, ENV_PRIVATE)) {
p = (u_int8_t *)((uintmax_t *)ptr - 1);
len = (size_t)*(uintmax_t *)p;
infop->allocated -= len;
if (F_ISSET(infop, REGION_SHARED))
env->reginfo->allocated -= len;
#ifdef DIAGNOSTIC
DB_ASSERT(env, p[len - 1] == GUARD_BYTE);
memset(p, CLEAR_BYTE, len);
#endif
__os_free(env, p);
return;
}
#ifdef HAVE_MUTEX_SUPPORT
MUTEX_REQUIRED(env, infop->mtx_alloc);
#endif
head = infop->head;
p = ptr;
elp = (ALLOC_ELEMENT *)(p - sizeof(ALLOC_ELEMENT));
STAT_INC_VERB(env, mpool, free, head->freed, elp->ulen, infop->id);
#ifdef DIAGNOSTIC
DB_ASSERT(env, p[elp->ulen] == GUARD_BYTE);
memset(p, CLEAR_BYTE, (size_t)elp->len - sizeof(ALLOC_ELEMENT));
#endif
elp->ulen = 0;
if ((elp_tmp =
SH_TAILQ_PREV(&head->addrq, elp, addrq, __alloc_element)) != NULL &&
elp_tmp->ulen == 0 &&
(u_int8_t *)elp_tmp + elp_tmp->len == (u_int8_t *)elp) {
SH_TAILQ_REMOVE(&head->addrq, elp, addrq, __alloc_element);
SET_QUEUE_FOR_SIZE(head, q, i, elp_tmp->len);
SH_TAILQ_REMOVE(q, elp_tmp, sizeq, __alloc_element);
elp_tmp->len += elp->len;
elp = elp_tmp;
}
if ((elp_tmp = SH_TAILQ_NEXT(elp, addrq, __alloc_element)) != NULL &&
elp_tmp->ulen == 0 &&
(u_int8_t *)elp + elp->len == (u_int8_t *)elp_tmp) {
SH_TAILQ_REMOVE(&head->addrq, elp_tmp, addrq, __alloc_element);
SET_QUEUE_FOR_SIZE(head, q, i, elp_tmp->len);
SH_TAILQ_REMOVE(q, elp_tmp, sizeq, __alloc_element);
elp->len += elp_tmp->len;
}
__env_size_insert(head, elp);
}
int
__env_alloc_extend(infop, ptr, lenp)
REGINFO *infop;
void *ptr;
size_t *lenp;
{
ALLOC_ELEMENT *elp, *elp_tmp;
ALLOC_LAYOUT *head;
ENV *env;
SIZEQ_HEAD *q;
size_t len, tlen;
u_int8_t i, *p;
int ret;
env = infop->env;
DB_ASSERT(env, !F_ISSET(env, ENV_PRIVATE));
#ifdef HAVE_MUTEX_SUPPORT
MUTEX_REQUIRED(env, infop->mtx_alloc);
#endif
head = infop->head;
p = ptr;
len = *lenp;
elp = (ALLOC_ELEMENT *)(p - sizeof(ALLOC_ELEMENT));
#ifdef DIAGNOSTIC
DB_ASSERT(env, p[elp->ulen] == GUARD_BYTE);
#endif
again: if ((elp_tmp = SH_TAILQ_NEXT(elp, addrq, __alloc_element)) != NULL &&
elp_tmp->ulen == 0 &&
(u_int8_t *)elp + elp->len == (u_int8_t *)elp_tmp) {
SH_TAILQ_REMOVE(&head->addrq, elp_tmp, addrq, __alloc_element);
SET_QUEUE_FOR_SIZE(head, q, i, elp_tmp->len);
SH_TAILQ_REMOVE(q, elp_tmp, sizeq, __alloc_element);
if (elp_tmp->len < len + SHALLOC_FRAGMENT) {
elp->len += elp_tmp->len;
if (elp_tmp->len < len)
len -= (size_t)elp_tmp->len;
else
len = 0;
} else {
tlen = (size_t)elp_tmp->len;
elp_tmp = (ALLOC_ELEMENT *) ((u_int8_t *)elp_tmp + len);
elp_tmp->len = tlen - len;
elp_tmp->ulen = 0;
elp->len += len;
len = 0;
SH_TAILQ_INSERT_AFTER(
&head->addrq, elp, elp_tmp, addrq, __alloc_element);
__env_size_insert(head, elp_tmp);
}
} else if (elp_tmp != NULL) {
__db_errx(env, DB_STR("1583", "block not at end of region"));
return (__env_panic(env, EINVAL));
}
if (len == 0)
goto done;
if ((ret = __env_region_extend(env, infop)) != 0) {
if (ret != ENOMEM)
return (ret);
goto done;
}
goto again;
done: elp->ulen = elp->len - sizeof(ALLOC_ELEMENT);
#ifdef DIAGNOSTIC
elp->ulen -= sizeof(uintmax_t);
p[elp->ulen] = GUARD_BYTE;
#endif
*lenp -= len;
infop->allocated += *lenp;
if (F_ISSET(infop, REGION_SHARED))
env->reginfo->allocated += *lenp;
return (0);
}
static void
__env_size_insert(head, elp)
ALLOC_LAYOUT *head;
ALLOC_ELEMENT *elp;
{
SIZEQ_HEAD *q;
ALLOC_ELEMENT *elp_tmp;
u_int i;
SET_QUEUE_FOR_SIZE(head, q, i, elp->len);
SH_TAILQ_FOREACH(elp_tmp, q, sizeq, __alloc_element)
if (elp->len >= elp_tmp->len)
break;
if (elp_tmp == NULL)
SH_TAILQ_INSERT_TAIL(q, elp, sizeq);
else
SH_TAILQ_INSERT_BEFORE(q, elp_tmp, elp, sizeq, __alloc_element);
}
int
__env_region_extend(env, infop)
ENV *env;
REGINFO *infop;
{
ALLOC_ELEMENT *elp;
REGION *rp;
int ret;
DB_ASSERT(env, !F_ISSET(env, ENV_PRIVATE));
ret = 0;
rp = infop->rp;
if (rp->size >= rp->max)
return (ENOMEM);
elp = (ALLOC_ELEMENT *)((u_int8_t *)infop->addr + rp->size);
if (rp->size + rp->alloc > rp->max)
rp->alloc = rp->max - rp->size;
rp->size += rp->alloc;
rp->size = (size_t)ALIGNP_INC(rp->size, sizeof(size_t));
if (rp->max - rp->size <= SHALLOC_FRAGMENT)
rp->size = rp->max;
if (infop->fhp &&
(ret = __db_file_extend(env, infop->fhp, rp->size)) != 0)
return (ret);
elp->len = rp->alloc;
elp->ulen = 0;
#ifdef DIAGNOSTIC
*(u_int8_t *)(elp+1) = GUARD_BYTE;
#endif
SH_TAILQ_INSERT_TAIL(&((ALLOC_LAYOUT *)infop->head)->addrq, elp, addrq);
__env_alloc_free(infop, elp + 1);
if (rp->alloc < MEGABYTE)
rp->alloc += rp->size;
if (rp->alloc > MEGABYTE)
rp->alloc = MEGABYTE;
return (ret);
}
uintmax_t
__env_elem_size(env, p)
ENV *env;
void *p;
{
ALLOC_ELEMENT *elp;
uintmax_t size;
if (F_ISSET(env, ENV_PRIVATE)) {
size = *((uintmax_t *)p - 1);
size -= sizeof(uintmax_t);
} else {
elp = (ALLOC_ELEMENT *)((u_int8_t *)p - sizeof(ALLOC_ELEMENT));
size = elp->ulen;
}
return (size);
}
void *
__env_get_chunk(infop, nextp, sizep)
REGINFO *infop;
void **nextp;
uintmax_t *sizep;
{
REGION_MEM *mem;
if (infop->mem == NULL)
return (NULL);
if (*nextp == NULL)
*nextp = infop->mem;
mem = *(REGION_MEM **)nextp;
*nextp = mem->next;
*sizep = __env_elem_size(infop->env, mem);
*sizep -= sizeof(*mem);
return ((void *)(mem + 1));
}
#ifdef HAVE_STATISTICS
void
__env_alloc_print(infop, flags)
REGINFO *infop;
u_int32_t flags;
{
ALLOC_ELEMENT *elp;
ALLOC_LAYOUT *head;
ENV *env;
u_int i;
env = infop->env;
head = infop->head;
if (F_ISSET(env, ENV_PRIVATE))
return;
__db_msg(env,
"Region allocations: %lu allocations, %lu failures, %lu frees, %lu longest",
(u_long)head->success, (u_long)head->failure, (u_long)head->freed,
(u_long)head->longest);
if (!LF_ISSET(DB_STAT_ALL))
return;
__db_msg(env, "%s", "Allocations by power-of-two sizes:");
for (i = 0; i < DB_SIZE_Q_COUNT; ++i)
__db_msg(env, "%3dKB\t%lu",
(1024 << i) / 1024, (u_long)head->pow2_size[i]);
if (!LF_ISSET(DB_STAT_ALLOC))
return;
__db_msg(env,
"Allocation list by address, offset: {chunk length, user length}");
SH_TAILQ_FOREACH(elp, &head->addrq, addrq, __alloc_element)
__db_msg(env, "\t%#lx, %lu {%lu, %lu}",
P_TO_ULONG(elp), (u_long)R_OFFSET(infop, elp),
(u_long)elp->len, (u_long)elp->ulen);
__db_msg(env, "Allocation free list by size: KB {chunk length}");
for (i = 0; i < DB_SIZE_Q_COUNT; ++i) {
__db_msg(env, "%3dKB", (1024 << i) / 1024);
SH_TAILQ_FOREACH(elp, &head->sizeq[i], sizeq, __alloc_element)
__db_msg(env,
"\t%#lx {%lu}", P_TO_ULONG(elp), (u_long)elp->len);
}
}
#endif