aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Hastings <abh@cray.com>2008-02-14 11:48:42 -0600
committerNishanth Aravamudan <nacc@us.ibm.com>2008-05-02 12:46:22 -0700
commit7e7b88643420becef7338f850b7bb3d0afe974d6 (patch)
tree2cdaabc8690fd1ca43718f76f3ec183ed2847b8a
parent4ee04fc4e0bd67fca758637778056d85c339b538 (diff)
downloadlibhugetlbfs-7e7b88643420becef7338f850b7bb3d0afe974d6.tar.gz
disable heap shrinking by default
Disable heap shrinking by default unless HUGETLB_MORECORE_SHRINK=yes is set in the environment. If malloc allocates memory on the heap before libhugetlbfs's setup_morecore is called, then when malloc calls hugetlbfs_morecore it will notice a gap between the previous top-of-heap and the memory allocated by hugetlbfs_morecore. Malloc treats this as a "foreign sbrk" and marks the gap as allocated so it will avoid using this memory. Unfortunately, it does so by calling a routine called _int_free, which will try to shrink the heap if there is too much free memory. Oops! The memory allocated by hugetlbfs_morecore hasn't yet been marked as allocated by malloc, and the heap shrinks. Without the newly-allocated memory, malloc returns NULL. I've reported this as a bug to the ptmalloc2 maintainer (upstream contributor to glibc) and provided a patch to correct the behavior in ptmalloc2. Unfortunately, I've not gotten a reply in two months, so it seems prudent to disable heap shrinking in libhugetlbfs until the behavior of glibc's malloc is changed. Also adds test cases to trigger the unexpected malloc behavior. Signed-off-by: Andrew Hastings <abh@cray.com> Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
-rw-r--r--HOWTO4
-rw-r--r--morecore.c31
-rw-r--r--tests/Makefile24
-rw-r--r--tests/heapshrink-helper.c25
-rw-r--r--tests/heapshrink.c23
-rwxr-xr-xtests/run_tests.sh6
6 files changed, 106 insertions, 7 deletions
diff --git a/HOWTO b/HOWTO
index c6c2fb3..083836d 100644
--- a/HOWTO
+++ b/HOWTO
@@ -268,6 +268,10 @@ hugepages are available to and used by many process can result in some
applications receving SIGKILL, so its use is not recommended in
high-availability or production environments.
+By default, the hugepage heap does not shrink. To enable hugepage heap
+shrinking, set HUGETLB_MORECORE_SHRINK=yes. NB: We have been seeing some
+unexpected behavior from glibc's malloc when this is enabled.
+
Using hugepage text, data, or BSS
---------------------------------
diff --git a/morecore.c b/morecore.c
index eb9c1ec..46897aa 100644
--- a/morecore.c
+++ b/morecore.c
@@ -35,6 +35,7 @@
#include "libhugetlbfs_internal.h"
static int heap_fd;
+static int shrink_ok; /* default = 0; no shrink */
static int zero_fd;
static long blocksize;
@@ -166,6 +167,12 @@ static void *hugetlbfs_morecore(ptrdiff_t increment)
} else if (delta < 0) {
/* shrinking the heap */
+ if (!shrink_ok) {
+ /* shouldn't ever get here */
+ WARNING("Heap shrinking is turned off\n");
+ return NULL;
+ }
+
if (!mapsize) {
WARNING("Can't shrink empty heap!\n");
return NULL;
@@ -231,6 +238,25 @@ void __hugetlbfs_setup_morecore(void)
return;
}
+ /*
+ * We have been seeing some unexpected behavior from malloc when
+ * heap shrinking is enabled, so heap shrinking is disabled by
+ * default.
+ *
+ * If malloc has been called successfully before setup_morecore,
+ * glibc will notice a gap between the previous top-of-heap and
+ * the new top-of-heap when it calls hugetlbfs_morecore. It treats
+ * this as a "foreign sbrk." Unfortunately, the "foreign sbrk"
+ * handling code will then immediately try to free the memory
+ * allocated by hugetlbfs_morecore!
+ *
+ * This behavior has been reported to the ptmalloc2 maintainer,
+ * along with a patch to correct the behavior.
+ */
+ env = getenv("HUGETLB_MORECORE_SHRINK");
+ if (env && strcasecmp(env, "yes") == 0)
+ shrink_ok = 1;
+
blocksize = gethugepagesize();
if (blocksize <= 0) {
if (errno == ENOSYS)
@@ -269,7 +295,10 @@ void __hugetlbfs_setup_morecore(void)
/* Set some allocator options more appropriate for hugepages */
- mallopt(M_TRIM_THRESHOLD, blocksize / 2);
+ if (shrink_ok)
+ mallopt(M_TRIM_THRESHOLD, blocksize / 2);
+ else
+ mallopt(M_TRIM_THRESHOLD, -1);
mallopt(M_TOP_PAD, blocksize / 2);
/* we always want to use our morecore, not ordinary mmap().
* This doesn't appear to prohibit malloc() from falling back
diff --git a/tests/Makefile b/tests/Makefile
index 6b60b5e..322aae1 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -15,6 +15,7 @@ LDSCRIPT_TESTS = zero_filesize_segment
HUGELINK_TESTS = linkhuge linkhuge_nofd linkshare
STRESS_TESTS = mmap-gettest mmap-cow shm-gettest shm-getraw shm-fork
HELPERS = get_hugetlbfs_path
+HELPER_LIBS = libheapshrink.so
BADTOOLCHAIN = bad-toolchain.sh
CFLAGS = -O2 -Wall -g
@@ -58,11 +59,12 @@ DEPFILES = $(LIB_TESTS:%=%.d) $(NOLIB_TESTS:%=%.d) $(HUGELINK_TESTS:%=%.d) \
ALLTESTS = $(foreach DIR,$(OBJDIRS),$(TESTS:%=$(DIR)/%))
ALLHELPERS = $(foreach DIR,$(OBJDIRS),$(HELPERS:%=$(DIR)/%))
+ALLHELPERLIBS = $(foreach DIR,$(OBJDIRS),$(HELPER_LIBS:%=$(DIR)/%))
ifdef CC64
ALLTESTS += $(TESTS_64:%=obj64/%)
endif
-all: $(ALLTESTS) $(ALLHELPERS)
+all: $(ALLTESTS) $(ALLHELPERS) $(ALLHELPERLIBS)
obj32/%.o: %.c
@$(VECHO) CC32 $@
@@ -74,6 +76,26 @@ obj64/%.o: %.c
@mkdir -p obj64
$(CC64) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
+obj32/%-pic.o: %.c
+ @$(VECHO) CC32 $@
+ @mkdir -p obj32
+ $(CC32) $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ -c $<
+
+obj64/%-pic.o: %.c
+ @$(VECHO) CC64 $@
+ @mkdir -p obj64
+ $(CC64) $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ -c $<
+
+obj32/libheapshrink.so: obj32/heapshrink-helper-pic.o
+ @$(VECHO) LD32 "(shared)" $@
+ @mkdir -p obj32
+ $(CC32) -Wl,-soname,$(notdir $@) -shared -o $@ $^
+
+obj64/libheapshrink.so: obj64/heapshrink-helper-pic.o
+ @$(VECHO) LD64 "(shared)" $@
+ @mkdir -p obj64
+ $(CC64) -Wl,-soname,$(notdir $@) -shared -o $@ $^
+
$(LIB_TESTS:%=obj32/%): %: %.o obj32/testutils.o
@$(VECHO) LD32 "(lib test)" $@
$(CC32) $(LDFLAGS) $(LDFLAGS32) -o $@ $^ $(LDLIBS) -lhugetlbfs
diff --git a/tests/heapshrink-helper.c b/tests/heapshrink-helper.c
new file mode 100644
index 0000000..e793ff6
--- /dev/null
+++ b/tests/heapshrink-helper.c
@@ -0,0 +1,25 @@
+/*
+ * Test heap shrinking for libhugetlbfs.
+ * Copyright 2008 Cray Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <malloc.h>
+
+static void __attribute__((constructor)) setup_heapshrink_helper(void)
+{
+ (void) malloc(1);
+}
diff --git a/tests/heapshrink.c b/tests/heapshrink.c
index 33a6dbc..9c83210 100644
--- a/tests/heapshrink.c
+++ b/tests/heapshrink.c
@@ -26,23 +26,38 @@
int main(int argc, char **argv)
{
- int is_huge, have_env;
+ int is_huge, have_env, shrink_ok, have_helper;
void *p;
test_init(argc, argv);
have_env = getenv("HUGETLB_MORECORE") != NULL;
+ shrink_ok = getenv("HUGETLB_MORECORE_SHRINK") != NULL;
+ p = getenv("LD_PRELOAD");
+ have_helper = p != NULL && strstr(p, "heapshrink") != NULL;
p = malloc(SIZE);
+ if (!p) {
+ if (shrink_ok && have_helper) {
+ /* Hitting unexpected behavior in malloc() */
+ PASS_INCONCLUSIVE();
+ } else
+ FAIL("malloc(%d) failed\n", SIZE);
+ }
memset(p, 0, SIZE);
is_huge = test_addr_huge(p+SIZE-1) == 1;
- if (have_env && !is_huge)
- FAIL("Heap not on hugepages");
+ if (have_env && !is_huge) {
+ if (shrink_ok && have_helper) {
+ /* Hitting unexpected behavior in malloc() */
+ PASS_INCONCLUSIVE();
+ } else
+ FAIL("Heap not on hugepages");
+ }
if (!have_env && is_huge)
FAIL("Heap unexpectedly on hugepages");
free(p);
- if (test_addr_huge(p+SIZE-1) == 1)
+ if (shrink_ok && test_addr_huge(p+SIZE-1) == 1)
FAIL("Heap did not shrink");
PASS();
}
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 07d10fe..8f8dc11 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -37,7 +37,7 @@ run_test_bits () {
if [ -d obj$BITS ]; then
echo -n "$@ ($BITS): "
- PATH="obj$BITS:$PATH" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../obj$BITS" $ENV "$@"
+ PATH="obj$BITS:$PATH" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../obj$BITS:obj$BITS" $ENV "$@"
fi
}
@@ -185,7 +185,11 @@ functional_tests () {
run_test malloc_manysmall
preload_test HUGETLB_MORECORE=yes malloc_manysmall
run_test heapshrink
+ run_test LD_PRELOAD=libheapshrink.so heapshrink
preload_test HUGETLB_MORECORE=yes heapshrink
+ run_test LD_PRELOAD="libhugetlbfs.so libheapshrink.so" HUGETLB_MORECORE=yes heapshrink
+ preload_test HUGETLB_MORECORE=yes HUGETLB_MORECORE_SHRINK=yes heapshrink
+ run_test LD_PRELOAD="libhugetlbfs.so libheapshrink.so" HUGETLB_MORECORE=yes HUGETLB_MORECORE_SHRINK=yes heapshrink
run_test HUGETLB_VERBOSE=1 HUGETLB_MORECORE=yes heap-overflow # warnings expected
elflink_test HUGETLB_VERBOSE=0 linkhuge_nofd # Lib error msgs expected
elflink_test linkhuge