[RFC] Are HugeTLB pages supported for building and running glibc?

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Fri Aug 15 15:54:29 GMT 2025



On 14/08/25 12:27, William Hunt wrote:
> When I enable a small number of HugeTLB pages such as 20 and try to run glibc 
> subdir tests with HugeTLB pages (i.e. glibc.malloc.hugetlb=2) on master branch,
> the system glibc crashes showing SIGBUS errors in stdlib. This happens even for
> subdirs that don't have exclusions for hugetlb2 test variants, such as elf. 
> 
> Even when running with a very large number of HugeTLB pages such as 16384, 
> there are usually some stdlib tests that fail, such as tst-arc4random-thread. 
> Below are some example results for x86-64, where stdlib/tst-arc4random-thread, 
> stdlib/isomac and stdlib/tst-atexit fail.
> 
> 		=== Summary of results ===
>       3 FAIL
>    7040 PASS
>      89 UNSUPPORTED
>      16 XFAIL
>       4 XPASS
> 
> Since tests are ran such that tunables are globally set, I am unsure whether 
> normal tests don't reset to glibc.malloc.hugetlb=0, or whether building and 
> running tests with huge pages was ever supported for glibc. As I'm developing 
> a branch that enables HugeTLB support for mremap and cannot get all tests to
> consistently pass, I would be curious if this is something I can be expected
> to fix?

I think this is an inherent problem on how hugepage reservation works on Linux.
>From Documentation/mm/hugetlbfs_reserv.rst:

  Huge pages as described at Documentation/admin-guide/mm/hugetlbpage.rst are
  typically preallocated for application use.  These huge pages are instantiated
  in a task's address space at page fault time if the VMA indicates huge pages
  are to be used.  If no huge page exists at page fault time, the task is sent
  a SIGBUS and often dies an unhappy death.

My understanding is if concurrent processes allocates more pages than the
available (HugePages_Free) they can be subject to SIGBUS when accessing the pages.
Assuming a system with 512 2MB huge pages reserved:

$ cat /proc/meminfo | grep ^HugePages
HugePages_Total:     512
HugePages_Free:      512
HugePages_Rsvd:        0
HugePages_Surp:        0
$ cat test.c

#include <assert.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <signal.h>

static const size_t hugepage_size = 2097152;
static const size_t npages = 512;
static const size_t pages_size = npages * hugepage_size;

static char *pages;

static void
sigbus_handler (int s)
{
  char msg[512];
  ssize_t msgsize = snprintf (msg, sizeof msg, "[%jd] SIGBUS\n",
                              (intmax_t) getpid ());
  write (STDERR_FILENO, msg, msgsize);
  _exit (EXIT_FAILURE);
}

static void
touch_pages ()
{
  for (int i = 0; i < npages; i++)
    pages[i*hugepage_size] = 0xcc;
  printf ("[%jd] touch_pages\n", (intmax_t) getpid ());
}

int main ()
{
  sigaction (SIGBUS, &(struct sigaction) { .sa_handler = sigbus_handler },
             NULL);
  pages = mmap (NULL,
                pages_size,
                PROT_READ | PROT_WRITE,
                MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB,
                0,
                0);
  assert (pages != MAP_FAILED);

  pid_t pid = fork ();
  assert (pid != -1);

  touch_pages ();

  if (pid != 0)
    waitpid (pid, NULL, 0);
}
$ ./t
[4034938] SIGBUS
[4034937] touch_pages

You can mitigate this to add overcommit pages hugepages to the pool:

$ echo 512 | sudo tee /proc/sys/vm/nr_overcommit_hugepages
$ cat /proc/meminfo | grep ^HugePages
HugePages_Total:     512
HugePages_Free:      512
HugePages_Rsvd:        0
HugePages_Surp:        0
$ ./t
[4035239] touch_pages
[4035238] touch_pages

The Documentation/admin-guide/mm/hugetlbpage.rst have a lot of more information.

It also seems that MAP_HUGETLB does not seems to be influenced by memory
overcommit (vm.overcommit_memory = 2), at least it seems that COW is always
enabled even at fork (meaning that SIGBUG will be generated for any
value of vm.overcommit_memory).


More information about the Libc-alpha mailing list