1cad347faSMaciej S. Szmigiero // SPDX-License-Identifier: GPL-2.0
2cad347faSMaciej S. Szmigiero /*
3cad347faSMaciej S. Szmigiero  * A memslot-related performance benchmark.
4cad347faSMaciej S. Szmigiero  *
5cad347faSMaciej S. Szmigiero  * Copyright (C) 2021 Oracle and/or its affiliates.
6cad347faSMaciej S. Szmigiero  *
7cad347faSMaciej S. Szmigiero  * Basic guest setup / host vCPU thread code lifted from set_memory_region_test.
8cad347faSMaciej S. Szmigiero  */
9cad347faSMaciej S. Szmigiero #include <pthread.h>
10cad347faSMaciej S. Szmigiero #include <sched.h>
11cad347faSMaciej S. Szmigiero #include <semaphore.h>
12cad347faSMaciej S. Szmigiero #include <stdatomic.h>
13cad347faSMaciej S. Szmigiero #include <stdbool.h>
14cad347faSMaciej S. Szmigiero #include <stdint.h>
15cad347faSMaciej S. Szmigiero #include <stdio.h>
16cad347faSMaciej S. Szmigiero #include <stdlib.h>
17cad347faSMaciej S. Szmigiero #include <string.h>
18cad347faSMaciej S. Szmigiero #include <sys/mman.h>
19cad347faSMaciej S. Szmigiero #include <time.h>
20cad347faSMaciej S. Szmigiero #include <unistd.h>
21cad347faSMaciej S. Szmigiero 
22cad347faSMaciej S. Szmigiero #include <linux/compiler.h>
23cad347faSMaciej S. Szmigiero 
24cad347faSMaciej S. Szmigiero #include <test_util.h>
25cad347faSMaciej S. Szmigiero #include <kvm_util.h>
26cad347faSMaciej S. Szmigiero #include <processor.h>
27cad347faSMaciej S. Szmigiero 
28cad347faSMaciej S. Szmigiero #define VCPU_ID 0
29cad347faSMaciej S. Szmigiero 
30cad347faSMaciej S. Szmigiero #define MEM_SIZE		((512U << 20) + 4096)
31cad347faSMaciej S. Szmigiero #define MEM_SIZE_PAGES		(MEM_SIZE / 4096)
32cad347faSMaciej S. Szmigiero #define MEM_GPA		0x10000000UL
33cad347faSMaciej S. Szmigiero #define MEM_AUX_GPA		MEM_GPA
34cad347faSMaciej S. Szmigiero #define MEM_SYNC_GPA		MEM_AUX_GPA
35cad347faSMaciej S. Szmigiero #define MEM_TEST_GPA		(MEM_AUX_GPA + 4096)
36cad347faSMaciej S. Szmigiero #define MEM_TEST_SIZE		(MEM_SIZE - 4096)
37cad347faSMaciej S. Szmigiero static_assert(MEM_SIZE % 4096 == 0, "invalid mem size");
38cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_SIZE % 4096 == 0, "invalid mem test size");
39cad347faSMaciej S. Szmigiero 
40cad347faSMaciej S. Szmigiero /*
41cad347faSMaciej S. Szmigiero  * 32 MiB is max size that gets well over 100 iterations on 509 slots.
42cad347faSMaciej S. Szmigiero  * Considering that each slot needs to have at least one page up to
43cad347faSMaciej S. Szmigiero  * 8194 slots in use can then be tested (although with slightly
44cad347faSMaciej S. Szmigiero  * limited resolution).
45cad347faSMaciej S. Szmigiero  */
46cad347faSMaciej S. Szmigiero #define MEM_SIZE_MAP		((32U << 20) + 4096)
47cad347faSMaciej S. Szmigiero #define MEM_SIZE_MAP_PAGES	(MEM_SIZE_MAP / 4096)
48cad347faSMaciej S. Szmigiero #define MEM_TEST_MAP_SIZE	(MEM_SIZE_MAP - 4096)
49cad347faSMaciej S. Szmigiero #define MEM_TEST_MAP_SIZE_PAGES (MEM_TEST_MAP_SIZE / 4096)
50cad347faSMaciej S. Szmigiero static_assert(MEM_SIZE_MAP % 4096 == 0, "invalid map test region size");
51cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_MAP_SIZE % 4096 == 0, "invalid map test region size");
52cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_MAP_SIZE_PAGES % 2 == 0, "invalid map test region size");
53cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_MAP_SIZE_PAGES > 2, "invalid map test region size");
54cad347faSMaciej S. Szmigiero 
55cad347faSMaciej S. Szmigiero /*
56cad347faSMaciej S. Szmigiero  * 128 MiB is min size that fills 32k slots with at least one page in each
57cad347faSMaciej S. Szmigiero  * while at the same time gets 100+ iterations in such test
58cad347faSMaciej S. Szmigiero  */
59cad347faSMaciej S. Szmigiero #define MEM_TEST_UNMAP_SIZE		(128U << 20)
60cad347faSMaciej S. Szmigiero #define MEM_TEST_UNMAP_SIZE_PAGES	(MEM_TEST_UNMAP_SIZE / 4096)
61cad347faSMaciej S. Szmigiero /* 2 MiB chunk size like a typical huge page */
62cad347faSMaciej S. Szmigiero #define MEM_TEST_UNMAP_CHUNK_PAGES	(2U << (20 - 12))
63cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_UNMAP_SIZE <= MEM_TEST_SIZE,
64cad347faSMaciej S. Szmigiero 	      "invalid unmap test region size");
65cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_UNMAP_SIZE % 4096 == 0,
66cad347faSMaciej S. Szmigiero 	      "invalid unmap test region size");
67cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_UNMAP_SIZE_PAGES %
68cad347faSMaciej S. Szmigiero 	      (2 * MEM_TEST_UNMAP_CHUNK_PAGES) == 0,
69cad347faSMaciej S. Szmigiero 	      "invalid unmap test region size");
70cad347faSMaciej S. Szmigiero 
71cad347faSMaciej S. Szmigiero /*
72cad347faSMaciej S. Szmigiero  * For the move active test the middle of the test area is placed on
73cad347faSMaciej S. Szmigiero  * a memslot boundary: half lies in the memslot being moved, half in
74cad347faSMaciej S. Szmigiero  * other memslot(s).
75cad347faSMaciej S. Szmigiero  *
76cad347faSMaciej S. Szmigiero  * When running this test with 32k memslots (32764, really) each memslot
77cad347faSMaciej S. Szmigiero  * contains 4 pages.
78cad347faSMaciej S. Szmigiero  * The last one additionally contains the remaining 21 pages of memory,
79cad347faSMaciej S. Szmigiero  * for the total size of 25 pages.
80cad347faSMaciej S. Szmigiero  * Hence, the maximum size here is 50 pages.
81cad347faSMaciej S. Szmigiero  */
82cad347faSMaciej S. Szmigiero #define MEM_TEST_MOVE_SIZE_PAGES	(50)
83cad347faSMaciej S. Szmigiero #define MEM_TEST_MOVE_SIZE		(MEM_TEST_MOVE_SIZE_PAGES * 4096)
84cad347faSMaciej S. Szmigiero #define MEM_TEST_MOVE_GPA_DEST		(MEM_GPA + MEM_SIZE)
85cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_MOVE_SIZE <= MEM_TEST_SIZE,
86cad347faSMaciej S. Szmigiero 	      "invalid move test region size");
87cad347faSMaciej S. Szmigiero 
88cad347faSMaciej S. Szmigiero #define MEM_TEST_VAL_1 0x1122334455667788
89cad347faSMaciej S. Szmigiero #define MEM_TEST_VAL_2 0x99AABBCCDDEEFF00
90cad347faSMaciej S. Szmigiero 
91cad347faSMaciej S. Szmigiero struct vm_data {
92cad347faSMaciej S. Szmigiero 	struct kvm_vm *vm;
93cad347faSMaciej S. Szmigiero 	pthread_t vcpu_thread;
94cad347faSMaciej S. Szmigiero 	uint32_t nslots;
95cad347faSMaciej S. Szmigiero 	uint64_t npages;
96cad347faSMaciej S. Szmigiero 	uint64_t pages_per_slot;
97cad347faSMaciej S. Szmigiero 	void **hva_slots;
98cad347faSMaciej S. Szmigiero 	bool mmio_ok;
99cad347faSMaciej S. Szmigiero 	uint64_t mmio_gpa_min;
100cad347faSMaciej S. Szmigiero 	uint64_t mmio_gpa_max;
101cad347faSMaciej S. Szmigiero };
102cad347faSMaciej S. Szmigiero 
103cad347faSMaciej S. Szmigiero struct sync_area {
104cad347faSMaciej S. Szmigiero 	atomic_bool start_flag;
105cad347faSMaciej S. Szmigiero 	atomic_bool exit_flag;
106cad347faSMaciej S. Szmigiero 	atomic_bool sync_flag;
107cad347faSMaciej S. Szmigiero 	void *move_area_ptr;
108cad347faSMaciej S. Szmigiero };
109cad347faSMaciej S. Szmigiero 
110cad347faSMaciej S. Szmigiero /*
111cad347faSMaciej S. Szmigiero  * Technically, we need also for the atomic bool to be address-free, which
112cad347faSMaciej S. Szmigiero  * is recommended, but not strictly required, by C11 for lockless
113cad347faSMaciej S. Szmigiero  * implementations.
114cad347faSMaciej S. Szmigiero  * However, in practice both GCC and Clang fulfill this requirement on
115cad347faSMaciej S. Szmigiero  * all KVM-supported platforms.
116cad347faSMaciej S. Szmigiero  */
117cad347faSMaciej S. Szmigiero static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "atomic bool is not lockless");
118cad347faSMaciej S. Szmigiero 
119cad347faSMaciej S. Szmigiero static sem_t vcpu_ready;
120cad347faSMaciej S. Szmigiero 
121cad347faSMaciej S. Szmigiero static bool map_unmap_verify;
122cad347faSMaciej S. Szmigiero 
123cad347faSMaciej S. Szmigiero static bool verbose;
124cad347faSMaciej S. Szmigiero #define pr_info_v(...)				\
125cad347faSMaciej S. Szmigiero 	do {					\
126cad347faSMaciej S. Szmigiero 		if (verbose)			\
127cad347faSMaciej S. Szmigiero 			pr_info(__VA_ARGS__);	\
128cad347faSMaciej S. Szmigiero 	} while (0)
129cad347faSMaciej S. Szmigiero 
130cad347faSMaciej S. Szmigiero static void *vcpu_worker(void *data)
131cad347faSMaciej S. Szmigiero {
132cad347faSMaciej S. Szmigiero 	struct vm_data *vm = data;
133cad347faSMaciej S. Szmigiero 	struct kvm_run *run;
134cad347faSMaciej S. Szmigiero 	struct ucall uc;
135cad347faSMaciej S. Szmigiero 	uint64_t cmd;
136cad347faSMaciej S. Szmigiero 
137cad347faSMaciej S. Szmigiero 	run = vcpu_state(vm->vm, VCPU_ID);
138cad347faSMaciej S. Szmigiero 	while (1) {
139cad347faSMaciej S. Szmigiero 		vcpu_run(vm->vm, VCPU_ID);
140cad347faSMaciej S. Szmigiero 
141cad347faSMaciej S. Szmigiero 		if (run->exit_reason == KVM_EXIT_IO) {
142cad347faSMaciej S. Szmigiero 			cmd = get_ucall(vm->vm, VCPU_ID, &uc);
143cad347faSMaciej S. Szmigiero 			if (cmd != UCALL_SYNC)
144cad347faSMaciej S. Szmigiero 				break;
145cad347faSMaciej S. Szmigiero 
146cad347faSMaciej S. Szmigiero 			sem_post(&vcpu_ready);
147cad347faSMaciej S. Szmigiero 			continue;
148cad347faSMaciej S. Szmigiero 		}
149cad347faSMaciej S. Szmigiero 
150cad347faSMaciej S. Szmigiero 		if (run->exit_reason != KVM_EXIT_MMIO)
151cad347faSMaciej S. Szmigiero 			break;
152cad347faSMaciej S. Szmigiero 
153cad347faSMaciej S. Szmigiero 		TEST_ASSERT(vm->mmio_ok, "Unexpected mmio exit");
154cad347faSMaciej S. Szmigiero 		TEST_ASSERT(run->mmio.is_write, "Unexpected mmio read");
155cad347faSMaciej S. Szmigiero 		TEST_ASSERT(run->mmio.len == 8,
156cad347faSMaciej S. Szmigiero 			    "Unexpected exit mmio size = %u", run->mmio.len);
157cad347faSMaciej S. Szmigiero 		TEST_ASSERT(run->mmio.phys_addr >= vm->mmio_gpa_min &&
158cad347faSMaciej S. Szmigiero 			    run->mmio.phys_addr <= vm->mmio_gpa_max,
159cad347faSMaciej S. Szmigiero 			    "Unexpected exit mmio address = 0x%llx",
160cad347faSMaciej S. Szmigiero 			    run->mmio.phys_addr);
161cad347faSMaciej S. Szmigiero 	}
162cad347faSMaciej S. Szmigiero 
163cad347faSMaciej S. Szmigiero 	if (run->exit_reason == KVM_EXIT_IO && cmd == UCALL_ABORT)
164cad347faSMaciej S. Szmigiero 		TEST_FAIL("%s at %s:%ld, val = %lu", (const char *)uc.args[0],
165cad347faSMaciej S. Szmigiero 			  __FILE__, uc.args[1], uc.args[2]);
166cad347faSMaciej S. Szmigiero 
167cad347faSMaciej S. Szmigiero 	return NULL;
168cad347faSMaciej S. Szmigiero }
169cad347faSMaciej S. Szmigiero 
170cad347faSMaciej S. Szmigiero static void wait_for_vcpu(void)
171cad347faSMaciej S. Szmigiero {
172cad347faSMaciej S. Szmigiero 	struct timespec ts;
173cad347faSMaciej S. Szmigiero 
174cad347faSMaciej S. Szmigiero 	TEST_ASSERT(!clock_gettime(CLOCK_REALTIME, &ts),
175cad347faSMaciej S. Szmigiero 		    "clock_gettime() failed: %d\n", errno);
176cad347faSMaciej S. Szmigiero 
177cad347faSMaciej S. Szmigiero 	ts.tv_sec += 2;
178cad347faSMaciej S. Szmigiero 	TEST_ASSERT(!sem_timedwait(&vcpu_ready, &ts),
179cad347faSMaciej S. Szmigiero 		    "sem_timedwait() failed: %d\n", errno);
180cad347faSMaciej S. Szmigiero }
181cad347faSMaciej S. Szmigiero 
182cad347faSMaciej S. Szmigiero static void *vm_gpa2hva(struct vm_data *data, uint64_t gpa, uint64_t *rempages)
183cad347faSMaciej S. Szmigiero {
184cad347faSMaciej S. Szmigiero 	uint64_t gpage, pgoffs;
185cad347faSMaciej S. Szmigiero 	uint32_t slot, slotoffs;
186cad347faSMaciej S. Szmigiero 	void *base;
187cad347faSMaciej S. Szmigiero 
188cad347faSMaciej S. Szmigiero 	TEST_ASSERT(gpa >= MEM_GPA, "Too low gpa to translate");
189cad347faSMaciej S. Szmigiero 	TEST_ASSERT(gpa < MEM_GPA + data->npages * 4096,
190cad347faSMaciej S. Szmigiero 		    "Too high gpa to translate");
191cad347faSMaciej S. Szmigiero 	gpa -= MEM_GPA;
192cad347faSMaciej S. Szmigiero 
193cad347faSMaciej S. Szmigiero 	gpage = gpa / 4096;
194cad347faSMaciej S. Szmigiero 	pgoffs = gpa % 4096;
195cad347faSMaciej S. Szmigiero 	slot = min(gpage / data->pages_per_slot, (uint64_t)data->nslots - 1);
196cad347faSMaciej S. Szmigiero 	slotoffs = gpage - (slot * data->pages_per_slot);
197cad347faSMaciej S. Szmigiero 
198cad347faSMaciej S. Szmigiero 	if (rempages) {
199cad347faSMaciej S. Szmigiero 		uint64_t slotpages;
200cad347faSMaciej S. Szmigiero 
201cad347faSMaciej S. Szmigiero 		if (slot == data->nslots - 1)
202cad347faSMaciej S. Szmigiero 			slotpages = data->npages - slot * data->pages_per_slot;
203cad347faSMaciej S. Szmigiero 		else
204cad347faSMaciej S. Szmigiero 			slotpages = data->pages_per_slot;
205cad347faSMaciej S. Szmigiero 
206cad347faSMaciej S. Szmigiero 		TEST_ASSERT(!pgoffs,
207cad347faSMaciej S. Szmigiero 			    "Asking for remaining pages in slot but gpa not page aligned");
208cad347faSMaciej S. Szmigiero 		*rempages = slotpages - slotoffs;
209cad347faSMaciej S. Szmigiero 	}
210cad347faSMaciej S. Szmigiero 
211cad347faSMaciej S. Szmigiero 	base = data->hva_slots[slot];
212cad347faSMaciej S. Szmigiero 	return (uint8_t *)base + slotoffs * 4096 + pgoffs;
213cad347faSMaciej S. Szmigiero }
214cad347faSMaciej S. Szmigiero 
215cad347faSMaciej S. Szmigiero static uint64_t vm_slot2gpa(struct vm_data *data, uint32_t slot)
216cad347faSMaciej S. Szmigiero {
217cad347faSMaciej S. Szmigiero 	TEST_ASSERT(slot < data->nslots, "Too high slot number");
218cad347faSMaciej S. Szmigiero 
219cad347faSMaciej S. Szmigiero 	return MEM_GPA + slot * data->pages_per_slot * 4096;
220cad347faSMaciej S. Szmigiero }
221cad347faSMaciej S. Szmigiero 
222cad347faSMaciej S. Szmigiero static struct vm_data *alloc_vm(void)
223cad347faSMaciej S. Szmigiero {
224cad347faSMaciej S. Szmigiero 	struct vm_data *data;
225cad347faSMaciej S. Szmigiero 
226cad347faSMaciej S. Szmigiero 	data = malloc(sizeof(*data));
227cad347faSMaciej S. Szmigiero 	TEST_ASSERT(data, "malloc(vmdata) failed");
228cad347faSMaciej S. Szmigiero 
229cad347faSMaciej S. Szmigiero 	data->vm = NULL;
230cad347faSMaciej S. Szmigiero 	data->hva_slots = NULL;
231cad347faSMaciej S. Szmigiero 
232cad347faSMaciej S. Szmigiero 	return data;
233cad347faSMaciej S. Szmigiero }
234cad347faSMaciej S. Szmigiero 
235cad347faSMaciej S. Szmigiero static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots,
236cad347faSMaciej S. Szmigiero 		       void *guest_code, uint64_t mempages,
237cad347faSMaciej S. Szmigiero 		       struct timespec *slot_runtime)
238cad347faSMaciej S. Szmigiero {
239cad347faSMaciej S. Szmigiero 	uint32_t max_mem_slots;
240cad347faSMaciej S. Szmigiero 	uint64_t rempages;
241cad347faSMaciej S. Szmigiero 	uint64_t guest_addr;
242cad347faSMaciej S. Szmigiero 	uint32_t slot;
243cad347faSMaciej S. Szmigiero 	struct timespec tstart;
244cad347faSMaciej S. Szmigiero 	struct sync_area *sync;
245cad347faSMaciej S. Szmigiero 
246cad347faSMaciej S. Szmigiero 	max_mem_slots = kvm_check_cap(KVM_CAP_NR_MEMSLOTS);
247cad347faSMaciej S. Szmigiero 	TEST_ASSERT(max_mem_slots > 1,
248cad347faSMaciej S. Szmigiero 		    "KVM_CAP_NR_MEMSLOTS should be greater than 1");
249cad347faSMaciej S. Szmigiero 	TEST_ASSERT(nslots > 1 || nslots == -1,
250cad347faSMaciej S. Szmigiero 		    "Slot count cap should be greater than 1");
251cad347faSMaciej S. Szmigiero 	if (nslots != -1)
252cad347faSMaciej S. Szmigiero 		max_mem_slots = min(max_mem_slots, (uint32_t)nslots);
253cad347faSMaciej S. Szmigiero 	pr_info_v("Allowed number of memory slots: %"PRIu32"\n", max_mem_slots);
254cad347faSMaciej S. Szmigiero 
255cad347faSMaciej S. Szmigiero 	TEST_ASSERT(mempages > 1,
256cad347faSMaciej S. Szmigiero 		    "Can't test without any memory");
257cad347faSMaciej S. Szmigiero 
258cad347faSMaciej S. Szmigiero 	data->npages = mempages;
259cad347faSMaciej S. Szmigiero 	data->nslots = max_mem_slots - 1;
260cad347faSMaciej S. Szmigiero 	data->pages_per_slot = mempages / data->nslots;
261cad347faSMaciej S. Szmigiero 	if (!data->pages_per_slot) {
262cad347faSMaciej S. Szmigiero 		*maxslots = mempages + 1;
263cad347faSMaciej S. Szmigiero 		return false;
264cad347faSMaciej S. Szmigiero 	}
265cad347faSMaciej S. Szmigiero 
266cad347faSMaciej S. Szmigiero 	rempages = mempages % data->nslots;
267cad347faSMaciej S. Szmigiero 	data->hva_slots = malloc(sizeof(*data->hva_slots) * data->nslots);
268cad347faSMaciej S. Szmigiero 	TEST_ASSERT(data->hva_slots, "malloc() fail");
269cad347faSMaciej S. Szmigiero 
270f53b16adSZhenzhong Duan 	data->vm = vm_create_default(VCPU_ID, mempages, guest_code);
271cad347faSMaciej S. Szmigiero 
272cad347faSMaciej S. Szmigiero 	pr_info_v("Adding slots 1..%i, each slot with %"PRIu64" pages + %"PRIu64" extra pages last\n",
273cad347faSMaciej S. Szmigiero 		max_mem_slots - 1, data->pages_per_slot, rempages);
274cad347faSMaciej S. Szmigiero 
275cad347faSMaciej S. Szmigiero 	clock_gettime(CLOCK_MONOTONIC, &tstart);
276cad347faSMaciej S. Szmigiero 	for (slot = 1, guest_addr = MEM_GPA; slot < max_mem_slots; slot++) {
277cad347faSMaciej S. Szmigiero 		uint64_t npages;
278cad347faSMaciej S. Szmigiero 
279cad347faSMaciej S. Szmigiero 		npages = data->pages_per_slot;
280cad347faSMaciej S. Szmigiero 		if (slot == max_mem_slots - 1)
281cad347faSMaciej S. Szmigiero 			npages += rempages;
282cad347faSMaciej S. Szmigiero 
283cad347faSMaciej S. Szmigiero 		vm_userspace_mem_region_add(data->vm, VM_MEM_SRC_ANONYMOUS,
284cad347faSMaciej S. Szmigiero 					    guest_addr, slot, npages,
285cad347faSMaciej S. Szmigiero 					    0);
286cad347faSMaciej S. Szmigiero 		guest_addr += npages * 4096;
287cad347faSMaciej S. Szmigiero 	}
288cad347faSMaciej S. Szmigiero 	*slot_runtime = timespec_elapsed(tstart);
289cad347faSMaciej S. Szmigiero 
290cad347faSMaciej S. Szmigiero 	for (slot = 0, guest_addr = MEM_GPA; slot < max_mem_slots - 1; slot++) {
291cad347faSMaciej S. Szmigiero 		uint64_t npages;
292cad347faSMaciej S. Szmigiero 		uint64_t gpa;
293cad347faSMaciej S. Szmigiero 
294cad347faSMaciej S. Szmigiero 		npages = data->pages_per_slot;
295cad347faSMaciej S. Szmigiero 		if (slot == max_mem_slots - 2)
296cad347faSMaciej S. Szmigiero 			npages += rempages;
297cad347faSMaciej S. Szmigiero 
298cad347faSMaciej S. Szmigiero 		gpa = vm_phy_pages_alloc(data->vm, npages, guest_addr,
299cad347faSMaciej S. Szmigiero 					 slot + 1);
300cad347faSMaciej S. Szmigiero 		TEST_ASSERT(gpa == guest_addr,
301cad347faSMaciej S. Szmigiero 			    "vm_phy_pages_alloc() failed\n");
302cad347faSMaciej S. Szmigiero 
303cad347faSMaciej S. Szmigiero 		data->hva_slots[slot] = addr_gpa2hva(data->vm, guest_addr);
304cad347faSMaciej S. Szmigiero 		memset(data->hva_slots[slot], 0, npages * 4096);
305cad347faSMaciej S. Szmigiero 
306cad347faSMaciej S. Szmigiero 		guest_addr += npages * 4096;
307cad347faSMaciej S. Szmigiero 	}
308cad347faSMaciej S. Szmigiero 
309*4307af73SSean Christopherson 	virt_map(data->vm, MEM_GPA, MEM_GPA, mempages);
310cad347faSMaciej S. Szmigiero 
311cad347faSMaciej S. Szmigiero 	sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL);
312cad347faSMaciej S. Szmigiero 	atomic_init(&sync->start_flag, false);
313cad347faSMaciej S. Szmigiero 	atomic_init(&sync->exit_flag, false);
314cad347faSMaciej S. Szmigiero 	atomic_init(&sync->sync_flag, false);
315cad347faSMaciej S. Szmigiero 
316cad347faSMaciej S. Szmigiero 	data->mmio_ok = false;
317cad347faSMaciej S. Szmigiero 
318cad347faSMaciej S. Szmigiero 	return true;
319cad347faSMaciej S. Szmigiero }
320cad347faSMaciej S. Szmigiero 
321cad347faSMaciej S. Szmigiero static void launch_vm(struct vm_data *data)
322cad347faSMaciej S. Szmigiero {
323cad347faSMaciej S. Szmigiero 	pr_info_v("Launching the test VM\n");
324cad347faSMaciej S. Szmigiero 
325cad347faSMaciej S. Szmigiero 	pthread_create(&data->vcpu_thread, NULL, vcpu_worker, data);
326cad347faSMaciej S. Szmigiero 
327cad347faSMaciej S. Szmigiero 	/* Ensure the guest thread is spun up. */
328cad347faSMaciej S. Szmigiero 	wait_for_vcpu();
329cad347faSMaciej S. Szmigiero }
330cad347faSMaciej S. Szmigiero 
331cad347faSMaciej S. Szmigiero static void free_vm(struct vm_data *data)
332cad347faSMaciej S. Szmigiero {
333cad347faSMaciej S. Szmigiero 	kvm_vm_free(data->vm);
334cad347faSMaciej S. Szmigiero 	free(data->hva_slots);
335cad347faSMaciej S. Szmigiero 	free(data);
336cad347faSMaciej S. Szmigiero }
337cad347faSMaciej S. Szmigiero 
338cad347faSMaciej S. Szmigiero static void wait_guest_exit(struct vm_data *data)
339cad347faSMaciej S. Szmigiero {
340cad347faSMaciej S. Szmigiero 	pthread_join(data->vcpu_thread, NULL);
341cad347faSMaciej S. Szmigiero }
342cad347faSMaciej S. Szmigiero 
343cad347faSMaciej S. Szmigiero static void let_guest_run(struct sync_area *sync)
344cad347faSMaciej S. Szmigiero {
345cad347faSMaciej S. Szmigiero 	atomic_store_explicit(&sync->start_flag, true, memory_order_release);
346cad347faSMaciej S. Szmigiero }
347cad347faSMaciej S. Szmigiero 
348cad347faSMaciej S. Szmigiero static void guest_spin_until_start(void)
349cad347faSMaciej S. Szmigiero {
350cad347faSMaciej S. Szmigiero 	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
351cad347faSMaciej S. Szmigiero 
352cad347faSMaciej S. Szmigiero 	while (!atomic_load_explicit(&sync->start_flag, memory_order_acquire))
353cad347faSMaciej S. Szmigiero 		;
354cad347faSMaciej S. Szmigiero }
355cad347faSMaciej S. Szmigiero 
356cad347faSMaciej S. Szmigiero static void make_guest_exit(struct sync_area *sync)
357cad347faSMaciej S. Szmigiero {
358cad347faSMaciej S. Szmigiero 	atomic_store_explicit(&sync->exit_flag, true, memory_order_release);
359cad347faSMaciej S. Szmigiero }
360cad347faSMaciej S. Szmigiero 
361cad347faSMaciej S. Szmigiero static bool _guest_should_exit(void)
362cad347faSMaciej S. Szmigiero {
363cad347faSMaciej S. Szmigiero 	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
364cad347faSMaciej S. Szmigiero 
365cad347faSMaciej S. Szmigiero 	return atomic_load_explicit(&sync->exit_flag, memory_order_acquire);
366cad347faSMaciej S. Szmigiero }
367cad347faSMaciej S. Szmigiero 
368cad347faSMaciej S. Szmigiero #define guest_should_exit() unlikely(_guest_should_exit())
369cad347faSMaciej S. Szmigiero 
370cad347faSMaciej S. Szmigiero /*
371cad347faSMaciej S. Szmigiero  * noinline so we can easily see how much time the host spends waiting
372cad347faSMaciej S. Szmigiero  * for the guest.
373cad347faSMaciej S. Szmigiero  * For the same reason use alarm() instead of polling clock_gettime()
374cad347faSMaciej S. Szmigiero  * to implement a wait timeout.
375cad347faSMaciej S. Szmigiero  */
376cad347faSMaciej S. Szmigiero static noinline void host_perform_sync(struct sync_area *sync)
377cad347faSMaciej S. Szmigiero {
378cad347faSMaciej S. Szmigiero 	alarm(2);
379cad347faSMaciej S. Szmigiero 
380cad347faSMaciej S. Szmigiero 	atomic_store_explicit(&sync->sync_flag, true, memory_order_release);
381cad347faSMaciej S. Szmigiero 	while (atomic_load_explicit(&sync->sync_flag, memory_order_acquire))
382cad347faSMaciej S. Szmigiero 		;
383cad347faSMaciej S. Szmigiero 
384cad347faSMaciej S. Szmigiero 	alarm(0);
385cad347faSMaciej S. Szmigiero }
386cad347faSMaciej S. Szmigiero 
387cad347faSMaciej S. Szmigiero static bool guest_perform_sync(void)
388cad347faSMaciej S. Szmigiero {
389cad347faSMaciej S. Szmigiero 	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
390cad347faSMaciej S. Szmigiero 	bool expected;
391cad347faSMaciej S. Szmigiero 
392cad347faSMaciej S. Szmigiero 	do {
393cad347faSMaciej S. Szmigiero 		if (guest_should_exit())
394cad347faSMaciej S. Szmigiero 			return false;
395cad347faSMaciej S. Szmigiero 
396cad347faSMaciej S. Szmigiero 		expected = true;
397cad347faSMaciej S. Szmigiero 	} while (!atomic_compare_exchange_weak_explicit(&sync->sync_flag,
398cad347faSMaciej S. Szmigiero 							&expected, false,
399cad347faSMaciej S. Szmigiero 							memory_order_acq_rel,
400cad347faSMaciej S. Szmigiero 							memory_order_relaxed));
401cad347faSMaciej S. Szmigiero 
402cad347faSMaciej S. Szmigiero 	return true;
403cad347faSMaciej S. Szmigiero }
404cad347faSMaciej S. Szmigiero 
405cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_move(void)
406cad347faSMaciej S. Szmigiero {
407cad347faSMaciej S. Szmigiero 	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
408cad347faSMaciej S. Szmigiero 	uintptr_t base = (typeof(base))READ_ONCE(sync->move_area_ptr);
409cad347faSMaciej S. Szmigiero 
410cad347faSMaciej S. Szmigiero 	GUEST_SYNC(0);
411cad347faSMaciej S. Szmigiero 
412cad347faSMaciej S. Szmigiero 	guest_spin_until_start();
413cad347faSMaciej S. Szmigiero 
414cad347faSMaciej S. Szmigiero 	while (!guest_should_exit()) {
415cad347faSMaciej S. Szmigiero 		uintptr_t ptr;
416cad347faSMaciej S. Szmigiero 
417cad347faSMaciej S. Szmigiero 		for (ptr = base; ptr < base + MEM_TEST_MOVE_SIZE;
418cad347faSMaciej S. Szmigiero 		     ptr += 4096)
419cad347faSMaciej S. Szmigiero 			*(uint64_t *)ptr = MEM_TEST_VAL_1;
420cad347faSMaciej S. Szmigiero 
421cad347faSMaciej S. Szmigiero 		/*
422cad347faSMaciej S. Szmigiero 		 * No host sync here since the MMIO exits are so expensive
423cad347faSMaciej S. Szmigiero 		 * that the host would spend most of its time waiting for
424cad347faSMaciej S. Szmigiero 		 * the guest and so instead of measuring memslot move
425cad347faSMaciej S. Szmigiero 		 * performance we would measure the performance and
426cad347faSMaciej S. Szmigiero 		 * likelihood of MMIO exits
427cad347faSMaciej S. Szmigiero 		 */
428cad347faSMaciej S. Szmigiero 	}
429cad347faSMaciej S. Szmigiero 
430cad347faSMaciej S. Szmigiero 	GUEST_DONE();
431cad347faSMaciej S. Szmigiero }
432cad347faSMaciej S. Szmigiero 
433cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_map(void)
434cad347faSMaciej S. Szmigiero {
435cad347faSMaciej S. Szmigiero 	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
436cad347faSMaciej S. Szmigiero 
437cad347faSMaciej S. Szmigiero 	GUEST_SYNC(0);
438cad347faSMaciej S. Szmigiero 
439cad347faSMaciej S. Szmigiero 	guest_spin_until_start();
440cad347faSMaciej S. Szmigiero 
441cad347faSMaciej S. Szmigiero 	while (1) {
442cad347faSMaciej S. Szmigiero 		uintptr_t ptr;
443cad347faSMaciej S. Szmigiero 
444cad347faSMaciej S. Szmigiero 		for (ptr = MEM_TEST_GPA;
445cad347faSMaciej S. Szmigiero 		     ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2; ptr += 4096)
446cad347faSMaciej S. Szmigiero 			*(uint64_t *)ptr = MEM_TEST_VAL_1;
447cad347faSMaciej S. Szmigiero 
448cad347faSMaciej S. Szmigiero 		if (!guest_perform_sync())
449cad347faSMaciej S. Szmigiero 			break;
450cad347faSMaciej S. Szmigiero 
451cad347faSMaciej S. Szmigiero 		for (ptr = MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2;
452cad347faSMaciej S. Szmigiero 		     ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE; ptr += 4096)
453cad347faSMaciej S. Szmigiero 			*(uint64_t *)ptr = MEM_TEST_VAL_2;
454cad347faSMaciej S. Szmigiero 
455cad347faSMaciej S. Szmigiero 		if (!guest_perform_sync())
456cad347faSMaciej S. Szmigiero 			break;
457cad347faSMaciej S. Szmigiero 	}
458cad347faSMaciej S. Szmigiero 
459cad347faSMaciej S. Szmigiero 	GUEST_DONE();
460cad347faSMaciej S. Szmigiero }
461cad347faSMaciej S. Szmigiero 
462cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_unmap(void)
463cad347faSMaciej S. Szmigiero {
464cad347faSMaciej S. Szmigiero 	struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA;
465cad347faSMaciej S. Szmigiero 
466cad347faSMaciej S. Szmigiero 	GUEST_SYNC(0);
467cad347faSMaciej S. Szmigiero 
468cad347faSMaciej S. Szmigiero 	guest_spin_until_start();
469cad347faSMaciej S. Szmigiero 
470cad347faSMaciej S. Szmigiero 	while (1) {
471cad347faSMaciej S. Szmigiero 		uintptr_t ptr = MEM_TEST_GPA;
472cad347faSMaciej S. Szmigiero 
473cad347faSMaciej S. Szmigiero 		/*
474cad347faSMaciej S. Szmigiero 		 * We can afford to access (map) just a small number of pages
475cad347faSMaciej S. Szmigiero 		 * per host sync as otherwise the host will spend
476cad347faSMaciej S. Szmigiero 		 * a significant amount of its time waiting for the guest
477cad347faSMaciej S. Szmigiero 		 * (instead of doing unmap operations), so this will
478cad347faSMaciej S. Szmigiero 		 * effectively turn this test into a map performance test.
479cad347faSMaciej S. Szmigiero 		 *
480cad347faSMaciej S. Szmigiero 		 * Just access a single page to be on the safe side.
481cad347faSMaciej S. Szmigiero 		 */
482cad347faSMaciej S. Szmigiero 		*(uint64_t *)ptr = MEM_TEST_VAL_1;
483cad347faSMaciej S. Szmigiero 
484cad347faSMaciej S. Szmigiero 		if (!guest_perform_sync())
485cad347faSMaciej S. Szmigiero 			break;
486cad347faSMaciej S. Szmigiero 
487cad347faSMaciej S. Szmigiero 		ptr += MEM_TEST_UNMAP_SIZE / 2;
488cad347faSMaciej S. Szmigiero 		*(uint64_t *)ptr = MEM_TEST_VAL_2;
489cad347faSMaciej S. Szmigiero 
490cad347faSMaciej S. Szmigiero 		if (!guest_perform_sync())
491cad347faSMaciej S. Szmigiero 			break;
492cad347faSMaciej S. Szmigiero 	}
493cad347faSMaciej S. Szmigiero 
494cad347faSMaciej S. Szmigiero 	GUEST_DONE();
495cad347faSMaciej S. Szmigiero }
496cad347faSMaciej S. Szmigiero 
497cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_rw(void)
498cad347faSMaciej S. Szmigiero {
499cad347faSMaciej S. Szmigiero 	GUEST_SYNC(0);
500cad347faSMaciej S. Szmigiero 
501cad347faSMaciej S. Szmigiero 	guest_spin_until_start();
502cad347faSMaciej S. Szmigiero 
503cad347faSMaciej S. Szmigiero 	while (1) {
504cad347faSMaciej S. Szmigiero 		uintptr_t ptr;
505cad347faSMaciej S. Szmigiero 
506cad347faSMaciej S. Szmigiero 		for (ptr = MEM_TEST_GPA;
507cad347faSMaciej S. Szmigiero 		     ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += 4096)
508cad347faSMaciej S. Szmigiero 			*(uint64_t *)ptr = MEM_TEST_VAL_1;
509cad347faSMaciej S. Szmigiero 
510cad347faSMaciej S. Szmigiero 		if (!guest_perform_sync())
511cad347faSMaciej S. Szmigiero 			break;
512cad347faSMaciej S. Szmigiero 
513cad347faSMaciej S. Szmigiero 		for (ptr = MEM_TEST_GPA + 4096 / 2;
514cad347faSMaciej S. Szmigiero 		     ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += 4096) {
515cad347faSMaciej S. Szmigiero 			uint64_t val = *(uint64_t *)ptr;
516cad347faSMaciej S. Szmigiero 
517cad347faSMaciej S. Szmigiero 			GUEST_ASSERT_1(val == MEM_TEST_VAL_2, val);
518cad347faSMaciej S. Szmigiero 			*(uint64_t *)ptr = 0;
519cad347faSMaciej S. Szmigiero 		}
520cad347faSMaciej S. Szmigiero 
521cad347faSMaciej S. Szmigiero 		if (!guest_perform_sync())
522cad347faSMaciej S. Szmigiero 			break;
523cad347faSMaciej S. Szmigiero 	}
524cad347faSMaciej S. Szmigiero 
525cad347faSMaciej S. Szmigiero 	GUEST_DONE();
526cad347faSMaciej S. Szmigiero }
527cad347faSMaciej S. Szmigiero 
528cad347faSMaciej S. Szmigiero static bool test_memslot_move_prepare(struct vm_data *data,
529cad347faSMaciej S. Szmigiero 				      struct sync_area *sync,
530cad347faSMaciej S. Szmigiero 				      uint64_t *maxslots, bool isactive)
531cad347faSMaciej S. Szmigiero {
532cad347faSMaciej S. Szmigiero 	uint64_t movesrcgpa, movetestgpa;
533cad347faSMaciej S. Szmigiero 
534cad347faSMaciej S. Szmigiero 	movesrcgpa = vm_slot2gpa(data, data->nslots - 1);
535cad347faSMaciej S. Szmigiero 
536cad347faSMaciej S. Szmigiero 	if (isactive) {
537cad347faSMaciej S. Szmigiero 		uint64_t lastpages;
538cad347faSMaciej S. Szmigiero 
539cad347faSMaciej S. Szmigiero 		vm_gpa2hva(data, movesrcgpa, &lastpages);
540cad347faSMaciej S. Szmigiero 		if (lastpages < MEM_TEST_MOVE_SIZE_PAGES / 2) {
541cad347faSMaciej S. Szmigiero 			*maxslots = 0;
542cad347faSMaciej S. Szmigiero 			return false;
543cad347faSMaciej S. Szmigiero 		}
544cad347faSMaciej S. Szmigiero 	}
545cad347faSMaciej S. Szmigiero 
546cad347faSMaciej S. Szmigiero 	movetestgpa = movesrcgpa - (MEM_TEST_MOVE_SIZE / (isactive ? 2 : 1));
547cad347faSMaciej S. Szmigiero 	sync->move_area_ptr = (void *)movetestgpa;
548cad347faSMaciej S. Szmigiero 
549cad347faSMaciej S. Szmigiero 	if (isactive) {
550cad347faSMaciej S. Szmigiero 		data->mmio_ok = true;
551cad347faSMaciej S. Szmigiero 		data->mmio_gpa_min = movesrcgpa;
552cad347faSMaciej S. Szmigiero 		data->mmio_gpa_max = movesrcgpa + MEM_TEST_MOVE_SIZE / 2 - 1;
553cad347faSMaciej S. Szmigiero 	}
554cad347faSMaciej S. Szmigiero 
555cad347faSMaciej S. Szmigiero 	return true;
556cad347faSMaciej S. Szmigiero }
557cad347faSMaciej S. Szmigiero 
558cad347faSMaciej S. Szmigiero static bool test_memslot_move_prepare_active(struct vm_data *data,
559cad347faSMaciej S. Szmigiero 					     struct sync_area *sync,
560cad347faSMaciej S. Szmigiero 					     uint64_t *maxslots)
561cad347faSMaciej S. Szmigiero {
562cad347faSMaciej S. Szmigiero 	return test_memslot_move_prepare(data, sync, maxslots, true);
563cad347faSMaciej S. Szmigiero }
564cad347faSMaciej S. Szmigiero 
565cad347faSMaciej S. Szmigiero static bool test_memslot_move_prepare_inactive(struct vm_data *data,
566cad347faSMaciej S. Szmigiero 					       struct sync_area *sync,
567cad347faSMaciej S. Szmigiero 					       uint64_t *maxslots)
568cad347faSMaciej S. Szmigiero {
569cad347faSMaciej S. Szmigiero 	return test_memslot_move_prepare(data, sync, maxslots, false);
570cad347faSMaciej S. Szmigiero }
571cad347faSMaciej S. Szmigiero 
572cad347faSMaciej S. Szmigiero static void test_memslot_move_loop(struct vm_data *data, struct sync_area *sync)
573cad347faSMaciej S. Szmigiero {
574cad347faSMaciej S. Szmigiero 	uint64_t movesrcgpa;
575cad347faSMaciej S. Szmigiero 
576cad347faSMaciej S. Szmigiero 	movesrcgpa = vm_slot2gpa(data, data->nslots - 1);
577cad347faSMaciej S. Szmigiero 	vm_mem_region_move(data->vm, data->nslots - 1 + 1,
578cad347faSMaciej S. Szmigiero 			   MEM_TEST_MOVE_GPA_DEST);
579cad347faSMaciej S. Szmigiero 	vm_mem_region_move(data->vm, data->nslots - 1 + 1, movesrcgpa);
580cad347faSMaciej S. Szmigiero }
581cad347faSMaciej S. Szmigiero 
582cad347faSMaciej S. Szmigiero static void test_memslot_do_unmap(struct vm_data *data,
583cad347faSMaciej S. Szmigiero 				  uint64_t offsp, uint64_t count)
584cad347faSMaciej S. Szmigiero {
585cad347faSMaciej S. Szmigiero 	uint64_t gpa, ctr;
586cad347faSMaciej S. Szmigiero 
587cad347faSMaciej S. Szmigiero 	for (gpa = MEM_TEST_GPA + offsp * 4096, ctr = 0; ctr < count; ) {
588cad347faSMaciej S. Szmigiero 		uint64_t npages;
589cad347faSMaciej S. Szmigiero 		void *hva;
590cad347faSMaciej S. Szmigiero 		int ret;
591cad347faSMaciej S. Szmigiero 
592cad347faSMaciej S. Szmigiero 		hva = vm_gpa2hva(data, gpa, &npages);
593cad347faSMaciej S. Szmigiero 		TEST_ASSERT(npages, "Empty memory slot at gptr 0x%"PRIx64, gpa);
594cad347faSMaciej S. Szmigiero 		npages = min(npages, count - ctr);
595cad347faSMaciej S. Szmigiero 		ret = madvise(hva, npages * 4096, MADV_DONTNEED);
596cad347faSMaciej S. Szmigiero 		TEST_ASSERT(!ret,
597cad347faSMaciej S. Szmigiero 			    "madvise(%p, MADV_DONTNEED) on VM memory should not fail for gptr 0x%"PRIx64,
598cad347faSMaciej S. Szmigiero 			    hva, gpa);
599cad347faSMaciej S. Szmigiero 		ctr += npages;
600cad347faSMaciej S. Szmigiero 		gpa += npages * 4096;
601cad347faSMaciej S. Szmigiero 	}
602cad347faSMaciej S. Szmigiero 	TEST_ASSERT(ctr == count,
603cad347faSMaciej S. Szmigiero 		    "madvise(MADV_DONTNEED) should exactly cover all of the requested area");
604cad347faSMaciej S. Szmigiero }
605cad347faSMaciej S. Szmigiero 
606cad347faSMaciej S. Szmigiero static void test_memslot_map_unmap_check(struct vm_data *data,
607cad347faSMaciej S. Szmigiero 					 uint64_t offsp, uint64_t valexp)
608cad347faSMaciej S. Szmigiero {
609cad347faSMaciej S. Szmigiero 	uint64_t gpa;
610cad347faSMaciej S. Szmigiero 	uint64_t *val;
611cad347faSMaciej S. Szmigiero 
612cad347faSMaciej S. Szmigiero 	if (!map_unmap_verify)
613cad347faSMaciej S. Szmigiero 		return;
614cad347faSMaciej S. Szmigiero 
615cad347faSMaciej S. Szmigiero 	gpa = MEM_TEST_GPA + offsp * 4096;
616cad347faSMaciej S. Szmigiero 	val = (typeof(val))vm_gpa2hva(data, gpa, NULL);
617cad347faSMaciej S. Szmigiero 	TEST_ASSERT(*val == valexp,
618cad347faSMaciej S. Szmigiero 		    "Guest written values should read back correctly before unmap (%"PRIu64" vs %"PRIu64" @ %"PRIx64")",
619cad347faSMaciej S. Szmigiero 		    *val, valexp, gpa);
620cad347faSMaciej S. Szmigiero 	*val = 0;
621cad347faSMaciej S. Szmigiero }
622cad347faSMaciej S. Szmigiero 
623cad347faSMaciej S. Szmigiero static void test_memslot_map_loop(struct vm_data *data, struct sync_area *sync)
624cad347faSMaciej S. Szmigiero {
625cad347faSMaciej S. Szmigiero 	/*
626cad347faSMaciej S. Szmigiero 	 * Unmap the second half of the test area while guest writes to (maps)
627cad347faSMaciej S. Szmigiero 	 * the first half.
628cad347faSMaciej S. Szmigiero 	 */
629cad347faSMaciej S. Szmigiero 	test_memslot_do_unmap(data, MEM_TEST_MAP_SIZE_PAGES / 2,
630cad347faSMaciej S. Szmigiero 			      MEM_TEST_MAP_SIZE_PAGES / 2);
631cad347faSMaciej S. Szmigiero 
632cad347faSMaciej S. Szmigiero 	/*
633cad347faSMaciej S. Szmigiero 	 * Wait for the guest to finish writing the first half of the test
634cad347faSMaciej S. Szmigiero 	 * area, verify the written value on the first and the last page of
635cad347faSMaciej S. Szmigiero 	 * this area and then unmap it.
636cad347faSMaciej S. Szmigiero 	 * Meanwhile, the guest is writing to (mapping) the second half of
637cad347faSMaciej S. Szmigiero 	 * the test area.
638cad347faSMaciej S. Szmigiero 	 */
639cad347faSMaciej S. Szmigiero 	host_perform_sync(sync);
640cad347faSMaciej S. Szmigiero 	test_memslot_map_unmap_check(data, 0, MEM_TEST_VAL_1);
641cad347faSMaciej S. Szmigiero 	test_memslot_map_unmap_check(data,
642cad347faSMaciej S. Szmigiero 				     MEM_TEST_MAP_SIZE_PAGES / 2 - 1,
643cad347faSMaciej S. Szmigiero 				     MEM_TEST_VAL_1);
644cad347faSMaciej S. Szmigiero 	test_memslot_do_unmap(data, 0, MEM_TEST_MAP_SIZE_PAGES / 2);
645cad347faSMaciej S. Szmigiero 
646cad347faSMaciej S. Szmigiero 
647cad347faSMaciej S. Szmigiero 	/*
648cad347faSMaciej S. Szmigiero 	 * Wait for the guest to finish writing the second half of the test
649cad347faSMaciej S. Szmigiero 	 * area and verify the written value on the first and the last page
650cad347faSMaciej S. Szmigiero 	 * of this area.
651cad347faSMaciej S. Szmigiero 	 * The area will be unmapped at the beginning of the next loop
652cad347faSMaciej S. Szmigiero 	 * iteration.
653cad347faSMaciej S. Szmigiero 	 * Meanwhile, the guest is writing to (mapping) the first half of
654cad347faSMaciej S. Szmigiero 	 * the test area.
655cad347faSMaciej S. Szmigiero 	 */
656cad347faSMaciej S. Szmigiero 	host_perform_sync(sync);
657cad347faSMaciej S. Szmigiero 	test_memslot_map_unmap_check(data, MEM_TEST_MAP_SIZE_PAGES / 2,
658cad347faSMaciej S. Szmigiero 				     MEM_TEST_VAL_2);
659cad347faSMaciej S. Szmigiero 	test_memslot_map_unmap_check(data, MEM_TEST_MAP_SIZE_PAGES - 1,
660cad347faSMaciej S. Szmigiero 				     MEM_TEST_VAL_2);
661cad347faSMaciej S. Szmigiero }
662cad347faSMaciej S. Szmigiero 
663cad347faSMaciej S. Szmigiero static void test_memslot_unmap_loop_common(struct vm_data *data,
664cad347faSMaciej S. Szmigiero 					   struct sync_area *sync,
665cad347faSMaciej S. Szmigiero 					   uint64_t chunk)
666cad347faSMaciej S. Szmigiero {
667cad347faSMaciej S. Szmigiero 	uint64_t ctr;
668cad347faSMaciej S. Szmigiero 
669cad347faSMaciej S. Szmigiero 	/*
670cad347faSMaciej S. Szmigiero 	 * Wait for the guest to finish mapping page(s) in the first half
671cad347faSMaciej S. Szmigiero 	 * of the test area, verify the written value and then perform unmap
672cad347faSMaciej S. Szmigiero 	 * of this area.
673cad347faSMaciej S. Szmigiero 	 * Meanwhile, the guest is writing to (mapping) page(s) in the second
674cad347faSMaciej S. Szmigiero 	 * half of the test area.
675cad347faSMaciej S. Szmigiero 	 */
676cad347faSMaciej S. Szmigiero 	host_perform_sync(sync);
677cad347faSMaciej S. Szmigiero 	test_memslot_map_unmap_check(data, 0, MEM_TEST_VAL_1);
678cad347faSMaciej S. Szmigiero 	for (ctr = 0; ctr < MEM_TEST_UNMAP_SIZE_PAGES / 2; ctr += chunk)
679cad347faSMaciej S. Szmigiero 		test_memslot_do_unmap(data, ctr, chunk);
680cad347faSMaciej S. Szmigiero 
681cad347faSMaciej S. Szmigiero 	/* Likewise, but for the opposite host / guest areas */
682cad347faSMaciej S. Szmigiero 	host_perform_sync(sync);
683cad347faSMaciej S. Szmigiero 	test_memslot_map_unmap_check(data, MEM_TEST_UNMAP_SIZE_PAGES / 2,
684cad347faSMaciej S. Szmigiero 				     MEM_TEST_VAL_2);
685cad347faSMaciej S. Szmigiero 	for (ctr = MEM_TEST_UNMAP_SIZE_PAGES / 2;
686cad347faSMaciej S. Szmigiero 	     ctr < MEM_TEST_UNMAP_SIZE_PAGES; ctr += chunk)
687cad347faSMaciej S. Szmigiero 		test_memslot_do_unmap(data, ctr, chunk);
688cad347faSMaciej S. Szmigiero }
689cad347faSMaciej S. Szmigiero 
690cad347faSMaciej S. Szmigiero static void test_memslot_unmap_loop(struct vm_data *data,
691cad347faSMaciej S. Szmigiero 				    struct sync_area *sync)
692cad347faSMaciej S. Szmigiero {
693cad347faSMaciej S. Szmigiero 	test_memslot_unmap_loop_common(data, sync, 1);
694cad347faSMaciej S. Szmigiero }
695cad347faSMaciej S. Szmigiero 
696cad347faSMaciej S. Szmigiero static void test_memslot_unmap_loop_chunked(struct vm_data *data,
697cad347faSMaciej S. Szmigiero 					    struct sync_area *sync)
698cad347faSMaciej S. Szmigiero {
699cad347faSMaciej S. Szmigiero 	test_memslot_unmap_loop_common(data, sync, MEM_TEST_UNMAP_CHUNK_PAGES);
700cad347faSMaciej S. Szmigiero }
701cad347faSMaciej S. Szmigiero 
702cad347faSMaciej S. Szmigiero static void test_memslot_rw_loop(struct vm_data *data, struct sync_area *sync)
703cad347faSMaciej S. Szmigiero {
704cad347faSMaciej S. Szmigiero 	uint64_t gptr;
705cad347faSMaciej S. Szmigiero 
706cad347faSMaciej S. Szmigiero 	for (gptr = MEM_TEST_GPA + 4096 / 2;
707cad347faSMaciej S. Szmigiero 	     gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += 4096)
708cad347faSMaciej S. Szmigiero 		*(uint64_t *)vm_gpa2hva(data, gptr, NULL) = MEM_TEST_VAL_2;
709cad347faSMaciej S. Szmigiero 
710cad347faSMaciej S. Szmigiero 	host_perform_sync(sync);
711cad347faSMaciej S. Szmigiero 
712cad347faSMaciej S. Szmigiero 	for (gptr = MEM_TEST_GPA;
713cad347faSMaciej S. Szmigiero 	     gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += 4096) {
714cad347faSMaciej S. Szmigiero 		uint64_t *vptr = (typeof(vptr))vm_gpa2hva(data, gptr, NULL);
715cad347faSMaciej S. Szmigiero 		uint64_t val = *vptr;
716cad347faSMaciej S. Szmigiero 
717cad347faSMaciej S. Szmigiero 		TEST_ASSERT(val == MEM_TEST_VAL_1,
718cad347faSMaciej S. Szmigiero 			    "Guest written values should read back correctly (is %"PRIu64" @ %"PRIx64")",
719cad347faSMaciej S. Szmigiero 			    val, gptr);
720cad347faSMaciej S. Szmigiero 		*vptr = 0;
721cad347faSMaciej S. Szmigiero 	}
722cad347faSMaciej S. Szmigiero 
723cad347faSMaciej S. Szmigiero 	host_perform_sync(sync);
724cad347faSMaciej S. Szmigiero }
725cad347faSMaciej S. Szmigiero 
726cad347faSMaciej S. Szmigiero struct test_data {
727cad347faSMaciej S. Szmigiero 	const char *name;
728cad347faSMaciej S. Szmigiero 	uint64_t mem_size;
729cad347faSMaciej S. Szmigiero 	void (*guest_code)(void);
730cad347faSMaciej S. Szmigiero 	bool (*prepare)(struct vm_data *data, struct sync_area *sync,
731cad347faSMaciej S. Szmigiero 			uint64_t *maxslots);
732cad347faSMaciej S. Szmigiero 	void (*loop)(struct vm_data *data, struct sync_area *sync);
733cad347faSMaciej S. Szmigiero };
734cad347faSMaciej S. Szmigiero 
735cad347faSMaciej S. Szmigiero static bool test_execute(int nslots, uint64_t *maxslots,
736cad347faSMaciej S. Szmigiero 			 unsigned int maxtime,
737cad347faSMaciej S. Szmigiero 			 const struct test_data *tdata,
738cad347faSMaciej S. Szmigiero 			 uint64_t *nloops,
739cad347faSMaciej S. Szmigiero 			 struct timespec *slot_runtime,
740cad347faSMaciej S. Szmigiero 			 struct timespec *guest_runtime)
741cad347faSMaciej S. Szmigiero {
742cad347faSMaciej S. Szmigiero 	uint64_t mem_size = tdata->mem_size ? : MEM_SIZE_PAGES;
743cad347faSMaciej S. Szmigiero 	struct vm_data *data;
744cad347faSMaciej S. Szmigiero 	struct sync_area *sync;
745cad347faSMaciej S. Szmigiero 	struct timespec tstart;
746cad347faSMaciej S. Szmigiero 	bool ret = true;
747cad347faSMaciej S. Szmigiero 
748cad347faSMaciej S. Szmigiero 	data = alloc_vm();
749cad347faSMaciej S. Szmigiero 	if (!prepare_vm(data, nslots, maxslots, tdata->guest_code,
750cad347faSMaciej S. Szmigiero 			mem_size, slot_runtime)) {
751cad347faSMaciej S. Szmigiero 		ret = false;
752cad347faSMaciej S. Szmigiero 		goto exit_free;
753cad347faSMaciej S. Szmigiero 	}
754cad347faSMaciej S. Szmigiero 
755cad347faSMaciej S. Szmigiero 	sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL);
756cad347faSMaciej S. Szmigiero 
757cad347faSMaciej S. Szmigiero 	if (tdata->prepare &&
758cad347faSMaciej S. Szmigiero 	    !tdata->prepare(data, sync, maxslots)) {
759cad347faSMaciej S. Szmigiero 		ret = false;
760cad347faSMaciej S. Szmigiero 		goto exit_free;
761cad347faSMaciej S. Szmigiero 	}
762cad347faSMaciej S. Szmigiero 
763cad347faSMaciej S. Szmigiero 	launch_vm(data);
764cad347faSMaciej S. Szmigiero 
765cad347faSMaciej S. Szmigiero 	clock_gettime(CLOCK_MONOTONIC, &tstart);
766cad347faSMaciej S. Szmigiero 	let_guest_run(sync);
767cad347faSMaciej S. Szmigiero 
768cad347faSMaciej S. Szmigiero 	while (1) {
769cad347faSMaciej S. Szmigiero 		*guest_runtime = timespec_elapsed(tstart);
770cad347faSMaciej S. Szmigiero 		if (guest_runtime->tv_sec >= maxtime)
771cad347faSMaciej S. Szmigiero 			break;
772cad347faSMaciej S. Szmigiero 
773cad347faSMaciej S. Szmigiero 		tdata->loop(data, sync);
774cad347faSMaciej S. Szmigiero 
775cad347faSMaciej S. Szmigiero 		(*nloops)++;
776cad347faSMaciej S. Szmigiero 	}
777cad347faSMaciej S. Szmigiero 
778cad347faSMaciej S. Szmigiero 	make_guest_exit(sync);
779cad347faSMaciej S. Szmigiero 	wait_guest_exit(data);
780cad347faSMaciej S. Szmigiero 
781cad347faSMaciej S. Szmigiero exit_free:
782cad347faSMaciej S. Szmigiero 	free_vm(data);
783cad347faSMaciej S. Szmigiero 
784cad347faSMaciej S. Szmigiero 	return ret;
785cad347faSMaciej S. Szmigiero }
786cad347faSMaciej S. Szmigiero 
787cad347faSMaciej S. Szmigiero static const struct test_data tests[] = {
788cad347faSMaciej S. Szmigiero 	{
789cad347faSMaciej S. Szmigiero 		.name = "map",
790cad347faSMaciej S. Szmigiero 		.mem_size = MEM_SIZE_MAP_PAGES,
791cad347faSMaciej S. Szmigiero 		.guest_code = guest_code_test_memslot_map,
792cad347faSMaciej S. Szmigiero 		.loop = test_memslot_map_loop,
793cad347faSMaciej S. Szmigiero 	},
794cad347faSMaciej S. Szmigiero 	{
795cad347faSMaciej S. Szmigiero 		.name = "unmap",
796cad347faSMaciej S. Szmigiero 		.mem_size = MEM_TEST_UNMAP_SIZE_PAGES + 1,
797cad347faSMaciej S. Szmigiero 		.guest_code = guest_code_test_memslot_unmap,
798cad347faSMaciej S. Szmigiero 		.loop = test_memslot_unmap_loop,
799cad347faSMaciej S. Szmigiero 	},
800cad347faSMaciej S. Szmigiero 	{
801cad347faSMaciej S. Szmigiero 		.name = "unmap chunked",
802cad347faSMaciej S. Szmigiero 		.mem_size = MEM_TEST_UNMAP_SIZE_PAGES + 1,
803cad347faSMaciej S. Szmigiero 		.guest_code = guest_code_test_memslot_unmap,
804cad347faSMaciej S. Szmigiero 		.loop = test_memslot_unmap_loop_chunked,
805cad347faSMaciej S. Szmigiero 	},
806cad347faSMaciej S. Szmigiero 	{
807cad347faSMaciej S. Szmigiero 		.name = "move active area",
808cad347faSMaciej S. Szmigiero 		.guest_code = guest_code_test_memslot_move,
809cad347faSMaciej S. Szmigiero 		.prepare = test_memslot_move_prepare_active,
810cad347faSMaciej S. Szmigiero 		.loop = test_memslot_move_loop,
811cad347faSMaciej S. Szmigiero 	},
812cad347faSMaciej S. Szmigiero 	{
813cad347faSMaciej S. Szmigiero 		.name = "move inactive area",
814cad347faSMaciej S. Szmigiero 		.guest_code = guest_code_test_memslot_move,
815cad347faSMaciej S. Szmigiero 		.prepare = test_memslot_move_prepare_inactive,
816cad347faSMaciej S. Szmigiero 		.loop = test_memslot_move_loop,
817cad347faSMaciej S. Szmigiero 	},
818cad347faSMaciej S. Szmigiero 	{
819cad347faSMaciej S. Szmigiero 		.name = "RW",
820cad347faSMaciej S. Szmigiero 		.guest_code = guest_code_test_memslot_rw,
821cad347faSMaciej S. Szmigiero 		.loop = test_memslot_rw_loop
822cad347faSMaciej S. Szmigiero 	},
823cad347faSMaciej S. Szmigiero };
824cad347faSMaciej S. Szmigiero 
825cad347faSMaciej S. Szmigiero #define NTESTS ARRAY_SIZE(tests)
826cad347faSMaciej S. Szmigiero 
827cad347faSMaciej S. Szmigiero struct test_args {
828cad347faSMaciej S. Szmigiero 	int tfirst;
829cad347faSMaciej S. Szmigiero 	int tlast;
830cad347faSMaciej S. Szmigiero 	int nslots;
831cad347faSMaciej S. Szmigiero 	int seconds;
832cad347faSMaciej S. Szmigiero 	int runs;
833cad347faSMaciej S. Szmigiero };
834cad347faSMaciej S. Szmigiero 
835cad347faSMaciej S. Szmigiero static void help(char *name, struct test_args *targs)
836cad347faSMaciej S. Szmigiero {
837cad347faSMaciej S. Szmigiero 	int ctr;
838cad347faSMaciej S. Szmigiero 
839cad347faSMaciej S. Szmigiero 	pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count]\n",
840cad347faSMaciej S. Szmigiero 		name);
841cad347faSMaciej S. Szmigiero 	pr_info(" -h: print this help screen.\n");
842cad347faSMaciej S. Szmigiero 	pr_info(" -v: enable verbose mode (not for benchmarking).\n");
843cad347faSMaciej S. Szmigiero 	pr_info(" -d: enable extra debug checks.\n");
844cad347faSMaciej S. Szmigiero 	pr_info(" -s: specify memslot count cap (-1 means no cap; currently: %i)\n",
845cad347faSMaciej S. Szmigiero 		targs->nslots);
846cad347faSMaciej S. Szmigiero 	pr_info(" -f: specify the first test to run (currently: %i; max %zu)\n",
847cad347faSMaciej S. Szmigiero 		targs->tfirst, NTESTS - 1);
848cad347faSMaciej S. Szmigiero 	pr_info(" -e: specify the last test to run (currently: %i; max %zu)\n",
849cad347faSMaciej S. Szmigiero 		targs->tlast, NTESTS - 1);
850cad347faSMaciej S. Szmigiero 	pr_info(" -l: specify the test length in seconds (currently: %i)\n",
851cad347faSMaciej S. Szmigiero 		targs->seconds);
852cad347faSMaciej S. Szmigiero 	pr_info(" -r: specify the number of runs per test (currently: %i)\n",
853cad347faSMaciej S. Szmigiero 		targs->runs);
854cad347faSMaciej S. Szmigiero 
855cad347faSMaciej S. Szmigiero 	pr_info("\nAvailable tests:\n");
856cad347faSMaciej S. Szmigiero 	for (ctr = 0; ctr < NTESTS; ctr++)
857cad347faSMaciej S. Szmigiero 		pr_info("%d: %s\n", ctr, tests[ctr].name);
858cad347faSMaciej S. Szmigiero }
859cad347faSMaciej S. Szmigiero 
860cad347faSMaciej S. Szmigiero static bool parse_args(int argc, char *argv[],
861cad347faSMaciej S. Szmigiero 		       struct test_args *targs)
862cad347faSMaciej S. Szmigiero {
863cad347faSMaciej S. Szmigiero 	int opt;
864cad347faSMaciej S. Szmigiero 
865cad347faSMaciej S. Szmigiero 	while ((opt = getopt(argc, argv, "hvds:f:e:l:r:")) != -1) {
866cad347faSMaciej S. Szmigiero 		switch (opt) {
867cad347faSMaciej S. Szmigiero 		case 'h':
868cad347faSMaciej S. Szmigiero 		default:
869cad347faSMaciej S. Szmigiero 			help(argv[0], targs);
870cad347faSMaciej S. Szmigiero 			return false;
871cad347faSMaciej S. Szmigiero 		case 'v':
872cad347faSMaciej S. Szmigiero 			verbose = true;
873cad347faSMaciej S. Szmigiero 			break;
874cad347faSMaciej S. Szmigiero 		case 'd':
875cad347faSMaciej S. Szmigiero 			map_unmap_verify = true;
876cad347faSMaciej S. Szmigiero 			break;
877cad347faSMaciej S. Szmigiero 		case 's':
878cad347faSMaciej S. Szmigiero 			targs->nslots = atoi(optarg);
879cad347faSMaciej S. Szmigiero 			if (targs->nslots <= 0 && targs->nslots != -1) {
880cad347faSMaciej S. Szmigiero 				pr_info("Slot count cap has to be positive or -1 for no cap\n");
881cad347faSMaciej S. Szmigiero 				return false;
882cad347faSMaciej S. Szmigiero 			}
883cad347faSMaciej S. Szmigiero 			break;
884cad347faSMaciej S. Szmigiero 		case 'f':
885cad347faSMaciej S. Szmigiero 			targs->tfirst = atoi(optarg);
886cad347faSMaciej S. Szmigiero 			if (targs->tfirst < 0) {
887cad347faSMaciej S. Szmigiero 				pr_info("First test to run has to be non-negative\n");
888cad347faSMaciej S. Szmigiero 				return false;
889cad347faSMaciej S. Szmigiero 			}
890cad347faSMaciej S. Szmigiero 			break;
891cad347faSMaciej S. Szmigiero 		case 'e':
892cad347faSMaciej S. Szmigiero 			targs->tlast = atoi(optarg);
893cad347faSMaciej S. Szmigiero 			if (targs->tlast < 0 || targs->tlast >= NTESTS) {
894cad347faSMaciej S. Szmigiero 				pr_info("Last test to run has to be non-negative and less than %zu\n",
895cad347faSMaciej S. Szmigiero 					NTESTS);
896cad347faSMaciej S. Szmigiero 				return false;
897cad347faSMaciej S. Szmigiero 			}
898cad347faSMaciej S. Szmigiero 			break;
899cad347faSMaciej S. Szmigiero 		case 'l':
900cad347faSMaciej S. Szmigiero 			targs->seconds = atoi(optarg);
901cad347faSMaciej S. Szmigiero 			if (targs->seconds < 0) {
902cad347faSMaciej S. Szmigiero 				pr_info("Test length in seconds has to be non-negative\n");
903cad347faSMaciej S. Szmigiero 				return false;
904cad347faSMaciej S. Szmigiero 			}
905cad347faSMaciej S. Szmigiero 			break;
906cad347faSMaciej S. Szmigiero 		case 'r':
907cad347faSMaciej S. Szmigiero 			targs->runs = atoi(optarg);
908cad347faSMaciej S. Szmigiero 			if (targs->runs <= 0) {
909cad347faSMaciej S. Szmigiero 				pr_info("Runs per test has to be positive\n");
910cad347faSMaciej S. Szmigiero 				return false;
911cad347faSMaciej S. Szmigiero 			}
912cad347faSMaciej S. Szmigiero 			break;
913cad347faSMaciej S. Szmigiero 		}
914cad347faSMaciej S. Szmigiero 	}
915cad347faSMaciej S. Szmigiero 
916cad347faSMaciej S. Szmigiero 	if (optind < argc) {
917cad347faSMaciej S. Szmigiero 		help(argv[0], targs);
918cad347faSMaciej S. Szmigiero 		return false;
919cad347faSMaciej S. Szmigiero 	}
920cad347faSMaciej S. Szmigiero 
921cad347faSMaciej S. Szmigiero 	if (targs->tfirst > targs->tlast) {
922cad347faSMaciej S. Szmigiero 		pr_info("First test to run cannot be greater than the last test to run\n");
923cad347faSMaciej S. Szmigiero 		return false;
924cad347faSMaciej S. Szmigiero 	}
925cad347faSMaciej S. Szmigiero 
926cad347faSMaciej S. Szmigiero 	return true;
927cad347faSMaciej S. Szmigiero }
928cad347faSMaciej S. Szmigiero 
929cad347faSMaciej S. Szmigiero struct test_result {
930cad347faSMaciej S. Szmigiero 	struct timespec slot_runtime, guest_runtime, iter_runtime;
931cad347faSMaciej S. Szmigiero 	int64_t slottimens, runtimens;
932cad347faSMaciej S. Szmigiero 	uint64_t nloops;
933cad347faSMaciej S. Szmigiero };
934cad347faSMaciej S. Szmigiero 
935cad347faSMaciej S. Szmigiero static bool test_loop(const struct test_data *data,
936cad347faSMaciej S. Szmigiero 		      const struct test_args *targs,
937cad347faSMaciej S. Szmigiero 		      struct test_result *rbestslottime,
938cad347faSMaciej S. Szmigiero 		      struct test_result *rbestruntime)
939cad347faSMaciej S. Szmigiero {
940cad347faSMaciej S. Szmigiero 	uint64_t maxslots;
941cad347faSMaciej S. Szmigiero 	struct test_result result;
942cad347faSMaciej S. Szmigiero 
943cad347faSMaciej S. Szmigiero 	result.nloops = 0;
944cad347faSMaciej S. Szmigiero 	if (!test_execute(targs->nslots, &maxslots, targs->seconds, data,
945cad347faSMaciej S. Szmigiero 			  &result.nloops,
946cad347faSMaciej S. Szmigiero 			  &result.slot_runtime, &result.guest_runtime)) {
947cad347faSMaciej S. Szmigiero 		if (maxslots)
948cad347faSMaciej S. Szmigiero 			pr_info("Memslot count too high for this test, decrease the cap (max is %"PRIu64")\n",
949cad347faSMaciej S. Szmigiero 				maxslots);
950cad347faSMaciej S. Szmigiero 		else
951cad347faSMaciej S. Szmigiero 			pr_info("Memslot count may be too high for this test, try adjusting the cap\n");
952cad347faSMaciej S. Szmigiero 
953cad347faSMaciej S. Szmigiero 		return false;
954cad347faSMaciej S. Szmigiero 	}
955cad347faSMaciej S. Szmigiero 
956cad347faSMaciej S. Szmigiero 	pr_info("Test took %ld.%.9lds for slot setup + %ld.%.9lds all iterations\n",
957cad347faSMaciej S. Szmigiero 		result.slot_runtime.tv_sec, result.slot_runtime.tv_nsec,
958cad347faSMaciej S. Szmigiero 		result.guest_runtime.tv_sec, result.guest_runtime.tv_nsec);
959cad347faSMaciej S. Szmigiero 	if (!result.nloops) {
960cad347faSMaciej S. Szmigiero 		pr_info("No full loops done - too short test time or system too loaded?\n");
961cad347faSMaciej S. Szmigiero 		return true;
962cad347faSMaciej S. Szmigiero 	}
963cad347faSMaciej S. Szmigiero 
964cad347faSMaciej S. Szmigiero 	result.iter_runtime = timespec_div(result.guest_runtime,
965cad347faSMaciej S. Szmigiero 					   result.nloops);
966cad347faSMaciej S. Szmigiero 	pr_info("Done %"PRIu64" iterations, avg %ld.%.9lds each\n",
967cad347faSMaciej S. Szmigiero 		result.nloops,
968cad347faSMaciej S. Szmigiero 		result.iter_runtime.tv_sec,
969cad347faSMaciej S. Szmigiero 		result.iter_runtime.tv_nsec);
970cad347faSMaciej S. Szmigiero 	result.slottimens = timespec_to_ns(result.slot_runtime);
971cad347faSMaciej S. Szmigiero 	result.runtimens = timespec_to_ns(result.iter_runtime);
972cad347faSMaciej S. Szmigiero 
973cad347faSMaciej S. Szmigiero 	/*
974cad347faSMaciej S. Szmigiero 	 * Only rank the slot setup time for tests using the whole test memory
975cad347faSMaciej S. Szmigiero 	 * area so they are comparable
976cad347faSMaciej S. Szmigiero 	 */
977cad347faSMaciej S. Szmigiero 	if (!data->mem_size &&
978cad347faSMaciej S. Szmigiero 	    (!rbestslottime->slottimens ||
979cad347faSMaciej S. Szmigiero 	     result.slottimens < rbestslottime->slottimens))
980cad347faSMaciej S. Szmigiero 		*rbestslottime = result;
981cad347faSMaciej S. Szmigiero 	if (!rbestruntime->runtimens ||
982cad347faSMaciej S. Szmigiero 	    result.runtimens < rbestruntime->runtimens)
983cad347faSMaciej S. Szmigiero 		*rbestruntime = result;
984cad347faSMaciej S. Szmigiero 
985cad347faSMaciej S. Szmigiero 	return true;
986cad347faSMaciej S. Szmigiero }
987cad347faSMaciej S. Szmigiero 
988cad347faSMaciej S. Szmigiero int main(int argc, char *argv[])
989cad347faSMaciej S. Szmigiero {
990cad347faSMaciej S. Szmigiero 	struct test_args targs = {
991cad347faSMaciej S. Szmigiero 		.tfirst = 0,
992cad347faSMaciej S. Szmigiero 		.tlast = NTESTS - 1,
993cad347faSMaciej S. Szmigiero 		.nslots = -1,
994cad347faSMaciej S. Szmigiero 		.seconds = 5,
995fb0f9479SPaolo Bonzini 		.runs = 1,
996cad347faSMaciej S. Szmigiero 	};
997cad347faSMaciej S. Szmigiero 	struct test_result rbestslottime;
998cad347faSMaciej S. Szmigiero 	int tctr;
999cad347faSMaciej S. Szmigiero 
1000cad347faSMaciej S. Szmigiero 	/* Tell stdout not to buffer its content */
1001cad347faSMaciej S. Szmigiero 	setbuf(stdout, NULL);
1002cad347faSMaciej S. Szmigiero 
1003cad347faSMaciej S. Szmigiero 	if (!parse_args(argc, argv, &targs))
1004cad347faSMaciej S. Szmigiero 		return -1;
1005cad347faSMaciej S. Szmigiero 
1006cad347faSMaciej S. Szmigiero 	rbestslottime.slottimens = 0;
1007cad347faSMaciej S. Szmigiero 	for (tctr = targs.tfirst; tctr <= targs.tlast; tctr++) {
1008cad347faSMaciej S. Szmigiero 		const struct test_data *data = &tests[tctr];
1009cad347faSMaciej S. Szmigiero 		unsigned int runctr;
1010cad347faSMaciej S. Szmigiero 		struct test_result rbestruntime;
1011cad347faSMaciej S. Szmigiero 
1012cad347faSMaciej S. Szmigiero 		if (tctr > targs.tfirst)
1013cad347faSMaciej S. Szmigiero 			pr_info("\n");
1014cad347faSMaciej S. Szmigiero 
1015cad347faSMaciej S. Szmigiero 		pr_info("Testing %s performance with %i runs, %d seconds each\n",
1016cad347faSMaciej S. Szmigiero 			data->name, targs.runs, targs.seconds);
1017cad347faSMaciej S. Szmigiero 
1018cad347faSMaciej S. Szmigiero 		rbestruntime.runtimens = 0;
1019cad347faSMaciej S. Szmigiero 		for (runctr = 0; runctr < targs.runs; runctr++)
1020cad347faSMaciej S. Szmigiero 			if (!test_loop(data, &targs,
1021cad347faSMaciej S. Szmigiero 				       &rbestslottime, &rbestruntime))
1022cad347faSMaciej S. Szmigiero 				break;
1023cad347faSMaciej S. Szmigiero 
1024cad347faSMaciej S. Szmigiero 		if (rbestruntime.runtimens)
1025cad347faSMaciej S. Szmigiero 			pr_info("Best runtime result was %ld.%.9lds per iteration (with %"PRIu64" iterations)\n",
1026cad347faSMaciej S. Szmigiero 				rbestruntime.iter_runtime.tv_sec,
1027cad347faSMaciej S. Szmigiero 				rbestruntime.iter_runtime.tv_nsec,
1028cad347faSMaciej S. Szmigiero 				rbestruntime.nloops);
1029cad347faSMaciej S. Szmigiero 	}
1030cad347faSMaciej S. Szmigiero 
1031cad347faSMaciej S. Szmigiero 	if (rbestslottime.slottimens)
1032cad347faSMaciej S. Szmigiero 		pr_info("Best slot setup time for the whole test area was %ld.%.9lds\n",
1033cad347faSMaciej S. Szmigiero 			rbestslottime.slot_runtime.tv_sec,
1034cad347faSMaciej S. Szmigiero 			rbestslottime.slot_runtime.tv_nsec);
1035cad347faSMaciej S. Szmigiero 
1036cad347faSMaciej S. Szmigiero 	return 0;
1037cad347faSMaciej S. Szmigiero }
1038