2007-05-13 16:26:08 +00:00
|
|
|
/*
|
|
|
|
* Memory management functions.
|
|
|
|
*
|
|
|
|
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-01-28 15:49:56 +00:00
|
|
|
#include <types/global.h>
|
2007-05-13 16:26:08 +00:00
|
|
|
#include <common/config.h>
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 21:53:44 +00:00
|
|
|
#include <common/debug.h>
|
2007-05-13 16:26:08 +00:00
|
|
|
#include <common/memory.h>
|
|
|
|
#include <common/mini-clist.h>
|
|
|
|
#include <common/standard.h>
|
|
|
|
|
|
|
|
#include <proto/log.h>
|
|
|
|
|
|
|
|
static struct list pools = LIST_HEAD_INIT(pools);
|
2015-10-08 12:12:13 +00:00
|
|
|
int mem_poison_byte = -1;
|
2007-05-13 16:26:08 +00:00
|
|
|
|
|
|
|
/* Try to find an existing shared pool with the same characteristics and
|
|
|
|
* returns it, otherwise creates this one. NULL is returned if no memory
|
|
|
|
* is available for a new creation.
|
|
|
|
*/
|
|
|
|
struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
|
|
|
|
{
|
|
|
|
struct pool_head *pool;
|
2007-05-13 22:16:13 +00:00
|
|
|
struct pool_head *entry;
|
|
|
|
struct list *start;
|
2007-05-13 16:26:08 +00:00
|
|
|
unsigned int align;
|
|
|
|
|
|
|
|
/* We need to store at least a (void *) in the chunks. Since we know
|
|
|
|
* that the malloc() function will never return such a small size,
|
|
|
|
* let's round the size up to something slightly bigger, in order to
|
|
|
|
* ease merging of entries. Note that the rounding is a power of two.
|
|
|
|
*/
|
|
|
|
|
2007-05-13 22:16:13 +00:00
|
|
|
align = 16;
|
2007-05-13 16:26:08 +00:00
|
|
|
size = (size + align - 1) & -align;
|
|
|
|
|
2007-05-13 22:16:13 +00:00
|
|
|
start = &pools;
|
2007-05-13 16:26:08 +00:00
|
|
|
pool = NULL;
|
2007-05-13 22:16:13 +00:00
|
|
|
|
|
|
|
list_for_each_entry(entry, &pools, list) {
|
|
|
|
if (entry->size == size) {
|
|
|
|
/* either we can share this place and we take it, or
|
|
|
|
* we look for a sharable one or for the next position
|
|
|
|
* before which we will insert a new one.
|
|
|
|
*/
|
|
|
|
if (flags & entry->flags & MEM_F_SHARED) {
|
|
|
|
/* we can share this one */
|
2007-05-13 16:26:08 +00:00
|
|
|
pool = entry;
|
[MEDIUM] Fix memory freeing at exit
New functions implemented:
- deinit_pollers: called at the end of deinit())
- prune_acl: called via list_for_each_entry_safe
Add missing pool_destroy2 calls:
- p->hdr_idx_pool
- pool2_tree64
Implement all task stopping:
- health-check: needs new "struct task" in the struct server
- queue processing: queue_mgt
- appsess_refresh: appsession_refresh
before (idle system):
==6079== LEAK SUMMARY:
==6079== definitely lost: 1,112 bytes in 75 blocks.
==6079== indirectly lost: 53,356 bytes in 2,090 blocks.
==6079== possibly lost: 52 bytes in 1 blocks.
==6079== still reachable: 150,996 bytes in 504 blocks.
==6079== suppressed: 0 bytes in 0 blocks.
after (idle system):
==6945== LEAK SUMMARY:
==6945== definitely lost: 7,644 bytes in 137 blocks.
==6945== indirectly lost: 9,913 bytes in 587 blocks.
==6945== possibly lost: 0 bytes in 0 blocks.
==6945== still reachable: 0 bytes in 0 blocks.
==6945== suppressed: 0 bytes in 0 blocks.
before (running system for ~2m):
==9343== LEAK SUMMARY:
==9343== definitely lost: 1,112 bytes in 75 blocks.
==9343== indirectly lost: 54,199 bytes in 2,122 blocks.
==9343== possibly lost: 52 bytes in 1 blocks.
==9343== still reachable: 151,128 bytes in 509 blocks.
==9343== suppressed: 0 bytes in 0 blocks.
after (running system for ~2m):
==11616== LEAK SUMMARY:
==11616== definitely lost: 7,644 bytes in 137 blocks.
==11616== indirectly lost: 9,981 bytes in 591 blocks.
==11616== possibly lost: 0 bytes in 0 blocks.
==11616== still reachable: 4 bytes in 1 blocks.
==11616== suppressed: 0 bytes in 0 blocks.
Still not perfect but significant improvement.
2008-05-29 21:53:44 +00:00
|
|
|
DPRINTF(stderr, "Sharing %s with %s\n", name, pool->name);
|
2007-05-13 16:26:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-05-13 22:16:13 +00:00
|
|
|
else if (entry->size > size) {
|
|
|
|
/* insert before this one */
|
|
|
|
start = &entry->list;
|
|
|
|
break;
|
|
|
|
}
|
2007-05-13 16:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pool) {
|
|
|
|
pool = CALLOC(1, sizeof(*pool));
|
|
|
|
if (!pool)
|
|
|
|
return NULL;
|
|
|
|
if (name)
|
|
|
|
strlcpy2(pool->name, name, sizeof(pool->name));
|
|
|
|
pool->size = size;
|
|
|
|
pool->flags = flags;
|
2007-05-13 22:16:13 +00:00
|
|
|
LIST_ADDQ(start, &pool->list);
|
2007-05-13 16:26:08 +00:00
|
|
|
}
|
2007-05-13 22:16:13 +00:00
|
|
|
pool->users++;
|
2007-05-13 16:26:08 +00:00
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
2014-12-03 14:25:28 +00:00
|
|
|
/* Allocates new entries for pool <pool> until there are at least <avail> + 1
|
|
|
|
* available, then returns the last one for immediate use, so that at least
|
|
|
|
* <avail> are left available in the pool upon return. NULL is returned if the
|
|
|
|
* last entry could not be allocated. It's important to note that at least one
|
|
|
|
* allocation is always performed even if there are enough entries in the pool.
|
|
|
|
* A call to the garbage collector is performed at most once in case malloc()
|
|
|
|
* returns an error, before returning NULL.
|
2007-05-13 16:26:08 +00:00
|
|
|
*/
|
2014-12-03 14:25:28 +00:00
|
|
|
void *pool_refill_alloc(struct pool_head *pool, unsigned int avail)
|
2007-05-13 16:26:08 +00:00
|
|
|
{
|
2014-12-03 14:25:28 +00:00
|
|
|
void *ptr = NULL;
|
|
|
|
int failed = 0;
|
|
|
|
|
|
|
|
/* stop point */
|
|
|
|
avail += pool->used;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (pool->limit && pool->allocated >= pool->limit)
|
2007-05-13 22:16:13 +00:00
|
|
|
return NULL;
|
2014-12-03 14:25:28 +00:00
|
|
|
|
|
|
|
ptr = MALLOC(pool->size);
|
|
|
|
if (!ptr) {
|
|
|
|
if (failed)
|
|
|
|
return NULL;
|
|
|
|
failed++;
|
|
|
|
pool_gc2();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (++pool->allocated > avail)
|
|
|
|
break;
|
|
|
|
|
|
|
|
*(void **)ptr = (void *)pool->free_list;
|
|
|
|
pool->free_list = ptr;
|
2007-05-13 22:16:13 +00:00
|
|
|
}
|
2007-05-13 16:26:08 +00:00
|
|
|
pool->used++;
|
2014-12-03 14:25:28 +00:00
|
|
|
return ptr;
|
2007-05-13 16:26:08 +00:00
|
|
|
}
|
|
|
|
|
2007-05-13 17:38:49 +00:00
|
|
|
/*
|
|
|
|
* This function frees whatever can be freed in pool <pool>.
|
|
|
|
*/
|
|
|
|
void pool_flush2(struct pool_head *pool)
|
|
|
|
{
|
|
|
|
void *temp, *next;
|
2007-05-13 22:39:29 +00:00
|
|
|
if (!pool)
|
|
|
|
return;
|
|
|
|
|
2007-05-13 17:38:49 +00:00
|
|
|
next = pool->free_list;
|
|
|
|
while (next) {
|
|
|
|
temp = next;
|
|
|
|
next = *(void **)temp;
|
|
|
|
pool->allocated--;
|
|
|
|
FREE(temp);
|
|
|
|
}
|
|
|
|
pool->free_list = next;
|
|
|
|
|
|
|
|
/* here, we should have pool->allocate == pool->used */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function frees whatever can be freed in all pools, but respecting
|
2009-04-21 00:17:45 +00:00
|
|
|
* the minimum thresholds imposed by owners. It takes care of avoiding
|
|
|
|
* recursion because it may be called from a signal handler.
|
2007-05-13 17:38:49 +00:00
|
|
|
*/
|
|
|
|
void pool_gc2()
|
|
|
|
{
|
2009-04-21 00:17:45 +00:00
|
|
|
static int recurse;
|
2007-05-13 17:38:49 +00:00
|
|
|
struct pool_head *entry;
|
2009-04-21 00:17:45 +00:00
|
|
|
|
|
|
|
if (recurse++)
|
|
|
|
goto out;
|
|
|
|
|
2007-05-13 17:38:49 +00:00
|
|
|
list_for_each_entry(entry, &pools, list) {
|
|
|
|
void *temp, *next;
|
|
|
|
//qfprintf(stderr, "Flushing pool %s\n", entry->name);
|
|
|
|
next = entry->free_list;
|
|
|
|
while (next &&
|
2014-12-22 20:40:55 +00:00
|
|
|
(int)(entry->allocated - entry->used) > (int)entry->minavail) {
|
2007-05-13 17:38:49 +00:00
|
|
|
temp = next;
|
|
|
|
next = *(void **)temp;
|
|
|
|
entry->allocated--;
|
|
|
|
FREE(temp);
|
|
|
|
}
|
|
|
|
entry->free_list = next;
|
|
|
|
}
|
2009-04-21 00:17:45 +00:00
|
|
|
out:
|
|
|
|
recurse--;
|
2007-05-13 17:38:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-06-16 21:19:53 +00:00
|
|
|
* This function destroys a pool by freeing it completely, unless it's still
|
|
|
|
* in use. This should be called only under extreme circumstances. It always
|
|
|
|
* returns NULL if the resulting pool is empty, easing the clearing of the old
|
|
|
|
* pointer, otherwise it returns the pool.
|
|
|
|
* .
|
2007-05-13 17:38:49 +00:00
|
|
|
*/
|
2007-05-13 22:39:29 +00:00
|
|
|
void *pool_destroy2(struct pool_head *pool)
|
2007-05-13 17:38:49 +00:00
|
|
|
{
|
2007-05-13 22:39:29 +00:00
|
|
|
if (pool) {
|
|
|
|
pool_flush2(pool);
|
2007-06-16 21:19:53 +00:00
|
|
|
if (pool->used)
|
|
|
|
return pool;
|
|
|
|
pool->users--;
|
|
|
|
if (!pool->users) {
|
|
|
|
LIST_DEL(&pool->list);
|
|
|
|
FREE(pool);
|
|
|
|
}
|
2007-05-13 22:39:29 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
2007-05-13 17:38:49 +00:00
|
|
|
}
|
|
|
|
|
2014-01-28 15:49:56 +00:00
|
|
|
/* This function dumps memory usage information into the trash buffer. */
|
|
|
|
void dump_pools_to_trash()
|
2007-05-13 16:26:08 +00:00
|
|
|
{
|
|
|
|
struct pool_head *entry;
|
|
|
|
unsigned long allocated, used;
|
|
|
|
int nbpools;
|
|
|
|
|
|
|
|
allocated = used = nbpools = 0;
|
2014-01-28 15:49:56 +00:00
|
|
|
chunk_printf(&trash, "Dumping pools usage. Use SIGQUIT to flush them.\n");
|
2007-05-13 16:26:08 +00:00
|
|
|
list_for_each_entry(entry, &pools, list) {
|
2014-01-28 15:49:56 +00:00
|
|
|
chunk_appendf(&trash, " - Pool %s (%d bytes) : %d allocated (%u bytes), %d used, %d users%s\n",
|
2007-05-13 17:38:49 +00:00
|
|
|
entry->name, entry->size, entry->allocated,
|
|
|
|
entry->size * entry->allocated, entry->used,
|
2007-05-13 22:16:13 +00:00
|
|
|
entry->users, (entry->flags & MEM_F_SHARED) ? " [SHARED]" : "");
|
2007-05-13 16:26:08 +00:00
|
|
|
|
|
|
|
allocated += entry->allocated * entry->size;
|
|
|
|
used += entry->used * entry->size;
|
|
|
|
nbpools++;
|
|
|
|
}
|
2014-01-28 15:49:56 +00:00
|
|
|
chunk_appendf(&trash, "Total: %d pools, %lu bytes allocated, %lu used.\n",
|
2007-05-13 16:26:08 +00:00
|
|
|
nbpools, allocated, used);
|
|
|
|
}
|
|
|
|
|
2014-01-28 15:49:56 +00:00
|
|
|
/* Dump statistics on pools usage. */
|
|
|
|
void dump_pools(void)
|
|
|
|
{
|
|
|
|
dump_pools_to_trash();
|
|
|
|
qfprintf(stderr, "%s", trash.str);
|
|
|
|
}
|
|
|
|
|
2007-05-13 16:26:08 +00:00
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-indent-level: 8
|
|
|
|
* c-basic-offset: 8
|
|
|
|
* End:
|
|
|
|
*/
|