projects/linux·blob master · refs

165 lines · 3.7 KiB

1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * This test is intended to reproduce a crash that happens when
4 * kvm_arch_hardware_disable is called and it attempts to unregister the user
5 * return notifiers.
6 */
7#include <fcntl.h>
8#include <semaphore.h>
9#include <stdint.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <sys/wait.h>
13
14#include <test_util.h>
15
16#include "kvm_syscalls.h"
17#include "kvm_util.h"
18#include "ucall_common.h"
19
20#define NR_VCPUS 4
21#define NR_SLEEPERS_PER_VCPU 16
22#define NR_ITERATIONS 512
23#define DELAY_US_MAX 2000
24
25static cpu_set_t threads_cpu_set;
26static sem_t *sem;
27
28static void guest_code(void)
29{
30 for (;;)
31 ; /* Some busy work */
32 GUEST_ASSERT(0);
33}
34
35static void *run_vcpu(void *arg)
36{
37 struct kvm_vcpu *vcpu = arg;
38 struct kvm_run *run = vcpu->run;
39
40#ifndef __GLIBC__
41 kvm_sched_setaffinity(0, sizeof(cpu_set_t), &threads_cpu_set);
42#endif
43
44 vcpu_run(vcpu);
45
46 TEST_FAIL("vCPU%d exited with reason %d: %s",
47 vcpu->id, run->exit_reason, exit_reason_str(run->exit_reason));
48}
49
50static void *sleeping_thread(void *arg)
51{
52 int fd;
53
54#ifndef __GLIBC__
55 kvm_sched_setaffinity(0, sizeof(cpu_set_t), &threads_cpu_set);
56#endif
57
58 while (1) {
59 fd = open("/dev/null", O_RDWR);
60 close(fd);
61 }
62 TEST_FAIL("%s: exited", __func__);
63}
64
65static void run_test(u32 run)
66{
67 struct kvm_vcpu *vcpu;
68 pthread_attr_t attr;
69 struct kvm_vm *vm;
70 pthread_t thread;
71 u32 i, j;
72
73 TEST_ASSERT_EQ(pthread_attr_init(&attr), 0);
74#ifdef __GLIBC__
75 TEST_ASSERT_EQ(pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &threads_cpu_set), 0);
76#endif
77
78 vm = vm_create(NR_VCPUS);
79
80 pr_debug("%s: [%d] start vcpus\n", __func__, run);
81 for (i = 0; i < NR_VCPUS; ++i) {
82 vcpu = vm_vcpu_add(vm, i, guest_code);
83
84 kvm_pthread_create(&thread, &attr, run_vcpu, vcpu);
85
86 for (j = 0; j < NR_SLEEPERS_PER_VCPU; ++j)
87 kvm_pthread_create(&thread, &attr, sleeping_thread, (void *)NULL);
88 }
89 pr_debug("%s: [%d] all threads launched\n", __func__, run);
90 sem_post(sem);
91
92 /* Wait for the parent to SIGKILL this child. */
93 while (1)
94 pause();
95}
96
97void wait_for_child_setup(pid_t pid)
98{
99 /*
100 * Wait for the child to post to the semaphore, but wake up periodically
101 * to check if the child exited prematurely.
102 */
103 for (;;) {
104 const struct timespec wait_period = { .tv_sec = 1 };
105 int status;
106
107 if (!sem_timedwait(sem, &wait_period))
108 return;
109
110 /* Child is still running, keep waiting. */
111 if (pid != waitpid(pid, &status, WNOHANG))
112 continue;
113
114 /*
115 * Child is no longer running, which is not expected.
116 *
117 * If it exited with a non-zero status, we explicitly forward
118 * the child's status in case it exited with KSFT_SKIP.
119 */
120 if (WIFEXITED(status))
121 exit(WEXITSTATUS(status));
122 else
123 TEST_ASSERT(false, "Child exited unexpectedly");
124 }
125}
126
127int main(int argc, char **argv)
128{
129 cpu_set_t allowed_cpu_set;
130 int s, r, cpu, i;
131 pid_t pid;
132
133 kvm_sched_getaffinity(0, sizeof(cpu_set_t), &allowed_cpu_set);
134
135 for (i = 0; i < NR_VCPUS && CPU_COUNT(&allowed_cpu_set); i++) {
136 cpu = kvm_pick_random_cpu(&allowed_cpu_set);
137 CPU_CLR(cpu, &allowed_cpu_set);
138 CPU_SET(cpu, &threads_cpu_set);
139 }
140
141 sem = sem_open("vm_sem", O_CREAT | O_EXCL, 0644, 0);
142 sem_unlink("vm_sem");
143
144 for (i = 0; i < NR_ITERATIONS; ++i) {
145 pid = fork();
146 TEST_ASSERT(pid >= 0, "%s: unable to fork", __func__);
147 if (pid == 0)
148 run_test(i); /* This function always exits */
149
150 pr_debug("%s: [%d] waiting semaphore\n", __func__, i);
151 wait_for_child_setup(pid);
152 r = (rand() % DELAY_US_MAX) + 1;
153 pr_debug("%s: [%d] waiting %dus\n", __func__, i, r);
154 usleep(r);
155 r = waitpid(pid, &s, WNOHANG);
156 TEST_ASSERT(r != pid,
157 "%s: [%d] child exited unexpectedly status: [%d]",
158 __func__, i, s);
159 pr_debug("%s: [%d] killing child\n", __func__, i);
160 kill(pid, SIGKILL);
161 }
162
163 sem_destroy(sem);
164 exit(0);
165}