Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | // 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); vm_create_irqchip(vm); pr_debug("%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); } } pr_debug("%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); } void wait_for_child_setup(pid_t pid) { /* * Wait for the child to post to the semaphore, but wake up periodically * to check if the child exited prematurely. */ for (;;) { const struct timespec wait_period = { .tv_sec = 1 }; int status; if (!sem_timedwait(sem, &wait_period)) return; /* Child is still running, keep waiting. */ if (pid != waitpid(pid, &status, WNOHANG)) continue; /* * Child is no longer running, which is not expected. * * If it exited with a non-zero status, we explicitly forward * the child's status in case it exited with KSFT_SKIP. */ if (WIFEXITED(status)) exit(WEXITSTATUS(status)); else TEST_ASSERT(false, "Child exited unexpectedly"); } } 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 */ pr_debug("%s: [%d] waiting semaphore\n", __func__, i); wait_for_child_setup(pid); r = (rand() % DELAY_US_MAX) + 1; pr_debug("%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); pr_debug("%s: [%d] killing child\n", __func__, i); kill(pid, SIGKILL); } sem_destroy(sem); exit(0); } |