Skip to content

Commit 73eaa2a

Browse files
committed
KVM: selftests: Ensure guest writes min number of pages in dirty_log_test
Ensure the vCPU fully completes at least one write in each dirty_log_test iteration, as failure to dirty any pages complicates verification and forces the test to be overly conservative about possible values. E.g. verification needs to allow the last dirty page from a previous iteration to have *any* value, because the vCPU could get stuck for multiple iterations, which is unlikely but can happen in heavily overloaded and/or nested virtualization setups. Somewhat arbitrarily set the minimum to 0x100/256; high enough to be interesting, but not so high as to lead to pointlessly long runtimes. Opportunistically report the number of writes per iteration for debug purposes, and so that a human can sanity check the test. Due to each write targeting a random page, the number of dirty pages will likely be lower than the number of total writes, but it shouldn't be absurdly lower (which would suggest the pRNG is broken) Reported-by: Maxim Levitsky <[email protected]> Reviewed-by: Maxim Levitsky <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent 485e27e commit 73eaa2a

File tree

1 file changed

+36
-4
lines changed

1 file changed

+36
-4
lines changed

tools/testing/selftests/kvm/dirty_log_test.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
/* Interval for each host loop (ms) */
3838
#define TEST_HOST_LOOP_INTERVAL 10UL
3939

40+
/*
41+
* Ensure the vCPU is able to perform a reasonable number of writes in each
42+
* iteration to provide a lower bound on coverage.
43+
*/
44+
#define TEST_MIN_WRITES_PER_ITERATION 0x100
45+
4046
/* Dirty bitmaps are always little endian, so we need to swap on big endian */
4147
#if defined(__s390x__)
4248
# define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7)
@@ -72,6 +78,7 @@ static uint64_t host_page_size;
7278
static uint64_t guest_page_size;
7379
static uint64_t guest_num_pages;
7480
static uint64_t iteration;
81+
static uint64_t nr_writes;
7582
static bool vcpu_stop;
7683

7784
/*
@@ -107,6 +114,7 @@ static void guest_code(void)
107114
for (i = 0; i < guest_num_pages; i++) {
108115
addr = guest_test_virt_mem + i * guest_page_size;
109116
vcpu_arch_put_guest(*(uint64_t *)addr, READ_ONCE(iteration));
117+
nr_writes++;
110118
}
111119
#endif
112120

@@ -118,6 +126,7 @@ static void guest_code(void)
118126
addr = align_down(addr, host_page_size);
119127

120128
vcpu_arch_put_guest(*(uint64_t *)addr, READ_ONCE(iteration));
129+
nr_writes++;
121130
}
122131

123132
GUEST_SYNC(1);
@@ -548,8 +557,8 @@ static void vm_dirty_log_verify(enum vm_guest_mode mode, unsigned long **bmap)
548557
}
549558
}
550559

551-
pr_info("Iteration %2ld: dirty: %-6lu clean: %-6lu\n",
552-
iteration, nr_dirty_pages, nr_clean_pages);
560+
pr_info("Iteration %2ld: dirty: %-6lu clean: %-6lu writes: %-6lu\n",
561+
iteration, nr_dirty_pages, nr_clean_pages, nr_writes);
553562

554563
host_dirty_count += nr_dirty_pages;
555564
host_clear_count += nr_clean_pages;
@@ -665,6 +674,8 @@ static void run_test(enum vm_guest_mode mode, void *arg)
665674
host_dirty_count = 0;
666675
host_clear_count = 0;
667676
WRITE_ONCE(dirty_ring_vcpu_ring_full, false);
677+
WRITE_ONCE(nr_writes, 0);
678+
sync_global_to_guest(vm, nr_writes);
668679

669680
/*
670681
* Ensure the previous iteration didn't leave a dangling semaphore, i.e.
@@ -683,10 +694,22 @@ static void run_test(enum vm_guest_mode mode, void *arg)
683694

684695
dirty_ring_prev_iteration_last_page = dirty_ring_last_page;
685696

686-
/* Give the vcpu thread some time to dirty some pages */
687-
for (i = 0; i < p->interval; i++) {
697+
/*
698+
* Let the vCPU run beyond the configured interval until it has
699+
* performed the minimum number of writes. This verifies the
700+
* guest is making forward progress, e.g. isn't stuck because
701+
* of a KVM bug, and puts a firm floor on test coverage.
702+
*/
703+
for (i = 0; i < p->interval || nr_writes < TEST_MIN_WRITES_PER_ITERATION; i++) {
704+
/*
705+
* Sleep in 1ms chunks to keep the interval math simple
706+
* and so that the test doesn't run too far beyond the
707+
* specified interval.
708+
*/
688709
usleep(1000);
689710

711+
sync_global_from_guest(vm, nr_writes);
712+
690713
/*
691714
* Reap dirty pages while the guest is running so that
692715
* dirty ring full events are resolved, i.e. so that a
@@ -737,6 +760,12 @@ static void run_test(enum vm_guest_mode mode, void *arg)
737760
WRITE_ONCE(vcpu_stop, false);
738761
sync_global_to_guest(vm, vcpu_stop);
739762

763+
/*
764+
* Sync the number of writes performed before verification, the
765+
* info will be printed along with the dirty/clean page counts.
766+
*/
767+
sync_global_from_guest(vm, nr_writes);
768+
740769
/*
741770
* NOTE: for dirty ring, it's possible that we didn't stop at
742771
* GUEST_SYNC but instead we stopped because ring is full;
@@ -760,6 +789,9 @@ static void run_test(enum vm_guest_mode mode, void *arg)
760789
WRITE_ONCE(host_quit, true);
761790
sync_global_to_guest(vm, iteration);
762791

792+
WRITE_ONCE(nr_writes, 0);
793+
sync_global_to_guest(vm, nr_writes);
794+
763795
WRITE_ONCE(dirty_ring_vcpu_ring_full, false);
764796

765797
sem_post(&sem_vcpu_cont);

0 commit comments

Comments
 (0)