On Tue,  6 Oct 2026 17:56:40 +0900 Hajime Tazaki <[email protected]> wrote:

> This patchset adds 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).
> 
> Initial shape for this patches is implemented as an opt-in TARGET, not
> built/invoked automatically if users don't specify this nommu target.

Thanks, I've updated mm.git's mm-unstable branch with this version.

Then I moved the series down into the tail of the mm-unstable branch,
in preparation for excluding it from the 7.3-rcX queue.

It's changing a lot and it isn't converging quickly and review hasn't
happened and everyone's traveling so I'll likely hold this series over
for 7.4-rcX.

Hang in there - it's great that you're caring for nommu!

> --

That should be --- (in the patches too).

<grabs the v4->v5 logging from the individual patches>

> v4 => v5
> - remove build-time selection and detect NOMMU at runtime
> - remove #ifdef NOMMU and use a global variable detected from /proc/meminfo

Here's how v5 altered mm.git:



 Documentation/dev-tools/kselftest.rst             |   18 +-
 tools/testing/selftests/kselftest/runner.sh       |    9 -
 tools/testing/selftests/nommu/Makefile            |    1 
 tools/testing/selftests/nommu/local.mk            |    7 
 tools/testing/selftests/nommu/nommu.h             |   33 ++++
 tools/testing/selftests/nommu/nommu_mmap_test.c   |   91 ++++++------
 tools/testing/selftests/nommu/nommu_mremap_test.c |   72 ++++-----
 7 files changed, 129 insertions(+), 102 deletions(-)

--- a/Documentation/dev-tools/kselftest.rst~b
+++ a/Documentation/dev-tools/kselftest.rst
@@ -233,16 +233,20 @@ section::
 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.
+The nommu selftests detect NOMMU at runtime by checking for the ``MmapCopy``
+entry in ``/proc/meminfo``. Ensure procfs is mounted at ``/proc`` before
+building and running the tests.
 
 ::
 
-  $ make ARCH=um NOMMU=1 O=build kselftest-all TARGETS=nommu  # <= build-only
-  $ make ARCH=um NOMMU=1 O=build kselftest-install TARGETS=nommu
-  $ NOMMU=1 ./build/kselftest/kselftest_install/run_kselftest.sh -p -c nommu
+  $ make ARCH=um O=build kselftest-all TARGETS=nommu  # <= build-only
+  $ make ARCH=um O=build kselftest-install TARGETS=nommu
+  $ ./build/kselftest/kselftest_install/run_kselftest.sh -p -c nommu
+
+The C tests report a skip if ``/proc/meminfo`` is unavailable. On a NOMMU
+system, the runner needs that file to avoid wrapping the tests in ``timeout``
+before they can report a skip. Mounting procfs is therefore a prerequisite
+for running this target through the kselftest runner.
 
 Contributing new tests
 ======================
--- a/tools/testing/selftests/kselftest/runner.sh~b
+++ a/tools/testing/selftests/kselftest/runner.sh
@@ -36,14 +36,19 @@ tap_prefix()
        fi
 }
 
+is_nommu()
+{
+       grep -q '^MmapCopy:' /proc/meminfo 2>/dev/null
+}
+
 tap_timeout()
 {
        # nommu doesn't support timeout command (missing fork(2))
-       if [ "$NOMMU" = "1" ] ; then
+       if is_nommu; then
                echo "timeout isn't supported for NOMMU"
                $1
        # Make sure tests will time out if utility is available.
-       elif [ -x /usr/bin/timeout ] ; then
+       elif [ -x /usr/bin/timeout ]; then
                /usr/bin/timeout --foreground "$kselftest_timeout" \
                        /usr/bin/timeout "$kselftest_timeout" $1
        else
diff --git a/tools/testing/selftests/nommu/local.mk 
a/tools/testing/selftests/nommu/local.mk
deleted file mode 100644
--- a/tools/testing/selftests/nommu/local.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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 += -DNOMMU
-export NOMMU
-endif
--- a/tools/testing/selftests/nommu/Makefile~b
+++ a/tools/testing/selftests/nommu/Makefile
@@ -5,4 +5,3 @@ TEST_GEN_PROGS += nommu_mmap_test
 TEST_GEN_PROGS += nommu_mremap_test
 
 include ../lib.mk
-include local.mk
diff --git a/tools/testing/selftests/nommu/nommu.h 
a/tools/testing/selftests/nommu/nommu.h
new file mode 100644
--- /dev/null
+++ a/tools/testing/selftests/nommu/nommu.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef NOMMU_H
+#define NOMMU_H
+
+#include <stdio.h>
+#include <string.h>
+
+/* Returns 1 for NOMMU, 0 for MMU, and -1 if /proc/meminfo is unavailable. */
+static inline int ksft_is_nommu(void)
+{
+       FILE *fp;
+       char line[256];
+       int nommu = 0;
+
+       fp = fopen("/proc/meminfo", "r");
+       if (!fp)
+               return -1;
+
+       while (fgets(line, sizeof(line), fp)) {
+               if (strncmp(line, "MmapCopy:", sizeof("MmapCopy:") - 1) == 0) {
+                       nommu = 1;
+                       break;
+               }
+       }
+
+       if (ferror(fp))
+               nommu = -1;
+       fclose(fp);
+
+       return nommu;
+}
+
+#endif
--- a/tools/testing/selftests/nommu/nommu_mmap_test.c~b
+++ a/tools/testing/selftests/nommu/nommu_mmap_test.c
@@ -9,6 +9,7 @@
 #include <string.h>
 #include <limits.h>
 #include "kselftest.h"
+#include "nommu.h"
 
 #include <sys/vfs.h>
 #ifndef RAMFS_MAGIC
@@ -16,6 +17,7 @@
 #endif
 
 static size_t ps;
+static int nommu;
 
 struct test_case_t {
        const char *name;
@@ -31,6 +33,9 @@ static int get_shm_expected_error(const
 {
        struct statfs fs;
 
+       if (!nommu)
+               return 0;
+
        if (statfs(path, &fs) == 0) {
                if (fs.f_type == RAMFS_MAGIC)
                        return 0; /* ramfs succeed with contiguous memory */
@@ -74,11 +79,7 @@ static struct test_case_t test_cases[] =
                .mmap_prot = PROT_READ | PROT_WRITE,
                .mmap_flags = MAP_SHARED,
                .exp_err = 0,
-#ifdef NOMMU
                .resolve_exp_err = get_shm_expected_error,
-#else
-               .resolve_exp_err = NULL,
-#endif
        },
        {
                .name = "non-anonymous shared file mapping (r--)",
@@ -87,18 +88,14 @@ static struct test_case_t test_cases[] =
                .mmap_prot = PROT_READ,
                .mmap_flags = MAP_SHARED,
                .exp_err = 0,
-#ifdef NOMMU
                .resolve_exp_err = get_shm_expected_error,
-#else
-               .resolve_exp_err = 0,
-#endif
        },
 };
 
 static int run_mapping_matrix_test(struct test_case_t *tcase)
 {
        int fd;
-       void *ptr;
+       void *ptr = MAP_FAILED;
        char path_buf[PATH_MAX];
        const char *path = tcase->pathname;
        int rc = KSFT_PASS;
@@ -120,10 +117,8 @@ static int run_mapping_matrix_test(struc
                }
                if (ftruncate(fd, ps) != 0) {
                        ksft_print_msg("ftruncate failed for: %s\n", 
tcase->pathname);
-                       ksft_test_result_fail("%s\n", tcase->name);
-                       close(fd);
-                       unlink(path_buf);
-                       return KSFT_FAIL;
+                       rc = KSFT_FAIL;
+                       goto cleanup;
                }
                path = path_buf;
        } else {
@@ -146,41 +141,42 @@ static int run_mapping_matrix_test(struc
                if (ptr != MAP_FAILED) {
                        ksft_print_msg("mmap unexpectedly succeeded (exp error 
%d)\n",
                                              expected_error);
-                       ksft_test_result_fail("%s\n", tcase->name);
-                       munmap(ptr, ps);
                        rc = KSFT_FAIL;
                        goto cleanup;
                }
                if (errno != expected_error) {
                        ksft_print_msg("mmap failed with %d (%s), but expected 
%d\n",
                                errno, strerror(errno), expected_error);
-                       ksft_test_result_fail("%s\n", tcase->name);
                        rc = KSFT_FAIL;
                        goto cleanup;
                }
                ksft_print_msg("Correctly rejected with expected error 
%s(%d)\n",
                        strerror(expected_error), expected_error);
-               ksft_test_result_pass("%s\n", tcase->name);
                rc = KSFT_PASS;
                goto cleanup;
        }
 
        if (ptr == MAP_FAILED) {
                ksft_print_msg("mmap failed unexpectedly: %s\n", 
strerror(errno));
-               ksft_test_result_fail("%s\n", tcase->name);
                rc = KSFT_FAIL;
                goto cleanup;
        }
 
-       ksft_test_result_pass("%s\n", tcase->name);
-       munmap(ptr, ps);
-
 cleanup:
+       if (ptr != MAP_FAILED && munmap(ptr, ps)) {
+               ksft_print_msg("munmap failed: %s\n", strerror(errno));
+               rc = KSFT_FAIL;
+       }
        if (fd >= 0) {
                close(fd);
-               if (tcase->pathname && strstr(tcase->pathname, "XXXXXX"))
-                       unlink(path_buf);
+               if (tcase->pathname && strstr(tcase->pathname, "XXXXXX") &&
+                   unlink(path_buf)) {
+                       ksft_print_msg("unlink failed for %s: %s\n",
+                                      path_buf, strerror(errno));
+                       rc = KSFT_FAIL;
+               }
        }
+       ksft_test_result_report(rc, "%s\n", tcase->name);
        return rc;
 }
 
@@ -210,48 +206,55 @@ static int test_map_fixed(void)
        ptr = mmap(fixed_addr, ps, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
 
-#ifdef NOMMU
-       if (ptr == MAP_FAILED && (errno == ENODEV || errno == EINVAL)) {
-               ksft_print_msg("MAP_FIXED correctly rejected under nommu\n");
-               ksft_test_result_pass("MAP_FIXED behavior\n");
-               return KSFT_PASS;
-       }
-       if (ptr != MAP_FAILED) {
-               ksft_print_msg("MAP_FIXED unexpectedly allowed under nommu\n");
+       if (nommu) {
+               if (ptr == MAP_FAILED && (errno == ENODEV || errno == EINVAL)) {
+                       ksft_print_msg("MAP_FIXED correctly rejected under 
nommu\n");
+                       ksft_test_result_pass("MAP_FIXED behavior\n");
+                       return KSFT_PASS;
+               }
+               if (ptr != MAP_FAILED) {
+                       ksft_print_msg("MAP_FIXED unexpectedly allowed under 
nommu\n");
+                       ksft_test_result_fail("MAP_FIXED behavior\n");
+                       if (munmap(ptr, ps))
+                               ksft_print_msg("munmap failed: %s\n", 
strerror(errno));
+                       return KSFT_FAIL;
+               }
+               ksft_print_msg("MAP_FIXED failed under NOMMU: %s\n",
+                                     strerror(errno));
                ksft_test_result_fail("MAP_FIXED behavior\n");
-               munmap(ptr, ps);
                return KSFT_FAIL;
        }
-       ksft_print_msg("MAP_FIXED failed under NOMMU: %s\n",
-                             strerror(errno));
-       ksft_test_result_fail("MAP_FIXED behavior\n");
-       return KSFT_FAIL;
-#else
        if (ptr != MAP_FAILED) {
                ksft_print_msg("MAP_FIXED successfully allocated under MMU\n");
+               if (munmap(ptr, ps)) {
+                       ksft_print_msg("munmap failed: %s\n", strerror(errno));
+                       ksft_test_result_fail("MAP_FIXED behavior\n");
+                       return KSFT_FAIL;
+               }
                ksft_test_result_pass("MAP_FIXED behavior\n");
-               munmap(ptr, ps);
                return KSFT_PASS;
        }
        ksft_print_msg("MAP_FIXED failed allocation under MMU\n");
        ksft_test_result_fail("MAP_FIXED behavior\n");
        return KSFT_FAIL;
-#endif
 }
 
 int main(int argc, char **argv)
 {
        int i;
 
+       nommu = ksft_is_nommu();
+       if (nommu < 0)
+               ksft_exit_skip("Cannot read /proc/meminfo; mount procfs at 
/proc\n");
+
        ps = sysconf(_SC_PAGESIZE);
        ksft_print_header();
        ksft_set_plan(ARRAY_SIZE(test_cases) + 1);
 
-#ifdef 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 (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");
 
        test_map_fixed();
        for (i = 0; i < (int)ARRAY_SIZE(test_cases); i++)
--- a/tools/testing/selftests/nommu/nommu_mremap_test.c~b
+++ a/tools/testing/selftests/nommu/nommu_mremap_test.c
@@ -9,6 +9,7 @@
 #include <string.h>
 #include <limits.h>
 #include "kselftest.h"
+#include "nommu.h"
 
 #include <sys/vfs.h>
 #ifndef RAMFS_MAGIC
@@ -16,6 +17,7 @@
 #endif
 
 static size_t ps;
+static int nommu;
 
 static long get_fs_type(const char *path)
 {
@@ -45,8 +47,7 @@ static void munmap_shrink_test(void)
                if (ret != 0) {
                        ksft_print_msg("memory %p isn't unmapped at %p\n",
                                addr, (char *)addr + ps * i);
-                       ksft_test_result_fail("munmap shrink\n");
-                       return;
+                       goto error;
                }
 
                if (i == 0) {
@@ -67,8 +68,10 @@ static void munmap_shrink_test(void)
        ksft_test_result_pass("munmap shrink\n");
        return;
 error:
-       for (int j = 0; j < 4; j++)
-               munmap((char *)addr + j * ps, ps);
+       for (int j = 0; j < 4; j++) {
+               if (munmap((char *)addr + j * ps, ps))
+                       ksft_print_msg("cleanup munmap failed: %s\n", 
strerror(errno));
+       }
        ksft_print_msg("clean up failures\n");
        ksft_test_result_fail("munmap shrink\n");
 }
@@ -132,7 +135,7 @@ static void mremap_shrink_test(void)
 
 static int get_shared_writable_file_expected_error(const char *path)
 {
-       if (get_fs_type(path) == RAMFS_MAGIC)
+       if (nommu && get_fs_type(path) == RAMFS_MAGIC)
                return EPERM; /* ramfs failed */
 
        return 0;
@@ -145,6 +148,7 @@ struct mremap_case_t {
        int mmap_prot;
        int mmap_flags;
        int exp_err;
+       int nommu_exp_err;
        int (*resolve_exp_err)(const char *path);
        unsigned int old_pages;
        unsigned int new_pages;
@@ -167,11 +171,7 @@ static struct mremap_case_t mremap_cases
                .mmap_prot = PROT_READ,
                .mmap_flags = MAP_SHARED,
                .exp_err = 0,
-#ifdef NOMMU
                .resolve_exp_err = get_shared_writable_file_expected_error,
-#else
-               .resolve_exp_err = 0,
-#endif
        },
        {
                .name = "private file unchanged length (r-)",
@@ -179,11 +179,8 @@ static struct mremap_case_t mremap_cases
                .open_flags = O_CREAT | O_RDWR | O_EXCL,
                .mmap_prot = PROT_READ,
                .mmap_flags = MAP_PRIVATE,
-#ifdef NOMMU
-               .exp_err = EPERM,
-#else
                .exp_err = 0,
-#endif
+               .nommu_exp_err = EPERM,
                .resolve_exp_err = 0,
                .old_pages = 4,
                .new_pages = 4,
@@ -205,11 +202,8 @@ static struct mremap_case_t mremap_cases
                .open_flags = O_CREAT | O_RDWR | O_EXCL,
                .mmap_prot = PROT_READ,
                .mmap_flags = MAP_PRIVATE,
-#ifdef NOMMU
-               .exp_err = EPERM,
-#else
                .exp_err = 0,
-#endif
+               .nommu_exp_err = EPERM,
                .resolve_exp_err = 0,
                .old_pages = 4,
                .new_pages = 8,
@@ -220,11 +214,8 @@ static struct mremap_case_t mremap_cases
                .open_flags = O_CREAT | O_RDWR | O_EXCL,
                .mmap_prot = PROT_READ | PROT_WRITE,
                .mmap_flags = MAP_PRIVATE,
-#ifdef NOMMU
-               .exp_err = ENOMEM,
-#else
                .exp_err = 0,
-#endif
+               .nommu_exp_err = ENOMEM,
                .resolve_exp_err = 0,
                .old_pages = 4,
                .new_pages = 8,
@@ -257,22 +248,17 @@ static int run_mremap_test(struct mremap
                }
                if (ftruncate(fd, ps * file_pages) != 0) {
                        ksft_print_msg("Failed to setup file backing\n");
-                       ksft_test_result_fail("%s\n", tcase->name);
-                       close(fd);
-                       unlink(pb);
-                       return KSFT_FAIL;
+                       rc = KSFT_FAIL;
+                       goto out;
                }
 
-#ifdef NOMMU
-               if ((tcase->mmap_flags & MAP_SHARED) && get_fs_type(pb) != 
RAMFS_MAGIC) {
+               if (nommu && (tcase->mmap_flags & MAP_SHARED) &&
+                   get_fs_type(pb) != RAMFS_MAGIC) {
                        ksft_print_msg("Skip the test under non-ramfs 
filesystem (%s)\n",
                                pb);
-                       ksft_test_result_skip("%s\n", tcase->name);
-                       close(fd);
-                       unlink(pb);
-                       return KSFT_SKIP;
+                       rc = KSFT_SKIP;
+                       goto out;
                }
-#endif
                path = pb;
        } else if (tcase->pathname) {
                fd = open(tcase->pathname, tcase->open_flags, 0600);
@@ -282,16 +268,13 @@ static int run_mremap_test(struct mremap
                        return KSFT_SKIP;
                }
 
-#ifdef NOMMU
-               if ((tcase->mmap_flags & MAP_SHARED) &&
+               if (nommu && (tcase->mmap_flags & MAP_SHARED) &&
                    get_fs_type(tcase->pathname) != RAMFS_MAGIC) {
                        ksft_print_msg("Skip the test under non-ramfs 
filesystem (%s)\n",
                                              tcase->pathname);
-                       ksft_test_result_skip("%s\n", tcase->name);
-                       close(fd);
-                       return KSFT_SKIP;
+                       rc = KSFT_SKIP;
+                       goto out;
                }
-#endif
        }
 
        addr = mmap(NULL, ps * old_pages, tcase->mmap_prot,
@@ -302,7 +285,7 @@ static int run_mremap_test(struct mremap
                goto out;
        }
 
-       expected_error = tcase->exp_err;
+       expected_error = nommu ? tcase->nommu_exp_err : tcase->exp_err;
        if (tcase->resolve_exp_err && fd >= 0)
                expected_error = tcase->resolve_exp_err(path);
 
@@ -340,8 +323,11 @@ static int run_mremap_test(struct mremap
 out:
        if (fd >= 0) {
                close(fd);
-               if (tcase->pathname && strstr(tcase->pathname, "XXXXXX"))
-                       unlink(pb);
+               if (tcase->pathname && strstr(tcase->pathname, "XXXXXX") &&
+                   unlink(pb)) {
+                       ksft_print_msg("unlink failed for %s: %s\n", pb, 
strerror(errno));
+                       rc = KSFT_FAIL;
+               }
        }
 
        ksft_test_result_report(rc, "%s\n", tcase->name);
@@ -352,6 +338,10 @@ int main(int argc, char **argv)
 {
        int i;
 
+       nommu = ksft_is_nommu();
+       if (nommu < 0)
+               ksft_exit_skip("Cannot read /proc/meminfo; mount procfs at 
/proc\n");
+
        ps = sysconf(_SC_PAGESIZE);
        ksft_print_header();
        ksft_set_plan(ARRAY_SIZE(mremap_cases) + 2);
_


Reply via email to