KVM selftests changes for 7.2

- Randomize the dirty log test's delay when reaping the bitmap on the first
    pass, as always waiting only 1ms hid a KVM RISC-V bug as the test reaped the
    bitmap before KVM could build up enough state to hit the bug.
 
  - A pile of one-off fixes and cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEKTobbabEP7vbhhN9OlYIJqCjN/0FAmorQNUACgkQOlYIJqCj
 N/3E2RAAqSMg2+iYYbfkjFw5kFafSDb7IWFxK4SWNWeh5C8jkfCrrviWTMOewTR8
 C7YtKCMGK8iCZQfmB3jhenTEoQJEcICJn4JOUNK2RkNcC/BuNlKlM/C8dX0x29xk
 m1XWZdLgwjmZr7LydzVGMgdhJdcdxK/WV/71vvFYze4Jxim4lnnTM3VoMbjj8FuS
 6FdMvKbclD6Mbfx1/wvYNndl6J9Y0fhKZbsj6tpAxDmXH/Pw9zx8b49znioV4HsD
 k3GE76w4Xe/cIgHwbXWTUmpf1s2Ou8ZO8ju+02u3gUz6UpIj2gbcV0mhu327EhyT
 IPlepblusG6hzJAXwfmb6D8u/aXg2VfZdSsiLNNhgisNtLnakFjxdKg0ViSnKS+3
 UZ49TWmPwyZ92JEC7paluB1PKv7n+GiJBfyLAU9lV7x4rXHn+nseW/ZPJ2Rvdq5n
 HsLlG9smz5Q7ea8AI8yHaGYQpTbw48t52hnNqLdt0mU5Tj027nNNWVP72s+Jhc/j
 N+1juth6qszxh7gLMD00AgxRtTKRpGMMV9zbieuQTg+mhmUrk5lJsCuk+su9TCzT
 /UjGrmRmz2TKtA9MQ0xckA3ysR519WYdBn9EHhiQLLfLd5V01LTo0FERMovjfjyH
 JUYKcyFhiZqbVqCNsloIGTv3/N3jOwxKzMhuFrmuGT9+NCyC5Fk=
 =nMTy
 -----END PGP SIGNATURE-----

Merge tag 'kvm-x86-selftests-7.2' of https://github.com/kvm-x86/linux into HEAD

KVM selftests changes for 7.2

 - Randomize the dirty log test's delay when reaping the bitmap on the first
   pass, as always waiting only 1ms hid a KVM RISC-V bug as the test reaped the
   bitmap before KVM could build up enough state to hit the bug.

 - A pile of one-off fixes and cleanups.
This commit is contained in:
Paolo Bonzini 2026-06-12 10:12:22 +02:00
commit b02a4f8c42
8 changed files with 63 additions and 45 deletions

View File

@ -694,7 +694,17 @@ static void run_test(enum vm_guest_mode mode, void *arg)
pthread_create(&vcpu_thread, NULL, vcpu_worker, vcpu);
for (iteration = 1; iteration <= p->iterations; iteration++) {
unsigned long i;
unsigned long i, reap_i;
/*
* Select a random point in the time interval to reap the dirty
* bitmap/ring while the guest is running, i.e. randomize how
* long the guest gets to initially run and thus how many pages
* it can dirty, before collecting the dirty bitmap/ring. See
* the loop below for details.
*/
reap_i = random() % p->interval;
printf("Reaping after a %lu ms delay\n", reap_i);
sync_global_to_guest(vm, iteration);
@ -729,13 +739,17 @@ static void run_test(enum vm_guest_mode mode, void *arg)
* that's effectively blocked. Collecting while the
* guest is running also verifies KVM doesn't lose any
* state.
*
*/
if (i < reap_i)
continue;
/*
* For bitmap modes, KVM overwrites the entire bitmap,
* i.e. collecting the bitmaps is destructive. Collect
* the bitmap only on the first pass, otherwise this
* test would lose track of dirty pages.
* the bitmap while the guest is running only once,
* otherwise this test would lose track of dirty pages.
*/
if (i && host_log_mode != LOG_MODE_DIRTY_RING)
if (i > reap_i && host_log_mode != LOG_MODE_DIRTY_RING)
continue;
/*
@ -745,7 +759,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
* the ring on every pass would make it unlikely the
* vCPU would ever fill the fing).
*/
if (i && !READ_ONCE(dirty_ring_vcpu_ring_full))
if (i > reap_i && !READ_ONCE(dirty_ring_vcpu_ring_full))
continue;
log_mode_collect_dirty_pages(vcpu, TEST_MEM_SLOT_INDEX,

View File

@ -89,4 +89,10 @@ __KVM_SYSCALL_DEFINE(fallocate, 4, int, fd, int, mode, loff_t, offset, loff_t, l
__KVM_SYSCALL_DEFINE(ftruncate, 2, unsigned int, fd, off_t, length);
__KVM_SYSCALL_DEFINE(madvise, 3, void *, addr, size_t, length, int, advice);
#define kvm_free_fd(fd) \
do { \
kvm_close(fd); \
(fd) = -1; \
} while (0)
#endif /* SELFTEST_KVM_SYSCALLS_H */

View File

@ -876,7 +876,7 @@ static inline int vcpu_get_stats_fd(struct kvm_vcpu *vcpu)
{
int fd = __vcpu_ioctl(vcpu, KVM_GET_STATS_FD, NULL);
TEST_ASSERT_VM_VCPU_IOCTL(fd >= 0, KVM_CHECK_EXTENSION, fd, vcpu->vm);
TEST_ASSERT_VM_VCPU_IOCTL(fd >= 0, KVM_GET_STATS_FD, fd, vcpu->vm);
return fd;
}

View File

@ -77,7 +77,8 @@ static ssize_t get_module_param(const char *module_name, const char *param,
int fd, r;
/* Verify KVM is loaded, to provide a more helpful SKIP message. */
close(open_kvm_dev_path_or_exit());
fd = open_kvm_dev_path_or_exit();
kvm_free_fd(fd);
r = snprintf(path, path_size, "/sys/module/%s/parameters/%s",
module_name, param);
@ -90,8 +91,7 @@ static ssize_t get_module_param(const char *module_name, const char *param,
TEST_ASSERT(bytes_read > 0, "read(%s) returned %ld, wanted %ld bytes",
path, bytes_read, buffer_size);
r = close(fd);
TEST_ASSERT(!r, "close(%s) failed", path);
kvm_free_fd(fd);
return bytes_read;
}
@ -160,7 +160,7 @@ unsigned int kvm_check_cap(long cap)
ret = __kvm_ioctl(kvm_fd, KVM_CHECK_EXTENSION, (void *)cap);
TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_CHECK_EXTENSION, ret));
close(kvm_fd);
kvm_free_fd(kvm_fd);
return (unsigned int)ret;
}
@ -747,8 +747,7 @@ static void kvm_stats_release(struct kvm_binary_stats *stats)
stats->desc = NULL;
}
kvm_close(stats->fd);
stats->fd = -1;
kvm_free_fd(stats->fd);
}
__weak void vcpu_arch_free(struct kvm_vcpu *vcpu)
@ -777,7 +776,7 @@ static void vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu)
kvm_munmap(vcpu->run, vcpu_mmap_sz());
kvm_close(vcpu->fd);
kvm_free_fd(vcpu->fd);
kvm_stats_release(&vcpu->stats);
list_del(&vcpu->list);
@ -793,8 +792,8 @@ void kvm_vm_release(struct kvm_vm *vmp)
list_for_each_entry_safe(vcpu, tmp, &vmp->vcpus, list)
vm_vcpu_rm(vmp, vcpu);
kvm_close(vmp->fd);
kvm_close(vmp->kvm_fd);
kvm_free_fd(vmp->fd);
kvm_free_fd(vmp->kvm_fd);
/* Free cached stats metadata and close FD */
kvm_stats_release(&vmp->stats);
@ -815,10 +814,10 @@ static void __vm_mem_region_delete(struct kvm_vm *vm,
if (region->fd >= 0) {
/* There's an extra map when using shared memory. */
kvm_munmap(region->mmap_alias, region->mmap_size);
close(region->fd);
kvm_free_fd(region->fd);
}
if (region->region.guest_memfd >= 0)
close(region->region.guest_memfd);
if ((int)region->region.guest_memfd >= 0)
kvm_free_fd(region->region.guest_memfd);
free(region);
}
@ -1311,7 +1310,7 @@ static size_t vcpu_mmap_sz(void)
TEST_ASSERT(ret >= 0 && ret >= sizeof(struct kvm_run),
KVM_IOCTL_ERROR(KVM_GET_VCPU_MMAP_SIZE, ret));
close(dev_fd);
kvm_free_fd(dev_fd);
return ret;
}

View File

@ -111,6 +111,7 @@ struct sync_area {
*/
static_assert(ATOMIC_BOOL_LOCK_FREE == 2, "atomic bool is not lockless");
static int wait_timeout = 10;
static sem_t vcpu_ready;
static bool map_unmap_verify;
@ -418,7 +419,7 @@ static bool _guest_should_exit(void)
*/
static noinline void host_perform_sync(struct sync_area *sync)
{
alarm(10);
alarm(wait_timeout);
atomic_store_explicit(&sync->sync_flag, true, memory_order_release);
while (atomic_load_explicit(&sync->sync_flag, memory_order_acquire))
@ -900,7 +901,7 @@ static void help(char *name, struct test_args *targs)
{
int ctr;
pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count]\n",
pr_info("usage: %s [-h] [-v] [-d] [-s slots] [-f first_test] [-e last_test] [-l test_length] [-r run_count] [-t wait_timeout]\n",
name);
pr_info(" -h: print this help screen.\n");
pr_info(" -v: enable verbose mode (not for benchmarking).\n");
@ -916,6 +917,8 @@ static void help(char *name, struct test_args *targs)
targs->seconds);
pr_info(" -r: specify the number of runs per test (currently: %i)\n",
targs->runs);
pr_info(" -t: specify the number of seconds for host wait timeout (currently: %i)\n",
wait_timeout);
pr_info("\nAvailable tests:\n");
for (ctr = 0; ctr < NTESTS; ctr++)
@ -964,7 +967,7 @@ static bool parse_args(int argc, char *argv[],
u32 max_mem_slots;
int opt;
while ((opt = getopt(argc, argv, "hvdqs:f:e:l:r:")) != -1) {
while ((opt = getopt(argc, argv, "hvdqs:f:e:l:r:t:")) != -1) {
switch (opt) {
case 'h':
default:
@ -1007,6 +1010,9 @@ static bool parse_args(int argc, char *argv[],
case 'r':
targs->runs = atoi_positive("Runs per test", optarg);
break;
case 't':
wait_timeout = atoi_positive("Host wait timeout", optarg);
break;
}
}

View File

@ -26,6 +26,7 @@ struct msr_data {
bool fault_expected;
bool write;
u64 write_val;
bool reset_expected;
};
struct hcall_data {
@ -267,14 +268,9 @@ static void guest_test_msrs_access(void)
case 16:
msr->idx = HV_X64_MSR_RESET;
msr->write = true;
/*
* TODO: the test only writes '0' to HV_X64_MSR_RESET
* at the moment, writing some other value there will
* trigger real vCPU reset and the code is not prepared
* to handle it yet.
*/
msr->write_val = 0;
msr->write_val = 1;
msr->fault_expected = false;
msr->reset_expected = true;
break;
case 17:
@ -457,7 +453,7 @@ static void guest_test_msrs_access(void)
msr->fault_expected = true;
break;
case 45:
/* MSR is vailable when CPUID feature bit is set */
/* MSR is available when CPUID feature bit is set */
if (!has_invtsc)
goto next_stage;
vcpu_set_cpuid_feature(vcpu, HV_ACCESS_TSC_INVARIANT);
@ -497,6 +493,15 @@ static void guest_test_msrs_access(void)
msr->idx, msr->write ? "write" : "read");
vcpu_run(vcpu);
if (msr->reset_expected) {
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SYSTEM_EVENT);
TEST_ASSERT(vcpu->run->system_event.type == KVM_SYSTEM_EVENT_RESET,
"Expected reset system event, got type %u",
vcpu->run->system_event.type);
goto next_stage;
}
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {

View File

@ -141,17 +141,6 @@ static void swap_two_test_pages(gpa_t pte_gva1, gpa_t pte_gva2)
*(u64 *)pte_gva2 = tmp;
}
/*
* TODO: replace the silly NOP loop with a proper udelay() implementation.
*/
static inline void do_delay(void)
{
int i;
for (i = 0; i < 1000000; i++)
asm volatile("nop");
}
/*
* Prepare to test: 'disable' workers by setting the expectation to '0',
* clear hypercall input page and then swap two test pages.
@ -169,7 +158,7 @@ static inline void prepare_to_test(struct test_data *data)
wmb();
/* Make sure workers have enough time to notice */
do_delay();
udelay(100);
/* Swap test page mappings */
swap_two_test_pages(data->test_pages_pte[0], data->test_pages_pte[1]);
@ -189,7 +178,7 @@ static inline void post_test(struct test_data *data, u64 exp1, u64 exp2)
set_expected_val((void *)data->test_pages, exp2, WORKER_VCPU_ID_2);
/* Make sure workers have enough time to test */
do_delay();
udelay(100);
}
#define TESTVAL1 0x0101010101010101

View File

@ -255,7 +255,6 @@ KVM_ONE_VCPU_TEST(sync_regs_test, req_and_verify_all_valid, guest_code)
struct kvm_regs regs;
/* Request and verify all valid register sets. */
/* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */
run->kvm_valid_regs = TEST_SYNC_FIELDS;
vcpu_run(vcpu);
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);