/* * Copyright (c) 2013-2018 Joris Vink * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "kore.h" #define POOL_ELEMENT_BUSY 0 #define POOL_ELEMENT_FREE 1 #if defined(KORE_USE_TASKS) static void pool_lock(struct kore_pool *); static void pool_unlock(struct kore_pool *); #endif static void pool_region_create(struct kore_pool *, size_t); static void pool_region_destroy(struct kore_pool *); void kore_pool_init(struct kore_pool *pool, const char *name, size_t len, size_t elm) { kore_debug("kore_pool_init(%p, %s, %zu, %zu)", pool, name, len, elm); if ((pool->name = strdup(name)) == NULL) fatal("kore_pool_init: strdup %s", errno_s); pool->lock = 0; pool->elms = 0; pool->inuse = 0; pool->elen = len; pool->growth = elm * 0.25f; pool->slen = pool->elen + sizeof(struct kore_pool_entry); LIST_INIT(&(pool->regions)); LIST_INIT(&(pool->freelist)); pool_region_create(pool, elm); } void kore_pool_cleanup(struct kore_pool *pool) { pool->lock = 0; pool->elms = 0; pool->inuse = 0; pool->elen = 0; pool->slen = 0; free(pool->name); pool->name = NULL; pool_region_destroy(pool); } void * kore_pool_get(struct kore_pool *pool) { u_int8_t *ptr; struct kore_pool_entry *entry; #if defined(KORE_USE_TASKS) pool_lock(pool); #endif if (LIST_EMPTY(&(pool->freelist))) pool_region_create(pool, pool->growth); entry = LIST_FIRST(&(pool->freelist)); if (entry->state != POOL_ELEMENT_FREE) fatal("%s: element %p was not free", pool->name, entry); LIST_REMOVE(entry, list); entry->state = POOL_ELEMENT_BUSY; ptr = (u_int8_t *)entry + sizeof(struct kore_pool_entry); pool->inuse++; #if defined(KORE_USE_TASKS) pool_unlock(pool); #endif return (ptr); } void kore_pool_put(struct kore_pool *pool, void *ptr) { struct kore_pool_entry *entry; #if defined(KORE_USE_TASKS) pool_lock(pool); #endif entry = (struct kore_pool_entry *) ((u_int8_t *)ptr - sizeof(struct kore_pool_entry)); if (entry->state != POOL_ELEMENT_BUSY) fatal("%s: element %p was not busy", pool->name, ptr); entry->state = POOL_ELEMENT_FREE; LIST_INSERT_HEAD(&(pool->freelist), entry, list); pool->inuse--; #if defined(KORE_USE_TASKS) pool_unlock(pool); #endif } static void pool_region_create(struct kore_pool *pool, size_t elms) { size_t i; u_int8_t *p; struct kore_pool_region *reg; struct kore_pool_entry *entry; kore_debug("pool_region_create(%p, %zu)", pool, elms); if ((reg = calloc(1, sizeof(struct kore_pool_region))) == NULL) fatal("pool_region_create: calloc: %s", errno_s); LIST_INSERT_HEAD(&(pool->regions), reg, list); if (SIZE_MAX / elms < pool->slen) fatal("pool_region_create: overflow"); reg->length = elms * pool->slen; reg->start = mmap(NULL, reg->length, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (reg->start == MAP_FAILED) fatal("mmap: %s", errno_s); p = (u_int8_t *)reg->start; for (i = 0; i < elms; i++) { entry = (struct kore_pool_entry *)p; entry->region = reg; entry->state = POOL_ELEMENT_FREE; LIST_INSERT_HEAD(&(pool->freelist), entry, list); p = p + pool->slen; } pool->elms += elms; } static void pool_region_destroy(struct kore_pool *pool) { struct kore_pool_region *reg; kore_debug("pool_region_destroy(%p)", pool); /* Take care iterating when modifying list contents */ while (!LIST_EMPTY(&pool->regions)) { reg = LIST_FIRST(&pool->regions); LIST_REMOVE(reg, list); (void)munmap(reg->start, reg->length); free(reg); } /* Freelist references into the regions memory allocations */ LIST_INIT(&pool->freelist); pool->elms = 0; } #if defined(KORE_USE_TASKS) static void pool_lock(struct kore_pool *pool) { for (;;) { if (__sync_bool_compare_and_swap(&pool->lock, 0, 1)) break; } } static void pool_unlock(struct kore_pool *pool) { if (!__sync_bool_compare_and_swap(&pool->lock, 1, 0)) fatal("pool_unlock: failed to release %s", pool->name); } #endif