On Linux 6.10.10 with CONFIG_READ_ONLY_THP_FOR_FS=y, madvise(MADV_COLLAPSE) on program text fails with EINVAL. To reproduce, compile the reproducer with clang -g -o text-hugepage text-hugepage.c \ -fuse-ld=lld \ -Wl,-zcommon-page-size=2097152 -Wl,-zmax-page-size=2097152 \ -Wl,-z,separate-loadable-segments and run: $ strace -e trace=madvise ./text-hugepage madvise(0x400000, 2097152, MADV_HUGEPAGE) = 0 madvise(0x400000, 2097152, MADV_POPULATE_READ) = 0 madvise(0x400000, 2097152, MADV_COLLAPSE) = -1 EINVAL (Invalid argument) (the funky linker options are needed to make sure the .text vma spans a hugepage). I say "possible regression" since I haven't tried it with an older kernel, but I believe it worked at some point or other seeing that others managed to get it to work. ==== text-hugepage.c ==== #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include <sys/mman.h> static void try_remap_text_segment() { FILE *fp = fopen("/proc/self/maps", "r"); if (!fp) { return; } char *buf = NULL; size_t n; while (getline(&buf, &n, fp) >= 0) { char *lstart = buf; char *lmid = strchr(lstart, '-'); if (!lmid) { continue; } *lmid++ = '\0'; char *lend = strchr(lmid, ' '); if (!lend) { continue; } *lend = '\0'; size_t start = strtoul(lstart, NULL, 16); size_t end = strtoul(lmid, NULL, 16); uintptr_t some_text_addr = (uintptr_t)&try_remap_text_segment; if (some_text_addr >= start && some_text_addr < end) { end &= ~(uintptr_t)0x1fffff; madvise((void*)start, end - start, MADV_HUGEPAGE); madvise((void*)start, end - start, MADV_POPULATE_READ); madvise((void*)start, end - start, MADV_COLLAPSE); break; } } free(buf); fclose(fp); } void huge_function() { // Make sure .text is has a huge page full of stuff asm volatile (".fill 4000000, 1, 0x90"); } int main() { try_remap_text_segment(); } ==== end text-hugepage.c ====