xref: /linux/tools/testing/selftests/kvm/hardware_disable_test.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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 
25 static cpu_set_t threads_cpu_set;
26 static sem_t *sem;
27 
28 static void guest_code(void)
29 {
30 	for (;;)
31 		;  /* Some busy work */
32 	GUEST_ASSERT(0);
33 }
34 
35 static void *run_vcpu(void *arg)
36 {
37 	struct kvm_vcpu *vcpu = arg;
38 	struct kvm_run *run = vcpu->run;
39 
40 #ifndef _GNU_SOURCE
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 
50 static void *sleeping_thread(void *arg)
51 {
52 	int fd;
53 
54 #ifndef _GNU_SOURCE
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 
65 static 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 _GNU_SOURCE
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 
97 void 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 
127 int 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 }
166