Files
linux/tools/testing/selftests/mm/va_high_addr_switch.c
Bala-Vignesh-Reddy e6fbd1759c selftests: complete kselftest include centralization
This follow-up patch completes centralization of kselftest.h and
ksefltest_harness.h includes in remaining seltests files, replacing all
relative paths with a non-relative paths using shared -I include path in
lib.mk

Tested with gcc-13.3 and clang-18.1, and cross-compiled successfully on
riscv, arm64, x86_64 and powerpc arch.

[reddybalavignesh9979@gmail.com: add selftests include path for kselftest.h]
  Link: https://lkml.kernel.org/r/20251017090201.317521-1-reddybalavignesh9979@gmail.com
Link: https://lkml.kernel.org/r/20251016104409.68985-1-reddybalavignesh9979@gmail.com
Signed-off-by: Bala-Vignesh-Reddy <reddybalavignesh9979@gmail.com>
Suggested-by: Andrew Morton <akpm@linux-foundation.org>
Link: https://lore.kernel.org/lkml/20250820143954.33d95635e504e94df01930d0@linux-foundation.org/
Reviewed-by: Wei Yang <richard.weiyang@gmail.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Günther Noack <gnoack@google.com>
Cc: Jakub Kacinski <kuba@kernel.org>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mickael Salaun <mic@digikod.net>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2025-11-27 14:24:31 -08:00

337 lines
8.1 KiB
C

// 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 "vm_util.h"
#include "kselftest.h"
/*
* The hint addr value is used to allocate addresses
* beyond the high address switch boundary.
*/
#define ADDR_MARK_128TB (1UL << 47)
#define ADDR_MARK_256TB (1UL << 48)
#define HIGH_ADDR_128TB (1UL << 48)
#define HIGH_ADDR_256TB (1UL << 49)
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;
static struct testcase *hugetlb_testcases;
static int sz_testcases, sz_hugetlb_testcases;
static unsigned long switch_hint;
/* Initialize testcases inside a function to compute parameters at runtime */
void testcases_init(void)
{
unsigned long pagesize = getpagesize();
unsigned long hugepagesize = default_huge_page_size();
unsigned long low_addr = (1UL << 30);
unsigned long addr_switch_hint = ADDR_MARK_128TB;
unsigned long high_addr = HIGH_ADDR_128TB;
#ifdef __aarch64__
/* Post LPA2, the lower userspace VA on a 16K pagesize is 47 bits. */
if (pagesize != (16UL << 10)) {
addr_switch_hint = ADDR_MARK_256TB;
high_addr = HIGH_ADDR_256TB;
}
#endif
struct testcase t[] = {
{
/*
* If stack is moved, we could possibly allocate
* this at the requested address.
*/
.addr = ((void *)(addr_switch_hint - pagesize)),
.size = pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint - pagesize, pagesize)",
.low_addr_required = 1,
},
{
/*
* Unless MAP_FIXED is specified, allocation based on hint
* addr is never at requested address or above it, which is
* beyond high address switch boundary in this case. Instead,
* a suitable allocation is found in lower address space.
*/
.addr = ((void *)(addr_switch_hint - pagesize)),
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint - pagesize, (2 * pagesize))",
.low_addr_required = 1,
},
{
/*
* Exact mapping at high address switch boundary, should
* be obtained even without MAP_FIXED as area is free.
*/
.addr = ((void *)(addr_switch_hint)),
.size = pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint, pagesize)",
.keep_mapped = 1,
},
{
.addr = (void *)(addr_switch_hint),
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
.msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)",
},
{
.addr = NULL,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(NULL)",
.low_addr_required = 1,
},
{
.addr = (void *)low_addr,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(low_addr)",
.low_addr_required = 1,
},
{
.addr = (void *)high_addr,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(high_addr)",
.keep_mapped = 1,
},
{
.addr = (void *)high_addr,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(high_addr) again",
.keep_mapped = 1,
},
{
.addr = (void *)high_addr,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
.msg = "mmap(high_addr, MAP_FIXED)",
},
{
.addr = (void *) -1,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(-1)",
.keep_mapped = 1,
},
{
.addr = (void *) -1,
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(-1) again",
},
{
.addr = ((void *)(addr_switch_hint - pagesize)),
.size = pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint - pagesize, pagesize)",
.low_addr_required = 1,
},
{
.addr = (void *)(addr_switch_hint - pagesize),
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint - pagesize, 2 * pagesize)",
.low_addr_required = 1,
.keep_mapped = 1,
},
{
.addr = (void *)(addr_switch_hint - pagesize / 2),
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint - pagesize/2 , 2 * pagesize)",
.low_addr_required = 1,
.keep_mapped = 1,
},
{
.addr = ((void *)(addr_switch_hint)),
.size = pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint, pagesize)",
},
{
.addr = (void *)(addr_switch_hint),
.size = 2 * pagesize,
.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
.msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)",
},
};
struct testcase ht[] = {
{
.addr = NULL,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(NULL, MAP_HUGETLB)",
.low_addr_required = 1,
},
{
.addr = (void *)low_addr,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(low_addr, MAP_HUGETLB)",
.low_addr_required = 1,
},
{
.addr = (void *)high_addr,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(high_addr, MAP_HUGETLB)",
.keep_mapped = 1,
},
{
.addr = (void *)high_addr,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(high_addr, MAP_HUGETLB) again",
.keep_mapped = 1,
},
{
.addr = (void *)high_addr,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
.msg = "mmap(high_addr, MAP_FIXED | MAP_HUGETLB)",
},
{
.addr = (void *) -1,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(-1, MAP_HUGETLB)",
.keep_mapped = 1,
},
{
.addr = (void *) -1,
.size = hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(-1, MAP_HUGETLB) again",
},
{
.addr = (void *)(addr_switch_hint - hugepagesize),
.size = 2 * hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
.msg = "mmap(addr_switch_hint - hugepagesize, 2*hugepagesize, MAP_HUGETLB)",
.low_addr_required = 1,
.keep_mapped = 1,
},
{
.addr = (void *)(addr_switch_hint),
.size = 2 * hugepagesize,
.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
.msg = "mmap(addr_switch_hint , 2*hugepagesize, MAP_FIXED | MAP_HUGETLB)",
},
};
testcases = malloc(sizeof(t));
hugetlb_testcases = malloc(sizeof(ht));
/* Copy into global arrays */
memcpy(testcases, t, sizeof(t));
memcpy(hugetlb_testcases, ht, sizeof(ht));
sz_testcases = ARRAY_SIZE(t);
sz_hugetlb_testcases = ARRAY_SIZE(ht);
switch_hint = addr_switch_hint;
}
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 *)(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;
}
#ifdef __aarch64__
/* Check if userspace VA > 48 bits */
static int high_address_present(void)
{
void *ptr = mmap((void *)(1UL << 50), 1, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (ptr == MAP_FAILED)
return 0;
munmap(ptr, 1);
return 1;
}
#endif
static int supported_arch(void)
{
#if defined(__powerpc64__)
return 1;
#elif defined(__x86_64__)
return 1;
#elif defined(__aarch64__)
return high_address_present();
#else
return 0;
#endif
}
int main(int argc, char **argv)
{
int ret;
if (!supported_arch())
return KSFT_SKIP;
testcases_init();
ret = run_test(testcases, sz_testcases);
if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
ret = run_test(hugetlb_testcases, sz_hugetlb_testcases);
return ret;
}