[PATCH v2] malloc: Don't call __get_thp_mode/__get_thp_size twice
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Mon Jun 22 16:14:03 GMT 2026
On 22/06/26 10:15, H.J. Lu wrote:
> This is identical to the first version. Now it can be applied
> to master branch since its prerequisite has been merged into
> master branch.
>
> From ad568336b63148195af93cf7bd4adf6efa282deb Mon Sep 17 00:00:00 2001
> From: "H.J. Lu" <hjl.tools@gmail.com>
> Date: Sat, 18 Apr 2026 11:36:41 +0800
> Subject: [PATCH v2] malloc: Don't call __get_thp_mode/__get_thp_size twice
>
> Both ld.so and malloc track kernel THP mode and THP page size when THP
> in ld.so is enabled by
>
> GLIBC_TUNABLES=glibc.elf.thp=1
>
> and THP in malloc is enabled by
>
> GLIBC_TUNABLES=glibc.malloc.hugetlb=1
>
> But DL_MAP_DEFAULT_THP_PAGESIZE and MALLOC_DEFAULT_THP_PAGESIZE may be
> different when they are defined in <hugepages.h>.
>
> If THP in ld.so is enabled, change malloc to use kernel THP mode from
> ld.so, instead of calling __get_thp_mode, and use THP page size from
> ld.so if it came from __get_thp_size. This avoids calling __get_thp_mode
> and __get_thp_size again, which are quite expensive:
>
> 1. Initialize mp_.thp_mode and GL(dl_thp_mode) to thp_mode_unknown.
> 2. Set mp_.thp_mode to GL(dl_thp_mode) if GL(dl_thp_mode) isn't
> thp_mode_unknown. Otherwise call __get_thp_mode to set mp_.thp_mode.
> 3. GL(dl_elf_thp_pagesize) is set to DL_MAP_DEFAULT_THP_PAGESIZE without
> calling __get_thp_size and THP page size for malloc may be different from
> THP page size for ld.so. Set mp_.thp_pagesize to GL(dl_elf_thp_pagesize)
> if DL_MAP_DEFAULT_THP_PAGESIZE is defined. Otherwise call __get_thp_size
> to set mp_.thp_pagesize.
>
> This fixes BZ #34083.
>
> Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Looks good, some comments below. I think Andreas will need to ack it now that
we are in soft-freeze.
> ---
> malloc/malloc.c | 56 +++++++++++++++++++++++++++++++++----
> sysdeps/generic/hugepages.h | 3 +-
> 2 files changed, 53 insertions(+), 6 deletions(-)
>
> diff --git a/malloc/malloc.c b/malloc/malloc.c
> index a354576aca..f0ed95213f 100644
> --- a/malloc/malloc.c
> +++ b/malloc/malloc.c
> @@ -1529,7 +1529,7 @@ static struct malloc_par mp_ =
> .mmap_threshold = DEFAULT_MMAP_THRESHOLD,
> .trim_threshold = DEFAULT_TRIM_THRESHOLD,
> .arena_test = sizeof (long) == 4 ? 2 : 8,
> - .thp_mode = thp_mode_not_supported
> + .thp_mode = thp_mode_unknown
> #if USE_TCACHE
> ,
> .tcache_count = TCACHE_FILL_COUNT,
> @@ -4657,13 +4657,41 @@ do_set_mxfast (size_t value)
> return 1;
> }
>
> +#ifdef HAVE_THP
> +static __always_inline enum thp_mode_t
> +get_dl_thp_mode (void)
> +{
> + return GL(dl_thp_mode);
> +}
> +
> +static __always_inline unsigned long int
> +get_dl_elf_thp_pagesize (void)
> +{
> + return GL(dl_elf_thp_pagesize);
> +}
> +#else
> +# define get_dl_thp_mode() __get_thp_mode ()
> +# define get_dl_elf_thp_pagesize() __get_thp_size ()
> +#endif
> +
> static __always_inline int
> do_set_hugetlb (size_t value)
> {
> + /* If __get_thp_mode and __get_thp_size have been called during
> + startup, don't call them again here. */
> + enum thp_mode_t thp_mode = get_dl_thp_mode ();
> +
> /* Enable THP if MALLOC_DEFAULT_THP_PAGESIZE is non-zero. */
> if (MALLOC_DEFAULT_THP_PAGESIZE > 0)
> {
> - mp_.thp_mode = thp_mode_madvise;
> + /* If thp_mode is unknown, THP segment load is disabled by
> + GLIBC_TUNABLES=glibc.elf.thp=0. In this case, set
> + mp_.thp_mode to madvise. Otherwise, set it to thp_mode to
> + keep mp_.thp_mode in sync with GL(dl_thp_mode). */
> + if (thp_mode == thp_mode_unknown)
> + mp_.thp_mode = thp_mode_madvise;
> + else
> + mp_.thp_mode = thp_mode;
> mp_.thp_pagesize = MALLOC_DEFAULT_THP_PAGESIZE;
> }
>
When glibc.elf.thp=1, it now mirrors the real kernel mode instead of defaulting
to thp_mode_madvise. The madvise_thp will then stops issuing MADV_HUGEPAGE, which
I think benign/beneficial (kernel already covers always; never madvise was wasteful).
But it means glibc.malloc.hugetlb behavior on aarch64 now depends on glibc.elf.thp;
two previously independent tunables. The default is unchanged:
elf.thp unset -> disabled -> unknown -> old forced-madvise
So it only matters when the tunable is set. Maybe add a note in the manual/NEWS
about this change.
> @@ -4680,9 +4708,27 @@ do_set_hugetlb (size_t value)
> if (MALLOC_DEFAULT_THP_PAGESIZE > 0)
> return 0;
>
> - mp_.thp_mode = __get_thp_mode ();
> - if (mp_.thp_mode == thp_mode_madvise || mp_.thp_mode == thp_mode_always)
> - mp_.thp_pagesize = __get_thp_size ();
> + if (thp_mode == thp_mode_unknown)
> + {
> + /* Call __get_thp_mode and __get_thp_size when THP segment load
> + is disabled. */
> + mp_.thp_mode = __get_thp_mode ();
> + if (mp_.thp_mode == thp_mode_madvise
> + || mp_.thp_mode == thp_mode_always)
> + mp_.thp_pagesize = __get_thp_size ();
> + }
> + else
> + {
> + /* THP segment load is enabled. GL(dl_elf_thp_pagesize) is
> + set to DL_MAP_DEFAULT_THP_PAGESIZE if it isn't zero . In
Stray space after 'zero'.
> + this case, call __get_thp_size () instead of using
> + DL_MAP_DEFAULT_THP_PAGESIZE for malloc. */
> + mp_.thp_mode = thp_mode;
> + if (DL_MAP_DEFAULT_THP_PAGESIZE != 0)
> + mp_.thp_pagesize = __get_thp_size ();
> + else
> + mp_.thp_pagesize = get_dl_elf_thp_pagesize ();
> + }
> }
> else if (value >= 2)
> __get_hugepage_config (value == 2 ? 0 : value, &mp_.hp_pagesize,
When elf.thp=1, malloc uses GL(dl_elf_thp_pagesize), which ld.so caps at
MAX_THP_PAGESIZE; when elf.thp=0/unset, malloc uses the uncapped __get_thp_size().
I think we should not matter for current supported ABIs, but maybe we should
consolidate the logic.
> diff --git a/sysdeps/generic/hugepages.h b/sysdeps/generic/hugepages.h
> index 8e54661527..49251d94ab 100644
> --- a/sysdeps/generic/hugepages.h
> +++ b/sysdeps/generic/hugepages.h
> @@ -37,7 +37,8 @@ unsigned long int __get_thp_size (void) attribute_hidden;
>
> enum thp_mode_t
> {
> - thp_mode_not_supported = 0,
> + thp_mode_unknown = 0,
> + thp_mode_not_supported,
> thp_mode_always,
> thp_mode_madvise,
> thp_mode_never
> --
> 2.54.0
>
More information about the Libc-alpha
mailing list