18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * KVM demand paging test 48c2ecf20Sopenharmony_ci * Adapted from dirty_log_test.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2018, Red Hat, Inc. 78c2ecf20Sopenharmony_ci * Copyright (C) 2019, Google, Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define _GNU_SOURCE /* for program_invocation_name */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <stdio.h> 138c2ecf20Sopenharmony_ci#include <stdlib.h> 148c2ecf20Sopenharmony_ci#include <sys/syscall.h> 158c2ecf20Sopenharmony_ci#include <unistd.h> 168c2ecf20Sopenharmony_ci#include <asm/unistd.h> 178c2ecf20Sopenharmony_ci#include <time.h> 188c2ecf20Sopenharmony_ci#include <poll.h> 198c2ecf20Sopenharmony_ci#include <pthread.h> 208c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 218c2ecf20Sopenharmony_ci#include <linux/bitops.h> 228c2ecf20Sopenharmony_ci#include <linux/userfaultfd.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "perf_test_util.h" 258c2ecf20Sopenharmony_ci#include "processor.h" 268c2ecf20Sopenharmony_ci#include "test_util.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#ifdef __NR_userfaultfd 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#ifdef PRINT_PER_PAGE_UPDATES 318c2ecf20Sopenharmony_ci#define PER_PAGE_DEBUG(...) printf(__VA_ARGS__) 328c2ecf20Sopenharmony_ci#else 338c2ecf20Sopenharmony_ci#define PER_PAGE_DEBUG(...) _no_printf(__VA_ARGS__) 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#ifdef PRINT_PER_VCPU_UPDATES 378c2ecf20Sopenharmony_ci#define PER_VCPU_DEBUG(...) printf(__VA_ARGS__) 388c2ecf20Sopenharmony_ci#else 398c2ecf20Sopenharmony_ci#define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__) 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic char *guest_data_prototype; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void *vcpu_worker(void *data) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci int ret; 478c2ecf20Sopenharmony_ci struct vcpu_args *vcpu_args = (struct vcpu_args *)data; 488c2ecf20Sopenharmony_ci int vcpu_id = vcpu_args->vcpu_id; 498c2ecf20Sopenharmony_ci struct kvm_vm *vm = perf_test_args.vm; 508c2ecf20Sopenharmony_ci struct kvm_run *run; 518c2ecf20Sopenharmony_ci struct timespec start; 528c2ecf20Sopenharmony_ci struct timespec ts_diff; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci vcpu_args_set(vm, vcpu_id, 1, vcpu_id); 558c2ecf20Sopenharmony_ci run = vcpu_state(vm, vcpu_id); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* Let the guest access its memory */ 608c2ecf20Sopenharmony_ci ret = _vcpu_run(vm, vcpu_id); 618c2ecf20Sopenharmony_ci TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); 628c2ecf20Sopenharmony_ci if (get_ucall(vm, vcpu_id, NULL) != UCALL_SYNC) { 638c2ecf20Sopenharmony_ci TEST_ASSERT(false, 648c2ecf20Sopenharmony_ci "Invalid guest sync status: exit_reason=%s\n", 658c2ecf20Sopenharmony_ci exit_reason_str(run->exit_reason)); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ts_diff = timespec_diff_now(start); 698c2ecf20Sopenharmony_ci PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_id, 708c2ecf20Sopenharmony_ci ts_diff.tv_sec, ts_diff.tv_nsec); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return NULL; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int handle_uffd_page_request(int uffd, uint64_t addr) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci pid_t tid; 788c2ecf20Sopenharmony_ci struct timespec start; 798c2ecf20Sopenharmony_ci struct timespec ts_diff; 808c2ecf20Sopenharmony_ci struct uffdio_copy copy; 818c2ecf20Sopenharmony_ci int r; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci tid = syscall(__NR_gettid); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci copy.src = (uint64_t)guest_data_prototype; 868c2ecf20Sopenharmony_ci copy.dst = addr; 878c2ecf20Sopenharmony_ci copy.len = perf_test_args.host_page_size; 888c2ecf20Sopenharmony_ci copy.mode = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci r = ioctl(uffd, UFFDIO_COPY, ©); 938c2ecf20Sopenharmony_ci if (r == -1) { 948c2ecf20Sopenharmony_ci pr_info("Failed Paged in 0x%lx from thread %d with errno: %d\n", 958c2ecf20Sopenharmony_ci addr, tid, errno); 968c2ecf20Sopenharmony_ci return r; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ts_diff = timespec_diff_now(start); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci PER_PAGE_DEBUG("UFFDIO_COPY %d \t%ld ns\n", tid, 1028c2ecf20Sopenharmony_ci timespec_to_ns(ts_diff)); 1038c2ecf20Sopenharmony_ci PER_PAGE_DEBUG("Paged in %ld bytes at 0x%lx from thread %d\n", 1048c2ecf20Sopenharmony_ci perf_test_args.host_page_size, addr, tid); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cibool quit_uffd_thread; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct uffd_handler_args { 1128c2ecf20Sopenharmony_ci int uffd; 1138c2ecf20Sopenharmony_ci int pipefd; 1148c2ecf20Sopenharmony_ci useconds_t delay; 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void *uffd_handler_thread_fn(void *arg) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct uffd_handler_args *uffd_args = (struct uffd_handler_args *)arg; 1208c2ecf20Sopenharmony_ci int uffd = uffd_args->uffd; 1218c2ecf20Sopenharmony_ci int pipefd = uffd_args->pipefd; 1228c2ecf20Sopenharmony_ci useconds_t delay = uffd_args->delay; 1238c2ecf20Sopenharmony_ci int64_t pages = 0; 1248c2ecf20Sopenharmony_ci struct timespec start; 1258c2ecf20Sopenharmony_ci struct timespec ts_diff; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 1288c2ecf20Sopenharmony_ci while (!quit_uffd_thread) { 1298c2ecf20Sopenharmony_ci struct uffd_msg msg; 1308c2ecf20Sopenharmony_ci struct pollfd pollfd[2]; 1318c2ecf20Sopenharmony_ci char tmp_chr; 1328c2ecf20Sopenharmony_ci int r; 1338c2ecf20Sopenharmony_ci uint64_t addr; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci pollfd[0].fd = uffd; 1368c2ecf20Sopenharmony_ci pollfd[0].events = POLLIN; 1378c2ecf20Sopenharmony_ci pollfd[1].fd = pipefd; 1388c2ecf20Sopenharmony_ci pollfd[1].events = POLLIN; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci r = poll(pollfd, 2, -1); 1418c2ecf20Sopenharmony_ci switch (r) { 1428c2ecf20Sopenharmony_ci case -1: 1438c2ecf20Sopenharmony_ci pr_info("poll err"); 1448c2ecf20Sopenharmony_ci continue; 1458c2ecf20Sopenharmony_ci case 0: 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci case 1: 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci default: 1508c2ecf20Sopenharmony_ci pr_info("Polling uffd returned %d", r); 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (pollfd[0].revents & POLLERR) { 1558c2ecf20Sopenharmony_ci pr_info("uffd revents has POLLERR"); 1568c2ecf20Sopenharmony_ci return NULL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (pollfd[1].revents & POLLIN) { 1608c2ecf20Sopenharmony_ci r = read(pollfd[1].fd, &tmp_chr, 1); 1618c2ecf20Sopenharmony_ci TEST_ASSERT(r == 1, 1628c2ecf20Sopenharmony_ci "Error reading pipefd in UFFD thread\n"); 1638c2ecf20Sopenharmony_ci return NULL; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (!pollfd[0].revents & POLLIN) 1678c2ecf20Sopenharmony_ci continue; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci r = read(uffd, &msg, sizeof(msg)); 1708c2ecf20Sopenharmony_ci if (r == -1) { 1718c2ecf20Sopenharmony_ci if (errno == EAGAIN) 1728c2ecf20Sopenharmony_ci continue; 1738c2ecf20Sopenharmony_ci pr_info("Read of uffd gor errno %d", errno); 1748c2ecf20Sopenharmony_ci return NULL; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (r != sizeof(msg)) { 1788c2ecf20Sopenharmony_ci pr_info("Read on uffd returned unexpected size: %d bytes", r); 1798c2ecf20Sopenharmony_ci return NULL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!(msg.event & UFFD_EVENT_PAGEFAULT)) 1838c2ecf20Sopenharmony_ci continue; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (delay) 1868c2ecf20Sopenharmony_ci usleep(delay); 1878c2ecf20Sopenharmony_ci addr = msg.arg.pagefault.address; 1888c2ecf20Sopenharmony_ci r = handle_uffd_page_request(uffd, addr); 1898c2ecf20Sopenharmony_ci if (r < 0) 1908c2ecf20Sopenharmony_ci return NULL; 1918c2ecf20Sopenharmony_ci pages++; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ts_diff = timespec_diff_now(start); 1958c2ecf20Sopenharmony_ci PER_VCPU_DEBUG("userfaulted %ld pages over %ld.%.9lds. (%f/sec)\n", 1968c2ecf20Sopenharmony_ci pages, ts_diff.tv_sec, ts_diff.tv_nsec, 1978c2ecf20Sopenharmony_ci pages / ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0)); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return NULL; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int setup_demand_paging(struct kvm_vm *vm, 2038c2ecf20Sopenharmony_ci pthread_t *uffd_handler_thread, int pipefd, 2048c2ecf20Sopenharmony_ci useconds_t uffd_delay, 2058c2ecf20Sopenharmony_ci struct uffd_handler_args *uffd_args, 2068c2ecf20Sopenharmony_ci void *hva, uint64_t len) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int uffd; 2098c2ecf20Sopenharmony_ci struct uffdio_api uffdio_api; 2108c2ecf20Sopenharmony_ci struct uffdio_register uffdio_register; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); 2138c2ecf20Sopenharmony_ci if (uffd == -1) { 2148c2ecf20Sopenharmony_ci pr_info("uffd creation failed\n"); 2158c2ecf20Sopenharmony_ci return -1; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci uffdio_api.api = UFFD_API; 2198c2ecf20Sopenharmony_ci uffdio_api.features = 0; 2208c2ecf20Sopenharmony_ci if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) { 2218c2ecf20Sopenharmony_ci pr_info("ioctl uffdio_api failed\n"); 2228c2ecf20Sopenharmony_ci return -1; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci uffdio_register.range.start = (uint64_t)hva; 2268c2ecf20Sopenharmony_ci uffdio_register.range.len = len; 2278c2ecf20Sopenharmony_ci uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; 2288c2ecf20Sopenharmony_ci if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) { 2298c2ecf20Sopenharmony_ci pr_info("ioctl uffdio_register failed\n"); 2308c2ecf20Sopenharmony_ci return -1; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if ((uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) != 2348c2ecf20Sopenharmony_ci UFFD_API_RANGE_IOCTLS) { 2358c2ecf20Sopenharmony_ci pr_info("unexpected userfaultfd ioctl set\n"); 2368c2ecf20Sopenharmony_ci return -1; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci uffd_args->uffd = uffd; 2408c2ecf20Sopenharmony_ci uffd_args->pipefd = pipefd; 2418c2ecf20Sopenharmony_ci uffd_args->delay = uffd_delay; 2428c2ecf20Sopenharmony_ci pthread_create(uffd_handler_thread, NULL, uffd_handler_thread_fn, 2438c2ecf20Sopenharmony_ci uffd_args); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci PER_VCPU_DEBUG("Created uffd thread for HVA range [%p, %p)\n", 2468c2ecf20Sopenharmony_ci hva, hva + len); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void run_test(enum vm_guest_mode mode, bool use_uffd, 2528c2ecf20Sopenharmony_ci useconds_t uffd_delay) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci pthread_t *vcpu_threads; 2558c2ecf20Sopenharmony_ci pthread_t *uffd_handler_threads = NULL; 2568c2ecf20Sopenharmony_ci struct uffd_handler_args *uffd_args = NULL; 2578c2ecf20Sopenharmony_ci struct timespec start; 2588c2ecf20Sopenharmony_ci struct timespec ts_diff; 2598c2ecf20Sopenharmony_ci int *pipefds = NULL; 2608c2ecf20Sopenharmony_ci struct kvm_vm *vm; 2618c2ecf20Sopenharmony_ci int vcpu_id; 2628c2ecf20Sopenharmony_ci int r; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci perf_test_args.wr_fract = 1; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci guest_data_prototype = malloc(perf_test_args.host_page_size); 2698c2ecf20Sopenharmony_ci TEST_ASSERT(guest_data_prototype, 2708c2ecf20Sopenharmony_ci "Failed to allocate buffer for guest data pattern"); 2718c2ecf20Sopenharmony_ci memset(guest_data_prototype, 0xAB, perf_test_args.host_page_size); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads)); 2748c2ecf20Sopenharmony_ci TEST_ASSERT(vcpu_threads, "Memory allocation failed"); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci add_vcpus(vm, nr_vcpus, guest_percpu_mem_size); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (use_uffd) { 2798c2ecf20Sopenharmony_ci uffd_handler_threads = 2808c2ecf20Sopenharmony_ci malloc(nr_vcpus * sizeof(*uffd_handler_threads)); 2818c2ecf20Sopenharmony_ci TEST_ASSERT(uffd_handler_threads, "Memory allocation failed"); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci uffd_args = malloc(nr_vcpus * sizeof(*uffd_args)); 2848c2ecf20Sopenharmony_ci TEST_ASSERT(uffd_args, "Memory allocation failed"); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci pipefds = malloc(sizeof(int) * nr_vcpus * 2); 2878c2ecf20Sopenharmony_ci TEST_ASSERT(pipefds, "Unable to allocate memory for pipefd"); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 2908c2ecf20Sopenharmony_ci vm_paddr_t vcpu_gpa; 2918c2ecf20Sopenharmony_ci void *vcpu_hva; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci vcpu_gpa = guest_test_phys_mem + (vcpu_id * guest_percpu_mem_size); 2948c2ecf20Sopenharmony_ci PER_VCPU_DEBUG("Added VCPU %d with test mem gpa [%lx, %lx)\n", 2958c2ecf20Sopenharmony_ci vcpu_id, vcpu_gpa, vcpu_gpa + guest_percpu_mem_size); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Cache the HVA pointer of the region */ 2988c2ecf20Sopenharmony_ci vcpu_hva = addr_gpa2hva(vm, vcpu_gpa); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Set up user fault fd to handle demand paging 3028c2ecf20Sopenharmony_ci * requests. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci r = pipe2(&pipefds[vcpu_id * 2], 3058c2ecf20Sopenharmony_ci O_CLOEXEC | O_NONBLOCK); 3068c2ecf20Sopenharmony_ci TEST_ASSERT(!r, "Failed to set up pipefd"); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci r = setup_demand_paging(vm, 3098c2ecf20Sopenharmony_ci &uffd_handler_threads[vcpu_id], 3108c2ecf20Sopenharmony_ci pipefds[vcpu_id * 2], 3118c2ecf20Sopenharmony_ci uffd_delay, &uffd_args[vcpu_id], 3128c2ecf20Sopenharmony_ci vcpu_hva, guest_percpu_mem_size); 3138c2ecf20Sopenharmony_ci if (r < 0) 3148c2ecf20Sopenharmony_ci exit(-r); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Export the shared variables to the guest */ 3198c2ecf20Sopenharmony_ci sync_global_to_guest(vm, perf_test_args); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci pr_info("Finished creating vCPUs and starting uffd threads\n"); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 3268c2ecf20Sopenharmony_ci pthread_create(&vcpu_threads[vcpu_id], NULL, vcpu_worker, 3278c2ecf20Sopenharmony_ci &perf_test_args.vcpu_args[vcpu_id]); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci pr_info("Started all vCPUs\n"); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Wait for the vcpu threads to quit */ 3338c2ecf20Sopenharmony_ci for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 3348c2ecf20Sopenharmony_ci pthread_join(vcpu_threads[vcpu_id], NULL); 3358c2ecf20Sopenharmony_ci PER_VCPU_DEBUG("Joined thread for vCPU %d\n", vcpu_id); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ts_diff = timespec_diff_now(start); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci pr_info("All vCPU threads joined\n"); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (use_uffd) { 3438c2ecf20Sopenharmony_ci char c; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Tell the user fault fd handler threads to quit */ 3468c2ecf20Sopenharmony_ci for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 3478c2ecf20Sopenharmony_ci r = write(pipefds[vcpu_id * 2 + 1], &c, 1); 3488c2ecf20Sopenharmony_ci TEST_ASSERT(r == 1, "Unable to write to pipefd"); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci pthread_join(uffd_handler_threads[vcpu_id], NULL); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci pr_info("Total guest execution time: %ld.%.9lds\n", 3558c2ecf20Sopenharmony_ci ts_diff.tv_sec, ts_diff.tv_nsec); 3568c2ecf20Sopenharmony_ci pr_info("Overall demand paging rate: %f pgs/sec\n", 3578c2ecf20Sopenharmony_ci perf_test_args.vcpu_args[0].pages * nr_vcpus / 3588c2ecf20Sopenharmony_ci ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0)); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ucall_uninit(vm); 3618c2ecf20Sopenharmony_ci kvm_vm_free(vm); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci free(guest_data_prototype); 3648c2ecf20Sopenharmony_ci free(vcpu_threads); 3658c2ecf20Sopenharmony_ci if (use_uffd) { 3668c2ecf20Sopenharmony_ci free(uffd_handler_threads); 3678c2ecf20Sopenharmony_ci free(uffd_args); 3688c2ecf20Sopenharmony_ci free(pipefds); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistruct guest_mode { 3738c2ecf20Sopenharmony_ci bool supported; 3748c2ecf20Sopenharmony_ci bool enabled; 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_cistatic struct guest_mode guest_modes[NUM_VM_MODES]; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci#define guest_mode_init(mode, supported, enabled) ({ \ 3798c2ecf20Sopenharmony_ci guest_modes[mode] = (struct guest_mode){ supported, enabled }; \ 3808c2ecf20Sopenharmony_ci}) 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void help(char *name) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int i; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci puts(""); 3878c2ecf20Sopenharmony_ci printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n" 3888c2ecf20Sopenharmony_ci " [-b memory] [-v vcpus]\n", name); 3898c2ecf20Sopenharmony_ci printf(" -m: specify the guest mode ID to test\n" 3908c2ecf20Sopenharmony_ci " (default: test all supported modes)\n" 3918c2ecf20Sopenharmony_ci " This option may be used multiple times.\n" 3928c2ecf20Sopenharmony_ci " Guest mode IDs:\n"); 3938c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VM_MODES; ++i) { 3948c2ecf20Sopenharmony_ci printf(" %d: %s%s\n", i, vm_guest_mode_string(i), 3958c2ecf20Sopenharmony_ci guest_modes[i].supported ? " (supported)" : ""); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci printf(" -u: use User Fault FD to handle vCPU page\n" 3988c2ecf20Sopenharmony_ci " faults.\n"); 3998c2ecf20Sopenharmony_ci printf(" -d: add a delay in usec to the User Fault\n" 4008c2ecf20Sopenharmony_ci " FD handler to simulate demand paging\n" 4018c2ecf20Sopenharmony_ci " overheads. Ignored without -u.\n"); 4028c2ecf20Sopenharmony_ci printf(" -b: specify the size of the memory region which should be\n" 4038c2ecf20Sopenharmony_ci " demand paged by each vCPU. e.g. 10M or 3G.\n" 4048c2ecf20Sopenharmony_ci " Default: 1G\n"); 4058c2ecf20Sopenharmony_ci printf(" -v: specify the number of vCPUs to run.\n"); 4068c2ecf20Sopenharmony_ci puts(""); 4078c2ecf20Sopenharmony_ci exit(0); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ciint main(int argc, char *argv[]) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS); 4138c2ecf20Sopenharmony_ci bool mode_selected = false; 4148c2ecf20Sopenharmony_ci unsigned int mode; 4158c2ecf20Sopenharmony_ci int opt, i; 4168c2ecf20Sopenharmony_ci bool use_uffd = false; 4178c2ecf20Sopenharmony_ci useconds_t uffd_delay = 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci#ifdef __x86_64__ 4208c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_PXXV48_4K, true, true); 4218c2ecf20Sopenharmony_ci#endif 4228c2ecf20Sopenharmony_ci#ifdef __aarch64__ 4238c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P40V48_4K, true, true); 4248c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P40V48_64K, true, true); 4258c2ecf20Sopenharmony_ci { 4268c2ecf20Sopenharmony_ci unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (limit >= 52) 4298c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P52V48_64K, true, true); 4308c2ecf20Sopenharmony_ci if (limit >= 48) { 4318c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P48V48_4K, true, true); 4328c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P48V48_64K, true, true); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci#endif 4368c2ecf20Sopenharmony_ci#ifdef __s390x__ 4378c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P40V48_4K, true, true); 4388c2ecf20Sopenharmony_ci#endif 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci while ((opt = getopt(argc, argv, "hm:ud:b:v:")) != -1) { 4418c2ecf20Sopenharmony_ci switch (opt) { 4428c2ecf20Sopenharmony_ci case 'm': 4438c2ecf20Sopenharmony_ci if (!mode_selected) { 4448c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VM_MODES; ++i) 4458c2ecf20Sopenharmony_ci guest_modes[i].enabled = false; 4468c2ecf20Sopenharmony_ci mode_selected = true; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci mode = strtoul(optarg, NULL, 10); 4498c2ecf20Sopenharmony_ci TEST_ASSERT(mode < NUM_VM_MODES, 4508c2ecf20Sopenharmony_ci "Guest mode ID %d too big", mode); 4518c2ecf20Sopenharmony_ci guest_modes[mode].enabled = true; 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case 'u': 4548c2ecf20Sopenharmony_ci use_uffd = true; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci case 'd': 4578c2ecf20Sopenharmony_ci uffd_delay = strtoul(optarg, NULL, 0); 4588c2ecf20Sopenharmony_ci TEST_ASSERT(uffd_delay >= 0, 4598c2ecf20Sopenharmony_ci "A negative UFFD delay is not supported."); 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case 'b': 4628c2ecf20Sopenharmony_ci guest_percpu_mem_size = parse_size(optarg); 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci case 'v': 4658c2ecf20Sopenharmony_ci nr_vcpus = atoi(optarg); 4668c2ecf20Sopenharmony_ci TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus, 4678c2ecf20Sopenharmony_ci "Invalid number of vcpus, must be between 1 and %d", max_vcpus); 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case 'h': 4708c2ecf20Sopenharmony_ci default: 4718c2ecf20Sopenharmony_ci help(argv[0]); 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VM_MODES; ++i) { 4778c2ecf20Sopenharmony_ci if (!guest_modes[i].enabled) 4788c2ecf20Sopenharmony_ci continue; 4798c2ecf20Sopenharmony_ci TEST_ASSERT(guest_modes[i].supported, 4808c2ecf20Sopenharmony_ci "Guest mode ID %d (%s) not supported.", 4818c2ecf20Sopenharmony_ci i, vm_guest_mode_string(i)); 4828c2ecf20Sopenharmony_ci run_test(i, use_uffd, uffd_delay); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci#else /* __NR_userfaultfd */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci#warning "missing __NR_userfaultfd definition" 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciint main(void) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci print_skip("__NR_userfaultfd must be present for userfaultfd test"); 4958c2ecf20Sopenharmony_ci return KSFT_SKIP; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci#endif /* __NR_userfaultfd */ 499