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