[PATCH v4 2/5] tools/lib/mm: add shared file helpers

From: Sarthak Sharma

Date: Wed May 27 2026 - 10:28:18 EST


Move read_file(), write_file(), read_num(), and write_num() 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.

Add tools/lib/mm/ to the MEMORY MANAGEMENT - MISC entry in MAINTAINERS.

Signed-off-by: Sarthak Sharma <sarthak.sharma@xxxxxxx>
---
MAINTAINERS | 1 +
tools/lib/mm/file_utils.c | 82 ++++++++++++++++++++++++++++
tools/lib/mm/file_utils.h | 12 ++++
tools/testing/selftests/mm/Makefile | 6 +-
tools/testing/selftests/mm/vm_util.c | 73 -------------------------
tools/testing/selftests/mm/vm_util.h | 6 +-
6 files changed, 99 insertions(+), 81 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 6a123fd3cfd5..5d2f72bbd128 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16936,6 +16936,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..00610013e80e
--- /dev/null
+++ b/tools/lib/mm/file_utils.c
@@ -0,0 +1,82 @@
+// 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;
+ ssize_t numread;
+
+ if (buflen < 2)
+ return -EINVAL;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -errno;
+
+ numread = read(fd, buf, buflen - 1);
+ if (numread < 1) {
+ int err = numread ? -errno : -ENODATA;
+
+ close(fd);
+ return err;
+ }
+
+ buf[numread] = '\0';
+ close(fd);
+
+ return (int)numread;
+}
+
+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)
+{
+ char buf[21];
+ int ret;
+
+ if (!num)
+ return -EINVAL;
+
+ ret = read_file(path, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ *num = strtoul(buf, NULL, 10);
+ return 0;
+}
+
+int write_num(const char *path, unsigned long num)
+{
+ char buf[21];
+
+ snprintf(buf, sizeof(buf), "%lu", num);
+ return write_file(path, buf, strlen(buf) + 1);
+}
diff --git a/tools/lib/mm/file_utils.h b/tools/lib/mm/file_utils.h
new file mode 100644
index 000000000000..38576790a342
--- /dev/null
+++ b/tools/lib/mm/file_utils.h
@@ -0,0 +1,12 @@
+/* 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);
+
+#endif
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index e6df968f0971..b5fb4b6ab31b 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -37,7 +37,7 @@ 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 $(EXTRA_CFLAGS) $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
CFLAGS += -Wunreachable-code
LDLIBS = -lrt -lpthread -lm

@@ -187,8 +187,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
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 290e5c29123e..b3ee00d71aa3 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -698,79 +698,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;
- ssize_t numread;
-
- if (buflen < 2)
- return -EINVAL;
-
- fd = open(path, O_RDONLY);
- if (fd == -1)
- return -errno;
-
- numread = read(fd, buf, buflen - 1);
- if (numread < 1) {
- int err = numread ? -errno : -ENODATA;
-
- close(fd);
- return err;
- }
-
- buf[numread] = '\0';
- close(fd);
-
- return (int)numread;
-}
-
-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 != buflen - 1)
- return -EIO;
-
- return 0;
-}
-
-int read_num(const char *path, unsigned long *num)
-{
- char buf[21];
- int ret;
-
- if (!num)
- return -EINVAL;
-
- ret = read_file(path, buf, sizeof(buf));
- if (ret < 0)
- return ret;
-
- *num = strtoul(buf, NULL, 10);
- 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);
-}
-
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 28c3d7c1faed..d45135283732 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)
@@ -164,11 +165,6 @@ int unpoison_memory(unsigned long pfn);
#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))

-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);
-
void shm_limits_prepare(unsigned long length);
void __shm_limits_restore(void);

--
2.39.5