[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260202005451.774496-7-usamaarif642@gmail.com>
Date: Sun, 1 Feb 2026 16:50:23 -0800
From: Usama Arif <usamaarif642@...il.com>
To: ziy@...dia.com,
Andrew Morton <akpm@...ux-foundation.org>,
David Hildenbrand <david@...nel.org>,
lorenzo.stoakes@...cle.com,
linux-mm@...ck.org
Cc: hannes@...xchg.org,
riel@...riel.com,
shakeel.butt@...ux.dev,
kas@...nel.org,
baohua@...nel.org,
dev.jain@....com,
baolin.wang@...ux.alibaba.com,
npache@...hat.com,
Liam.Howlett@...cle.com,
ryan.roberts@....com,
vbabka@...e.cz,
lance.yang@...ux.dev,
linux-kernel@...r.kernel.org,
kernel-team@...a.com,
Usama Arif <usamaarif642@...il.com>
Subject: [RFC 06/12] selftests/mm: add PUD THP basic allocation test
Add a selftest for PUD-level THPs (1GB THPs) with test infrastructure
and a basic allocation test.
The test uses the kselftest harness FIXTURE/TEST_F framework. A shared
fixture allocates a 2GB anonymous mapping and computes a PUD-aligned
address within it. Helper functions read THP counters from /proc/vmstat
and mTHP statistics from sysfs.
The basic allocation test verifies the fundamental PUD THP allocation
path by touching a PUD-aligned region and checking that the mTHP
anon_fault_alloc counter increments, confirming a 1GB folio was
allocated.
Signed-off-by: Usama Arif <usamaarif642@...il.com>
---
tools/testing/selftests/mm/Makefile | 1 +
tools/testing/selftests/mm/pud_thp_test.c | 161 ++++++++++++++++++++++
2 files changed, 162 insertions(+)
create mode 100644 tools/testing/selftests/mm/pud_thp_test.c
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index eaf9312097f7b..ab79f1693941a 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -88,6 +88,7 @@ TEST_GEN_FILES += pagemap_ioctl
TEST_GEN_FILES += pfnmap
TEST_GEN_FILES += process_madv
TEST_GEN_FILES += prctl_thp_disable
+TEST_GEN_FILES += pud_thp_test
TEST_GEN_FILES += thuge-gen
TEST_GEN_FILES += transhuge-stress
TEST_GEN_FILES += uffd-stress
diff --git a/tools/testing/selftests/mm/pud_thp_test.c b/tools/testing/selftests/mm/pud_thp_test.c
new file mode 100644
index 0000000000000..6f0c02c6afd3a
--- /dev/null
+++ b/tools/testing/selftests/mm/pud_thp_test.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test program for PUD-level Transparent Huge Pages (1GB anonymous THP)
+ *
+ * Prerequisites:
+ * - Kernel with PUD THP support (CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)
+ * - THP enabled: echo always > /sys/kernel/mm/transparent_hugepage/enabled
+ * - PUD THP enabled: echo always > /sys/kernel/mm/transparent_hugepage/hugepages-1048576kB/enabled
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/syscall.h>
+
+#include "kselftest_harness.h"
+
+#define PUD_SIZE (1UL << 30) /* 1GB */
+#define PMD_SIZE (1UL << 21) /* 2MB */
+#define PAGE_SIZE (1UL << 12) /* 4KB */
+
+#define TEST_REGION_SIZE (2 * PUD_SIZE) /* 2GB to ensure PUD alignment */
+
+/* Get PUD-aligned address within a region */
+static inline void *pud_align(void *addr)
+{
+ return (void *)(((unsigned long)addr + PUD_SIZE - 1) & ~(PUD_SIZE - 1));
+}
+
+/* Read vmstat counter */
+static unsigned long read_vmstat(const char *name)
+{
+ FILE *fp;
+ char line[256];
+ unsigned long value = 0;
+
+ fp = fopen("/proc/vmstat", "r");
+ if (!fp)
+ return 0;
+
+ while (fgets(line, sizeof(line), fp)) {
+ if (strncmp(line, name, strlen(name)) == 0 &&
+ line[strlen(name)] == ' ') {
+ sscanf(line + strlen(name), " %lu", &value);
+ break;
+ }
+ }
+ fclose(fp);
+ return value;
+}
+
+/* Read mTHP stats for PUD order (1GB = 1048576kB) */
+static unsigned long read_mthp_stat(const char *stat_name)
+{
+ char path[256];
+ char buf[64];
+ int fd;
+ ssize_t ret;
+ unsigned long value = 0;
+
+ snprintf(path, sizeof(path),
+ "/sys/kernel/mm/transparent_hugepage/hugepages-1048576kB/stats/%s",
+ stat_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret <= 0)
+ return 0;
+ buf[ret] = '\0';
+ sscanf(buf, "%lu", &value);
+ return value;
+}
+
+/* Check if PUD THP is enabled */
+static int pud_thp_enabled(void)
+{
+ char buf[64];
+ int fd;
+ ssize_t ret;
+
+ fd = open("/sys/kernel/mm/transparent_hugepage/hugepages-1048576kB/enabled", O_RDONLY);
+ if (fd < 0)
+ return 0;
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret <= 0)
+ return 0;
+ buf[ret] = '\0';
+
+ /* Check if [always] or [madvise] is set */
+ if (strstr(buf, "[always]") || strstr(buf, "[madvise]"))
+ return 1;
+ return 0;
+}
+
+/*
+ * Main fixture for PUD THP tests
+ * Allocates a 2GB region and provides a PUD-aligned pointer within it
+ */
+FIXTURE(pud_thp)
+{
+ void *mem; /* Base mmap allocation */
+ void *aligned; /* PUD-aligned pointer within mem */
+ unsigned long mthp_alloc_before;
+ unsigned long split_before;
+};
+
+FIXTURE_SETUP(pud_thp)
+{
+ if (!pud_thp_enabled())
+ SKIP(return, "PUD THP not enabled in sysfs");
+
+ self->mthp_alloc_before = read_mthp_stat("anon_fault_alloc");
+ self->split_before = read_vmstat("thp_split_pud");
+
+ self->mem = mmap(NULL, TEST_REGION_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(self->mem, MAP_FAILED);
+
+ self->aligned = pud_align(self->mem);
+}
+
+FIXTURE_TEARDOWN(pud_thp)
+{
+ if (self->mem && self->mem != MAP_FAILED)
+ munmap(self->mem, TEST_REGION_SIZE);
+}
+
+/*
+ * Test: Basic PUD THP allocation
+ * Verifies that touching a PUD-aligned region allocates a PUD THP
+ */
+TEST_F(pud_thp, basic_allocation)
+{
+ unsigned long mthp_alloc_after;
+
+ /* Touch memory to trigger page fault and PUD THP allocation */
+ memset(self->aligned, 0xAB, PUD_SIZE);
+
+ mthp_alloc_after = read_mthp_stat("anon_fault_alloc");
+
+ /*
+ * If mTHP allocation counter increased, a PUD THP was allocated.
+ */
+ if (mthp_alloc_after <= self->mthp_alloc_before)
+ SKIP(return, "PUD THP not allocated");
+
+ TH_LOG("PUD THP allocated (anon_fault_alloc: %lu -> %lu)",
+ self->mthp_alloc_before, mthp_alloc_after);
+}
+
+TEST_HARNESS_MAIN
--
2.47.3
Powered by blists - more mailing lists