* [PATCH v9 1/6] selftests/mm: make file helpers return errors
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
@ 2026-09-04 12:36 ` Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 2/6] tools/lib/mm: add shared file helpers Sarthak Sharma
` (6 subsequent siblings)
7 siblings, 0 replies; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-04 12:36 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel, Sarthak Sharma
Change read_file(), write_file(), read_num(), write_num() and
write_num_ignore_einval() in vm_util.c to report failures to callers
instead of exiting from the helper.
Make read_file() return a negative errno on failure and 0 on success, so
callers can distinguish a successful read from an I/O error. Also make
read_num() reject negative and malformed values.
Keep write_num_ignore_einval() silent for -EINVAL while returning other
errors to its caller.
Update callers to print diagnostics and fail wherever required. Modify
a comment which implies write_num() uses ksft_exit_fail_msg(). Also add
a helper print_file_access_error() in hugepage_settings.c to print
TAP-compatible errors without a kselftest dependency. This prepares the
helpers to be moved to tools/lib/mm without a kselftest dependency.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
.../testing/selftests/mm/hugepage_settings.c | 98 +++++++++++---
.../selftests/mm/hugetlb-soft-offline.c | 16 ++-
tools/testing/selftests/mm/khugepaged.c | 14 +-
.../selftests/mm/split_huge_page_test.c | 5 +-
tools/testing/selftests/mm/vm_util.c | 122 ++++++++++++------
tools/testing/selftests/mm/vm_util.h | 8 +-
6 files changed, 192 insertions(+), 71 deletions(-)
diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c
index d7917dce3aba..5bcda01ac4f6 100644
--- a/tools/testing/selftests/mm/hugepage_settings.c
+++ b/tools/testing/selftests/mm/hugepage_settings.c
@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include "vm_util.h"
#include "hugepage_settings.h"
@@ -48,6 +49,11 @@ static const char * const shmem_enabled_strings[] = {
NULL
};
+static void print_file_access_error(const char *path, int ret)
+{
+ printf("# %s: %s (%d)\n", path, strerror(-ret), -ret);
+}
+
int thp_read_string(const char *name, const char * const strings[])
{
char path[PATH_MAX];
@@ -61,8 +67,9 @@ int thp_read_string(const char *name, const char * const strings[])
exit(EXIT_FAILURE);
}
- if (!read_file(path, buf, sizeof(buf))) {
- perror(path);
+ ret = read_file(path, buf, sizeof(buf));
+ if (ret) {
+ print_file_access_error(path, ret);
exit(EXIT_FAILURE);
}
@@ -103,12 +110,17 @@ void thp_write_string(const char *name, const char *val)
printf("%s: Pathname is too long\n", __func__);
exit(EXIT_FAILURE);
}
- write_file(path, val, strlen(val) + 1);
+ ret = write_file(path, val, strlen(val) + 1);
+ if (ret) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
}
unsigned long thp_read_num(const char *name)
{
char path[PATH_MAX];
+ unsigned long num;
int ret;
ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
@@ -116,7 +128,13 @@ unsigned long thp_read_num(const char *name)
printf("%s: Pathname is too long\n", __func__);
exit(EXIT_FAILURE);
}
- return read_num(path);
+ ret = read_num(path, &num);
+ if (ret) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
+
+ return num;
}
void thp_write_num(const char *name, unsigned long num)
@@ -129,7 +147,11 @@ void thp_write_num(const char *name, unsigned long num)
printf("%s: Pathname is too long\n", __func__);
exit(EXIT_FAILURE);
}
- write_num(path, num);
+ ret = write_num(path, num);
+ if (ret) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
}
void thp_read_settings(struct thp_settings *settings)
@@ -157,8 +179,15 @@ void thp_read_settings(struct thp_settings *settings)
.max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared"),
.pages_to_scan = thp_read_num("khugepaged/pages_to_scan"),
};
- if (dev_queue_read_ahead_path[0])
- settings->read_ahead_kb = read_num(dev_queue_read_ahead_path);
+ if (dev_queue_read_ahead_path[0]) {
+ int ret = read_num(dev_queue_read_ahead_path,
+ &settings->read_ahead_kb);
+
+ if (ret) {
+ print_file_access_error(dev_queue_read_ahead_path, ret);
+ exit(EXIT_FAILURE);
+ }
+ }
for (i = 0; i < NR_ORDERS; i++) {
if (!((1 << i) & orders)) {
@@ -208,8 +237,15 @@ void thp_write_settings(struct thp_settings *settings)
thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
- if (dev_queue_read_ahead_path[0])
- write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
+ if (dev_queue_read_ahead_path[0]) {
+ int ret = write_num(dev_queue_read_ahead_path,
+ settings->read_ahead_kb);
+
+ if (ret) {
+ print_file_access_error(dev_queue_read_ahead_path, ret);
+ exit(EXIT_FAILURE);
+ }
+ }
for (i = 0; i < NR_ORDERS; i++) {
if (!((1 << i) & orders))
@@ -307,8 +343,15 @@ static unsigned long __thp_supported_orders(bool is_shmem)
}
ret = read_file(path, buf, sizeof(buf));
- if (ret)
- orders |= 1UL << i;
+ if (ret) {
+ if (ret != -ENOENT) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
+ continue;
+ }
+
+ orders |= 1UL << i;
}
return orders;
@@ -382,8 +425,7 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
continue;
sizes[count++] = kb * 1024;
- ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
- kb);
+ printf("# [INFO] detected hugetlb page size: %zu KiB\n", kb);
}
closedir(dir);
return count;
@@ -425,28 +467,49 @@ static void hugetlb_sysfs_path(char *buf, size_t buflen,
unsigned long hugetlb_nr_pages(unsigned long size)
{
char path[PATH_MAX];
+ unsigned long nr;
+ int ret;
hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages");
- return read_num(path);
+ ret = read_num(path, &nr);
+ if (ret) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
+
+ return nr;
}
void hugetlb_set_nr_pages(unsigned long size, unsigned long nr)
{
char path[PATH_MAX];
+ int ret;
hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages");
- write_num_ignore_einval(path, nr);
+ ret = write_num_ignore_einval(path, nr);
+ if (ret) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
}
unsigned long hugetlb_free_pages(unsigned long size)
{
char path[PATH_MAX];
+ unsigned long nr;
+ int ret;
hugetlb_sysfs_path(path, sizeof(path), size, "free_hugepages");
- return read_num(path);
+ ret = read_num(path, &nr);
+ if (ret) {
+ print_file_access_error(path, ret);
+ exit(EXIT_FAILURE);
+ }
+
+ return nr;
}
static bool __hugetlb_setup(unsigned long size, unsigned long nr)
@@ -502,7 +565,8 @@ unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
return 0;
if (nr_enabled > max) {
- ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max);
+ printf("# detected %d huge page sizes, will only test %d\n",
+ nr_enabled, max);
nr_enabled = max;
}
diff --git a/tools/testing/selftests/mm/hugetlb-soft-offline.c b/tools/testing/selftests/mm/hugetlb-soft-offline.c
index 4af9d3db7b5b..ffc85b958c69 100644
--- a/tools/testing/selftests/mm/hugetlb-soft-offline.c
+++ b/tools/testing/selftests/mm/hugetlb-soft-offline.c
@@ -85,8 +85,7 @@ static unsigned long orig_enable_soft_offline = -1UL;
/*
* Runs from an atexit handler, so it must not call anything that
- * exits on failure: write_num() would re-enter exit() through
- * ksft_exit_fail_msg().
+ * exits on failure.
*/
static void restore_enable_soft_offline(void)
{
@@ -152,7 +151,10 @@ static void test_soft_offline_common(int enable_soft_offline)
hugepagesize_kb = file_stat.f_bsize / 1024;
ksft_print_msg("Hugepagesize is %ldkB\n", hugepagesize_kb);
- write_num(ENABLE_SOFT_OFFLINE_PATH, enable_soft_offline);
+ ret = write_num(ENABLE_SOFT_OFFLINE_PATH, enable_soft_offline);
+ if (ret)
+ ksft_exit_fail_msg("Failed to write to %s: %s\n",
+ ENABLE_SOFT_OFFLINE_PATH, strerror(-ret));
nr_hugepages_before = hugetlb_nr_default_pages();
@@ -189,6 +191,8 @@ static void test_soft_offline_common(int enable_soft_offline)
int main(int argc, char **argv)
{
+ int ret;
+
ksft_print_header();
if (!hugetlb_setup_default(8))
@@ -196,7 +200,11 @@ int main(int argc, char **argv)
ksft_set_plan(2);
- orig_enable_soft_offline = read_num(ENABLE_SOFT_OFFLINE_PATH);
+ ret = read_num(ENABLE_SOFT_OFFLINE_PATH, &orig_enable_soft_offline);
+ if (ret)
+ ksft_exit_fail_msg("Failed to read %s: %s\n",
+ ENABLE_SOFT_OFFLINE_PATH, strerror(-ret));
+
atexit(restore_enable_soft_offline);
test_soft_offline_common(1);
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index f82673f5f6b4..6daa22f6da2f 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -122,6 +122,7 @@ static void get_finfo(const char *dir)
char buf[1 << 10];
char path[PATH_MAX];
char *str, *end;
+ int ret;
finfo.dir = dir;
if (stat(finfo.dir, &path_stat))
@@ -142,8 +143,9 @@ static void get_finfo(const char *dir)
major(path_stat.st_dev), minor(path_stat.st_dev))
>= sizeof(path))
ksft_exit_fail_msg("%s: Pathname is too long\n", __func__);
- if (!read_file(path, buf, sizeof(buf)))
- ksft_exit_fail_perror("read_file(uevent)");
+ ret = read_file(path, buf, sizeof(buf));
+ if (ret)
+ ksft_exit_fail_msg("read_file(%s): %s\n", path, strerror(-ret));
if (strstr(buf, "DEVTYPE=disk")) {
/* Found it */
if (snprintf(finfo.dev_queue_read_ahead_path,
@@ -324,7 +326,7 @@ static void *file_setup_area_common(int nr_hpages, enum file_setup_ops setup)
{
const int open_opt = setup == FILE_SETUP_READ_ONLY_FS ? O_RDONLY : O_RDWR;
const int mmap_prot = setup == FILE_SETUP_READ_ONLY_FS ? PROT_READ : (PROT_READ | PROT_WRITE);
- int fd;
+ int fd, ret;
void *p;
unsigned long size;
@@ -362,7 +364,11 @@ static void *file_setup_area_common(int nr_hpages, enum file_setup_ops setup)
ksft_exit_fail_perror("mmap()");
/* Drop page cache */
- write_file("/proc/sys/vm/drop_caches", "3", 2);
+ ret = write_file("/proc/sys/vm/drop_caches", "3", 2);
+ if (ret)
+ ksft_exit_fail_msg("write_file(drop_caches): %s\n",
+ strerror(-ret));
+
success("OK");
return p;
}
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index c01d227d7fd6..a30927514b4f 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -145,7 +145,10 @@ static void write_debugfs(const char *fmt, ...)
if (ret >= INPUT_MAX)
ksft_exit_fail_msg("%s: Debugfs input is too long\n", __func__);
- write_file(SPLIT_DEBUGFS, input, ret + 1);
+ ret = write_file(SPLIT_DEBUGFS, input, ret + 1);
+ if (ret)
+ ksft_exit_fail_msg("write_file(%s): %s\n", SPLIT_DEBUGFS,
+ strerror(-ret));
}
static char *allocate_zero_filled_hugepage(size_t len)
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 80bc9f597b52..c1b8d60e519d 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -891,109 +891,149 @@ int unpoison_memory(unsigned long pfn)
int read_file(const char *path, char *buf, size_t buflen)
{
- int fd;
+ int fd, err;
ssize_t numread;
fd = open(path, O_RDONLY);
if (fd == -1)
- return 0;
+ return -errno;
numread = read(fd, buf, buflen - 1);
if (numread < 1) {
+ err = numread ? errno : ENODATA;
close(fd);
- return 0;
+ return -err;
}
buf[numread] = '\0';
close(fd);
- return (unsigned int) numread;
+ return 0;
}
-static void __write_file(const char *path, const char *buf, size_t buflen, bool ignore_einval)
+int write_file(const char *path, const char *buf, size_t buflen)
{
int fd, saved_errno;
ssize_t numwritten;
if (buflen < 2)
- ksft_exit_fail_msg("Incorrect buffer len: %zu\n", buflen);
+ return -EINVAL;
fd = open(path, O_WRONLY);
if (fd == -1)
- ksft_exit_fail_msg("%s open failed: %s\n", path, strerror(errno));
+ return -errno;
numwritten = write(fd, buf, buflen - 1);
saved_errno = errno;
close(fd);
- errno = saved_errno;
- if (numwritten < 0) {
- if (ignore_einval && errno == EINVAL)
- return;
- ksft_exit_fail_msg("%s write(%.*s) failed: %s\n", path, (int)(buflen - 1),
- buf, strerror(errno));
- }
- if (numwritten != buflen - 1)
- ksft_exit_fail_msg("%s write(%.*s) is truncated, expected %zu bytes, got %zd bytes\n",
- path, (int)(buflen - 1), buf, buflen - 1, numwritten);
-}
-void write_file(const char *path, const char *buf, size_t buflen)
-{
- __write_file(path, buf, buflen, /* ignore_einval = */ false);
+ if (numwritten < 0)
+ return -saved_errno;
+
+ if (numwritten != (ssize_t)(buflen - 1))
+ return -EIO;
+
+ return 0;
}
-unsigned long read_num(const char *path)
+int read_num(const char *path, unsigned long *num)
{
+ unsigned long val;
+ int ret;
char buf[21];
+ char *end;
- if (!read_file(path, buf, sizeof(buf)))
- ksft_exit_fail_perror("read_file()");
+ if (!num)
+ return -EINVAL;
- return strtoul(buf, NULL, 10);
+ ret = read_file(path, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ /* Reject signs and leading whitespace that are accepted by strtoul() */
+ if (buf[0] < '0' || buf[0] > '9')
+ return -EINVAL;
+
+ errno = 0;
+ val = strtoul(buf, &end, 10);
+ if (errno)
+ return -errno;
+
+ /* Only allow a newline after the number */
+ if (*end == '\n')
+ end++;
+
+ if (*end != '\0')
+ return -EINVAL;
+
+ *num = val;
+ return 0;
}
-static void __write_num(const char *path, unsigned long num, bool ignore_einval)
+int write_num(const char *path, unsigned long num)
{
char buf[21];
sprintf(buf, "%lu", num);
- __write_file(path, buf, strlen(buf) + 1, ignore_einval);
+ return write_file(path, buf, strlen(buf) + 1);
}
-void write_num(const char *path, unsigned long num)
+int write_num_ignore_einval(const char *path, unsigned long num)
{
- return __write_num(path, num, /* ignore_einval = */ false);
-}
+ int ret;
-void write_num_ignore_einval(const char *path, unsigned long num)
-{
- return __write_num(path, num, /* ignore_einval = */ true);
+ ret = write_num(path, num);
+ return ret == -EINVAL ? 0 : ret;
}
static unsigned long shmall, shmmax;
void __shm_limits_restore(void)
{
- if (shmmax)
- write_num("/proc/sys/kernel/shmmax", shmmax);
- if (shmall)
- write_num("/proc/sys/kernel/shmall", shmall);
+ int ret;
+
+ if (shmmax) {
+ ret = write_num("/proc/sys/kernel/shmmax", shmmax);
+ if (ret < 0)
+ ksft_exit_fail_msg("Failed to restore shmmax: %s\n",
+ strerror(-ret));
+ }
+ if (shmall) {
+ ret = write_num("/proc/sys/kernel/shmall", shmall);
+ if (ret < 0)
+ ksft_exit_fail_msg("Failed to restore shmall: %s\n",
+ strerror(-ret));
+ }
}
void shm_limits_prepare(unsigned long length)
{
unsigned long nr = length / psize();
unsigned long val;
+ int ret;
+
+ ret = read_num("/proc/sys/kernel/shmmax", &val);
+ if (ret < 0)
+ ksft_exit_fail_msg("Failed to read /proc/sys/kernel/shmmax: %s\n",
+ strerror(-ret));
- val = read_num("/proc/sys/kernel/shmmax");
if (val < length) {
- write_num("/proc/sys/kernel/shmmax", length);
+ ret = write_num("/proc/sys/kernel/shmmax", length);
+ if (ret < 0)
+ ksft_exit_fail_msg("Failed to write %lu to /proc/sys/kernel/shmmax: %s\n",
+ length, strerror(-ret));
shmmax = val;
}
- val = read_num("/proc/sys/kernel/shmall");
+ ret = read_num("/proc/sys/kernel/shmall", &val);
+ if (ret < 0)
+ ksft_exit_fail_msg("Failed to read /proc/sys/kernel/shmall: %s\n",
+ strerror(-ret));
if (val < nr) {
- write_num("/proc/sys/kernel/shmall", nr);
+ ret = write_num("/proc/sys/kernel/shmall", nr);
+ if (ret < 0)
+ ksft_exit_fail_msg("Failed to write %lu to /proc/sys/kernel/shmall: %s\n",
+ nr, strerror(-ret));
shmall = val;
}
}
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 9a49af88702e..62f6f5b42649 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -166,11 +166,11 @@ int unpoison_memory(unsigned long pfn);
#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
-void write_file(const char *path, const char *buf, size_t buflen);
+int write_file(const char *path, const char *buf, size_t buflen);
int read_file(const char *path, char *buf, size_t buflen);
-unsigned long read_num(const char *path);
-void write_num(const char *path, unsigned long num);
-void write_num_ignore_einval(const char *path, unsigned long num);
+int read_num(const char *path, unsigned long *num);
+int write_num(const char *path, unsigned long num);
+int write_num_ignore_einval(const char *path, unsigned long num);
void shm_limits_prepare(unsigned long length);
void __shm_limits_restore(void);
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v9 2/6] tools/lib/mm: add shared file helpers
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 1/6] selftests/mm: make file helpers return errors Sarthak Sharma
@ 2026-09-04 12:36 ` Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 3/6] tools/lib/mm: move hugepage_settings out of selftests Sarthak Sharma
` (5 subsequent siblings)
7 siblings, 0 replies; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-04 12:36 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel, Sarthak Sharma
Move read_file(), write_file(), read_num(), write_num() and
write_num_ignore_einval() out of tools/testing/selftests/mm/vm_util.c
into a new shared helper under tools/lib/mm/.
These helpers are used by mm selftests today and will also be needed by
shared hugepage helpers in subsequent patches. Move them to a generic
location so they can be reused outside selftests as well.
Keep the helpers exposed to mm selftests through vm_util.h by including
the new shared header there, and link the new helper into the
selftests/mm build.
Update the explicit x86 protection_keys 32-bit and 64-bit build rules
to preserve prerequisite paths, now that file_utils.c is built from
tools/lib/mm.
Add tools/lib/mm/ to the MEMORY MANAGEMENT - MISC entry in MAINTAINERS.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
MAINTAINERS | 1 +
tools/lib/mm/file_utils.c | 106 +++++++++++++++++++++++++++
tools/lib/mm/file_utils.h | 13 ++++
tools/testing/selftests/mm/Makefile | 11 +--
tools/testing/selftests/mm/vm_util.c | 97 ------------------------
tools/testing/selftests/mm/vm_util.h | 7 +-
6 files changed, 127 insertions(+), 108 deletions(-)
create mode 100644 tools/lib/mm/file_utils.c
create mode 100644 tools/lib/mm/file_utils.h
diff --git a/MAINTAINERS b/MAINTAINERS
index a9245d827ddb..285c6083ca79 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17294,6 +17294,7 @@ F: mm/memory-tiers.c
F: mm/page_idle.c
F: mm/pgalloc-track.h
F: mm/process_vm_access.c
+F: tools/lib/mm/
F: tools/testing/selftests/mm/
MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION
diff --git a/tools/lib/mm/file_utils.c b/tools/lib/mm/file_utils.c
new file mode 100644
index 000000000000..9b2237e9823e
--- /dev/null
+++ b/tools/lib/mm/file_utils.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "file_utils.h"
+
+int read_file(const char *path, char *buf, size_t buflen)
+{
+ int fd, err;
+ ssize_t numread;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -errno;
+
+ numread = read(fd, buf, buflen - 1);
+ if (numread < 1) {
+ err = numread ? errno : ENODATA;
+ close(fd);
+ return -err;
+ }
+
+ buf[numread] = '\0';
+ close(fd);
+
+ return 0;
+}
+
+int write_file(const char *path, const char *buf, size_t buflen)
+{
+ int fd, saved_errno;
+ ssize_t numwritten;
+
+ if (buflen < 2)
+ return -EINVAL;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -errno;
+
+ numwritten = write(fd, buf, buflen - 1);
+ saved_errno = errno;
+ close(fd);
+
+ if (numwritten < 0)
+ return -saved_errno;
+
+ if (numwritten != (ssize_t)(buflen - 1))
+ return -EIO;
+
+ return 0;
+}
+
+int read_num(const char *path, unsigned long *num)
+{
+ unsigned long val;
+ int ret;
+ char buf[21];
+ char *end;
+
+ if (!num)
+ return -EINVAL;
+
+ ret = read_file(path, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ /* Reject signs and leading whitespace that are accepted by strtoul() */
+ if (buf[0] < '0' || buf[0] > '9')
+ return -EINVAL;
+
+ errno = 0;
+ val = strtoul(buf, &end, 10);
+ if (errno)
+ return -errno;
+
+ /* Only allow a newline after the number */
+ if (*end == '\n')
+ end++;
+
+ if (*end != '\0')
+ return -EINVAL;
+
+ *num = val;
+ return 0;
+}
+
+int write_num(const char *path, unsigned long num)
+{
+ char buf[21];
+
+ sprintf(buf, "%lu", num);
+ return write_file(path, buf, strlen(buf) + 1);
+}
+
+int write_num_ignore_einval(const char *path, unsigned long num)
+{
+ int ret;
+
+ ret = write_num(path, num);
+ return ret == -EINVAL ? 0 : ret;
+}
diff --git a/tools/lib/mm/file_utils.h b/tools/lib/mm/file_utils.h
new file mode 100644
index 000000000000..50daa82c2b2b
--- /dev/null
+++ b/tools/lib/mm/file_utils.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __MM_FILE_UTILS_H__
+#define __MM_FILE_UTILS_H__
+
+#include <stddef.h>
+
+int read_file(const char *path, char *buf, size_t buflen);
+int write_file(const char *path, const char *buf, size_t buflen);
+int read_num(const char *path, unsigned long *num);
+int write_num(const char *path, unsigned long num);
+int write_num_ignore_einval(const char *path, unsigned long num);
+
+#endif
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index 2d5366196e30..61d974d2cbae 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -37,7 +37,8 @@ endif
# LDLIBS.
MAKEFLAGS += --no-builtin-rules
-CFLAGS = -Wall -O2 -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
+CFLAGS = -Wall -O2 -I $(top_srcdir) -I $(top_srcdir)/tools/lib
+CFLAGS += $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
CFLAGS += -Wunreachable-code
LDLIBS = -lrt -lpthread -lm
@@ -187,8 +188,8 @@ TEST_FILES += write_hugetlb_memory.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c
-$(TEST_GEN_FILES): vm_util.c hugepage_settings.c
+$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
@@ -217,7 +218,7 @@ $(BINARIES_32): CFLAGS += -m32 -mxsave
$(BINARIES_32): LDLIBS += -lrt -ldl -lm
$(BINARIES_32): $(OUTPUT)/%_32: %.c
$(call msg,CC,,$@)
- $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@
+ $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $^ $(LDLIBS) -o $@
$(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-32,$(t))))
endif
@@ -226,7 +227,7 @@ $(BINARIES_64): CFLAGS += -m64 -mxsave
$(BINARIES_64): LDLIBS += -lrt -ldl
$(BINARIES_64): $(OUTPUT)/%_64: %.c
$(call msg,CC,,$@)
- $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@
+ $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $^ $(LDLIBS) -o $@
$(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-64,$(t))))
endif
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index c1b8d60e519d..4751db798c3a 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -889,103 +889,6 @@ int unpoison_memory(unsigned long pfn)
return ret > 0 ? 0 : -errno;
}
-int read_file(const char *path, char *buf, size_t buflen)
-{
- int fd, err;
- ssize_t numread;
-
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return -errno;
-
- numread = read(fd, buf, buflen - 1);
- if (numread < 1) {
- err = numread ? errno : ENODATA;
- close(fd);
- return -err;
- }
-
- buf[numread] = '\0';
- close(fd);
-
- return 0;
-}
-
-int write_file(const char *path, const char *buf, size_t buflen)
-{
- int fd, saved_errno;
- ssize_t numwritten;
-
- if (buflen < 2)
- return -EINVAL;
-
- fd = open(path, O_WRONLY);
- if (fd == -1)
- return -errno;
-
- numwritten = write(fd, buf, buflen - 1);
- saved_errno = errno;
- close(fd);
-
- if (numwritten < 0)
- return -saved_errno;
-
- if (numwritten != (ssize_t)(buflen - 1))
- return -EIO;
-
- return 0;
-}
-
-int read_num(const char *path, unsigned long *num)
-{
- unsigned long val;
- int ret;
- char buf[21];
- char *end;
-
- if (!num)
- return -EINVAL;
-
- ret = read_file(path, buf, sizeof(buf));
- if (ret)
- return ret;
-
- /* Reject signs and leading whitespace that are accepted by strtoul() */
- if (buf[0] < '0' || buf[0] > '9')
- return -EINVAL;
-
- errno = 0;
- val = strtoul(buf, &end, 10);
- if (errno)
- return -errno;
-
- /* Only allow a newline after the number */
- if (*end == '\n')
- end++;
-
- if (*end != '\0')
- return -EINVAL;
-
- *num = val;
- return 0;
-}
-
-int write_num(const char *path, unsigned long num)
-{
- char buf[21];
-
- sprintf(buf, "%lu", num);
- return write_file(path, buf, strlen(buf) + 1);
-}
-
-int write_num_ignore_einval(const char *path, unsigned long num)
-{
- int ret;
-
- ret = write_num(path, num);
- return ret == -EINVAL ? 0 : ret;
-}
-
static unsigned long shmall, shmmax;
void __shm_limits_restore(void)
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 62f6f5b42649..fe0475f2bdf2 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -8,6 +8,7 @@
#include <unistd.h> /* _SC_PAGESIZE */
#include "kselftest.h"
#include <linux/fs.h>
+#include <mm/file_utils.h>
#define BIT_ULL(nr) (1ULL << (nr))
#define PM_SOFT_DIRTY BIT_ULL(55)
@@ -166,12 +167,6 @@ int unpoison_memory(unsigned long pfn);
#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
-int write_file(const char *path, const char *buf, size_t buflen);
-int read_file(const char *path, char *buf, size_t buflen);
-int read_num(const char *path, unsigned long *num);
-int write_num(const char *path, unsigned long num);
-int write_num_ignore_einval(const char *path, unsigned long num);
-
void shm_limits_prepare(unsigned long length);
void __shm_limits_restore(void);
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v9 3/6] tools/lib/mm: move hugepage_settings out of selftests
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 1/6] selftests/mm: make file helpers return errors Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 2/6] tools/lib/mm: add shared file helpers Sarthak Sharma
@ 2026-09-04 12:36 ` Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 4/6] tools/mm: move gup_test from selftests/mm to tools/mm Sarthak Sharma
` (4 subsequent siblings)
7 siblings, 0 replies; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-04 12:36 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel, Sarthak Sharma
Move hugepage_settings.[ch] from tools/testing/selftests/mm/ to
tools/lib/mm/ so the THP and HugeTLB helpers can be shared more easily
between selftests and other tools.
Keep the helpers exposed to mm selftests through vm_util.h where possible,
and use direct <mm/hugepage_settings.h> includes for files that do not
include vm_util.h. Adjust the selftests/mm build to compile the moved
implementation from its new location.
Remove the remaining kselftest dependency by including file_utils.h
directly and using EXIT_FAILURE in the signal handler.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
.../{testing/selftests => lib}/mm/hugepage_settings.c | 11 +++++++++--
.../{testing/selftests => lib}/mm/hugepage_settings.h | 0
tools/testing/selftests/mm/Makefile | 6 ++++--
tools/testing/selftests/mm/compaction_test.c | 2 +-
tools/testing/selftests/mm/cow.c | 1 -
tools/testing/selftests/mm/folio_split_race_test.c | 1 -
tools/testing/selftests/mm/guard-regions.c | 1 -
tools/testing/selftests/mm/gup_longterm.c | 1 -
tools/testing/selftests/mm/gup_test.c | 1 -
tools/testing/selftests/mm/hmm-tests.c | 6 +++---
tools/testing/selftests/mm/hugetlb-madvise.c | 1 -
tools/testing/selftests/mm/hugetlb-mmap.c | 1 -
tools/testing/selftests/mm/hugetlb-mremap.c | 1 -
tools/testing/selftests/mm/hugetlb-shm.c | 1 -
tools/testing/selftests/mm/hugetlb-soft-offline.c | 2 +-
tools/testing/selftests/mm/hugetlb_dio.c | 1 -
tools/testing/selftests/mm/hugetlb_fault_after_madv.c | 1 -
tools/testing/selftests/mm/hugetlb_madv_vs_map.c | 1 -
tools/testing/selftests/mm/khugepaged.c | 1 -
tools/testing/selftests/mm/ksm_tests.c | 1 -
tools/testing/selftests/mm/migration.c | 2 +-
tools/testing/selftests/mm/pagemap_ioctl.c | 1 -
tools/testing/selftests/mm/prctl_thp_disable.c | 1 -
tools/testing/selftests/mm/protection_keys.c | 2 +-
tools/testing/selftests/mm/soft-dirty.c | 1 -
tools/testing/selftests/mm/split_huge_page_test.c | 1 -
tools/testing/selftests/mm/thuge-gen.c | 1 -
tools/testing/selftests/mm/transhuge-stress.c | 1 -
tools/testing/selftests/mm/uffd-common.h | 1 -
tools/testing/selftests/mm/uffd-wp-mremap.c | 2 +-
tools/testing/selftests/mm/va_high_addr_switch.c | 1 -
tools/testing/selftests/mm/vm_util.h | 1 +
32 files changed, 22 insertions(+), 34 deletions(-)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.c (99%)
rename tools/{testing/selftests => lib}/mm/hugepage_settings.h (100%)
diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/lib/mm/hugepage_settings.c
similarity index 99%
rename from tools/testing/selftests/mm/hugepage_settings.c
rename to tools/lib/mm/hugepage_settings.c
index 5bcda01ac4f6..abd9f7182f88 100644
--- a/tools/testing/selftests/mm/hugepage_settings.c
+++ b/tools/lib/mm/hugepage_settings.c
@@ -10,11 +10,16 @@
#include <unistd.h>
#include <errno.h>
-#include "vm_util.h"
+#include "file_utils.h"
#include "hugepage_settings.h"
#define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
#define MAX_SETTINGS_DEPTH 4
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
static int settings_index;
static struct thp_settings saved_settings;
@@ -638,8 +643,10 @@ static void hugepage_restore_settings_atexit(void)
static void hugepage_restore_settings_sighandler(int sig)
{
+ (void)sig;
+
/* exit() will invoke the hugepage_restore_settings_atexit handler. */
- exit(KSFT_FAIL);
+ exit(EXIT_FAILURE);
}
void hugepage_save_settings(bool thp, bool hugetlb)
diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/lib/mm/hugepage_settings.h
similarity index 100%
rename from tools/testing/selftests/mm/hugepage_settings.h
rename to tools/lib/mm/hugepage_settings.h
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index 61d974d2cbae..7cea3228020d 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -188,8 +188,10 @@ TEST_FILES += write_hugetlb_memory.sh
include ../lib.mk
-$(TEST_GEN_PROGS): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
-$(TEST_GEN_FILES): vm_util.c hugepage_settings.c $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_PROGS): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.c \
+ $(top_srcdir)/tools/lib/mm/file_utils.c
+$(TEST_GEN_FILES): vm_util.c $(top_srcdir)/tools/lib/mm/hugepage_settings.c \
+ $(top_srcdir)/tools/lib/mm/file_utils.c
$(OUTPUT)/uffd-stress: uffd-common.c
$(OUTPUT)/uffd-unit-tests: uffd-common.c
diff --git a/tools/testing/selftests/mm/compaction_test.c b/tools/testing/selftests/mm/compaction_test.c
index 30d4ace7155a..b3f5377119cb 100644
--- a/tools/testing/selftests/mm/compaction_test.c
+++ b/tools/testing/selftests/mm/compaction_test.c
@@ -15,9 +15,9 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
-#include "hugepage_settings.h"
#define MAP_SIZE_MB 100
#define MAP_SIZE (MAP_SIZE_MB * 1024 * 1024)
diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index 8aa5249d9bef..3264a828575b 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -29,7 +29,6 @@
#include "../../../../mm/gup_test.h"
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
static size_t pagesize;
static int pagemap_fd;
diff --git a/tools/testing/selftests/mm/folio_split_race_test.c b/tools/testing/selftests/mm/folio_split_race_test.c
index 1960635a953e..e4660bf89b62 100644
--- a/tools/testing/selftests/mm/folio_split_race_test.c
+++ b/tools/testing/selftests/mm/folio_split_race_test.c
@@ -25,7 +25,6 @@
#include <unistd.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
uint64_t page_size;
uint64_t pmd_pagesize;
diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
index 5c8ec3ca75d7..af5cd9b30339 100644
--- a/tools/testing/selftests/mm/guard-regions.c
+++ b/tools/testing/selftests/mm/guard-regions.c
@@ -21,7 +21,6 @@
#include <sys/uio.h>
#include <unistd.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#include "../pidfd/pidfd.h"
diff --git a/tools/testing/selftests/mm/gup_longterm.c b/tools/testing/selftests/mm/gup_longterm.c
index 510de93be681..c9d8b4491263 100644
--- a/tools/testing/selftests/mm/gup_longterm.c
+++ b/tools/testing/selftests/mm/gup_longterm.c
@@ -29,7 +29,6 @@
#include "../../../../mm/gup_test.h"
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
static size_t pagesize;
static int nr_hugetlbsizes;
diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/testing/selftests/mm/gup_test.c
index 3f841a96f870..5f44761dbec0 100644
--- a/tools/testing/selftests/mm/gup_test.c
+++ b/tools/testing/selftests/mm/gup_test.c
@@ -14,7 +14,6 @@
#include <mm/gup_test.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define MB (1UL << 20)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index e2642eca0d02..fa1a651963fd 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -10,9 +10,6 @@
* bugs.
*/
-#include "kselftest_harness.h"
-#include "hugepage_settings.h"
-
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@@ -33,6 +30,9 @@
#include <sys/eventfd.h>
#include <linux/userfaultfd.h>
#include <poll.h>
+#include <mm/hugepage_settings.h>
+
+#include "kselftest_harness.h"
/*
* This is a private UAPI to the kernel test module so it isn't exported
diff --git a/tools/testing/selftests/mm/hugetlb-madvise.c b/tools/testing/selftests/mm/hugetlb-madvise.c
index 555b4b3d1430..57cf790ca478 100644
--- a/tools/testing/selftests/mm/hugetlb-madvise.c
+++ b/tools/testing/selftests/mm/hugetlb-madvise.c
@@ -14,7 +14,6 @@
#include <fcntl.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define MIN_FREE_PAGES 20
#define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */
diff --git a/tools/testing/selftests/mm/hugetlb-mmap.c b/tools/testing/selftests/mm/hugetlb-mmap.c
index 0f2aad1b7dbd..a458becf5832 100644
--- a/tools/testing/selftests/mm/hugetlb-mmap.c
+++ b/tools/testing/selftests/mm/hugetlb-mmap.c
@@ -18,7 +18,6 @@
#include <linux/memfd.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define LENGTH (256UL*1024*1024)
#define PROTECTION (PROT_READ | PROT_WRITE)
diff --git a/tools/testing/selftests/mm/hugetlb-mremap.c b/tools/testing/selftests/mm/hugetlb-mremap.c
index ed3d92e862d8..9b724af66e93 100644
--- a/tools/testing/selftests/mm/hugetlb-mremap.c
+++ b/tools/testing/selftests/mm/hugetlb-mremap.c
@@ -26,7 +26,6 @@
#include <stdbool.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define DEFAULT_LENGTH_MB 10UL
#define MB_TO_BYTES(x) (x * 1024 * 1024)
diff --git a/tools/testing/selftests/mm/hugetlb-shm.c b/tools/testing/selftests/mm/hugetlb-shm.c
index 3ff7f062b7eb..f4514da49e1d 100644
--- a/tools/testing/selftests/mm/hugetlb-shm.c
+++ b/tools/testing/selftests/mm/hugetlb-shm.c
@@ -29,7 +29,6 @@
#include <sys/mman.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define LENGTH (256UL*1024*1024)
diff --git a/tools/testing/selftests/mm/hugetlb-soft-offline.c b/tools/testing/selftests/mm/hugetlb-soft-offline.c
index ffc85b958c69..d9565219378a 100644
--- a/tools/testing/selftests/mm/hugetlb-soft-offline.c
+++ b/tools/testing/selftests/mm/hugetlb-soft-offline.c
@@ -22,10 +22,10 @@
#include <sys/mman.h>
#include <sys/statfs.h>
#include <sys/types.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#ifndef MADV_SOFT_OFFLINE
#define MADV_SOFT_OFFLINE 101
diff --git a/tools/testing/selftests/mm/hugetlb_dio.c b/tools/testing/selftests/mm/hugetlb_dio.c
index fb4600570e13..9495974eccbe 100644
--- a/tools/testing/selftests/mm/hugetlb_dio.c
+++ b/tools/testing/selftests/mm/hugetlb_dio.c
@@ -20,7 +20,6 @@
#include <sys/syscall.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#ifndef STATX_DIOALIGN
#define STATX_DIOALIGN 0x00002000U
diff --git a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
index 2dc158054f66..56c5a8533e9d 100644
--- a/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
+++ b/tools/testing/selftests/mm/hugetlb_fault_after_madv.c
@@ -10,7 +10,6 @@
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define INLOOP_ITER 100
diff --git a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
index f94549efcc6f..2532a42b98df 100644
--- a/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
+++ b/tools/testing/selftests/mm/hugetlb_madv_vs_map.c
@@ -25,7 +25,6 @@
#include <unistd.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define INLOOP_ITER 100
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 6daa22f6da2f..525108cace54 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -22,7 +22,6 @@
#include "linux/magic.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define BASE_ADDR ((void *)(1UL << 30))
static unsigned long hpage_pmd_size;
diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c
index 5fd7792a0d47..6711f4c61371 100644
--- a/tools/testing/selftests/mm/ksm_tests.c
+++ b/tools/testing/selftests/mm/ksm_tests.c
@@ -15,7 +15,6 @@
#include "kselftest.h"
#include <include/vdso/time64.h>
#include "vm_util.h"
-#include "hugepage_settings.h"
#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
#define KSM_FP(s) (KSM_SYSFS_PATH s)
diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
index f19d53c69576..a35e2b57e05b 100644
--- a/tools/testing/selftests/mm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
@@ -5,7 +5,6 @@
*/
#include "kselftest_harness.h"
-#include "hugepage_settings.h"
#include <string.h>
#include <pthread.h>
@@ -16,6 +15,7 @@
#include <sys/types.h>
#include <signal.h>
#include <time.h>
+
#include "vm_util.h"
#define TWOMEG (2<<20)
diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
index eadc7159ca5b..0c5de6ec06c8 100644
--- a/tools/testing/selftests/mm/pagemap_ioctl.c
+++ b/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -24,7 +24,6 @@
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#define PAGEMAP_BITS_ALL (PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | \
PAGE_IS_FILE | PAGE_IS_PRESENT | \
diff --git a/tools/testing/selftests/mm/prctl_thp_disable.c b/tools/testing/selftests/mm/prctl_thp_disable.c
index 82c6e96ea6eb..f9ec1408a6e3 100644
--- a/tools/testing/selftests/mm/prctl_thp_disable.c
+++ b/tools/testing/selftests/mm/prctl_thp_disable.c
@@ -14,7 +14,6 @@
#include <sys/wait.h>
#include "kselftest_harness.h"
-#include "hugepage_settings.h"
#include "vm_util.h"
#ifndef PR_THP_DISABLE_EXCEPT_ADVISED
diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c
index ae6e1530b354..b7882ab97683 100644
--- a/tools/testing/selftests/mm/protection_keys.c
+++ b/tools/testing/selftests/mm/protection_keys.c
@@ -45,8 +45,8 @@
#include <unistd.h>
#include <sys/ptrace.h>
#include <setjmp.h>
+#include <mm/hugepage_settings.h>
-#include "hugepage_settings.h"
#include "pkey-helpers.h"
u64 shadow_pkey_reg;
diff --git a/tools/testing/selftests/mm/soft-dirty.c b/tools/testing/selftests/mm/soft-dirty.c
index 5f278913c4d7..7f649b673355 100644
--- a/tools/testing/selftests/mm/soft-dirty.c
+++ b/tools/testing/selftests/mm/soft-dirty.c
@@ -9,7 +9,6 @@
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define PAGEMAP_FILE_PATH "/proc/self/pagemap"
#define TEST_ITERATIONS 10000
diff --git a/tools/testing/selftests/mm/split_huge_page_test.c b/tools/testing/selftests/mm/split_huge_page_test.c
index a30927514b4f..68f508c9a355 100644
--- a/tools/testing/selftests/mm/split_huge_page_test.c
+++ b/tools/testing/selftests/mm/split_huge_page_test.c
@@ -21,7 +21,6 @@
#include <time.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
uint64_t pagesize;
unsigned int pageshift;
diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c
index 50d0805b65db..a04f588df780 100644
--- a/tools/testing/selftests/mm/thuge-gen.c
+++ b/tools/testing/selftests/mm/thuge-gen.c
@@ -14,7 +14,6 @@
#include <string.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
#if !defined(MAP_HUGETLB)
#define MAP_HUGETLB 0x40000
diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c
index 8eb0c5630e7e..96f72898ebe0 100644
--- a/tools/testing/selftests/mm/transhuge-stress.c
+++ b/tools/testing/selftests/mm/transhuge-stress.c
@@ -17,7 +17,6 @@
#include <sys/mman.h>
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
int backing_fd = -1;
int mmap_flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE;
diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h
index 92a21b97f745..0723843a7626 100644
--- a/tools/testing/selftests/mm/uffd-common.h
+++ b/tools/testing/selftests/mm/uffd-common.h
@@ -37,7 +37,6 @@
#include "kselftest.h"
#include "vm_util.h"
-#include "hugepage_settings.h"
#define UFFD_FLAGS (O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY)
diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c
index 572c2516e874..c48eaab8e75c 100644
--- a/tools/testing/selftests/mm/uffd-wp-mremap.c
+++ b/tools/testing/selftests/mm/uffd-wp-mremap.c
@@ -7,8 +7,8 @@
#include <assert.h>
#include <linux/mman.h>
#include <sys/mman.h>
+#include <mm/hugepage_settings.h>
#include "kselftest.h"
-#include "hugepage_settings.h"
#include "uffd-common.h"
static int pagemap_fd;
diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c
index e24d7ba00b44..5a354a664d1f 100644
--- a/tools/testing/selftests/mm/va_high_addr_switch.c
+++ b/tools/testing/selftests/mm/va_high_addr_switch.c
@@ -11,7 +11,6 @@
#include "vm_util.h"
#include "kselftest.h"
-#include "hugepage_settings.h"
/*
* The hint addr value is used to allocate addresses
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index fe0475f2bdf2..64a86e8a0c41 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -9,6 +9,7 @@
#include "kselftest.h"
#include <linux/fs.h>
#include <mm/file_utils.h>
+#include <mm/hugepage_settings.h>
#define BIT_ULL(nr) (1ULL << (nr))
#define PM_SOFT_DIRTY BIT_ULL(55)
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v9 4/6] tools/mm: move gup_test from selftests/mm to tools/mm
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (2 preceding siblings ...)
2026-09-04 12:36 ` [PATCH v9 3/6] tools/lib/mm: move hugepage_settings out of selftests Sarthak Sharma
@ 2026-09-04 12:36 ` Sarthak Sharma
2026-09-04 12:36 ` [PATCH v9 5/6] tools/mm: make gup_bench a benchmark only tool Sarthak Sharma
` (3 subsequent siblings)
7 siblings, 0 replies; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-04 12:36 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel, Sarthak Sharma
Move tools/testing/selftests/mm/gup_test.c to tools/mm/gup_bench.c.
This is the first step in separating its benchmarking and
functional testing components. Later patches will make this a
purely benchmarking tool and introduce a new functional selftest
under selftests/mm.
Include hugepage_settings.h directly instead of vm_util.h and
use getpagesize() instead of psize().
Adjust the Makefiles in both locations and add gup_bench to
tools/mm/.gitignore. Remove the gup_test invocations from
run_vmtests.sh and update MAINTAINERS.
Also remove the gup_test reference from
Documentation/core-api/pin_user_pages.rst. The selftest
added later in the series is standalone and does not
need per command documentation here.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
Documentation/core-api/pin_user_pages.rst | 9 -----
MAINTAINERS | 2 +-
tools/mm/.gitignore | 1 +
tools/mm/Makefile | 11 ++++--
.../mm/gup_test.c => mm/gup_bench.c} | 8 ++---
tools/testing/selftests/mm/Makefile | 1 -
tools/testing/selftests/mm/run_vmtests.sh | 36 -------------------
7 files changed, 14 insertions(+), 54 deletions(-)
rename tools/{testing/selftests/mm/gup_test.c => mm/gup_bench.c} (97%)
diff --git a/Documentation/core-api/pin_user_pages.rst b/Documentation/core-api/pin_user_pages.rst
index c16ca163b55e..e0acedbd1d48 100644
--- a/Documentation/core-api/pin_user_pages.rst
+++ b/Documentation/core-api/pin_user_pages.rst
@@ -226,15 +226,6 @@ will be pinned longterm, and whose data will be accessed.
Unit testing
============
-This file::
-
- tools/testing/selftests/mm/gup_test.c
-
-has the following new calls to exercise the new pin*() wrapper functions:
-
-* PIN_FAST_BENCHMARK (./gup_test -a)
-* PIN_BASIC_TEST (./gup_test -b)
-
You can monitor how many total dma-pinned pages have been acquired and released
since the system was booted, via two new /proc/vmstat entries: ::
diff --git a/MAINTAINERS b/MAINTAINERS
index 285c6083ca79..d7a146b093de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17188,8 +17188,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
F: mm/gup.c
F: mm/gup_test.c
F: mm/gup_test.h
+F: tools/mm/gup_bench.c
F: tools/testing/selftests/mm/gup_longterm.c
-F: tools/testing/selftests/mm/gup_test.c
MEMORY MANAGEMENT - KSM (Kernel Samepage Merging)
M: Andrew Morton <akpm@linux-foundation.org>
diff --git a/tools/mm/.gitignore b/tools/mm/.gitignore
index 1446a659e540..154d740be02e 100644
--- a/tools/mm/.gitignore
+++ b/tools/mm/.gitignore
@@ -3,3 +3,4 @@ slabinfo
page-types
page_owner_sort
thp_swap_allocator_test
+gup_bench
diff --git a/tools/mm/Makefile b/tools/mm/Makefile
index 858186a6eefd..f20a32d8cc22 100644
--- a/tools/mm/Makefile
+++ b/tools/mm/Makefile
@@ -3,13 +3,15 @@
#
include ../scripts/Makefile.include
-BUILD_TARGETS=page-types slabinfo page_owner_sort page_owner_filter thp_swap_allocator_test
+BUILD_TARGETS=page-types slabinfo page_owner_sort page_owner_filter
+BUILD_TARGETS += thp_swap_allocator_test gup_bench
INSTALL_TARGETS = $(BUILD_TARGETS) thpmaps
LIB_DIR = ../lib/api
LIBS = $(LIB_DIR)/libapi.a
+GUP_BENCH_OBJS = gup_bench.c ../lib/mm/hugepage_settings.c ../lib/mm/file_utils.c
-CFLAGS += -Wall -Wextra -I../lib/ -pthread
+CFLAGS += -Wall -Wextra -I../lib/ -I../.. -pthread
LDFLAGS += $(LIBS) -pthread
all: $(BUILD_TARGETS)
@@ -22,8 +24,11 @@ $(LIBS):
%: %.c
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+gup_bench: $(GUP_BENCH_OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(GUP_BENCH_OBJS) $(LDFLAGS)
+
clean:
- $(RM) page-types slabinfo page_owner_sort page_owner_filter thp_swap_allocator_test
+ $(RM) page-types slabinfo page_owner_sort page_owner_filter thp_swap_allocator_test gup_bench
make -C $(LIB_DIR) clean
sbindir ?= /usr/sbin
diff --git a/tools/testing/selftests/mm/gup_test.c b/tools/mm/gup_bench.c
similarity index 97%
rename from tools/testing/selftests/mm/gup_test.c
rename to tools/mm/gup_bench.c
index 5f44761dbec0..da56aa5324d3 100644
--- a/tools/testing/selftests/mm/gup_test.c
+++ b/tools/mm/gup_bench.c
@@ -12,8 +12,8 @@
#include <pthread.h>
#include <assert.h>
#include <mm/gup_test.h>
-#include "kselftest.h"
-#include "vm_util.h"
+#include <mm/hugepage_settings.h>
+#include "../testing/selftests/kselftest.h"
#define MB (1UL << 20)
@@ -140,7 +140,7 @@ int main(int argc, char **argv)
case 'n':
nr_pages = atoi(optarg);
if (nr_pages < 0)
- nr_pages = size / psize();
+ nr_pages = size / getpagesize();
break;
case 't':
thp = 1;
@@ -254,7 +254,7 @@ int main(int argc, char **argv)
madvise(p, size, MADV_NOHUGEPAGE);
/* Fault them in here, from user space. */
- for (; (unsigned long)p < gup.addr + size; p += psize())
+ for (; (unsigned long)p < gup.addr + size; p += getpagesize())
p[0] = 0;
tid = malloc(sizeof(pthread_t) * nthreads);
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index 7cea3228020d..11ca9b11fef1 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -59,7 +59,6 @@ endif
TEST_GEN_FILES = cow
TEST_GEN_FILES += compaction_test
TEST_GEN_FILES += gup_longterm
-TEST_GEN_FILES += gup_test
TEST_GEN_FILES += hmm-tests
TEST_GEN_FILES += hugetlb-madvise
TEST_GEN_FILES += hugetlb-mmap
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index d09f9f6a384e..8f1e828e4f39 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -130,30 +130,6 @@ test_selected() {
fi
}
-run_gup_matrix() {
- # -t: thp=on, -T: thp=off, -H: hugetlb=on
- local hugetlb_mb=256
-
- for huge in -t -T "-H -m $hugetlb_mb"; do
- # -u: gup-fast, -U: gup-basic, -a: pin-fast, -b: pin-basic, -L: pin-longterm
- for test_cmd in -u -U -a -b -L; do
- # -w: write=1, -W: write=0
- for write in -w -W; do
- # -S: shared
- for share in -S " "; do
- # -n: How many pages to fetch together? 512 is special
- # because it's default thp size (or 2M on x86), 123 to
- # just test partial gup when hit a huge in whatever form
- for num in "-n 1" "-n 512" "-n 123" "-n -1"; do
- CATEGORY="gup_test" run_test ./gup_test \
- $huge $test_cmd $write $share $num
- done
- done
- done
- done
- done
-}
-
# filter 64bit architectures
ARCH64STR="arm64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sparc64 x86_64"
if [ -z "$ARCH" ]; then
@@ -275,18 +251,6 @@ fi
CATEGORY="mmap" run_test ./map_fixed_noreplace
-if $RUN_ALL; then
- run_gup_matrix
-else
- # get_user_pages_fast() benchmark
- CATEGORY="gup_test" run_test ./gup_test -u -n 1
- CATEGORY="gup_test" run_test ./gup_test -u -n -1
- # pin_user_pages_fast() benchmark
- CATEGORY="gup_test" run_test ./gup_test -a -n 1
- CATEGORY="gup_test" run_test ./gup_test -a -n -1
-fi
-# Dump pages 0, 19, and 4096, using pin_user_pages:
-CATEGORY="gup_test" run_test ./gup_test -ct -F 0x1 0 19 0x1000
CATEGORY="gup_test" run_test ./gup_longterm
CATEGORY="userfaultfd" run_test ./uffd-unit-tests
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v9 5/6] tools/mm: make gup_bench a benchmark only tool
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (3 preceding siblings ...)
2026-09-04 12:36 ` [PATCH v9 4/6] tools/mm: move gup_test from selftests/mm to tools/mm Sarthak Sharma
@ 2026-09-04 12:36 ` Sarthak Sharma
2026-09-07 15:04 ` David Hildenbrand (Arm)
2026-09-04 12:36 ` [PATCH v9 6/6] selftests/mm: add a GUP selftest Sarthak Sharma
` (2 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-04 12:36 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel, Sarthak Sharma
Remove the functional modes (GUP_BASIC_TEST, PIN_BASIC_TEST and
DUMP_USER_PAGES_TEST) from gup_bench. Drop the kselftest dependency
and use normal diagnostics and exit statuses.
When no arguments are supplied, run a single GUP_FAST_BENCHMARK
with the existing default values. Let users select other
configurations through command-line options. Report ioctl failures
and handle errors without relying on assert().
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
tools/mm/gup_bench.c | 184 +++++++++++++++++++------------------------
1 file changed, 79 insertions(+), 105 deletions(-)
diff --git a/tools/mm/gup_bench.c b/tools/mm/gup_bench.c
index da56aa5324d3..e6a80ba5195a 100644
--- a/tools/mm/gup_bench.c
+++ b/tools/mm/gup_bench.c
@@ -10,10 +10,10 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <pthread.h>
-#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
#include <mm/gup_test.h>
#include <mm/hugepage_settings.h>
-#include "../testing/selftests/kselftest.h"
#define MB (1UL << 20)
@@ -37,12 +37,6 @@ static char *cmd_to_str(unsigned long cmd)
return "PIN_FAST_BENCHMARK";
case PIN_LONGTERM_BENCHMARK:
return "PIN_LONGTERM_BENCHMARK";
- case GUP_BASIC_TEST:
- return "GUP_BASIC_TEST";
- case PIN_BASIC_TEST:
- return "PIN_BASIC_TEST";
- case DUMP_USER_PAGES_TEST:
- return "DUMP_USER_PAGES_TEST";
}
return "Unknown command";
}
@@ -52,39 +46,29 @@ void *gup_thread(void *data)
struct gup_test gup = *(struct gup_test *)data;
int i, status;
- /* Only report timing information on the *_BENCHMARK commands: */
- if ((cmd == PIN_FAST_BENCHMARK) || (cmd == GUP_FAST_BENCHMARK) ||
- (cmd == PIN_LONGTERM_BENCHMARK)) {
- for (i = 0; i < repeats; i++) {
- gup.size = size;
- status = ioctl(gup_fd, cmd, &gup);
- if (status)
- break;
+ for (i = 0; i < repeats; i++) {
+ gup.size = size;
+ status = ioctl(gup_fd, cmd, &gup);
+ if (status) {
+ int err = errno;
pthread_mutex_lock(&print_mutex);
- ksft_print_msg("%s: Time: get:%lld put:%lld us",
- cmd_to_str(cmd), gup.get_delta_usec,
- gup.put_delta_usec);
- if (gup.size != size)
- ksft_print_msg(", truncated (size: %lld)", gup.size);
- ksft_print_msg("\n");
+ fprintf(stderr, "%s ioctl failed: %s\n", cmd_to_str(cmd),
+ strerror(err));
pthread_mutex_unlock(&print_mutex);
+ return data;
}
- } else {
- gup.size = size;
- status = ioctl(gup_fd, cmd, &gup);
- if (status)
- goto return_;
pthread_mutex_lock(&print_mutex);
- ksft_print_msg("%s: done\n", cmd_to_str(cmd));
+ printf("%s: Time: get:%lld put:%lld us",
+ cmd_to_str(cmd), gup.get_delta_usec,
+ gup.put_delta_usec);
if (gup.size != size)
- ksft_print_msg("Truncated (size: %lld)\n", gup.size);
+ printf(", truncated (size: %lld)", gup.size);
+ printf("\n");
pthread_mutex_unlock(&print_mutex);
}
-return_:
- ksft_test_result(!status, "ioctl status %d\n", status);
return NULL;
}
@@ -92,38 +76,21 @@ int main(int argc, char **argv)
{
struct gup_test gup = { 0 };
int filed, i, opt, nr_pages = 1, thp = -1, write = 1, nthreads = 1, ret;
- int flags = MAP_PRIVATE;
+ int flags = MAP_PRIVATE, started_threads = 0, exit_status = 1;
char *file = "/dev/zero";
- bool hugetlb = false;
+ bool hugetlb = false, thread_error = false;
+ void *thread_result;
pthread_t *tid;
char *p;
- while ((opt = getopt(argc, argv, "m:r:n:F:f:abcj:tTLUuwWSHpz")) != -1) {
+ while ((opt = getopt(argc, argv, "m:r:n:F:f:aj:tTLuwWSH")) != -1) {
switch (opt) {
case 'a':
cmd = PIN_FAST_BENCHMARK;
break;
- case 'b':
- cmd = PIN_BASIC_TEST;
- break;
case 'L':
cmd = PIN_LONGTERM_BENCHMARK;
break;
- case 'c':
- cmd = DUMP_USER_PAGES_TEST;
- /*
- * Dump page 0 (index 1). May be overridden later, by
- * user's non-option arguments.
- *
- * .which_pages is zero-based, so that zero can mean "do
- * nothing".
- */
- gup.which_pages[0] = 1;
- break;
- case 'p':
- /* works only with DUMP_USER_PAGES_TEST */
- gup.test_flags |= GUP_TEST_FLAG_DUMP_PAGES_USE_PIN;
- break;
case 'F':
/* strtol, so you can pass flags in hex form */
gup.gup_flags = strtol(optarg, 0, 0);
@@ -148,9 +115,6 @@ int main(int argc, char **argv)
case 'T':
thp = 0;
break;
- case 'U':
- cmd = GUP_BASIC_TEST;
- break;
case 'u':
cmd = GUP_FAST_BENCHMARK;
break;
@@ -172,52 +136,41 @@ int main(int argc, char **argv)
hugetlb = true;
break;
default:
- ksft_exit_fail_msg("Wrong argument\n");
+ fprintf(stderr, "Wrong argument\n");
+ exit(1);
}
}
- if (optind < argc) {
- int extra_arg_count = 0;
- /*
- * For example:
- *
- * ./gup_test -c 0 1 0x1001
- *
- * ...to dump pages 0, 1, and 4097
- */
-
- while ((optind < argc) &&
- (extra_arg_count < GUP_TEST_MAX_PAGES_TO_DUMP)) {
- /*
- * Do the 1-based indexing here, so that the user can
- * use normal 0-based indexing on the command line.
- */
- long page_index = strtol(argv[optind], 0, 0) + 1;
-
- gup.which_pages[extra_arg_count] = page_index;
- extra_arg_count++;
- optind++;
- }
+ if (optind != argc) {
+ fprintf(stderr, "Unexpected argument '%s'\n", argv[optind]);
+ exit(1);
}
- ksft_print_header();
+ if (getuid()) {
+ fprintf(stderr, "Please run this test as root\n");
+ exit(1);
+ }
if (hugetlb) {
unsigned long hp_size = default_huge_page_size();
- if (!hp_size)
- ksft_exit_skip("HugeTLB is unavailable\n");
+ if (!hp_size) {
+ fprintf(stderr, "Could not determine huge page size\n");
+ return 1;
+ }
size = (size + hp_size - 1) & ~(hp_size - 1);
- if (!hugetlb_setup_default(size / hp_size))
- ksft_exit_skip("Not enough huge pages\n");
+ if (!hugetlb_setup_default(size / hp_size)) {
+ fprintf(stderr, "Not enough huge pages\n");
+ return 1;
+ }
}
- ksft_set_plan(nthreads);
-
filed = open(file, O_RDWR|O_CREAT, 0664);
- if (filed < 0)
- ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
+ if (filed < 0) {
+ fprintf(stderr, "Unable to open %s: %s\n", file, strerror(errno));
+ return 1;
+ }
gup.nr_pages_per_call = nr_pages;
if (write)
@@ -226,26 +179,24 @@ int main(int argc, char **argv)
gup_fd = open(GUP_TEST_FILE, O_RDWR);
if (gup_fd == -1) {
switch (errno) {
- case EACCES:
- if (getuid())
- ksft_print_msg("Please run this test as root\n");
- break;
case ENOENT:
if (opendir("/sys/kernel/debug") == NULL)
- ksft_print_msg("mount debugfs at /sys/kernel/debug\n");
- ksft_print_msg("check if CONFIG_GUP_TEST is enabled in kernel config\n");
+ fprintf(stderr, "mount debugfs at /sys/kernel/debug\n");
+ fprintf(stderr, "check if CONFIG_GUP_TEST is enabled in kernel config\n");
break;
default:
- ksft_print_msg("failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
+ fprintf(stderr, "failed to open %s: %s\n", GUP_TEST_FILE,
+ strerror(errno));
break;
}
- ksft_test_result_skip("Please run this test as root\n");
- ksft_exit_pass();
+ goto err_close_filed;
}
p = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, filed, 0);
- if (p == MAP_FAILED)
- ksft_exit_fail_msg("mmap: %s\n", strerror(errno));
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "mmap: %s\n", strerror(errno));
+ goto err_close_gup_fd;
+ }
gup.addr = (unsigned long)p;
if (thp == 1)
@@ -258,17 +209,40 @@ int main(int argc, char **argv)
p[0] = 0;
tid = malloc(sizeof(pthread_t) * nthreads);
- assert(tid);
+ if (!tid) {
+ fprintf(stderr, "Failed to allocate %d threads: %s\n",
+ nthreads, strerror(errno));
+ goto err_unmap;
+ }
+
for (i = 0; i < nthreads; i++) {
ret = pthread_create(&tid[i], NULL, gup_thread, &gup);
- assert(ret == 0);
+ if (ret) {
+ fprintf(stderr, "pthread_create failed: %s\n", strerror(ret));
+ thread_error = true;
+ break;
+ }
+ started_threads++;
}
- for (i = 0; i < nthreads; i++) {
- ret = pthread_join(tid[i], NULL);
- assert(ret == 0);
+ for (i = 0; i < started_threads; i++) {
+ ret = pthread_join(tid[i], &thread_result);
+ if (ret) {
+ fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
+ thread_error = true;
+ } else if (thread_result)
+ thread_error = true;
}
free(tid);
-
- ksft_exit_pass();
+ if (thread_error)
+ goto err_unmap;
+ exit_status = 0;
+
+err_unmap:
+ munmap((void *)gup.addr, size);
+err_close_gup_fd:
+ close(gup_fd);
+err_close_filed:
+ close(filed);
+ return exit_status;
}
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 5/6] tools/mm: make gup_bench a benchmark only tool
2026-09-04 12:36 ` [PATCH v9 5/6] tools/mm: make gup_bench a benchmark only tool Sarthak Sharma
@ 2026-09-07 15:04 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 18+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-07 15:04 UTC (permalink / raw)
To: Sarthak Sharma, Andrew Morton
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
> free(tid);
> -
> - ksft_exit_pass();
> + if (thread_error)
> + goto err_unmap;
Nit:
if (!thread_error)
exit_status = 0;
--
Cheers,
David
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (4 preceding siblings ...)
2026-09-04 12:36 ` [PATCH v9 5/6] tools/mm: make gup_bench a benchmark only tool Sarthak Sharma
@ 2026-09-04 12:36 ` Sarthak Sharma
2026-09-07 15:15 ` David Hildenbrand (Arm)
2026-09-05 5:30 ` [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
2026-09-06 0:32 ` Andrew Morton
7 siblings, 1 reply; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-04 12:36 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel, Sarthak Sharma
Add a new GUP selftest which uses kselftest_harness.h. Cover
12 mapping configurations: THP enabled, THP disabled and
HugeTLB, each across private/shared mappings and with/without
FOLL_WRITE. Run 5 test cases for every variant: get_user_pages,
get_user_pages_fast, pin_user_pages, pin_user_pages_fast and
pin_user_pages_longterm.
Choose the number of default hugeTLB pages using a 256 MiB target,
with a minimum of 1 page and derive the mapping size from that
number. This avoids reserving excess memory when the hugeTLB page
size is too large and retains 128 pages for the most common case
of 2MiB hugeTLB pages.
Sweep four nr_pages_per_call values for each test: 1, 512, 123 and
all pages. This preserves the coverage previously provided by
run_gup_matrix(): 12 mapping combinations x 5 GUP/PUP operations x 4
batch sizes. In total the selftest reports 60 TAP cases and issues
240 ioctls.
Do not carry DUMP_USER_PAGES_TEST into the new selftest because its
output is written to the kernel log and the selftest does not verify
that output.
Add the new gup binary to the selftests/mm build, run_vmtests.sh and
MAINTAINERS. Update mm/Kconfig to describe the benchmark and
selftest split.
Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
---
MAINTAINERS | 1 +
mm/Kconfig | 21 +-
tools/testing/selftests/mm/Makefile | 1 +
tools/testing/selftests/mm/gup.c | 287 ++++++++++++++++++++++
tools/testing/selftests/mm/run_vmtests.sh | 1 +
5 files changed, 300 insertions(+), 11 deletions(-)
create mode 100644 tools/testing/selftests/mm/gup.c
diff --git a/MAINTAINERS b/MAINTAINERS
index d7a146b093de..1ca7de3e440e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17189,6 +17189,7 @@ F: mm/gup.c
F: mm/gup_test.c
F: mm/gup_test.h
F: tools/mm/gup_bench.c
+F: tools/testing/selftests/mm/gup.c
F: tools/testing/selftests/mm/gup_longterm.c
MEMORY MANAGEMENT - KSM (Kernel Samepage Merging)
diff --git a/mm/Kconfig b/mm/Kconfig
index c1ddf59c0d71..79163b7d795a 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1287,24 +1287,23 @@ config PERCPU_STATS
be used to help understand percpu memory usage.
config GUP_TEST
- bool "Enable infrastructure for get_user_pages()-related unit tests"
+ bool "Enable infrastructure for get_user_pages()-related unit tests and benchmarks"
depends on DEBUG_FS
help
Provides /sys/kernel/debug/gup_test, which in turn provides a way
- to make ioctl calls that can launch kernel-based unit tests for
- the get_user_pages*() and pin_user_pages*() family of API calls.
+ to make ioctl calls that can launch kernel-based unit tests and
+ benchmarks for the get_user_pages*() and pin_user_pages*() families
+ of API calls.
- These tests include benchmark testing of the _fast variants of
- get_user_pages*() and pin_user_pages*(), as well as smoke tests of
+ These include benchmark testing of the _fast variants of
+ get_user_pages*() and pin_user_pages*(), as well as tests of
the non-_fast variants.
- There is also a sub-test that allows running dump_page() on any
- of up to eight pages (selected by command line args) within the
- range of user-space addresses. These pages are either pinned via
- pin_user_pages*(), or pinned via get_user_pages*(), as specified
- by other command line arguments.
+ There is also a test that allows running dump_page() on any of up
+ to eight pages within the range of user-space addresses. These
+ pages are either acquired via pin_user_pages*() or get_user_pages*().
- See tools/testing/selftests/mm/gup_test.c
+ See tools/testing/selftests/mm/gup.c and tools/mm/gup_bench.c.
comment "GUP_TEST needs to have DEBUG_FS enabled"
depends on !GUP_TEST && !DEBUG_FS
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index 11ca9b11fef1..9c03624fd293 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -58,6 +58,7 @@ endif
TEST_GEN_FILES = cow
TEST_GEN_FILES += compaction_test
+TEST_GEN_FILES += gup
TEST_GEN_FILES += gup_longterm
TEST_GEN_FILES += hmm-tests
TEST_GEN_FILES += hugetlb-madvise
diff --git a/tools/testing/selftests/mm/gup.c b/tools/testing/selftests/mm/gup.c
new file mode 100644
index 000000000000..6a0dd415fb9a
--- /dev/null
+++ b/tools/testing/selftests/mm/gup.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0
+#define __SANE_USERSPACE_TYPES__ // Use ll64
+#include <fcntl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <mm/gup_test.h>
+#include "vm_util.h"
+#include "kselftest_harness.h"
+
+#define MB (1UL << 20)
+
+/* Just the flags we need, copied from the kernel internals. */
+#define FOLL_WRITE 0x01 /* check pte is writable */
+
+/* Page counts exercising single, THP-batch, partial, and full-mapping GUP. */
+static const int nr_pages_list[] = { 1, 512, 123, -1 };
+
+#define GUP_TEST_FILE "/sys/kernel/debug/gup_test"
+#define HUGETLB_TARGET_SIZE (256 * MB)
+
+static unsigned long nr_huge_pages, hp_size;
+static bool hugetlb_setup_succeeded;
+
+FIXTURE(gup_test)
+{
+ int gup_fd;
+ char *addr;
+ unsigned long size;
+};
+
+FIXTURE_VARIANT(gup_test)
+{
+ bool thp;
+ bool hugetlb;
+ bool write;
+ bool shared;
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_read)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_read_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_write_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = true,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, private_read_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = false,
+ .shared = false,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_read)
+{
+ .thp = false,
+ .hugetlb = false,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_read_thp)
+{
+ .thp = true,
+ .hugetlb = false,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_write_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = true,
+ .shared = true,
+};
+
+FIXTURE_VARIANT_ADD(gup_test, shared_read_hugetlb)
+{
+ .thp = false,
+ .hugetlb = true,
+ .write = false,
+ .shared = true,
+};
+
+FIXTURE_SETUP(gup_test)
+{
+ int mmap_flags = MAP_PRIVATE;
+ int zero_fd;
+ char *p;
+
+ self->size = 128 * MB;
+
+ if (variant->hugetlb) {
+ if (!hp_size)
+ SKIP(return, "HugeTLB not available\n");
+
+ if (!hugetlb_setup_succeeded ||
+ hugetlb_free_default_pages() < nr_huge_pages)
+ SKIP(return, "Not enough huge pages\n");
+
+ self->size = nr_huge_pages * hp_size;
+ mmap_flags |= (MAP_HUGETLB | MAP_ANONYMOUS);
+ }
+
+ if (variant->shared)
+ mmap_flags = (mmap_flags & ~MAP_PRIVATE) | MAP_SHARED;
+
+ /* zero_fd has to be >= 0. Already checked in main() */
+ zero_fd = open("/dev/zero", O_RDWR);
+ ASSERT_GE(zero_fd, 0);
+
+ /* gup_fd has to be >= 0. Already checked in main() */
+ self->gup_fd = open(GUP_TEST_FILE, O_RDWR);
+ ASSERT_GE(self->gup_fd, 0);
+
+ self->addr = mmap(NULL, self->size, PROT_READ | PROT_WRITE,
+ mmap_flags, zero_fd, 0);
+
+ ASSERT_NE(self->addr, MAP_FAILED) {
+ int err = errno;
+
+ close(zero_fd);
+ close(self->gup_fd);
+ TH_LOG("mmap failed: %s", strerror(err));
+ }
+ close(zero_fd);
+
+ if (variant->thp)
+ madvise(self->addr, self->size, MADV_HUGEPAGE);
+ else if (!variant->hugetlb)
+ madvise(self->addr, self->size, MADV_NOHUGEPAGE);
+
+ for (p = self->addr; (unsigned long)p < (unsigned long)self->addr
+ + self->size; p += psize())
+ p[0] = 0;
+}
+
+FIXTURE_TEARDOWN(gup_test)
+{
+ munmap(self->addr, self->size);
+ close(self->gup_fd);
+}
+
+static void run_gup_cmd(struct __test_metadata *_metadata,
+ FIXTURE_DATA(gup_test) *self,
+ const FIXTURE_VARIANT(gup_test) *variant,
+ unsigned long command)
+{
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(nr_pages_list); i++) {
+ struct gup_test gup = {
+ .addr = (unsigned long)self->addr,
+ .size = self->size,
+ .nr_pages_per_call = nr_pages_list[i] < 0 ?
+ self->size / psize() : nr_pages_list[i],
+ .gup_flags = variant->write ? FOLL_WRITE : 0,
+ };
+
+ TH_LOG("nr_pages_per_call=%u", gup.nr_pages_per_call);
+ ASSERT_EQ(ioctl(self->gup_fd, command, &gup), 0);
+ ASSERT_EQ(gup.size, self->size);
+ }
+}
+
+TEST_F(gup_test, get_user_pages)
+{
+ run_gup_cmd(_metadata, self, variant, GUP_BASIC_TEST);
+}
+
+TEST_F(gup_test, pin_user_pages)
+{
+ run_gup_cmd(_metadata, self, variant, PIN_BASIC_TEST);
+}
+
+TEST_F(gup_test, get_user_pages_fast)
+{
+ run_gup_cmd(_metadata, self, variant, GUP_FAST_BENCHMARK);
+}
+
+TEST_F(gup_test, pin_user_pages_fast)
+{
+ run_gup_cmd(_metadata, self, variant, PIN_FAST_BENCHMARK);
+}
+
+TEST_F(gup_test, pin_user_pages_longterm)
+{
+ run_gup_cmd(_metadata, self, variant, PIN_LONGTERM_BENCHMARK);
+}
+
+int main(int argc, char **argv)
+{
+ char *file = "/dev/zero";
+ int fd;
+
+ fd = open(file, O_RDWR);
+ if (fd < 0) {
+ ksft_print_header();
+ ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
+ }
+ close(fd);
+
+ fd = open(GUP_TEST_FILE, O_RDWR);
+ if (fd == -1) {
+ ksft_print_header();
+ if (errno == EACCES)
+ ksft_exit_skip("Please run this test as root\n");
+ if (errno == ENOENT) {
+ DIR *debugfs = opendir("/sys/kernel/debug");
+
+ if (!debugfs) {
+ ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n");
+ } else {
+ closedir(debugfs);
+ ksft_exit_skip("Check CONFIG_GUP_TEST in kernel config\n");
+ }
+ }
+ ksft_exit_fail_msg("Failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
+ }
+ close(fd);
+
+ hp_size = default_huge_page_size();
+ if (hp_size) {
+ nr_huge_pages = HUGETLB_TARGET_SIZE / hp_size;
+ if (!nr_huge_pages)
+ nr_huge_pages = 1;
+
+ hugetlb_setup_succeeded = hugetlb_setup_default(nr_huge_pages);
+ }
+
+ return test_harness_run(argc, argv);
+}
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 8f1e828e4f39..ae0ab5efabae 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -251,6 +251,7 @@ fi
CATEGORY="mmap" run_test ./map_fixed_noreplace
+CATEGORY="gup_test" run_test ./gup
CATEGORY="gup_test" run_test ./gup_longterm
CATEGORY="userfaultfd" run_test ./uffd-unit-tests
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-04 12:36 ` [PATCH v9 6/6] selftests/mm: add a GUP selftest Sarthak Sharma
@ 2026-09-07 15:15 ` David Hildenbrand (Arm)
2026-09-08 5:56 ` Sarthak Sharma
0 siblings, 1 reply; 18+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-07 15:15 UTC (permalink / raw)
To: Sarthak Sharma, Andrew Morton
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
On 9/4/26 14:36, Sarthak Sharma wrote:
> Add a new GUP selftest which uses kselftest_harness.h. Cover
> 12 mapping configurations: THP enabled, THP disabled and
> HugeTLB, each across private/shared mappings and with/without
> FOLL_WRITE. Run 5 test cases for every variant: get_user_pages,
> get_user_pages_fast, pin_user_pages, pin_user_pages_fast and
> pin_user_pages_longterm.
>
> Choose the number of default hugeTLB pages using a 256 MiB target,
> with a minimum of 1 page and derive the mapping size from that
> number. This avoids reserving excess memory when the hugeTLB page
> size is too large and retains 128 pages for the most common case
> of 2MiB hugeTLB pages.
>
> Sweep four nr_pages_per_call values for each test: 1, 512, 123 and
> all pages. This preserves the coverage previously provided by
> run_gup_matrix(): 12 mapping combinations x 5 GUP/PUP operations x 4
> batch sizes. In total the selftest reports 60 TAP cases and issues
> 240 ioctls.
>
> Do not carry DUMP_USER_PAGES_TEST into the new selftest because its
> output is written to the kernel log and the selftest does not verify
> that output.
>
> Add the new gup binary to the selftests/mm build, run_vmtests.sh and
> MAINTAINERS. Update mm/Kconfig to describe the benchmark and
> selftest split.
>
> Suggested-by: David Hildenbrand (Arm) <david@kernel.org>
> Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com>
> Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
> ---
> MAINTAINERS | 1 +
> mm/Kconfig | 21 +-
> tools/testing/selftests/mm/Makefile | 1 +
> tools/testing/selftests/mm/gup.c | 287 ++++++++++++++++++++++
> tools/testing/selftests/mm/run_vmtests.sh | 1 +
> 5 files changed, 300 insertions(+), 11 deletions(-)
> create mode 100644 tools/testing/selftests/mm/gup.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d7a146b093de..1ca7de3e440e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17189,6 +17189,7 @@ F: mm/gup.c
> F: mm/gup_test.c
> F: mm/gup_test.h
> F: tools/mm/gup_bench.c
> +F: tools/testing/selftests/mm/gup.c
> F: tools/testing/selftests/mm/gup_longterm.c
>
> MEMORY MANAGEMENT - KSM (Kernel Samepage Merging)
> diff --git a/mm/Kconfig b/mm/Kconfig
> index c1ddf59c0d71..79163b7d795a 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -1287,24 +1287,23 @@ config PERCPU_STATS
> be used to help understand percpu memory usage.
>
> config GUP_TEST
> - bool "Enable infrastructure for get_user_pages()-related unit tests"
> + bool "Enable infrastructure for get_user_pages()-related unit tests and benchmarks"
> depends on DEBUG_FS
> help
> Provides /sys/kernel/debug/gup_test, which in turn provides a way
> - to make ioctl calls that can launch kernel-based unit tests for
> - the get_user_pages*() and pin_user_pages*() family of API calls.
> + to make ioctl calls that can launch kernel-based unit tests and
> + benchmarks for the get_user_pages*() and pin_user_pages*() families
> + of API calls.
>
> - These tests include benchmark testing of the _fast variants of
> - get_user_pages*() and pin_user_pages*(), as well as smoke tests of
> + These include benchmark testing of the _fast variants of
> + get_user_pages*() and pin_user_pages*(), as well as tests of
> the non-_fast variants.
>
> - There is also a sub-test that allows running dump_page() on any
> - of up to eight pages (selected by command line args) within the
> - range of user-space addresses. These pages are either pinned via
> - pin_user_pages*(), or pinned via get_user_pages*(), as specified
> - by other command line arguments.
> + There is also a test that allows running dump_page() on any of up
> + to eight pages within the range of user-space addresses. These
> + pages are either acquired via pin_user_pages*() or get_user_pages*().
>
> - See tools/testing/selftests/mm/gup_test.c
> + See tools/testing/selftests/mm/gup.c and tools/mm/gup_bench.c.
BTW, I was wondering what it would take to:
1) Turn mm/gup_test.o into an OOT module (would we need more EXPORT_SYMBOL_GPL?
EXPORT_SYMBOL_FOOR_MODULE ?)
2) Move it to tools/mm/modules or sth like that.
3) Build it with the selftests etc
4) Remove GUP_TEST
5) Try insmod'ing it from the tools+selftests that need it.
[...]
> +int main(int argc, char **argv)
> +{
> + char *file = "/dev/zero";
> + int fd;
> +
> + fd = open(file, O_RDWR);
> + if (fd < 0) {
> + ksft_print_header();
> + ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
> + }
> + close(fd);
I'm confused. Why do we have to open+close /dev/zero?
> +
> + fd = open(GUP_TEST_FILE, O_RDWR);
> + if (fd == -1) {
> + ksft_print_header();
> + if (errno == EACCES)
> + ksft_exit_skip("Please run this test as root\n");
Wouldn't we want to fail here?
> + if (errno == ENOENT) {
> + DIR *debugfs = opendir("/sys/kernel/debug");
> +
> + if (!debugfs) {
> + ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n");
> + } else {
> + closedir(debugfs);
> + ksft_exit_skip("Check CONFIG_GUP_TEST in kernel config\n");
> + }
You can remove the } else { part as you skip on !debugfs.
> + }
> + ksft_exit_fail_msg("Failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
> + }
> + close(fd);
> +
> + hp_size = default_huge_page_size();
> + if (hp_size) {
> + nr_huge_pages = HUGETLB_TARGET_SIZE / hp_size;
> + if (!nr_huge_pages)
> + nr_huge_pages = 1;
> +
> + hugetlb_setup_succeeded = hugetlb_setup_default(nr_huge_pages);
> + }
BTW, why are we using HUGETLB_TARGET_SIZE instead of just using the
default_huge_page_size()?
> +
> + return test_harness_run(argc, argv);
> +}
> diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
> index 8f1e828e4f39..ae0ab5efabae 100755
> --- a/tools/testing/selftests/mm/run_vmtests.sh
> +++ b/tools/testing/selftests/mm/run_vmtests.sh
> @@ -251,6 +251,7 @@ fi
>
> CATEGORY="mmap" run_test ./map_fixed_noreplace
>
> +CATEGORY="gup_test" run_test ./gup
> CATEGORY="gup_test" run_test ./gup_longterm
Nice
--
Cheers,
David
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-07 15:15 ` David Hildenbrand (Arm)
@ 2026-09-08 5:56 ` Sarthak Sharma
2026-09-09 17:03 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-08 5:56 UTC (permalink / raw)
To: David Hildenbrand (Arm), Andrew Morton
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
Hi David!
On 9/7/26 8:45 PM, David Hildenbrand (Arm) wrote:
> On 9/4/26 14:36, Sarthak Sharma wrote:
>> Add a new GUP selftest which uses kselftest_harness.h. Cover
>> 12 mapping configurations: THP enabled, THP disabled and
>> HugeTLB, each across private/shared mappings and with/without
>> FOLL_WRITE. Run 5 test cases for every variant: get_user_pages,
>> get_user_pages_fast, pin_user_pages, pin_user_pages_fast and
>> pin_user_pages_longterm.
>>
>> Choose the number of default hugeTLB pages using a 256 MiB target,
>> with a minimum of 1 page and derive the mapping size from that
>> number. This avoids reserving excess memory when the hugeTLB page
>> size is too large and retains 128 pages for the most common case
>> of 2MiB hugeTLB pages.
>>
>> Sweep four nr_pages_per_call values for each test: 1, 512, 123 and
>> all pages. This preserves the coverage previously provided by
>> run_gup_matrix(): 12 mapping combinations x 5 GUP/PUP operations x 4
>> batch sizes. In total the selftest reports 60 TAP cases and issues
>> 240 ioctls.
[...]
>
>
> BTW, I was wondering what it would take to:
>
> 1) Turn mm/gup_test.o into an OOT module (would we need more EXPORT_SYMBOL_GPL?
> EXPORT_SYMBOL_FOOR_MODULE ?)
>
> 2) Move it to tools/mm/modules or sth like that.
>
> 3) Build it with the selftests etc
>
> 4) Remove GUP_TEST
>
> 5) Try insmod'ing it from the tools+selftests that need it.
This is an interesting change. We can keep this open for discussion
here. If required, I can work on this in the future.
>
> [...]
>
>> +int main(int argc, char **argv)
>> +{
>> + char *file = "/dev/zero";
>> + int fd;
>> +
>> + fd = open(file, O_RDWR);
>> + if (fd < 0) {
>> + ksft_print_header();
>> + ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
>> + }
>> + close(fd);
>
>
> I'm confused. Why do we have to open+close /dev/zero?
This is a pre requisite check. Every test opens and closes /dev/zero and
/sys/kernel/debug/gup_test of its own. So I wanted to check before
running the harness if these two are available, so that we don't have
setup failures for 60 test cases.
>
>> +
>> + fd = open(GUP_TEST_FILE, O_RDWR);
>> + if (fd == -1) {
>> + ksft_print_header();
>> + if (errno == EACCES)
>> + ksft_exit_skip("Please run this test as root\n");
>
> Wouldn't we want to fail here?
mm selftests normally skip if the test is not run as root. So I tried
keeping the same thing here. Do you think I should change it to fail?
>
>> + if (errno == ENOENT) {
>> + DIR *debugfs = opendir("/sys/kernel/debug");
>> +
>> + if (!debugfs) {
>> + ksft_exit_skip("Mount debugfs at /sys/kernel/debug\n");
>> + } else {
>> + closedir(debugfs);
>> + ksft_exit_skip("Check CONFIG_GUP_TEST in kernel config\n");
>> + }
>
> You can remove the } else { part as you skip on !debugfs.
Agreed
>
>> + }
>> + ksft_exit_fail_msg("Failed to open %s: %s\n", GUP_TEST_FILE, strerror(errno));
>> + }
>> + close(fd);
>> +
>> + hp_size = default_huge_page_size();
>> + if (hp_size) {
>> + nr_huge_pages = HUGETLB_TARGET_SIZE / hp_size;
>> + if (!nr_huge_pages)
>> + nr_huge_pages = 1;
>> +
>> + hugetlb_setup_succeeded = hugetlb_setup_default(nr_huge_pages);
>> + }
>
> BTW, why are we using HUGETLB_TARGET_SIZE instead of just using the
> default_huge_page_size()?
HUGETLB_TARGET_SIZE is the target mapping size and
default_huge_page_size() gives the size of a single hugetlb page.
Using default_huge_page_size() would reduce coverage for 2MB hugetlb
pages. The old test set self->size to be 256 MB for the hugetlb case.
Now it was discussed in a previous version of this patchset that we can
derive the self->size for hugetlb case by fixing the nr_hugepages and
multiplying by hugetlb size, and thought 128 would be a good number for
nr_hugepages [1].
But in case the hugetlb pages are very large, eg we can have 1 GB
hugepages as well, reserving 128 GB is not a good idea. So I tried to
keep the target size of the mapping as 256 MB, as it was before in the
old gup test. If the hugetlb pages are larger than this, we'll reserve
only one of them. Else, we'll reserve (256 MB /
default_huge_page_size()) hugetlb pages, which comes out to be 128 for
the case of 2MB hugetlb pages.
[1] https://lore.kernel.org/all/anGyXt_PDImUglIN@kernel.org/
>
>> +
>> + return test_harness_run(argc, argv);
>> +}
>> diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
>> index 8f1e828e4f39..ae0ab5efabae 100755
>> --- a/tools/testing/selftests/mm/run_vmtests.sh
>> +++ b/tools/testing/selftests/mm/run_vmtests.sh
>> @@ -251,6 +251,7 @@ fi
>>
>> CATEGORY="mmap" run_test ./map_fixed_noreplace
>>
>> +CATEGORY="gup_test" run_test ./gup
>> CATEGORY="gup_test" run_test ./gup_longterm
>
> Nice
:)
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-08 5:56 ` Sarthak Sharma
@ 2026-09-09 17:03 ` David Hildenbrand (Arm)
2026-09-09 17:34 ` Mark Brown
2026-09-11 4:32 ` Sarthak Sharma
0 siblings, 2 replies; 18+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-09 17:03 UTC (permalink / raw)
To: Sarthak Sharma, Andrew Morton
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
>> BTW, I was wondering what it would take to:
>>
>> 1) Turn mm/gup_test.o into an OOT module (would we need more EXPORT_SYMBOL_GPL?
>> EXPORT_SYMBOL_FOOR_MODULE ?)
>>
>> 2) Move it to tools/mm/modules or sth like that.
>>
>> 3) Build it with the selftests etc
>>
>> 4) Remove GUP_TEST
>>
>> 5) Try insmod'ing it from the tools+selftests that need it.
>
> This is an interesting change. We can keep this open for discussion
> here. If required, I can work on this in the future.
Yes, we should in general try moving all test modules out of the core.
>>
>>> +int main(int argc, char **argv)
>>> +{
>>> + char *file = "/dev/zero";
>>> + int fd;
>>> +
>>> + fd = open(file, O_RDWR);
>>> + if (fd < 0) {
>>> + ksft_print_header();
>>> + ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
>>> + }
>>> + close(fd);
>>
>>
>> I'm confused. Why do we have to open+close /dev/zero?
>
> This is a pre requisite check. Every test opens and closes /dev/zero and
> /sys/kernel/debug/gup_test of its own. So I wanted to check before
> running the harness if these two are available, so that we don't have
> setup failures for 60 test cases.
But why /dev/zero? We should understand why that would possibly be required.
>
>>
>>> +
>>> + fd = open(GUP_TEST_FILE, O_RDWR);
>>> + if (fd == -1) {
>>> + ksft_print_header();
>>> + if (errno == EACCES)
>>> + ksft_exit_skip("Please run this test as root\n");
>>
>> Wouldn't we want to fail here?
>
> mm selftests normally skip if the test is not run as root. So I tried
> keeping the same thing here. Do you think I should change it to fail?
If other tests do that, it's fine!
[...]
>>
>> BTW, why are we using HUGETLB_TARGET_SIZE instead of just using the
>> default_huge_page_size()?
>
> HUGETLB_TARGET_SIZE is the target mapping size and
> default_huge_page_size() gives the size of a single hugetlb page.
>
> Using default_huge_page_size() would reduce coverage for 2MB hugetlb
> pages. The old test set self->size to be 256 MB for the hugetlb case.
>
> Now it was discussed in a previous version of this patchset that we can
> derive the self->size for hugetlb case by fixing the nr_hugepages and
> multiplying by hugetlb size, and thought 128 would be a good number for
> nr_hugepages [1].
>
> But in case the hugetlb pages are very large, eg we can have 1 GB
> hugepages as well, reserving 128 GB is not a good idea. So I tried to
> keep the target size of the mapping as 256 MB, as it was before in the
> old gup test. If the hugetlb pages are larger than this, we'll reserve
> only one of them. Else, we'll reserve (256 MB /
> default_huge_page_size()) hugetlb pages, which comes out to be 128 for
> the case of 2MB hugetlb pages.
It's odd that 2M gets better test coverage than 512M or 1G.
Is there really a lot of value in testing 128 2M pages? Would, like, 2 already
be good enough?
--
Cheers,
David
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-09 17:03 ` David Hildenbrand (Arm)
@ 2026-09-09 17:34 ` Mark Brown
2026-09-10 7:53 ` Muhammad Usama Anjum
2026-09-11 4:32 ` Sarthak Sharma
1 sibling, 1 reply; 18+ messages in thread
From: Mark Brown @ 2026-09-09 17:34 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Sarthak Sharma, Andrew Morton, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jonathan Corbet, Jason Gunthorpe,
John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 674 bytes --]
On Wed, Sep 09, 2026 at 07:03:59PM +0200, David Hildenbrand (Arm) wrote:
> >> 2) Move it to tools/mm/modules or sth like that.
...
> > This is an interesting change. We can keep this open for discussion
> > here. If required, I can work on this in the future.
> Yes, we should in general try moving all test modules out of the core.
If you're working on this please make sure to take a look at how the
various automated testing systems cope with modules built under
selftests (personally I think everything I care about uses tuxmake
builds, but there's other stuff out there). I think the answer for a
bunch of them right now is badly, but I didn't actually go check.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-09 17:34 ` Mark Brown
@ 2026-09-10 7:53 ` Muhammad Usama Anjum
2026-09-10 9:18 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 18+ messages in thread
From: Muhammad Usama Anjum @ 2026-09-10 7:53 UTC (permalink / raw)
To: Mark Brown, David Hildenbrand (Arm), Sarthak Sharma
Cc: usama.anjum, Andrew Morton, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jonathan Corbet, Jason Gunthorpe,
John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Anshuman Khandual, linux-mm, linux-kselftest, linux-doc,
linux-kernel
On 09/09/2026 6:34 pm, Mark Brown wrote:
> On Wed, Sep 09, 2026 at 07:03:59PM +0200, David Hildenbrand (Arm) wrote:
>
>>>> 2) Move it to tools/mm/modules or sth like that.
>
> ...
>
>>> This is an interesting change. We can keep this open for discussion
>>> here. If required, I can work on this in the future.
>
>> Yes, we should in general try moving all test modules out of the core.
>
> If you're working on this please make sure to take a look at how the
> various automated testing systems cope with modules built under
> selftests (personally I think everything I care about uses tuxmake
> builds, but there's other stuff out there). I think the answer for a
> bunch of them right now is badly, but I didn't actually go check.
There is huge discussion in the past on converting test modules into
kunit modules [1]. Selftests is not a correct place to keep kernel modules.
People have moved a lot of such modules. But new test modules get
added over time again.
[1] https://lore.kernel.org/all/20250207-bitmap-kunit-convert-v1-0-c520675343b6@gmail.com/
--
Thanks,
Usama
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-10 7:53 ` Muhammad Usama Anjum
@ 2026-09-10 9:18 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 18+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-10 9:18 UTC (permalink / raw)
To: Muhammad Usama Anjum, Mark Brown, Sarthak Sharma
Cc: Andrew Morton, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jonathan Corbet, Jason Gunthorpe,
John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Anshuman Khandual, linux-mm, linux-kselftest, linux-doc,
linux-kernel
On 9/10/26 09:53, Muhammad Usama Anjum wrote:
> On 09/09/2026 6:34 pm, Mark Brown wrote:
>> On Wed, Sep 09, 2026 at 07:03:59PM +0200, David Hildenbrand (Arm) wrote:
>>
>>
>> ...
>>
>>
>>> Yes, we should in general try moving all test modules out of the core.
>>
>> If you're working on this please make sure to take a look at how the
>> various automated testing systems cope with modules built under
>> selftests (personally I think everything I care about uses tuxmake
>> builds, but there's other stuff out there). I think the answer for a
>> bunch of them right now is badly, but I didn't actually go check.
>
> There is huge discussion in the past on converting test modules into
> kunit modules [1]. Selftests is not a correct place to keep kernel modules.
I don't care where we move them, but mm/ and lib/ is *certainly not* the right
place for MM specific test modules.
Anyhow, I think converting stuff to kunit makes sense. I am not sure if that is
really applicable to e.g., gup_tests though.
--
Cheers,
David
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-09 17:03 ` David Hildenbrand (Arm)
2026-09-09 17:34 ` Mark Brown
@ 2026-09-11 4:32 ` Sarthak Sharma
2026-09-11 11:08 ` David Hildenbrand (Arm)
1 sibling, 1 reply; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-11 4:32 UTC (permalink / raw)
To: David Hildenbrand (Arm), Andrew Morton
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
Hi David!
On 9/9/26 10:33 PM, David Hildenbrand (Arm) wrote:
>>> BTW, I was wondering what it would take to:
>>>
>>> 1) Turn mm/gup_test.o into an OOT module (would we need more EXPORT_SYMBOL_GPL?
>>> EXPORT_SYMBOL_FOOR_MODULE ?)
>>>
>>> 2) Move it to tools/mm/modules or sth like that.
>>>
>>> 3) Build it with the selftests etc
>>>
>>> 4) Remove GUP_TEST
>>>
>>> 5) Try insmod'ing it from the tools+selftests that need it.
>>
>> This is an interesting change. We can keep this open for discussion
>> here. If required, I can work on this in the future.
>
> Yes, we should in general try moving all test modules out of the core.
>
>>>
>>>> +int main(int argc, char **argv)
>>>> +{
>>>> + char *file = "/dev/zero";
>>>> + int fd;
>>>> +
>>>> + fd = open(file, O_RDWR);
>>>> + if (fd < 0) {
>>>> + ksft_print_header();
>>>> + ksft_exit_fail_msg("Unable to open %s: %s\n", file, strerror(errno));
>>>> + }
>>>> + close(fd);
>>>
>>>
>>> I'm confused. Why do we have to open+close /dev/zero?
>>
>> This is a pre requisite check. Every test opens and closes /dev/zero and
>> /sys/kernel/debug/gup_test of its own. So I wanted to check before
>> running the harness if these two are available, so that we don't have
>> setup failures for 60 test cases.
>
> But why /dev/zero? We should understand why that would possibly be required.
This was carried over from the old test, where /dev/zero was the default
backing for mmap unless the user selected another file to back the
mapping. Now since we don't support file backed mappings, we can
directly use MAP_ANONYMOUS here. Thanks for pointing it out, I'll remove
it from this patch.
>
>>
>>>
>>>> +
>>>> + fd = open(GUP_TEST_FILE, O_RDWR);
>>>> + if (fd == -1) {
>>>> + ksft_print_header();
>>>> + if (errno == EACCES)
>>>> + ksft_exit_skip("Please run this test as root\n");
>>>
>>> Wouldn't we want to fail here?
>>
>> mm selftests normally skip if the test is not run as root. So I tried
>> keeping the same thing here. Do you think I should change it to fail?
>
> If other tests do that, it's fine!
>
> [...]
>
>>>
>>> BTW, why are we using HUGETLB_TARGET_SIZE instead of just using the
>>> default_huge_page_size()?
>>
>> HUGETLB_TARGET_SIZE is the target mapping size and
>> default_huge_page_size() gives the size of a single hugetlb page.
>>
>> Using default_huge_page_size() would reduce coverage for 2MB hugetlb
>> pages. The old test set self->size to be 256 MB for the hugetlb case.
>>
>> Now it was discussed in a previous version of this patchset that we can
>> derive the self->size for hugetlb case by fixing the nr_hugepages and
>> multiplying by hugetlb size, and thought 128 would be a good number for
>> nr_hugepages [1].
>>
>> But in case the hugetlb pages are very large, eg we can have 1 GB
>> hugepages as well, reserving 128 GB is not a good idea. So I tried to
>> keep the target size of the mapping as 256 MB, as it was before in the
>> old gup test. If the hugetlb pages are larger than this, we'll reserve
>> only one of them. Else, we'll reserve (256 MB /
>> default_huge_page_size()) hugetlb pages, which comes out to be 128 for
>> the case of 2MB hugetlb pages.
> It's odd that 2M gets better test coverage than 512M or 1G.
>
> Is there really a lot of value in testing 128 2M pages? Would, like, 2 already
> be good enough?
Yup, seems like keeping 128 pages is not adding an extra value. I'll go
with 2 hugetlb pages to test both pinning within a hugetlb page and
across the hugetlb boundary. This would make things a lot simpler.
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 6/6] selftests/mm: add a GUP selftest
2026-09-11 4:32 ` Sarthak Sharma
@ 2026-09-11 11:08 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 18+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-11 11:08 UTC (permalink / raw)
To: Sarthak Sharma, Andrew Morton
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
On 9/11/26 06:32, Sarthak Sharma wrote:
> Hi David!
>
> On 9/9/26 10:33 PM, David Hildenbrand (Arm) wrote:
>>>
>>> This is an interesting change. We can keep this open for discussion
>>> here. If required, I can work on this in the future.
>>
>> Yes, we should in general try moving all test modules out of the core.
>>
>>>
>>> This is a pre requisite check. Every test opens and closes /dev/zero and
>>> /sys/kernel/debug/gup_test of its own. So I wanted to check before
>>> running the harness if these two are available, so that we don't have
>>> setup failures for 60 test cases.
>>
>> But why /dev/zero? We should understand why that would possibly be required.
>
> This was carried over from the old test, where /dev/zero was the default
> backing for mmap unless the user selected another file to back the
> mapping. Now since we don't support file backed mappings, we can
> directly use MAP_ANONYMOUS here. Thanks for pointing it out, I'll remove
> it from this patch.
>
>>
>>>
>>>
>>> mm selftests normally skip if the test is not run as root. So I tried
>>> keeping the same thing here. Do you think I should change it to fail?
>>
>> If other tests do that, it's fine!
>>
>> [...]
>>
>>>
>>> HUGETLB_TARGET_SIZE is the target mapping size and
>>> default_huge_page_size() gives the size of a single hugetlb page.
>>>
>>> Using default_huge_page_size() would reduce coverage for 2MB hugetlb
>>> pages. The old test set self->size to be 256 MB for the hugetlb case.
>>>
>>> Now it was discussed in a previous version of this patchset that we can
>>> derive the self->size for hugetlb case by fixing the nr_hugepages and
>>> multiplying by hugetlb size, and thought 128 would be a good number for
>>> nr_hugepages [1].
>>>
>>> But in case the hugetlb pages are very large, eg we can have 1 GB
>>> hugepages as well, reserving 128 GB is not a good idea. So I tried to
>>> keep the target size of the mapping as 256 MB, as it was before in the
>>> old gup test. If the hugetlb pages are larger than this, we'll reserve
>>> only one of them. Else, we'll reserve (256 MB /
>>> default_huge_page_size()) hugetlb pages, which comes out to be 128 for
>>> the case of 2MB hugetlb pages.
>> It's odd that 2M gets better test coverage than 512M or 1G.
>>
>> Is there really a lot of value in testing 128 2M pages? Would, like, 2 already
>> be good enough?
>
> Yup, seems like keeping 128 pages is not adding an extra value. I'll go
> with 2 hugetlb pages to test both pinning within a hugetlb page and
> across the hugetlb boundary. This would make things a lot simpler.
Thanks!
--
Cheers,
David
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (5 preceding siblings ...)
2026-09-04 12:36 ` [PATCH v9 6/6] selftests/mm: add a GUP selftest Sarthak Sharma
@ 2026-09-05 5:30 ` Sarthak Sharma
2026-09-06 0:32 ` Andrew Morton
7 siblings, 0 replies; 18+ messages in thread
From: Sarthak Sharma @ 2026-09-05 5:30 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand
Cc: Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Shuah Khan,
Shuah Khan, Jonathan Corbet, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mark Brown,
Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
On 9/4/26 6:06 PM, Sarthak Sharma wrote:
> gup_test.c currently serves two separate purposes: benchmarking
> (GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK and PIN_LONGTERM_BENCHMARK) and
> functional testing (GUP_BASIC_TEST, PIN_BASIC_TEST and
> DUMP_USER_PAGES_TEST). Keeping both in one program makes the functional
> tests harder to run and report individually, while run_vmtests.sh has to
> invoke the program repeatedly with different options.
>
> Separate these roles into tools/mm/gup_bench for benchmarking and
> tools/testing/selftests/mm/gup for functional testing. Move the shared
> file and hugepage helpers to tools/lib/mm/ so both programs can use them
> without duplicating the implementation.
>
I went through Sashiko's review on this patchset [1] and wanted to
address the concerns it raises.
Both the concerns here are false positives, as it assumes that
FIXTURE_TEARDOWN() will run if FIXTURE_SETUP() fails. But in the
kselftest harness, we initialize the variable no_teardown to true before
calling FIXTURE_SETUP() and make it false only when FIXTURE_SETUP()
completes successfully. So, if ASSERT_*() fails in FIXTURE_SETUP(),
FIXTURE_TEARDOWN() won't run. Therefore neither stdin will be closed nor
self->gup_fd will be closed twice.
[1]
https://sashiko.dev/#/patchset/20260904123631.198697-1-sarthak.sharma%40arm.com
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing
2026-09-04 12:36 [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
` (6 preceding siblings ...)
2026-09-05 5:30 ` [PATCH v9 0/6] selftests/mm: separate GUP microbenchmarking from functional testing Sarthak Sharma
@ 2026-09-06 0:32 ` Andrew Morton
7 siblings, 0 replies; 18+ messages in thread
From: Andrew Morton @ 2026-09-06 0:32 UTC (permalink / raw)
To: Sarthak Sharma
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Shuah Khan, Jonathan Corbet, Jason Gunthorpe,
John Hubbard, Peter Xu, Leon Romanovsky, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Mark Brown, Anshuman Khandual, Muhammad Usama Anjum, linux-mm,
linux-kselftest, linux-doc, linux-kernel
On Fri, 4 Sep 2026 18:06:25 +0530 Sarthak Sharma <sarthak.sharma@arm.com> wrote:
> gup_test.c currently serves two separate purposes: benchmarking
> (GUP_FAST_BENCHMARK, PIN_FAST_BENCHMARK and PIN_LONGTERM_BENCHMARK) and
> functional testing (GUP_BASIC_TEST, PIN_BASIC_TEST and
> DUMP_USER_PAGES_TEST). Keeping both in one program makes the functional
> tests harder to run and report individually, while run_vmtests.sh has to
> invoke the program repeatedly with different options.
>
> Separate these roles into tools/mm/gup_bench for benchmarking and
> tools/testing/selftests/mm/gup for functional testing. Move the shared
> file and hugepage helpers to tools/lib/mm/ so both programs can use them
> without duplicating the implementation.
Thanks, I've added this series to mm.git's mm-new branch for testing
and hopefully further progression.
^ permalink raw reply [flat|nested] 18+ messages in thread