162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * A memslot-related performance benchmark.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2021 Oracle and/or its affiliates.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Basic guest setup / host vCPU thread code lifted from set_memory_region_test.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <pthread.h>
1062306a36Sopenharmony_ci#include <sched.h>
1162306a36Sopenharmony_ci#include <semaphore.h>
1262306a36Sopenharmony_ci#include <stdatomic.h>
1362306a36Sopenharmony_ci#include <stdbool.h>
1462306a36Sopenharmony_ci#include <stdint.h>
1562306a36Sopenharmony_ci#include <stdio.h>
1662306a36Sopenharmony_ci#include <stdlib.h>
1762306a36Sopenharmony_ci#include <string.h>
1862306a36Sopenharmony_ci#include <sys/mman.h>
1962306a36Sopenharmony_ci#include <time.h>
2062306a36Sopenharmony_ci#include <unistd.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/compiler.h>
2362306a36Sopenharmony_ci#include <linux/sizes.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <test_util.h>
2662306a36Sopenharmony_ci#include <kvm_util.h>
2762306a36Sopenharmony_ci#include <processor.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define MEM_EXTRA_SIZE		SZ_64K
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MEM_SIZE		(SZ_512M + MEM_EXTRA_SIZE)
3262306a36Sopenharmony_ci#define MEM_GPA			SZ_256M
3362306a36Sopenharmony_ci#define MEM_AUX_GPA		MEM_GPA
3462306a36Sopenharmony_ci#define MEM_SYNC_GPA		MEM_AUX_GPA
3562306a36Sopenharmony_ci#define MEM_TEST_GPA		(MEM_AUX_GPA + MEM_EXTRA_SIZE)
3662306a36Sopenharmony_ci#define MEM_TEST_SIZE		(MEM_SIZE - MEM_EXTRA_SIZE)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * 32 MiB is max size that gets well over 100 iterations on 509 slots.
4062306a36Sopenharmony_ci * Considering that each slot needs to have at least one page up to
4162306a36Sopenharmony_ci * 8194 slots in use can then be tested (although with slightly
4262306a36Sopenharmony_ci * limited resolution).
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci#define MEM_SIZE_MAP		(SZ_32M + MEM_EXTRA_SIZE)
4562306a36Sopenharmony_ci#define MEM_TEST_MAP_SIZE	(MEM_SIZE_MAP - MEM_EXTRA_SIZE)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * 128 MiB is min size that fills 32k slots with at least one page in each
4962306a36Sopenharmony_ci * while at the same time gets 100+ iterations in such test
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * 2 MiB chunk size like a typical huge page
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#define MEM_TEST_UNMAP_SIZE		SZ_128M
5462306a36Sopenharmony_ci#define MEM_TEST_UNMAP_CHUNK_SIZE	SZ_2M
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * For the move active test the middle of the test area is placed on
5862306a36Sopenharmony_ci * a memslot boundary: half lies in the memslot being moved, half in
5962306a36Sopenharmony_ci * other memslot(s).
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * We have different number of memory slots, excluding the reserved
6262306a36Sopenharmony_ci * memory slot 0, on various architectures and configurations. The
6362306a36Sopenharmony_ci * memory size in this test is calculated by picking the maximal
6462306a36Sopenharmony_ci * last memory slot's memory size, with alignment to the largest
6562306a36Sopenharmony_ci * supported page size (64KB). In this way, the selected memory
6662306a36Sopenharmony_ci * size for this test is compatible with test_memslot_move_prepare().
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * architecture   slots    memory-per-slot    memory-on-last-slot
6962306a36Sopenharmony_ci * --------------------------------------------------------------
7062306a36Sopenharmony_ci * x86-4KB        32763    16KB               160KB
7162306a36Sopenharmony_ci * arm64-4KB      32766    16KB               112KB
7262306a36Sopenharmony_ci * arm64-16KB     32766    16KB               112KB
7362306a36Sopenharmony_ci * arm64-64KB     8192     64KB               128KB
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci#define MEM_TEST_MOVE_SIZE		(3 * SZ_64K)
7662306a36Sopenharmony_ci#define MEM_TEST_MOVE_GPA_DEST		(MEM_GPA + MEM_SIZE)
7762306a36Sopenharmony_cistatic_assert(MEM_TEST_MOVE_SIZE <= MEM_TEST_SIZE,
7862306a36Sopenharmony_ci	      "invalid move test region size");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define MEM_TEST_VAL_1 0x1122334455667788
8162306a36Sopenharmony_ci#define MEM_TEST_VAL_2 0x99AABBCCDDEEFF00
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct vm_data {
8462306a36Sopenharmony_ci	struct kvm_vm *vm;
8562306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
8662306a36Sopenharmony_ci	pthread_t vcpu_thread;
8762306a36Sopenharmony_ci	uint32_t nslots;
8862306a36Sopenharmony_ci	uint64_t npages;
8962306a36Sopenharmony_ci	uint64_t pages_per_slot;
9062306a36Sopenharmony_ci	void **hva_slots;
9162306a36Sopenharmony_ci	bool mmio_ok;
9262306a36Sopenharmony_ci	uint64_t mmio_gpa_min;
9362306a36Sopenharmony_ci	uint64_t mmio_gpa_max;
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct sync_area {
9762306a36Sopenharmony_ci	uint32_t    guest_page_size;
9862306a36Sopenharmony_ci	atomic_bool start_flag;
9962306a36Sopenharmony_ci	atomic_bool exit_flag;
10062306a36Sopenharmony_ci	atomic_bool sync_flag;
10162306a36Sopenharmony_ci	void *move_area_ptr;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * Technically, we need also for the atomic bool to be address-free, which
10662306a36Sopenharmony_ci * is recommended, but not strictly required, by C11 for lockless
10762306a36Sopenharmony_ci * implementations.
10862306a36Sopenharmony_ci * However, in practice both GCC and Clang fulfill this requirement on
10962306a36Sopenharmony_ci * all KVM-supported platforms.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic_assert(ATOMIC_BOOL_LOCK_FREE == 2, "atomic bool is not lockless");
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic sem_t vcpu_ready;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic bool map_unmap_verify;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic bool verbose;
11862306a36Sopenharmony_ci#define pr_info_v(...)				\
11962306a36Sopenharmony_ci	do {					\
12062306a36Sopenharmony_ci		if (verbose)			\
12162306a36Sopenharmony_ci			pr_info(__VA_ARGS__);	\
12262306a36Sopenharmony_ci	} while (0)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void check_mmio_access(struct vm_data *data, struct kvm_run *run)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	TEST_ASSERT(data->mmio_ok, "Unexpected mmio exit");
12762306a36Sopenharmony_ci	TEST_ASSERT(run->mmio.is_write, "Unexpected mmio read");
12862306a36Sopenharmony_ci	TEST_ASSERT(run->mmio.len == 8,
12962306a36Sopenharmony_ci		    "Unexpected exit mmio size = %u", run->mmio.len);
13062306a36Sopenharmony_ci	TEST_ASSERT(run->mmio.phys_addr >= data->mmio_gpa_min &&
13162306a36Sopenharmony_ci		    run->mmio.phys_addr <= data->mmio_gpa_max,
13262306a36Sopenharmony_ci		    "Unexpected exit mmio address = 0x%llx",
13362306a36Sopenharmony_ci		    run->mmio.phys_addr);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void *vcpu_worker(void *__data)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct vm_data *data = __data;
13962306a36Sopenharmony_ci	struct kvm_vcpu *vcpu = data->vcpu;
14062306a36Sopenharmony_ci	struct kvm_run *run = vcpu->run;
14162306a36Sopenharmony_ci	struct ucall uc;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	while (1) {
14462306a36Sopenharmony_ci		vcpu_run(vcpu);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		switch (get_ucall(vcpu, &uc)) {
14762306a36Sopenharmony_ci		case UCALL_SYNC:
14862306a36Sopenharmony_ci			TEST_ASSERT(uc.args[1] == 0,
14962306a36Sopenharmony_ci				"Unexpected sync ucall, got %lx",
15062306a36Sopenharmony_ci				(ulong)uc.args[1]);
15162306a36Sopenharmony_ci			sem_post(&vcpu_ready);
15262306a36Sopenharmony_ci			continue;
15362306a36Sopenharmony_ci		case UCALL_NONE:
15462306a36Sopenharmony_ci			if (run->exit_reason == KVM_EXIT_MMIO)
15562306a36Sopenharmony_ci				check_mmio_access(data, run);
15662306a36Sopenharmony_ci			else
15762306a36Sopenharmony_ci				goto done;
15862306a36Sopenharmony_ci			break;
15962306a36Sopenharmony_ci		case UCALL_ABORT:
16062306a36Sopenharmony_ci			REPORT_GUEST_ASSERT(uc);
16162306a36Sopenharmony_ci			break;
16262306a36Sopenharmony_ci		case UCALL_DONE:
16362306a36Sopenharmony_ci			goto done;
16462306a36Sopenharmony_ci		default:
16562306a36Sopenharmony_ci			TEST_FAIL("Unknown ucall %lu", uc.cmd);
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cidone:
17062306a36Sopenharmony_ci	return NULL;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void wait_for_vcpu(void)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct timespec ts;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	TEST_ASSERT(!clock_gettime(CLOCK_REALTIME, &ts),
17862306a36Sopenharmony_ci		    "clock_gettime() failed: %d\n", errno);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ts.tv_sec += 2;
18162306a36Sopenharmony_ci	TEST_ASSERT(!sem_timedwait(&vcpu_ready, &ts),
18262306a36Sopenharmony_ci		    "sem_timedwait() failed: %d\n", errno);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void *vm_gpa2hva(struct vm_data *data, uint64_t gpa, uint64_t *rempages)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	uint64_t gpage, pgoffs;
18862306a36Sopenharmony_ci	uint32_t slot, slotoffs;
18962306a36Sopenharmony_ci	void *base;
19062306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	TEST_ASSERT(gpa >= MEM_GPA, "Too low gpa to translate");
19362306a36Sopenharmony_ci	TEST_ASSERT(gpa < MEM_GPA + data->npages * guest_page_size,
19462306a36Sopenharmony_ci		    "Too high gpa to translate");
19562306a36Sopenharmony_ci	gpa -= MEM_GPA;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	gpage = gpa / guest_page_size;
19862306a36Sopenharmony_ci	pgoffs = gpa % guest_page_size;
19962306a36Sopenharmony_ci	slot = min(gpage / data->pages_per_slot, (uint64_t)data->nslots - 1);
20062306a36Sopenharmony_ci	slotoffs = gpage - (slot * data->pages_per_slot);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (rempages) {
20362306a36Sopenharmony_ci		uint64_t slotpages;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		if (slot == data->nslots - 1)
20662306a36Sopenharmony_ci			slotpages = data->npages - slot * data->pages_per_slot;
20762306a36Sopenharmony_ci		else
20862306a36Sopenharmony_ci			slotpages = data->pages_per_slot;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		TEST_ASSERT(!pgoffs,
21162306a36Sopenharmony_ci			    "Asking for remaining pages in slot but gpa not page aligned");
21262306a36Sopenharmony_ci		*rempages = slotpages - slotoffs;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	base = data->hva_slots[slot];
21662306a36Sopenharmony_ci	return (uint8_t *)base + slotoffs * guest_page_size + pgoffs;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic uint64_t vm_slot2gpa(struct vm_data *data, uint32_t slot)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	TEST_ASSERT(slot < data->nslots, "Too high slot number");
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return MEM_GPA + slot * data->pages_per_slot * guest_page_size;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic struct vm_data *alloc_vm(void)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct vm_data *data;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	data = malloc(sizeof(*data));
23362306a36Sopenharmony_ci	TEST_ASSERT(data, "malloc(vmdata) failed");
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	data->vm = NULL;
23662306a36Sopenharmony_ci	data->vcpu = NULL;
23762306a36Sopenharmony_ci	data->hva_slots = NULL;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return data;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic bool check_slot_pages(uint32_t host_page_size, uint32_t guest_page_size,
24362306a36Sopenharmony_ci			     uint64_t pages_per_slot, uint64_t rempages)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	if (!pages_per_slot)
24662306a36Sopenharmony_ci		return false;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if ((pages_per_slot * guest_page_size) % host_page_size)
24962306a36Sopenharmony_ci		return false;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if ((rempages * guest_page_size) % host_page_size)
25262306a36Sopenharmony_ci		return false;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return true;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic uint64_t get_max_slots(struct vm_data *data, uint32_t host_page_size)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
26162306a36Sopenharmony_ci	uint64_t mempages, pages_per_slot, rempages;
26262306a36Sopenharmony_ci	uint64_t slots;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	mempages = data->npages;
26562306a36Sopenharmony_ci	slots = data->nslots;
26662306a36Sopenharmony_ci	while (--slots > 1) {
26762306a36Sopenharmony_ci		pages_per_slot = mempages / slots;
26862306a36Sopenharmony_ci		if (!pages_per_slot)
26962306a36Sopenharmony_ci			continue;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		rempages = mempages % pages_per_slot;
27262306a36Sopenharmony_ci		if (check_slot_pages(host_page_size, guest_page_size,
27362306a36Sopenharmony_ci				     pages_per_slot, rempages))
27462306a36Sopenharmony_ci			return slots + 1;	/* slot 0 is reserved */
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots,
28162306a36Sopenharmony_ci		       void *guest_code, uint64_t mem_size,
28262306a36Sopenharmony_ci		       struct timespec *slot_runtime)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	uint64_t mempages, rempages;
28562306a36Sopenharmony_ci	uint64_t guest_addr;
28662306a36Sopenharmony_ci	uint32_t slot, host_page_size, guest_page_size;
28762306a36Sopenharmony_ci	struct timespec tstart;
28862306a36Sopenharmony_ci	struct sync_area *sync;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	host_page_size = getpagesize();
29162306a36Sopenharmony_ci	guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size;
29262306a36Sopenharmony_ci	mempages = mem_size / guest_page_size;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	data->vm = __vm_create_with_one_vcpu(&data->vcpu, mempages, guest_code);
29562306a36Sopenharmony_ci	TEST_ASSERT(data->vm->page_size == guest_page_size, "Invalid VM page size");
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	data->npages = mempages;
29862306a36Sopenharmony_ci	TEST_ASSERT(data->npages > 1, "Can't test without any memory");
29962306a36Sopenharmony_ci	data->nslots = nslots;
30062306a36Sopenharmony_ci	data->pages_per_slot = data->npages / data->nslots;
30162306a36Sopenharmony_ci	rempages = data->npages % data->nslots;
30262306a36Sopenharmony_ci	if (!check_slot_pages(host_page_size, guest_page_size,
30362306a36Sopenharmony_ci			      data->pages_per_slot, rempages)) {
30462306a36Sopenharmony_ci		*maxslots = get_max_slots(data, host_page_size);
30562306a36Sopenharmony_ci		return false;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	data->hva_slots = malloc(sizeof(*data->hva_slots) * data->nslots);
30962306a36Sopenharmony_ci	TEST_ASSERT(data->hva_slots, "malloc() fail");
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	pr_info_v("Adding slots 1..%i, each slot with %"PRIu64" pages + %"PRIu64" extra pages last\n",
31262306a36Sopenharmony_ci		data->nslots, data->pages_per_slot, rempages);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &tstart);
31562306a36Sopenharmony_ci	for (slot = 1, guest_addr = MEM_GPA; slot <= data->nslots; slot++) {
31662306a36Sopenharmony_ci		uint64_t npages;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		npages = data->pages_per_slot;
31962306a36Sopenharmony_ci		if (slot == data->nslots)
32062306a36Sopenharmony_ci			npages += rempages;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		vm_userspace_mem_region_add(data->vm, VM_MEM_SRC_ANONYMOUS,
32362306a36Sopenharmony_ci					    guest_addr, slot, npages,
32462306a36Sopenharmony_ci					    0);
32562306a36Sopenharmony_ci		guest_addr += npages * guest_page_size;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci	*slot_runtime = timespec_elapsed(tstart);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	for (slot = 1, guest_addr = MEM_GPA; slot <= data->nslots; slot++) {
33062306a36Sopenharmony_ci		uint64_t npages;
33162306a36Sopenharmony_ci		uint64_t gpa;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		npages = data->pages_per_slot;
33462306a36Sopenharmony_ci		if (slot == data->nslots)
33562306a36Sopenharmony_ci			npages += rempages;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		gpa = vm_phy_pages_alloc(data->vm, npages, guest_addr, slot);
33862306a36Sopenharmony_ci		TEST_ASSERT(gpa == guest_addr,
33962306a36Sopenharmony_ci			    "vm_phy_pages_alloc() failed\n");
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		data->hva_slots[slot - 1] = addr_gpa2hva(data->vm, guest_addr);
34262306a36Sopenharmony_ci		memset(data->hva_slots[slot - 1], 0, npages * guest_page_size);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		guest_addr += npages * guest_page_size;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	virt_map(data->vm, MEM_GPA, MEM_GPA, data->npages);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL);
35062306a36Sopenharmony_ci	sync->guest_page_size = data->vm->page_size;
35162306a36Sopenharmony_ci	atomic_init(&sync->start_flag, false);
35262306a36Sopenharmony_ci	atomic_init(&sync->exit_flag, false);
35362306a36Sopenharmony_ci	atomic_init(&sync->sync_flag, false);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	data->mmio_ok = false;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return true;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void launch_vm(struct vm_data *data)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	pr_info_v("Launching the test VM\n");
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	pthread_create(&data->vcpu_thread, NULL, vcpu_worker, data);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Ensure the guest thread is spun up. */
36762306a36Sopenharmony_ci	wait_for_vcpu();
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void free_vm(struct vm_data *data)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	kvm_vm_free(data->vm);
37362306a36Sopenharmony_ci	free(data->hva_slots);
37462306a36Sopenharmony_ci	free(data);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void wait_guest_exit(struct vm_data *data)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	pthread_join(data->vcpu_thread, NULL);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void let_guest_run(struct sync_area *sync)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	atomic_store_explicit(&sync->start_flag, true, memory_order_release);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void guest_spin_until_start(void)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	while (!atomic_load_explicit(&sync->start_flag, memory_order_acquire))
39262306a36Sopenharmony_ci		;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void make_guest_exit(struct sync_area *sync)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	atomic_store_explicit(&sync->exit_flag, true, memory_order_release);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic bool _guest_should_exit(void)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return atomic_load_explicit(&sync->exit_flag, memory_order_acquire);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci#define guest_should_exit() unlikely(_guest_should_exit())
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/*
41062306a36Sopenharmony_ci * noinline so we can easily see how much time the host spends waiting
41162306a36Sopenharmony_ci * for the guest.
41262306a36Sopenharmony_ci * For the same reason use alarm() instead of polling clock_gettime()
41362306a36Sopenharmony_ci * to implement a wait timeout.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_cistatic noinline void host_perform_sync(struct sync_area *sync)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	alarm(2);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	atomic_store_explicit(&sync->sync_flag, true, memory_order_release);
42062306a36Sopenharmony_ci	while (atomic_load_explicit(&sync->sync_flag, memory_order_acquire))
42162306a36Sopenharmony_ci		;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	alarm(0);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic bool guest_perform_sync(void)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
42962306a36Sopenharmony_ci	bool expected;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	do {
43262306a36Sopenharmony_ci		if (guest_should_exit())
43362306a36Sopenharmony_ci			return false;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		expected = true;
43662306a36Sopenharmony_ci	} while (!atomic_compare_exchange_weak_explicit(&sync->sync_flag,
43762306a36Sopenharmony_ci							&expected, false,
43862306a36Sopenharmony_ci							memory_order_acq_rel,
43962306a36Sopenharmony_ci							memory_order_relaxed));
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return true;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void guest_code_test_memslot_move(void)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
44762306a36Sopenharmony_ci	uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size);
44862306a36Sopenharmony_ci	uintptr_t base = (typeof(base))READ_ONCE(sync->move_area_ptr);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	GUEST_SYNC(0);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	guest_spin_until_start();
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	while (!guest_should_exit()) {
45562306a36Sopenharmony_ci		uintptr_t ptr;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		for (ptr = base; ptr < base + MEM_TEST_MOVE_SIZE;
45862306a36Sopenharmony_ci		     ptr += page_size)
45962306a36Sopenharmony_ci			*(uint64_t *)ptr = MEM_TEST_VAL_1;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/*
46262306a36Sopenharmony_ci		 * No host sync here since the MMIO exits are so expensive
46362306a36Sopenharmony_ci		 * that the host would spend most of its time waiting for
46462306a36Sopenharmony_ci		 * the guest and so instead of measuring memslot move
46562306a36Sopenharmony_ci		 * performance we would measure the performance and
46662306a36Sopenharmony_ci		 * likelihood of MMIO exits
46762306a36Sopenharmony_ci		 */
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	GUEST_DONE();
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic void guest_code_test_memslot_map(void)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
47662306a36Sopenharmony_ci	uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	GUEST_SYNC(0);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	guest_spin_until_start();
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	while (1) {
48362306a36Sopenharmony_ci		uintptr_t ptr;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		for (ptr = MEM_TEST_GPA;
48662306a36Sopenharmony_ci		     ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2;
48762306a36Sopenharmony_ci		     ptr += page_size)
48862306a36Sopenharmony_ci			*(uint64_t *)ptr = MEM_TEST_VAL_1;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		if (!guest_perform_sync())
49162306a36Sopenharmony_ci			break;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		for (ptr = MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2;
49462306a36Sopenharmony_ci		     ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE;
49562306a36Sopenharmony_ci		     ptr += page_size)
49662306a36Sopenharmony_ci			*(uint64_t *)ptr = MEM_TEST_VAL_2;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if (!guest_perform_sync())
49962306a36Sopenharmony_ci			break;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	GUEST_DONE();
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic void guest_code_test_memslot_unmap(void)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	GUEST_SYNC(0);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	guest_spin_until_start();
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	while (1) {
51462306a36Sopenharmony_ci		uintptr_t ptr = MEM_TEST_GPA;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		/*
51762306a36Sopenharmony_ci		 * We can afford to access (map) just a small number of pages
51862306a36Sopenharmony_ci		 * per host sync as otherwise the host will spend
51962306a36Sopenharmony_ci		 * a significant amount of its time waiting for the guest
52062306a36Sopenharmony_ci		 * (instead of doing unmap operations), so this will
52162306a36Sopenharmony_ci		 * effectively turn this test into a map performance test.
52262306a36Sopenharmony_ci		 *
52362306a36Sopenharmony_ci		 * Just access a single page to be on the safe side.
52462306a36Sopenharmony_ci		 */
52562306a36Sopenharmony_ci		*(uint64_t *)ptr = MEM_TEST_VAL_1;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		if (!guest_perform_sync())
52862306a36Sopenharmony_ci			break;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		ptr += MEM_TEST_UNMAP_SIZE / 2;
53162306a36Sopenharmony_ci		*(uint64_t *)ptr = MEM_TEST_VAL_2;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		if (!guest_perform_sync())
53462306a36Sopenharmony_ci			break;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	GUEST_DONE();
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void guest_code_test_memslot_rw(void)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
54362306a36Sopenharmony_ci	uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	GUEST_SYNC(0);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	guest_spin_until_start();
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	while (1) {
55062306a36Sopenharmony_ci		uintptr_t ptr;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		for (ptr = MEM_TEST_GPA;
55362306a36Sopenharmony_ci		     ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size)
55462306a36Sopenharmony_ci			*(uint64_t *)ptr = MEM_TEST_VAL_1;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		if (!guest_perform_sync())
55762306a36Sopenharmony_ci			break;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		for (ptr = MEM_TEST_GPA + page_size / 2;
56062306a36Sopenharmony_ci		     ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size) {
56162306a36Sopenharmony_ci			uint64_t val = *(uint64_t *)ptr;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci			GUEST_ASSERT_EQ(val, MEM_TEST_VAL_2);
56462306a36Sopenharmony_ci			*(uint64_t *)ptr = 0;
56562306a36Sopenharmony_ci		}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		if (!guest_perform_sync())
56862306a36Sopenharmony_ci			break;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	GUEST_DONE();
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic bool test_memslot_move_prepare(struct vm_data *data,
57562306a36Sopenharmony_ci				      struct sync_area *sync,
57662306a36Sopenharmony_ci				      uint64_t *maxslots, bool isactive)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
57962306a36Sopenharmony_ci	uint64_t movesrcgpa, movetestgpa;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	movesrcgpa = vm_slot2gpa(data, data->nslots - 1);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (isactive) {
58462306a36Sopenharmony_ci		uint64_t lastpages;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		vm_gpa2hva(data, movesrcgpa, &lastpages);
58762306a36Sopenharmony_ci		if (lastpages * guest_page_size < MEM_TEST_MOVE_SIZE / 2) {
58862306a36Sopenharmony_ci			*maxslots = 0;
58962306a36Sopenharmony_ci			return false;
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	movetestgpa = movesrcgpa - (MEM_TEST_MOVE_SIZE / (isactive ? 2 : 1));
59462306a36Sopenharmony_ci	sync->move_area_ptr = (void *)movetestgpa;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (isactive) {
59762306a36Sopenharmony_ci		data->mmio_ok = true;
59862306a36Sopenharmony_ci		data->mmio_gpa_min = movesrcgpa;
59962306a36Sopenharmony_ci		data->mmio_gpa_max = movesrcgpa + MEM_TEST_MOVE_SIZE / 2 - 1;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return true;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic bool test_memslot_move_prepare_active(struct vm_data *data,
60662306a36Sopenharmony_ci					     struct sync_area *sync,
60762306a36Sopenharmony_ci					     uint64_t *maxslots)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	return test_memslot_move_prepare(data, sync, maxslots, true);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic bool test_memslot_move_prepare_inactive(struct vm_data *data,
61362306a36Sopenharmony_ci					       struct sync_area *sync,
61462306a36Sopenharmony_ci					       uint64_t *maxslots)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	return test_memslot_move_prepare(data, sync, maxslots, false);
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic void test_memslot_move_loop(struct vm_data *data, struct sync_area *sync)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	uint64_t movesrcgpa;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	movesrcgpa = vm_slot2gpa(data, data->nslots - 1);
62462306a36Sopenharmony_ci	vm_mem_region_move(data->vm, data->nslots - 1 + 1,
62562306a36Sopenharmony_ci			   MEM_TEST_MOVE_GPA_DEST);
62662306a36Sopenharmony_ci	vm_mem_region_move(data->vm, data->nslots - 1 + 1, movesrcgpa);
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic void test_memslot_do_unmap(struct vm_data *data,
63062306a36Sopenharmony_ci				  uint64_t offsp, uint64_t count)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	uint64_t gpa, ctr;
63362306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	for (gpa = MEM_TEST_GPA + offsp * guest_page_size, ctr = 0; ctr < count; ) {
63662306a36Sopenharmony_ci		uint64_t npages;
63762306a36Sopenharmony_ci		void *hva;
63862306a36Sopenharmony_ci		int ret;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		hva = vm_gpa2hva(data, gpa, &npages);
64162306a36Sopenharmony_ci		TEST_ASSERT(npages, "Empty memory slot at gptr 0x%"PRIx64, gpa);
64262306a36Sopenharmony_ci		npages = min(npages, count - ctr);
64362306a36Sopenharmony_ci		ret = madvise(hva, npages * guest_page_size, MADV_DONTNEED);
64462306a36Sopenharmony_ci		TEST_ASSERT(!ret,
64562306a36Sopenharmony_ci			    "madvise(%p, MADV_DONTNEED) on VM memory should not fail for gptr 0x%"PRIx64,
64662306a36Sopenharmony_ci			    hva, gpa);
64762306a36Sopenharmony_ci		ctr += npages;
64862306a36Sopenharmony_ci		gpa += npages * guest_page_size;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	TEST_ASSERT(ctr == count,
65162306a36Sopenharmony_ci		    "madvise(MADV_DONTNEED) should exactly cover all of the requested area");
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void test_memslot_map_unmap_check(struct vm_data *data,
65562306a36Sopenharmony_ci					 uint64_t offsp, uint64_t valexp)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	uint64_t gpa;
65862306a36Sopenharmony_ci	uint64_t *val;
65962306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (!map_unmap_verify)
66262306a36Sopenharmony_ci		return;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	gpa = MEM_TEST_GPA + offsp * guest_page_size;
66562306a36Sopenharmony_ci	val = (typeof(val))vm_gpa2hva(data, gpa, NULL);
66662306a36Sopenharmony_ci	TEST_ASSERT(*val == valexp,
66762306a36Sopenharmony_ci		    "Guest written values should read back correctly before unmap (%"PRIu64" vs %"PRIu64" @ %"PRIx64")",
66862306a36Sopenharmony_ci		    *val, valexp, gpa);
66962306a36Sopenharmony_ci	*val = 0;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic void test_memslot_map_loop(struct vm_data *data, struct sync_area *sync)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
67562306a36Sopenharmony_ci	uint64_t guest_pages = MEM_TEST_MAP_SIZE / guest_page_size;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/*
67862306a36Sopenharmony_ci	 * Unmap the second half of the test area while guest writes to (maps)
67962306a36Sopenharmony_ci	 * the first half.
68062306a36Sopenharmony_ci	 */
68162306a36Sopenharmony_ci	test_memslot_do_unmap(data, guest_pages / 2, guest_pages / 2);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/*
68462306a36Sopenharmony_ci	 * Wait for the guest to finish writing the first half of the test
68562306a36Sopenharmony_ci	 * area, verify the written value on the first and the last page of
68662306a36Sopenharmony_ci	 * this area and then unmap it.
68762306a36Sopenharmony_ci	 * Meanwhile, the guest is writing to (mapping) the second half of
68862306a36Sopenharmony_ci	 * the test area.
68962306a36Sopenharmony_ci	 */
69062306a36Sopenharmony_ci	host_perform_sync(sync);
69162306a36Sopenharmony_ci	test_memslot_map_unmap_check(data, 0, MEM_TEST_VAL_1);
69262306a36Sopenharmony_ci	test_memslot_map_unmap_check(data, guest_pages / 2 - 1, MEM_TEST_VAL_1);
69362306a36Sopenharmony_ci	test_memslot_do_unmap(data, 0, guest_pages / 2);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/*
69762306a36Sopenharmony_ci	 * Wait for the guest to finish writing the second half of the test
69862306a36Sopenharmony_ci	 * area and verify the written value on the first and the last page
69962306a36Sopenharmony_ci	 * of this area.
70062306a36Sopenharmony_ci	 * The area will be unmapped at the beginning of the next loop
70162306a36Sopenharmony_ci	 * iteration.
70262306a36Sopenharmony_ci	 * Meanwhile, the guest is writing to (mapping) the first half of
70362306a36Sopenharmony_ci	 * the test area.
70462306a36Sopenharmony_ci	 */
70562306a36Sopenharmony_ci	host_perform_sync(sync);
70662306a36Sopenharmony_ci	test_memslot_map_unmap_check(data, guest_pages / 2, MEM_TEST_VAL_2);
70762306a36Sopenharmony_ci	test_memslot_map_unmap_check(data, guest_pages - 1, MEM_TEST_VAL_2);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic void test_memslot_unmap_loop_common(struct vm_data *data,
71162306a36Sopenharmony_ci					   struct sync_area *sync,
71262306a36Sopenharmony_ci					   uint64_t chunk)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
71562306a36Sopenharmony_ci	uint64_t guest_pages = MEM_TEST_UNMAP_SIZE / guest_page_size;
71662306a36Sopenharmony_ci	uint64_t ctr;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/*
71962306a36Sopenharmony_ci	 * Wait for the guest to finish mapping page(s) in the first half
72062306a36Sopenharmony_ci	 * of the test area, verify the written value and then perform unmap
72162306a36Sopenharmony_ci	 * of this area.
72262306a36Sopenharmony_ci	 * Meanwhile, the guest is writing to (mapping) page(s) in the second
72362306a36Sopenharmony_ci	 * half of the test area.
72462306a36Sopenharmony_ci	 */
72562306a36Sopenharmony_ci	host_perform_sync(sync);
72662306a36Sopenharmony_ci	test_memslot_map_unmap_check(data, 0, MEM_TEST_VAL_1);
72762306a36Sopenharmony_ci	for (ctr = 0; ctr < guest_pages / 2; ctr += chunk)
72862306a36Sopenharmony_ci		test_memslot_do_unmap(data, ctr, chunk);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Likewise, but for the opposite host / guest areas */
73162306a36Sopenharmony_ci	host_perform_sync(sync);
73262306a36Sopenharmony_ci	test_memslot_map_unmap_check(data, guest_pages / 2, MEM_TEST_VAL_2);
73362306a36Sopenharmony_ci	for (ctr = guest_pages / 2; ctr < guest_pages; ctr += chunk)
73462306a36Sopenharmony_ci		test_memslot_do_unmap(data, ctr, chunk);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic void test_memslot_unmap_loop(struct vm_data *data,
73862306a36Sopenharmony_ci				    struct sync_area *sync)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	uint32_t host_page_size = getpagesize();
74162306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
74262306a36Sopenharmony_ci	uint64_t guest_chunk_pages = guest_page_size >= host_page_size ?
74362306a36Sopenharmony_ci					1 : host_page_size / guest_page_size;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	test_memslot_unmap_loop_common(data, sync, guest_chunk_pages);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic void test_memslot_unmap_loop_chunked(struct vm_data *data,
74962306a36Sopenharmony_ci					    struct sync_area *sync)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
75262306a36Sopenharmony_ci	uint64_t guest_chunk_pages = MEM_TEST_UNMAP_CHUNK_SIZE / guest_page_size;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	test_memslot_unmap_loop_common(data, sync, guest_chunk_pages);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void test_memslot_rw_loop(struct vm_data *data, struct sync_area *sync)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	uint64_t gptr;
76062306a36Sopenharmony_ci	uint32_t guest_page_size = data->vm->page_size;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	for (gptr = MEM_TEST_GPA + guest_page_size / 2;
76362306a36Sopenharmony_ci	     gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += guest_page_size)
76462306a36Sopenharmony_ci		*(uint64_t *)vm_gpa2hva(data, gptr, NULL) = MEM_TEST_VAL_2;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	host_perform_sync(sync);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	for (gptr = MEM_TEST_GPA;
76962306a36Sopenharmony_ci	     gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += guest_page_size) {
77062306a36Sopenharmony_ci		uint64_t *vptr = (typeof(vptr))vm_gpa2hva(data, gptr, NULL);
77162306a36Sopenharmony_ci		uint64_t val = *vptr;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		TEST_ASSERT(val == MEM_TEST_VAL_1,
77462306a36Sopenharmony_ci			    "Guest written values should read back correctly (is %"PRIu64" @ %"PRIx64")",
77562306a36Sopenharmony_ci			    val, gptr);
77662306a36Sopenharmony_ci		*vptr = 0;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	host_perform_sync(sync);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistruct test_data {
78362306a36Sopenharmony_ci	const char *name;
78462306a36Sopenharmony_ci	uint64_t mem_size;
78562306a36Sopenharmony_ci	void (*guest_code)(void);
78662306a36Sopenharmony_ci	bool (*prepare)(struct vm_data *data, struct sync_area *sync,
78762306a36Sopenharmony_ci			uint64_t *maxslots);
78862306a36Sopenharmony_ci	void (*loop)(struct vm_data *data, struct sync_area *sync);
78962306a36Sopenharmony_ci};
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic bool test_execute(int nslots, uint64_t *maxslots,
79262306a36Sopenharmony_ci			 unsigned int maxtime,
79362306a36Sopenharmony_ci			 const struct test_data *tdata,
79462306a36Sopenharmony_ci			 uint64_t *nloops,
79562306a36Sopenharmony_ci			 struct timespec *slot_runtime,
79662306a36Sopenharmony_ci			 struct timespec *guest_runtime)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	uint64_t mem_size = tdata->mem_size ? : MEM_SIZE;
79962306a36Sopenharmony_ci	struct vm_data *data;
80062306a36Sopenharmony_ci	struct sync_area *sync;
80162306a36Sopenharmony_ci	struct timespec tstart;
80262306a36Sopenharmony_ci	bool ret = true;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	data = alloc_vm();
80562306a36Sopenharmony_ci	if (!prepare_vm(data, nslots, maxslots, tdata->guest_code,
80662306a36Sopenharmony_ci			mem_size, slot_runtime)) {
80762306a36Sopenharmony_ci		ret = false;
80862306a36Sopenharmony_ci		goto exit_free;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL);
81262306a36Sopenharmony_ci	if (tdata->prepare &&
81362306a36Sopenharmony_ci	    !tdata->prepare(data, sync, maxslots)) {
81462306a36Sopenharmony_ci		ret = false;
81562306a36Sopenharmony_ci		goto exit_free;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	launch_vm(data);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &tstart);
82162306a36Sopenharmony_ci	let_guest_run(sync);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	while (1) {
82462306a36Sopenharmony_ci		*guest_runtime = timespec_elapsed(tstart);
82562306a36Sopenharmony_ci		if (guest_runtime->tv_sec >= maxtime)
82662306a36Sopenharmony_ci			break;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		tdata->loop(data, sync);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		(*nloops)++;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	make_guest_exit(sync);
83462306a36Sopenharmony_ci	wait_guest_exit(data);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ciexit_free:
83762306a36Sopenharmony_ci	free_vm(data);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return ret;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic const struct test_data tests[] = {
84362306a36Sopenharmony_ci	{
84462306a36Sopenharmony_ci		.name = "map",
84562306a36Sopenharmony_ci		.mem_size = MEM_SIZE_MAP,
84662306a36Sopenharmony_ci		.guest_code = guest_code_test_memslot_map,
84762306a36Sopenharmony_ci		.loop = test_memslot_map_loop,
84862306a36Sopenharmony_ci	},
84962306a36Sopenharmony_ci	{
85062306a36Sopenharmony_ci		.name = "unmap",
85162306a36Sopenharmony_ci		.mem_size = MEM_TEST_UNMAP_SIZE + MEM_EXTRA_SIZE,
85262306a36Sopenharmony_ci		.guest_code = guest_code_test_memslot_unmap,
85362306a36Sopenharmony_ci		.loop = test_memslot_unmap_loop,
85462306a36Sopenharmony_ci	},
85562306a36Sopenharmony_ci	{
85662306a36Sopenharmony_ci		.name = "unmap chunked",
85762306a36Sopenharmony_ci		.mem_size = MEM_TEST_UNMAP_SIZE + MEM_EXTRA_SIZE,
85862306a36Sopenharmony_ci		.guest_code = guest_code_test_memslot_unmap,
85962306a36Sopenharmony_ci		.loop = test_memslot_unmap_loop_chunked,
86062306a36Sopenharmony_ci	},
86162306a36Sopenharmony_ci	{
86262306a36Sopenharmony_ci		.name = "move active area",
86362306a36Sopenharmony_ci		.guest_code = guest_code_test_memslot_move,
86462306a36Sopenharmony_ci		.prepare = test_memslot_move_prepare_active,
86562306a36Sopenharmony_ci		.loop = test_memslot_move_loop,
86662306a36Sopenharmony_ci	},
86762306a36Sopenharmony_ci	{
86862306a36Sopenharmony_ci		.name = "move inactive area",
86962306a36Sopenharmony_ci		.guest_code = guest_code_test_memslot_move,
87062306a36Sopenharmony_ci		.prepare = test_memslot_move_prepare_inactive,
87162306a36Sopenharmony_ci		.loop = test_memslot_move_loop,
87262306a36Sopenharmony_ci	},
87362306a36Sopenharmony_ci	{
87462306a36Sopenharmony_ci		.name = "RW",
87562306a36Sopenharmony_ci		.guest_code = guest_code_test_memslot_rw,
87662306a36Sopenharmony_ci		.loop = test_memslot_rw_loop
87762306a36Sopenharmony_ci	},
87862306a36Sopenharmony_ci};
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci#define NTESTS ARRAY_SIZE(tests)
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistruct test_args {
88362306a36Sopenharmony_ci	int tfirst;
88462306a36Sopenharmony_ci	int tlast;
88562306a36Sopenharmony_ci	int nslots;
88662306a36Sopenharmony_ci	int seconds;
88762306a36Sopenharmony_ci	int runs;
88862306a36Sopenharmony_ci};
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic void help(char *name, struct test_args *targs)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	int ctr;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count]\n",
89562306a36Sopenharmony_ci		name);
89662306a36Sopenharmony_ci	pr_info(" -h: print this help screen.\n");
89762306a36Sopenharmony_ci	pr_info(" -v: enable verbose mode (not for benchmarking).\n");
89862306a36Sopenharmony_ci	pr_info(" -d: enable extra debug checks.\n");
89962306a36Sopenharmony_ci	pr_info(" -s: specify memslot count cap (-1 means no cap; currently: %i)\n",
90062306a36Sopenharmony_ci		targs->nslots);
90162306a36Sopenharmony_ci	pr_info(" -f: specify the first test to run (currently: %i; max %zu)\n",
90262306a36Sopenharmony_ci		targs->tfirst, NTESTS - 1);
90362306a36Sopenharmony_ci	pr_info(" -e: specify the last test to run (currently: %i; max %zu)\n",
90462306a36Sopenharmony_ci		targs->tlast, NTESTS - 1);
90562306a36Sopenharmony_ci	pr_info(" -l: specify the test length in seconds (currently: %i)\n",
90662306a36Sopenharmony_ci		targs->seconds);
90762306a36Sopenharmony_ci	pr_info(" -r: specify the number of runs per test (currently: %i)\n",
90862306a36Sopenharmony_ci		targs->runs);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	pr_info("\nAvailable tests:\n");
91162306a36Sopenharmony_ci	for (ctr = 0; ctr < NTESTS; ctr++)
91262306a36Sopenharmony_ci		pr_info("%d: %s\n", ctr, tests[ctr].name);
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic bool check_memory_sizes(void)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	uint32_t host_page_size = getpagesize();
91862306a36Sopenharmony_ci	uint32_t guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (host_page_size > SZ_64K || guest_page_size > SZ_64K) {
92162306a36Sopenharmony_ci		pr_info("Unsupported page size on host (0x%x) or guest (0x%x)\n",
92262306a36Sopenharmony_ci			host_page_size, guest_page_size);
92362306a36Sopenharmony_ci		return false;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (MEM_SIZE % guest_page_size ||
92762306a36Sopenharmony_ci	    MEM_TEST_SIZE % guest_page_size) {
92862306a36Sopenharmony_ci		pr_info("invalid MEM_SIZE or MEM_TEST_SIZE\n");
92962306a36Sopenharmony_ci		return false;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (MEM_SIZE_MAP % guest_page_size		||
93362306a36Sopenharmony_ci	    MEM_TEST_MAP_SIZE % guest_page_size		||
93462306a36Sopenharmony_ci	    (MEM_TEST_MAP_SIZE / guest_page_size) <= 2	||
93562306a36Sopenharmony_ci	    (MEM_TEST_MAP_SIZE / guest_page_size) % 2) {
93662306a36Sopenharmony_ci		pr_info("invalid MEM_SIZE_MAP or MEM_TEST_MAP_SIZE\n");
93762306a36Sopenharmony_ci		return false;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (MEM_TEST_UNMAP_SIZE > MEM_TEST_SIZE		||
94162306a36Sopenharmony_ci	    MEM_TEST_UNMAP_SIZE % guest_page_size	||
94262306a36Sopenharmony_ci	    (MEM_TEST_UNMAP_SIZE / guest_page_size) %
94362306a36Sopenharmony_ci	    (2 * MEM_TEST_UNMAP_CHUNK_SIZE / guest_page_size)) {
94462306a36Sopenharmony_ci		pr_info("invalid MEM_TEST_UNMAP_SIZE or MEM_TEST_UNMAP_CHUNK_SIZE\n");
94562306a36Sopenharmony_ci		return false;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return true;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic bool parse_args(int argc, char *argv[],
95262306a36Sopenharmony_ci		       struct test_args *targs)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	uint32_t max_mem_slots;
95562306a36Sopenharmony_ci	int opt;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, "hvds:f:e:l:r:")) != -1) {
95862306a36Sopenharmony_ci		switch (opt) {
95962306a36Sopenharmony_ci		case 'h':
96062306a36Sopenharmony_ci		default:
96162306a36Sopenharmony_ci			help(argv[0], targs);
96262306a36Sopenharmony_ci			return false;
96362306a36Sopenharmony_ci		case 'v':
96462306a36Sopenharmony_ci			verbose = true;
96562306a36Sopenharmony_ci			break;
96662306a36Sopenharmony_ci		case 'd':
96762306a36Sopenharmony_ci			map_unmap_verify = true;
96862306a36Sopenharmony_ci			break;
96962306a36Sopenharmony_ci		case 's':
97062306a36Sopenharmony_ci			targs->nslots = atoi_paranoid(optarg);
97162306a36Sopenharmony_ci			if (targs->nslots <= 1 && targs->nslots != -1) {
97262306a36Sopenharmony_ci				pr_info("Slot count cap must be larger than 1 or -1 for no cap\n");
97362306a36Sopenharmony_ci				return false;
97462306a36Sopenharmony_ci			}
97562306a36Sopenharmony_ci			break;
97662306a36Sopenharmony_ci		case 'f':
97762306a36Sopenharmony_ci			targs->tfirst = atoi_non_negative("First test", optarg);
97862306a36Sopenharmony_ci			break;
97962306a36Sopenharmony_ci		case 'e':
98062306a36Sopenharmony_ci			targs->tlast = atoi_non_negative("Last test", optarg);
98162306a36Sopenharmony_ci			if (targs->tlast >= NTESTS) {
98262306a36Sopenharmony_ci				pr_info("Last test to run has to be non-negative and less than %zu\n",
98362306a36Sopenharmony_ci					NTESTS);
98462306a36Sopenharmony_ci				return false;
98562306a36Sopenharmony_ci			}
98662306a36Sopenharmony_ci			break;
98762306a36Sopenharmony_ci		case 'l':
98862306a36Sopenharmony_ci			targs->seconds = atoi_non_negative("Test length", optarg);
98962306a36Sopenharmony_ci			break;
99062306a36Sopenharmony_ci		case 'r':
99162306a36Sopenharmony_ci			targs->runs = atoi_positive("Runs per test", optarg);
99262306a36Sopenharmony_ci			break;
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (optind < argc) {
99762306a36Sopenharmony_ci		help(argv[0], targs);
99862306a36Sopenharmony_ci		return false;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (targs->tfirst > targs->tlast) {
100262306a36Sopenharmony_ci		pr_info("First test to run cannot be greater than the last test to run\n");
100362306a36Sopenharmony_ci		return false;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	max_mem_slots = kvm_check_cap(KVM_CAP_NR_MEMSLOTS);
100762306a36Sopenharmony_ci	if (max_mem_slots <= 1) {
100862306a36Sopenharmony_ci		pr_info("KVM_CAP_NR_MEMSLOTS should be greater than 1\n");
100962306a36Sopenharmony_ci		return false;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/* Memory slot 0 is reserved */
101362306a36Sopenharmony_ci	if (targs->nslots == -1)
101462306a36Sopenharmony_ci		targs->nslots = max_mem_slots - 1;
101562306a36Sopenharmony_ci	else
101662306a36Sopenharmony_ci		targs->nslots = min_t(int, targs->nslots, max_mem_slots) - 1;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	pr_info_v("Allowed Number of memory slots: %"PRIu32"\n",
101962306a36Sopenharmony_ci		  targs->nslots + 1);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	return true;
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistruct test_result {
102562306a36Sopenharmony_ci	struct timespec slot_runtime, guest_runtime, iter_runtime;
102662306a36Sopenharmony_ci	int64_t slottimens, runtimens;
102762306a36Sopenharmony_ci	uint64_t nloops;
102862306a36Sopenharmony_ci};
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic bool test_loop(const struct test_data *data,
103162306a36Sopenharmony_ci		      const struct test_args *targs,
103262306a36Sopenharmony_ci		      struct test_result *rbestslottime,
103362306a36Sopenharmony_ci		      struct test_result *rbestruntime)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	uint64_t maxslots;
103662306a36Sopenharmony_ci	struct test_result result = {};
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!test_execute(targs->nslots, &maxslots, targs->seconds, data,
103962306a36Sopenharmony_ci			  &result.nloops,
104062306a36Sopenharmony_ci			  &result.slot_runtime, &result.guest_runtime)) {
104162306a36Sopenharmony_ci		if (maxslots)
104262306a36Sopenharmony_ci			pr_info("Memslot count too high for this test, decrease the cap (max is %"PRIu64")\n",
104362306a36Sopenharmony_ci				maxslots);
104462306a36Sopenharmony_ci		else
104562306a36Sopenharmony_ci			pr_info("Memslot count may be too high for this test, try adjusting the cap\n");
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci		return false;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	pr_info("Test took %ld.%.9lds for slot setup + %ld.%.9lds all iterations\n",
105162306a36Sopenharmony_ci		result.slot_runtime.tv_sec, result.slot_runtime.tv_nsec,
105262306a36Sopenharmony_ci		result.guest_runtime.tv_sec, result.guest_runtime.tv_nsec);
105362306a36Sopenharmony_ci	if (!result.nloops) {
105462306a36Sopenharmony_ci		pr_info("No full loops done - too short test time or system too loaded?\n");
105562306a36Sopenharmony_ci		return true;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	result.iter_runtime = timespec_div(result.guest_runtime,
105962306a36Sopenharmony_ci					   result.nloops);
106062306a36Sopenharmony_ci	pr_info("Done %"PRIu64" iterations, avg %ld.%.9lds each\n",
106162306a36Sopenharmony_ci		result.nloops,
106262306a36Sopenharmony_ci		result.iter_runtime.tv_sec,
106362306a36Sopenharmony_ci		result.iter_runtime.tv_nsec);
106462306a36Sopenharmony_ci	result.slottimens = timespec_to_ns(result.slot_runtime);
106562306a36Sopenharmony_ci	result.runtimens = timespec_to_ns(result.iter_runtime);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	/*
106862306a36Sopenharmony_ci	 * Only rank the slot setup time for tests using the whole test memory
106962306a36Sopenharmony_ci	 * area so they are comparable
107062306a36Sopenharmony_ci	 */
107162306a36Sopenharmony_ci	if (!data->mem_size &&
107262306a36Sopenharmony_ci	    (!rbestslottime->slottimens ||
107362306a36Sopenharmony_ci	     result.slottimens < rbestslottime->slottimens))
107462306a36Sopenharmony_ci		*rbestslottime = result;
107562306a36Sopenharmony_ci	if (!rbestruntime->runtimens ||
107662306a36Sopenharmony_ci	    result.runtimens < rbestruntime->runtimens)
107762306a36Sopenharmony_ci		*rbestruntime = result;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return true;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ciint main(int argc, char *argv[])
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct test_args targs = {
108562306a36Sopenharmony_ci		.tfirst = 0,
108662306a36Sopenharmony_ci		.tlast = NTESTS - 1,
108762306a36Sopenharmony_ci		.nslots = -1,
108862306a36Sopenharmony_ci		.seconds = 5,
108962306a36Sopenharmony_ci		.runs = 1,
109062306a36Sopenharmony_ci	};
109162306a36Sopenharmony_ci	struct test_result rbestslottime = {};
109262306a36Sopenharmony_ci	int tctr;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (!check_memory_sizes())
109562306a36Sopenharmony_ci		return -1;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (!parse_args(argc, argv, &targs))
109862306a36Sopenharmony_ci		return -1;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	for (tctr = targs.tfirst; tctr <= targs.tlast; tctr++) {
110162306a36Sopenharmony_ci		const struct test_data *data = &tests[tctr];
110262306a36Sopenharmony_ci		unsigned int runctr;
110362306a36Sopenharmony_ci		struct test_result rbestruntime = {};
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		if (tctr > targs.tfirst)
110662306a36Sopenharmony_ci			pr_info("\n");
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci		pr_info("Testing %s performance with %i runs, %d seconds each\n",
110962306a36Sopenharmony_ci			data->name, targs.runs, targs.seconds);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		for (runctr = 0; runctr < targs.runs; runctr++)
111262306a36Sopenharmony_ci			if (!test_loop(data, &targs,
111362306a36Sopenharmony_ci				       &rbestslottime, &rbestruntime))
111462306a36Sopenharmony_ci				break;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		if (rbestruntime.runtimens)
111762306a36Sopenharmony_ci			pr_info("Best runtime result was %ld.%.9lds per iteration (with %"PRIu64" iterations)\n",
111862306a36Sopenharmony_ci				rbestruntime.iter_runtime.tv_sec,
111962306a36Sopenharmony_ci				rbestruntime.iter_runtime.tv_nsec,
112062306a36Sopenharmony_ci				rbestruntime.nloops);
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (rbestslottime.slottimens)
112462306a36Sopenharmony_ci		pr_info("Best slot setup time for the whole test area was %ld.%.9lds\n",
112562306a36Sopenharmony_ci			rbestslottime.slot_runtime.tv_sec,
112662306a36Sopenharmony_ci			rbestslottime.slot_runtime.tv_nsec);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	return 0;
112962306a36Sopenharmony_ci}
1130