2010-01-17 01:53:50 +08:00
|
|
|
#define JEMALLOC_CHUNK_C_
|
2010-02-12 06:45:59 +08:00
|
|
|
#include "jemalloc/internal/jemalloc_internal.h"
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/* Data. */
|
|
|
|
|
|
|
|
size_t opt_lg_chunk = LG_CHUNK_DEFAULT;
|
|
|
|
|
2010-01-28 05:10:55 +08:00
|
|
|
malloc_mutex_t chunks_mtx;
|
2010-01-17 01:53:50 +08:00
|
|
|
chunk_stats_t stats_chunks;
|
|
|
|
|
2012-04-13 11:20:58 +08:00
|
|
|
/*
|
|
|
|
* Trees of chunks that were previously allocated (trees differ only in node
|
|
|
|
* ordering). These are used when allocating chunks, in an attempt to re-use
|
|
|
|
* address space. Depending on function, different tree orderings are needed,
|
|
|
|
* which is why there are two trees with the same contents.
|
|
|
|
*/
|
|
|
|
static extent_tree_t chunks_szad;
|
|
|
|
static extent_tree_t chunks_ad;
|
|
|
|
|
2010-09-06 01:35:13 +08:00
|
|
|
rtree_t *chunks_rtree;
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
/* Various chunk-related settings. */
|
|
|
|
size_t chunksize;
|
|
|
|
size_t chunksize_mask; /* (chunksize - 1). */
|
|
|
|
size_t chunk_npages;
|
2010-10-02 08:35:43 +08:00
|
|
|
size_t map_bias;
|
2010-01-17 01:53:50 +08:00
|
|
|
size_t arena_maxclass; /* Max size class for arenas. */
|
|
|
|
|
|
|
|
/******************************************************************************/
|
2012-04-13 11:20:58 +08:00
|
|
|
/* Function prototypes for non-inline static functions. */
|
|
|
|
|
|
|
|
static void *chunk_recycle(size_t size, size_t alignment, bool *zero);
|
|
|
|
static void chunk_record(void *chunk, size_t size);
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
static void *
|
|
|
|
chunk_recycle(size_t size, size_t alignment, bool *zero)
|
|
|
|
{
|
|
|
|
void *ret;
|
|
|
|
extent_node_t *node;
|
|
|
|
extent_node_t key;
|
|
|
|
size_t alloc_size, leadsize, trailsize;
|
|
|
|
|
|
|
|
alloc_size = size + alignment - chunksize;
|
|
|
|
/* Beware size_t wrap-around. */
|
|
|
|
if (alloc_size < size)
|
|
|
|
return (NULL);
|
|
|
|
key.addr = NULL;
|
|
|
|
key.size = alloc_size;
|
|
|
|
malloc_mutex_lock(&chunks_mtx);
|
|
|
|
node = extent_tree_szad_nsearch(&chunks_szad, &key);
|
|
|
|
if (node == NULL) {
|
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
leadsize = ALIGNMENT_CEILING((uintptr_t)node->addr, alignment) -
|
|
|
|
(uintptr_t)node->addr;
|
|
|
|
assert(alloc_size >= leadsize + size);
|
|
|
|
trailsize = alloc_size - leadsize - size;
|
|
|
|
ret = (void *)((uintptr_t)node->addr + leadsize);
|
|
|
|
/* Remove node from the tree. */
|
|
|
|
extent_tree_szad_remove(&chunks_szad, node);
|
|
|
|
extent_tree_ad_remove(&chunks_ad, node);
|
|
|
|
if (leadsize != 0) {
|
|
|
|
/* Insert the leading space as a smaller chunk. */
|
|
|
|
node->size = leadsize;
|
|
|
|
extent_tree_szad_insert(&chunks_szad, node);
|
|
|
|
extent_tree_ad_insert(&chunks_ad, node);
|
|
|
|
node = NULL;
|
|
|
|
}
|
|
|
|
if (trailsize != 0) {
|
|
|
|
/* Insert the trailing space as a smaller chunk. */
|
|
|
|
if (node == NULL) {
|
|
|
|
/*
|
|
|
|
* An additional node is required, but
|
|
|
|
* base_node_alloc() can cause a new base chunk to be
|
|
|
|
* allocated. Drop chunks_mtx in order to avoid
|
|
|
|
* deadlock, and if node allocation fails, deallocate
|
|
|
|
* the result before returning an error.
|
|
|
|
*/
|
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
|
|
|
node = base_node_alloc();
|
|
|
|
if (node == NULL) {
|
|
|
|
chunk_dealloc(ret, size, true);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
malloc_mutex_lock(&chunks_mtx);
|
|
|
|
}
|
|
|
|
node->addr = (void *)((uintptr_t)(ret) + size);
|
|
|
|
node->size = trailsize;
|
|
|
|
extent_tree_szad_insert(&chunks_szad, node);
|
|
|
|
extent_tree_ad_insert(&chunks_ad, node);
|
|
|
|
node = NULL;
|
|
|
|
}
|
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
|
|
|
|
|
|
|
if (node != NULL)
|
|
|
|
base_node_dealloc(node);
|
|
|
|
#ifdef JEMALLOC_PURGE_MADVISE_FREE
|
|
|
|
if (*zero) {
|
|
|
|
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
|
|
|
memset(ret, 0, size);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return (ret);
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2010-01-25 09:13:07 +08:00
|
|
|
/*
|
|
|
|
* If the caller specifies (*zero == false), it is still possible to receive
|
|
|
|
* zeroed memory, in which case *zero is toggled to true. arena_chunk_alloc()
|
|
|
|
* takes advantage of this to avoid demanding zeroed chunks, but taking
|
|
|
|
* advantage of them if they are returned.
|
|
|
|
*/
|
2010-01-17 01:53:50 +08:00
|
|
|
void *
|
2012-04-11 01:50:33 +08:00
|
|
|
chunk_alloc(size_t size, size_t alignment, bool base, bool *zero)
|
2010-01-17 01:53:50 +08:00
|
|
|
{
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
assert(size != 0);
|
|
|
|
assert((size & chunksize_mask) == 0);
|
2012-04-11 01:50:33 +08:00
|
|
|
assert((alignment & chunksize_mask) == 0);
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2012-04-13 11:20:58 +08:00
|
|
|
ret = chunk_recycle(size, alignment, zero);
|
|
|
|
if (ret != NULL)
|
|
|
|
goto label_return;
|
2012-02-14 02:56:17 +08:00
|
|
|
if (config_dss) {
|
2012-04-11 01:50:33 +08:00
|
|
|
ret = chunk_alloc_dss(size, alignment, zero);
|
2010-01-24 18:53:40 +08:00
|
|
|
if (ret != NULL)
|
2012-04-11 06:07:44 +08:00
|
|
|
goto label_return;
|
2010-01-17 01:53:50 +08:00
|
|
|
}
|
2012-04-11 01:50:33 +08:00
|
|
|
ret = chunk_alloc_mmap(size, alignment);
|
2012-02-14 02:56:17 +08:00
|
|
|
if (ret != NULL) {
|
|
|
|
*zero = true;
|
2012-04-11 06:07:44 +08:00
|
|
|
goto label_return;
|
2010-01-24 18:53:40 +08:00
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/* All strategies for allocation failed. */
|
|
|
|
ret = NULL;
|
2012-04-11 06:07:44 +08:00
|
|
|
label_return:
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_ivsalloc && base == false && ret != NULL) {
|
2010-09-06 01:35:13 +08:00
|
|
|
if (rtree_set(chunks_rtree, (uintptr_t)ret, ret)) {
|
2011-11-12 06:41:59 +08:00
|
|
|
chunk_dealloc(ret, size, true);
|
2010-09-06 01:35:13 +08:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
}
|
2012-02-11 12:22:09 +08:00
|
|
|
if ((config_stats || config_prof) && ret != NULL) {
|
2010-10-24 09:37:06 +08:00
|
|
|
bool gdump;
|
2010-01-28 05:10:55 +08:00
|
|
|
malloc_mutex_lock(&chunks_mtx);
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_stats)
|
|
|
|
stats_chunks.nchunks += (size / chunksize);
|
2010-01-17 01:53:50 +08:00
|
|
|
stats_chunks.curchunks += (size / chunksize);
|
2010-02-11 02:37:56 +08:00
|
|
|
if (stats_chunks.curchunks > stats_chunks.highchunks) {
|
2010-01-28 05:10:55 +08:00
|
|
|
stats_chunks.highchunks = stats_chunks.curchunks;
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_prof)
|
|
|
|
gdump = true;
|
|
|
|
} else if (config_prof)
|
2010-10-24 09:37:06 +08:00
|
|
|
gdump = false;
|
2010-01-28 05:10:55 +08:00
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_prof && opt_prof && opt_prof_gdump && gdump)
|
2010-10-24 09:37:06 +08:00
|
|
|
prof_gdump();
|
2010-01-17 01:53:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(CHUNK_ADDR2BASE(ret) == ret);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2012-04-13 11:20:58 +08:00
|
|
|
static void
|
|
|
|
chunk_record(void *chunk, size_t size)
|
|
|
|
{
|
|
|
|
extent_node_t *xnode, *node, *prev, key;
|
|
|
|
|
2012-04-19 00:29:43 +08:00
|
|
|
pages_purge(chunk, size);
|
2012-04-13 11:20:58 +08:00
|
|
|
|
|
|
|
xnode = NULL;
|
|
|
|
malloc_mutex_lock(&chunks_mtx);
|
|
|
|
while (true) {
|
|
|
|
key.addr = (void *)((uintptr_t)chunk + size);
|
|
|
|
node = extent_tree_ad_nsearch(&chunks_ad, &key);
|
|
|
|
/* Try to coalesce forward. */
|
|
|
|
if (node != NULL && node->addr == key.addr) {
|
|
|
|
/*
|
|
|
|
* Coalesce chunk with the following address range.
|
|
|
|
* This does not change the position within chunks_ad,
|
|
|
|
* so only remove/insert from/into chunks_szad.
|
|
|
|
*/
|
|
|
|
extent_tree_szad_remove(&chunks_szad, node);
|
|
|
|
node->addr = chunk;
|
|
|
|
node->size += size;
|
|
|
|
extent_tree_szad_insert(&chunks_szad, node);
|
|
|
|
break;
|
|
|
|
} else if (xnode == NULL) {
|
|
|
|
/*
|
|
|
|
* It is possible that base_node_alloc() will cause a
|
|
|
|
* new base chunk to be allocated, so take care not to
|
|
|
|
* deadlock on chunks_mtx, and recover if another thread
|
|
|
|
* deallocates an adjacent chunk while this one is busy
|
|
|
|
* allocating xnode.
|
|
|
|
*/
|
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
|
|
|
xnode = base_node_alloc();
|
|
|
|
if (xnode == NULL)
|
|
|
|
return;
|
|
|
|
malloc_mutex_lock(&chunks_mtx);
|
|
|
|
} else {
|
|
|
|
/* Coalescing forward failed, so insert a new node. */
|
|
|
|
node = xnode;
|
|
|
|
xnode = NULL;
|
|
|
|
node->addr = chunk;
|
|
|
|
node->size = size;
|
|
|
|
extent_tree_ad_insert(&chunks_ad, node);
|
|
|
|
extent_tree_szad_insert(&chunks_szad, node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Discard xnode if it ended up unused due to a race. */
|
|
|
|
if (xnode != NULL)
|
|
|
|
base_node_dealloc(xnode);
|
|
|
|
|
|
|
|
/* Try to coalesce backward. */
|
|
|
|
prev = extent_tree_ad_prev(&chunks_ad, node);
|
|
|
|
if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
|
|
|
|
chunk) {
|
|
|
|
/*
|
|
|
|
* Coalesce chunk with the previous address range. This does
|
|
|
|
* not change the position within chunks_ad, so only
|
|
|
|
* remove/insert node from/into chunks_szad.
|
|
|
|
*/
|
|
|
|
extent_tree_szad_remove(&chunks_szad, prev);
|
|
|
|
extent_tree_ad_remove(&chunks_ad, prev);
|
|
|
|
|
|
|
|
extent_tree_szad_remove(&chunks_szad, node);
|
|
|
|
node->addr = prev->addr;
|
|
|
|
node->size += prev->size;
|
|
|
|
extent_tree_szad_insert(&chunks_szad, node);
|
|
|
|
|
|
|
|
base_node_dealloc(prev);
|
|
|
|
}
|
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
|
|
|
}
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
void
|
2011-11-12 06:41:59 +08:00
|
|
|
chunk_dealloc(void *chunk, size_t size, bool unmap)
|
2010-01-17 01:53:50 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
assert(chunk != NULL);
|
|
|
|
assert(CHUNK_ADDR2BASE(chunk) == chunk);
|
|
|
|
assert(size != 0);
|
|
|
|
assert((size & chunksize_mask) == 0);
|
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_ivsalloc)
|
|
|
|
rtree_set(chunks_rtree, (uintptr_t)chunk, NULL);
|
|
|
|
if (config_stats || config_prof) {
|
|
|
|
malloc_mutex_lock(&chunks_mtx);
|
|
|
|
stats_chunks.curchunks -= (size / chunksize);
|
|
|
|
malloc_mutex_unlock(&chunks_mtx);
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2011-11-12 06:41:59 +08:00
|
|
|
if (unmap) {
|
2012-04-13 11:20:58 +08:00
|
|
|
if (chunk_dealloc_mmap(chunk, size) == false)
|
2011-11-12 06:41:59 +08:00
|
|
|
return;
|
2012-04-13 11:20:58 +08:00
|
|
|
chunk_record(chunk, size);
|
2011-11-12 06:41:59 +08:00
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2012-03-22 09:33:03 +08:00
|
|
|
chunk_boot0(void)
|
2010-01-17 01:53:50 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
/* Set variables according to the value of opt_lg_chunk. */
|
2010-09-06 01:35:13 +08:00
|
|
|
chunksize = (ZU(1) << opt_lg_chunk);
|
2012-04-02 22:04:34 +08:00
|
|
|
assert(chunksize >= PAGE);
|
2010-01-17 01:53:50 +08:00
|
|
|
chunksize_mask = chunksize - 1;
|
2012-04-02 22:04:34 +08:00
|
|
|
chunk_npages = (chunksize >> LG_PAGE);
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_stats || config_prof) {
|
|
|
|
if (malloc_mutex_init(&chunks_mtx))
|
|
|
|
return (true);
|
|
|
|
memset(&stats_chunks, 0, sizeof(chunk_stats_t));
|
|
|
|
}
|
|
|
|
if (config_dss && chunk_dss_boot())
|
2010-01-17 01:53:50 +08:00
|
|
|
return (true);
|
2012-04-13 11:20:58 +08:00
|
|
|
extent_tree_szad_new(&chunks_szad);
|
|
|
|
extent_tree_ad_new(&chunks_ad);
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_ivsalloc) {
|
|
|
|
chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) -
|
|
|
|
opt_lg_chunk);
|
|
|
|
if (chunks_rtree == NULL)
|
|
|
|
return (true);
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
return (false);
|
|
|
|
}
|
2012-03-22 09:33:03 +08:00
|
|
|
|
|
|
|
bool
|
|
|
|
chunk_boot1(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (chunk_mmap_boot())
|
|
|
|
return (true);
|
|
|
|
|
|
|
|
return (false);
|
|
|
|
}
|