/* -*- Mode: C ; c-basic-offset: 2 -*- */ /***************************************************************************** * * Non-sleeping memory allocation * * Copyright (C) 2006,2007 Nedko Arnaudov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * *****************************************************************************/ #include #include #include "memory_atomic.h" #include "list.h" #define TRUE 1 #define FALSE 0 struct rtsafe_memory_pool { size_t data_size; size_t min_preallocated; size_t max_preallocated; struct list_head used; unsigned int used_count; struct list_head unused; unsigned int unused_count; }; #define RTSAFE_GROUPS_PREALLOCATE 1024 int rtsafe_memory_pool_create( size_t data_size, size_t min_preallocated, size_t max_preallocated, rtsafe_memory_pool_handle * pool_handle_ptr) { struct rtsafe_memory_pool * pool_ptr; assert(min_preallocated <= max_preallocated); pool_ptr = malloc(sizeof(struct rtsafe_memory_pool)); if (pool_ptr == NULL) { return FALSE; } pool_ptr->data_size = data_size; pool_ptr->min_preallocated = min_preallocated; pool_ptr->max_preallocated = max_preallocated; INIT_LIST_HEAD(&pool_ptr->used); pool_ptr->used_count = 0; INIT_LIST_HEAD(&pool_ptr->unused); pool_ptr->unused_count = 0; rtsafe_memory_pool_sleepy((rtsafe_memory_pool_handle)pool_ptr); *pool_handle_ptr = pool_ptr; return TRUE; } #define pool_ptr ((struct rtsafe_memory_pool *)pool_handle) void rtsafe_memory_pool_destroy( rtsafe_memory_pool_handle pool_handle) { struct list_head * node_ptr; assert(pool_ptr->used_count == 0); /* called should deallocate all chunks prior releasing pool itself */ assert(list_empty(&pool_ptr->used)); while (pool_ptr->unused_count != 0) { assert(!list_empty(&pool_ptr->unused)); node_ptr = pool_ptr->unused.next; list_del(node_ptr); pool_ptr->unused_count--; free(node_ptr); } assert(list_empty(&pool_ptr->unused)); free(pool_ptr); } /* adjust unused list size */ void rtsafe_memory_pool_sleepy( rtsafe_memory_pool_handle pool_handle) { struct list_head * node_ptr; while (pool_ptr->unused_count < pool_ptr->min_preallocated) { node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size); if (node_ptr == NULL) { return; } list_add_tail(node_ptr, &pool_ptr->unused); pool_ptr->unused_count++; } while (pool_ptr->unused_count > pool_ptr->max_preallocated) { assert(!list_empty(&pool_ptr->unused)); node_ptr = pool_ptr->unused.next; list_del(node_ptr); pool_ptr->unused_count--; free(node_ptr); } } /* find entry in unused list, fail if it is empty */ void * rtsafe_memory_pool_allocate( rtsafe_memory_pool_handle pool_handle) { struct list_head * node_ptr; if (list_empty(&pool_ptr->unused)) { return NULL; } node_ptr = pool_ptr->unused.next; list_del(node_ptr); pool_ptr->unused_count--; pool_ptr->used_count++; return (node_ptr + 1); } /* move from used to unused list */ void rtsafe_memory_pool_deallocate( rtsafe_memory_pool_handle pool_handle, void * data) { list_add_tail((struct list_head *)data - 1, &pool_ptr->unused); pool_ptr->used_count--; pool_ptr->unused_count++; } void * rtsafe_memory_pool_allocate_sleepy( rtsafe_memory_pool_handle pool_handle) { void * data; do { rtsafe_memory_pool_sleepy(pool_handle); data = rtsafe_memory_pool_allocate(pool_handle); } while (data == NULL); return data; } /* max alloc is DATA_MIN * (2 ^ POOLS_COUNT) - DATA_SUB */ #define DATA_MIN 1024 #define DATA_SUB 100 /* alloc slightly smaller chunks in hope to not allocating additional page for control data */ struct rtsafe_memory_pool_generic { size_t size; rtsafe_memory_pool_handle pool; }; struct rtsafe_memory { struct rtsafe_memory_pool_generic * pools; size_t pools_count; }; int rtsafe_memory_init( size_t max_size, size_t prealloc_min, size_t prealloc_max, rtsafe_memory_handle * handle_ptr) { size_t i; size_t size; struct rtsafe_memory * memory_ptr; //LOG_DEBUG("rtsafe_memory_init() called."); memory_ptr = malloc(sizeof(struct rtsafe_memory)); if (memory_ptr == NULL) { goto fail; } size = DATA_MIN; memory_ptr->pools_count = 1; while ((size << memory_ptr->pools_count) < max_size + DATA_SUB) { memory_ptr->pools_count++; if (memory_ptr->pools_count > sizeof(size_t) * 8) { assert(0); /* chances that caller really need such huge size are close to zero */ goto fail_free; } } memory_ptr->pools = malloc(memory_ptr->pools_count * sizeof(struct rtsafe_memory_pool_generic)); if (memory_ptr->pools == NULL) { goto fail_free; } size = DATA_MIN; for (i = 0 ; i < memory_ptr->pools_count ; i++) { memory_ptr->pools[i].size = size - DATA_SUB; if (!rtsafe_memory_pool_create( memory_ptr->pools[i].size, prealloc_min, prealloc_max, &memory_ptr->pools[i].pool)) { while (i > 0) { i--; rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool); } goto fail_free_pools; } size = size << 1; } *handle_ptr = (rtsafe_memory_handle)memory_ptr; return TRUE; fail_free_pools: free(memory_ptr->pools); fail_free: free(memory_ptr); fail: return FALSE; } #define memory_ptr ((struct rtsafe_memory *)handle_ptr) void rtsafe_memory_uninit( rtsafe_memory_handle handle_ptr) { unsigned int i; //LOG_DEBUG("rtsafe_memory_uninit() called."); for (i = 0 ; i < memory_ptr->pools_count ; i++) { //LOG_DEBUG("Destroying pool for size %u", (unsigned int)memory_ptr->pools[i].size); rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool); } free(memory_ptr->pools); free(memory_ptr); } void * rtsafe_memory_allocate( rtsafe_memory_handle handle_ptr, size_t size) { rtsafe_memory_pool_handle * data_ptr; size_t i; //LOG_DEBUG("rtsafe_memory_allocate() called."); /* pool handle is stored just before user data to ease deallocation */ size += sizeof(rtsafe_memory_pool_handle); for (i = 0 ; i < memory_ptr->pools_count ; i++) { if (size <= memory_ptr->pools[i].size) { //LOG_DEBUG("Using chunk with size %u.", (unsigned int)memory_ptr->pools[i].size); data_ptr = rtsafe_memory_pool_allocate(memory_ptr->pools[i].pool); if (data_ptr == NULL) { //LOG_DEBUG("rtsafe_memory_pool_allocate() failed."); return FALSE; } *data_ptr = memory_ptr->pools[i].pool; //LOG_DEBUG("rtsafe_memory_allocate() returning %p", (data_ptr + 1)); return (data_ptr + 1); } } /* data size too big, increase POOLS_COUNT */ //LOG_WARNING("Data size is too big"); return FALSE; } void rtsafe_memory_sleepy( rtsafe_memory_handle handle_ptr) { unsigned int i; for (i = 0 ; i < memory_ptr->pools_count ; i++) { rtsafe_memory_pool_sleepy(memory_ptr->pools[i].pool); } } void rtsafe_memory_deallocate( void * data) { //LOG_DEBUG("rtsafe_memory_deallocate(%p) called.", data); rtsafe_memory_pool_deallocate( *((rtsafe_memory_pool_handle *)data -1), (rtsafe_memory_pool_handle *)data - 1); }