selftests/mm: rename va_128TBswitch to va_high_addr_switch
authorChaitanya S Prakash <chaitanyas.prakash@arm.com>
Thu, 23 Mar 2023 10:52:40 +0000 (16:22 +0530)
committerAndrew Morton <akpm@linux-foundation.org>
Tue, 18 Apr 2023 23:53:51 +0000 (16:53 -0700)
As the initial selftest only took into consideration PowperPC and x86
architectures, on adding support for arm64, a platform independent naming
convention is chosen.

Link: https://lkml.kernel.org/r/20230323105243.2807166-3-chaitanyas.prakash@arm.com
Signed-off-by: Chaitanya S Prakash <chaitanyas.prakash@arm.com>
Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
tools/testing/selftests/mm/Makefile
tools/testing/selftests/mm/run_vmtests.sh
tools/testing/selftests/mm/va_128TBswitch.c [deleted file]
tools/testing/selftests/mm/va_128TBswitch.sh [deleted file]
tools/testing/selftests/mm/va_high_addr_switch.c [new file with mode: 0644]
tools/testing/selftests/mm/va_high_addr_switch.sh [new file with mode: 0644]

index 63c03a6414fcd06737ecf7ec6e08c005ba80b96e..de32f7dafa5d3439ae2caf39814e643b96aa7660 100644 (file)
@@ -94,7 +94,7 @@ endif
 endif
 
 ifneq (,$(filter $(MACHINE),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sh64 sparc64 x86_64))
-TEST_GEN_PROGS += va_128TBswitch
+TEST_GEN_PROGS += va_high_addr_switch
 TEST_GEN_PROGS += virtual_address_range
 TEST_GEN_PROGS += write_to_hugetlbfs
 endif
@@ -103,7 +103,7 @@ TEST_PROGS := run_vmtests.sh
 
 TEST_FILES := test_vmalloc.sh
 TEST_FILES += test_hmm.sh
-TEST_FILES += va_128TBswitch.sh
+TEST_FILES += va_high_addr_switch.sh
 
 include ../lib.mk
 
index 438eb49567b6bff8b816bb52f366a40b452211fe..83b59ae2d6f08a13ce82cd80c50a141c57d7d954 100644 (file)
@@ -237,7 +237,7 @@ if [ $VADDR64 -ne 0 ]; then
        echo $prev_policy > /proc/sys/vm/overcommit_memory
 
        # virtual address 128TB switch test
-       CATEGORY="hugevm" run_test ./va_128TBswitch.sh
+       CATEGORY="hugevm" run_test ./va_high_addr_switch.sh
 fi # VADDR64
 
 # vmalloc stability smoke test
diff --git a/tools/testing/selftests/mm/va_128TBswitch.c b/tools/testing/selftests/mm/va_128TBswitch.c
deleted file mode 100644 (file)
index 6679213..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- *
- * Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
- * Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
- */
-
-#include <stdio.h>
-#include <sys/mman.h>
-#include <string.h>
-
-#include "../kselftest.h"
-
-#ifdef __powerpc64__
-#define PAGE_SIZE      (64 << 10)
-/*
- * This will work with 16M and 2M hugepage size
- */
-#define HUGETLB_SIZE   (16 << 20)
-#elif __aarch64__
-/*
- * The default hugepage size for 64k base pagesize
- * is 512MB.
- */
-#define PAGE_SIZE      (64 << 10)
-#define HUGETLB_SIZE   (512 << 20)
-#else
-#define PAGE_SIZE      (4 << 10)
-#define HUGETLB_SIZE   (2 << 20)
-#endif
-
-/*
- * >= 128TB is the hint addr value we used to select
- * large address space.
- */
-
-#define ADDR_MARK_128TB        (1UL << 47)
-#define ADDR_MARK_256TB        (1UL << 48)
-
-#define HIGH_ADDR_128TB        ((void *) (1UL << 48))
-#define HIGH_ADDR_256TB        ((void *) (1UL << 49))
-
-#define LOW_ADDR       ((void *) (1UL << 30))
-
-#ifdef __aarch64__
-#define ADDR_SWITCH_HINT ADDR_MARK_256TB
-#define HIGH_ADDR       HIGH_ADDR_256TB
-#else
-#define ADDR_SWITCH_HINT ADDR_MARK_128TB
-#define HIGH_ADDR       HIGH_ADDR_128TB
-#endif
-
-struct testcase {
-       void *addr;
-       unsigned long size;
-       unsigned long flags;
-       const char *msg;
-       unsigned int low_addr_required:1;
-       unsigned int keep_mapped:1;
-};
-
-static struct testcase testcases[] = {
-       {
-               /*
-                * If stack is moved, we could possibly allocate
-                * this at the requested address.
-                */
-               .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
-               .size = PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
-               .low_addr_required = 1,
-       },
-       {
-               /*
-                * We should never allocate at the requested address or above it
-                * The len cross the 128TB boundary. Without MAP_FIXED
-                * we will always search in the lower address space.
-                */
-               .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, (2 * PAGE_SIZE))",
-               .low_addr_required = 1,
-       },
-       {
-               /*
-                * Exact mapping at 128TB, the area is free we should get that
-                * even without MAP_FIXED.
-                */
-               .addr = ((void *)(ADDR_SWITCH_HINT)),
-               .size = PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = (void *)(ADDR_SWITCH_HINT),
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-               .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
-       },
-       {
-               .addr = NULL,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(NULL)",
-               .low_addr_required = 1,
-       },
-       {
-               .addr = LOW_ADDR,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(LOW_ADDR)",
-               .low_addr_required = 1,
-       },
-       {
-               .addr = HIGH_ADDR,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(HIGH_ADDR)",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = HIGH_ADDR,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(HIGH_ADDR) again",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = HIGH_ADDR,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-               .msg = "mmap(HIGH_ADDR, MAP_FIXED)",
-       },
-       {
-               .addr = (void *) -1,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(-1)",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = (void *) -1,
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(-1) again",
-       },
-       {
-               .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
-               .size = PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
-               .low_addr_required = 1,
-       },
-       {
-               .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2 * PAGE_SIZE)",
-               .low_addr_required = 1,
-               .keep_mapped = 1,
-       },
-       {
-               .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE / 2),
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE/2 , 2 * PAGE_SIZE)",
-               .low_addr_required = 1,
-               .keep_mapped = 1,
-       },
-       {
-               .addr = ((void *)(ADDR_SWITCH_HINT)),
-               .size = PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
-       },
-       {
-               .addr = (void *)(ADDR_SWITCH_HINT),
-               .size = 2 * PAGE_SIZE,
-               .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-               .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
-       },
-};
-
-static struct testcase hugetlb_testcases[] = {
-       {
-               .addr = NULL,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(NULL, MAP_HUGETLB)",
-               .low_addr_required = 1,
-       },
-       {
-               .addr = LOW_ADDR,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(LOW_ADDR, MAP_HUGETLB)",
-               .low_addr_required = 1,
-       },
-       {
-               .addr = HIGH_ADDR,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(HIGH_ADDR, MAP_HUGETLB)",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = HIGH_ADDR,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = HIGH_ADDR,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-               .msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)",
-       },
-       {
-               .addr = (void *) -1,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(-1, MAP_HUGETLB)",
-               .keep_mapped = 1,
-       },
-       {
-               .addr = (void *) -1,
-               .size = HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(-1, MAP_HUGETLB) again",
-       },
-       {
-               .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
-               .size = 2 * HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
-               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2*HUGETLB_SIZE, MAP_HUGETLB)",
-               .low_addr_required = 1,
-               .keep_mapped = 1,
-       },
-       {
-               .addr = (void *)(ADDR_SWITCH_HINT),
-               .size = 2 * HUGETLB_SIZE,
-               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-               .msg = "mmap(ADDR_SWITCH_HINT , 2*HUGETLB_SIZE, MAP_FIXED | MAP_HUGETLB)",
-       },
-};
-
-static int run_test(struct testcase *test, int count)
-{
-       void *p;
-       int i, ret = KSFT_PASS;
-
-       for (i = 0; i < count; i++) {
-               struct testcase *t = test + i;
-
-               p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
-
-               printf("%s: %p - ", t->msg, p);
-
-               if (p == MAP_FAILED) {
-                       printf("FAILED\n");
-                       ret = KSFT_FAIL;
-                       continue;
-               }
-
-               if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) {
-                       printf("FAILED\n");
-                       ret = KSFT_FAIL;
-               } else {
-                       /*
-                        * Do a dereference of the address returned so that we catch
-                        * bugs in page fault handling
-                        */
-                       memset(p, 0, t->size);
-                       printf("OK\n");
-               }
-               if (!t->keep_mapped)
-                       munmap(p, t->size);
-       }
-
-       return ret;
-}
-
-static int supported_arch(void)
-{
-#if defined(__powerpc64__)
-       return 1;
-#elif defined(__x86_64__)
-       return 1;
-#elif defined(__aarch64__)
-       return 1;
-#else
-       return 0;
-#endif
-}
-
-int main(int argc, char **argv)
-{
-       int ret;
-
-       if (!supported_arch())
-               return KSFT_SKIP;
-
-       ret = run_test(testcases, ARRAY_SIZE(testcases));
-       if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
-               ret = run_test(hugetlb_testcases, ARRAY_SIZE(hugetlb_testcases));
-       return ret;
-}
diff --git a/tools/testing/selftests/mm/va_128TBswitch.sh b/tools/testing/selftests/mm/va_128TBswitch.sh
deleted file mode 100644 (file)
index 4158075..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-#
-# Copyright (C) 2022 Adam Sindelar (Meta) <adam@wowsignal.io>
-#
-# This is a test for mmap behavior with 5-level paging. This script wraps the
-# real test to check that the kernel is configured to support at least 5
-# pagetable levels.
-
-# 1 means the test failed
-exitcode=1
-
-# Kselftest framework requirement - SKIP code is 4.
-ksft_skip=4
-
-fail()
-{
-       echo "$1"
-       exit $exitcode
-}
-
-check_supported_x86_64()
-{
-       local config="/proc/config.gz"
-       [[ -f "${config}" ]] || config="/boot/config-$(uname -r)"
-       [[ -f "${config}" ]] || fail "Cannot find kernel config in /proc or /boot"
-
-       # gzip -dcfq automatically handles both compressed and plaintext input.
-       # See man 1 gzip under '-f'.
-       local pg_table_levels=$(gzip -dcfq "${config}" | grep PGTABLE_LEVELS | cut -d'=' -f 2)
-
-       if [[ "${pg_table_levels}" -lt 5 ]]; then
-               echo "$0: PGTABLE_LEVELS=${pg_table_levels}, must be >= 5 to run this test"
-               exit $ksft_skip
-       fi
-}
-
-check_test_requirements()
-{
-       # The test supports x86_64 and powerpc64. We currently have no useful
-       # eligibility check for powerpc64, and the test itself will reject other
-       # architectures.
-       case `uname -m` in
-               "x86_64")
-                       check_supported_x86_64
-               ;;
-               *)
-                       return 0
-               ;;
-       esac
-}
-
-check_test_requirements
-./va_128TBswitch
diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c
new file mode 100644 (file)
index 0000000..6679213
--- /dev/null
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+ * Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ */
+
+#include <stdio.h>
+#include <sys/mman.h>
+#include <string.h>
+
+#include "../kselftest.h"
+
+#ifdef __powerpc64__
+#define PAGE_SIZE      (64 << 10)
+/*
+ * This will work with 16M and 2M hugepage size
+ */
+#define HUGETLB_SIZE   (16 << 20)
+#elif __aarch64__
+/*
+ * The default hugepage size for 64k base pagesize
+ * is 512MB.
+ */
+#define PAGE_SIZE      (64 << 10)
+#define HUGETLB_SIZE   (512 << 20)
+#else
+#define PAGE_SIZE      (4 << 10)
+#define HUGETLB_SIZE   (2 << 20)
+#endif
+
+/*
+ * >= 128TB is the hint addr value we used to select
+ * large address space.
+ */
+
+#define ADDR_MARK_128TB        (1UL << 47)
+#define ADDR_MARK_256TB        (1UL << 48)
+
+#define HIGH_ADDR_128TB        ((void *) (1UL << 48))
+#define HIGH_ADDR_256TB        ((void *) (1UL << 49))
+
+#define LOW_ADDR       ((void *) (1UL << 30))
+
+#ifdef __aarch64__
+#define ADDR_SWITCH_HINT ADDR_MARK_256TB
+#define HIGH_ADDR       HIGH_ADDR_256TB
+#else
+#define ADDR_SWITCH_HINT ADDR_MARK_128TB
+#define HIGH_ADDR       HIGH_ADDR_128TB
+#endif
+
+struct testcase {
+       void *addr;
+       unsigned long size;
+       unsigned long flags;
+       const char *msg;
+       unsigned int low_addr_required:1;
+       unsigned int keep_mapped:1;
+};
+
+static struct testcase testcases[] = {
+       {
+               /*
+                * If stack is moved, we could possibly allocate
+                * this at the requested address.
+                */
+               .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
+               .size = PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
+               .low_addr_required = 1,
+       },
+       {
+               /*
+                * We should never allocate at the requested address or above it
+                * The len cross the 128TB boundary. Without MAP_FIXED
+                * we will always search in the lower address space.
+                */
+               .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, (2 * PAGE_SIZE))",
+               .low_addr_required = 1,
+       },
+       {
+               /*
+                * Exact mapping at 128TB, the area is free we should get that
+                * even without MAP_FIXED.
+                */
+               .addr = ((void *)(ADDR_SWITCH_HINT)),
+               .size = PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = (void *)(ADDR_SWITCH_HINT),
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+               .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
+       },
+       {
+               .addr = NULL,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(NULL)",
+               .low_addr_required = 1,
+       },
+       {
+               .addr = LOW_ADDR,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(LOW_ADDR)",
+               .low_addr_required = 1,
+       },
+       {
+               .addr = HIGH_ADDR,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(HIGH_ADDR)",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = HIGH_ADDR,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(HIGH_ADDR) again",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = HIGH_ADDR,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+               .msg = "mmap(HIGH_ADDR, MAP_FIXED)",
+       },
+       {
+               .addr = (void *) -1,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(-1)",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = (void *) -1,
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(-1) again",
+       },
+       {
+               .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
+               .size = PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
+               .low_addr_required = 1,
+       },
+       {
+               .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2 * PAGE_SIZE)",
+               .low_addr_required = 1,
+               .keep_mapped = 1,
+       },
+       {
+               .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE / 2),
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE/2 , 2 * PAGE_SIZE)",
+               .low_addr_required = 1,
+               .keep_mapped = 1,
+       },
+       {
+               .addr = ((void *)(ADDR_SWITCH_HINT)),
+               .size = PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
+       },
+       {
+               .addr = (void *)(ADDR_SWITCH_HINT),
+               .size = 2 * PAGE_SIZE,
+               .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+               .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
+       },
+};
+
+static struct testcase hugetlb_testcases[] = {
+       {
+               .addr = NULL,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(NULL, MAP_HUGETLB)",
+               .low_addr_required = 1,
+       },
+       {
+               .addr = LOW_ADDR,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(LOW_ADDR, MAP_HUGETLB)",
+               .low_addr_required = 1,
+       },
+       {
+               .addr = HIGH_ADDR,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(HIGH_ADDR, MAP_HUGETLB)",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = HIGH_ADDR,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = HIGH_ADDR,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+               .msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)",
+       },
+       {
+               .addr = (void *) -1,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(-1, MAP_HUGETLB)",
+               .keep_mapped = 1,
+       },
+       {
+               .addr = (void *) -1,
+               .size = HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(-1, MAP_HUGETLB) again",
+       },
+       {
+               .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
+               .size = 2 * HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
+               .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2*HUGETLB_SIZE, MAP_HUGETLB)",
+               .low_addr_required = 1,
+               .keep_mapped = 1,
+       },
+       {
+               .addr = (void *)(ADDR_SWITCH_HINT),
+               .size = 2 * HUGETLB_SIZE,
+               .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+               .msg = "mmap(ADDR_SWITCH_HINT , 2*HUGETLB_SIZE, MAP_FIXED | MAP_HUGETLB)",
+       },
+};
+
+static int run_test(struct testcase *test, int count)
+{
+       void *p;
+       int i, ret = KSFT_PASS;
+
+       for (i = 0; i < count; i++) {
+               struct testcase *t = test + i;
+
+               p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
+
+               printf("%s: %p - ", t->msg, p);
+
+               if (p == MAP_FAILED) {
+                       printf("FAILED\n");
+                       ret = KSFT_FAIL;
+                       continue;
+               }
+
+               if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) {
+                       printf("FAILED\n");
+                       ret = KSFT_FAIL;
+               } else {
+                       /*
+                        * Do a dereference of the address returned so that we catch
+                        * bugs in page fault handling
+                        */
+                       memset(p, 0, t->size);
+                       printf("OK\n");
+               }
+               if (!t->keep_mapped)
+                       munmap(p, t->size);
+       }
+
+       return ret;
+}
+
+static int supported_arch(void)
+{
+#if defined(__powerpc64__)
+       return 1;
+#elif defined(__x86_64__)
+       return 1;
+#elif defined(__aarch64__)
+       return 1;
+#else
+       return 0;
+#endif
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+
+       if (!supported_arch())
+               return KSFT_SKIP;
+
+       ret = run_test(testcases, ARRAY_SIZE(testcases));
+       if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
+               ret = run_test(hugetlb_testcases, ARRAY_SIZE(hugetlb_testcases));
+       return ret;
+}
diff --git a/tools/testing/selftests/mm/va_high_addr_switch.sh b/tools/testing/selftests/mm/va_high_addr_switch.sh
new file mode 100644 (file)
index 0000000..3056788
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2022 Adam Sindelar (Meta) <adam@wowsignal.io>
+#
+# This is a test for mmap behavior with 5-level paging. This script wraps the
+# real test to check that the kernel is configured to support at least 5
+# pagetable levels.
+
+# 1 means the test failed
+exitcode=1
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+fail()
+{
+       echo "$1"
+       exit $exitcode
+}
+
+check_supported_x86_64()
+{
+       local config="/proc/config.gz"
+       [[ -f "${config}" ]] || config="/boot/config-$(uname -r)"
+       [[ -f "${config}" ]] || fail "Cannot find kernel config in /proc or /boot"
+
+       # gzip -dcfq automatically handles both compressed and plaintext input.
+       # See man 1 gzip under '-f'.
+       local pg_table_levels=$(gzip -dcfq "${config}" | grep PGTABLE_LEVELS | cut -d'=' -f 2)
+
+       if [[ "${pg_table_levels}" -lt 5 ]]; then
+               echo "$0: PGTABLE_LEVELS=${pg_table_levels}, must be >= 5 to run this test"
+               exit $ksft_skip
+       fi
+}
+
+check_test_requirements()
+{
+       # The test supports x86_64 and powerpc64. We currently have no useful
+       # eligibility check for powerpc64, and the test itself will reject other
+       # architectures.
+       case `uname -m` in
+               "x86_64")
+                       check_supported_x86_64
+               ;;
+               *)
+                       return 0
+               ;;
+       esac
+}
+
+check_test_requirements
+./va_high_addr_switch