Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * KVM memslot modification stress test
0004  * Adapted from demand_paging_test.c
0005  *
0006  * Copyright (C) 2018, Red Hat, Inc.
0007  * Copyright (C) 2020, Google, Inc.
0008  */
0009 
0010 #define _GNU_SOURCE /* for program_invocation_name */
0011 
0012 #include <stdio.h>
0013 #include <stdlib.h>
0014 #include <sys/syscall.h>
0015 #include <unistd.h>
0016 #include <asm/unistd.h>
0017 #include <time.h>
0018 #include <poll.h>
0019 #include <pthread.h>
0020 #include <linux/bitmap.h>
0021 #include <linux/bitops.h>
0022 #include <linux/userfaultfd.h>
0023 
0024 #include "perf_test_util.h"
0025 #include "processor.h"
0026 #include "test_util.h"
0027 #include "guest_modes.h"
0028 
0029 #define DUMMY_MEMSLOT_INDEX 7
0030 
0031 #define DEFAULT_MEMSLOT_MODIFICATION_ITERATIONS 10
0032 
0033 
0034 static int nr_vcpus = 1;
0035 static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
0036 
0037 static bool run_vcpus = true;
0038 
0039 static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args)
0040 {
0041     struct kvm_vcpu *vcpu = vcpu_args->vcpu;
0042     struct kvm_run *run;
0043     int ret;
0044 
0045     run = vcpu->run;
0046 
0047     /* Let the guest access its memory until a stop signal is received */
0048     while (READ_ONCE(run_vcpus)) {
0049         ret = _vcpu_run(vcpu);
0050         TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
0051 
0052         if (get_ucall(vcpu, NULL) == UCALL_SYNC)
0053             continue;
0054 
0055         TEST_ASSERT(false,
0056                 "Invalid guest sync status: exit_reason=%s\n",
0057                 exit_reason_str(run->exit_reason));
0058     }
0059 }
0060 
0061 struct memslot_antagonist_args {
0062     struct kvm_vm *vm;
0063     useconds_t delay;
0064     uint64_t nr_modifications;
0065 };
0066 
0067 static void add_remove_memslot(struct kvm_vm *vm, useconds_t delay,
0068                    uint64_t nr_modifications)
0069 {
0070     const uint64_t pages = 1;
0071     uint64_t gpa;
0072     int i;
0073 
0074     /*
0075      * Add the dummy memslot just below the perf_test_util memslot, which is
0076      * at the top of the guest physical address space.
0077      */
0078     gpa = perf_test_args.gpa - pages * vm->page_size;
0079 
0080     for (i = 0; i < nr_modifications; i++) {
0081         usleep(delay);
0082         vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, gpa,
0083                         DUMMY_MEMSLOT_INDEX, pages, 0);
0084 
0085         vm_mem_region_delete(vm, DUMMY_MEMSLOT_INDEX);
0086     }
0087 }
0088 
0089 struct test_params {
0090     useconds_t memslot_modification_delay;
0091     uint64_t nr_memslot_modifications;
0092     bool partition_vcpu_memory_access;
0093 };
0094 
0095 static void run_test(enum vm_guest_mode mode, void *arg)
0096 {
0097     struct test_params *p = arg;
0098     struct kvm_vm *vm;
0099 
0100     vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 1,
0101                  VM_MEM_SRC_ANONYMOUS,
0102                  p->partition_vcpu_memory_access);
0103 
0104     pr_info("Finished creating vCPUs\n");
0105 
0106     perf_test_start_vcpu_threads(nr_vcpus, vcpu_worker);
0107 
0108     pr_info("Started all vCPUs\n");
0109 
0110     add_remove_memslot(vm, p->memslot_modification_delay,
0111                p->nr_memslot_modifications);
0112 
0113     run_vcpus = false;
0114 
0115     perf_test_join_vcpu_threads(nr_vcpus);
0116     pr_info("All vCPU threads joined\n");
0117 
0118     perf_test_destroy_vm(vm);
0119 }
0120 
0121 static void help(char *name)
0122 {
0123     puts("");
0124     printf("usage: %s [-h] [-m mode] [-d delay_usec]\n"
0125            "          [-b memory] [-v vcpus] [-o] [-i iterations]\n", name);
0126     guest_modes_help();
0127     printf(" -d: add a delay between each iteration of adding and\n"
0128            "     deleting a memslot in usec.\n");
0129     printf(" -b: specify the size of the memory region which should be\n"
0130            "     accessed by each vCPU. e.g. 10M or 3G.\n"
0131            "     Default: 1G\n");
0132     printf(" -v: specify the number of vCPUs to run.\n");
0133     printf(" -o: Overlap guest memory accesses instead of partitioning\n"
0134            "     them into a separate region of memory for each vCPU.\n");
0135     printf(" -i: specify the number of iterations of adding and removing\n"
0136            "     a memslot.\n"
0137            "     Default: %d\n", DEFAULT_MEMSLOT_MODIFICATION_ITERATIONS);
0138     puts("");
0139     exit(0);
0140 }
0141 
0142 int main(int argc, char *argv[])
0143 {
0144     int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
0145     int opt;
0146     struct test_params p = {
0147         .memslot_modification_delay = 0,
0148         .nr_memslot_modifications =
0149             DEFAULT_MEMSLOT_MODIFICATION_ITERATIONS,
0150         .partition_vcpu_memory_access = true
0151     };
0152 
0153     guest_modes_append_default();
0154 
0155     while ((opt = getopt(argc, argv, "hm:d:b:v:oi:")) != -1) {
0156         switch (opt) {
0157         case 'm':
0158             guest_modes_cmdline(optarg);
0159             break;
0160         case 'd':
0161             p.memslot_modification_delay = strtoul(optarg, NULL, 0);
0162             TEST_ASSERT(p.memslot_modification_delay >= 0,
0163                     "A negative delay is not supported.");
0164             break;
0165         case 'b':
0166             guest_percpu_mem_size = parse_size(optarg);
0167             break;
0168         case 'v':
0169             nr_vcpus = atoi(optarg);
0170             TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
0171                     "Invalid number of vcpus, must be between 1 and %d",
0172                     max_vcpus);
0173             break;
0174         case 'o':
0175             p.partition_vcpu_memory_access = false;
0176             break;
0177         case 'i':
0178             p.nr_memslot_modifications = atoi(optarg);
0179             break;
0180         case 'h':
0181         default:
0182             help(argv[0]);
0183             break;
0184         }
0185     }
0186 
0187     for_each_guest_mode(run_test, &p);
0188 
0189     return 0;
0190 }