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