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 <[EMAIL PROTECTED]> on behalf of Cray Inc.
---
Any suggestions on how I can move the malloc bugfix forward? Should
I send the bug report and patch to glibc? I have a test program which
triggers the malloc misbehavior without using the morecore hook:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#define GAP (2 * 1024 * 1024)
#define SIZE (8 * 1024 * 1024)
main(int argc, char **argv)
{
void *p;
mallopt(M_MMAP_MAX, 0);
p = malloc(32);
printf("sbrk(%d)\n", GAP);
p = sbrk(GAP);
if (p == (void *)-1)
perror("sbrk");
p = malloc(SIZE);
if (!p)
perror("malloc");
else
printf("malloc returned %p\n", p);
}
I've tested the patch below on x86_64 and PPC64.
-Andrew Hastings
Cray Inc.
HOWTO | 4 ++++
morecore.c | 31 ++++++++++++++++++++++++++++++-
tests/Makefile | 24 +++++++++++++++++++++++-
tests/heapshrink-helper.c | 25 +++++++++++++++++++++++++
tests/heapshrink.c | 23 +++++++++++++++++++----
tests/run_tests.sh | 6 +++++-
6 files changed, 106 insertions(+), 7 deletions(-)
diff -ruNp libhugetlbfs-dev-20080205/HOWTO
libhugetlbfs-dev-20080205-shrinkoff/HOWTO
--- libhugetlbfs-dev-20080205/HOWTO 2008-02-05 10:59:13.000000000 -0600
+++ libhugetlbfs-dev-20080205-shrinkoff/HOWTO 2008-02-13 16:18:46.333992000
-0600
@@ -208,6 +208,10 @@ segment); for 64-bit PowerPC binaries th
multiple of 1TB. On all other platforms the address is rounded-up to
the size of a hugepage.
+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 -ruNp libhugetlbfs-dev-20080205/morecore.c
libhugetlbfs-dev-20080205-shrinkoff/morecore.c
--- libhugetlbfs-dev-20080205/morecore.c 2008-02-05 10:59:13.000000000
-0600
+++ libhugetlbfs-dev-20080205-shrinkoff/morecore.c 2008-02-13
16:28:33.223636000 -0600
@@ -33,6 +33,7 @@
#include "libhugetlbfs_internal.h"
static int heap_fd;
+static int shrink_ok; /* default = 0: no shrink */
static long blocksize;
static void *heapbase;
@@ -135,6 +136,12 @@ static void *hugetlbfs_morecore(ptrdiff_
} 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!");
return NULL;
@@ -201,6 +208,25 @@ static void __attribute__((constructor))
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) {
ERROR("Hugepages unavailable\n");
@@ -233,7 +259,10 @@ static void __attribute__((constructor))
/* 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 -ruNp libhugetlbfs-dev-20080205/tests/Makefile
libhugetlbfs-dev-20080205-shrinkoff/tests/Makefile
--- libhugetlbfs-dev-20080205/tests/Makefile 2008-02-05 10:59:13.000000000
-0600
+++ libhugetlbfs-dev-20080205-shrinkoff/tests/Makefile 2008-02-13
15:49:16.356591000 -0600
@@ -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_TE
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 -ruNp libhugetlbfs-dev-20080205/tests/heapshrink-helper.c
libhugetlbfs-dev-20080205-shrinkoff/tests/heapshrink-helper.c
--- libhugetlbfs-dev-20080205/tests/heapshrink-helper.c 1969-12-31
18:00:00.000000000 -0600
+++ libhugetlbfs-dev-20080205-shrinkoff/tests/heapshrink-helper.c
2008-02-13 16:04:31.228023000 -0600
@@ -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 -ruNp libhugetlbfs-dev-20080205/tests/heapshrink.c
libhugetlbfs-dev-20080205-shrinkoff/tests/heapshrink.c
--- libhugetlbfs-dev-20080205/tests/heapshrink.c 2008-02-05
10:59:13.000000000 -0600
+++ libhugetlbfs-dev-20080205-shrinkoff/tests/heapshrink.c 2008-02-13
16:16:11.523282000 -0600
@@ -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 -ruNp libhugetlbfs-dev-20080205/tests/run_tests.sh
libhugetlbfs-dev-20080205-shrinkoff/tests/run_tests.sh
--- libhugetlbfs-dev-20080205/tests/run_tests.sh 2008-02-05
10:59:13.000000000 -0600
+++ libhugetlbfs-dev-20080205-shrinkoff/tests/run_tests.sh 2008-02-13
16:12:53.877440000 -0600
@@ -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
}
@@ -181,7 +181,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
elflink_test HUGETLB_VERBOSE=0 linkhuge_nofd # Lib error msgs expected
elflink_test linkhuge
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Libhugetlbfs-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libhugetlbfs-devel