support: Skip malloc hugetlb={1, 2} variants when kernel cannot honor them
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
The malloc test variants run with GLIBC_TUNABLES=glibc.malloc.hugetlb=1
exercise transparent huge pages via MADV_HUGEPAGE, which is only
meaningful when /sys/kernel/mm/transparent_hugepage/enabled is set to
'madvise' ('always' makes the madvise redundant and 'never' makes it
ineffective). The hugetlb=2 variants rely on MAP_HUGETLB, which
requires a positive /proc/sys/vm/nr_hugepages. On systems that do not
satisfy these prerequisites - including any non-Linux target - those
runs only consume CPU time in this case.
Add support/support_check_hugetlb.{c,h} exposing:
- support_thp_is_madvise: true iff THP is in 'madvise' mode;
- support_hugepages_reserved: true iff nr_hugepages > 0;
- support_check_malloc_hugetlb: inspects GLIBC_TUNABLES and calls
FAIL_UNSUPPORTED when the requested hugetlb mode cannot be honored.
Gate the check at compile time so only the variant binaries pay for
it.
---
Rules | 6 +-
malloc/Makefile | 13 +++++
support/Makefile | 1 +
support/support_check_hugetlb.c | 99 +++++++++++++++++++++++++++++++++
support/support_check_hugetlb.h | 48 ++++++++++++++++
support/test-driver.c | 10 ++++
6 files changed, 175 insertions(+), 2 deletions(-)
create mode 100644 support/support_check_hugetlb.c
create mode 100644 support/support_check_hugetlb.h
Comments
Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
> diff --git a/malloc/Makefile b/malloc/Makefile
> index f95bca7f8fa..72aa77af206 100644
> --- a/malloc/Makefile
> +++ b/malloc/Makefile
> @@ -351,6 +351,19 @@ $(objpfx)%-threaded-worker.o: CPPFLAGS += -DTEST_IN_THREAD=TEST_THREAD_WORKER
> $(objpfx)%-threaded-worker.o: %.c $(before-compile)
> $(compile-command.c)
>
> +# Compile the hugetlb={1,2} test variants from their base sources with
> +# -DTEST_HUGETLB so support/test-driver.c gates them on kernel support.
We really should have called that test-driver.h or something; at first
glance I thought "that can't work" but the .c file is always #included.
Sigh.
> diff --git a/support/support_check_hugetlb.c b/support/support_check_hugetlb.c
> +bool
> +support_thp_is_madvise (void)
> +{
> +#ifdef __linux__
There's nothing in here that is linux-specific at compile time, might as
well leave it unconditional.
> + FILE *fp = fopen ("/sys/kernel/mm/transparent_hugepage/enabled", "r");
> + if (fp == NULL)
> + return false;
> + char line[256];
> + bool madvise = false;
> + if (fgets (line, sizeof (line), fp) != NULL)
Could use fread instead; the whole file fits in the buffer and fread
allows you to avoid buffer overflow.
> +bool
> +support_hugepages_reserved (void)
> +{
> +#ifdef __linux__
Same here, nothing in this is Linux-specific at compile time.
> +/* Returns true if TUNABLES (a GLIBC_TUNABLES-style colon-separated list)
> + contains an entry of the form NAME=VALUE. */
Why can't you just use tunable_get() ? That would avoid complex parsing
code and support future (i.e. current ;) efforts to pull in tunables
from other sources than the environment variable. Yes, I know we expect
this from the Makefile, but still, tunable_get() alrady does what you
want.
> +static bool
> +tunable_equals (const char *tunables, const char *name, const char *value)
> +void
> +support_check_malloc_hugetlb (void)
> +{
On 26/05/26 18:52, DJ Delorie wrote:
> Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
>> diff --git a/malloc/Makefile b/malloc/Makefile
>> index f95bca7f8fa..72aa77af206 100644
>> --- a/malloc/Makefile
>> +++ b/malloc/Makefile
>> @@ -351,6 +351,19 @@ $(objpfx)%-threaded-worker.o: CPPFLAGS += -DTEST_IN_THREAD=TEST_THREAD_WORKER
>> $(objpfx)%-threaded-worker.o: %.c $(before-compile)
>> $(compile-command.c)
>>
>> +# Compile the hugetlb={1,2} test variants from their base sources with
>> +# -DTEST_HUGETLB so support/test-driver.c gates them on kernel support.
>
> We really should have called that test-driver.h or something; at first
> glance I thought "that can't work" but the .c file is always #included.
> Sigh.
>
>> diff --git a/support/support_check_hugetlb.c b/support/support_check_hugetlb.c
>
>> +bool
>> +support_thp_is_madvise (void)
>> +{
>> +#ifdef __linux__
>
> There's nothing in here that is linux-specific at compile time, might as
> well leave it unconditional.
Alright, although the sysfs is really a Linux interface.
>
>> + FILE *fp = fopen ("/sys/kernel/mm/transparent_hugepage/enabled", "r");
>> + if (fp == NULL)
>> + return false;
>> + char line[256];
>> + bool madvise = false;
>> + if (fgets (line, sizeof (line), fp) != NULL)
>
> Could use fread instead; the whole file fits in the buffer and fread
> allows you to avoid buffer overflow.
Ack, I will used something similar to __get_thp_mode.
>
>> +bool
>> +support_hugepages_reserved (void)
>> +{
>> +#ifdef __linux__
>
> Same here, nothing in this is Linux-specific at compile time.
>
>> +/* Returns true if TUNABLES (a GLIBC_TUNABLES-style colon-separated list)
>> + contains an entry of the form NAME=VALUE. */
>
> Why can't you just use tunable_get() ? That would avoid complex parsing
> code and support future (i.e. current ;) efforts to pull in tunables
> from other sources than the environment variable. Yes, I know we expect
> this from the Makefile, but still, tunable_get() alrady does what you
> want.
I tried to avoid using GLIBC_PRIVATE and other private interfaces on libsupport,
but TUNABLE_GET_FULL does simplify things.
>
>> +static bool
>> +tunable_equals (const char *tunables, const char *name, const char *value)
>
>> +void
>> +support_check_malloc_hugetlb (void)
>> +{
>
Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> writes:
>> There's nothing in here that is linux-specific at compile time, might as
>> well leave it unconditional.
>
> Alright, although the sysfs is really a Linux interface.
Sure, but it's a file, and it just doesn't exist elsewhere, so fopen()
is sufficient to "test" for it. And there's no reason why non-Linux
devs couldn't just make a plain file there to test their
contributions...
@@ -288,7 +288,8 @@ $(addprefix $(objpfx),$(binaries-malloc-check-tests)): %-malloc-check: %.o \
endif
ifneq "$(strip $(binaries-malloc-hugetlb1-tests))" ""
-$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o \
+$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): \
+%-malloc-hugetlb1: %-malloc-hugetlb1.o \
$(link-extra-libs-tests) \
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
@@ -296,7 +297,8 @@ $(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o
endif
ifneq "$(strip $(binaries-malloc-hugetlb2-tests))" ""
-$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): %-malloc-hugetlb2: %.o \
+$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): \
+%-malloc-hugetlb2: %-malloc-hugetlb2.o \
$(link-extra-libs-tests) \
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
@@ -351,6 +351,19 @@ $(objpfx)%-threaded-worker.o: CPPFLAGS += -DTEST_IN_THREAD=TEST_THREAD_WORKER
$(objpfx)%-threaded-worker.o: %.c $(before-compile)
$(compile-command.c)
+# Compile the hugetlb={1,2} test variants from their base sources with
+# -DTEST_HUGETLB so support/test-driver.c gates them on kernel support.
+$(foreach t,$(tests-malloc-hugetlb1),\
+ $(eval libof-$(t)-malloc-hugetlb1 := testsuite))
+$(foreach t,$(tests-malloc-hugetlb2),\
+ $(eval libof-$(t)-malloc-hugetlb2 := testsuite))
+$(objpfx)%-malloc-hugetlb1.o: CPPFLAGS += -DTEST_HUGETLB
+$(objpfx)%-malloc-hugetlb1.o: %.c $(before-compile)
+ $(compile-command.c)
+$(objpfx)%-malloc-hugetlb2.o: CPPFLAGS += -DTEST_HUGETLB
+$(objpfx)%-malloc-hugetlb2.o: %.c $(before-compile)
+ $(compile-command.c)
+
# Include the cleanup handler.
aux := set-freeres thread-freeres
@@ -55,6 +55,7 @@ libsupport-routines = \
support_can_chroot \
support_capture_subprocess \
support_capture_subprocess_check \
+ support_check_hugetlb \
support_check_nss \
support_check_stat_fd \
support_check_stat_path \
new file mode 100644
@@ -0,0 +1,99 @@
+/* Runtime detection of huge-page support for malloc tests.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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.1 of the License, or (at your option) any later version.
+
+ The GNU C 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 the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/support_check_hugetlb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+
+bool
+support_thp_is_madvise (void)
+{
+#ifdef __linux__
+ FILE *fp = fopen ("/sys/kernel/mm/transparent_hugepage/enabled", "r");
+ if (fp == NULL)
+ return false;
+ char line[256];
+ bool madvise = false;
+ if (fgets (line, sizeof (line), fp) != NULL)
+ madvise = strstr (line, "[madvise]") != NULL;
+ fclose (fp);
+ return madvise;
+#else
+ return false;
+#endif
+}
+
+bool
+support_hugepages_reserved (void)
+{
+#ifdef __linux__
+ FILE *fp = fopen ("/proc/sys/vm/nr_hugepages", "r");
+ if (fp == NULL)
+ return false;
+ unsigned long n = 0;
+ bool any = fscanf (fp, "%lu", &n) == 1 && n > 0;
+ fclose (fp);
+ return any;
+#else
+ return false;
+#endif
+}
+
+/* Returns true if TUNABLES (a GLIBC_TUNABLES-style colon-separated list)
+ contains an entry of the form NAME=VALUE. */
+static bool
+tunable_equals (const char *tunables, const char *name, const char *value)
+{
+ size_t name_len = strlen (name);
+ size_t value_len = strlen (value);
+ const char *p = tunables;
+ while (*p != '\0')
+ {
+ if (strncmp (p, name, name_len) == 0
+ && p[name_len] == '='
+ && strncmp (p + name_len + 1, value, value_len) == 0
+ && (p[name_len + 1 + value_len] == '\0'
+ || p[name_len + 1 + value_len] == ':'))
+ return true;
+ const char *colon = strchr (p, ':');
+ if (colon == NULL)
+ break;
+ p = colon + 1;
+ }
+ return false;
+}
+
+void
+support_check_malloc_hugetlb (void)
+{
+ const char *tunables = getenv ("GLIBC_TUNABLES");
+ if (tunables == NULL)
+ return;
+ if (tunable_equals (tunables, "glibc.malloc.hugetlb", "1")
+ && !support_thp_is_madvise ())
+ FAIL_UNSUPPORTED ("glibc.malloc.hugetlb=1 requires"
+ " /sys/kernel/mm/transparent_hugepage/enabled"
+ " = madvise");
+ if (tunable_equals (tunables, "glibc.malloc.hugetlb", "2")
+ && !support_hugepages_reserved ())
+ FAIL_UNSUPPORTED ("glibc.malloc.hugetlb=2 requires"
+ " /proc/sys/vm/nr_hugepages > 0");
+}
new file mode 100644
@@ -0,0 +1,48 @@
+/* Runtime detection of huge-page support for malloc tests.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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.1 of the License, or (at your option) any later version.
+
+ The GNU C 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 the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef SUPPORT_SUPPORT_CHECK_HUGETLB_H
+#define SUPPORT_SUPPORT_CHECK_HUGETLB_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Returns true if /sys/kernel/mm/transparent_hugepage/enabled selects
+ `madvise' as the active mode (i.e. MADV_HUGEPAGE is honored, but
+ THP is not applied automatically). Returns false on any other
+ configuration, on read failure, or on non-Linux systems. */
+bool support_thp_is_madvise (void);
+
+/* Returns true if /proc/sys/vm/nr_hugepages reports a strictly
+ positive number of pre-allocated huge pages (the prerequisite for
+ MAP_HUGETLB allocations). Returns false on read failure or on
+ non-Linux systems. */
+bool support_hugepages_reserved (void);
+
+/* If the current process is running with GLIBC_TUNABLES requesting
+ glibc.malloc.hugetlb=1 or glibc.malloc.hugetlb=2, verifies that the
+ kernel can actually satisfy the requested mode. If not, terminates
+ the test with EXIT_UNSUPPORTED. No-op when no such tunable is set,
+ so it is safe to call unconditionally. */
+void support_check_malloc_hugetlb (void);
+
+__END_DECLS
+
+#endif /* SUPPORT_SUPPORT_CHECK_HUGETLB_H */
@@ -103,9 +103,19 @@
#include <string.h>
+#ifdef TEST_HUGETLB
+# include <support/support_check_hugetlb.h>
+#endif
+
int
main (int argc, char **argv)
{
+#ifdef TEST_HUGETLB
+ /* For malloc hugetlb=1/=2 test variants: skip the test (UNSUPPORTED)
+ when the kernel cannot honor the requested mode. */
+ support_check_malloc_hugetlb ();
+#endif
+
struct test_config test_config;
memset (&test_config, 0, sizeof (test_config));