/*-
* 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 <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <unistd.h>
#include "ipa_mod.h"
#ifdef WITH_PTHREAD
# include <pthread.h>
# 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 */
};
syntax highlighted by Code2HTML, v. 0.9.1