162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2020 Google LLC
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#define _GNU_SOURCE
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <errno.h>
862306a36Sopenharmony_ci#include <stdlib.h>
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <string.h>
1162306a36Sopenharmony_ci#include <sys/mman.h>
1262306a36Sopenharmony_ci#include <time.h>
1362306a36Sopenharmony_ci#include <stdbool.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "../kselftest.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define EXPECT_SUCCESS 0
1862306a36Sopenharmony_ci#define EXPECT_FAILURE 1
1962306a36Sopenharmony_ci#define NON_OVERLAPPING 0
2062306a36Sopenharmony_ci#define OVERLAPPING 1
2162306a36Sopenharmony_ci#define NS_PER_SEC 1000000000ULL
2262306a36Sopenharmony_ci#define VALIDATION_DEFAULT_THRESHOLD 4	/* 4MB */
2362306a36Sopenharmony_ci#define VALIDATION_NO_THRESHOLD 0	/* Verify the entire region */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct config {
2862306a36Sopenharmony_ci	unsigned long long src_alignment;
2962306a36Sopenharmony_ci	unsigned long long dest_alignment;
3062306a36Sopenharmony_ci	unsigned long long region_size;
3162306a36Sopenharmony_ci	int overlapping;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct test {
3562306a36Sopenharmony_ci	const char *name;
3662306a36Sopenharmony_ci	struct config config;
3762306a36Sopenharmony_ci	int expect_failure;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cienum {
4162306a36Sopenharmony_ci	_1KB = 1ULL << 10,	/* 1KB -> not page aligned */
4262306a36Sopenharmony_ci	_4KB = 4ULL << 10,
4362306a36Sopenharmony_ci	_8KB = 8ULL << 10,
4462306a36Sopenharmony_ci	_1MB = 1ULL << 20,
4562306a36Sopenharmony_ci	_2MB = 2ULL << 20,
4662306a36Sopenharmony_ci	_4MB = 4ULL << 20,
4762306a36Sopenharmony_ci	_1GB = 1ULL << 30,
4862306a36Sopenharmony_ci	_2GB = 2ULL << 30,
4962306a36Sopenharmony_ci	PMD = _2MB,
5062306a36Sopenharmony_ci	PUD = _1GB,
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define PTE page_size
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define MAKE_TEST(source_align, destination_align, size,	\
5662306a36Sopenharmony_ci		  overlaps, should_fail, test_name)		\
5762306a36Sopenharmony_ci(struct test){							\
5862306a36Sopenharmony_ci	.name = test_name,					\
5962306a36Sopenharmony_ci	.config = {						\
6062306a36Sopenharmony_ci		.src_alignment = source_align,			\
6162306a36Sopenharmony_ci		.dest_alignment = destination_align,		\
6262306a36Sopenharmony_ci		.region_size = size,				\
6362306a36Sopenharmony_ci		.overlapping = overlaps,			\
6462306a36Sopenharmony_ci	},							\
6562306a36Sopenharmony_ci	.expect_failure = should_fail				\
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * Returns false if the requested remap region overlaps with an
7062306a36Sopenharmony_ci * existing mapping (e.g text, stack) else returns true.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic bool is_remap_region_valid(void *addr, unsigned long long size)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	void *remap_addr = NULL;
7562306a36Sopenharmony_ci	bool ret = true;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Use MAP_FIXED_NOREPLACE flag to ensure region is not mapped */
7862306a36Sopenharmony_ci	remap_addr = mmap(addr, size, PROT_READ | PROT_WRITE,
7962306a36Sopenharmony_ci					 MAP_FIXED_NOREPLACE | MAP_ANONYMOUS | MAP_SHARED,
8062306a36Sopenharmony_ci					 -1, 0);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (remap_addr == MAP_FAILED) {
8362306a36Sopenharmony_ci		if (errno == EEXIST)
8462306a36Sopenharmony_ci			ret = false;
8562306a36Sopenharmony_ci	} else {
8662306a36Sopenharmony_ci		munmap(remap_addr, size);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return ret;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Returns mmap_min_addr sysctl tunable from procfs */
9362306a36Sopenharmony_cistatic unsigned long long get_mmap_min_addr(void)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	FILE *fp;
9662306a36Sopenharmony_ci	int n_matched;
9762306a36Sopenharmony_ci	static unsigned long long addr;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (addr)
10062306a36Sopenharmony_ci		return addr;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	fp = fopen("/proc/sys/vm/mmap_min_addr", "r");
10362306a36Sopenharmony_ci	if (fp == NULL) {
10462306a36Sopenharmony_ci		ksft_print_msg("Failed to open /proc/sys/vm/mmap_min_addr: %s\n",
10562306a36Sopenharmony_ci			strerror(errno));
10662306a36Sopenharmony_ci		exit(KSFT_SKIP);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	n_matched = fscanf(fp, "%llu", &addr);
11062306a36Sopenharmony_ci	if (n_matched != 1) {
11162306a36Sopenharmony_ci		ksft_print_msg("Failed to read /proc/sys/vm/mmap_min_addr: %s\n",
11262306a36Sopenharmony_ci			strerror(errno));
11362306a36Sopenharmony_ci		fclose(fp);
11462306a36Sopenharmony_ci		exit(KSFT_SKIP);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	fclose(fp);
11862306a36Sopenharmony_ci	return addr;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * Using /proc/self/maps, assert that the specified address range is contained
12362306a36Sopenharmony_ci * within a single mapping.
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistatic bool is_range_mapped(FILE *maps_fp, void *start, void *end)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	char *line = NULL;
12862306a36Sopenharmony_ci	size_t len = 0;
12962306a36Sopenharmony_ci	bool success = false;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	rewind(maps_fp);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	while (getline(&line, &len, maps_fp) != -1) {
13462306a36Sopenharmony_ci		char *first = strtok(line, "- ");
13562306a36Sopenharmony_ci		void *first_val = (void *)strtol(first, NULL, 16);
13662306a36Sopenharmony_ci		char *second = strtok(NULL, "- ");
13762306a36Sopenharmony_ci		void *second_val = (void *) strtol(second, NULL, 16);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		if (first_val <= start && second_val >= end) {
14062306a36Sopenharmony_ci			success = true;
14162306a36Sopenharmony_ci			break;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return success;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci * This test validates that merge is called when expanding a mapping.
15062306a36Sopenharmony_ci * Mapping containing three pages is created, middle page is unmapped
15162306a36Sopenharmony_ci * and then the mapping containing the first page is expanded so that
15262306a36Sopenharmony_ci * it fills the created hole. The two parts should merge creating
15362306a36Sopenharmony_ci * single mapping with three pages.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic void mremap_expand_merge(FILE *maps_fp, unsigned long page_size)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	char *test_name = "mremap expand merge";
15862306a36Sopenharmony_ci	bool success = false;
15962306a36Sopenharmony_ci	char *remap, *start;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
16262306a36Sopenharmony_ci		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (start == MAP_FAILED) {
16562306a36Sopenharmony_ci		ksft_print_msg("mmap failed: %s\n", strerror(errno));
16662306a36Sopenharmony_ci		goto out;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	munmap(start + page_size, page_size);
17062306a36Sopenharmony_ci	remap = mremap(start, page_size, 2 * page_size, 0);
17162306a36Sopenharmony_ci	if (remap == MAP_FAILED) {
17262306a36Sopenharmony_ci		ksft_print_msg("mremap failed: %s\n", strerror(errno));
17362306a36Sopenharmony_ci		munmap(start, page_size);
17462306a36Sopenharmony_ci		munmap(start + 2 * page_size, page_size);
17562306a36Sopenharmony_ci		goto out;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	success = is_range_mapped(maps_fp, start, start + 3 * page_size);
17962306a36Sopenharmony_ci	munmap(start, 3 * page_size);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciout:
18262306a36Sopenharmony_ci	if (success)
18362306a36Sopenharmony_ci		ksft_test_result_pass("%s\n", test_name);
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		ksft_test_result_fail("%s\n", test_name);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Similar to mremap_expand_merge() except instead of removing the middle page,
19062306a36Sopenharmony_ci * we remove the last then attempt to remap offset from the second page. This
19162306a36Sopenharmony_ci * should result in the mapping being restored to its former state.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic void mremap_expand_merge_offset(FILE *maps_fp, unsigned long page_size)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	char *test_name = "mremap expand merge offset";
19762306a36Sopenharmony_ci	bool success = false;
19862306a36Sopenharmony_ci	char *remap, *start;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	start = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
20162306a36Sopenharmony_ci		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (start == MAP_FAILED) {
20462306a36Sopenharmony_ci		ksft_print_msg("mmap failed: %s\n", strerror(errno));
20562306a36Sopenharmony_ci		goto out;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Unmap final page to ensure we have space to expand. */
20962306a36Sopenharmony_ci	munmap(start + 2 * page_size, page_size);
21062306a36Sopenharmony_ci	remap = mremap(start + page_size, page_size, 2 * page_size, 0);
21162306a36Sopenharmony_ci	if (remap == MAP_FAILED) {
21262306a36Sopenharmony_ci		ksft_print_msg("mremap failed: %s\n", strerror(errno));
21362306a36Sopenharmony_ci		munmap(start, 2 * page_size);
21462306a36Sopenharmony_ci		goto out;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	success = is_range_mapped(maps_fp, start, start + 3 * page_size);
21862306a36Sopenharmony_ci	munmap(start, 3 * page_size);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciout:
22162306a36Sopenharmony_ci	if (success)
22262306a36Sopenharmony_ci		ksft_test_result_pass("%s\n", test_name);
22362306a36Sopenharmony_ci	else
22462306a36Sopenharmony_ci		ksft_test_result_fail("%s\n", test_name);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * Returns the start address of the mapping on success, else returns
22962306a36Sopenharmony_ci * NULL on failure.
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_cistatic void *get_source_mapping(struct config c)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	unsigned long long addr = 0ULL;
23462306a36Sopenharmony_ci	void *src_addr = NULL;
23562306a36Sopenharmony_ci	unsigned long long mmap_min_addr;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	mmap_min_addr = get_mmap_min_addr();
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ciretry:
24062306a36Sopenharmony_ci	addr += c.src_alignment;
24162306a36Sopenharmony_ci	if (addr < mmap_min_addr)
24262306a36Sopenharmony_ci		goto retry;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	src_addr = mmap((void *) addr, c.region_size, PROT_READ | PROT_WRITE,
24562306a36Sopenharmony_ci					MAP_FIXED_NOREPLACE | MAP_ANONYMOUS | MAP_SHARED,
24662306a36Sopenharmony_ci					-1, 0);
24762306a36Sopenharmony_ci	if (src_addr == MAP_FAILED) {
24862306a36Sopenharmony_ci		if (errno == EPERM || errno == EEXIST)
24962306a36Sopenharmony_ci			goto retry;
25062306a36Sopenharmony_ci		goto error;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	/*
25362306a36Sopenharmony_ci	 * Check that the address is aligned to the specified alignment.
25462306a36Sopenharmony_ci	 * Addresses which have alignments that are multiples of that
25562306a36Sopenharmony_ci	 * specified are not considered valid. For instance, 1GB address is
25662306a36Sopenharmony_ci	 * 2MB-aligned, however it will not be considered valid for a
25762306a36Sopenharmony_ci	 * requested alignment of 2MB. This is done to reduce coincidental
25862306a36Sopenharmony_ci	 * alignment in the tests.
25962306a36Sopenharmony_ci	 */
26062306a36Sopenharmony_ci	if (((unsigned long long) src_addr & (c.src_alignment - 1)) ||
26162306a36Sopenharmony_ci			!((unsigned long long) src_addr & c.src_alignment)) {
26262306a36Sopenharmony_ci		munmap(src_addr, c.region_size);
26362306a36Sopenharmony_ci		goto retry;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (!src_addr)
26762306a36Sopenharmony_ci		goto error;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return src_addr;
27062306a36Sopenharmony_cierror:
27162306a36Sopenharmony_ci	ksft_print_msg("Failed to map source region: %s\n",
27262306a36Sopenharmony_ci			strerror(errno));
27362306a36Sopenharmony_ci	return NULL;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/* Returns the time taken for the remap on success else returns -1. */
27762306a36Sopenharmony_cistatic long long remap_region(struct config c, unsigned int threshold_mb,
27862306a36Sopenharmony_ci			      char pattern_seed)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	void *addr, *src_addr, *dest_addr;
28162306a36Sopenharmony_ci	unsigned long long i;
28262306a36Sopenharmony_ci	struct timespec t_start = {0, 0}, t_end = {0, 0};
28362306a36Sopenharmony_ci	long long  start_ns, end_ns, align_mask, ret, offset;
28462306a36Sopenharmony_ci	unsigned long long threshold;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (threshold_mb == VALIDATION_NO_THRESHOLD)
28762306a36Sopenharmony_ci		threshold = c.region_size;
28862306a36Sopenharmony_ci	else
28962306a36Sopenharmony_ci		threshold = MIN(threshold_mb * _1MB, c.region_size);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	src_addr = get_source_mapping(c);
29262306a36Sopenharmony_ci	if (!src_addr) {
29362306a36Sopenharmony_ci		ret = -1;
29462306a36Sopenharmony_ci		goto out;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Set byte pattern */
29862306a36Sopenharmony_ci	srand(pattern_seed);
29962306a36Sopenharmony_ci	for (i = 0; i < threshold; i++)
30062306a36Sopenharmony_ci		memset((char *) src_addr + i, (char) rand(), 1);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Mask to zero out lower bits of address for alignment */
30362306a36Sopenharmony_ci	align_mask = ~(c.dest_alignment - 1);
30462306a36Sopenharmony_ci	/* Offset of destination address from the end of the source region */
30562306a36Sopenharmony_ci	offset = (c.overlapping) ? -c.dest_alignment : c.dest_alignment;
30662306a36Sopenharmony_ci	addr = (void *) (((unsigned long long) src_addr + c.region_size
30762306a36Sopenharmony_ci			  + offset) & align_mask);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* See comment in get_source_mapping() */
31062306a36Sopenharmony_ci	if (!((unsigned long long) addr & c.dest_alignment))
31162306a36Sopenharmony_ci		addr = (void *) ((unsigned long long) addr | c.dest_alignment);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* Don't destroy existing mappings unless expected to overlap */
31462306a36Sopenharmony_ci	while (!is_remap_region_valid(addr, c.region_size) && !c.overlapping) {
31562306a36Sopenharmony_ci		/* Check for unsigned overflow */
31662306a36Sopenharmony_ci		if (addr + c.dest_alignment < addr) {
31762306a36Sopenharmony_ci			ksft_print_msg("Couldn't find a valid region to remap to\n");
31862306a36Sopenharmony_ci			ret = -1;
31962306a36Sopenharmony_ci			goto out;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci		addr += c.dest_alignment;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &t_start);
32562306a36Sopenharmony_ci	dest_addr = mremap(src_addr, c.region_size, c.region_size,
32662306a36Sopenharmony_ci					  MREMAP_MAYMOVE|MREMAP_FIXED, (char *) addr);
32762306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &t_end);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (dest_addr == MAP_FAILED) {
33062306a36Sopenharmony_ci		ksft_print_msg("mremap failed: %s\n", strerror(errno));
33162306a36Sopenharmony_ci		ret = -1;
33262306a36Sopenharmony_ci		goto clean_up_src;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Verify byte pattern after remapping */
33662306a36Sopenharmony_ci	srand(pattern_seed);
33762306a36Sopenharmony_ci	for (i = 0; i < threshold; i++) {
33862306a36Sopenharmony_ci		char c = (char) rand();
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		if (((char *) dest_addr)[i] != c) {
34162306a36Sopenharmony_ci			ksft_print_msg("Data after remap doesn't match at offset %d\n",
34262306a36Sopenharmony_ci				       i);
34362306a36Sopenharmony_ci			ksft_print_msg("Expected: %#x\t Got: %#x\n", c & 0xff,
34462306a36Sopenharmony_ci					((char *) dest_addr)[i] & 0xff);
34562306a36Sopenharmony_ci			ret = -1;
34662306a36Sopenharmony_ci			goto clean_up_dest;
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	start_ns = t_start.tv_sec * NS_PER_SEC + t_start.tv_nsec;
35162306a36Sopenharmony_ci	end_ns = t_end.tv_sec * NS_PER_SEC + t_end.tv_nsec;
35262306a36Sopenharmony_ci	ret = end_ns - start_ns;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/*
35562306a36Sopenharmony_ci * Since the destination address is specified using MREMAP_FIXED, subsequent
35662306a36Sopenharmony_ci * mremap will unmap any previous mapping at the address range specified by
35762306a36Sopenharmony_ci * dest_addr and region_size. This significantly affects the remap time of
35862306a36Sopenharmony_ci * subsequent tests. So we clean up mappings after each test.
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_ciclean_up_dest:
36162306a36Sopenharmony_ci	munmap(dest_addr, c.region_size);
36262306a36Sopenharmony_ciclean_up_src:
36362306a36Sopenharmony_ci	munmap(src_addr, c.region_size);
36462306a36Sopenharmony_ciout:
36562306a36Sopenharmony_ci	return ret;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void run_mremap_test_case(struct test test_case, int *failures,
36962306a36Sopenharmony_ci				 unsigned int threshold_mb,
37062306a36Sopenharmony_ci				 unsigned int pattern_seed)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	long long remap_time = remap_region(test_case.config, threshold_mb,
37362306a36Sopenharmony_ci					    pattern_seed);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (remap_time < 0) {
37662306a36Sopenharmony_ci		if (test_case.expect_failure)
37762306a36Sopenharmony_ci			ksft_test_result_xfail("%s\n\tExpected mremap failure\n",
37862306a36Sopenharmony_ci					      test_case.name);
37962306a36Sopenharmony_ci		else {
38062306a36Sopenharmony_ci			ksft_test_result_fail("%s\n", test_case.name);
38162306a36Sopenharmony_ci			*failures += 1;
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci	} else {
38462306a36Sopenharmony_ci		/*
38562306a36Sopenharmony_ci		 * Comparing mremap time is only applicable if entire region
38662306a36Sopenharmony_ci		 * was faulted in.
38762306a36Sopenharmony_ci		 */
38862306a36Sopenharmony_ci		if (threshold_mb == VALIDATION_NO_THRESHOLD ||
38962306a36Sopenharmony_ci		    test_case.config.region_size <= threshold_mb * _1MB)
39062306a36Sopenharmony_ci			ksft_test_result_pass("%s\n\tmremap time: %12lldns\n",
39162306a36Sopenharmony_ci					      test_case.name, remap_time);
39262306a36Sopenharmony_ci		else
39362306a36Sopenharmony_ci			ksft_test_result_pass("%s\n", test_case.name);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic void usage(const char *cmd)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	fprintf(stderr,
40062306a36Sopenharmony_ci		"Usage: %s [[-t <threshold_mb>] [-p <pattern_seed>]]\n"
40162306a36Sopenharmony_ci		"-t\t only validate threshold_mb of the remapped region\n"
40262306a36Sopenharmony_ci		"  \t if 0 is supplied no threshold is used; all tests\n"
40362306a36Sopenharmony_ci		"  \t are run and remapped regions validated fully.\n"
40462306a36Sopenharmony_ci		"  \t The default threshold used is 4MB.\n"
40562306a36Sopenharmony_ci		"-p\t provide a seed to generate the random pattern for\n"
40662306a36Sopenharmony_ci		"  \t validating the remapped region.\n", cmd);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int parse_args(int argc, char **argv, unsigned int *threshold_mb,
41062306a36Sopenharmony_ci		      unsigned int *pattern_seed)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	const char *optstr = "t:p:";
41362306a36Sopenharmony_ci	int opt;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, optstr)) != -1) {
41662306a36Sopenharmony_ci		switch (opt) {
41762306a36Sopenharmony_ci		case 't':
41862306a36Sopenharmony_ci			*threshold_mb = atoi(optarg);
41962306a36Sopenharmony_ci			break;
42062306a36Sopenharmony_ci		case 'p':
42162306a36Sopenharmony_ci			*pattern_seed = atoi(optarg);
42262306a36Sopenharmony_ci			break;
42362306a36Sopenharmony_ci		default:
42462306a36Sopenharmony_ci			usage(argv[0]);
42562306a36Sopenharmony_ci			return -1;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (optind < argc) {
43062306a36Sopenharmony_ci		usage(argv[0]);
43162306a36Sopenharmony_ci		return -1;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci#define MAX_TEST 13
43862306a36Sopenharmony_ci#define MAX_PERF_TEST 3
43962306a36Sopenharmony_ciint main(int argc, char **argv)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	int failures = 0;
44262306a36Sopenharmony_ci	int i, run_perf_tests;
44362306a36Sopenharmony_ci	unsigned int threshold_mb = VALIDATION_DEFAULT_THRESHOLD;
44462306a36Sopenharmony_ci	unsigned int pattern_seed;
44562306a36Sopenharmony_ci	int num_expand_tests = 2;
44662306a36Sopenharmony_ci	struct test test_cases[MAX_TEST];
44762306a36Sopenharmony_ci	struct test perf_test_cases[MAX_PERF_TEST];
44862306a36Sopenharmony_ci	int page_size;
44962306a36Sopenharmony_ci	time_t t;
45062306a36Sopenharmony_ci	FILE *maps_fp;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	pattern_seed = (unsigned int) time(&t);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (parse_args(argc, argv, &threshold_mb, &pattern_seed) < 0)
45562306a36Sopenharmony_ci		exit(EXIT_FAILURE);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	ksft_print_msg("Test configs:\n\tthreshold_mb=%u\n\tpattern_seed=%u\n\n",
45862306a36Sopenharmony_ci		       threshold_mb, pattern_seed);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	page_size = sysconf(_SC_PAGESIZE);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Expected mremap failures */
46362306a36Sopenharmony_ci	test_cases[0] =	MAKE_TEST(page_size, page_size, page_size,
46462306a36Sopenharmony_ci				  OVERLAPPING, EXPECT_FAILURE,
46562306a36Sopenharmony_ci				  "mremap - Source and Destination Regions Overlapping");
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	test_cases[1] = MAKE_TEST(page_size, page_size/4, page_size,
46862306a36Sopenharmony_ci				  NON_OVERLAPPING, EXPECT_FAILURE,
46962306a36Sopenharmony_ci				  "mremap - Destination Address Misaligned (1KB-aligned)");
47062306a36Sopenharmony_ci	test_cases[2] = MAKE_TEST(page_size/4, page_size, page_size,
47162306a36Sopenharmony_ci				  NON_OVERLAPPING, EXPECT_FAILURE,
47262306a36Sopenharmony_ci				  "mremap - Source Address Misaligned (1KB-aligned)");
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Src addr PTE aligned */
47562306a36Sopenharmony_ci	test_cases[3] = MAKE_TEST(PTE, PTE, PTE * 2,
47662306a36Sopenharmony_ci				  NON_OVERLAPPING, EXPECT_SUCCESS,
47762306a36Sopenharmony_ci				  "8KB mremap - Source PTE-aligned, Destination PTE-aligned");
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Src addr 1MB aligned */
48062306a36Sopenharmony_ci	test_cases[4] = MAKE_TEST(_1MB, PTE, _2MB, NON_OVERLAPPING, EXPECT_SUCCESS,
48162306a36Sopenharmony_ci				  "2MB mremap - Source 1MB-aligned, Destination PTE-aligned");
48262306a36Sopenharmony_ci	test_cases[5] = MAKE_TEST(_1MB, _1MB, _2MB, NON_OVERLAPPING, EXPECT_SUCCESS,
48362306a36Sopenharmony_ci				  "2MB mremap - Source 1MB-aligned, Destination 1MB-aligned");
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Src addr PMD aligned */
48662306a36Sopenharmony_ci	test_cases[6] = MAKE_TEST(PMD, PTE, _4MB, NON_OVERLAPPING, EXPECT_SUCCESS,
48762306a36Sopenharmony_ci				  "4MB mremap - Source PMD-aligned, Destination PTE-aligned");
48862306a36Sopenharmony_ci	test_cases[7] =	MAKE_TEST(PMD, _1MB, _4MB, NON_OVERLAPPING, EXPECT_SUCCESS,
48962306a36Sopenharmony_ci				  "4MB mremap - Source PMD-aligned, Destination 1MB-aligned");
49062306a36Sopenharmony_ci	test_cases[8] = MAKE_TEST(PMD, PMD, _4MB, NON_OVERLAPPING, EXPECT_SUCCESS,
49162306a36Sopenharmony_ci				  "4MB mremap - Source PMD-aligned, Destination PMD-aligned");
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Src addr PUD aligned */
49462306a36Sopenharmony_ci	test_cases[9] = MAKE_TEST(PUD, PTE, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
49562306a36Sopenharmony_ci				  "2GB mremap - Source PUD-aligned, Destination PTE-aligned");
49662306a36Sopenharmony_ci	test_cases[10] = MAKE_TEST(PUD, _1MB, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
49762306a36Sopenharmony_ci				   "2GB mremap - Source PUD-aligned, Destination 1MB-aligned");
49862306a36Sopenharmony_ci	test_cases[11] = MAKE_TEST(PUD, PMD, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
49962306a36Sopenharmony_ci				   "2GB mremap - Source PUD-aligned, Destination PMD-aligned");
50062306a36Sopenharmony_ci	test_cases[12] = MAKE_TEST(PUD, PUD, _2GB, NON_OVERLAPPING, EXPECT_SUCCESS,
50162306a36Sopenharmony_ci				   "2GB mremap - Source PUD-aligned, Destination PUD-aligned");
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	perf_test_cases[0] =  MAKE_TEST(page_size, page_size, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
50462306a36Sopenharmony_ci					"1GB mremap - Source PTE-aligned, Destination PTE-aligned");
50562306a36Sopenharmony_ci	/*
50662306a36Sopenharmony_ci	 * mremap 1GB region - Page table level aligned time
50762306a36Sopenharmony_ci	 * comparison.
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	perf_test_cases[1] = MAKE_TEST(PMD, PMD, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
51062306a36Sopenharmony_ci				       "1GB mremap - Source PMD-aligned, Destination PMD-aligned");
51162306a36Sopenharmony_ci	perf_test_cases[2] = MAKE_TEST(PUD, PUD, _1GB, NON_OVERLAPPING, EXPECT_SUCCESS,
51262306a36Sopenharmony_ci				       "1GB mremap - Source PUD-aligned, Destination PUD-aligned");
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	run_perf_tests =  (threshold_mb == VALIDATION_NO_THRESHOLD) ||
51562306a36Sopenharmony_ci				(threshold_mb * _1MB >= _1GB);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ksft_set_plan(ARRAY_SIZE(test_cases) + (run_perf_tests ?
51862306a36Sopenharmony_ci		      ARRAY_SIZE(perf_test_cases) : 0) + num_expand_tests);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(test_cases); i++)
52162306a36Sopenharmony_ci		run_mremap_test_case(test_cases[i], &failures, threshold_mb,
52262306a36Sopenharmony_ci				     pattern_seed);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	maps_fp = fopen("/proc/self/maps", "r");
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (maps_fp == NULL) {
52762306a36Sopenharmony_ci		ksft_print_msg("Failed to read /proc/self/maps: %s\n", strerror(errno));
52862306a36Sopenharmony_ci		exit(KSFT_FAIL);
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mremap_expand_merge(maps_fp, page_size);
53262306a36Sopenharmony_ci	mremap_expand_merge_offset(maps_fp, page_size);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	fclose(maps_fp);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (run_perf_tests) {
53762306a36Sopenharmony_ci		ksft_print_msg("\n%s\n",
53862306a36Sopenharmony_ci		 "mremap HAVE_MOVE_PMD/PUD optimization time comparison for 1GB region:");
53962306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(perf_test_cases); i++)
54062306a36Sopenharmony_ci			run_mremap_test_case(perf_test_cases[i], &failures,
54162306a36Sopenharmony_ci					     threshold_mb, pattern_seed);
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (failures > 0)
54562306a36Sopenharmony_ci		ksft_exit_fail();
54662306a36Sopenharmony_ci	else
54762306a36Sopenharmony_ci		ksft_exit_pass();
54862306a36Sopenharmony_ci}
549