xref: /openbmc/linux/tools/testing/selftests/kvm/memslot_modification_stress_test.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1f73a3446SBen Gardon // SPDX-License-Identifier: GPL-2.0
2f73a3446SBen Gardon /*
3f73a3446SBen Gardon  * KVM memslot modification stress test
4f73a3446SBen Gardon  * Adapted from demand_paging_test.c
5f73a3446SBen Gardon  *
6f73a3446SBen Gardon  * Copyright (C) 2018, Red Hat, Inc.
7f73a3446SBen Gardon  * Copyright (C) 2020, Google, Inc.
8f73a3446SBen Gardon  */
9f73a3446SBen Gardon 
10f73a3446SBen Gardon #define _GNU_SOURCE /* for program_invocation_name */
11f73a3446SBen Gardon 
12f73a3446SBen Gardon #include <stdio.h>
13f73a3446SBen Gardon #include <stdlib.h>
14f73a3446SBen Gardon #include <sys/syscall.h>
15f73a3446SBen Gardon #include <unistd.h>
16f73a3446SBen Gardon #include <asm/unistd.h>
17f73a3446SBen Gardon #include <time.h>
18f73a3446SBen Gardon #include <poll.h>
19f73a3446SBen Gardon #include <pthread.h>
20f73a3446SBen Gardon #include <linux/bitmap.h>
21f73a3446SBen Gardon #include <linux/bitops.h>
22f73a3446SBen Gardon #include <linux/userfaultfd.h>
23f73a3446SBen Gardon 
249fda6753SDavid Matlack #include "memstress.h"
25f73a3446SBen Gardon #include "processor.h"
26f73a3446SBen Gardon #include "test_util.h"
27f73a3446SBen Gardon #include "guest_modes.h"
28f73a3446SBen Gardon 
29f73a3446SBen Gardon #define DUMMY_MEMSLOT_INDEX 7
30f73a3446SBen Gardon 
31f73a3446SBen Gardon #define DEFAULT_MEMSLOT_MODIFICATION_ITERATIONS 10
32f73a3446SBen Gardon 
33f73a3446SBen Gardon 
34f73a3446SBen Gardon static int nr_vcpus = 1;
35f73a3446SBen Gardon static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
36f73a3446SBen Gardon 
vcpu_worker(struct memstress_vcpu_args * vcpu_args)377812d80cSDavid Matlack static void vcpu_worker(struct memstress_vcpu_args *vcpu_args)
38f73a3446SBen Gardon {
39df84cef5SSean Christopherson 	struct kvm_vcpu *vcpu = vcpu_args->vcpu;
40f73a3446SBen Gardon 	struct kvm_run *run;
41df84cef5SSean Christopherson 	int ret;
42f73a3446SBen Gardon 
43df84cef5SSean Christopherson 	run = vcpu->run;
44f73a3446SBen Gardon 
45f73a3446SBen Gardon 	/* Let the guest access its memory until a stop signal is received */
46*eb561891SPaolo Bonzini 	while (!READ_ONCE(memstress_args.stop_vcpus)) {
47768e9a61SSean Christopherson 		ret = _vcpu_run(vcpu);
48f73a3446SBen Gardon 		TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
49f73a3446SBen Gardon 
50768e9a61SSean Christopherson 		if (get_ucall(vcpu, NULL) == UCALL_SYNC)
51f73a3446SBen Gardon 			continue;
52f73a3446SBen Gardon 
53f73a3446SBen Gardon 		TEST_ASSERT(false,
54f73a3446SBen Gardon 			    "Invalid guest sync status: exit_reason=%s\n",
55f73a3446SBen Gardon 			    exit_reason_str(run->exit_reason));
56f73a3446SBen Gardon 	}
57f73a3446SBen Gardon }
58f73a3446SBen Gardon 
59f73a3446SBen Gardon struct memslot_antagonist_args {
60f73a3446SBen Gardon 	struct kvm_vm *vm;
61f73a3446SBen Gardon 	useconds_t delay;
62f73a3446SBen Gardon 	uint64_t nr_modifications;
63f73a3446SBen Gardon };
64f73a3446SBen Gardon 
add_remove_memslot(struct kvm_vm * vm,useconds_t delay,uint64_t nr_modifications)65f73a3446SBen Gardon static void add_remove_memslot(struct kvm_vm *vm, useconds_t delay,
66ef4c9f4fSDavid Matlack 			       uint64_t nr_modifications)
67f73a3446SBen Gardon {
6805c2224dSGavin Shan 	uint64_t pages = max_t(int, vm->page_size, getpagesize()) / vm->page_size;
69ef4c9f4fSDavid Matlack 	uint64_t gpa;
70f73a3446SBen Gardon 	int i;
71f73a3446SBen Gardon 
72ef4c9f4fSDavid Matlack 	/*
739fda6753SDavid Matlack 	 * Add the dummy memslot just below the memstress memslot, which is
74ef4c9f4fSDavid Matlack 	 * at the top of the guest physical address space.
75ef4c9f4fSDavid Matlack 	 */
767812d80cSDavid Matlack 	gpa = memstress_args.gpa - pages * vm->page_size;
77ef4c9f4fSDavid Matlack 
78f73a3446SBen Gardon 	for (i = 0; i < nr_modifications; i++) {
79f73a3446SBen Gardon 		usleep(delay);
80f73a3446SBen Gardon 		vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, gpa,
81ef4c9f4fSDavid Matlack 					    DUMMY_MEMSLOT_INDEX, pages, 0);
82f73a3446SBen Gardon 
83f73a3446SBen Gardon 		vm_mem_region_delete(vm, DUMMY_MEMSLOT_INDEX);
84f73a3446SBen Gardon 	}
85f73a3446SBen Gardon }
86f73a3446SBen Gardon 
87f73a3446SBen Gardon struct test_params {
88c15bdebbSVipin Sharma 	useconds_t delay;
89c15bdebbSVipin Sharma 	uint64_t nr_iterations;
90f73a3446SBen Gardon 	bool partition_vcpu_memory_access;
91f73a3446SBen Gardon };
92f73a3446SBen Gardon 
run_test(enum vm_guest_mode mode,void * arg)93f73a3446SBen Gardon static void run_test(enum vm_guest_mode mode, void *arg)
94f73a3446SBen Gardon {
95f73a3446SBen Gardon 	struct test_params *p = arg;
96f73a3446SBen Gardon 	struct kvm_vm *vm;
97f73a3446SBen Gardon 
987812d80cSDavid Matlack 	vm = memstress_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 1,
99cf1d5930SSean Christopherson 				 VM_MEM_SRC_ANONYMOUS,
100cf1d5930SSean Christopherson 				 p->partition_vcpu_memory_access);
101f73a3446SBen Gardon 
102f73a3446SBen Gardon 	pr_info("Finished creating vCPUs\n");
103f73a3446SBen Gardon 
1047812d80cSDavid Matlack 	memstress_start_vcpu_threads(nr_vcpus, vcpu_worker);
105f73a3446SBen Gardon 
106f73a3446SBen Gardon 	pr_info("Started all vCPUs\n");
107f73a3446SBen Gardon 
108c15bdebbSVipin Sharma 	add_remove_memslot(vm, p->delay, p->nr_iterations);
109f73a3446SBen Gardon 
1107812d80cSDavid Matlack 	memstress_join_vcpu_threads(nr_vcpus);
111f73a3446SBen Gardon 	pr_info("All vCPU threads joined\n");
112f73a3446SBen Gardon 
1137812d80cSDavid Matlack 	memstress_destroy_vm(vm);
114f73a3446SBen Gardon }
115f73a3446SBen Gardon 
help(char * name)116f73a3446SBen Gardon static void help(char *name)
117f73a3446SBen Gardon {
118f73a3446SBen Gardon 	puts("");
119f73a3446SBen Gardon 	printf("usage: %s [-h] [-m mode] [-d delay_usec]\n"
120f73a3446SBen Gardon 	       "          [-b memory] [-v vcpus] [-o] [-i iterations]\n", name);
121f73a3446SBen Gardon 	guest_modes_help();
122f73a3446SBen Gardon 	printf(" -d: add a delay between each iteration of adding and\n"
123f73a3446SBen Gardon 	       "     deleting a memslot in usec.\n");
124f73a3446SBen Gardon 	printf(" -b: specify the size of the memory region which should be\n"
125f73a3446SBen Gardon 	       "     accessed by each vCPU. e.g. 10M or 3G.\n"
126f73a3446SBen Gardon 	       "     Default: 1G\n");
127f73a3446SBen Gardon 	printf(" -v: specify the number of vCPUs to run.\n");
128f73a3446SBen Gardon 	printf(" -o: Overlap guest memory accesses instead of partitioning\n"
129f73a3446SBen Gardon 	       "     them into a separate region of memory for each vCPU.\n");
130f73a3446SBen Gardon 	printf(" -i: specify the number of iterations of adding and removing\n"
131f73a3446SBen Gardon 	       "     a memslot.\n"
132f73a3446SBen Gardon 	       "     Default: %d\n", DEFAULT_MEMSLOT_MODIFICATION_ITERATIONS);
133f73a3446SBen Gardon 	puts("");
134f73a3446SBen Gardon 	exit(0);
135f73a3446SBen Gardon }
136f73a3446SBen Gardon 
main(int argc,char * argv[])137f73a3446SBen Gardon int main(int argc, char *argv[])
138f73a3446SBen Gardon {
139f73a3446SBen Gardon 	int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
140f73a3446SBen Gardon 	int opt;
141f73a3446SBen Gardon 	struct test_params p = {
142c15bdebbSVipin Sharma 		.delay = 0,
143c15bdebbSVipin Sharma 		.nr_iterations = DEFAULT_MEMSLOT_MODIFICATION_ITERATIONS,
144f73a3446SBen Gardon 		.partition_vcpu_memory_access = true
145f73a3446SBen Gardon 	};
146f73a3446SBen Gardon 
147f73a3446SBen Gardon 	guest_modes_append_default();
148f73a3446SBen Gardon 
149f73a3446SBen Gardon 	while ((opt = getopt(argc, argv, "hm:d:b:v:oi:")) != -1) {
150f73a3446SBen Gardon 		switch (opt) {
151f73a3446SBen Gardon 		case 'm':
152f73a3446SBen Gardon 			guest_modes_cmdline(optarg);
153f73a3446SBen Gardon 			break;
154f73a3446SBen Gardon 		case 'd':
1550001725dSVipin Sharma 			p.delay = atoi_non_negative("Delay", optarg);
156f73a3446SBen Gardon 			break;
157f73a3446SBen Gardon 		case 'b':
158f73a3446SBen Gardon 			guest_percpu_mem_size = parse_size(optarg);
159f73a3446SBen Gardon 			break;
160f73a3446SBen Gardon 		case 'v':
1610001725dSVipin Sharma 			nr_vcpus = atoi_positive("Number of vCPUs", optarg);
1620001725dSVipin Sharma 			TEST_ASSERT(nr_vcpus <= max_vcpus,
163f73a3446SBen Gardon 				    "Invalid number of vcpus, must be between 1 and %d",
164f73a3446SBen Gardon 				    max_vcpus);
165f73a3446SBen Gardon 			break;
166f73a3446SBen Gardon 		case 'o':
167f73a3446SBen Gardon 			p.partition_vcpu_memory_access = false;
168f73a3446SBen Gardon 			break;
169f73a3446SBen Gardon 		case 'i':
1700001725dSVipin Sharma 			p.nr_iterations = atoi_positive("Number of iterations", optarg);
171f73a3446SBen Gardon 			break;
172f73a3446SBen Gardon 		case 'h':
173f73a3446SBen Gardon 		default:
174f73a3446SBen Gardon 			help(argv[0]);
175f73a3446SBen Gardon 			break;
176f73a3446SBen Gardon 		}
177f73a3446SBen Gardon 	}
178f73a3446SBen Gardon 
179f73a3446SBen Gardon 	for_each_guest_mode(run_test, &p);
180f73a3446SBen Gardon 
181f73a3446SBen Gardon 	return 0;
182f73a3446SBen Gardon }
183