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> 2388a64e65SGavin Shan #include <linux/sizes.h> 24cad347faSMaciej S. Szmigiero 25cad347faSMaciej S. Szmigiero #include <test_util.h> 26cad347faSMaciej S. Szmigiero #include <kvm_util.h> 27cad347faSMaciej S. Szmigiero #include <processor.h> 28cad347faSMaciej S. Szmigiero 2988a64e65SGavin Shan #define MEM_EXTRA_SIZE SZ_64K 3088a64e65SGavin Shan 3188a64e65SGavin Shan #define MEM_SIZE (SZ_512M + MEM_EXTRA_SIZE) 3288a64e65SGavin Shan #define MEM_GPA SZ_256M 33cad347faSMaciej S. Szmigiero #define MEM_AUX_GPA MEM_GPA 34cad347faSMaciej S. Szmigiero #define MEM_SYNC_GPA MEM_AUX_GPA 3588a64e65SGavin Shan #define MEM_TEST_GPA (MEM_AUX_GPA + MEM_EXTRA_SIZE) 3688a64e65SGavin Shan #define MEM_TEST_SIZE (MEM_SIZE - MEM_EXTRA_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 */ 4488a64e65SGavin Shan #define MEM_SIZE_MAP (SZ_32M + MEM_EXTRA_SIZE) 4588a64e65SGavin Shan #define MEM_TEST_MAP_SIZE (MEM_SIZE_MAP - MEM_EXTRA_SIZE) 46cad347faSMaciej S. Szmigiero 47cad347faSMaciej S. Szmigiero /* 48cad347faSMaciej S. Szmigiero * 128 MiB is min size that fills 32k slots with at least one page in each 49cad347faSMaciej S. Szmigiero * while at the same time gets 100+ iterations in such test 508675c6f2SGavin Shan * 518675c6f2SGavin Shan * 2 MiB chunk size like a typical huge page 52cad347faSMaciej S. Szmigiero */ 5388a64e65SGavin Shan #define MEM_TEST_UNMAP_SIZE SZ_128M 5488a64e65SGavin Shan #define MEM_TEST_UNMAP_CHUNK_SIZE SZ_2M 55cad347faSMaciej S. Szmigiero 56cad347faSMaciej S. Szmigiero /* 57cad347faSMaciej S. Szmigiero * For the move active test the middle of the test area is placed on 58cad347faSMaciej S. Szmigiero * a memslot boundary: half lies in the memslot being moved, half in 59cad347faSMaciej S. Szmigiero * other memslot(s). 60cad347faSMaciej S. Szmigiero * 618675c6f2SGavin Shan * We have different number of memory slots, excluding the reserved 628675c6f2SGavin Shan * memory slot 0, on various architectures and configurations. The 638675c6f2SGavin Shan * memory size in this test is calculated by picking the maximal 648675c6f2SGavin Shan * last memory slot's memory size, with alignment to the largest 658675c6f2SGavin Shan * supported page size (64KB). In this way, the selected memory 668675c6f2SGavin Shan * size for this test is compatible with test_memslot_move_prepare(). 678675c6f2SGavin Shan * 688675c6f2SGavin Shan * architecture slots memory-per-slot memory-on-last-slot 698675c6f2SGavin Shan * -------------------------------------------------------------- 7088a64e65SGavin Shan * x86-4KB 32763 16KB 160KB 7188a64e65SGavin Shan * arm64-4KB 32766 16KB 112KB 7288a64e65SGavin Shan * arm64-16KB 32766 16KB 112KB 7388a64e65SGavin Shan * arm64-64KB 8192 64KB 128KB 74cad347faSMaciej S. Szmigiero */ 7588a64e65SGavin Shan #define MEM_TEST_MOVE_SIZE (3 * SZ_64K) 76cad347faSMaciej S. Szmigiero #define MEM_TEST_MOVE_GPA_DEST (MEM_GPA + MEM_SIZE) 77cad347faSMaciej S. Szmigiero static_assert(MEM_TEST_MOVE_SIZE <= MEM_TEST_SIZE, 78cad347faSMaciej S. Szmigiero "invalid move test region size"); 79cad347faSMaciej S. Szmigiero 80cad347faSMaciej S. Szmigiero #define MEM_TEST_VAL_1 0x1122334455667788 81cad347faSMaciej S. Szmigiero #define MEM_TEST_VAL_2 0x99AABBCCDDEEFF00 82cad347faSMaciej S. Szmigiero 83cad347faSMaciej S. Szmigiero struct vm_data { 84cad347faSMaciej S. Szmigiero struct kvm_vm *vm; 85e82e630bSSean Christopherson struct kvm_vcpu *vcpu; 86cad347faSMaciej S. Szmigiero pthread_t vcpu_thread; 87cad347faSMaciej S. Szmigiero uint32_t nslots; 88cad347faSMaciej S. Szmigiero uint64_t npages; 89cad347faSMaciej S. Szmigiero uint64_t pages_per_slot; 90cad347faSMaciej S. Szmigiero void **hva_slots; 91cad347faSMaciej S. Szmigiero bool mmio_ok; 92cad347faSMaciej S. Szmigiero uint64_t mmio_gpa_min; 93cad347faSMaciej S. Szmigiero uint64_t mmio_gpa_max; 94cad347faSMaciej S. Szmigiero }; 95cad347faSMaciej S. Szmigiero 96cad347faSMaciej S. Szmigiero struct sync_area { 978675c6f2SGavin Shan uint32_t guest_page_size; 98cad347faSMaciej S. Szmigiero atomic_bool start_flag; 99cad347faSMaciej S. Szmigiero atomic_bool exit_flag; 100cad347faSMaciej S. Szmigiero atomic_bool sync_flag; 101cad347faSMaciej S. Szmigiero void *move_area_ptr; 102cad347faSMaciej S. Szmigiero }; 103cad347faSMaciej S. Szmigiero 104cad347faSMaciej S. Szmigiero /* 105cad347faSMaciej S. Szmigiero * Technically, we need also for the atomic bool to be address-free, which 106cad347faSMaciej S. Szmigiero * is recommended, but not strictly required, by C11 for lockless 107cad347faSMaciej S. Szmigiero * implementations. 108cad347faSMaciej S. Szmigiero * However, in practice both GCC and Clang fulfill this requirement on 109cad347faSMaciej S. Szmigiero * all KVM-supported platforms. 110cad347faSMaciej S. Szmigiero */ 111cad347faSMaciej S. Szmigiero static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "atomic bool is not lockless"); 112cad347faSMaciej S. Szmigiero 113cad347faSMaciej S. Szmigiero static sem_t vcpu_ready; 114cad347faSMaciej S. Szmigiero 115cad347faSMaciej S. Szmigiero static bool map_unmap_verify; 116cad347faSMaciej S. Szmigiero 117cad347faSMaciej S. Szmigiero static bool verbose; 118cad347faSMaciej S. Szmigiero #define pr_info_v(...) \ 119cad347faSMaciej S. Szmigiero do { \ 120cad347faSMaciej S. Szmigiero if (verbose) \ 121cad347faSMaciej S. Szmigiero pr_info(__VA_ARGS__); \ 122cad347faSMaciej S. Szmigiero } while (0) 123cad347faSMaciej S. Szmigiero 124e82e630bSSean Christopherson static void check_mmio_access(struct vm_data *data, struct kvm_run *run) 125cad347faSMaciej S. Szmigiero { 126e82e630bSSean Christopherson TEST_ASSERT(data->mmio_ok, "Unexpected mmio exit"); 127cad347faSMaciej S. Szmigiero TEST_ASSERT(run->mmio.is_write, "Unexpected mmio read"); 128cad347faSMaciej S. Szmigiero TEST_ASSERT(run->mmio.len == 8, 129cad347faSMaciej S. Szmigiero "Unexpected exit mmio size = %u", run->mmio.len); 130e82e630bSSean Christopherson TEST_ASSERT(run->mmio.phys_addr >= data->mmio_gpa_min && 131e82e630bSSean Christopherson run->mmio.phys_addr <= data->mmio_gpa_max, 132cad347faSMaciej S. Szmigiero "Unexpected exit mmio address = 0x%llx", 133cad347faSMaciej S. Szmigiero run->mmio.phys_addr); 134cad347faSMaciej S. Szmigiero } 135cad347faSMaciej S. Szmigiero 136e82e630bSSean Christopherson static void *vcpu_worker(void *__data) 137ffb4ce3cSRicardo Koller { 138e82e630bSSean Christopherson struct vm_data *data = __data; 139e82e630bSSean Christopherson struct kvm_vcpu *vcpu = data->vcpu; 140e82e630bSSean Christopherson struct kvm_run *run = vcpu->run; 141ffb4ce3cSRicardo Koller struct ucall uc; 142cad347faSMaciej S. Szmigiero 143ffb4ce3cSRicardo Koller while (1) { 144768e9a61SSean Christopherson vcpu_run(vcpu); 145ffb4ce3cSRicardo Koller 146768e9a61SSean Christopherson switch (get_ucall(vcpu, &uc)) { 147ffb4ce3cSRicardo Koller case UCALL_SYNC: 148ffb4ce3cSRicardo Koller TEST_ASSERT(uc.args[1] == 0, 149ffb4ce3cSRicardo Koller "Unexpected sync ucall, got %lx", 150ffb4ce3cSRicardo Koller (ulong)uc.args[1]); 151ffb4ce3cSRicardo Koller sem_post(&vcpu_ready); 152ffb4ce3cSRicardo Koller continue; 153ffb4ce3cSRicardo Koller case UCALL_NONE: 154ffb4ce3cSRicardo Koller if (run->exit_reason == KVM_EXIT_MMIO) 155e82e630bSSean Christopherson check_mmio_access(data, run); 156ffb4ce3cSRicardo Koller else 157ffb4ce3cSRicardo Koller goto done; 158ffb4ce3cSRicardo Koller break; 159ffb4ce3cSRicardo Koller case UCALL_ABORT: 160594a1c27SColton Lewis REPORT_GUEST_ASSERT_1(uc, "val = %lu"); 161ffb4ce3cSRicardo Koller break; 162ffb4ce3cSRicardo Koller case UCALL_DONE: 163ffb4ce3cSRicardo Koller goto done; 164ffb4ce3cSRicardo Koller default: 165ffb4ce3cSRicardo Koller TEST_FAIL("Unknown ucall %lu", uc.cmd); 166ffb4ce3cSRicardo Koller } 167ffb4ce3cSRicardo Koller } 168ffb4ce3cSRicardo Koller 169ffb4ce3cSRicardo Koller done: 170cad347faSMaciej S. Szmigiero return NULL; 171cad347faSMaciej S. Szmigiero } 172cad347faSMaciej S. Szmigiero 173cad347faSMaciej S. Szmigiero static void wait_for_vcpu(void) 174cad347faSMaciej S. Szmigiero { 175cad347faSMaciej S. Szmigiero struct timespec ts; 176cad347faSMaciej S. Szmigiero 177cad347faSMaciej S. Szmigiero TEST_ASSERT(!clock_gettime(CLOCK_REALTIME, &ts), 178cad347faSMaciej S. Szmigiero "clock_gettime() failed: %d\n", errno); 179cad347faSMaciej S. Szmigiero 180cad347faSMaciej S. Szmigiero ts.tv_sec += 2; 181cad347faSMaciej S. Szmigiero TEST_ASSERT(!sem_timedwait(&vcpu_ready, &ts), 182cad347faSMaciej S. Szmigiero "sem_timedwait() failed: %d\n", errno); 183cad347faSMaciej S. Szmigiero } 184cad347faSMaciej S. Szmigiero 185cad347faSMaciej S. Szmigiero static void *vm_gpa2hva(struct vm_data *data, uint64_t gpa, uint64_t *rempages) 186cad347faSMaciej S. Szmigiero { 187cad347faSMaciej S. Szmigiero uint64_t gpage, pgoffs; 188cad347faSMaciej S. Szmigiero uint32_t slot, slotoffs; 189cad347faSMaciej S. Szmigiero void *base; 1908675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 191cad347faSMaciej S. Szmigiero 192cad347faSMaciej S. Szmigiero TEST_ASSERT(gpa >= MEM_GPA, "Too low gpa to translate"); 1938675c6f2SGavin Shan TEST_ASSERT(gpa < MEM_GPA + data->npages * guest_page_size, 194cad347faSMaciej S. Szmigiero "Too high gpa to translate"); 195cad347faSMaciej S. Szmigiero gpa -= MEM_GPA; 196cad347faSMaciej S. Szmigiero 1978675c6f2SGavin Shan gpage = gpa / guest_page_size; 1988675c6f2SGavin Shan pgoffs = gpa % guest_page_size; 199cad347faSMaciej S. Szmigiero slot = min(gpage / data->pages_per_slot, (uint64_t)data->nslots - 1); 200cad347faSMaciej S. Szmigiero slotoffs = gpage - (slot * data->pages_per_slot); 201cad347faSMaciej S. Szmigiero 202cad347faSMaciej S. Szmigiero if (rempages) { 203cad347faSMaciej S. Szmigiero uint64_t slotpages; 204cad347faSMaciej S. Szmigiero 205cad347faSMaciej S. Szmigiero if (slot == data->nslots - 1) 206cad347faSMaciej S. Szmigiero slotpages = data->npages - slot * data->pages_per_slot; 207cad347faSMaciej S. Szmigiero else 208cad347faSMaciej S. Szmigiero slotpages = data->pages_per_slot; 209cad347faSMaciej S. Szmigiero 210cad347faSMaciej S. Szmigiero TEST_ASSERT(!pgoffs, 211cad347faSMaciej S. Szmigiero "Asking for remaining pages in slot but gpa not page aligned"); 212cad347faSMaciej S. Szmigiero *rempages = slotpages - slotoffs; 213cad347faSMaciej S. Szmigiero } 214cad347faSMaciej S. Szmigiero 215cad347faSMaciej S. Szmigiero base = data->hva_slots[slot]; 2168675c6f2SGavin Shan return (uint8_t *)base + slotoffs * guest_page_size + pgoffs; 217cad347faSMaciej S. Szmigiero } 218cad347faSMaciej S. Szmigiero 219cad347faSMaciej S. Szmigiero static uint64_t vm_slot2gpa(struct vm_data *data, uint32_t slot) 220cad347faSMaciej S. Szmigiero { 2218675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 2228675c6f2SGavin Shan 223cad347faSMaciej S. Szmigiero TEST_ASSERT(slot < data->nslots, "Too high slot number"); 224cad347faSMaciej S. Szmigiero 2258675c6f2SGavin Shan return MEM_GPA + slot * data->pages_per_slot * guest_page_size; 226cad347faSMaciej S. Szmigiero } 227cad347faSMaciej S. Szmigiero 228cad347faSMaciej S. Szmigiero static struct vm_data *alloc_vm(void) 229cad347faSMaciej S. Szmigiero { 230cad347faSMaciej S. Szmigiero struct vm_data *data; 231cad347faSMaciej S. Szmigiero 232cad347faSMaciej S. Szmigiero data = malloc(sizeof(*data)); 233cad347faSMaciej S. Szmigiero TEST_ASSERT(data, "malloc(vmdata) failed"); 234cad347faSMaciej S. Szmigiero 235cad347faSMaciej S. Szmigiero data->vm = NULL; 236e82e630bSSean Christopherson data->vcpu = NULL; 237cad347faSMaciej S. Szmigiero data->hva_slots = NULL; 238cad347faSMaciej S. Szmigiero 239cad347faSMaciej S. Szmigiero return data; 240cad347faSMaciej S. Szmigiero } 241cad347faSMaciej S. Szmigiero 242*a69170c6SGavin Shan static bool check_slot_pages(uint32_t host_page_size, uint32_t guest_page_size, 243*a69170c6SGavin Shan uint64_t pages_per_slot, uint64_t rempages) 244*a69170c6SGavin Shan { 245*a69170c6SGavin Shan if (!pages_per_slot) 246*a69170c6SGavin Shan return false; 247*a69170c6SGavin Shan 248*a69170c6SGavin Shan if ((pages_per_slot * guest_page_size) % host_page_size) 249*a69170c6SGavin Shan return false; 250*a69170c6SGavin Shan 251*a69170c6SGavin Shan if ((rempages * guest_page_size) % host_page_size) 252*a69170c6SGavin Shan return false; 253*a69170c6SGavin Shan 254*a69170c6SGavin Shan return true; 255*a69170c6SGavin Shan } 256*a69170c6SGavin Shan 257*a69170c6SGavin Shan 258*a69170c6SGavin Shan static uint64_t get_max_slots(struct vm_data *data, uint32_t host_page_size) 259*a69170c6SGavin Shan { 260*a69170c6SGavin Shan uint32_t guest_page_size = data->vm->page_size; 261*a69170c6SGavin Shan uint64_t mempages, pages_per_slot, rempages; 262*a69170c6SGavin Shan uint64_t slots; 263*a69170c6SGavin Shan 264*a69170c6SGavin Shan mempages = data->npages; 265*a69170c6SGavin Shan slots = data->nslots; 266*a69170c6SGavin Shan while (--slots > 1) { 267*a69170c6SGavin Shan pages_per_slot = mempages / slots; 268*a69170c6SGavin Shan rempages = mempages % pages_per_slot; 269*a69170c6SGavin Shan if (check_slot_pages(host_page_size, guest_page_size, 270*a69170c6SGavin Shan pages_per_slot, rempages)) 271*a69170c6SGavin Shan return slots + 1; /* slot 0 is reserved */ 272*a69170c6SGavin Shan } 273*a69170c6SGavin Shan 274*a69170c6SGavin Shan return 0; 275*a69170c6SGavin Shan } 276*a69170c6SGavin Shan 277cad347faSMaciej S. Szmigiero static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots, 2788675c6f2SGavin Shan void *guest_code, uint64_t mem_size, 279cad347faSMaciej S. Szmigiero struct timespec *slot_runtime) 280cad347faSMaciej S. Szmigiero { 2818675c6f2SGavin Shan uint64_t mempages, rempages; 282cad347faSMaciej S. Szmigiero uint64_t guest_addr; 283*a69170c6SGavin Shan uint32_t slot, host_page_size, guest_page_size; 284cad347faSMaciej S. Szmigiero struct timespec tstart; 285cad347faSMaciej S. Szmigiero struct sync_area *sync; 286cad347faSMaciej S. Szmigiero 287*a69170c6SGavin Shan host_page_size = getpagesize(); 2888675c6f2SGavin Shan guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size; 2898675c6f2SGavin Shan mempages = mem_size / guest_page_size; 290cad347faSMaciej S. Szmigiero 291e82e630bSSean Christopherson data->vm = __vm_create_with_one_vcpu(&data->vcpu, mempages, guest_code); 292ffb4ce3cSRicardo Koller ucall_init(data->vm, NULL); 2938675c6f2SGavin Shan TEST_ASSERT(data->vm->page_size == guest_page_size, "Invalid VM page size"); 2948675c6f2SGavin Shan 2958675c6f2SGavin Shan data->npages = mempages; 2968675c6f2SGavin Shan TEST_ASSERT(data->npages > 1, "Can't test without any memory"); 2978675c6f2SGavin Shan data->nslots = nslots; 2988675c6f2SGavin Shan data->pages_per_slot = data->npages / data->nslots; 299*a69170c6SGavin Shan rempages = data->npages % data->nslots; 300*a69170c6SGavin Shan if (!check_slot_pages(host_page_size, guest_page_size, 301*a69170c6SGavin Shan data->pages_per_slot, rempages)) { 302*a69170c6SGavin Shan *maxslots = get_max_slots(data, host_page_size); 3038675c6f2SGavin Shan return false; 3048675c6f2SGavin Shan } 3058675c6f2SGavin Shan 3068675c6f2SGavin Shan data->hva_slots = malloc(sizeof(*data->hva_slots) * data->nslots); 3078675c6f2SGavin Shan TEST_ASSERT(data->hva_slots, "malloc() fail"); 308cad347faSMaciej S. Szmigiero 309cad347faSMaciej S. Szmigiero pr_info_v("Adding slots 1..%i, each slot with %"PRIu64" pages + %"PRIu64" extra pages last\n", 3103bfadb23SGavin Shan data->nslots, data->pages_per_slot, rempages); 311cad347faSMaciej S. Szmigiero 312cad347faSMaciej S. Szmigiero clock_gettime(CLOCK_MONOTONIC, &tstart); 3133bfadb23SGavin Shan for (slot = 1, guest_addr = MEM_GPA; slot <= data->nslots; slot++) { 314cad347faSMaciej S. Szmigiero uint64_t npages; 315cad347faSMaciej S. Szmigiero 316cad347faSMaciej S. Szmigiero npages = data->pages_per_slot; 3173bfadb23SGavin Shan if (slot == data->nslots) 318cad347faSMaciej S. Szmigiero npages += rempages; 319cad347faSMaciej S. Szmigiero 320cad347faSMaciej S. Szmigiero vm_userspace_mem_region_add(data->vm, VM_MEM_SRC_ANONYMOUS, 321cad347faSMaciej S. Szmigiero guest_addr, slot, npages, 322cad347faSMaciej S. Szmigiero 0); 3238675c6f2SGavin Shan guest_addr += npages * guest_page_size; 324cad347faSMaciej S. Szmigiero } 325cad347faSMaciej S. Szmigiero *slot_runtime = timespec_elapsed(tstart); 326cad347faSMaciej S. Szmigiero 3272aae5e67SGavin Shan for (slot = 1, guest_addr = MEM_GPA; slot <= data->nslots; slot++) { 328cad347faSMaciej S. Szmigiero uint64_t npages; 329cad347faSMaciej S. Szmigiero uint64_t gpa; 330cad347faSMaciej S. Szmigiero 331cad347faSMaciej S. Szmigiero npages = data->pages_per_slot; 3322aae5e67SGavin Shan if (slot == data->nslots) 333cad347faSMaciej S. Szmigiero npages += rempages; 334cad347faSMaciej S. Szmigiero 3352aae5e67SGavin Shan gpa = vm_phy_pages_alloc(data->vm, npages, guest_addr, slot); 336cad347faSMaciej S. Szmigiero TEST_ASSERT(gpa == guest_addr, 337cad347faSMaciej S. Szmigiero "vm_phy_pages_alloc() failed\n"); 338cad347faSMaciej S. Szmigiero 3392aae5e67SGavin Shan data->hva_slots[slot - 1] = addr_gpa2hva(data->vm, guest_addr); 3408675c6f2SGavin Shan memset(data->hva_slots[slot - 1], 0, npages * guest_page_size); 341cad347faSMaciej S. Szmigiero 3428675c6f2SGavin Shan guest_addr += npages * guest_page_size; 343cad347faSMaciej S. Szmigiero } 344cad347faSMaciej S. Szmigiero 3458675c6f2SGavin Shan virt_map(data->vm, MEM_GPA, MEM_GPA, data->npages); 346cad347faSMaciej S. Szmigiero 347cad347faSMaciej S. Szmigiero sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL); 348cad347faSMaciej S. Szmigiero atomic_init(&sync->start_flag, false); 349cad347faSMaciej S. Szmigiero atomic_init(&sync->exit_flag, false); 350cad347faSMaciej S. Szmigiero atomic_init(&sync->sync_flag, false); 351cad347faSMaciej S. Szmigiero 352cad347faSMaciej S. Szmigiero data->mmio_ok = false; 353cad347faSMaciej S. Szmigiero 354cad347faSMaciej S. Szmigiero return true; 355cad347faSMaciej S. Szmigiero } 356cad347faSMaciej S. Szmigiero 357cad347faSMaciej S. Szmigiero static void launch_vm(struct vm_data *data) 358cad347faSMaciej S. Szmigiero { 359cad347faSMaciej S. Szmigiero pr_info_v("Launching the test VM\n"); 360cad347faSMaciej S. Szmigiero 361cad347faSMaciej S. Szmigiero pthread_create(&data->vcpu_thread, NULL, vcpu_worker, data); 362cad347faSMaciej S. Szmigiero 363cad347faSMaciej S. Szmigiero /* Ensure the guest thread is spun up. */ 364cad347faSMaciej S. Szmigiero wait_for_vcpu(); 365cad347faSMaciej S. Szmigiero } 366cad347faSMaciej S. Szmigiero 367cad347faSMaciej S. Szmigiero static void free_vm(struct vm_data *data) 368cad347faSMaciej S. Szmigiero { 369cad347faSMaciej S. Szmigiero kvm_vm_free(data->vm); 370cad347faSMaciej S. Szmigiero free(data->hva_slots); 371cad347faSMaciej S. Szmigiero free(data); 372cad347faSMaciej S. Szmigiero } 373cad347faSMaciej S. Szmigiero 374cad347faSMaciej S. Szmigiero static void wait_guest_exit(struct vm_data *data) 375cad347faSMaciej S. Szmigiero { 376cad347faSMaciej S. Szmigiero pthread_join(data->vcpu_thread, NULL); 377cad347faSMaciej S. Szmigiero } 378cad347faSMaciej S. Szmigiero 379cad347faSMaciej S. Szmigiero static void let_guest_run(struct sync_area *sync) 380cad347faSMaciej S. Szmigiero { 381cad347faSMaciej S. Szmigiero atomic_store_explicit(&sync->start_flag, true, memory_order_release); 382cad347faSMaciej S. Szmigiero } 383cad347faSMaciej S. Szmigiero 384cad347faSMaciej S. Szmigiero static void guest_spin_until_start(void) 385cad347faSMaciej S. Szmigiero { 386cad347faSMaciej S. Szmigiero struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 387cad347faSMaciej S. Szmigiero 388cad347faSMaciej S. Szmigiero while (!atomic_load_explicit(&sync->start_flag, memory_order_acquire)) 389cad347faSMaciej S. Szmigiero ; 390cad347faSMaciej S. Szmigiero } 391cad347faSMaciej S. Szmigiero 392cad347faSMaciej S. Szmigiero static void make_guest_exit(struct sync_area *sync) 393cad347faSMaciej S. Szmigiero { 394cad347faSMaciej S. Szmigiero atomic_store_explicit(&sync->exit_flag, true, memory_order_release); 395cad347faSMaciej S. Szmigiero } 396cad347faSMaciej S. Szmigiero 397cad347faSMaciej S. Szmigiero static bool _guest_should_exit(void) 398cad347faSMaciej S. Szmigiero { 399cad347faSMaciej S. Szmigiero struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 400cad347faSMaciej S. Szmigiero 401cad347faSMaciej S. Szmigiero return atomic_load_explicit(&sync->exit_flag, memory_order_acquire); 402cad347faSMaciej S. Szmigiero } 403cad347faSMaciej S. Szmigiero 404cad347faSMaciej S. Szmigiero #define guest_should_exit() unlikely(_guest_should_exit()) 405cad347faSMaciej S. Szmigiero 406cad347faSMaciej S. Szmigiero /* 407cad347faSMaciej S. Szmigiero * noinline so we can easily see how much time the host spends waiting 408cad347faSMaciej S. Szmigiero * for the guest. 409cad347faSMaciej S. Szmigiero * For the same reason use alarm() instead of polling clock_gettime() 410cad347faSMaciej S. Szmigiero * to implement a wait timeout. 411cad347faSMaciej S. Szmigiero */ 412cad347faSMaciej S. Szmigiero static noinline void host_perform_sync(struct sync_area *sync) 413cad347faSMaciej S. Szmigiero { 414cad347faSMaciej S. Szmigiero alarm(2); 415cad347faSMaciej S. Szmigiero 416cad347faSMaciej S. Szmigiero atomic_store_explicit(&sync->sync_flag, true, memory_order_release); 417cad347faSMaciej S. Szmigiero while (atomic_load_explicit(&sync->sync_flag, memory_order_acquire)) 418cad347faSMaciej S. Szmigiero ; 419cad347faSMaciej S. Szmigiero 420cad347faSMaciej S. Szmigiero alarm(0); 421cad347faSMaciej S. Szmigiero } 422cad347faSMaciej S. Szmigiero 423cad347faSMaciej S. Szmigiero static bool guest_perform_sync(void) 424cad347faSMaciej S. Szmigiero { 425cad347faSMaciej S. Szmigiero struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 426cad347faSMaciej S. Szmigiero bool expected; 427cad347faSMaciej S. Szmigiero 428cad347faSMaciej S. Szmigiero do { 429cad347faSMaciej S. Szmigiero if (guest_should_exit()) 430cad347faSMaciej S. Szmigiero return false; 431cad347faSMaciej S. Szmigiero 432cad347faSMaciej S. Szmigiero expected = true; 433cad347faSMaciej S. Szmigiero } while (!atomic_compare_exchange_weak_explicit(&sync->sync_flag, 434cad347faSMaciej S. Szmigiero &expected, false, 435cad347faSMaciej S. Szmigiero memory_order_acq_rel, 436cad347faSMaciej S. Szmigiero memory_order_relaxed)); 437cad347faSMaciej S. Szmigiero 438cad347faSMaciej S. Szmigiero return true; 439cad347faSMaciej S. Szmigiero } 440cad347faSMaciej S. Szmigiero 441cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_move(void) 442cad347faSMaciej S. Szmigiero { 443cad347faSMaciej S. Szmigiero struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 4448675c6f2SGavin Shan uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); 445cad347faSMaciej S. Szmigiero uintptr_t base = (typeof(base))READ_ONCE(sync->move_area_ptr); 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 (!guest_should_exit()) { 452cad347faSMaciej S. Szmigiero uintptr_t ptr; 453cad347faSMaciej S. Szmigiero 454cad347faSMaciej S. Szmigiero for (ptr = base; ptr < base + MEM_TEST_MOVE_SIZE; 4558675c6f2SGavin Shan ptr += page_size) 456cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = MEM_TEST_VAL_1; 457cad347faSMaciej S. Szmigiero 458cad347faSMaciej S. Szmigiero /* 459cad347faSMaciej S. Szmigiero * No host sync here since the MMIO exits are so expensive 460cad347faSMaciej S. Szmigiero * that the host would spend most of its time waiting for 461cad347faSMaciej S. Szmigiero * the guest and so instead of measuring memslot move 462cad347faSMaciej S. Szmigiero * performance we would measure the performance and 463cad347faSMaciej S. Szmigiero * likelihood of MMIO exits 464cad347faSMaciej S. Szmigiero */ 465cad347faSMaciej S. Szmigiero } 466cad347faSMaciej S. Szmigiero 467cad347faSMaciej S. Szmigiero GUEST_DONE(); 468cad347faSMaciej S. Szmigiero } 469cad347faSMaciej S. Szmigiero 470cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_map(void) 471cad347faSMaciej S. Szmigiero { 472cad347faSMaciej S. Szmigiero struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 4738675c6f2SGavin Shan uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); 474cad347faSMaciej S. Szmigiero 475cad347faSMaciej S. Szmigiero GUEST_SYNC(0); 476cad347faSMaciej S. Szmigiero 477cad347faSMaciej S. Szmigiero guest_spin_until_start(); 478cad347faSMaciej S. Szmigiero 479cad347faSMaciej S. Szmigiero while (1) { 480cad347faSMaciej S. Szmigiero uintptr_t ptr; 481cad347faSMaciej S. Szmigiero 482cad347faSMaciej S. Szmigiero for (ptr = MEM_TEST_GPA; 4838675c6f2SGavin Shan ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2; 4848675c6f2SGavin Shan ptr += page_size) 485cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = MEM_TEST_VAL_1; 486cad347faSMaciej S. Szmigiero 487cad347faSMaciej S. Szmigiero if (!guest_perform_sync()) 488cad347faSMaciej S. Szmigiero break; 489cad347faSMaciej S. Szmigiero 490cad347faSMaciej S. Szmigiero for (ptr = MEM_TEST_GPA + MEM_TEST_MAP_SIZE / 2; 4918675c6f2SGavin Shan ptr < MEM_TEST_GPA + MEM_TEST_MAP_SIZE; 4928675c6f2SGavin Shan ptr += page_size) 493cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = MEM_TEST_VAL_2; 494cad347faSMaciej S. Szmigiero 495cad347faSMaciej S. Szmigiero if (!guest_perform_sync()) 496cad347faSMaciej S. Szmigiero break; 497cad347faSMaciej S. Szmigiero } 498cad347faSMaciej S. Szmigiero 499cad347faSMaciej S. Szmigiero GUEST_DONE(); 500cad347faSMaciej S. Szmigiero } 501cad347faSMaciej S. Szmigiero 502cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_unmap(void) 503cad347faSMaciej S. Szmigiero { 504cad347faSMaciej S. Szmigiero struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 505cad347faSMaciej S. Szmigiero 506cad347faSMaciej S. Szmigiero GUEST_SYNC(0); 507cad347faSMaciej S. Szmigiero 508cad347faSMaciej S. Szmigiero guest_spin_until_start(); 509cad347faSMaciej S. Szmigiero 510cad347faSMaciej S. Szmigiero while (1) { 511cad347faSMaciej S. Szmigiero uintptr_t ptr = MEM_TEST_GPA; 512cad347faSMaciej S. Szmigiero 513cad347faSMaciej S. Szmigiero /* 514cad347faSMaciej S. Szmigiero * We can afford to access (map) just a small number of pages 515cad347faSMaciej S. Szmigiero * per host sync as otherwise the host will spend 516cad347faSMaciej S. Szmigiero * a significant amount of its time waiting for the guest 517cad347faSMaciej S. Szmigiero * (instead of doing unmap operations), so this will 518cad347faSMaciej S. Szmigiero * effectively turn this test into a map performance test. 519cad347faSMaciej S. Szmigiero * 520cad347faSMaciej S. Szmigiero * Just access a single page to be on the safe side. 521cad347faSMaciej S. Szmigiero */ 522cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = MEM_TEST_VAL_1; 523cad347faSMaciej S. Szmigiero 524cad347faSMaciej S. Szmigiero if (!guest_perform_sync()) 525cad347faSMaciej S. Szmigiero break; 526cad347faSMaciej S. Szmigiero 527cad347faSMaciej S. Szmigiero ptr += MEM_TEST_UNMAP_SIZE / 2; 528cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = MEM_TEST_VAL_2; 529cad347faSMaciej S. Szmigiero 530cad347faSMaciej S. Szmigiero if (!guest_perform_sync()) 531cad347faSMaciej S. Szmigiero break; 532cad347faSMaciej S. Szmigiero } 533cad347faSMaciej S. Szmigiero 534cad347faSMaciej S. Szmigiero GUEST_DONE(); 535cad347faSMaciej S. Szmigiero } 536cad347faSMaciej S. Szmigiero 537cad347faSMaciej S. Szmigiero static void guest_code_test_memslot_rw(void) 538cad347faSMaciej S. Szmigiero { 5398675c6f2SGavin Shan struct sync_area *sync = (typeof(sync))MEM_SYNC_GPA; 5408675c6f2SGavin Shan uint32_t page_size = (typeof(page_size))READ_ONCE(sync->guest_page_size); 5418675c6f2SGavin Shan 542cad347faSMaciej S. Szmigiero GUEST_SYNC(0); 543cad347faSMaciej S. Szmigiero 544cad347faSMaciej S. Szmigiero guest_spin_until_start(); 545cad347faSMaciej S. Szmigiero 546cad347faSMaciej S. Szmigiero while (1) { 547cad347faSMaciej S. Szmigiero uintptr_t ptr; 548cad347faSMaciej S. Szmigiero 549cad347faSMaciej S. Szmigiero for (ptr = MEM_TEST_GPA; 5508675c6f2SGavin Shan ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size) 551cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = MEM_TEST_VAL_1; 552cad347faSMaciej S. Szmigiero 553cad347faSMaciej S. Szmigiero if (!guest_perform_sync()) 554cad347faSMaciej S. Szmigiero break; 555cad347faSMaciej S. Szmigiero 5568675c6f2SGavin Shan for (ptr = MEM_TEST_GPA + page_size / 2; 5578675c6f2SGavin Shan ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size) { 558cad347faSMaciej S. Szmigiero uint64_t val = *(uint64_t *)ptr; 559cad347faSMaciej S. Szmigiero 560cad347faSMaciej S. Szmigiero GUEST_ASSERT_1(val == MEM_TEST_VAL_2, val); 561cad347faSMaciej S. Szmigiero *(uint64_t *)ptr = 0; 562cad347faSMaciej S. Szmigiero } 563cad347faSMaciej S. Szmigiero 564cad347faSMaciej S. Szmigiero if (!guest_perform_sync()) 565cad347faSMaciej S. Szmigiero break; 566cad347faSMaciej S. Szmigiero } 567cad347faSMaciej S. Szmigiero 568cad347faSMaciej S. Szmigiero GUEST_DONE(); 569cad347faSMaciej S. Szmigiero } 570cad347faSMaciej S. Szmigiero 571cad347faSMaciej S. Szmigiero static bool test_memslot_move_prepare(struct vm_data *data, 572cad347faSMaciej S. Szmigiero struct sync_area *sync, 573cad347faSMaciej S. Szmigiero uint64_t *maxslots, bool isactive) 574cad347faSMaciej S. Szmigiero { 5758675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 576cad347faSMaciej S. Szmigiero uint64_t movesrcgpa, movetestgpa; 577cad347faSMaciej S. Szmigiero 578cad347faSMaciej S. Szmigiero movesrcgpa = vm_slot2gpa(data, data->nslots - 1); 579cad347faSMaciej S. Szmigiero 580cad347faSMaciej S. Szmigiero if (isactive) { 581cad347faSMaciej S. Szmigiero uint64_t lastpages; 582cad347faSMaciej S. Szmigiero 583cad347faSMaciej S. Szmigiero vm_gpa2hva(data, movesrcgpa, &lastpages); 58488a64e65SGavin Shan if (lastpages * guest_page_size < MEM_TEST_MOVE_SIZE / 2) { 585cad347faSMaciej S. Szmigiero *maxslots = 0; 586cad347faSMaciej S. Szmigiero return false; 587cad347faSMaciej S. Szmigiero } 588cad347faSMaciej S. Szmigiero } 589cad347faSMaciej S. Szmigiero 590cad347faSMaciej S. Szmigiero movetestgpa = movesrcgpa - (MEM_TEST_MOVE_SIZE / (isactive ? 2 : 1)); 591cad347faSMaciej S. Szmigiero sync->move_area_ptr = (void *)movetestgpa; 592cad347faSMaciej S. Szmigiero 593cad347faSMaciej S. Szmigiero if (isactive) { 594cad347faSMaciej S. Szmigiero data->mmio_ok = true; 595cad347faSMaciej S. Szmigiero data->mmio_gpa_min = movesrcgpa; 596cad347faSMaciej S. Szmigiero data->mmio_gpa_max = movesrcgpa + MEM_TEST_MOVE_SIZE / 2 - 1; 597cad347faSMaciej S. Szmigiero } 598cad347faSMaciej S. Szmigiero 599cad347faSMaciej S. Szmigiero return true; 600cad347faSMaciej S. Szmigiero } 601cad347faSMaciej S. Szmigiero 602cad347faSMaciej S. Szmigiero static bool test_memslot_move_prepare_active(struct vm_data *data, 603cad347faSMaciej S. Szmigiero struct sync_area *sync, 604cad347faSMaciej S. Szmigiero uint64_t *maxslots) 605cad347faSMaciej S. Szmigiero { 606cad347faSMaciej S. Szmigiero return test_memslot_move_prepare(data, sync, maxslots, true); 607cad347faSMaciej S. Szmigiero } 608cad347faSMaciej S. Szmigiero 609cad347faSMaciej S. Szmigiero static bool test_memslot_move_prepare_inactive(struct vm_data *data, 610cad347faSMaciej S. Szmigiero struct sync_area *sync, 611cad347faSMaciej S. Szmigiero uint64_t *maxslots) 612cad347faSMaciej S. Szmigiero { 613cad347faSMaciej S. Szmigiero return test_memslot_move_prepare(data, sync, maxslots, false); 614cad347faSMaciej S. Szmigiero } 615cad347faSMaciej S. Szmigiero 616cad347faSMaciej S. Szmigiero static void test_memslot_move_loop(struct vm_data *data, struct sync_area *sync) 617cad347faSMaciej S. Szmigiero { 618cad347faSMaciej S. Szmigiero uint64_t movesrcgpa; 619cad347faSMaciej S. Szmigiero 620cad347faSMaciej S. Szmigiero movesrcgpa = vm_slot2gpa(data, data->nslots - 1); 621cad347faSMaciej S. Szmigiero vm_mem_region_move(data->vm, data->nslots - 1 + 1, 622cad347faSMaciej S. Szmigiero MEM_TEST_MOVE_GPA_DEST); 623cad347faSMaciej S. Szmigiero vm_mem_region_move(data->vm, data->nslots - 1 + 1, movesrcgpa); 624cad347faSMaciej S. Szmigiero } 625cad347faSMaciej S. Szmigiero 626cad347faSMaciej S. Szmigiero static void test_memslot_do_unmap(struct vm_data *data, 627cad347faSMaciej S. Szmigiero uint64_t offsp, uint64_t count) 628cad347faSMaciej S. Szmigiero { 629cad347faSMaciej S. Szmigiero uint64_t gpa, ctr; 6308675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 631cad347faSMaciej S. Szmigiero 6328675c6f2SGavin Shan for (gpa = MEM_TEST_GPA + offsp * guest_page_size, ctr = 0; ctr < count; ) { 633cad347faSMaciej S. Szmigiero uint64_t npages; 634cad347faSMaciej S. Szmigiero void *hva; 635cad347faSMaciej S. Szmigiero int ret; 636cad347faSMaciej S. Szmigiero 637cad347faSMaciej S. Szmigiero hva = vm_gpa2hva(data, gpa, &npages); 638cad347faSMaciej S. Szmigiero TEST_ASSERT(npages, "Empty memory slot at gptr 0x%"PRIx64, gpa); 639cad347faSMaciej S. Szmigiero npages = min(npages, count - ctr); 6408675c6f2SGavin Shan ret = madvise(hva, npages * guest_page_size, MADV_DONTNEED); 641cad347faSMaciej S. Szmigiero TEST_ASSERT(!ret, 642cad347faSMaciej S. Szmigiero "madvise(%p, MADV_DONTNEED) on VM memory should not fail for gptr 0x%"PRIx64, 643cad347faSMaciej S. Szmigiero hva, gpa); 644cad347faSMaciej S. Szmigiero ctr += npages; 6458675c6f2SGavin Shan gpa += npages * guest_page_size; 646cad347faSMaciej S. Szmigiero } 647cad347faSMaciej S. Szmigiero TEST_ASSERT(ctr == count, 648cad347faSMaciej S. Szmigiero "madvise(MADV_DONTNEED) should exactly cover all of the requested area"); 649cad347faSMaciej S. Szmigiero } 650cad347faSMaciej S. Szmigiero 651cad347faSMaciej S. Szmigiero static void test_memslot_map_unmap_check(struct vm_data *data, 652cad347faSMaciej S. Szmigiero uint64_t offsp, uint64_t valexp) 653cad347faSMaciej S. Szmigiero { 654cad347faSMaciej S. Szmigiero uint64_t gpa; 655cad347faSMaciej S. Szmigiero uint64_t *val; 6568675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 657cad347faSMaciej S. Szmigiero 658cad347faSMaciej S. Szmigiero if (!map_unmap_verify) 659cad347faSMaciej S. Szmigiero return; 660cad347faSMaciej S. Szmigiero 6618675c6f2SGavin Shan gpa = MEM_TEST_GPA + offsp * guest_page_size; 662cad347faSMaciej S. Szmigiero val = (typeof(val))vm_gpa2hva(data, gpa, NULL); 663cad347faSMaciej S. Szmigiero TEST_ASSERT(*val == valexp, 664cad347faSMaciej S. Szmigiero "Guest written values should read back correctly before unmap (%"PRIu64" vs %"PRIu64" @ %"PRIx64")", 665cad347faSMaciej S. Szmigiero *val, valexp, gpa); 666cad347faSMaciej S. Szmigiero *val = 0; 667cad347faSMaciej S. Szmigiero } 668cad347faSMaciej S. Szmigiero 669cad347faSMaciej S. Szmigiero static void test_memslot_map_loop(struct vm_data *data, struct sync_area *sync) 670cad347faSMaciej S. Szmigiero { 6718675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 6728675c6f2SGavin Shan uint64_t guest_pages = MEM_TEST_MAP_SIZE / guest_page_size; 6738675c6f2SGavin Shan 674cad347faSMaciej S. Szmigiero /* 675cad347faSMaciej S. Szmigiero * Unmap the second half of the test area while guest writes to (maps) 676cad347faSMaciej S. Szmigiero * the first half. 677cad347faSMaciej S. Szmigiero */ 6788675c6f2SGavin Shan test_memslot_do_unmap(data, guest_pages / 2, guest_pages / 2); 679cad347faSMaciej S. Szmigiero 680cad347faSMaciej S. Szmigiero /* 681cad347faSMaciej S. Szmigiero * Wait for the guest to finish writing the first half of the test 682cad347faSMaciej S. Szmigiero * area, verify the written value on the first and the last page of 683cad347faSMaciej S. Szmigiero * this area and then unmap it. 684cad347faSMaciej S. Szmigiero * Meanwhile, the guest is writing to (mapping) the second half of 685cad347faSMaciej S. Szmigiero * the test area. 686cad347faSMaciej S. Szmigiero */ 687cad347faSMaciej S. Szmigiero host_perform_sync(sync); 688cad347faSMaciej S. Szmigiero test_memslot_map_unmap_check(data, 0, MEM_TEST_VAL_1); 6898675c6f2SGavin Shan test_memslot_map_unmap_check(data, guest_pages / 2 - 1, MEM_TEST_VAL_1); 6908675c6f2SGavin Shan test_memslot_do_unmap(data, 0, guest_pages / 2); 691cad347faSMaciej S. Szmigiero 692cad347faSMaciej S. Szmigiero 693cad347faSMaciej S. Szmigiero /* 694cad347faSMaciej S. Szmigiero * Wait for the guest to finish writing the second half of the test 695cad347faSMaciej S. Szmigiero * area and verify the written value on the first and the last page 696cad347faSMaciej S. Szmigiero * of this area. 697cad347faSMaciej S. Szmigiero * The area will be unmapped at the beginning of the next loop 698cad347faSMaciej S. Szmigiero * iteration. 699cad347faSMaciej S. Szmigiero * Meanwhile, the guest is writing to (mapping) the first half of 700cad347faSMaciej S. Szmigiero * the test area. 701cad347faSMaciej S. Szmigiero */ 702cad347faSMaciej S. Szmigiero host_perform_sync(sync); 7038675c6f2SGavin Shan test_memslot_map_unmap_check(data, guest_pages / 2, MEM_TEST_VAL_2); 7048675c6f2SGavin Shan test_memslot_map_unmap_check(data, guest_pages - 1, MEM_TEST_VAL_2); 705cad347faSMaciej S. Szmigiero } 706cad347faSMaciej S. Szmigiero 707cad347faSMaciej S. Szmigiero static void test_memslot_unmap_loop_common(struct vm_data *data, 708cad347faSMaciej S. Szmigiero struct sync_area *sync, 709cad347faSMaciej S. Szmigiero uint64_t chunk) 710cad347faSMaciej S. Szmigiero { 7118675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 7128675c6f2SGavin Shan uint64_t guest_pages = MEM_TEST_UNMAP_SIZE / guest_page_size; 713cad347faSMaciej S. Szmigiero uint64_t ctr; 714cad347faSMaciej S. Szmigiero 715cad347faSMaciej S. Szmigiero /* 716cad347faSMaciej S. Szmigiero * Wait for the guest to finish mapping page(s) in the first half 717cad347faSMaciej S. Szmigiero * of the test area, verify the written value and then perform unmap 718cad347faSMaciej S. Szmigiero * of this area. 719cad347faSMaciej S. Szmigiero * Meanwhile, the guest is writing to (mapping) page(s) in the second 720cad347faSMaciej S. Szmigiero * half of the test area. 721cad347faSMaciej S. Szmigiero */ 722cad347faSMaciej S. Szmigiero host_perform_sync(sync); 723cad347faSMaciej S. Szmigiero test_memslot_map_unmap_check(data, 0, MEM_TEST_VAL_1); 7248675c6f2SGavin Shan for (ctr = 0; ctr < guest_pages / 2; ctr += chunk) 725cad347faSMaciej S. Szmigiero test_memslot_do_unmap(data, ctr, chunk); 726cad347faSMaciej S. Szmigiero 727cad347faSMaciej S. Szmigiero /* Likewise, but for the opposite host / guest areas */ 728cad347faSMaciej S. Szmigiero host_perform_sync(sync); 7298675c6f2SGavin Shan test_memslot_map_unmap_check(data, guest_pages / 2, MEM_TEST_VAL_2); 7308675c6f2SGavin Shan for (ctr = guest_pages / 2; ctr < guest_pages; ctr += chunk) 731cad347faSMaciej S. Szmigiero test_memslot_do_unmap(data, ctr, chunk); 732cad347faSMaciej S. Szmigiero } 733cad347faSMaciej S. Szmigiero 734cad347faSMaciej S. Szmigiero static void test_memslot_unmap_loop(struct vm_data *data, 735cad347faSMaciej S. Szmigiero struct sync_area *sync) 736cad347faSMaciej S. Szmigiero { 7378675c6f2SGavin Shan uint32_t host_page_size = getpagesize(); 7388675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 7398675c6f2SGavin Shan uint64_t guest_chunk_pages = guest_page_size >= host_page_size ? 7408675c6f2SGavin Shan 1 : host_page_size / guest_page_size; 7418675c6f2SGavin Shan 7428675c6f2SGavin Shan test_memslot_unmap_loop_common(data, sync, guest_chunk_pages); 743cad347faSMaciej S. Szmigiero } 744cad347faSMaciej S. Szmigiero 745cad347faSMaciej S. Szmigiero static void test_memslot_unmap_loop_chunked(struct vm_data *data, 746cad347faSMaciej S. Szmigiero struct sync_area *sync) 747cad347faSMaciej S. Szmigiero { 7488675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 7498675c6f2SGavin Shan uint64_t guest_chunk_pages = MEM_TEST_UNMAP_CHUNK_SIZE / guest_page_size; 7508675c6f2SGavin Shan 7518675c6f2SGavin Shan test_memslot_unmap_loop_common(data, sync, guest_chunk_pages); 752cad347faSMaciej S. Szmigiero } 753cad347faSMaciej S. Szmigiero 754cad347faSMaciej S. Szmigiero static void test_memslot_rw_loop(struct vm_data *data, struct sync_area *sync) 755cad347faSMaciej S. Szmigiero { 756cad347faSMaciej S. Szmigiero uint64_t gptr; 7578675c6f2SGavin Shan uint32_t guest_page_size = data->vm->page_size; 758cad347faSMaciej S. Szmigiero 7598675c6f2SGavin Shan for (gptr = MEM_TEST_GPA + guest_page_size / 2; 7608675c6f2SGavin Shan gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += guest_page_size) 761cad347faSMaciej S. Szmigiero *(uint64_t *)vm_gpa2hva(data, gptr, NULL) = MEM_TEST_VAL_2; 762cad347faSMaciej S. Szmigiero 763cad347faSMaciej S. Szmigiero host_perform_sync(sync); 764cad347faSMaciej S. Szmigiero 765cad347faSMaciej S. Szmigiero for (gptr = MEM_TEST_GPA; 7668675c6f2SGavin Shan gptr < MEM_TEST_GPA + MEM_TEST_SIZE; gptr += guest_page_size) { 767cad347faSMaciej S. Szmigiero uint64_t *vptr = (typeof(vptr))vm_gpa2hva(data, gptr, NULL); 768cad347faSMaciej S. Szmigiero uint64_t val = *vptr; 769cad347faSMaciej S. Szmigiero 770cad347faSMaciej S. Szmigiero TEST_ASSERT(val == MEM_TEST_VAL_1, 771cad347faSMaciej S. Szmigiero "Guest written values should read back correctly (is %"PRIu64" @ %"PRIx64")", 772cad347faSMaciej S. Szmigiero val, gptr); 773cad347faSMaciej S. Szmigiero *vptr = 0; 774cad347faSMaciej S. Szmigiero } 775cad347faSMaciej S. Szmigiero 776cad347faSMaciej S. Szmigiero host_perform_sync(sync); 777cad347faSMaciej S. Szmigiero } 778cad347faSMaciej S. Szmigiero 779cad347faSMaciej S. Szmigiero struct test_data { 780cad347faSMaciej S. Szmigiero const char *name; 781cad347faSMaciej S. Szmigiero uint64_t mem_size; 782cad347faSMaciej S. Szmigiero void (*guest_code)(void); 783cad347faSMaciej S. Szmigiero bool (*prepare)(struct vm_data *data, struct sync_area *sync, 784cad347faSMaciej S. Szmigiero uint64_t *maxslots); 785cad347faSMaciej S. Szmigiero void (*loop)(struct vm_data *data, struct sync_area *sync); 786cad347faSMaciej S. Szmigiero }; 787cad347faSMaciej S. Szmigiero 788cad347faSMaciej S. Szmigiero static bool test_execute(int nslots, uint64_t *maxslots, 789cad347faSMaciej S. Szmigiero unsigned int maxtime, 790cad347faSMaciej S. Szmigiero const struct test_data *tdata, 791cad347faSMaciej S. Szmigiero uint64_t *nloops, 792cad347faSMaciej S. Szmigiero struct timespec *slot_runtime, 793cad347faSMaciej S. Szmigiero struct timespec *guest_runtime) 794cad347faSMaciej S. Szmigiero { 7958675c6f2SGavin Shan uint64_t mem_size = tdata->mem_size ? : MEM_SIZE; 796cad347faSMaciej S. Szmigiero struct vm_data *data; 797cad347faSMaciej S. Szmigiero struct sync_area *sync; 798cad347faSMaciej S. Szmigiero struct timespec tstart; 799cad347faSMaciej S. Szmigiero bool ret = true; 800cad347faSMaciej S. Szmigiero 801cad347faSMaciej S. Szmigiero data = alloc_vm(); 802cad347faSMaciej S. Szmigiero if (!prepare_vm(data, nslots, maxslots, tdata->guest_code, 803cad347faSMaciej S. Szmigiero mem_size, slot_runtime)) { 804cad347faSMaciej S. Szmigiero ret = false; 805cad347faSMaciej S. Szmigiero goto exit_free; 806cad347faSMaciej S. Szmigiero } 807cad347faSMaciej S. Szmigiero 808cad347faSMaciej S. Szmigiero sync = (typeof(sync))vm_gpa2hva(data, MEM_SYNC_GPA, NULL); 809cad347faSMaciej S. Szmigiero 8108675c6f2SGavin Shan sync->guest_page_size = data->vm->page_size; 811cad347faSMaciej S. Szmigiero if (tdata->prepare && 812cad347faSMaciej S. Szmigiero !tdata->prepare(data, sync, maxslots)) { 813cad347faSMaciej S. Szmigiero ret = false; 814cad347faSMaciej S. Szmigiero goto exit_free; 815cad347faSMaciej S. Szmigiero } 816cad347faSMaciej S. Szmigiero 817cad347faSMaciej S. Szmigiero launch_vm(data); 818cad347faSMaciej S. Szmigiero 819cad347faSMaciej S. Szmigiero clock_gettime(CLOCK_MONOTONIC, &tstart); 820cad347faSMaciej S. Szmigiero let_guest_run(sync); 821cad347faSMaciej S. Szmigiero 822cad347faSMaciej S. Szmigiero while (1) { 823cad347faSMaciej S. Szmigiero *guest_runtime = timespec_elapsed(tstart); 824cad347faSMaciej S. Szmigiero if (guest_runtime->tv_sec >= maxtime) 825cad347faSMaciej S. Szmigiero break; 826cad347faSMaciej S. Szmigiero 827cad347faSMaciej S. Szmigiero tdata->loop(data, sync); 828cad347faSMaciej S. Szmigiero 829cad347faSMaciej S. Szmigiero (*nloops)++; 830cad347faSMaciej S. Szmigiero } 831cad347faSMaciej S. Szmigiero 832cad347faSMaciej S. Szmigiero make_guest_exit(sync); 833cad347faSMaciej S. Szmigiero wait_guest_exit(data); 834cad347faSMaciej S. Szmigiero 835cad347faSMaciej S. Szmigiero exit_free: 836cad347faSMaciej S. Szmigiero free_vm(data); 837cad347faSMaciej S. Szmigiero 838cad347faSMaciej S. Szmigiero return ret; 839cad347faSMaciej S. Szmigiero } 840cad347faSMaciej S. Szmigiero 841cad347faSMaciej S. Szmigiero static const struct test_data tests[] = { 842cad347faSMaciej S. Szmigiero { 843cad347faSMaciej S. Szmigiero .name = "map", 8448675c6f2SGavin Shan .mem_size = MEM_SIZE_MAP, 845cad347faSMaciej S. Szmigiero .guest_code = guest_code_test_memslot_map, 846cad347faSMaciej S. Szmigiero .loop = test_memslot_map_loop, 847cad347faSMaciej S. Szmigiero }, 848cad347faSMaciej S. Szmigiero { 849cad347faSMaciej S. Szmigiero .name = "unmap", 85088a64e65SGavin Shan .mem_size = MEM_TEST_UNMAP_SIZE + MEM_EXTRA_SIZE, 851cad347faSMaciej S. Szmigiero .guest_code = guest_code_test_memslot_unmap, 852cad347faSMaciej S. Szmigiero .loop = test_memslot_unmap_loop, 853cad347faSMaciej S. Szmigiero }, 854cad347faSMaciej S. Szmigiero { 855cad347faSMaciej S. Szmigiero .name = "unmap chunked", 85688a64e65SGavin Shan .mem_size = MEM_TEST_UNMAP_SIZE + MEM_EXTRA_SIZE, 857cad347faSMaciej S. Szmigiero .guest_code = guest_code_test_memslot_unmap, 858cad347faSMaciej S. Szmigiero .loop = test_memslot_unmap_loop_chunked, 859cad347faSMaciej S. Szmigiero }, 860cad347faSMaciej S. Szmigiero { 861cad347faSMaciej S. Szmigiero .name = "move active area", 862cad347faSMaciej S. Szmigiero .guest_code = guest_code_test_memslot_move, 863cad347faSMaciej S. Szmigiero .prepare = test_memslot_move_prepare_active, 864cad347faSMaciej S. Szmigiero .loop = test_memslot_move_loop, 865cad347faSMaciej S. Szmigiero }, 866cad347faSMaciej S. Szmigiero { 867cad347faSMaciej S. Szmigiero .name = "move inactive area", 868cad347faSMaciej S. Szmigiero .guest_code = guest_code_test_memslot_move, 869cad347faSMaciej S. Szmigiero .prepare = test_memslot_move_prepare_inactive, 870cad347faSMaciej S. Szmigiero .loop = test_memslot_move_loop, 871cad347faSMaciej S. Szmigiero }, 872cad347faSMaciej S. Szmigiero { 873cad347faSMaciej S. Szmigiero .name = "RW", 874cad347faSMaciej S. Szmigiero .guest_code = guest_code_test_memslot_rw, 875cad347faSMaciej S. Szmigiero .loop = test_memslot_rw_loop 876cad347faSMaciej S. Szmigiero }, 877cad347faSMaciej S. Szmigiero }; 878cad347faSMaciej S. Szmigiero 879cad347faSMaciej S. Szmigiero #define NTESTS ARRAY_SIZE(tests) 880cad347faSMaciej S. Szmigiero 881cad347faSMaciej S. Szmigiero struct test_args { 882cad347faSMaciej S. Szmigiero int tfirst; 883cad347faSMaciej S. Szmigiero int tlast; 884cad347faSMaciej S. Szmigiero int nslots; 885cad347faSMaciej S. Szmigiero int seconds; 886cad347faSMaciej S. Szmigiero int runs; 887cad347faSMaciej S. Szmigiero }; 888cad347faSMaciej S. Szmigiero 889cad347faSMaciej S. Szmigiero static void help(char *name, struct test_args *targs) 890cad347faSMaciej S. Szmigiero { 891cad347faSMaciej S. Szmigiero int ctr; 892cad347faSMaciej S. Szmigiero 893cad347faSMaciej S. Szmigiero pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count]\n", 894cad347faSMaciej S. Szmigiero name); 895cad347faSMaciej S. Szmigiero pr_info(" -h: print this help screen.\n"); 896cad347faSMaciej S. Szmigiero pr_info(" -v: enable verbose mode (not for benchmarking).\n"); 897cad347faSMaciej S. Szmigiero pr_info(" -d: enable extra debug checks.\n"); 898cad347faSMaciej S. Szmigiero pr_info(" -s: specify memslot count cap (-1 means no cap; currently: %i)\n", 899cad347faSMaciej S. Szmigiero targs->nslots); 900cad347faSMaciej S. Szmigiero pr_info(" -f: specify the first test to run (currently: %i; max %zu)\n", 901cad347faSMaciej S. Szmigiero targs->tfirst, NTESTS - 1); 902cad347faSMaciej S. Szmigiero pr_info(" -e: specify the last test to run (currently: %i; max %zu)\n", 903cad347faSMaciej S. Szmigiero targs->tlast, NTESTS - 1); 904cad347faSMaciej S. Szmigiero pr_info(" -l: specify the test length in seconds (currently: %i)\n", 905cad347faSMaciej S. Szmigiero targs->seconds); 906cad347faSMaciej S. Szmigiero pr_info(" -r: specify the number of runs per test (currently: %i)\n", 907cad347faSMaciej S. Szmigiero targs->runs); 908cad347faSMaciej S. Szmigiero 909cad347faSMaciej S. Szmigiero pr_info("\nAvailable tests:\n"); 910cad347faSMaciej S. Szmigiero for (ctr = 0; ctr < NTESTS; ctr++) 911cad347faSMaciej S. Szmigiero pr_info("%d: %s\n", ctr, tests[ctr].name); 912cad347faSMaciej S. Szmigiero } 913cad347faSMaciej S. Szmigiero 9148675c6f2SGavin Shan static bool check_memory_sizes(void) 9158675c6f2SGavin Shan { 91688a64e65SGavin Shan uint32_t host_page_size = getpagesize(); 9178675c6f2SGavin Shan uint32_t guest_page_size = vm_guest_mode_params[VM_MODE_DEFAULT].page_size; 9188675c6f2SGavin Shan 91988a64e65SGavin Shan if (host_page_size > SZ_64K || guest_page_size > SZ_64K) { 92088a64e65SGavin Shan pr_info("Unsupported page size on host (0x%x) or guest (0x%x)\n", 92188a64e65SGavin Shan host_page_size, guest_page_size); 92288a64e65SGavin Shan return false; 92388a64e65SGavin Shan } 92488a64e65SGavin Shan 9258675c6f2SGavin Shan if (MEM_SIZE % guest_page_size || 9268675c6f2SGavin Shan MEM_TEST_SIZE % guest_page_size) { 9278675c6f2SGavin Shan pr_info("invalid MEM_SIZE or MEM_TEST_SIZE\n"); 9288675c6f2SGavin Shan return false; 9298675c6f2SGavin Shan } 9308675c6f2SGavin Shan 9318675c6f2SGavin Shan if (MEM_SIZE_MAP % guest_page_size || 9328675c6f2SGavin Shan MEM_TEST_MAP_SIZE % guest_page_size || 9338675c6f2SGavin Shan (MEM_TEST_MAP_SIZE / guest_page_size) <= 2 || 9348675c6f2SGavin Shan (MEM_TEST_MAP_SIZE / guest_page_size) % 2) { 9358675c6f2SGavin Shan pr_info("invalid MEM_SIZE_MAP or MEM_TEST_MAP_SIZE\n"); 9368675c6f2SGavin Shan return false; 9378675c6f2SGavin Shan } 9388675c6f2SGavin Shan 9398675c6f2SGavin Shan if (MEM_TEST_UNMAP_SIZE > MEM_TEST_SIZE || 9408675c6f2SGavin Shan MEM_TEST_UNMAP_SIZE % guest_page_size || 9418675c6f2SGavin Shan (MEM_TEST_UNMAP_SIZE / guest_page_size) % 9428675c6f2SGavin Shan (2 * MEM_TEST_UNMAP_CHUNK_SIZE / guest_page_size)) { 9438675c6f2SGavin Shan pr_info("invalid MEM_TEST_UNMAP_SIZE or MEM_TEST_UNMAP_CHUNK_SIZE\n"); 9448675c6f2SGavin Shan return false; 9458675c6f2SGavin Shan } 9468675c6f2SGavin Shan 9478675c6f2SGavin Shan return true; 9488675c6f2SGavin Shan } 9498675c6f2SGavin Shan 950cad347faSMaciej S. Szmigiero static bool parse_args(int argc, char *argv[], 951cad347faSMaciej S. Szmigiero struct test_args *targs) 952cad347faSMaciej S. Szmigiero { 95334396437SGavin Shan uint32_t max_mem_slots; 954cad347faSMaciej S. Szmigiero int opt; 955cad347faSMaciej S. Szmigiero 956cad347faSMaciej S. Szmigiero while ((opt = getopt(argc, argv, "hvds:f:e:l:r:")) != -1) { 957cad347faSMaciej S. Szmigiero switch (opt) { 958cad347faSMaciej S. Szmigiero case 'h': 959cad347faSMaciej S. Szmigiero default: 960cad347faSMaciej S. Szmigiero help(argv[0], targs); 961cad347faSMaciej S. Szmigiero return false; 962cad347faSMaciej S. Szmigiero case 'v': 963cad347faSMaciej S. Szmigiero verbose = true; 964cad347faSMaciej S. Szmigiero break; 965cad347faSMaciej S. Szmigiero case 'd': 966cad347faSMaciej S. Szmigiero map_unmap_verify = true; 967cad347faSMaciej S. Szmigiero break; 968cad347faSMaciej S. Szmigiero case 's': 969cad347faSMaciej S. Szmigiero targs->nslots = atoi(optarg); 97034396437SGavin Shan if (targs->nslots <= 1 && targs->nslots != -1) { 97134396437SGavin Shan pr_info("Slot count cap must be larger than 1 or -1 for no cap\n"); 972cad347faSMaciej S. Szmigiero return false; 973cad347faSMaciej S. Szmigiero } 974cad347faSMaciej S. Szmigiero break; 975cad347faSMaciej S. Szmigiero case 'f': 976cad347faSMaciej S. Szmigiero targs->tfirst = atoi(optarg); 977cad347faSMaciej S. Szmigiero if (targs->tfirst < 0) { 978cad347faSMaciej S. Szmigiero pr_info("First test to run has to be non-negative\n"); 979cad347faSMaciej S. Szmigiero return false; 980cad347faSMaciej S. Szmigiero } 981cad347faSMaciej S. Szmigiero break; 982cad347faSMaciej S. Szmigiero case 'e': 983cad347faSMaciej S. Szmigiero targs->tlast = atoi(optarg); 984cad347faSMaciej S. Szmigiero if (targs->tlast < 0 || targs->tlast >= NTESTS) { 985cad347faSMaciej S. Szmigiero pr_info("Last test to run has to be non-negative and less than %zu\n", 986cad347faSMaciej S. Szmigiero NTESTS); 987cad347faSMaciej S. Szmigiero return false; 988cad347faSMaciej S. Szmigiero } 989cad347faSMaciej S. Szmigiero break; 990cad347faSMaciej S. Szmigiero case 'l': 991cad347faSMaciej S. Szmigiero targs->seconds = atoi(optarg); 992cad347faSMaciej S. Szmigiero if (targs->seconds < 0) { 993cad347faSMaciej S. Szmigiero pr_info("Test length in seconds has to be non-negative\n"); 994cad347faSMaciej S. Szmigiero return false; 995cad347faSMaciej S. Szmigiero } 996cad347faSMaciej S. Szmigiero break; 997cad347faSMaciej S. Szmigiero case 'r': 998cad347faSMaciej S. Szmigiero targs->runs = atoi(optarg); 999cad347faSMaciej S. Szmigiero if (targs->runs <= 0) { 1000cad347faSMaciej S. Szmigiero pr_info("Runs per test has to be positive\n"); 1001cad347faSMaciej S. Szmigiero return false; 1002cad347faSMaciej S. Szmigiero } 1003cad347faSMaciej S. Szmigiero break; 1004cad347faSMaciej S. Szmigiero } 1005cad347faSMaciej S. Szmigiero } 1006cad347faSMaciej S. Szmigiero 1007cad347faSMaciej S. Szmigiero if (optind < argc) { 1008cad347faSMaciej S. Szmigiero help(argv[0], targs); 1009cad347faSMaciej S. Szmigiero return false; 1010cad347faSMaciej S. Szmigiero } 1011cad347faSMaciej S. Szmigiero 1012cad347faSMaciej S. Szmigiero if (targs->tfirst > targs->tlast) { 1013cad347faSMaciej S. Szmigiero pr_info("First test to run cannot be greater than the last test to run\n"); 1014cad347faSMaciej S. Szmigiero return false; 1015cad347faSMaciej S. Szmigiero } 1016cad347faSMaciej S. Szmigiero 101734396437SGavin Shan max_mem_slots = kvm_check_cap(KVM_CAP_NR_MEMSLOTS); 101834396437SGavin Shan if (max_mem_slots <= 1) { 101934396437SGavin Shan pr_info("KVM_CAP_NR_MEMSLOTS should be greater than 1\n"); 102034396437SGavin Shan return false; 102134396437SGavin Shan } 102234396437SGavin Shan 102334396437SGavin Shan /* Memory slot 0 is reserved */ 102434396437SGavin Shan if (targs->nslots == -1) 102534396437SGavin Shan targs->nslots = max_mem_slots - 1; 102634396437SGavin Shan else 102734396437SGavin Shan targs->nslots = min_t(int, targs->nslots, max_mem_slots) - 1; 102834396437SGavin Shan 102934396437SGavin Shan pr_info_v("Allowed Number of memory slots: %"PRIu32"\n", 103034396437SGavin Shan targs->nslots + 1); 103134396437SGavin Shan 1032cad347faSMaciej S. Szmigiero return true; 1033cad347faSMaciej S. Szmigiero } 1034cad347faSMaciej S. Szmigiero 1035cad347faSMaciej S. Szmigiero struct test_result { 1036cad347faSMaciej S. Szmigiero struct timespec slot_runtime, guest_runtime, iter_runtime; 1037cad347faSMaciej S. Szmigiero int64_t slottimens, runtimens; 1038cad347faSMaciej S. Szmigiero uint64_t nloops; 1039cad347faSMaciej S. Szmigiero }; 1040cad347faSMaciej S. Szmigiero 1041cad347faSMaciej S. Szmigiero static bool test_loop(const struct test_data *data, 1042cad347faSMaciej S. Szmigiero const struct test_args *targs, 1043cad347faSMaciej S. Szmigiero struct test_result *rbestslottime, 1044cad347faSMaciej S. Szmigiero struct test_result *rbestruntime) 1045cad347faSMaciej S. Szmigiero { 1046cad347faSMaciej S. Szmigiero uint64_t maxslots; 1047cad347faSMaciej S. Szmigiero struct test_result result; 1048cad347faSMaciej S. Szmigiero 1049cad347faSMaciej S. Szmigiero result.nloops = 0; 1050cad347faSMaciej S. Szmigiero if (!test_execute(targs->nslots, &maxslots, targs->seconds, data, 1051cad347faSMaciej S. Szmigiero &result.nloops, 1052cad347faSMaciej S. Szmigiero &result.slot_runtime, &result.guest_runtime)) { 1053cad347faSMaciej S. Szmigiero if (maxslots) 1054cad347faSMaciej S. Szmigiero pr_info("Memslot count too high for this test, decrease the cap (max is %"PRIu64")\n", 1055cad347faSMaciej S. Szmigiero maxslots); 1056cad347faSMaciej S. Szmigiero else 1057cad347faSMaciej S. Szmigiero pr_info("Memslot count may be too high for this test, try adjusting the cap\n"); 1058cad347faSMaciej S. Szmigiero 1059cad347faSMaciej S. Szmigiero return false; 1060cad347faSMaciej S. Szmigiero } 1061cad347faSMaciej S. Szmigiero 1062cad347faSMaciej S. Szmigiero pr_info("Test took %ld.%.9lds for slot setup + %ld.%.9lds all iterations\n", 1063cad347faSMaciej S. Szmigiero result.slot_runtime.tv_sec, result.slot_runtime.tv_nsec, 1064cad347faSMaciej S. Szmigiero result.guest_runtime.tv_sec, result.guest_runtime.tv_nsec); 1065cad347faSMaciej S. Szmigiero if (!result.nloops) { 1066cad347faSMaciej S. Szmigiero pr_info("No full loops done - too short test time or system too loaded?\n"); 1067cad347faSMaciej S. Szmigiero return true; 1068cad347faSMaciej S. Szmigiero } 1069cad347faSMaciej S. Szmigiero 1070cad347faSMaciej S. Szmigiero result.iter_runtime = timespec_div(result.guest_runtime, 1071cad347faSMaciej S. Szmigiero result.nloops); 1072cad347faSMaciej S. Szmigiero pr_info("Done %"PRIu64" iterations, avg %ld.%.9lds each\n", 1073cad347faSMaciej S. Szmigiero result.nloops, 1074cad347faSMaciej S. Szmigiero result.iter_runtime.tv_sec, 1075cad347faSMaciej S. Szmigiero result.iter_runtime.tv_nsec); 1076cad347faSMaciej S. Szmigiero result.slottimens = timespec_to_ns(result.slot_runtime); 1077cad347faSMaciej S. Szmigiero result.runtimens = timespec_to_ns(result.iter_runtime); 1078cad347faSMaciej S. Szmigiero 1079cad347faSMaciej S. Szmigiero /* 1080cad347faSMaciej S. Szmigiero * Only rank the slot setup time for tests using the whole test memory 1081cad347faSMaciej S. Szmigiero * area so they are comparable 1082cad347faSMaciej S. Szmigiero */ 1083cad347faSMaciej S. Szmigiero if (!data->mem_size && 1084cad347faSMaciej S. Szmigiero (!rbestslottime->slottimens || 1085cad347faSMaciej S. Szmigiero result.slottimens < rbestslottime->slottimens)) 1086cad347faSMaciej S. Szmigiero *rbestslottime = result; 1087cad347faSMaciej S. Szmigiero if (!rbestruntime->runtimens || 1088cad347faSMaciej S. Szmigiero result.runtimens < rbestruntime->runtimens) 1089cad347faSMaciej S. Szmigiero *rbestruntime = result; 1090cad347faSMaciej S. Szmigiero 1091cad347faSMaciej S. Szmigiero return true; 1092cad347faSMaciej S. Szmigiero } 1093cad347faSMaciej S. Szmigiero 1094cad347faSMaciej S. Szmigiero int main(int argc, char *argv[]) 1095cad347faSMaciej S. Szmigiero { 1096cad347faSMaciej S. Szmigiero struct test_args targs = { 1097cad347faSMaciej S. Szmigiero .tfirst = 0, 1098cad347faSMaciej S. Szmigiero .tlast = NTESTS - 1, 1099cad347faSMaciej S. Szmigiero .nslots = -1, 1100cad347faSMaciej S. Szmigiero .seconds = 5, 1101fb0f9479SPaolo Bonzini .runs = 1, 1102cad347faSMaciej S. Szmigiero }; 1103cad347faSMaciej S. Szmigiero struct test_result rbestslottime; 1104cad347faSMaciej S. Szmigiero int tctr; 1105cad347faSMaciej S. Szmigiero 1106cad347faSMaciej S. Szmigiero /* Tell stdout not to buffer its content */ 1107cad347faSMaciej S. Szmigiero setbuf(stdout, NULL); 1108cad347faSMaciej S. Szmigiero 11098675c6f2SGavin Shan if (!check_memory_sizes()) 11108675c6f2SGavin Shan return -1; 11118675c6f2SGavin Shan 1112cad347faSMaciej S. Szmigiero if (!parse_args(argc, argv, &targs)) 1113cad347faSMaciej S. Szmigiero return -1; 1114cad347faSMaciej S. Szmigiero 1115cad347faSMaciej S. Szmigiero rbestslottime.slottimens = 0; 1116cad347faSMaciej S. Szmigiero for (tctr = targs.tfirst; tctr <= targs.tlast; tctr++) { 1117cad347faSMaciej S. Szmigiero const struct test_data *data = &tests[tctr]; 1118cad347faSMaciej S. Szmigiero unsigned int runctr; 1119cad347faSMaciej S. Szmigiero struct test_result rbestruntime; 1120cad347faSMaciej S. Szmigiero 1121cad347faSMaciej S. Szmigiero if (tctr > targs.tfirst) 1122cad347faSMaciej S. Szmigiero pr_info("\n"); 1123cad347faSMaciej S. Szmigiero 1124cad347faSMaciej S. Szmigiero pr_info("Testing %s performance with %i runs, %d seconds each\n", 1125cad347faSMaciej S. Szmigiero data->name, targs.runs, targs.seconds); 1126cad347faSMaciej S. Szmigiero 1127cad347faSMaciej S. Szmigiero rbestruntime.runtimens = 0; 1128cad347faSMaciej S. Szmigiero for (runctr = 0; runctr < targs.runs; runctr++) 1129cad347faSMaciej S. Szmigiero if (!test_loop(data, &targs, 1130cad347faSMaciej S. Szmigiero &rbestslottime, &rbestruntime)) 1131cad347faSMaciej S. Szmigiero break; 1132cad347faSMaciej S. Szmigiero 1133cad347faSMaciej S. Szmigiero if (rbestruntime.runtimens) 1134cad347faSMaciej S. Szmigiero pr_info("Best runtime result was %ld.%.9lds per iteration (with %"PRIu64" iterations)\n", 1135cad347faSMaciej S. Szmigiero rbestruntime.iter_runtime.tv_sec, 1136cad347faSMaciej S. Szmigiero rbestruntime.iter_runtime.tv_nsec, 1137cad347faSMaciej S. Szmigiero rbestruntime.nloops); 1138cad347faSMaciej S. Szmigiero } 1139cad347faSMaciej S. Szmigiero 1140cad347faSMaciej S. Szmigiero if (rbestslottime.slottimens) 1141cad347faSMaciej S. Szmigiero pr_info("Best slot setup time for the whole test area was %ld.%.9lds\n", 1142cad347faSMaciej S. Szmigiero rbestslottime.slot_runtime.tv_sec, 1143cad347faSMaciej S. Szmigiero rbestslottime.slot_runtime.tv_nsec); 1144cad347faSMaciej S. Szmigiero 1145cad347faSMaciej S. Szmigiero return 0; 1146cad347faSMaciej S. Szmigiero } 1147