* [PATCH v2 0/2] support kselftest on nommu platform
@ 2026-08-25 1:59 Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 1/2] selftests: run tests on nommu architecture Hajime Tazaki
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Hajime Tazaki @ 2026-08-25 1:59 UTC (permalink / raw)
To: linux-mm, liam, rbm, akpm, luto, brendan.jackman, david,
liuhangbin, corbet, kees, ljs, broonie, mhocko, rppt, shuah,
surenb, vbabka, wad, linux-doc, linux-kselftest, linux-um
Cc: geert, daniel, Hajime Tazaki
We add an ability to execute kselftest on nommu platforms.
Currently there are several issues if we wish to run kselftests on nommu
targets:
- it cannot compile/build test binaries because the current files mainly
assume to build with glibc,
- some of the tests are not able to run on nommu targets as there are no
fork(2) syscall.
The first issue can be avoided if we can build static PIE binaries (if
targets support it), but in our case (build on ubuntu/glibc and run on
alpine/musl-libc), it fails to invoke due to lack of the GNU ifunc
mechanism. Thus, we need to cross-compile with musl toolchain, which
needs to be solved the first issue.
The second issue is the lack of fork(2) syscall on those platforms.
Especially the test harness helper (kselftest_harness.h) uses the
syscall, which cannot be simply with vfork(2). `timeout` command used
in `runner.sh` never works for nommu platform as it uses fork(2).
nommu component in the mm subsystem has several known issues and having
test cases should help this situation, thus this patchset is very first
step toward enriching test environment which has not been well tested
for a while. The test cases is implemented based on the document
(Documentation/admin-guide/mm/nommu-mmap.rst).
So, for the first step, nommu targets only support low-level API of
kselftests (kselftest.h), and implement tests in a new target,
TARGETS=mm/nommu. Other targets are currently not even able to build
due to toolchain issues but will be addressed once the initial
introduction which mainly focuses on nommu test will be settled.
The patch was initially combined with other patches but is decoupled to
only focus on test framework and testcases.
- rfc:
https://lore.kernel.org/linux-mm/20260813063401.1786548-1-thehajime@gmail.com/
Hajime Tazaki (2):
selftests: run tests on nommu architecture
selftests/mm: add nommu mmap and mremap behavior tests
Documentation/dev-tools/kselftest.rst | 14 +
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/kselftest/runner.sh | 9 +-
tools/testing/selftests/kselftest_harness.h | 4 +
tools/testing/selftests/lib.mk | 8 +
tools/testing/selftests/mm/nommu/Makefile | 7 +
.../selftests/mm/nommu/nommu_mmap_test.c | 265 +++++++++++++
.../selftests/mm/nommu/nommu_mremap_test.c | 353 ++++++++++++++++++
8 files changed, 659 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/mm/nommu/Makefile
create mode 100644 tools/testing/selftests/mm/nommu/nommu_mmap_test.c
create mode 100644 tools/testing/selftests/mm/nommu/nommu_mremap_test.c
base-commit: 55b50d965aee5f66cefe4dde3907a0e272f96b91
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/2] selftests: run tests on nommu architecture
2026-08-25 1:59 [PATCH v2 0/2] support kselftest on nommu platform Hajime Tazaki
@ 2026-08-25 1:59 ` Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 2/2] selftests/mm: add nommu mmap and mremap behavior tests Hajime Tazaki
2026-08-25 8:37 ` [PATCH v2 0/2] support kselftest on nommu platform David Hildenbrand (Arm)
2 siblings, 0 replies; 4+ messages in thread
From: Hajime Tazaki @ 2026-08-25 1:59 UTC (permalink / raw)
To: linux-mm, liam, rbm, akpm, luto, brendan.jackman, david,
liuhangbin, corbet, kees, ljs, broonie, mhocko, rppt, shuah,
surenb, vbabka, wad, linux-doc, linux-kselftest, linux-um
Cc: geert, daniel, Hajime Tazaki
Architectures lacks MMU doesn't support fork(2) syscall and only
vfork(2) is available with limitations. Thus, we cannot run kselftest
on nommu architecture as is.
This commit addresses this issue with the following changes:
- on build stage, add -DCONFIG_NOMMU to CFLAGS when NOMMU=1 variable
added to the build/make argument.
- on test run stage, avoid calling timeout command when NOMMU=1 variable
added to environmental variable, since timeout command uses fork
syscall which nommu platform doesn't support.
- kselftest_harness.h: skip the tests if the file is include when
executing on NOMMU platform, as there is no fork(2) syscall.
- replace "cd -" use as it is not available a shell supported on nommu
(e.g., busybox hush), use cd "$OLDDIR" instead.
- describe the difference of nommu tests in the document.
So command line to build/execute tests for nommu should be like below:
$ make ARCH=um NOMMU=1 O=build kselftest-all TARGETS=mm/nommu
$ make ARCH=um NOMMU=1 O=build kselftest-install TARGETS=mm/nommu
$ NOMMU=1 ./build/kselftest/kselftest_install/run_kselftest.sh -p \
-c mm/nommu
Cc: Shuah Khan <shuah@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kees Cook <kees@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Will Drewry <wad@chromium.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Brendan Jackman <brendan.jackman@linux.dev>
Cc: Hangbin Liu <liuhangbin@gmail.com>
Cc: "Ricardo B. Marliere" <rbm@suse.com>
Cc: linux-kselftest@vger.kernel.org
Cc: linux-um@lists.infradead.org
Cc: linux-mm@kvack.org
Cc: linux-doc@vger.kernel.org
Closes: https://sashiko.dev/#/patchset/20260813063401.1786548-1-thehajime%40gmail.com
Signed-off-by: Hajime Tazaki <thehajime@gmail.com>
--
rfc => v2
- reformat ktap header, reported by Sashiko review
rfc: https://lore.kernel.org/linux-mm/20260813063401.1786548-1-thehajime@gmail.com/
---
Documentation/dev-tools/kselftest.rst | 14 ++++++++++++++
tools/testing/selftests/kselftest/runner.sh | 9 +++++++--
tools/testing/selftests/kselftest_harness.h | 4 ++++
tools/testing/selftests/lib.mk | 8 ++++++++
4 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst
index 64c0ec7428a2..a65f3ebc5906 100644
--- a/Documentation/dev-tools/kselftest.rst
+++ b/Documentation/dev-tools/kselftest.rst
@@ -230,6 +230,20 @@ section::
.. _tar's auto-compress: https://www.gnu.org/software/tar/manual/html_node/gzip.html#auto_002dcompress
+Build and test on nommu target
+==============================
+
+If you (cross-)build kselftests for nommu targets, or run tests on nommu targets, use
+``NOMMU=1`` as a make variable/environment setting to tell build system to do the additional
+checks. These nommu targets may differ in several ways, such as not supporting fork(2) or
+using musl or another libc. Set this variable to apply the necessary build and test adjustments.
+
+::
+
+ $ make ARCH=um NOMMU=1 O=build kselftest-all TARGETS=mm/nommu # <= build-only
+ $ make ARCH=um NOMMU=1 O=build kselftest-install TARGETS=mm/nommu
+ $ NOMMU=1 ./build/kselftest/kselftest_install/run_kselftest.sh -p -c mm/nommu
+
Contributing new tests
======================
diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh
index 311811dc55a0..7287d8290b6c 100644
--- a/tools/testing/selftests/kselftest/runner.sh
+++ b/tools/testing/selftests/kselftest/runner.sh
@@ -38,8 +38,12 @@ tap_prefix()
tap_timeout()
{
+ # nommu doesn't support timeout command (missing fork(2))
+ if [ "$NOMMU" = "1" ] ; then
+ echo "timeout isn't supported for nommu"
+ $1
# Make sure tests will time out if utility is available.
- if [ -x /usr/bin/timeout ] ; then
+ elif [ -x /usr/bin/timeout ] ; then
/usr/bin/timeout --foreground "$kselftest_timeout" \
/usr/bin/timeout "$kselftest_timeout" $1
else
@@ -130,6 +134,7 @@ run_one()
return $KSFT_FAIL
fi
fi
+ OLDDIR=$(pwd)
cd `dirname $TEST` > /dev/null
(((( tap_timeout "$cmd" 2>&1; echo $? >&3) |
tap_prefix >&4) 3>&1) |
@@ -147,7 +152,7 @@ run_one()
*)
ktap_test_fail "$TEST_HDR_MSG # exit=$rc";;
esac
- cd - >/dev/null
+ cd "$OLDDIR" >/dev/null
fi
return $rc
diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h
index 261e4df94d9d..0d05946a5e7a 100644
--- a/tools/testing/selftests/kselftest_harness.h
+++ b/tools/testing/selftests/kselftest_harness.h
@@ -1274,6 +1274,10 @@ static int test_harness_run(int argc, char **argv)
unsigned int count = 0;
unsigned int pass_count = 0;
+#ifdef CONFIG_NOMMU
+ ksft_print_header();
+ ksft_exit_skip("kselftest harness requires fork(2), unavailable on NOMMU\n");
+#endif /* CONFIG_NOMMU */
ret = test_harness_argv_check(argc, argv);
if (ret != KSFT_PASS)
return ret;
diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk
index f02cc8a2e4ae..fdb895967768 100644
--- a/tools/testing/selftests/lib.mk
+++ b/tools/testing/selftests/lib.mk
@@ -97,6 +97,14 @@ TEST_GEN_PROGS := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS))
TEST_GEN_PROGS_EXTENDED := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS_EXTENDED))
TEST_GEN_FILES := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_FILES))
+# detect if users request NOMMU build or not
+# User can set NOMMU to 1 to build/test for NOMMU platforms
+NOMMU ?= 0
+ifeq ($(NOMMU),1)
+CFLAGS += -DCONFIG_NOMMU
+export NOMMU
+endif
+
all: $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) \
$(if $(TEST_GEN_MODS_DIR),gen_mods_dir)
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] selftests/mm: add nommu mmap and mremap behavior tests
2026-08-25 1:59 [PATCH v2 0/2] support kselftest on nommu platform Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 1/2] selftests: run tests on nommu architecture Hajime Tazaki
@ 2026-08-25 1:59 ` Hajime Tazaki
2026-08-25 8:37 ` [PATCH v2 0/2] support kselftest on nommu platform David Hildenbrand (Arm)
2 siblings, 0 replies; 4+ messages in thread
From: Hajime Tazaki @ 2026-08-25 1:59 UTC (permalink / raw)
To: linux-mm, liam, rbm, akpm, luto, brendan.jackman, david,
liuhangbin, corbet, kees, ljs, broonie, mhocko, rppt, shuah,
surenb, vbabka, wad, linux-doc, linux-kselftest, linux-um
Cc: geert, daniel, Hajime Tazaki
Introduce a kselftest utility to validate memory mapping capabilities
under nommu kernels, aligned with
Documentation/admin-guide/mm/nommu-mmap.rst.
The test implements basic checks into a generic architecture-agnostic
test matrix applicable across nommu targets. It evaluates:
1. MAP_FIXED allocation rejections.
2. Standard MAP_PRIVATE and MAP_ANONYMOUS allocation resilience.
3. Regular file mappings via standard filesystem storage.
4. Block device subsystem mappings (gracefully skipping if node is
missing).
5. Shared vs Private backing discrepancies under nommu conditions.
6. mremap limits, ensuring non-expandable restrictions behave properly.
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: "Liam R. Howlett" <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-um@lists.infradead.org
Assisted-by: Gemini:Pro [AI_Reviewer] [Sashiko_Linter]
Assisted-by: cubic.dev:unspecified
Signed-off-by: Hajime Tazaki <thehajime@gmail.com>
--
rfc => v2
- prepare a different TARGETS for tests (mm/nommu)
- drop shrink fix tests
- drop mmap /dev/zero tests
rfc:
- https://lore.kernel.org/linux-mm/20260813063401.1786548-1-thehajime@gmail.com/
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/mm/nommu/Makefile | 7 +
.../selftests/mm/nommu/nommu_mmap_test.c | 265 +++++++++++++
.../selftests/mm/nommu/nommu_mremap_test.c | 353 ++++++++++++++++++
4 files changed, 626 insertions(+)
create mode 100644 tools/testing/selftests/mm/nommu/Makefile
create mode 100644 tools/testing/selftests/mm/nommu/nommu_mmap_test.c
create mode 100644 tools/testing/selftests/mm/nommu/nommu_mremap_test.c
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 2cc63e4134fb..940d2cab4f71 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -135,6 +135,7 @@ TARGETS += uevent
TARGETS += user_events
TARGETS += vDSO
TARGETS += mm
+TARGETS += mm/nommu
TARGETS += vfio
TARGETS += x86
TARGETS += x86/bugs
diff --git a/tools/testing/selftests/mm/nommu/Makefile b/tools/testing/selftests/mm/nommu/Makefile
new file mode 100644
index 000000000000..9e28be37bc8f
--- /dev/null
+++ b/tools/testing/selftests/mm/nommu/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for mm/nommu selftests
+
+TEST_GEN_PROGS += nommu_mmap_test
+TEST_GEN_PROGS += nommu_mremap_test
+
+include ../../lib.mk
diff --git a/tools/testing/selftests/mm/nommu/nommu_mmap_test.c b/tools/testing/selftests/mm/nommu/nommu_mmap_test.c
new file mode 100644
index 000000000000..425ec425daad
--- /dev/null
+++ b/tools/testing/selftests/mm/nommu/nommu_mmap_test.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "kselftest.h"
+
+#include <sys/vfs.h>
+#ifndef RAMFS_MAGIC
+#define RAMFS_MAGIC 0x858458f6
+#endif
+
+static size_t ps;
+
+struct test_case_t {
+ const char *name;
+ const char *pathname;
+ int open_flags;
+ int mmap_prot;
+ int mmap_flags;
+ int exp_err;
+ int (*resolve_exp_err)(const char *path);
+};
+
+static int get_shm_expected_error(const char *path)
+{
+ struct statfs fs;
+
+ if (statfs(path, &fs) == 0) {
+ if (fs.f_type == RAMFS_MAGIC)
+ return 0; /* ramfs succeed with contiguous memory */
+ }
+ /* hostfs, etc returns ENODEV due to lack of contiguous allocation */
+ return ENODEV;
+}
+
+static struct test_case_t test_cases[] = {
+ {
+ .name = "anonymous private allocation",
+ .pathname = NULL,
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ | PROT_WRITE,
+ .mmap_flags = MAP_ANONYMOUS | MAP_PRIVATE,
+ .exp_err = 0,
+ .resolve_exp_err = NULL,
+ },
+ {
+ .name = "non-anonymous private file mapping (rw-)",
+ .pathname = "/tmp/ksft.nommu-reg-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ | PROT_WRITE,
+ .mmap_flags = MAP_PRIVATE,
+ .exp_err = 0,
+ .resolve_exp_err = NULL,
+ },
+ {
+ .name = "non-anonymous private file mapping (r--)",
+ .pathname = "/tmp/ksft.nommu-reg-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ,
+ .mmap_flags = MAP_PRIVATE,
+ .exp_err = 0,
+ .resolve_exp_err = NULL,
+ },
+ {
+ .name = "non-anonymous shared file mapping (rw-)",
+ .pathname = "/tmp/ksft.nommu-shm-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ | PROT_WRITE,
+ .mmap_flags = MAP_SHARED,
+ .exp_err = 0,
+#ifdef CONFIG_NOMMU
+ .resolve_exp_err = get_shm_expected_error,
+#else
+ .resolve_exp_err = NULL,
+#endif
+ },
+ {
+ .name = "non-anonymous shared file mapping (r--)",
+ .pathname = "/tmp/ksft.nommu-shm-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ,
+ .mmap_flags = MAP_SHARED,
+ .exp_err = 0,
+#ifdef CONFIG_NOMMU
+ .resolve_exp_err = get_shm_expected_error,
+#else
+ .resolve_exp_err = 0,
+#endif
+ },
+ {
+ .name = "block device volatile node mapping",
+ .pathname = "/dev/loop0",
+ .open_flags = O_RDWR,
+ .mmap_prot = PROT_READ | PROT_WRITE,
+ .mmap_flags = MAP_PRIVATE,
+ .exp_err = 0,
+ .resolve_exp_err = NULL,
+ }
+};
+
+static int run_mapping_matrix_test(struct test_case_t *tcase)
+{
+ int fd;
+ void *ptr;
+ char path_buf[PATH_MAX];
+ int rc = KSFT_PASS;
+ int expected_error;
+
+ ksft_print_msg("[RUN] Testing: %s\n", tcase->name);
+
+ if (tcase->pathname == NULL) {
+ fd = -1;
+ } else if (strstr(tcase->pathname, "XXXXXX")) {
+ strncpy(path_buf, tcase->pathname, sizeof(path_buf) - 1);
+ path_buf[sizeof(path_buf) - 1] = '\0';
+ fd = mkstemp(path_buf);
+ if (fd < 0) {
+ ksft_test_result_skip("Failed to setup temp node: %s\n",
+ tcase->pathname);
+ return KSFT_SKIP;
+ }
+ if (ftruncate(fd, ps) != 0) {
+ ksft_test_result_fail("ftruncate failed for: %s\n",
+ tcase->pathname);
+ close(fd);
+ unlink(path_buf);
+ return KSFT_FAIL;
+ }
+ } else {
+ fd = open(tcase->pathname, tcase->open_flags, 0600);
+ if (fd < 0) {
+ ksft_test_result_skip("Device node not accessible: %s\n",
+ tcase->pathname);
+ return KSFT_SKIP;
+ }
+ }
+
+ expected_error = tcase->exp_err;
+ if (tcase->resolve_exp_err && fd >= 0)
+ expected_error = tcase->resolve_exp_err(path_buf);
+
+ ptr = mmap(NULL, ps, tcase->mmap_prot, tcase->mmap_flags, fd, 0);
+
+ if (expected_error != 0) {
+ if (ptr != MAP_FAILED) {
+ ksft_test_result_fail("%s: mmap unexpectedly succeeded (exp error %d)\n",
+ tcase->name, expected_error);
+ munmap(ptr, ps);
+ rc = KSFT_FAIL;
+ goto cleanup;
+ }
+ if (errno != expected_error) {
+ ksft_test_result_fail("%s: mmap failed with %d (%s), but expected %d\n",
+ tcase->name, errno, strerror(errno), expected_error);
+ rc = KSFT_FAIL;
+ goto cleanup;
+ }
+ ksft_test_result_pass("%s: Correctly rejected with expected error %s(%d)\n",
+ tcase->name, strerror(expected_error), expected_error);
+ rc = KSFT_PASS;
+ goto cleanup;
+ }
+
+ if (ptr == MAP_FAILED) {
+ ksft_test_result_fail("%s: mmap failed unexpectedly: %s\n",
+ tcase->name, strerror(errno));
+ rc = KSFT_FAIL;
+ goto cleanup;
+ }
+
+ ksft_test_result_pass("%s: mmap validation successfully passed\n", tcase->name);
+ munmap(ptr, ps);
+
+cleanup:
+ if (fd >= 0) {
+ close(fd);
+ if (tcase->pathname && strstr(tcase->pathname, "XXXXXX"))
+ unlink(path_buf);
+ }
+ return rc;
+}
+
+static int test_map_fixed(void)
+{
+ void *fixed_addr;
+ void *ptr;
+
+ ksft_print_msg("[RUN] Testing MAP_FIXED behavior\n");
+
+ fixed_addr = mmap(NULL, ps, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (fixed_addr == MAP_FAILED) {
+ ksft_test_result_skip("Unable to reserve test address: %s\n",
+ strerror(errno));
+ return KSFT_SKIP;
+ }
+
+ if (munmap(fixed_addr, ps)) {
+ ksft_test_result_fail("Unable to release test address: %s\n",
+ strerror(errno));
+ return KSFT_FAIL;
+ }
+
+ ptr = mmap(fixed_addr, ps, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+
+#ifdef CONFIG_NOMMU
+ if (ptr == MAP_FAILED && (errno == ENODEV || errno == EINVAL)) {
+ ksft_test_result_pass("MAP_FIXED correctly rejected under nommu\n");
+ return KSFT_PASS;
+ }
+ if (ptr != MAP_FAILED) {
+ ksft_test_result_fail("MAP_FIXED unexpectedly allowed under nommu\n");
+ munmap(ptr, ps);
+ return KSFT_FAIL;
+ }
+ ksft_test_result_fail("MAP_FIXED failed under NOMMU: %s\n",
+ strerror(errno));
+ return KSFT_FAIL;
+#else
+ if (ptr != MAP_FAILED) {
+ ksft_test_result_pass("MAP_FIXED successfully allocated under MMU\n");
+ munmap(ptr, ps);
+ return KSFT_PASS;
+ }
+ ksft_test_result_fail("MAP_FIXED failed allocation under MMU\n");
+ return KSFT_FAIL;
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ int result = KSFT_PASS;
+ int i, rc;
+
+ ps = sysconf(_SC_PAGESIZE);
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(test_cases) + 1);
+
+#ifdef CONFIG_NOMMU
+ ksft_print_msg("Running strict MMAP test criteria under nommu architecture\n");
+#else
+ ksft_print_msg("Running MMAP test criteria under MMU architecture\n");
+#endif
+
+ if (test_map_fixed() == KSFT_FAIL)
+ result = KSFT_FAIL;
+
+ for (i = 0; i < (int)ARRAY_SIZE(test_cases); i++) {
+ rc = run_mapping_matrix_test(&test_cases[i]);
+ if (rc == KSFT_FAIL)
+ result = KSFT_FAIL;
+ }
+
+ if (result == KSFT_PASS)
+ ksft_finished();
+
+ ksft_exit_fail();
+}
diff --git a/tools/testing/selftests/mm/nommu/nommu_mremap_test.c b/tools/testing/selftests/mm/nommu/nommu_mremap_test.c
new file mode 100644
index 000000000000..8d577d960b11
--- /dev/null
+++ b/tools/testing/selftests/mm/nommu/nommu_mremap_test.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include "kselftest.h"
+
+#include <sys/vfs.h>
+#ifndef RAMFS_MAGIC
+#define RAMFS_MAGIC 0x858458f6
+#endif
+
+static size_t ps;
+
+static long get_fs_type(const char *path)
+{
+ struct statfs fs;
+
+ if (statfs(path, &fs) == 0)
+ return fs.f_type;
+
+ return 0;
+}
+
+static void munmap_shrink_test(void)
+{
+ void *addr;
+
+ /* munmap shrink test */
+ for (int i = 0; i < 4; i++) {
+ addr = mmap(NULL, ps * 4, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (addr == MAP_FAILED) {
+ ksft_test_result_fail("mmap failed: %s(%d)\n", strerror(errno), errno);
+ return;
+ }
+ if (munmap(addr + ps * i, ps) != 0) {
+ ksft_test_result_fail("memory %p isn't unmapped at %p\n",
+ addr, addr + ps * i);
+ for (int j = 0; j < 4; j++)
+ munmap((char *)addr + j * ps, ps);
+ return;
+ }
+
+ if (i == 0) {
+ if (munmap(addr + ps, ps * 3))
+ goto error;
+ } else if (i == 1) {
+ if (munmap(addr, ps) || munmap(addr + (ps * 2), ps * 2))
+ goto error;
+ } else if (i == 2) {
+ if (munmap(addr, ps * 2) || munmap(addr + (ps * 3), ps))
+ goto error;
+ } else if (i == 3) {
+ if (munmap(addr, ps * 3))
+ goto error;
+ }
+ }
+
+ ksft_test_result_pass("%s success\n", __func__);
+ return;
+error:
+ for (int j = 0; j < 4; j++)
+ munmap((char *)addr + j * ps, ps);
+ ksft_test_result_fail("%s clean up failures\n", __func__);
+}
+
+static size_t page_align(size_t len)
+{
+ return (len + ps - 1) / ps * ps;
+}
+
+static void mremap_shrink_test(void)
+{
+ void *addr, *addr2;
+ size_t current_len;
+ size_t old_len, new_len;
+ struct param {
+ size_t old;
+ size_t new;
+ } params[] = {
+ /* should not happen any shrink */
+ { .old = ps * 4 - 1, .new = ps * 4 - 2 },
+ /* should not happen any shrink */
+ { .old = ps * 4 - 1, .new = ps * 4 },
+ { .old = ps * 4, .new = ps * 2 },
+ /* should not happen any shrink */
+ { .old = ps * 2, .new = ps * 2 - 2 },
+ { .old = ps * 2 - 2, .new = ps * 1 },
+ };
+
+ /* mremap shrink test */
+ current_len = page_align(ps * 4 - 1);
+ addr = mmap(NULL, ps * 4 - 1, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (addr == MAP_FAILED) {
+ ksft_test_result_fail("mmap failed: %s(%d)\n", strerror(errno), errno);
+ return;
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(params); i++) {
+ old_len = page_align(params[i].old);
+ new_len = page_align(params[i].new);
+ addr2 = mremap(addr, old_len, new_len, MREMAP_MAYMOVE);
+ if (addr2 == MAP_FAILED) {
+ ksft_test_result_fail("memory %p isn't remapped at %p\n", addr, addr2);
+ munmap(addr, old_len);
+ return;
+ }
+
+ addr = addr2;
+ current_len = new_len;
+ }
+
+ if (munmap(addr, current_len)) {
+ ksft_test_result_fail("%s cleanup failed: %s\n",
+ __func__, strerror(errno));
+ return;
+ }
+ ksft_test_result_pass("%s success\n", __func__);
+}
+
+static int get_shared_writable_file_expected_error(const char *path)
+{
+ if (get_fs_type(path) == RAMFS_MAGIC)
+ return EPERM; /* ramfs failed */
+
+ return 0;
+}
+
+struct mremap_case_t {
+ const char *name;
+ const char *pathname;
+ int open_flags;
+ int mmap_prot;
+ int mmap_flags;
+ int exp_err;
+ int (*resolve_exp_err)(const char *path);
+ unsigned int old_pages;
+ unsigned int new_pages;
+};
+
+static struct mremap_case_t mremap_cases[] = {
+ {
+ .name = "anonymous shrink (r--)",
+ .pathname = NULL,
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ,
+ .mmap_flags = MAP_ANONYMOUS | MAP_PRIVATE,
+ .exp_err = 0,
+ .resolve_exp_err = 0,
+ },
+ {
+ .name = "shared file shrink (r--)",
+ .pathname = "/tmp/ksft.nommu-remap-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ,
+ .mmap_flags = MAP_SHARED,
+ .exp_err = 0,
+#ifdef CONFIG_NOMMU
+ .resolve_exp_err = get_shared_writable_file_expected_error,
+#else
+ .resolve_exp_err = 0,
+#endif
+ },
+ {
+ .name = "private file unchanged length (r-)",
+ .pathname = "/tmp/ksft.nommu-remap-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ,
+ .mmap_flags = MAP_PRIVATE,
+#ifdef CONFIG_NOMMU
+ .exp_err = EPERM,
+#else
+ .exp_err = 0,
+#endif
+ .resolve_exp_err = 0,
+ .old_pages = 4,
+ .new_pages = 4,
+ },
+ {
+ .name = "private file unchanged length (rw-)",
+ .pathname = "/tmp/ksft.nommu-remap-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ | PROT_WRITE,
+ .mmap_flags = MAP_PRIVATE,
+ .exp_err = 0,
+ .resolve_exp_err = 0,
+ .old_pages = 4,
+ .new_pages = 4,
+ },
+ {
+ .name = "private file growth (r-)",
+ .pathname = "/tmp/ksft.nommu-remap-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ,
+ .mmap_flags = MAP_PRIVATE,
+#ifdef CONFIG_NOMMU
+ .exp_err = EPERM,
+#else
+ .exp_err = 0,
+#endif
+ .resolve_exp_err = 0,
+ .old_pages = 4,
+ .new_pages = 8,
+ },
+ {
+ .name = "private file growth (rw-)",
+ .pathname = "/tmp/ksft.nommu-remap-XXXXXX",
+ .open_flags = O_CREAT | O_RDWR | O_EXCL,
+ .mmap_prot = PROT_READ | PROT_WRITE,
+ .mmap_flags = MAP_PRIVATE,
+#ifdef CONFIG_NOMMU
+ .exp_err = ENOMEM,
+#else
+ .exp_err = 0,
+#endif
+ .resolve_exp_err = 0,
+ .old_pages = 4,
+ .new_pages = 8,
+ },
+};
+
+static int run_mremap_test(struct mremap_case_t *tcase)
+{
+ int fd = -1;
+ void *addr, *addr2;
+ char pb[PATH_MAX];
+ int rc = KSFT_PASS;
+ int expected_error;
+ unsigned int old_pages = tcase->old_pages ?: 4;
+ unsigned int new_pages = tcase->new_pages ?: 2;
+ unsigned int file_pages = old_pages > new_pages ?
+ old_pages : new_pages;
+ int orig_nr_trim_pages = -1;
+
+ ksft_print_msg("[RUN] Testing mremap: %s\n", tcase->name);
+
+ if (tcase->pathname && strstr(tcase->pathname, "XXXXXX")) {
+ strncpy(pb, tcase->pathname, sizeof(pb) - 1);
+ pb[sizeof(pb) - 1] = '\0';
+ fd = mkstemp(pb);
+ if (fd < 0) {
+ ksft_test_result_skip("Failed to setup file backing\n");
+ return KSFT_SKIP;
+ }
+ if (ftruncate(fd, ps * file_pages) != 0) {
+ ksft_test_result_fail("Failed to setup file backing\n");
+ close(fd);
+ unlink(pb);
+ return KSFT_FAIL;
+ }
+
+ if ((tcase->mmap_flags & MAP_SHARED) && get_fs_type(pb) != RAMFS_MAGIC) {
+ ksft_test_result_skip("Skip the test under non-ramfs filesystem (%s)\n",
+ pb);
+ close(fd);
+ unlink(pb);
+ return KSFT_SKIP;
+ }
+ } else if (tcase->pathname) {
+ fd = open(tcase->pathname, tcase->open_flags, 0600);
+ if (fd < 0) {
+ ksft_test_result_skip("Backing node not accessible\n");
+ return KSFT_SKIP;
+ }
+
+ if ((tcase->mmap_flags & MAP_SHARED) &&
+ get_fs_type(tcase->pathname) != RAMFS_MAGIC) {
+ ksft_test_result_skip("Skip the test under non-ramfs filesystem (%s)\n",
+ tcase->pathname);
+ close(fd);
+ return KSFT_SKIP;
+ }
+ }
+
+ addr = mmap(NULL, ps * old_pages, tcase->mmap_prot,
+ tcase->mmap_flags, fd, 0);
+ if (addr == MAP_FAILED) {
+ ksft_print_msg("mmap mapping failed %s(%d)\n", strerror(errno), errno);
+ rc = KSFT_FAIL;
+ goto out;
+ }
+
+ expected_error = tcase->exp_err;
+ if (tcase->resolve_exp_err && fd >= 0)
+ expected_error = tcase->resolve_exp_err(pb);
+
+ addr2 = mremap(addr, ps * old_pages, ps * new_pages,
+ MREMAP_MAYMOVE);
+
+ if (expected_error != 0) {
+ if (addr2 != MAP_FAILED || errno != expected_error) {
+ ksft_print_msg("Expected error %d, got %s(%d)\n",
+ expected_error, strerror(errno), errno);
+ rc = KSFT_FAIL;
+ } else {
+ ksft_print_msg("%s/%s: Handled expected error path (errno=%d)\n",
+ __func__, tcase->name, expected_error);
+ }
+ } else if (addr2 == MAP_FAILED) {
+ ksft_print_msg("mremap shrink failed unexpectedly: %s\n",
+ strerror(errno));
+ rc = KSFT_FAIL;
+ } else {
+ ksft_print_msg("%s/%s step successful\n", __func__, tcase->name);
+ }
+
+ /* clean up */
+ if (munmap(addr2 == MAP_FAILED ? addr : addr2,
+ addr2 == MAP_FAILED ? ps * old_pages : ps * new_pages)) {
+ ksft_print_msg("munmap failed: %s\n", strerror(errno));
+ rc = KSFT_FAIL;
+ }
+
+out:
+ if (fd >= 0) {
+ close(fd);
+ if (tcase->pathname && strstr(tcase->pathname, "XXXXXX"))
+ unlink(pb);
+ }
+
+ ksft_test_result_report(rc, "%s:%s\n", __func__, tcase->name);
+ return rc;
+}
+
+int main(int argc, char **argv)
+{
+ int res = KSFT_PASS;
+ int i;
+
+ ps = sysconf(_SC_PAGESIZE);
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(mremap_cases) + 2);
+
+ munmap_shrink_test();
+ mremap_shrink_test();
+
+ for (i = 0; i < (int)ARRAY_SIZE(mremap_cases); i++) {
+ if (run_mremap_test(&mremap_cases[i]) == KSFT_FAIL)
+ res = KSFT_FAIL;
+ }
+
+ if (res == KSFT_PASS)
+ ksft_finished();
+
+ ksft_exit_fail();
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 0/2] support kselftest on nommu platform
2026-08-25 1:59 [PATCH v2 0/2] support kselftest on nommu platform Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 1/2] selftests: run tests on nommu architecture Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 2/2] selftests/mm: add nommu mmap and mremap behavior tests Hajime Tazaki
@ 2026-08-25 8:37 ` David Hildenbrand (Arm)
2 siblings, 0 replies; 4+ messages in thread
From: David Hildenbrand (Arm) @ 2026-08-25 8:37 UTC (permalink / raw)
To: Hajime Tazaki, linux-mm, liam, rbm, akpm, luto, brendan.jackman,
liuhangbin, corbet, kees, ljs, broonie, mhocko, rppt, shuah,
surenb, vbabka, wad, linux-doc, linux-kselftest, linux-um
Cc: geert, daniel
On 8/25/26 03:59, Hajime Tazaki wrote:
> We add an ability to execute kselftest on nommu platforms.
>
> Currently there are several issues if we wish to run kselftests on nommu
> targets:
>
> - it cannot compile/build test binaries because the current files mainly
> assume to build with glibc,
> - some of the tests are not able to run on nommu targets as there are no
> fork(2) syscall.
>
> The first issue can be avoided if we can build static PIE binaries (if
> targets support it), but in our case (build on ubuntu/glibc and run on
> alpine/musl-libc), it fails to invoke due to lack of the GNU ifunc
> mechanism. Thus, we need to cross-compile with musl toolchain, which
> needs to be solved the first issue.
>
> The second issue is the lack of fork(2) syscall on those platforms.
> Especially the test harness helper (kselftest_harness.h) uses the
> syscall, which cannot be simply with vfork(2). `timeout` command used
> in `runner.sh` never works for nommu platform as it uses fork(2).
>
> nommu component in the mm subsystem has several known issues and having
> test cases should help this situation, thus this patchset is very first
> step toward enriching test environment which has not been well tested
> for a while. The test cases is implemented based on the document
> (Documentation/admin-guide/mm/nommu-mmap.rst).
>
> So, for the first step, nommu targets only support low-level API of
> kselftests (kselftest.h), and implement tests in a new target,
> TARGETS=mm/nommu. Other targets are currently not even able to build
> due to toolchain issues but will be addressed once the initial
> introduction which mainly focuses on nommu test will be settled.
>
> The patch was initially combined with other patches but is decoupled to
> only focus on test framework and testcases.
>
> - rfc:
> https://lore.kernel.org/linux-mm/20260813063401.1786548-1-thehajime@gmail.com/
>
> Hajime Tazaki (2):
> selftests: run tests on nommu architecture
> selftests/mm: add nommu mmap and mremap behavior tests
>
> Documentation/dev-tools/kselftest.rst | 14 +
> tools/testing/selftests/Makefile | 1 +
> tools/testing/selftests/kselftest/runner.sh | 9 +-
> tools/testing/selftests/kselftest_harness.h | 4 +
> tools/testing/selftests/lib.mk | 8 +
> tools/testing/selftests/mm/nommu/Makefile | 7 +
> .../selftests/mm/nommu/nommu_mmap_test.c | 265 +++++++++++++
> .../selftests/mm/nommu/nommu_mremap_test.c | 353 ++++++++++++++++++
That's odd.
tools/testing/selftests/mm
itself should know which tests can be built and ran on nommu. nommu-only tests
can be placed in mm/nommu, but I would expect tools/testing/selftests/mm's
Makefile and run script to compile and run only selftests that are supported on
the given platform.
--
Cheers,
David
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-25 8:37 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25 1:59 [PATCH v2 0/2] support kselftest on nommu platform Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 1/2] selftests: run tests on nommu architecture Hajime Tazaki
2026-08-25 1:59 ` [PATCH v2 2/2] selftests/mm: add nommu mmap and mremap behavior tests Hajime Tazaki
2026-08-25 8:37 ` [PATCH v2 0/2] support kselftest on nommu platform David Hildenbrand (Arm)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox