tools/lib/mm: add shared file helpers

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.

Link: https://lore.kernel.org/20260918112234.195857-3-sarthak.sharma@arm.com
Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.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>
Cc: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: John Hubbard <jhubbard@nvidia.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Nico Pache <npache@redhat.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Zi Yan <ziy@nvidia.com>
This commit is contained in:
Sarthak Sharma 2026-09-18 16:52:30 +05:30 committed by Andrew Morton
parent 23d0a7444f
commit 1d59dd1383
6 changed files with 127 additions and 108 deletions

View File

@ -17290,6 +17290,7 @@ F: mm/mapping_dirty_helpers.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

106
tools/lib/mm/file_utils.c Normal file
View File

@ -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;
}

13
tools/lib/mm/file_utils.h Normal file
View File

@ -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

View File

@ -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
@ -184,8 +185,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
@ -214,7 +215,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
@ -223,7 +224,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

View File

@ -885,103 +885,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)

View File

@ -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);