/*- * Copyright (c) 2003-2004 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: memfunc.c,v 1.2.2.5 2006/12/15 16:31:22 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include "ipa_mod.h" #ifdef WITH_PTHREAD # include # define MEMFUNC_INTERNAL_FLAGS IPA_MEMFUNC_FLAG_PTHREAD #else # define MEMFUNC_INTERNAL_FLAGS 0 #endif #include "memfunc.h" #define MEMFLAG_OPTIMIZE IPA_MEMFUNC_FLAG_OPTIMIZE /* * All functions from this file call memfunc_panic() if some * critical error occurred. */ /* * All functions from this file do as many as possible checks, * but these checks should not decrease speed a lot. */ /* * If some pool in a mzone becomes free, then it is probably will * be returned to free memory. */ #define MEM_SIGNATURE 0x1a77bc01 /* Anything, except all 0s or 1s. */ #define MEM_NOSIGNATURE 0x00000000 /* Anything, but not MEM_SIGNATURE. */ #define MEM_ALIGN_T double /* Is it good for all archs? */ struct ipa_mem_type_struct { STAILQ_ENTRY(ipa_mem_type_struct) link; /* For list of all mem_types. */ const char *name; /* Name of type. */ const char *desc; /* Description. */ u_int flags; /* Flags. */ size_t size; /* Amount of allocated memory. */ u_int reqs; /* Number of requests. */ int local; /* Non-zero if not owned by module. */ #ifdef WITH_PTHREAD pthread_mutex_t mutex; /* Mutex for this type. */ #endif }; static STAILQ_HEAD(, ipa_mem_type_struct) mem_type_list; /* List of all mem_types. */ #define MTYPE_NAME(x) "memfunc:m_" #x static ipa_mzone *mem_type_mzone; /* Mzone of mem_type structures. */ struct mem_desc { int signature; /* MEM_SIGNATURE & size. */ size_t size; /* Actual size. */ union { MEM_ALIGN_T align; char buf[1]; /* Actual buffer. */ } u; }; #define MEM_TYPE_NSIZE 30 #define MEM_TYPE_NALLOC 10 /* Offset of actual buffer in struct mem_desc. */ #define MEM_DESC_BUF_OFFSET (offsetof(struct mem_desc, u.buf[0])) /* Pointer to struct mem_desc for the given buffer address. */ #define MEM_DESC_PTR(ptr) ((struct mem_desc *)((char *)(ptr) - MEM_DESC_BUF_OFFSET)) /* Pointer to the buffer in struct mem_desc. */ #define MEM_DESC_BUF(ptr) ((ptr)->u.buf) #define MZONE_NAME(x) "memfunc:" #x static int memfunc_inited = 0; /* Non-zero if memfunc_init() was called. */ /* * Nitems and nalloc in marrays are rounded to BIT_WORD_BIT, so each * bitword is used completely. */ /* Pool's items array alignment. */ #define MZONE_POOL_ITEMS_ALIGN_T MEM_ALIGN_T /* Rounding for size of each item. */ #define MZONE_POOL_ITEM_ROUNDING (sizeof(MZONE_POOL_ITEMS_ALIGN_T)) /* Address of pool's items array. */ #define MZONE_POOL_ITEMS(pool) ((void *)(pool)->u.buf) /* Offset of the start of items array in a pool. */ #define MZONE_POOL_ITEMS_OFFSET (offsetof(struct mzone_pool, u.buf[0])) /* Return real size of item without pool index. */ #define MZONE_REAL_ISIZE(mzone) ((mzone)->isize - sizeof(u_int)) /* Return pointer to index of pool for item. */ #define MZONE_POOL_INDEX(mzone, item) ((u_int *)((char *)(item) + MZONE_REAL_ISIZE(mzone))) /* Pointer to next free item. */ #define MZONE_FREE_ITEM_NEXT(mzone, item) (*(void **)(item)) /* * Item should be in pool's items addresses and should be * correctly aligned. */ #define MZONE_POOL_VALID_ITEM(mzone, pool, item) ( \ (item) >= MZONE_POOL_ITEMS(pool) && \ (item) <= (pool)->last_item && \ ((char *)(item) - (char *)MZONE_POOL_ITEMS(pool)) % (mzone)->isize == 0 \ ) /* Return index of item in pool's items array. */ #define MZONE_POOL_ITEM_TO_INDEX(mzone, pool, item) \ (((char *)(item) - (char *)MZONE_POOL_ITEMS(pool)) / (mzone)->isize) /* * One pool of mzone's items. */ struct mzone_pool { TAILQ_ENTRY(mzone_pool) link; /* Link for free pools list. */ void *last_item; /* Pointer to last item. */ void *free_items; /* List of free items. */ BITMAP_TYPE *bitmap; /* Pointer to items bitmap. */ u_int nitems; /* Number of all items. */ u_int nfree; /* Number of free items. */ size_t pool_size; /* Size of pool. */ u_int pool_idx; /* Index of pool. */ union { MZONE_POOL_ITEMS_ALIGN_T align; char buf[1]; /* Array of items + bitmap. */ } u; }; TAILQ_HEAD(mzone_pool_list, mzone_pool); struct ipa_mzone_struct { TAILQ_ENTRY(ipa_mzone_struct) link; /* For list of all mzones. */ struct mzone_pool_list free_pools; /* List of free pools. */ size_t isize; /* Size of one item. */ u_int nalloc; /* How may items to allocate. */ u_int nused; /* Number of used items. */ u_int nfree; /* Number of free items. */ const char *name; /* Name of the mzone. */ const char *desc; /* Description. */ size_t pools_size; /* Used memory by pools. */ u_int flags; /* Mzone flags. */ u_int reqs; /* Number of requests. */ struct mzone_pool **pool_ptr; /* Pointers to pools. */ u_int pool_ptr_size; /* Size of pool_ptr in entries. */ #ifdef WITH_PTHREAD pthread_mutex_t mzone_mutex; /* Mutex for whole mzone. */ #endif }; static TAILQ_HEAD(, ipa_mzone_struct) mzone_list; /* List of all mzones. */ struct ipa_marray_struct { TAILQ_ENTRY(ipa_marray_struct) link; /* Linked list of all marrays. */ void **arr; /* Pointer to pointer of array of items. */ BITMAP_TYPE *bitmap; /* Bitmap of used/unused items. */ BITMAP_TYPE *bitmap_hint; /* Hint for free items search. */ u_int nitems; /* Number of all items. */ u_int nfree; /* Number of free items */ u_int nalloc; /* How many items to allocate. */ size_t isize; /* Size of one item. */ const char *name; /* Name of the marray. */ const char *desc; /* Description. */ size_t arr_size; /* Used memory by *arr. */ size_t bitmap_size; /* Used memory by bitmap. */ u_int flags; /* Marray flags. */ u_int reqs; /* Number of requests. */ #ifdef WITH_PTHREAD pthread_mutex_t marray_mutex; /* Mutex for marray. */ #endif }; static TAILQ_HEAD(, ipa_marray_struct) marray_list; /* List of all marrays. */ #define MZONE_NSIZE 20 #define MZONE_NALLOC 10 static ipa_mzone mzone_mzone; /* Mzone of mzones. */ static ipa_mem_type m_mzone; /* Mem_type for mzones. */ #define MARRAY_NSIZE 20 #define MARRAY_NALLOC 10 static ipa_mzone *marray_mzone; /* Mzone of marrays. */ static ipa_mem_type *m_marray; /* Mem_type for marrays. */ #define PAGESIZE_DEFAULT 4096 /* Default size of a VM page. */ static long pagesize; /* Size of a VM page. */ /* * Implementation of mvlogmsgx() should be provided by the * function, which uses any of exported functions from this file. */ void (*mvlogmsgx)(const char *, va_list); extern ipa_mem_type *m_parser; /* Declared in parser.c */ #ifdef WITH_MEMFUNC_DEBUG # ifndef FILL_ALLOC # define FILL_ALLOC 0xfc /* Fill byte for just allocated memory. */ # endif # ifndef FILL_FREE # define FILL_FREE 0xa7 /* Fill byte for just freed memory. */ # endif #endif static const char gbytes[] = { 0x27, 0x17, 0x4b, 0xe7 }; /* * This variables has values only after memfunc_deinit_1() and * memfunc_deinit_2() invocations. */ static size_t mem_size_cnt_all = 0; /* Amount of allocated memory after start. */ static size_t mem_size_cnt; /* Amount of allocated memory after reconfiguration. */ #ifdef WITH_PTHREAD static pthread_mutexattr_t mutexattr; /* Default attributes for mutexes. */ # define IS_PTHREADED(x) ((x)->flags & IPA_MEMFUNC_FLAG_PTHREAD) # define LOCK_MZONE(x) do { \ if (IS_PTHREADED(x)) \ lock_mzone(x); \ } while (/* CONSTCOND */ 0) # define UNLOCK_MZONE(x) do { \ if (IS_PTHREADED(x)) \ unlock_mzone(x); \ } while (/* CONSTCOND */ 0) # define LOCK_MARRAY(x) do { \ if (IS_PTHREADED(x)) \ lock_marray(x); \ } while (/* CONSTCOND */ 0) # define UNLOCK_MARRAY(x) do { \ if (IS_PTHREADED(x)) \ unlock_marray(x); \ } while (/* CONSTCOND */ 0) # define LOCK_MEM_TYPE(x) do { \ if (IS_PTHREADED(x)) \ lock_mem_type(x); \ } while (/* CONSTCOND */ 0) # define UNLOCK_MEM_TYPE(x) do { \ if (IS_PTHREADED(x)) \ unlock_mem_type(x); \ } while (/* CONSTCOND */ 0) #define LOCK_MEM_TYPE_LIST() LOCK_MZONE(mem_type_mzone) #define UNLOCK_MEM_TYPE_LIST() UNLOCK_MZONE(mem_type_mzone) #define LOCK_MZONE_LIST() LOCK_MZONE(&mzone_mzone) #define UNLOCK_MZONE_LIST() UNLOCK_MZONE(&mzone_mzone) #define LOCK_MARRAY_LIST() LOCK_MZONE(marray_mzone) #define UNLOCK_MARRAY_LIST() UNLOCK_MZONE(marray_mzone) #define STRERRBUF_SIZE 128 /* Size of buffer for strerror_r(). */ #define my_strerror(e) safe_strerror(e, strerrbuf) #else # define LOCK_MZONE(x) # define UNLOCK_MZONE(x) # define LOCK_MARRAY(x) # define UNLOCK_MARRAY(x) # define LOCK_MEM_TYPE(x) # define UNLOCK_MEM_TYPE(x) #define LOCK_MEM_TYPE_LIST() #define UNLOCK_MEM_TYPE_LIST() #define LOCK_MZONE_LIST() #define UNLOCK_MZONE_LIST() #define LOCK_MARRAY_LIST() #define UNLOCK_MARRAY_LIST() #define my_strerror(e) strerror(e) #endif /* WITH_PTHREAD */ static void logmsgx(const char *, ...) ATTR_FORMAT(printf, 1, 2); static void memfunc_panic(const char *, ...) ATTR_FORMAT(printf, 1, 2); /* * This log function is used mostly for checking printf-like format, * since the external implementation of log function is vprintf-like. */ static void logmsgx(const char *format, ...) { va_list ap; va_start(ap, format); mvlogmsgx(format, ap); va_end(ap); } /* * Send last panic message and exit by sending SIGABRT signal * to itself. */ static void memfunc_panic(const char *format, ...) { va_list ap; va_start(ap, format); mvlogmsgx(format, ap); va_end(ap); logmsgx("!!! PANIC EXIT FROM MEMFUNC !!!"); abort(); exit(1); } #ifdef WITH_PTHREAD /* * Any buffer passed to this function should be at least STRERRBUF_SIZE * bytes in size. */ static const char * safe_strerror(int error, char *buf) { if (strerror_r(error, buf, STRERRBUF_SIZE) != 0) if (snprintf(buf, STRERRBUF_SIZE, "error code %d", error) < 0) return ""; return buf; } static int mutex_init(const char *name, pthread_mutex_t *mutex) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_init(mutex, &mutexattr)) != 0) { logmsgx("mutex_init: pthread_mutex_init(%s) failed: %s!", name, my_strerror(error)); return -1; } return 0; } /* * Just init mutexattr with default settings and set PTHREAD_MUTEX_ERRORCHECK * type for it, to prevent recursive locking, etc. */ static int memfunc_mutexattr_init(void) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutexattr_init(&mutexattr)) != 0) { logmsgx("memfunc_mutexattr_init: pthread_mutexattr_init: %s", my_strerror(error)); return -1; } if ( (error = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK)) != 0) memfunc_panic("memfunc_mutexattr_init: pthread_mutexattr_settype: %s", my_strerror(error)); return 0; } /* * These two functions are used for locking and unlocking mzone * structure, they should not fail. */ static void lock_mzone(ipa_mzone *mzone) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_lock(&mzone->mzone_mutex)) != 0) memfunc_panic("lock_mzone(%s): pthread_mutex_lock failed: %s!", mzone->name, my_strerror(error)); } static void unlock_mzone(ipa_mzone *mzone) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_unlock(&mzone->mzone_mutex)) != 0) memfunc_panic("unlock_mzone(%s): pthread_mutex_unlock failed: %s!", mzone->name, my_strerror(error)); } /* * These two functions are used for locking and unlocking marray * structure, they should not fail. */ static void lock_marray(ipa_marray *marray) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_lock(&marray->marray_mutex)) != 0) memfunc_panic("lock_marray(%s): pthread_mutex_lock failed: %s!", marray->name, my_strerror(error)); } static void unlock_marray(ipa_marray *marray) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_unlock(&marray->marray_mutex)) != 0) memfunc_panic("unlock_marray(%s): pthread_mutex_unlock failed: %s!", marray->name, my_strerror(error)); } /* * These two functions are used for locking and unlocking mem_type * structure, they should not fail. */ static void lock_mem_type(ipa_mem_type *mem_type) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_lock(&mem_type->mutex)) != 0) memfunc_panic("lock_mem_type(%s): pthread_mutex_lock failed: %s!", mem_type->name, my_strerror(error)); } static void unlock_mem_type(ipa_mem_type *mem_type) { int error; char strerrbuf[STRERRBUF_SIZE]; if ( (error = pthread_mutex_unlock(&mem_type->mutex)) != 0) memfunc_panic("unlock_mem_type(%s): pthread_mutex_unlock failed: %s!", mem_type->name, my_strerror(error)); } #endif /* WITH_PTHREAD */ static int mem_type_init_internal(ipa_mem_type *mem_type, const char *name, const char *desc, u_int flags) { if (name == NULL || desc == NULL) { logmsgx("mem_type_init_internal: name or desc is NULL"); return -1; } mem_type->name = name; mem_type->desc = desc; mem_type->flags = flags; mem_type->size = 0; mem_type->reqs = 0; #ifdef WITH_PTHREAD if (IS_PTHREADED(mem_type)) if (mutex_init(name, &mem_type->mutex) < 0) { logmsgx("mem_type_init_internal: mutex_init failed!"); return -1; } #endif return 0; } #ifdef WITH_PTHREAD static void mem_type_deinit_internal(ipa_mem_type *mem_type) { int error; char strerrbuf[STRERRBUF_SIZE]; if (IS_PTHREADED(mem_type)) if ( (error = pthread_mutex_destroy(&mem_type->mutex)) != 0) memfunc_panic("mem_type_deinit_internal(%s): pthread_mutex_destroy failed: %s!", mem_type->name, my_strerror(error)); } #else static void mem_type_deinit_internal(ipa_mem_type *mem_type ATTR_UNUSED) { } #endif /* WITH_PTHREAD */ static ipa_mem_type * mem_type_new(const char *name, const char *desc, u_int flags) { ipa_mem_type *mem_type; if ( (mem_type = mzone_alloc(mem_type_mzone)) == NULL) { logmsgx("mem_type_new(%s): mzone_alloc failed", name); return NULL; } if (mem_type_init_internal(mem_type, name, desc, flags) < 0) { logmsgx("mem_type_new(%s): mem_type_init_internal failed", name); mzone_free(mem_type_mzone, mem_type); return NULL; } mem_type->local = 0; LOCK_MEM_TYPE_LIST(); STAILQ_INSERT_TAIL(&mem_type_list, mem_type, link); UNLOCK_MEM_TYPE_LIST(); return mem_type; } ipa_mem_type * mem_type_new_local(const char *name, const char *desc, u_int flags) { ipa_mem_type *mem_type; if ( (mem_type = mem_type_new(name, desc, flags)) == NULL) return NULL; mem_type->local = 1; return mem_type; } /* * Allocate buffer of size bytes. */ void * mem_malloc(size_t size, ipa_mem_type *mem_type) { struct mem_desc *mem; #ifdef WITH_PTHREAD char strerrbuf[STRERRBUF_SIZE]; #endif if (size == 0) memfunc_panic("mem_malloc(%s): size is zero", mem_type->name); if ( (mem = malloc(size + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) { logmsgx("mem_malloc(%lu + %lu, %s) failed: malloc: %s", (u_long)size, (u_long)MEM_DESC_BUF_OFFSET, mem_type->name, my_strerror(errno)); return NULL; } mem->signature = MEM_SIGNATURE & (int)size; mem->size = size; #ifdef WITH_MEMFUNC_DEBUG memset(MEM_DESC_BUF(mem), FILL_ALLOC, size); #endif memcpy(MEM_DESC_BUF(mem) + size, gbytes, sizeof gbytes); LOCK_MEM_TYPE(mem_type); mem_type->size += size; mem_type->reqs++; UNLOCK_MEM_TYPE(mem_type); return MEM_DESC_BUF(mem); } /* * Allocate buffer of number * size bytes and fill it with zeros. */ void * mem_calloc(size_t number, size_t size, ipa_mem_type *mem_type) { struct mem_desc *mem; #ifdef WITH_PTHREAD char strerrbuf[STRERRBUF_SIZE]; #endif if (number == 0 || size == 0) memfunc_panic("mem_calloc(%s): wrong number %lu or size %lu", mem_type->name, (u_long)number, (u_long)size); size *= number; if ( (mem = malloc(size + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) { logmsgx("mem_calloc(%lu + %lu, %s) failed: %s", (u_long)size, (u_long)MEM_DESC_BUF_OFFSET, mem_type->name, my_strerror(errno)); return NULL; } mem->signature = MEM_SIGNATURE & (int)size; mem->size = size; memset(MEM_DESC_BUF(mem), 0, size); memcpy(MEM_DESC_BUF(mem) + size, gbytes, sizeof gbytes); LOCK_MEM_TYPE(mem_type); mem_type->size += size; mem_type->reqs++; UNLOCK_MEM_TYPE(mem_type); return MEM_DESC_BUF(mem); } /* * Free previously allocated buffer. */ void mem_free(void *ptr, ipa_mem_type *mem_type) { size_t size; struct mem_desc *mem; if (ptr == NULL) return; mem = MEM_DESC_PTR(ptr); if (mem->signature != (MEM_SIGNATURE & (int)(mem->size))) memfunc_panic("mem_free(%p, %p): wrong signature", ptr, mem_type); size = mem->size; if (memcmp(MEM_DESC_BUF(mem) + size, gbytes, sizeof gbytes) != 0) memfunc_panic("mem_free(%p, %s): guard bytes were changed", ptr, mem_type->name); LOCK_MEM_TYPE(mem_type); mem_type->size -= size; UNLOCK_MEM_TYPE(mem_type); #ifdef WITH_MEMFUNC_DEBUG memset(mem, FILL_FREE, size + MEM_DESC_BUF_OFFSET); #endif mem->signature = MEM_NOSIGNATURE; free(mem); } /* * Reallocate buffer, if can't reallocate then keep previous buffer * unchanged and return NULL. */ void * mem_realloc(void *ptr, size_t size2, ipa_mem_type *mem_type) { size_t size1; struct mem_desc *mem1, *mem2; #ifdef WITH_PTHREAD char strerrbuf[STRERRBUF_SIZE]; #endif if (size2 == 0) memfunc_panic("mem_realloc(%s): size is zero", mem_type->name); if (ptr == NULL) { /* realloc(NULL, size2) is the same as malloc(size2) */ if ( (ptr = mem_malloc(size2, mem_type)) == NULL) logmsgx("mem_realloc(%s): mem_malloc failed", mem_type->name); return ptr; } mem1 = MEM_DESC_PTR(ptr); size1 = mem1->size; if (mem1->signature != (MEM_SIGNATURE & (int)size1)) memfunc_panic("mem_realloc(%p, %p): wrong signature", ptr, mem_type); if (memcmp(MEM_DESC_BUF(mem1) + size1, gbytes, sizeof gbytes) != 0) memfunc_panic("mem_realloc(%p, %s): guard bytes were changed", ptr, mem_type->name); #ifdef WITH_MEMFUNC_DEBUG /* malloc() --> memcpy() --> free() */ if ( (mem2 = malloc(size2 + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) { logmsgx("mem_realloc(%s): malloc(%lu + %lu) failed: %s", mem_type->name, (u_long)size2, (u_long)MEM_DESC_BUF_OFFSET, my_strerror(errno)); return NULL; } if (size2 > size1) { memcpy(MEM_DESC_BUF(mem2), ptr, size1); memset(MEM_DESC_BUF(mem2) + size1, FILL_ALLOC, size2 - size1); } else memcpy(MEM_DESC_BUF(mem2), ptr, size2); mem_free(ptr, mem_type); LOCK_MEM_TYPE(mem_type); mem_type->size += size2; mem_type->reqs++; UNLOCK_MEM_TYPE(mem_type); #else /* True realloc(). */ mem1->signature = MEM_NOSIGNATURE; if ( (mem2 = realloc(mem1, size2 + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) { logmsgx("mem_realloc(%lu + %lu, %s) failed: %s", (u_long)size2, (u_long)MEM_DESC_BUF_OFFSET, mem_type->name, my_strerror(errno)); mem1->signature = MEM_SIGNATURE & (int)size1; return NULL; } LOCK_MEM_TYPE(mem_type); mem_type->size -= size1, mem_type->size += size2; mem_type->reqs++; UNLOCK_MEM_TYPE(mem_type); #endif /* WITH_MEMFUNC_DEBUG */ mem2->signature = MEM_SIGNATURE & (int)size2; memcpy(MEM_DESC_BUF(mem2) + size2, gbytes, sizeof gbytes); mem2->size = size2; return MEM_DESC_BUF(mem2); } /* * Allocate memory for a string and copy it. */ char * mem_strdup(const char *src, ipa_mem_type *mem_type) { char *dst; size_t size; if (src == NULL) memfunc_panic("mem_strdup: NULL pointer argument"); size = strlen(src); if ( (dst = mem_malloc(size + 1, mem_type)) == NULL) { logmsgx("mem_strdup(%s): mem_malloc failed", mem_type->name); return NULL; } return strncpy(dst, src, size + 1); } /* * Analog of vasprintf(3) function, found on some systems. */ int mem_vasprintf(ipa_mem_type *mem_type, char **bufp, const char *format, va_list ap) { int size, rv; char *ptr; char buf[128]; /* Try to use buffer in stack. */ #ifdef WITH_PTHREAD char strerrbuf[STRERRBUF_SIZE]; #endif if ( (size = vsnprintf(buf, sizeof buf, format, ap)) < 0) { logmsgx("mem_vasprintf: vsnprintf failed (1): %s", my_strerror(errno)); *bufp = NULL; return -1; } if ( (ptr = mem_malloc(++size, mem_type)) == NULL) { logmsgx("mem_vasprintf(%s): mem_malloc failed", mem_type->name); return -1; } /* Since size was incremented, so use '>' operator here. */ if (size > sizeof buf) { if ( (rv = vsnprintf(ptr, size, format, ap)) < 0) { logmsgx("mem_vasprintf(%s): vsnprintf failed (2): %s", mem_type->name, my_strerror(errno)); goto failed; } if (rv >= size) { logmsgx("mem_vasprintf(%s): not enough space in vsnprintf", mem_type->name); goto failed; } } else memcpy(ptr, buf, size); *bufp = ptr; return 0; failed: mem_free(ptr, mem_type); *bufp = NULL; return -1; } /* * Analog of asprintf(3) function, found on some systems. */ int mem_asprintf(ipa_mem_type *mem_type, char **bufp, const char *format, ...) { int ret; va_list ap; va_start(ap, format); ret = mem_vasprintf(mem_type, bufp, format, ap); va_end(ap); return ret; } /* * Adjust pools_size and nfree in mzone and release memory * used by the given pool of items. */ static void mzone_pool_free(ipa_mzone *mzone, struct mzone_pool *pool) { mzone->pools_size -= pool->pool_size; mzone->nfree -= pool->nfree; mem_free(pool->bitmap, &m_mzone); mem_free(pool, &m_mzone); } /* * Create one mzone_pool with nitems items. */ static struct mzone_pool * mzone_pool_create(ipa_mzone *mzone, u_int nitems) { u_int i, pool_idx; void *item, *item_next; size_t pool_size; struct mzone_pool *pool, **pool_ptr; /* Check if free_pools list is empty. */ if (!TAILQ_EMPTY(&mzone->free_pools)) memfunc_panic("mzone_pool_create(%s): there are already free pools", mzone->name); pool_size = MZONE_POOL_ITEMS_OFFSET + nitems * mzone->isize; /* Allocate pool. */ if ( (pool = mem_malloc(pool_size, &m_mzone)) == NULL) { logmsgx("mzone_pool_create(%s): mem_malloc failed for pool", mzone->name); return NULL; } /* Allocate memory for bitmap. */ if ( (pool->bitmap = mem_calloc(BITMAP_SIZE(nitems), 1, &m_mzone)) == NULL) { mem_free(pool, &m_mzone); logmsgx("mzone_pool_create(%s): mem_calloc failed for bitmap", mzone->name); return NULL; } mzone->pools_size += pool->pool_size = pool_size; mzone->nfree += pool->nitems = pool->nfree = nitems; /* Find first unused pool index. */ for (pool_idx = 0; pool_idx < mzone->pool_ptr_size; ++pool_idx) if (mzone->pool_ptr[pool_idx] == NULL) break; if (pool_idx == mzone->pool_ptr_size) { /* No free index. */ if ( (pool_ptr = mem_realloc(mzone->pool_ptr, (mzone->pool_ptr_size + 1) * sizeof *mzone->pool_ptr, &m_mzone)) == NULL) { logmsgx("mzone_pool_create(%s): mem_realloc failed, cannot extend pool_ptr", mzone->name); mzone_pool_free(mzone, pool); return NULL; } mzone->pool_ptr = pool_ptr; mzone->pool_ptr_size++; } pool->pool_idx = pool_idx; mzone->pool_ptr[pool_idx] = pool; pool->free_items = MZONE_POOL_ITEMS(pool); /* * Let's keep free items in a list sorted by addresses, * at least until mzone_free() is called. */ for (i = 0, item = pool->free_items; i < nitems; ++i) { item_next = (char *)item + mzone->isize; MZONE_FREE_ITEM_NEXT(mzone, item) = item_next; *MZONE_POOL_INDEX(mzone, item) = pool_idx; item = item_next; } pool->last_item = (char *)item - mzone->isize; /* And insert this pool to free_pools list. */ TAILQ_INSERT_HEAD(&mzone->free_pools, pool, link); return pool; } /* * Reserve memory for header struct mzone_pool{} and for * header struct mem_desc{} and return number of items which * fit to (n * pagesize). */ static u_int round_to_pagesize(u_int nitems, u_int isize) { size_t size; size = (nitems * isize + pagesize - 1) / pagesize; size *= pagesize; nitems = (size - MEM_DESC_BUF_OFFSET - MZONE_POOL_ITEMS_OFFSET) / isize; return nitems != 0 ? nitems : 1; } /* * Init fields in mzone and allocate one pool. */ static int mzone_init_internal(ipa_mzone *mzone, const char *name, const char *desc, u_int flags, size_t isize, u_int nitems, u_int nalloc) { struct mzone_pool *pool; /* Validate arguments. */ if (isize == 0 || nitems == 0 || nalloc == 0) memfunc_panic("mzone_init_internal(%s): isize, nitems and nalloc must be greater than zero", mzone->name); /* There should be enough space for storing (void *) inside one item. */ if (isize < sizeof(void *)) isize = sizeof(void *); /* * Reserve memory for pool index, this value will also can help * to find buffer overflow errors, also we keep not a pointer * to make additional tests. Alternatively pools can be allocated * to page boundary and in this case it is easy to access pool's * description from item's address, but this optimization is system * specific. */ isize += sizeof(u_int); /* Round isize to MZONE_POOL_ITEM_ROUNDING. */ isize = (isize + MZONE_POOL_ITEM_ROUNDING - 1) / MZONE_POOL_ITEM_ROUNDING; isize *= MZONE_POOL_ITEM_ROUNDING; if (flags & IPA_MEMFUNC_FLAG_OPTIMIZE) { /* * Adjust nitems and nalloc, so one pool should use * (pagesize * n) bytes. */ nitems = round_to_pagesize(nitems, isize); nalloc = round_to_pagesize(nalloc, isize); } TAILQ_INIT(&mzone->free_pools); mzone->isize = isize; mzone->nalloc = nalloc; mzone->nused = mzone->nfree = 0; mzone->name = name; mzone->desc = desc; mzone->pools_size = 0; mzone->pool_ptr = NULL; mzone->pool_ptr_size = 0; if ( (pool = mzone_pool_create(mzone, nitems)) == NULL) { logmsgx("mzone_init_internal(%s): mzone_pool_create failed", name); return -1; } mzone->flags = flags; mzone->reqs = 0; #ifdef WITH_PTHREAD if (IS_PTHREADED(mzone)) if (mutex_init(name, &mzone->mzone_mutex) < 0) { logmsgx("mzone_init_internal: mutex_init failed!"); mzone_pool_free(mzone, pool); return -1; } #endif return 0; } /* * Init mzone, should be called before any other mzone_*() function. */ ipa_mzone * mzone_init(const char *name, const char *desc, u_int flags, size_t isize, u_int nitems, u_int nalloc) { ipa_mzone *mzone; if (name == NULL || desc == NULL) memfunc_panic("mzone_init: name or description of mzone must be given"); if ( (mzone = mzone_alloc(&mzone_mzone)) == NULL) { logmsgx("mzone_init(%s): mzone_alloc failed", name); return NULL; } if (mzone_init_internal(mzone, name, desc, flags, isize, nitems, nalloc) < 0) { logmsgx("mzone_init(%s): mzone_init_internal failed", name); mzone_free(&mzone_mzone, mzone); return NULL; } LOCK_MZONE_LIST(); TAILQ_INSERT_TAIL(&mzone_list, mzone, link); UNLOCK_MZONE_LIST(); return mzone; } /* * Free all mzone_pools in mzone and destroy mutex. */ static void mzone_deinit_internal(ipa_mzone *mzone) { u_int pool_idx; struct mzone_pool *pool; #ifdef WITH_PTHREAD int error; char strerrbuf[STRERRBUF_SIZE]; #endif for (pool_idx = 0; pool_idx < mzone->pool_ptr_size; ++pool_idx) { pool = mzone->pool_ptr[pool_idx]; if (pool != NULL) { if (pool->pool_idx != pool_idx) memfunc_panic("mzone_deinit_internal(%s): pool index is wrong", mzone->name); mzone_pool_free(mzone, pool); } } if (mzone->pools_size != 0) memfunc_panic("mzone_deinit_internal(%s): pools_size is not equal to zero", mzone->name); if (mzone->nfree != 0) memfunc_panic("mzone_deinit_internal(%s): nfree is not equal to zero", mzone->name); mem_free(mzone->pool_ptr, &m_mzone); #ifdef WITH_PTHREAD if (IS_PTHREADED(mzone)) if ( (error = pthread_mutex_destroy(&mzone->mzone_mutex)) != 0) memfunc_panic("mzone_deinit_internal(%s): pthread_mutex_destroy failed: %s!", mzone->name, my_strerror(error)); #endif } /* * Free all memory used by mzone and by mzone structure as well. */ void mzone_deinit(ipa_mzone *mzone) { if (mzone == NULL) return; mzone_deinit_internal(mzone); LOCK_MZONE_LIST(); TAILQ_REMOVE(&mzone_list, mzone, link); UNLOCK_MZONE_LIST(); mzone_free(&mzone_mzone, mzone); } /* * Allocate one item in mzone, possibly allocate new pool if needed. */ void * mzone_alloc(ipa_mzone *mzone) { u_int idx; void *item; BITMAP_TYPE *bitword, bitmask; struct mzone_pool *pool; if (mzone == NULL) memfunc_panic("mzone_alloc: NULL mzone"); LOCK_MZONE(mzone); /* Get pool with free items. */ if (mzone->nfree > 0) { pool = TAILQ_FIRST(&mzone->free_pools); if (pool == NULL) memfunc_panic("mzone_alloc(%s): free pool is NULL", mzone->name); if (pool->nfree == 0) memfunc_panic("mzone_alloc(%s): free pool does not have free items", mzone->name); } else { if ( (pool = mzone_pool_create(mzone, mzone->nalloc)) == NULL) { logmsgx("mzone_alloc(%s): mzone_pool_create failed", mzone->name); item = NULL; goto out; } } /* Decrement number of free items. */ pool->nfree--; mzone->nfree--; /* Increment number of used items. */ mzone->nused++; /* Get item from free items list. */ item = pool->free_items; pool->free_items = MZONE_FREE_ITEM_NEXT(mzone, item); /* Check if just allocated item has the same pool index as its pool. */ if (*MZONE_POOL_INDEX(mzone, item) != pool->pool_idx) memfunc_panic("mzone_alloc(%s): just allocated item %p does not have the same pool index as its pool", mzone->name, item); /* Check if just allocated item belongs to pool. */ if (!MZONE_POOL_VALID_ITEM(mzone, pool, item)) memfunc_panic("mzone_alloc(%s): just allocated item %p does not belong to pool", mzone->name, item); /* Get index of just allocated item in its pool. */ idx = MZONE_POOL_ITEM_TO_INDEX(mzone, pool, item); bitword = BIT_WORD(pool->bitmap, idx); bitmask = BIT_MASK(idx); /* Check if just allocated item is used. */ if (BIT_TEST(bitword, bitmask)) memfunc_panic("mzone_alloc(%s): just allocated item %p already is used", mzone->name, item); /* Mark just allocated item as used. */ BIT_SET(bitword, bitmask); #ifdef WITH_MEMFUNC_DEBUG memset(item, FILL_ALLOC, MZONE_REAL_ISIZE(mzone)); #endif /* Pool became empty. */ if (pool->nfree == 0) TAILQ_REMOVE(&mzone->free_pools, pool, link); out: mzone->reqs++; UNLOCK_MZONE(mzone); return item; } /* * Return previously allocated item to mzone. * Check if we can completely free its pool. */ void mzone_free(ipa_mzone *mzone, void *item) { u_int idx, pool_idx; BITMAP_TYPE *bitword, bitmask; struct mzone_pool *pool, **pool_ptr; if (mzone == NULL) memfunc_panic("mzone_free: NULL mzone"); if (item == NULL) return; LOCK_MZONE(mzone); pool_idx = *MZONE_POOL_INDEX(mzone, item); /* Out of pools? */ if (pool_idx >= mzone->pool_ptr_size) memfunc_panic("mzone_free(%s): index of item's pool is %u, last pool's index is %u", mzone->name, pool_idx, mzone->pool_ptr_size - 1); pool = mzone->pool_ptr[pool_idx]; /* Check if this pool really exist. */ if (pool == NULL) memfunc_panic("mzone_free(%s): index of returned item's pool is %u, but this pool does not exist", mzone->name, pool_idx); /* Check if returned item has the same pool index as its pool. */ if (pool_idx != pool->pool_idx) memfunc_panic("mzone_alloc(%s): returned item %p does not have the same pool index as its pool", mzone->name, item); /* Check if returned item belongs to pool. */ if (!MZONE_POOL_VALID_ITEM(mzone, pool, item)) memfunc_panic("mzone_alloc(%s): returned item %p does not belong to its pool", mzone->name, item); idx = MZONE_POOL_ITEM_TO_INDEX(mzone, pool, item); bitword = BIT_WORD(pool->bitmap, idx); bitmask = BIT_MASK(idx); /* Test if this item is marked as used. */ if (!BIT_TEST(bitword, bitmask)) memfunc_panic("mzone_free(%s): returned item %p is marked as free", mzone->name, item); #ifdef WITH_MEMFUNC_DEBUG memset(item, FILL_FREE, MZONE_REAL_ISIZE(mzone)); #endif /* Return item to free items list. */ BIT_CLEAR(bitword, bitmask); MZONE_FREE_ITEM_NEXT(mzone, item) = pool->free_items; pool->free_items = item; /* Increment number of free items. */ pool->nfree++; mzone->nfree++; /* Decrement number of used items. */ mzone->nused--; if (pool->nfree == pool->nitems) { /* If pool has more than one free item, then it was in free_pools list. */ if (pool->nfree > 1) TAILQ_REMOVE(&mzone->free_pools, pool, link); if (mzone->nfree - pool->nfree >= mzone->nalloc) { /* Mzone has enough unused items. */ free_pool: /* Mark this pool as not existent. */ mzone->pool_ptr[pool_idx] = NULL; /* Actually release memory used by pool. */ mzone_pool_free(mzone, pool); /* Can we reduce pool_ptr? */ if (pool_idx + 1 == mzone->pool_ptr_size) { /* Last pool in pool_ptr --> find first used pool from the end. */ for (--pool_idx;; --pool_idx) if (mzone->pool_ptr[pool_idx] != NULL) break; if ( (pool_ptr = mem_realloc(mzone->pool_ptr, (pool_idx + 1) * sizeof *mzone->pool_ptr, &m_mzone)) == NULL) logmsgx("mzone_free(%s): mem_realloc failed, cannot reduce pool_ptr", mzone->name); else { /* Pool_ptr was reduced. */ mzone->pool_ptr = pool_ptr; mzone->pool_ptr_size = pool_idx + 1; } } } else { /* Link completely free pool to the end of free_pools list. */ TAILQ_INSERT_TAIL(&mzone->free_pools, pool, link); } } else { /* If pool has only one free item, then it is not in free_pools list. */ if (pool->nfree == 1) TAILQ_INSERT_HEAD(&mzone->free_pools, pool, link); /* * If last pool in free_pools list is completely free and * mzone has enough unused items, then free it. */ pool = TAILQ_LAST(&mzone->free_pools, mzone_pool_list); if (pool->nfree == pool->nitems && mzone->nfree - pool->nfree >= mzone->nalloc) { TAILQ_REMOVE(&mzone->free_pools, pool, link); pool_idx = pool->pool_idx; goto free_pool; } } UNLOCK_MZONE(mzone); } /* * Return number of allocated items in mzone. */ u_int mzone_nused(ipa_mzone *mzone) { u_int ret; if (mzone == NULL) memfunc_panic("mzone_nused: NULL mzone"); LOCK_MZONE(mzone); ret = mzone->nused; UNLOCK_MZONE(mzone); return ret; } /* * Init marray, should be called before any other marray_*() function. */ ipa_marray * marray_init(const char *name, const char *desc, u_int flags, void **arr, size_t isize, u_int nitems, u_int nalloc) { size_t arr_size, bitmap_size; BITMAP_TYPE *bitmap; ipa_marray *marray; /* Validate arguments. */ if (name == NULL || desc == NULL) memfunc_panic("marray_init: name or description of marray must be given"); if (isize == 0 || nitems == 0 || nalloc == 0) memfunc_panic("marray_init(%s): isize, nitems and nalloc must be greater than zero", name); /* To simplify checks let nitems will be >= than nalloc. */ if (nitems < nalloc) nitems = nalloc; /* Round nitems and nalloc to BIT_WORD_NBITS. */ nitems = (nitems + BIT_WORD_NBITS - 1) & ~(BIT_WORD_NBITS - 1); nalloc = (nalloc + BIT_WORD_NBITS - 1) & ~(BIT_WORD_NBITS - 1); /* Allocate memory for actual array. */ arr_size = isize * nitems; if ( (*arr = mem_malloc(arr_size, m_marray)) == NULL) { logmsgx("marray_init(%s): mem_malloc for array failed", name); return NULL; } /* Allocate memory for bitmap. */ bitmap_size = BITMAP_SIZE(nitems); if ( (bitmap = mem_calloc(bitmap_size, 1, m_marray)) == NULL) { logmsgx("marray_init(%s): mem_calloc for bitmap failed", name); goto fail_dealloc; } /* Allocate memory for marray structure and fill it. */ if ( (marray = mzone_alloc(marray_mzone)) == NULL) { logmsgx("marray_init(%s): mzone_alloc failed", name); goto fail_dealloc; } marray->arr = arr; marray->bitmap = marray->bitmap_hint = bitmap; marray->nitems = marray->nfree = nitems; marray->nalloc = nalloc; marray->isize = isize; marray->name = name; marray->desc = desc; marray->arr_size = arr_size; marray->bitmap_size = bitmap_size; marray->flags = flags; marray->reqs = 0; #ifdef WITH_PTHREAD if (IS_PTHREADED(marray)) if (mutex_init(name, &marray->marray_mutex) < 0) { logmsgx("marray_init: mutex_init failed!"); mzone_free(marray_mzone, marray); goto fail_dealloc; } #endif LOCK_MARRAY_LIST(); TAILQ_INSERT_TAIL(&marray_list, marray, link); UNLOCK_MARRAY_LIST(); return marray; fail_dealloc: mem_free(*arr, m_marray); mem_free(bitmap, m_marray); return NULL; } /* * Free all memory used by marray and by marray structure as well. */ void marray_deinit(ipa_marray *marray) { #ifdef WITH_PTHREAD int error; char strerrbuf[STRERRBUF_SIZE]; #endif if (marray == NULL) return; mem_free(*marray->arr, m_marray); mem_free(marray->bitmap, m_marray); #ifdef WITH_PTHREAD if (IS_PTHREADED(marray)) if ( (error = pthread_mutex_destroy(&marray->marray_mutex)) != 0) memfunc_panic("marray_deinit(%s): pthread_mutex_destroy failed: %s!", marray->name, my_strerror(error)); #endif LOCK_MARRAY_LIST(); TAILQ_REMOVE(&marray_list, marray, link); UNLOCK_MARRAY_LIST(); mzone_free(marray_mzone, marray); } /* * Allocate item in marray: if fixed is non-zero, then allocate * item with index *idxp, else find a free item with the lowest * index and allocate it and return index in *idxp. */ int marray_alloc(ipa_marray *marray, u_int *idxp, int fixed) { int error; u_int idx; BITMAP_TYPE *bitword, bitmask; #ifdef WITH_PTHREAD char strerrbuf[STRERRBUF_SIZE]; #endif if (marray == NULL) memfunc_panic("marray_alloc: NULL marray"); if (idxp == NULL) memfunc_panic("marray_alloc(%s): NULL idxp", marray->name); LOCK_MARRAY(marray); error = 0; if (fixed) { idx = *idxp; if (idx < marray->nitems) { bitword = BIT_WORD(marray->bitmap, idx); bitmask = BIT_MASK(idx); if (BIT_TEST(bitword, bitmask)) memfunc_panic("marray_alloc(%s, %u, 1): index is already used", marray->name, idx); } else bitword = NULL; } else { if (marray->nfree > 0) { int bit; for (bitword = marray->bitmap_hint; *bitword == BIT_WORD_ALL_BITS; ++bitword) ; marray->bitmap_hint = bitword; #if 0 for (bit = 0, bitmask = 1; BIT_TEST(bitword, bitmask); ++bit, bitmask <<= 1) ; #else bit = ffs(~*bitword) - 1; bitmask = 1 << bit; #endif idx = BIT_WORD_NBITS * (bitword - marray->bitmap) + bit; } else bitword = NULL; } if (bitword == NULL) { u_int new_nitems, new_nfree; size_t size1, size2; void *arr; new_nitems = marray->nitems + marray->nalloc; new_nfree = marray->nfree + marray->nalloc; if (fixed) { if (idx >= new_nitems) { /* Round new nitems to BIT_WORD_NBITS. */ new_nitems = (idx + 1 + BIT_WORD_NBITS - 1) & ~(BIT_WORD_NBITS - 1); new_nfree = marray->nfree + (new_nitems - marray->nitems); } } else idx = marray->nitems; /* Realloc bitmap and mark extended bitwords as unused. */ size1 = BITMAP_SIZE(new_nitems); if ( (bitword = mem_realloc(marray->bitmap, size1, m_marray)) == NULL) { logmsgx("marray_alloc(%s): mem_realloc for bitmap failed: %s", marray->name, my_strerror(errno)); error = -1; goto out; } size2 = BITMAP_SIZE(marray->nitems); if (fixed) { /* Simply update hint, since bitword can have new address. */ marray->bitmap_hint = (BITMAP_TYPE *)(bitword + (size_t)(marray->bitmap_hint - marray->bitmap)); } else { /* No free items, so point hint to just allocated bitwords. */ marray->bitmap_hint = (BITMAP_TYPE *)((char *)bitword + size2); } marray->bitmap_size = size1; memset((char *)bitword + size2, 0, size1 - size2); marray->bitmap = bitword; /* * Realloc array, if we can't realloc it, then keep * bitmap unchanged, everything is Ok since marray->nfree * hasn't been changed yet and this is also correct for * already updated bitmap_hint. */ size1 = new_nitems * marray->isize; if ( (arr = mem_realloc(*marray->arr, size1, m_marray)) == NULL) { logmsgx("marray_alloc(%s): mem_realloc for array failed", marray->name); error = -1; goto out; } *marray->arr = arr; marray->arr_size = size1; marray->nitems = new_nitems; marray->nfree = new_nfree; bitword = BIT_WORD(marray->bitmap, idx); bitmask = BIT_MASK(idx); } BIT_SET(bitword, bitmask); #ifdef WITH_MEMFUNC_DEBUG memset((char *)*marray->arr + idx * marray->isize, FILL_ALLOC, marray->isize); #endif marray->nfree--; *idxp = idx; out: marray->reqs++; UNLOCK_MARRAY(marray); return error; } /* * Return item with the given index to the marray, check if it is * possible to reduce size of marray. */ void marray_free(ipa_marray *marray, u_int idx) { BITMAP_TYPE *bitword, bitmask; if (marray == NULL) memfunc_panic("marray_free: NULL marray"); LOCK_MARRAY(marray); if (idx >= marray->nitems) memfunc_panic("marray_free(%s): index %u is out of range (0..%u)", marray->name, idx, marray->nitems - 1); bitword = BIT_WORD(marray->bitmap, idx); bitmask = BIT_MASK(idx); if (!BIT_TEST(bitword, bitmask)) memfunc_panic("marray_free(%s): item with index %u was not allocated", marray->name, idx); BIT_CLEAR(bitword, bitmask); #ifdef WITH_MEMFUNC_DEBUG memset((char *)(*marray->arr) + idx * marray->isize, FILL_FREE, marray->isize); #endif marray->nfree++; if (BIT_SLOT(idx) == BIT_SLOT(marray->nitems - 1) && *bitword == 0 && marray->nfree >= marray->nalloc) { u_int new_nitems, new_nfree; size_t size; void *arr; /* * Last bitword and it is free and there are enough * unused items -- try to decrease array. */ if (marray->nfree == marray->nitems) /* Easy case, all items are free. */ new_nitems = new_nfree = marray->nalloc; else { new_nitems = marray->nitems; new_nfree = marray->nfree; for (; *bitword == 0; --bitword) if (new_nfree - BIT_WORD_NBITS >= marray->nalloc) { /* Has enough unused items. */ new_nitems -= BIT_WORD_NBITS; new_nfree -= BIT_WORD_NBITS; } else break; } if (new_nitems < marray->nitems) { /* * Number of items was decreased, * so reallocate array and bitmap. */ size = new_nitems * marray->isize; if ( (arr = mem_realloc(*marray->arr, size, m_marray)) == NULL) logmsgx("marray_free(%s): mem_realloc for array failed", marray->name); else { *marray->arr = arr; marray->nitems = new_nitems; marray->nfree = new_nfree; marray->arr_size = size; /* * If can't reallocate bitmap, then it * will be bigger than we need. */ size = BITMAP_SIZE(new_nitems); if ( (bitword = mem_realloc(marray->bitmap, size, m_marray)) == NULL) logmsgx("marray_free(%s): mem_realloc for bitmap failed", marray->name); else { marray->bitmap_hint = (BITMAP_TYPE *)(bitword + (size_t)(marray->bitmap_hint - marray->bitmap)); marray->bitmap = bitword; marray->bitmap_size = size; } } } } else { if (marray->bitmap_hint > bitword) marray->bitmap_hint = bitword; } UNLOCK_MARRAY(marray); } /* * Reduce array if it uses more memory than needed. */ void marray_minimize(ipa_marray *marray) { u_int new_nitems, new_nfree; void *arr; size_t size; BITMAP_TYPE *bitword; if (marray == NULL) memfunc_panic("marray_minimize: NULL marray"); LOCK_MARRAY(marray); if (marray->nitems != marray->nfree) { /* * There are at least one used item in marray. * If marray is free why not to deinit it? */ new_nitems = marray->nitems; new_nfree = marray->nfree; for (bitword = marray->bitmap + BIT_SLOT(marray->nitems - 1); *bitword == 0; --bitword) { new_nitems -= BIT_WORD_NBITS; new_nfree -= BIT_WORD_NBITS; } size = new_nitems * marray->isize; if ( (arr = mem_realloc(*marray->arr, size, m_marray)) == NULL) logmsgx("marray_minimize(%s): mem_realloc for array failed", marray->name); else { *marray->arr = arr; marray->nitems = new_nitems; marray->nfree = new_nfree; marray->arr_size = size; /* * If can't realloc bitmap, then it will be bigger * than we need. */ size = BITMAP_SIZE(new_nitems); if ( (bitword = mem_realloc(marray->bitmap, size, m_marray)) == NULL) logmsgx("marray_minimize(%s): mem_realloc for bitmap failed", marray->name); else { marray->bitmap = marray->bitmap_hint = bitword; marray->bitmap_size = size; } } } UNLOCK_MARRAY(marray); } /* * Return 0 if item with the given index is not used, * otherwise return non-zero. */ int marray_check_index(ipa_marray *marray, u_int idx) { int ret; if (marray == NULL) memfunc_panic("marray_check_index: NULL marray"); LOCK_MARRAY(marray); ret = idx >= marray->nitems ? 0 : BIT_TEST(BIT_WORD(marray->bitmap, idx), BIT_MASK(idx)); UNLOCK_MARRAY(marray); return ret; } /* * Return number of allocated items in marray. */ u_int marray_nused(ipa_marray *marray) { u_int ret; if (marray == NULL) memfunc_panic("marray_nused: NULL marray"); LOCK_MARRAY(marray); ret = marray->nitems - marray->nfree; UNLOCK_MARRAY(marray); return ret; } void memfunc_pre_init(void) { #if defined(WITH_PTHREAD) && (defined(_SC_PAGE_SIZE) || defined(_SC_PAGESIZE)) char strerrbuf[STRERRBUF_SIZE]; #endif #if defined(_SC_PAGE_SIZE) errno = 0; if ( (pagesize = sysconf(_SC_PAGE_SIZE)) < 0) { logmsgx("memfunc_pre_init: sysconf(_SC_PAGE_SIZE): %s", my_strerror(errno)); pagesize = PAGESIZE_DEFAULT; } #elif defined(_SC_PAGESIZE) errno = 0; if ( (pagesize = sysconf(_SC_PAGESIZE)) < 0) { logmsgx("memfunc_pre_init: sysconf(_SC_PAGESIZE): %s", my_strerror(errno)); pagesize = PAGESIZE_DEFAULT; } #elif defined(PAGE_SIZE) pagesize = PAGE_SIZE #elif defined(PAGESIZE) pagesize = PAGESIZE; #else pagesize = PAGESIZE_DEFAULT; #endif } /* * Init mzone_mzone and marray_mzone zones. */ int memfunc_init(void) { static int first_call = 1; switch (memfunc_inited) { case 1: memfunc_panic("memfunc_init: memfunc was not completely inited previous time"); /* NOTREACHED */ case 2: return 0; } memfunc_inited = 1; #ifdef WITH_PTHREAD if (first_call) if (memfunc_mutexattr_init() < 0) { logmsgx("memfunc_init: cannot init default mutex attributes"); return -1; } #endif STAILQ_INIT(&mem_type_list); TAILQ_INIT(&mzone_list); TAILQ_INIT(&marray_list); if (mem_type_init_internal(&m_mzone, MTYPE_NAME(mzone), "Memory used by all mzones", MEMFUNC_INTERNAL_FLAGS) < 0) { logmsgx("memfunc_init: mem_type_init_internal(%s) failed", MTYPE_NAME(mzone)); return -1; } m_mzone.local = 1; if (mzone_init_internal(&mzone_mzone, MZONE_NAME(mzone), "Mzone structures", MEMFUNC_INTERNAL_FLAGS|MEMFLAG_OPTIMIZE, sizeof(ipa_mzone), MZONE_NSIZE, MZONE_NALLOC) < 0) { logmsgx("memfunc_init: mzone_init_internal(%s) failed", MZONE_NAME(mzone)); return -1; } if ( (mem_type_mzone = mzone_init(MZONE_NAME(mem_type), "Memory types structures", MEMFUNC_INTERNAL_FLAGS, sizeof(ipa_mem_type), MEM_TYPE_NSIZE, MEM_TYPE_NALLOC)) == NULL) { logmsgx("memfunc_init: mzone_init(%s) failed", MZONE_NAME(mem_type)); return -1; } if ( (marray_mzone = mzone_init(MZONE_NAME(marray), "Marray structures", MEMFUNC_INTERNAL_FLAGS|MEMFLAG_OPTIMIZE, sizeof(ipa_marray), MARRAY_NSIZE, MARRAY_NALLOC)) == NULL) { logmsgx("memfunc_init: mzone_init(%s) failed", MZONE_NAME(marray)); return -1; } if ( (m_marray = mem_type_new_local(MTYPE_NAME(marray), "Memory used by all marrays", MEMFUNC_INTERNAL_FLAGS|MEMFLAG_OPTIMIZE)) == NULL) { logmsgx("memfunc_init: mem_type_new_local(%s) failed", MTYPE_NAME(marray)); return -1; } memfunc_inited = 2; first_call = 0; return 0; } static void mem_types_deinit(int foreign, int local) { ipa_mem_type *mem_type; LOCK_MEM_TYPE_LIST(); STAILQ_FOREACH(mem_type, &mem_type_list, link) if (mem_type->local == local) { if (mem_type->size != 0 && !foreign) { logmsgx("mem_types_deinit(%d): memory leak: mem_type %s \"%s\" has not freed %lu bytes", local, mem_type->name, mem_type->desc, (u_long)mem_type->size); mem_size_cnt += mem_type->size; } mem_type_deinit_internal(mem_type); } UNLOCK_MEM_TYPE_LIST(); } /* * First step: deinit marrays and mzones (except mem_type_mzone and * mzone_mzone) and check nonlocal mem_types, we need to run this step * before unloading modules, since we need to access mzones', marrays' * and mem_types' names, which are hold in modules' address spaces. */ void memfunc_deinit_1(int foreign) { ipa_mzone *mzone; ipa_marray *marray; switch (memfunc_inited) { case 0: return; case 1: memfunc_panic("memfunc_deinit: memfunc was not completely inited, cannot deinit it"); } memfunc_inited = 0; while ( (marray = TAILQ_FIRST(&marray_list)) != NULL) { if (!foreign) logmsgx("memfunc_deinit_1: memory leak: marray %s \"%s\" was not deinited (%u used items)", marray->name, marray->desc, marray->nitems - marray->nfree); marray_deinit(marray); } mzone_deinit(marray_mzone); mem_size_cnt = 0; mem_types_deinit(foreign, 0); TAILQ_REMOVE(&mzone_list, mem_type_mzone, link); while ( (mzone = TAILQ_FIRST(&mzone_list)) != NULL) { if (!foreign) logmsgx("memfunc_deinit_1: memory leak, mzone %s \"%s\" was not deinited (%u used items)", mzone->name, mzone->desc, mzone->nused); mzone_deinit(mzone); } TAILQ_INSERT_HEAD(&mzone_list, mem_type_mzone, link); } /* * Second step: check local mem_types */ void memfunc_deinit_2(int foreign) { mem_types_deinit(foreign, 1); mzone_deinit(mem_type_mzone); mzone_deinit_internal(&mzone_mzone); if (m_mzone.size != 0 && !foreign) { logmsgx("memfunc_deinit_2: memory leak: mem_type %s \"%s\" has not freed %lu bytes", m_mzone.name, m_mzone.desc, (u_long)m_mzone.size); mem_size_cnt += m_mzone.size; } mem_type_deinit_internal(&m_mzone); if (mem_size_cnt != 0 && !foreign) { logmsgx("memfunc_deinit_2: memory leak: %lu bytes were not freed after reconfiguration", (u_long)mem_size_cnt); mem_size_cnt_all += mem_size_cnt; } if (!foreign) { if (mem_size_cnt_all != 0) logmsgx("memfunc_deinit_2: memory leak: %lu bytes were not freed after start", (u_long)mem_size_cnt_all); } else mem_size_cnt_all = 0; } /* * This function is called only from modules. Note, that this is not * a deep-free, so some memory will be not deallocated. */ static void memfunc_deinit(int foreign) { memfunc_deinit_1(foreign); memfunc_deinit_2(foreign); } #ifdef WITH_MEMFUNC_STAT #include "ipactl.h" extern void *m_ctl; int memfunc_get_stat(void **bufp, size_t *buf_sizep) { int flag; u_int nmem_type, nmzones, nmarrays; char *buf; size_t buf_size, size_cnt; ipa_mzone *mzone; ipa_marray *marray; ipa_mem_type *mem_type; struct ctl_cmd_answer_memory *answer_memory; struct ctl_cmd_answer_mzone *answer_mzone; struct ctl_cmd_answer_marray *answer_marray; struct ctl_cmd_answer_mem_type *answer_mem_type; buf_size = sizeof *answer_memory; if ( (buf = mem_malloc(sizeof *answer_memory, m_ctl)) == NULL) { logmsgx("memfunc_get_stat: mem_malloc failed"); return -1; } /* * Get stat for mem_types. */ nmem_type = 0; size_cnt = 0; LOCK_MEM_TYPE_LIST(); STAILQ_INSERT_HEAD(&mem_type_list, &m_mzone, link); STAILQ_FOREACH(mem_type, &mem_type_list, link) { if ( (buf = mem_realloc(buf, buf_size + sizeof *answer_mem_type, m_ctl)) == NULL) { STAILQ_REMOVE_HEAD(&mem_type_list, link); UNLOCK_MEM_TYPE_LIST(); logmsgx("memfunc_get_stat: mem_realloc failed"); goto failed; } answer_mem_type = (struct ctl_cmd_answer_mem_type *)(buf + buf_size); buf_size += sizeof *answer_mem_type; LOCK_MEM_TYPE(mem_type); strncpy(answer_mem_type->name, mem_type->name, sizeof(answer_mem_type->name) - 1); answer_mem_type->name[sizeof(answer_mem_type->name) - 1] = '\0'; strncpy(answer_mem_type->desc, mem_type->desc, sizeof(answer_mem_type->desc) - 1); answer_mem_type->desc[sizeof(answer_mem_type->desc) - 1] = '\0'; size_cnt += answer_mem_type->size = mem_type->size; answer_mem_type->reqs = mem_type->reqs; UNLOCK_MEM_TYPE(mem_type); ++nmem_type; } STAILQ_REMOVE_HEAD(&mem_type_list, link); UNLOCK_MEM_TYPE_LIST(); /* * Get stat for mzones. */ nmzones = 0; LOCK_MZONE_LIST(); TAILQ_INSERT_HEAD(&mzone_list, &mzone_mzone, link); flag = 0; TAILQ_FOREACH(mzone, &mzone_list, link) { if ( (buf = mem_realloc(buf, buf_size + sizeof *answer_mzone, m_ctl)) == NULL) { logmsgx("memfunc_get_stat: mem_realloc failed"); TAILQ_REMOVE(&mzone_list, &mzone_mzone, link); UNLOCK_MZONE_LIST(); goto failed; } answer_mzone = (struct ctl_cmd_answer_mzone *)(buf + buf_size); buf_size += sizeof *answer_mzone; if (flag) LOCK_MZONE(mzone); strncpy(answer_mzone->name, mzone->name, sizeof(answer_mzone->name) - 1); answer_mzone->name[sizeof(answer_mzone->name) - 1] = '\0'; strncpy(answer_mzone->desc, mzone->desc, sizeof(answer_mzone->desc) - 1); answer_mzone->desc[sizeof(answer_mzone->desc) - 1] = '\0'; answer_mzone->isize = mzone->isize; answer_mzone->nused = mzone->nused; answer_mzone->nfree = mzone->nfree; answer_mzone->pools_size = mzone->pools_size; answer_mzone->reqs = mzone->reqs; if (flag) UNLOCK_MZONE(mzone); else flag = 1; ++nmzones; } TAILQ_REMOVE(&mzone_list, &mzone_mzone, link); UNLOCK_MZONE_LIST(); /* * Get stat for marrays. */ nmarrays = 0; LOCK_MARRAY_LIST(); TAILQ_FOREACH(marray, &marray_list, link) { if ( (buf = mem_realloc(buf, buf_size + sizeof *answer_marray, m_ctl)) == NULL) { logmsgx("memfunc_get_stat: mem_realloc failed"); UNLOCK_MARRAY_LIST(); goto failed; } answer_marray = (struct ctl_cmd_answer_marray *)(buf + buf_size); buf_size += sizeof *answer_marray; LOCK_MARRAY(marray); strncpy(answer_marray->name, marray->name, sizeof(answer_marray->name) - 1); answer_marray->name[sizeof(answer_marray->name) - 1] = '\0'; strncpy(answer_marray->desc, marray->desc, sizeof(answer_marray->desc) - 1); answer_marray->desc[sizeof(answer_marray->desc) - 1] = '\0'; answer_marray->isize = marray->isize; answer_marray->nused = marray->nitems - marray->nfree; answer_marray->nfree = marray->nfree; answer_marray->arr_size = marray->arr_size; answer_marray->bitmap_size = marray->bitmap_size; answer_marray->reqs = marray->reqs; UNLOCK_MARRAY(marray); ++nmarrays; } UNLOCK_MARRAY_LIST(); answer_memory = (struct ctl_cmd_answer_memory *)buf; answer_memory->size_cnt = size_cnt + mem_size_cnt_all; answer_memory->nmem_type = nmem_type; answer_memory->nmzones = nmzones; answer_memory->nmarrays = nmarrays; *bufp = buf; *buf_sizep = buf_size; return 0; failed: mem_free(buf, m_ctl); return -1; } #endif /* WITH_MEMFUNC_STAT */ #ifdef MEMFUNC_STANDALONE static void vlogmsgx(const char *format, va_list ap) { fflush(stdout); fprintf(stderr, "ERROR: "); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); } static void show_bitmap(const char *msg, const BITMAP_TYPE *bitmap, size_t size) { u_int bit; const BITMAP_TYPE *bitword; if (msg != NULL) printf("%s\n", msg); for (bitword = bitmap; bitword < bitmap + size; ++bitword) { for (bit = 1 << (BIT_WORD_NBITS - 1); bit > 0; bit >>= 1) if (*bitword & bit) printf("1"); else printf("0"); printf(" "); } printf("\n"); } static void show_marray_bitmap(const char *msg, const struct marray *marray) { show_bitmap(msg, marray->bitmap, BITMAP_SIZE(marray->nitems) / sizeof(BITMAP_TYPE)); } int main(void) { mvlogmsgx = vlogmsgx; if (memfunc_init() < 0) return 1; memfunc_deinit(0); return 0; } #endif /* MEMFUNC_STANDALONE */ /* * This structure is exported to modules. */ ipa_memfunc memfunc = { IPA_MEMFUNC_API_VERSION,/* api_ver */ memfunc_init, /* memfunc_init */ memfunc_deinit, /* memfunc_deinit */ NULL, /* m_parser */ mem_type_new, /* mem_type_new */ mem_malloc, /* mem_malloc */ mem_calloc, /* mem_calloc */ mem_realloc, /* mem_realloc */ mem_strdup, /* mem_strdup */ mem_vasprintf, /* mem_vasprintf */ mem_free, /* mem_free */ marray_init, /* marray_init */ marray_deinit, /* marray_deinit */ marray_alloc, /* marray_alloc */ marray_free, /* marray_free */ marray_minimize, /* marray_minimize */ marray_check_index, /* marray_check_index */ marray_nused, /* marray_nused */ mzone_init, /* mzone_init */ mzone_deinit, /* mzone_deinit */ mzone_alloc, /* mzone_alloc */ mzone_free, /* mzone_free */ mzone_nused /* mzone_nused */ };