[PATCH] Support hugepages in mremap_chunk
William Hunt
william.hunt@arm.com
Mon Aug 18 10:30:00 GMT 2025
Add mremap_chunk support for mmap()ed chunks using hugepages by accounting for
their alignment, to prevent the mremap call failing in most cases where the
size passed is not a hugepage size multiple. It also improves robustness for
reallocating hugepages since mremap is much less likely to fail, so running
out of memory when reallocating a larger size and having to copy the old
contents after mremap fails is also less likely.
To track whether an mmap()ed chunk uses hugepages, have a flag in the lowest
bit of the mchunk_prev_size field which is set after a call to sysmalloc_mmap,
and accessed later in mremap_chunk. Create macros for getting and setting this
bit, and for mapping the bit off when accessing the field for mmap()ed chunks.
Since the alignment cannot be lower than 8 bits, this flag cannot affect the
alignment data.
Update mem2chunk in malloc-check.c to check whether the memory is from a top
chunk, to prevent later checks from triggering a SEGSEGV by attempting to
access the mchunk_prev_size field of the next chunk. This prevents the
malloc/tst-tcfree4-malloc-check test from failing when ran using hugepages and
glibc.malloc.hugetlb=2, since the main arena is marked non-contiguous and the
previous checks that would cause NULL to be returned do not run before the
next chunk is attempted to be accessed.
Passed regress for THPs, neither and HPs using overcommit pages, OK for commit?
---
malloc/malloc-check.c | 6 +++---
malloc/malloc.c | 29 +++++++++++++++++++++++------
2 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/malloc/malloc-check.c b/malloc/malloc-check.c
index 9532316a29..350086e58c 100644
--- a/malloc/malloc-check.c
+++ b/malloc/malloc-check.c
@@ -121,7 +121,7 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
{
/* Must be a chunk in conventional heap memory. */
int contig = contiguous (&main_arena);
- if ((contig &&
+ if (((contig || p == main_arena.top) &&
((char *) p < mp_.sbrk_base ||
((char *) p + sz) >= (mp_.sbrk_base + main_arena.system_mem))) ||
sz < MINSIZE || sz & MALLOC_ALIGN_MASK || !inuse (p) ||
@@ -151,8 +151,8 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
offset != 0x200 && offset != 0x400 && offset != 0x800 && offset != 0x1000 &&
offset < 0x2000) ||
!chunk_is_mmapped (p) || prev_inuse (p) ||
- ((((unsigned long) p - prev_size (p)) & page_mask) != 0) ||
- ((prev_size (p) + sz) & page_mask) != 0)
+ ((((unsigned long) p - prev_size_mmap (p)) & page_mask) != 0) ||
+ ((prev_size_mmap (p) + sz) & page_mask) != 0)
return NULL;
for (sz = CHUNK_HDR_SZ + memsize (p) - 1;
diff --git a/malloc/malloc.c b/malloc/malloc.c
index e08873cad5..1a7035da30 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1477,6 +1477,18 @@ tag_new_usable (void *ptr)
return ptr;
}
+/* HP page used for an mmap()'ed chunk. */
+#define MMAP_HP 0x1
+
+/* Check for HP usage from an mmap()'ed chunk. */
+#define mmap_is_hp(p) (prev_size (p) & MMAP_HP)
+
+/* Set HP advised field for an mmap()'ed chunk. */
+#define set_mmap_is_hp(p) (prev_size (p) |= MMAP_HP)
+
+/* Get an mmap()ed chunk's offset, ignoring huge page bits. */
+#define prev_size_mmap(p) (prev_size (p) & ~MMAP_HP)
+
/*
-------------------- Internal data structures --------------------
@@ -2105,7 +2117,7 @@ do_check_chunk (mstate av, mchunkptr p)
else
{
/* chunk is page-aligned */
- assert (((prev_size (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
+ assert (((prev_size_mmap (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
/* mem is aligned */
assert (!misaligned_chunk (p));
}
@@ -2429,6 +2441,11 @@ sysmalloc_mmap (INTERNAL_SIZE_T nb, size_t pagesize, int extra_flags)
set_prev_size (p, padding);
set_head (p, (size - padding) | IS_MMAPPED);
+ /* Must also check whether huge pages were used in the mmap call
+ and this is not the fallback call after using huge pages failed */
+ if (__glibc_unlikely (extra_flags & mp_.hp_flags))
+ set_mmap_is_hp (p);
+
/* update statistics */
int new = atomic_fetch_add_relaxed (&mp_.n_mmaps, 1) + 1;
atomic_max (&mp_.max_n_mmaps, new);
@@ -2982,8 +2999,8 @@ munmap_chunk (mchunkptr p)
assert (chunk_is_mmapped (p));
uintptr_t mem = (uintptr_t) chunk2mem (p);
- uintptr_t block = (uintptr_t) p - prev_size (p);
- size_t total_size = prev_size (p) + size;
+ uintptr_t block = (uintptr_t) p - prev_size_mmap (p);
+ size_t total_size = prev_size_mmap (p) + size;
/* Unfortunately we have to do the compilers job by hand here. Normally
we would test BLOCK and TOTAL-SIZE separately for compliance with the
page size. But gcc does not recognize the optimization possibility
@@ -3007,8 +3024,8 @@ munmap_chunk (mchunkptr p)
static mchunkptr
mremap_chunk (mchunkptr p, size_t new_size)
{
- size_t pagesize = GLRO (dl_pagesize);
- INTERNAL_SIZE_T offset = prev_size (p);
+ size_t pagesize = mmap_is_hp (p) ? mp_.hp_pagesize : GLRO (dl_pagesize);
+ INTERNAL_SIZE_T offset = prev_size_mmap (p);
INTERNAL_SIZE_T size = chunksize (p);
char *cp;
@@ -3040,7 +3057,7 @@ mremap_chunk (mchunkptr p, size_t new_size)
assert (!misaligned_chunk (p));
- assert (prev_size (p) == offset);
+ assert (prev_size_mmap (p) == offset);
set_head (p, (new_size - offset) | IS_MMAPPED);
INTERNAL_SIZE_T new;
--
2.43.0
More information about the Libc-alpha
mailing list