Messages in this thread | | | Subject | Re: [PATCH] selftests: kvm: add hardware_disable test | From | Paolo Bonzini <> | Date | Mon, 15 Feb 2021 17:43:55 +0100 |
| |
On 13/02/21 01:14, Marc Orr wrote: > From: Ignacio Alvarado <ikalvarado@google.com> > > This test launches 512 VMs in serial and kills them after a random > amount of time. > > The test was original written to exercise KVM user notifiers in > the context of1650b4ebc99d: > - KVM: Disable irq while unregistering user notifier > - https://lore.kernel.org/kvm/CACXrx53vkO=HKfwWwk+fVpvxcNjPrYmtDZ10qWxFvVX_PTGp3g@mail.gmail.com/ > > Recently, this test piqued my interest because it proved useful to > for AMD SNP in exercising the "in-use" pages, described in APM section > 15.36.12, "Running SNP-Active Virtual Machines". > > To run the test, first compile: > $ make "CPPFLAGS=-static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive" \ > -C tools/testing/selftests/kvm/ > > Then, copy the test over to a machine with the kernel and run: > $ ./hardware_disable_test
Queued, but these two paragraphs do not belong in an upstream commit message. Thanks!
Paolo
> Signed-off-by: Ignacio Alvarado <ikalvarado@google.com> > Signed-off-by: Marc Orr <marcorr@google.com> > --- > tools/testing/selftests/kvm/.gitignore | 1 + > tools/testing/selftests/kvm/Makefile | 1 + > .../selftests/kvm/hardware_disable_test.c | 165 ++++++++++++++++++ > 3 files changed, 167 insertions(+) > create mode 100644 tools/testing/selftests/kvm/hardware_disable_test.c > > diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore > index ce8f4ad39684..d631e111441a 100644 > --- a/tools/testing/selftests/kvm/.gitignore > +++ b/tools/testing/selftests/kvm/.gitignore > @@ -28,6 +28,7 @@ > /demand_paging_test > /dirty_log_test > /dirty_log_perf_test > +/hardware_disable_test > /kvm_create_max_vcpus > /set_memory_region_test > /steal_time > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile > index fe41c6a0fa67..c1c403d878f6 100644 > --- a/tools/testing/selftests/kvm/Makefile > +++ b/tools/testing/selftests/kvm/Makefile > @@ -62,6 +62,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test > TEST_GEN_PROGS_x86_64 += demand_paging_test > TEST_GEN_PROGS_x86_64 += dirty_log_test > TEST_GEN_PROGS_x86_64 += dirty_log_perf_test > +TEST_GEN_PROGS_x86_64 += hardware_disable_test > TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus > TEST_GEN_PROGS_x86_64 += set_memory_region_test > TEST_GEN_PROGS_x86_64 += steal_time > diff --git a/tools/testing/selftests/kvm/hardware_disable_test.c b/tools/testing/selftests/kvm/hardware_disable_test.c > new file mode 100644 > index 000000000000..2f2eeb8a1d86 > --- /dev/null > +++ b/tools/testing/selftests/kvm/hardware_disable_test.c > @@ -0,0 +1,165 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * This test is intended to reproduce a crash that happens when > + * kvm_arch_hardware_disable is called and it attempts to unregister the user > + * return notifiers. > + */ > + > +#define _GNU_SOURCE > + > +#include <fcntl.h> > +#include <pthread.h> > +#include <semaphore.h> > +#include <stdint.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <sys/wait.h> > + > +#include <test_util.h> > + > +#include "kvm_util.h" > + > +#define VCPU_NUM 4 > +#define SLEEPING_THREAD_NUM (1 << 4) > +#define FORK_NUM (1ULL << 9) > +#define DELAY_US_MAX 2000 > +#define GUEST_CODE_PIO_PORT 4 > + > +sem_t *sem; > + > +/* Arguments for the pthreads */ > +struct payload { > + struct kvm_vm *vm; > + uint32_t index; > +}; > + > +static void guest_code(void) > +{ > + for (;;) > + ; /* Some busy work */ > + printf("Should not be reached.\n"); > +} > + > +static void *run_vcpu(void *arg) > +{ > + struct payload *payload = (struct payload *)arg; > + struct kvm_run *state = vcpu_state(payload->vm, payload->index); > + > + vcpu_run(payload->vm, payload->index); > + > + TEST_ASSERT(false, "%s: exited with reason %d: %s\n", > + __func__, state->exit_reason, > + exit_reason_str(state->exit_reason)); > + pthread_exit(NULL); > +} > + > +static void *sleeping_thread(void *arg) > +{ > + int fd; > + > + while (true) { > + fd = open("/dev/null", O_RDWR); > + close(fd); > + } > + TEST_ASSERT(false, "%s: exited\n", __func__); > + pthread_exit(NULL); > +} > + > +static inline void check_create_thread(pthread_t *thread, pthread_attr_t *attr, > + void *(*f)(void *), void *arg) > +{ > + int r; > + > + r = pthread_create(thread, attr, f, arg); > + TEST_ASSERT(r == 0, "%s: failed to create thread", __func__); > +} > + > +static inline void check_set_affinity(pthread_t thread, cpu_set_t *cpu_set) > +{ > + int r; > + > + r = pthread_setaffinity_np(thread, sizeof(cpu_set_t), cpu_set); > + TEST_ASSERT(r == 0, "%s: failed set affinity", __func__); > +} > + > +static inline void check_join(pthread_t thread, void **retval) > +{ > + int r; > + > + r = pthread_join(thread, retval); > + TEST_ASSERT(r == 0, "%s: failed to join thread", __func__); > +} > + > +static void run_test(uint32_t run) > +{ > + struct kvm_vm *vm; > + cpu_set_t cpu_set; > + pthread_t threads[VCPU_NUM]; > + pthread_t throw_away; > + struct payload payloads[VCPU_NUM]; > + void *b; > + uint32_t i, j; > + > + CPU_ZERO(&cpu_set); > + for (i = 0; i < VCPU_NUM; i++) > + CPU_SET(i, &cpu_set); > + > + vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); > + kvm_vm_elf_load(vm, program_invocation_name, 0, 0); > + vm_create_irqchip(vm); > + > + fprintf(stderr, "%s: [%d] start vcpus\n", __func__, run); > + for (i = 0; i < VCPU_NUM; ++i) { > + vm_vcpu_add_default(vm, i, guest_code); > + payloads[i].vm = vm; > + payloads[i].index = i; > + > + check_create_thread(&threads[i], NULL, run_vcpu, > + (void *)&payloads[i]); > + check_set_affinity(threads[i], &cpu_set); > + > + for (j = 0; j < SLEEPING_THREAD_NUM; ++j) { > + check_create_thread(&throw_away, NULL, sleeping_thread, > + (void *)NULL); > + check_set_affinity(throw_away, &cpu_set); > + } > + } > + fprintf(stderr, "%s: [%d] all threads launched\n", __func__, run); > + sem_post(sem); > + for (i = 0; i < VCPU_NUM; ++i) > + check_join(threads[i], &b); > + /* Should not be reached */ > + TEST_ASSERT(false, "%s: [%d] child escaped the ninja\n", __func__, run); > +} > + > +int main(int argc, char **argv) > +{ > + uint32_t i; > + int s, r; > + pid_t pid; > + > + sem = sem_open("vm_sem", O_CREAT | O_EXCL, 0644, 0); > + sem_unlink("vm_sem"); > + > + for (i = 0; i < FORK_NUM; ++i) { > + pid = fork(); > + TEST_ASSERT(pid >= 0, "%s: unable to fork", __func__); > + if (pid == 0) > + run_test(i); /* This function always exits */ > + > + fprintf(stderr, "%s: [%d] waiting semaphore\n", __func__, i); > + sem_wait(sem); > + r = (rand() % DELAY_US_MAX) + 1; > + fprintf(stderr, "%s: [%d] waiting %dus\n", __func__, i, r); > + usleep(r); > + r = waitpid(pid, &s, WNOHANG); > + TEST_ASSERT(r != pid, > + "%s: [%d] child exited unexpectedly status: [%d]", > + __func__, i, s); > + fprintf(stderr, "%s: [%d] killing child\n", __func__, i); > + kill(pid, SIGKILL); > + } > + > + sem_destroy(sem); > + exit(0); > +} >
| |