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, &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