From eb7ef395d27b1104f684e21836f200c052736d07 Mon Sep 17 00:00:00 2001 From: Michael Graff Date: Tue, 8 Jun 1999 02:38:30 +0000 Subject: [PATCH] implement memory pools --- lib/isc/include/isc/mem.h | 25 ++ lib/isc/include/isc/types.h | 1 + lib/isc/mem.c | 455 ++++++++++++++++++++++++++++++++++-- 3 files changed, 456 insertions(+), 25 deletions(-) diff --git a/lib/isc/include/isc/mem.h b/lib/isc/include/isc/mem.h index 5db4beda5e..baef6eaed6 100644 --- a/lib/isc/include/isc/mem.h +++ b/lib/isc/include/isc/mem.h @@ -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 */ diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index 12c17275d4..d12f39a421 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.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; diff --git a/lib/isc/mem.c b/lib/isc/mem.c index dea8c9faeb..0af964ed23 100644 --- a/lib/isc/mem.c +++ b/lib/isc/mem.c @@ -22,6 +22,8 @@ #include #include +#include + #include #include #include @@ -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 (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); + 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); +}