KVM: selftests: Trigger save+restore randomly in the #PF stress test

Instead of an explicit GUEST_SYNC() after each access+#PF, run another
thread that keeps sending SIGUSR to the vCPU thread, essentially
triggering exits to userspace and save+restore on random points in guest
execution. This makes the test a lot more meaningful as it opens the
door to exercising race conditions between #PF handling in the guest
and save+restore in the host.

The signals are ignored using SIG_IGN outside of __vcpu_run() to avoid
interrupting other ioctls/sysctls performed by the test.

Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Yosry Ahmed <yosry@kernel.org>
Link: https://patch.msgid.link/20260728174232.2423257-12-yosry@kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
Yosry Ahmed
2026-07-28 17:42:30 +00:00
committed by Sean Christopherson
parent d24dde74f7
commit d99ad3c842

View File

@@ -5,6 +5,8 @@
#include <errno.h>
#include <sys/types.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include "test_util.h"
@@ -76,15 +78,41 @@ static void guest_access_memory(void *arg)
/* Clear the present bit again so it faults next time */
*guest_get_pte(vaddr) &= ~PTE_PRESENT_MASK(&guest_mmu);
invlpg(vaddr);
GUEST_SYNC(guest_faults);
}
}
static void *sigusr_thread_fn(void *arg)
{
pthread_t vcpu_thread = (pthread_t)arg;
for (;;) {
pthread_testcancel();
pthread_kill(vcpu_thread, SIGUSR1);
usleep(msecs_to_usecs(1));
}
return NULL;
}
static void dummy_signal_handler(int signo) {}
static struct sigaction sa;
static void vcpu_sigusr_listen(void)
{
sa.sa_handler = dummy_signal_handler;
sigaction(SIGUSR1, &sa, NULL);
}
static void vcpu_sigusr_ignore(void)
{
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR1, &sa, NULL);
}
int main(int argc, char *argv[])
{
struct kvm_x86_state *state;
int r, i, level;
pthread_t sigusr_thread;
gpa_t gpa, pgtable_gpa;
struct kvm_vcpu *vcpu;
struct kvm_vm *vm;
@@ -128,18 +156,29 @@ int main(int argc, char *argv[])
pgtable_gpa = PTE_GET_PA(pte);
}
for (i = 1; i <= NR_ITERATIONS; i++) {
r = __vcpu_run(vcpu);
TEST_ASSERT(!r, "vcpu_run failed");
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
/* Initialize the thread sending SIGUSR and install the handler */
vcpu_sigusr_ignore();
r = pthread_create(&sigusr_thread, NULL, sigusr_thread_fn,
(void *)pthread_self());
TEST_ASSERT(!r, "pthread_create() failed: %d", r);
get_ucall(vcpu, &uc);
if (uc.cmd == UCALL_ABORT) {
for (i = 1; i <= NR_ITERATIONS; i++) {
/*
* Only handle SIGUSR while the vCPU is running, otherwise
* ignore it to avoid interrupting other ioctls/syscalls.
*/
vcpu_sigusr_listen();
r = __vcpu_run(vcpu);
TEST_ASSERT(!r || errno == EINTR, "Expected success or SIGUSR1");
vcpu_sigusr_ignore();
/* The guest only exits due to a signal or failed assertion */
if (!r) {
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_ABORT);
REPORT_GUEST_ASSERT(uc);
break;
}
TEST_ASSERT_EQ(uc.cmd, UCALL_SYNC);
TEST_ASSERT_EQ(uc.args[1], i);
state = vcpu_save_state(vcpu);
@@ -153,8 +192,11 @@ int main(int argc, char *argv[])
pr_info("\n");
sync_global_from_guest(vm, guest_faults);
TEST_ASSERT(guest_faults, "No guest page faults triggered");
pr_info("Guest page faults: %lu\n", guest_faults);
pthread_cancel(sigusr_thread);
pthread_join(sigusr_thread, NULL);
kvm_vm_free(vm);
return 0;
}