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