Use mremap(2) for huge realloc().
If mremap(2) is available and supports MREMAP_FIXED, use it for huge realloc(). Initialize rtree later during bootstrapping, so that --enable-debug --enable-dss works. Fix a minor swap_avail stats bug.
This commit is contained in:
parent
aee7fd2b70
commit
cfdc8cfbd6
2
.gitignore
vendored
2
.gitignore
vendored
@ -18,4 +18,6 @@
|
|||||||
/jemalloc/test/*.[od]
|
/jemalloc/test/*.[od]
|
||||||
/jemalloc/test/*.out
|
/jemalloc/test/*.out
|
||||||
/jemalloc/test/[a-z]*
|
/jemalloc/test/[a-z]*
|
||||||
|
!/jemalloc/test/*.c
|
||||||
|
!/jemalloc/test/*.exp
|
||||||
/jemalloc/VERSION
|
/jemalloc/VERSION
|
||||||
|
@ -64,7 +64,7 @@ DOCS_HTML := $(DOCS_XML:@objroot@%.xml=@srcroot@%.html)
|
|||||||
DOCS_MAN3 := $(DOCS_XML:@objroot@%.xml=@srcroot@%.3)
|
DOCS_MAN3 := $(DOCS_XML:@objroot@%.xml=@srcroot@%.3)
|
||||||
DOCS := $(DOCS_HTML) $(DOCS_MAN3)
|
DOCS := $(DOCS_HTML) $(DOCS_MAN3)
|
||||||
CTESTS := @srcroot@test/allocated.c @srcroot@test/allocm.c \
|
CTESTS := @srcroot@test/allocated.c @srcroot@test/allocm.c \
|
||||||
@srcroot@test/posix_memalign.c \
|
@srcroot@test/mremap.c @srcroot@test/posix_memalign.c \
|
||||||
@srcroot@test/rallocm.c @srcroot@test/thread_arena.c
|
@srcroot@test/rallocm.c @srcroot@test/thread_arena.c
|
||||||
|
|
||||||
.PHONY: all dist doc_html doc_man doc
|
.PHONY: all dist doc_html doc_man doc
|
||||||
|
@ -227,6 +227,16 @@ esac
|
|||||||
AC_SUBST([abi])
|
AC_SUBST([abi])
|
||||||
AC_SUBST([RPATH])
|
AC_SUBST([RPATH])
|
||||||
|
|
||||||
|
JE_COMPILABLE([mremap(...MREMAP_FIXED...)], [
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <sys/mman.h>
|
||||||
|
], [
|
||||||
|
void *p = mremap((void *)0, 0, 0, MREMAP_MAYMOVE|MREMAP_FIXED, (void *)0);
|
||||||
|
], [mremap_fixed])
|
||||||
|
if test "x${mremap_fixed}" = "xyes" ; then
|
||||||
|
AC_DEFINE([JEMALLOC_MREMAP_FIXED])
|
||||||
|
fi
|
||||||
|
|
||||||
dnl Support optional additions to rpath.
|
dnl Support optional additions to rpath.
|
||||||
AC_ARG_WITH([rpath],
|
AC_ARG_WITH([rpath],
|
||||||
[AS_HELP_STRING([--with-rpath=<rpath>], [Colon-separated rpath (ELF systems only)])],
|
[AS_HELP_STRING([--with-rpath=<rpath>], [Colon-separated rpath (ELF systems only)])],
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
extern malloc_mutex_t dss_mtx;
|
extern malloc_mutex_t dss_mtx;
|
||||||
|
|
||||||
void *chunk_alloc_dss(size_t size, bool *zero);
|
void *chunk_alloc_dss(size_t size, bool *zero);
|
||||||
|
bool chunk_in_dss(void *chunk);
|
||||||
bool chunk_dealloc_dss(void *chunk, size_t size);
|
bool chunk_dealloc_dss(void *chunk, size_t size);
|
||||||
bool chunk_dss_boot(void);
|
bool chunk_dss_boot(void);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ extern size_t swap_avail;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void *chunk_alloc_swap(size_t size, bool *zero);
|
void *chunk_alloc_swap(size_t size, bool *zero);
|
||||||
|
bool chunk_in_swap(void *chunk);
|
||||||
bool chunk_dealloc_swap(void *chunk, size_t size);
|
bool chunk_dealloc_swap(void *chunk, size_t size);
|
||||||
bool chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed);
|
bool chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed);
|
||||||
bool chunk_swap_boot(void);
|
bool chunk_swap_boot(void);
|
||||||
|
@ -25,7 +25,7 @@ void *huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size,
|
|||||||
size_t extra);
|
size_t extra);
|
||||||
void *huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
void *huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||||
size_t alignment, bool zero);
|
size_t alignment, bool zero);
|
||||||
void huge_dalloc(void *ptr);
|
void huge_dalloc(void *ptr, bool unmap);
|
||||||
size_t huge_salloc(const void *ptr);
|
size_t huge_salloc(const void *ptr);
|
||||||
#ifdef JEMALLOC_PROF
|
#ifdef JEMALLOC_PROF
|
||||||
prof_ctx_t *huge_prof_ctx_get(const void *ptr);
|
prof_ctx_t *huge_prof_ctx_get(const void *ptr);
|
||||||
|
@ -666,7 +666,7 @@ idalloc(void *ptr)
|
|||||||
if (chunk != ptr)
|
if (chunk != ptr)
|
||||||
arena_dalloc(chunk->arena, chunk, ptr);
|
arena_dalloc(chunk->arena, chunk, ptr);
|
||||||
else
|
else
|
||||||
huge_dalloc(ptr);
|
huge_dalloc(ptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
JEMALLOC_INLINE void *
|
JEMALLOC_INLINE void *
|
||||||
|
@ -115,6 +115,9 @@
|
|||||||
#undef JEMALLOC_ZONE
|
#undef JEMALLOC_ZONE
|
||||||
#undef JEMALLOC_ZONE_VERSION
|
#undef JEMALLOC_ZONE_VERSION
|
||||||
|
|
||||||
|
/* If defined, use mremap(...MREMAP_FIXED...) for huge realloc(). */
|
||||||
|
#undef JEMALLOC_MREMAP_FIXED
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Methods for purging unused pages differ between operating systems.
|
* Methods for purging unused pages differ between operating systems.
|
||||||
*
|
*
|
||||||
|
@ -146,11 +146,6 @@ chunk_boot(void)
|
|||||||
chunksize_mask = chunksize - 1;
|
chunksize_mask = chunksize - 1;
|
||||||
chunk_npages = (chunksize >> PAGE_SHIFT);
|
chunk_npages = (chunksize >> PAGE_SHIFT);
|
||||||
|
|
||||||
#ifdef JEMALLOC_IVSALLOC
|
|
||||||
chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) - opt_lg_chunk);
|
|
||||||
if (chunks_rtree == NULL)
|
|
||||||
return (true);
|
|
||||||
#endif
|
|
||||||
#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
|
#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
|
||||||
if (malloc_mutex_init(&chunks_mtx))
|
if (malloc_mutex_init(&chunks_mtx))
|
||||||
return (true);
|
return (true);
|
||||||
@ -166,6 +161,11 @@ chunk_boot(void)
|
|||||||
if (chunk_dss_boot())
|
if (chunk_dss_boot())
|
||||||
return (true);
|
return (true);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef JEMALLOC_IVSALLOC
|
||||||
|
chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) - opt_lg_chunk);
|
||||||
|
if (chunks_rtree == NULL)
|
||||||
|
return (true);
|
||||||
|
#endif
|
||||||
|
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
@ -199,6 +199,22 @@ chunk_dealloc_dss_record(void *chunk, size_t size)
|
|||||||
return (node);
|
return (node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
chunk_in_dss(void *chunk)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
malloc_mutex_lock(&dss_mtx);
|
||||||
|
if ((uintptr_t)chunk >= (uintptr_t)dss_base
|
||||||
|
&& (uintptr_t)chunk < (uintptr_t)dss_max)
|
||||||
|
ret = true;
|
||||||
|
else
|
||||||
|
ret = false;
|
||||||
|
malloc_mutex_unlock(&dss_mtx);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
chunk_dealloc_dss(void *chunk, size_t size)
|
chunk_dealloc_dss(void *chunk, size_t size)
|
||||||
{
|
{
|
||||||
|
@ -184,6 +184,24 @@ chunk_dealloc_swap_record(void *chunk, size_t size)
|
|||||||
return (node);
|
return (node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
chunk_in_swap(void *chunk)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
assert(swap_enabled);
|
||||||
|
|
||||||
|
malloc_mutex_lock(&swap_mtx);
|
||||||
|
if ((uintptr_t)chunk >= (uintptr_t)swap_base
|
||||||
|
&& (uintptr_t)chunk < (uintptr_t)swap_max)
|
||||||
|
ret = true;
|
||||||
|
else
|
||||||
|
ret = false;
|
||||||
|
malloc_mutex_unlock(&swap_mtx);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
chunk_dealloc_swap(void *chunk, size_t size)
|
chunk_dealloc_swap(void *chunk, size_t size)
|
||||||
{
|
{
|
||||||
@ -219,15 +237,15 @@ chunk_dealloc_swap(void *chunk, size_t size)
|
|||||||
} else
|
} else
|
||||||
madvise(chunk, size, MADV_DONTNEED);
|
madvise(chunk, size, MADV_DONTNEED);
|
||||||
|
|
||||||
|
#ifdef JEMALLOC_STATS
|
||||||
|
swap_avail += size;
|
||||||
|
#endif
|
||||||
ret = false;
|
ret = false;
|
||||||
goto RETURN;
|
goto RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
RETURN:
|
RETURN:
|
||||||
#ifdef JEMALLOC_STATS
|
|
||||||
swap_avail += size;
|
|
||||||
#endif
|
|
||||||
malloc_mutex_unlock(&swap_mtx);
|
malloc_mutex_unlock(&swap_mtx);
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
@ -215,13 +215,56 @@ huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
|||||||
* expectation that the extra bytes will be reliably preserved.
|
* expectation that the extra bytes will be reliably preserved.
|
||||||
*/
|
*/
|
||||||
copysize = (size < oldsize) ? size : oldsize;
|
copysize = (size < oldsize) ? size : oldsize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use mremap(2) if this is a huge-->huge reallocation, and neither the
|
||||||
|
* source nor the destination are in swap or dss.
|
||||||
|
*/
|
||||||
|
#ifdef JEMALLOC_MREMAP_FIXED
|
||||||
|
if (oldsize >= chunksize
|
||||||
|
# ifdef JEMALLOC_SWAP
|
||||||
|
&& (swap_enabled == false || (chunk_in_swap(ptr) == false &&
|
||||||
|
chunk_in_swap(ret) == false))
|
||||||
|
# endif
|
||||||
|
# ifdef JEMALLOC_DSS
|
||||||
|
&& chunk_in_dss(ptr) == false && chunk_in_dss(ret) == false
|
||||||
|
# endif
|
||||||
|
) {
|
||||||
|
size_t newsize = huge_salloc(ret);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
buferror(errno, buf, sizeof(buf));
|
||||||
|
malloc_write("<jemalloc>: Error in mremap(): ");
|
||||||
|
malloc_write(buf);
|
||||||
|
malloc_write("\n");
|
||||||
|
if (opt_abort)
|
||||||
|
abort();
|
||||||
memcpy(ret, ptr, copysize);
|
memcpy(ret, ptr, copysize);
|
||||||
idalloc(ptr);
|
idalloc(ptr);
|
||||||
|
} else
|
||||||
|
huge_dalloc(ptr, false);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
memcpy(ret, ptr, copysize);
|
||||||
|
idalloc(ptr);
|
||||||
|
}
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
huge_dalloc(void *ptr)
|
huge_dalloc(void *ptr, bool unmap)
|
||||||
{
|
{
|
||||||
extent_node_t *node, key;
|
extent_node_t *node, key;
|
||||||
|
|
||||||
@ -241,6 +284,7 @@ huge_dalloc(void *ptr)
|
|||||||
|
|
||||||
malloc_mutex_unlock(&huge_mtx);
|
malloc_mutex_unlock(&huge_mtx);
|
||||||
|
|
||||||
|
if (unmap) {
|
||||||
/* Unmap chunk. */
|
/* Unmap chunk. */
|
||||||
#ifdef JEMALLOC_FILL
|
#ifdef JEMALLOC_FILL
|
||||||
#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS))
|
#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS))
|
||||||
@ -249,6 +293,7 @@ huge_dalloc(void *ptr)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
chunk_dealloc(node->addr, node->size);
|
chunk_dealloc(node->addr, node->size);
|
||||||
|
}
|
||||||
|
|
||||||
base_node_dealloc(node);
|
base_node_dealloc(node);
|
||||||
}
|
}
|
||||||
|
67
jemalloc/test/mremap.c
Normal file
67
jemalloc/test/mremap.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define JEMALLOC_MANGLE
|
||||||
|
#include "jemalloc_test.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
int ret, err;
|
||||||
|
size_t sz, lg_chunk, chunksize, i;
|
||||||
|
char *p, *q;
|
||||||
|
|
||||||
|
fprintf(stderr, "Test begin\n");
|
||||||
|
|
||||||
|
sz = sizeof(lg_chunk);
|
||||||
|
if ((err = JEMALLOC_P(mallctl)("opt.lg_chunk", &lg_chunk, &sz, NULL,
|
||||||
|
0))) {
|
||||||
|
assert(err != ENOENT);
|
||||||
|
fprintf(stderr, "%s(): Error in mallctl(): %s\n", __func__,
|
||||||
|
strerror(err));
|
||||||
|
ret = 1;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
chunksize = ((size_t)1U) << lg_chunk;
|
||||||
|
|
||||||
|
p = (char *)malloc(chunksize);
|
||||||
|
if (p == NULL) {
|
||||||
|
fprintf(stderr, "malloc(%zu) --> %p\n", chunksize, p);
|
||||||
|
ret = 1;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
memset(p, 'a', chunksize);
|
||||||
|
|
||||||
|
q = (char *)realloc(p, chunksize * 2);
|
||||||
|
if (q == NULL) {
|
||||||
|
fprintf(stderr, "realloc(%p, %zu) --> %p\n", p, chunksize * 2,
|
||||||
|
q);
|
||||||
|
ret = 1;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
for (i = 0; i < chunksize; i++) {
|
||||||
|
assert(q[i] == 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
p = q;
|
||||||
|
|
||||||
|
q = (char *)realloc(p, chunksize);
|
||||||
|
if (q == NULL) {
|
||||||
|
fprintf(stderr, "realloc(%p, %zu) --> %p\n", p, chunksize, q);
|
||||||
|
ret = 1;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
for (i = 0; i < chunksize; i++) {
|
||||||
|
assert(q[i] == 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
free(q);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
RETURN:
|
||||||
|
fprintf(stderr, "Test end\n");
|
||||||
|
return (ret);
|
||||||
|
}
|
2
jemalloc/test/mremap.exp
Normal file
2
jemalloc/test/mremap.exp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Test begin
|
||||||
|
Test end
|
Loading…
Reference in New Issue
Block a user