Commit eb7ef395 authored by Michael Graff's avatar Michael Graff
Browse files

implement memory pools

parent 47d1506e
......@@ -31,9 +31,14 @@ ISC_LANG_BEGINDECLS
#ifdef ISC_MEM_DEBUG
#define isc_mem_get(c, s) __isc_mem_getdebug(c, s, __FILE__, __LINE__)
#define isc_mem_put(c, p, s) __isc_mem_putdebug(c, p, s, __FILE__, __LINE__)
#define isc_mempool_get(c) __isc_mempool_getdebug(c, __FILE__, __LINE__)
#define isc_mempool_put(c, p) __isc_mempool_putdebug(c, p, s, \
__FILE__, __LINE__)
#else
#define isc_mem_get __isc_mem_get
#define isc_mem_put __isc_mem_put
#define isc_mempool_get __isc_mempool_get
#define isc_mempool_put __isc_mempool_put
#endif /* ISC_MEM_DEBUG */
isc_result_t isc_mem_create(size_t, size_t, isc_mem_t **);
......@@ -82,6 +87,26 @@ void memstats(FILE *);
#endif /* ISC_MEMCLUSTER_LEGACY */
/*
* Memory pools
*/
isc_result_t isc_mempool_create(isc_mem_t *, size_t, isc_mempool_t **);
void isc_mempool_destroy(isc_mempool_t **);
void * __isc_mempool_get(isc_mempool_t *);
void __isc_mempool_put(isc_mempool_t *, void *);
void * __isc_mempool_getdebug(isc_mempool_t *, const char *, int);
void __isc_mempool_putdebug(isc_mempool_t *, void *,
const char *, int);
unsigned int isc_mempool_getfreemax(isc_mempool_t *);
void isc_mempool_setfreemax(isc_mempool_t *, unsigned int);
unsigned int isc_mempool_getfreecount(isc_mempool_t *);
unsigned int isc_mempool_getmaxalloc(isc_mempool_t *);
void isc_mempool_setmaxalloc(isc_mempool_t *, unsigned int);
unsigned int isc_mempool_getallocated(isc_mempool_t *);
unsigned int isc_mempool_getfillcount(isc_mempool_t *);
void isc_mempool_setfillcount(isc_mempool_t *, unsigned int);
ISC_LANG_ENDDECLS
#endif /* MEM_H */
......@@ -26,6 +26,7 @@
***/
typedef struct isc_mem isc_mem_t;
typedef struct isc_mempool isc_mempool_t;
typedef unsigned int isc_eventtype_t;
typedef struct isc_event isc_event_t;
typedef ISC_LIST(struct isc_event) isc_eventlist_t;
......
......@@ -22,6 +22,8 @@
#include <stddef.h>
#include <string.h>
#include <limits.h>
#include <isc/assertions.h>
#include <isc/error.h>
#include <isc/mem.h>
......@@ -74,11 +76,32 @@ struct isc_mem {
struct stats * stats;
size_t quota;
size_t total;
ISC_LIST(isc_mempool_t) pools;
};
#define MEMPOOL_MAGIC 0x4D454d70U /* MEMp. */
#define VALID_MEMPOOL(c) ((c) != NULL && (c)->magic == MEMPOOL_MAGIC)
struct isc_mempool {
unsigned int magic; /* magic number */
ISC_LINK(isc_mempool_t) link; /* next pool in this mem context */
isc_mem_t *mctx; /* our memory context */
element *items; /* low water item list */
size_t size; /* size of each item on this pool */
unsigned int maxalloc; /* max number of items allowed */
unsigned int allocated; /* # of items currently given out */
unsigned int freecount; /* # of items on reserved list */
unsigned int freemax; /* # of items allowed on free list */
unsigned int fillcount; /* # of items to fetch on each fill */
/* Stats only. */
unsigned int gets; /* # of requests to this pool */
};
/* Forward. */
static size_t quantize(size_t);
static inline size_t quantize(size_t);
static inline void mem_putunlocked(isc_mem_t *, void *, size_t);
static inline void * mem_getunlocked(isc_mem_t *, size_t);
/* Constants. */
......@@ -115,6 +138,9 @@ isc_mem_create(size_t init_max_size, size_t target_size,
REQUIRE(ctxp != NULL && *ctxp == NULL);
ctx = malloc(sizeof *ctx);
if (ctx == NULL)
return (ISC_R_NOMEMORY);
if (init_max_size == 0)
ctx->max_size = DEF_MAX_SIZE;
else
......@@ -154,6 +180,8 @@ isc_mem_create(size_t init_max_size, size_t target_size,
ctx->quota = 0;
ctx->total = 0;
ctx->magic = MEM_MAGIC;
ISC_LIST_INIT(ctx->pools);
*ctxp = ctx;
return (ISC_R_SUCCESS);
}
......@@ -169,6 +197,8 @@ isc_mem_destroy(isc_mem_t **ctxp) {
ctx->magic = 0;
INSIST(ISC_LIST_EMPTY(ctx->pools));
for (i = 0; i <= ctx->max_size; i++)
INSIST(ctx->stats[i].gets == 0);
......@@ -247,13 +277,25 @@ more_basic_blocks(isc_mem_t *ctx) {
}
void *
__isc_mem_get(isc_mem_t *ctx, size_t size) {
size_t new_size = quantize(size);
__isc_mem_get(isc_mem_t *ctx, size_t size)
{
void *ret;
REQUIRE(size > 0);
REQUIRE(VALID_CONTEXT(ctx));
LOCK(&ctx->lock);
ret = mem_getunlocked(ctx, size);
UNLOCK(&ctx->lock);
return (ret);
}
static inline void *
mem_getunlocked(isc_mem_t *ctx, size_t size)
{
size_t new_size = quantize(size);
void *ret;
if (size >= ctx->max_size || new_size >= ctx->max_size) {
/* memget() was called on something beyond our upper limit. */
......@@ -322,7 +364,6 @@ __isc_mem_get(isc_mem_t *ctx, size_t size) {
ctx->stats[new_size].freefrags--;
done:
UNLOCK(&ctx->lock);
#if ISC_MEM_FILL
if (ret != NULL)
......@@ -333,12 +374,20 @@ __isc_mem_get(isc_mem_t *ctx, size_t size) {
}
void
__isc_mem_put(isc_mem_t *ctx, void *mem, size_t size) {
size_t new_size = quantize(size);
__isc_mem_put(isc_mem_t *ctx, void *mem, size_t size)
{
REQUIRE(size > 0);
REQUIRE(VALID_CONTEXT(ctx));
LOCK(&ctx->lock);
mem_putunlocked(ctx, mem, size);
UNLOCK(&ctx->lock);
}
static inline void
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size)
{
size_t new_size = quantize(size);
#if ISC_MEM_FILL
memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
......@@ -351,7 +400,7 @@ __isc_mem_put(isc_mem_t *ctx, void *mem, size_t size) {
ctx->stats[ctx->max_size].gets--;
INSIST(size <= ctx->total);
ctx->total -= size;
goto done;
return;
}
/* The free list uses the "rounded-up" size "new_size": */
......@@ -367,9 +416,6 @@ __isc_mem_put(isc_mem_t *ctx, void *mem, size_t size) {
INSIST(ctx->stats[size].gets != 0);
ctx->stats[size].gets--;
ctx->stats[new_size].freefrags++;
done:
UNLOCK(&ctx->lock);
}
void *
......@@ -395,26 +441,44 @@ __isc_mem_putdebug(isc_mem_t *ctx, void *ptr, size_t size, const char *file,
* Print the stats[] on the stream "out" with suitable formatting.
*/
void
isc_mem_stats(isc_mem_t *ctx, FILE *out) {
isc_mem_stats(isc_mem_t *ctx, FILE *out)
{
size_t i;
const struct stats *s;
const isc_mempool_t *pool;
REQUIRE(VALID_CONTEXT(ctx));
LOCK(&ctx->lock);
if (ctx->freelists == NULL)
return;
for (i = 1; i <= ctx->max_size; i++) {
const struct stats *s = &ctx->stats[i];
if (s->totalgets == 0 && s->gets == 0)
continue;
fprintf(out, "%s%5d: %11lu gets, %11lu rem",
(i == ctx->max_size) ? ">=" : " ",
i, s->totalgets, s->gets);
if (s->blocks != 0)
fprintf(out, " (%lu bl, %lu ff)",
s->blocks, s->freefrags);
fputc('\n', out);
if (ctx->freelists != NULL) {
for (i = 1; i <= ctx->max_size; i++) {
s = &ctx->stats[i];
if (s->totalgets == 0 && s->gets == 0)
continue;
fprintf(out, "%s%5d: %11lu gets, %11lu rem",
(i == ctx->max_size) ? ">=" : " ",
i, s->totalgets, s->gets);
if (s->blocks != 0)
fprintf(out, " (%lu bl, %lu ff)",
s->blocks, s->freefrags);
fputc('\n', out);
}
}
pool = ISC_LIST_HEAD(ctx->pools);
if (pool != NULL) {
fprintf(out, "[Pool statistics]\n");
fprintf(out, "%10s %10s %10s %10s %10s %10s %10s\n",
"size", "maxalloc", "allocated", "freecount",
"freemax", "fillcount", "gets");
}
while (pool != NULL) {
fprintf(out, "%10u %10u %10u %10u %10u %10u %10u\n",
pool->size, pool->maxalloc, pool->allocated,
pool->freecount, pool->freemax, pool->fillcount,
pool->gets);
pool = ISC_LIST_NEXT(pool, link);
}
UNLOCK(&ctx->lock);
......@@ -576,3 +640,344 @@ memstats(FILE *out) {
}
#endif /* ISC_MEMCLUSTER_LEGACY */
/*
* Memory pool stuff
*/
#if 0
/*
* Free all but "n" items from the pool's free list. If n == 0, all items
* will be returned to the mctx.
*/
static void
mempool_release(isc_mempool_t *mpctx, unsigned int n)
{
isc_mem_t *mctx;
element *item;
element *next;
unsigned int count;
mctx = mpctx->mctx;
if (mpctx->freecount <= n)
return;
INSIST(mpctx->items != NULL);
item = mpctx->items;
for (count = 0 ; count < n ; count++) {
item = ((element *)item)->next;
INSIST(item != NULL);
}
/*
* All remaining items are to be freed. Lock the context once,
* free them all, and unlock the context.
*/
LOCK(&mctx->lock);
do {
next = ((element *)item)->next;
mem_putunlocked(mctx, item, mpctx->size);
INSIST(mpctx->freecount > 0);
mpctx->freecount--;
item = next;
} while (item != NULL);
UNLOCK(&mctx->lock);
}
#endif
/*
* Release all items on the free list. No locking is done, the memory
* context must be locked.
*/
static void
mempool_releaseall(isc_mempool_t *mpctx)
{
isc_mem_t *mctx;
element *item;
element *next;
mctx = mpctx->mctx;
if (mpctx->freecount == 0)
return;
INSIST(mpctx->items != NULL);
item = mpctx->items;
do {
next = ((element *)item)->next;
mem_putunlocked(mctx, item, mpctx->size);
INSIST(mpctx->freecount > 0);
mpctx->freecount--;
item = next;
} while (item != NULL);
}
isc_result_t
isc_mempool_create(isc_mem_t *mctx, size_t target_size,
isc_mempool_t **mpctxp)
{
isc_mempool_t *mpctx;
REQUIRE(VALID_CONTEXT(mctx));
REQUIRE(target_size > 0);
REQUIRE(mpctxp != NULL && *mpctxp == NULL);
/*
* Allocate space for this pool, initialize values, and if all works
* well, attach to the memory context.
*/
LOCK(&mctx->lock);
mpctx = mem_getunlocked(mctx, sizeof(isc_mempool_t));
if (mpctx == NULL) {
UNLOCK(&mctx->lock);
return (ISC_R_NOMEMORY);
}
mpctx->magic = MEMPOOL_MAGIC;
mpctx->mctx = mctx;
mpctx->size = target_size;
mpctx->maxalloc = UINT_MAX;
mpctx->allocated = 0;
mpctx->freecount = 0;
mpctx->freemax = 1;
mpctx->fillcount = 1;
mpctx->gets = 0;
mpctx->items = NULL;
*mpctxp = mpctx;
ISC_LIST_APPEND(mctx->pools, mpctx, link);
UNLOCK(&mctx->lock);
return (ISC_R_SUCCESS);
}
void
isc_mempool_destroy(isc_mempool_t **mpctxp)
{
isc_mempool_t *mpctx;
isc_mem_t *mctx;
REQUIRE(mpctxp != NULL);
mpctx = *mpctxp;
REQUIRE(VALID_MEMPOOL(mpctx));
REQUIRE(mpctx->allocated == 0);
mctx = mpctx->mctx;
LOCK(&mctx->lock);
/*
* Return any items on the free list
*/
mempool_releaseall(mpctx);
/*
* Remove our linked list entry from the memory context.
*/
ISC_LIST_UNLINK(mctx->pools, mpctx, link);
mpctx->magic = 0;
mem_putunlocked(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
UNLOCK(&mctx->lock);
*mpctxp = NULL;
}
void *
__isc_mempool_get(isc_mempool_t *mpctx)
{
element *item;
isc_mem_t *mctx;
unsigned int i;
REQUIRE(VALID_MEMPOOL(mpctx));
mctx = mpctx->mctx;
/*
* Don't let the caller go over quota
*/
if (mpctx->allocated >= mpctx->maxalloc)
return (NULL);
/*
* if we have a free list item, return the first here
*/
item = mpctx->items;
if (item != NULL) {
mpctx->items = item->next;
INSIST(mpctx->freecount > 0);
mpctx->freecount--;
mpctx->gets++;
mpctx->allocated++;
return (item);
}
/*
* We need to dip into the well. Lock the memory context here and
* fill up our free list.
*/
LOCK(&mctx->lock);
for (i = 0 ; i < mpctx->fillcount ; i++) {
item = mem_getunlocked(mctx, mpctx->size);
if (item == NULL)
break;
item->next = mpctx->items;
mpctx->items = item;
mpctx->freecount++;
}
UNLOCK(&mctx->lock);
/*
* If we didn't get any items, return NULL.
*/
item = mpctx->items;
if (item == NULL)
return (NULL);
mpctx->items = item->next;
mpctx->freecount--;
mpctx->gets++;
mpctx->allocated++;
return (item);
}
void
__isc_mempool_put(isc_mempool_t *mpctx, void *mem)
{
isc_mem_t *mctx;
element *item;
REQUIRE(VALID_MEMPOOL(mpctx));
REQUIRE(mem != NULL);
mctx = mpctx->mctx;
INSIST(mpctx->allocated > 0);
mpctx->allocated--;
/*
* If our free list is full, return this to the mctx directly.
*/
if (mpctx->freecount >= mpctx->freemax) {
__isc_mem_put(mctx, mem, mpctx->size);
return;
}
/*
* Otherwise, attach it to our free list and bump the counter.
*/
mpctx->freecount++;
item = (element *)mem;
item->next = mpctx->items;
mpctx->items = item;
}
void *
__isc_mempool_getdebug(isc_mempool_t *mpctx,
const char *file, int line)
{
void *ptr;
ptr = __isc_mempool_get(mpctx);
fprintf(stderr, "%s:%d: mempool_get(%p) -> %p\n", file, line,
mpctx, ptr);
return (ptr);
}
void
__isc_mempool_putdebug(isc_mempool_t *mpctx, void *ptr,
const char *file, int line)
{
fprintf(stderr, "%s:%d: mempool_put(%p, %p)\n", file, line,
mpctx, ptr);
__isc_mempool_put(mpctx, ptr);
}
/*
* Quotas
*/
void
isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit)
{
REQUIRE(VALID_MEMPOOL(mpctx));
mpctx->freemax = limit;
/*
* XXXMLG Should clamp the count to the maximum number of items we
* should have on our free list. For now, allow the condition where
* allocated + freecount > maxalloc, but in the future this should
* be prevented. alos, freecount > freemax should be checked for.
*/
}
unsigned int
isc_mempool_getfreemax(isc_mempool_t *mpctx)
{
REQUIRE(VALID_MEMPOOL(mpctx));
return (mpctx->freemax);
}
unsigned int
isc_mempool_getfreecount(isc_mempool_t *mpctx)
{
REQUIRE(VALID_MEMPOOL(mpctx));
return (mpctx->freecount);
}
void
isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit)
{
REQUIRE(limit > 0);
REQUIRE(VALID_MEMPOOL(mpctx));
mpctx->maxalloc = limit;
}
unsigned int
isc_mempool_getmaxalloc(isc_mempool_t *mpctx)
{
REQUIRE(VALID_MEMPOOL(mpctx));
return (mpctx->maxalloc);
}
unsigned int
isc_mempool_getallocated(isc_mempool_t *mpctx)
{
REQUIRE(VALID_MEMPOOL(mpctx));
return (mpctx->allocated);
}
void
isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit)
{
REQUIRE(limit > 0);
REQUIRE(VALID_MEMPOOL(mpctx));
mpctx->fillcount = limit;
}
unsigned int
isc_mempool_getfillcount(isc_mempool_t *mpctx)
{
REQUIRE(VALID_MEMPOOL(mpctx));
return (mpctx->fillcount);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment