[PATCH] Support hugepages in mremap_chunk
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Wed Aug 20 11:28:21 GMT 2025
On 18/08/25 07:30, William Hunt wrote:
> 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)
The patch looks good, the only thing I would ask is to move away from
macros and used static inline function in new code (even though the
practice for this old implementation is to use macros).
> +
> /*
> -------------------- 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;
More information about the Libc-alpha
mailing list