182 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * This test is intended to reproduce a crash that happens when4 * kvm_arch_hardware_disable is called and it attempts to unregister the user5 * return notifiers.6 */7#include <fcntl.h>8#include <pthread.h>9#include <semaphore.h>10#include <stdint.h>11#include <stdlib.h>12#include <unistd.h>13#include <sys/wait.h>14 15#include <test_util.h>16 17#include "kvm_util.h"18 19#define VCPU_NUM 420#define SLEEPING_THREAD_NUM (1 << 4)21#define FORK_NUM (1ULL << 9)22#define DELAY_US_MAX 200023#define GUEST_CODE_PIO_PORT 424 25sem_t *sem;26 27static void guest_code(void)28{29 for (;;)30 ; /* Some busy work */31 printf("Should not be reached.\n");32}33 34static void *run_vcpu(void *arg)35{36 struct kvm_vcpu *vcpu = arg;37 struct kvm_run *run = vcpu->run;38 39 vcpu_run(vcpu);40 41 TEST_ASSERT(false, "%s: exited with reason %d: %s",42 __func__, run->exit_reason,43 exit_reason_str(run->exit_reason));44 pthread_exit(NULL);45}46 47static void *sleeping_thread(void *arg)48{49 int fd;50 51 while (true) {52 fd = open("/dev/null", O_RDWR);53 close(fd);54 }55 TEST_ASSERT(false, "%s: exited", __func__);56 pthread_exit(NULL);57}58 59static inline void check_create_thread(pthread_t *thread, pthread_attr_t *attr,60 void *(*f)(void *), void *arg)61{62 int r;63 64 r = pthread_create(thread, attr, f, arg);65 TEST_ASSERT(r == 0, "%s: failed to create thread", __func__);66}67 68static inline void check_set_affinity(pthread_t thread, cpu_set_t *cpu_set)69{70 int r;71 72 r = pthread_setaffinity_np(thread, sizeof(cpu_set_t), cpu_set);73 TEST_ASSERT(r == 0, "%s: failed set affinity", __func__);74}75 76static inline void check_join(pthread_t thread, void **retval)77{78 int r;79 80 r = pthread_join(thread, retval);81 TEST_ASSERT(r == 0, "%s: failed to join thread", __func__);82}83 84static void run_test(uint32_t run)85{86 struct kvm_vcpu *vcpu;87 struct kvm_vm *vm;88 cpu_set_t cpu_set;89 pthread_t threads[VCPU_NUM];90 pthread_t throw_away;91 void *b;92 uint32_t i, j;93 94 CPU_ZERO(&cpu_set);95 for (i = 0; i < VCPU_NUM; i++)96 CPU_SET(i, &cpu_set);97 98 vm = vm_create(VCPU_NUM);99 100 pr_debug("%s: [%d] start vcpus\n", __func__, run);101 for (i = 0; i < VCPU_NUM; ++i) {102 vcpu = vm_vcpu_add(vm, i, guest_code);103 104 check_create_thread(&threads[i], NULL, run_vcpu, vcpu);105 check_set_affinity(threads[i], &cpu_set);106 107 for (j = 0; j < SLEEPING_THREAD_NUM; ++j) {108 check_create_thread(&throw_away, NULL, sleeping_thread,109 (void *)NULL);110 check_set_affinity(throw_away, &cpu_set);111 }112 }113 pr_debug("%s: [%d] all threads launched\n", __func__, run);114 sem_post(sem);115 for (i = 0; i < VCPU_NUM; ++i)116 check_join(threads[i], &b);117 /* Should not be reached */118 TEST_ASSERT(false, "%s: [%d] child escaped the ninja", __func__, run);119}120 121void wait_for_child_setup(pid_t pid)122{123 /*124 * Wait for the child to post to the semaphore, but wake up periodically125 * to check if the child exited prematurely.126 */127 for (;;) {128 const struct timespec wait_period = { .tv_sec = 1 };129 int status;130 131 if (!sem_timedwait(sem, &wait_period))132 return;133 134 /* Child is still running, keep waiting. */135 if (pid != waitpid(pid, &status, WNOHANG))136 continue;137 138 /*139 * Child is no longer running, which is not expected.140 *141 * If it exited with a non-zero status, we explicitly forward142 * the child's status in case it exited with KSFT_SKIP.143 */144 if (WIFEXITED(status))145 exit(WEXITSTATUS(status));146 else147 TEST_ASSERT(false, "Child exited unexpectedly");148 }149}150 151int main(int argc, char **argv)152{153 uint32_t i;154 int s, r;155 pid_t pid;156 157 sem = sem_open("vm_sem", O_CREAT | O_EXCL, 0644, 0);158 sem_unlink("vm_sem");159 160 for (i = 0; i < FORK_NUM; ++i) {161 pid = fork();162 TEST_ASSERT(pid >= 0, "%s: unable to fork", __func__);163 if (pid == 0)164 run_test(i); /* This function always exits */165 166 pr_debug("%s: [%d] waiting semaphore\n", __func__, i);167 wait_for_child_setup(pid);168 r = (rand() % DELAY_US_MAX) + 1;169 pr_debug("%s: [%d] waiting %dus\n", __func__, i, r);170 usleep(r);171 r = waitpid(pid, &s, WNOHANG);172 TEST_ASSERT(r != pid,173 "%s: [%d] child exited unexpectedly status: [%d]",174 __func__, i, s);175 pr_debug("%s: [%d] killing child\n", __func__, i);176 kill(pid, SIGKILL);177 }178 179 sem_destroy(sem);180 exit(0);181}182