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