Files
linux/tools/testing/selftests/kvm/hardware_disable_test.c
David Matlack 0c3a877469 KVM: selftests: Use u32 instead of uint32_t
Use u32 instead of uint32_t to make the KVM selftests code more concise
and more similar to the kernel (since selftests are primarily developed
by kernel developers).

This commit was generated with the following command:

  git ls-files tools/testing/selftests/kvm | xargs sed -i 's/uint32_t/u32/g'

Then by manually adjusting whitespace to make checkpatch.pl happy.

No functional change intended.

Signed-off-by: David Matlack <dmatlack@google.com>
Link: https://patch.msgid.link/20260420212004.3938325-7-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
2026-04-20 14:54:16 -07:00

181 lines
4.0 KiB
C

// 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.
*/
#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
sem_t *sem;
static void guest_code(void)
{
for (;;)
; /* Some busy work */
printf("Should not be reached.\n");
}
static void *run_vcpu(void *arg)
{
struct kvm_vcpu *vcpu = arg;
struct kvm_run *run = vcpu->run;
vcpu_run(vcpu);
TEST_ASSERT(false, "%s: exited with reason %d: %s",
__func__, run->exit_reason,
exit_reason_str(run->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", __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(u32 run)
{
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
cpu_set_t cpu_set;
pthread_t threads[VCPU_NUM];
pthread_t throw_away;
void *b;
u32 i, j;
CPU_ZERO(&cpu_set);
for (i = 0; i < VCPU_NUM; i++)
CPU_SET(i, &cpu_set);
vm = vm_create(VCPU_NUM);
pr_debug("%s: [%d] start vcpus\n", __func__, run);
for (i = 0; i < VCPU_NUM; ++i) {
vcpu = vm_vcpu_add(vm, i, guest_code);
check_create_thread(&threads[i], NULL, run_vcpu, vcpu);
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", __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)
{
u32 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);
}