2010-01-17 01:53:50 +08:00
|
|
|
#define JEMALLOC_HUGE_C_
|
2010-02-12 06:45:59 +08:00
|
|
|
#include "jemalloc/internal/jemalloc_internal.h"
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/* Data. */
|
|
|
|
|
|
|
|
uint64_t huge_nmalloc;
|
|
|
|
uint64_t huge_ndalloc;
|
|
|
|
size_t huge_allocated;
|
|
|
|
|
|
|
|
malloc_mutex_t huge_mtx;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/* Tree of chunks that are stand-alone huge allocations. */
|
|
|
|
static extent_tree_t huge;
|
|
|
|
|
|
|
|
void *
|
|
|
|
huge_malloc(size_t size, bool zero)
|
2012-04-11 01:50:33 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
return (huge_palloc(size, chunksize, zero));
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
huge_palloc(size_t size, size_t alignment, bool zero)
|
2010-01-17 01:53:50 +08:00
|
|
|
{
|
|
|
|
void *ret;
|
|
|
|
size_t csize;
|
|
|
|
extent_node_t *node;
|
2012-04-22 07:04:51 +08:00
|
|
|
bool is_zeroed;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/* Allocate one or more contiguous chunks for this request. */
|
|
|
|
|
|
|
|
csize = CHUNK_CEILING(size);
|
|
|
|
if (csize == 0) {
|
|
|
|
/* size is large enough to cause size_t wrap-around. */
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate an extent node with which to track the chunk. */
|
|
|
|
node = base_node_alloc();
|
|
|
|
if (node == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
2012-04-22 07:04:51 +08:00
|
|
|
/*
|
|
|
|
* Copy zero into is_zeroed and pass the copy to chunk_alloc(), so that
|
|
|
|
* it is possible to make correct junk/zero fill decisions below.
|
|
|
|
*/
|
|
|
|
is_zeroed = zero;
|
2012-10-12 04:53:15 +08:00
|
|
|
ret = chunk_alloc(csize, alignment, false, &is_zeroed,
|
|
|
|
chunk_dss_prec_get());
|
2010-01-17 01:53:50 +08:00
|
|
|
if (ret == NULL) {
|
|
|
|
base_node_dealloc(node);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Insert node into huge. */
|
|
|
|
node->addr = ret;
|
|
|
|
node->size = csize;
|
|
|
|
|
|
|
|
malloc_mutex_lock(&huge_mtx);
|
|
|
|
extent_tree_ad_insert(&huge, node);
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_stats) {
|
|
|
|
stats_cactive_add(csize);
|
|
|
|
huge_nmalloc++;
|
|
|
|
huge_allocated += csize;
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
malloc_mutex_unlock(&huge_mtx);
|
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_fill && zero == false) {
|
2010-01-17 01:53:50 +08:00
|
|
|
if (opt_junk)
|
|
|
|
memset(ret, 0xa5, csize);
|
2012-04-22 07:04:51 +08:00
|
|
|
else if (opt_zero && is_zeroed == false)
|
2010-01-17 01:53:50 +08:00
|
|
|
memset(ret, 0, csize);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra)
|
2010-01-17 01:53:50 +08:00
|
|
|
{
|
|
|
|
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
/*
|
|
|
|
* Avoid moving the allocation if the size class can be left the same.
|
|
|
|
*/
|
|
|
|
if (oldsize > arena_maxclass
|
|
|
|
&& CHUNK_CEILING(oldsize) >= CHUNK_CEILING(size)
|
|
|
|
&& CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) {
|
|
|
|
assert(CHUNK_CEILING(oldsize) == oldsize);
|
2010-01-17 01:53:50 +08:00
|
|
|
return (ptr);
|
|
|
|
}
|
|
|
|
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
/* Reallocation would require a move. */
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
2012-10-12 04:53:15 +08:00
|
|
|
size_t alignment, bool zero, bool try_tcache_dalloc)
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
{
|
|
|
|
void *ret;
|
|
|
|
size_t copysize;
|
|
|
|
|
|
|
|
/* Try to avoid moving the allocation. */
|
|
|
|
ret = huge_ralloc_no_move(ptr, oldsize, size, extra);
|
|
|
|
if (ret != NULL)
|
|
|
|
return (ret);
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
/*
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
* size and oldsize are different enough that we need to use a
|
|
|
|
* different size class. In that case, fall back to allocating new
|
|
|
|
* space and copying.
|
2010-01-17 01:53:50 +08:00
|
|
|
*/
|
2011-02-01 11:58:22 +08:00
|
|
|
if (alignment > chunksize)
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
ret = huge_palloc(size + extra, alignment, zero);
|
|
|
|
else
|
|
|
|
ret = huge_malloc(size + extra, zero);
|
|
|
|
|
|
|
|
if (ret == NULL) {
|
|
|
|
if (extra == 0)
|
|
|
|
return (NULL);
|
|
|
|
/* Try again, this time without extra. */
|
2011-02-01 11:58:22 +08:00
|
|
|
if (alignment > chunksize)
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
ret = huge_palloc(size, alignment, zero);
|
|
|
|
else
|
|
|
|
ret = huge_malloc(size, zero);
|
|
|
|
|
|
|
|
if (ret == NULL)
|
|
|
|
return (NULL);
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
/*
|
|
|
|
* Copy at most size bytes (not size+extra), since the caller has no
|
|
|
|
* expectation that the extra bytes will be reliably preserved.
|
|
|
|
*/
|
2010-01-17 01:53:50 +08:00
|
|
|
copysize = (size < oldsize) ? size : oldsize;
|
2010-12-01 08:50:58 +08:00
|
|
|
|
2012-05-10 07:12:00 +08:00
|
|
|
#ifdef JEMALLOC_MREMAP
|
2010-12-01 08:50:58 +08:00
|
|
|
/*
|
|
|
|
* Use mremap(2) if this is a huge-->huge reallocation, and neither the
|
2012-02-14 02:56:17 +08:00
|
|
|
* source nor the destination are in dss.
|
2010-12-01 08:50:58 +08:00
|
|
|
*/
|
2012-02-14 02:56:17 +08:00
|
|
|
if (oldsize >= chunksize && (config_dss == false || (chunk_in_dss(ptr)
|
|
|
|
== false && chunk_in_dss(ret) == false))) {
|
2010-12-01 08:50:58 +08:00
|
|
|
size_t newsize = huge_salloc(ret);
|
|
|
|
|
2011-11-10 03:55:19 +08:00
|
|
|
/*
|
|
|
|
* Remove ptr from the tree of huge allocations before
|
|
|
|
* performing the remap operation, in order to avoid the
|
|
|
|
* possibility of another thread acquiring that mapping before
|
|
|
|
* this one removes it from the tree.
|
|
|
|
*/
|
|
|
|
huge_dalloc(ptr, false);
|
2010-12-01 08:50:58 +08:00
|
|
|
if (mremap(ptr, oldsize, newsize, MREMAP_MAYMOVE|MREMAP_FIXED,
|
|
|
|
ret) == MAP_FAILED) {
|
|
|
|
/*
|
|
|
|
* Assuming no chunk management bugs in the allocator,
|
|
|
|
* the only documented way an error can occur here is
|
|
|
|
* if the application changed the map type for a
|
|
|
|
* portion of the old allocation. This is firmly in
|
|
|
|
* undefined behavior territory, so write a diagnostic
|
|
|
|
* message, and optionally abort.
|
|
|
|
*/
|
|
|
|
char buf[BUFERROR_BUF];
|
|
|
|
|
2013-12-09 12:52:21 +08:00
|
|
|
buferror(get_errno(), buf, sizeof(buf));
|
2012-03-07 06:57:45 +08:00
|
|
|
malloc_printf("<jemalloc>: Error in mremap(): %s\n",
|
|
|
|
buf);
|
2010-12-01 08:50:58 +08:00
|
|
|
if (opt_abort)
|
|
|
|
abort();
|
|
|
|
memcpy(ret, ptr, copysize);
|
2011-11-12 06:41:59 +08:00
|
|
|
chunk_dealloc_mmap(ptr, oldsize);
|
2011-11-10 03:55:19 +08:00
|
|
|
}
|
2010-12-01 08:50:58 +08:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
memcpy(ret, ptr, copysize);
|
Implement the *allocx() API.
Implement the *allocx() API, which is a successor to the *allocm() API.
The *allocx() functions are slightly simpler to use because they have
fewer parameters, they directly return the results of primary interest,
and mallocx()/rallocx() avoid the strict aliasing pitfall that
allocm()/rallocx() share with posix_memalign(). The following code
violates strict aliasing rules:
foo_t *foo;
allocm((void **)&foo, NULL, 42, 0);
whereas the following is safe:
foo_t *foo;
void *p;
allocm(&p, NULL, 42, 0);
foo = (foo_t *)p;
mallocx() does not have this problem:
foo_t *foo = (foo_t *)mallocx(42, 0);
2013-12-13 14:35:52 +08:00
|
|
|
iqalloct(ptr, try_tcache_dalloc);
|
2010-12-01 08:50:58 +08:00
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2014-01-08 08:47:56 +08:00
|
|
|
#ifdef JEMALLOC_JET
|
|
|
|
#undef huge_dalloc_junk
|
|
|
|
#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk_impl)
|
|
|
|
#endif
|
|
|
|
static void
|
|
|
|
huge_dalloc_junk(void *ptr, size_t usize)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (config_fill && config_dss && opt_junk) {
|
|
|
|
/*
|
|
|
|
* Only bother junk filling if the chunk isn't about to be
|
|
|
|
* unmapped.
|
|
|
|
*/
|
|
|
|
if (config_munmap == false || (config_dss && chunk_in_dss(ptr)))
|
|
|
|
memset(ptr, 0x5a, usize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef JEMALLOC_JET
|
|
|
|
#undef huge_dalloc_junk
|
|
|
|
#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk)
|
|
|
|
huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl);
|
|
|
|
#endif
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
void
|
2010-12-01 08:50:58 +08:00
|
|
|
huge_dalloc(void *ptr, bool unmap)
|
2010-01-17 01:53:50 +08:00
|
|
|
{
|
|
|
|
extent_node_t *node, key;
|
|
|
|
|
|
|
|
malloc_mutex_lock(&huge_mtx);
|
|
|
|
|
|
|
|
/* Extract from tree of huge allocations. */
|
|
|
|
key.addr = ptr;
|
|
|
|
node = extent_tree_ad_search(&huge, &key);
|
|
|
|
assert(node != NULL);
|
|
|
|
assert(node->addr == ptr);
|
|
|
|
extent_tree_ad_remove(&huge, node);
|
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_stats) {
|
|
|
|
stats_cactive_sub(node->size);
|
|
|
|
huge_ndalloc++;
|
|
|
|
huge_allocated -= node->size;
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
malloc_mutex_unlock(&huge_mtx);
|
|
|
|
|
2014-01-08 08:47:56 +08:00
|
|
|
if (unmap)
|
|
|
|
huge_dalloc_junk(node->addr, node->size);
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2011-11-12 06:41:59 +08:00
|
|
|
chunk_dealloc(node->addr, node->size, unmap);
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
base_node_dealloc(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
huge_salloc(const void *ptr)
|
|
|
|
{
|
|
|
|
size_t ret;
|
|
|
|
extent_node_t *node, key;
|
|
|
|
|
|
|
|
malloc_mutex_lock(&huge_mtx);
|
|
|
|
|
|
|
|
/* Extract from tree of huge allocations. */
|
|
|
|
key.addr = __DECONST(void *, ptr);
|
|
|
|
node = extent_tree_ad_search(&huge, &key);
|
|
|
|
assert(node != NULL);
|
|
|
|
|
|
|
|
ret = node->size;
|
|
|
|
|
|
|
|
malloc_mutex_unlock(&huge_mtx);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2010-04-14 07:13:54 +08:00
|
|
|
prof_ctx_t *
|
|
|
|
huge_prof_ctx_get(const void *ptr)
|
2010-02-11 02:37:56 +08:00
|
|
|
{
|
2010-04-14 07:13:54 +08:00
|
|
|
prof_ctx_t *ret;
|
2010-02-11 02:37:56 +08:00
|
|
|
extent_node_t *node, key;
|
|
|
|
|
|
|
|
malloc_mutex_lock(&huge_mtx);
|
|
|
|
|
|
|
|
/* Extract from tree of huge allocations. */
|
|
|
|
key.addr = __DECONST(void *, ptr);
|
|
|
|
node = extent_tree_ad_search(&huge, &key);
|
|
|
|
assert(node != NULL);
|
|
|
|
|
2010-04-14 07:13:54 +08:00
|
|
|
ret = node->prof_ctx;
|
2010-02-11 02:37:56 +08:00
|
|
|
|
|
|
|
malloc_mutex_unlock(&huge_mtx);
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-04-14 07:13:54 +08:00
|
|
|
huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
|
2010-02-11 02:37:56 +08:00
|
|
|
{
|
|
|
|
extent_node_t *node, key;
|
|
|
|
|
|
|
|
malloc_mutex_lock(&huge_mtx);
|
|
|
|
|
|
|
|
/* Extract from tree of huge allocations. */
|
|
|
|
key.addr = __DECONST(void *, ptr);
|
|
|
|
node = extent_tree_ad_search(&huge, &key);
|
|
|
|
assert(node != NULL);
|
|
|
|
|
2010-04-14 07:13:54 +08:00
|
|
|
node->prof_ctx = ctx;
|
2010-02-11 02:37:56 +08:00
|
|
|
|
|
|
|
malloc_mutex_unlock(&huge_mtx);
|
|
|
|
}
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
bool
|
|
|
|
huge_boot(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Initialize chunks data. */
|
|
|
|
if (malloc_mutex_init(&huge_mtx))
|
|
|
|
return (true);
|
|
|
|
extent_tree_ad_new(&huge);
|
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_stats) {
|
|
|
|
huge_nmalloc = 0;
|
|
|
|
huge_ndalloc = 0;
|
|
|
|
huge_allocated = 0;
|
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
return (false);
|
|
|
|
}
|
2012-03-14 07:31:41 +08:00
|
|
|
|
|
|
|
void
|
|
|
|
huge_prefork(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
malloc_mutex_prefork(&huge_mtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
huge_postfork_parent(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
malloc_mutex_postfork_parent(&huge_mtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
huge_postfork_child(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
malloc_mutex_postfork_child(&huge_mtx);
|
|
|
|
}
|