Skip to content

Commit afdb196

Browse files
xzpeterbonzini
authored andcommitted
KVM: selftests: Use a single binary for dirty/clear log test
Remove the clear_dirty_log test, instead merge it into the existing dirty_log_test. It should be cleaner to use this single binary to do both tests, also it's a preparation for the upcoming dirty ring test. The default behavior will run all the modes in sequence. Reviewed-by: Andrew Jones <drjones@redhat.com> Signed-off-by: Peter Xu <peterx@redhat.com> Message-Id: <20201001012233.6013-1-peterx@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 3031e02 commit afdb196

3 files changed

Lines changed: 156 additions & 39 deletions

File tree

tools/testing/selftests/kvm/Makefile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
5959
TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
6060
TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
6161
TEST_GEN_PROGS_x86_64 += x86_64/user_msr_test
62-
TEST_GEN_PROGS_x86_64 += clear_dirty_log_test
6362
TEST_GEN_PROGS_x86_64 += demand_paging_test
6463
TEST_GEN_PROGS_x86_64 += dirty_log_test
6564
TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus
@@ -68,7 +67,6 @@ TEST_GEN_PROGS_x86_64 += steal_time
6867

6968
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
7069
TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list-sve
71-
TEST_GEN_PROGS_aarch64 += clear_dirty_log_test
7270
TEST_GEN_PROGS_aarch64 += demand_paging_test
7371
TEST_GEN_PROGS_aarch64 += dirty_log_test
7472
TEST_GEN_PROGS_aarch64 += kvm_create_max_vcpus

tools/testing/selftests/kvm/clear_dirty_log_test.c

Lines changed: 0 additions & 6 deletions
This file was deleted.

tools/testing/selftests/kvm/dirty_log_test.c

Lines changed: 156 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,78 @@ static uint64_t host_dirty_count;
128128
static uint64_t host_clear_count;
129129
static uint64_t host_track_next_count;
130130

131+
enum log_mode_t {
132+
/* Only use KVM_GET_DIRTY_LOG for logging */
133+
LOG_MODE_DIRTY_LOG = 0,
134+
135+
/* Use both KVM_[GET|CLEAR]_DIRTY_LOG for logging */
136+
LOG_MODE_CLEAR_LOG = 1,
137+
138+
LOG_MODE_NUM,
139+
140+
/* Run all supported modes */
141+
LOG_MODE_ALL = LOG_MODE_NUM,
142+
};
143+
144+
/* Mode of logging to test. Default is to run all supported modes */
145+
static enum log_mode_t host_log_mode_option = LOG_MODE_ALL;
146+
/* Logging mode for current run */
147+
static enum log_mode_t host_log_mode;
148+
149+
static bool clear_log_supported(void)
150+
{
151+
return kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
152+
}
153+
154+
static void clear_log_create_vm_done(struct kvm_vm *vm)
155+
{
156+
struct kvm_enable_cap cap = {};
157+
u64 manual_caps;
158+
159+
manual_caps = kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
160+
TEST_ASSERT(manual_caps, "MANUAL_CAPS is zero!");
161+
manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
162+
KVM_DIRTY_LOG_INITIALLY_SET);
163+
cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2;
164+
cap.args[0] = manual_caps;
165+
vm_enable_cap(vm, &cap);
166+
}
167+
168+
static void dirty_log_collect_dirty_pages(struct kvm_vm *vm, int slot,
169+
void *bitmap, uint32_t num_pages)
170+
{
171+
kvm_vm_get_dirty_log(vm, slot, bitmap);
172+
}
173+
174+
static void clear_log_collect_dirty_pages(struct kvm_vm *vm, int slot,
175+
void *bitmap, uint32_t num_pages)
176+
{
177+
kvm_vm_get_dirty_log(vm, slot, bitmap);
178+
kvm_vm_clear_dirty_log(vm, slot, bitmap, 0, num_pages);
179+
}
180+
181+
struct log_mode {
182+
const char *name;
183+
/* Return true if this mode is supported, otherwise false */
184+
bool (*supported)(void);
185+
/* Hook when the vm creation is done (before vcpu creation) */
186+
void (*create_vm_done)(struct kvm_vm *vm);
187+
/* Hook to collect the dirty pages into the bitmap provided */
188+
void (*collect_dirty_pages) (struct kvm_vm *vm, int slot,
189+
void *bitmap, uint32_t num_pages);
190+
} log_modes[LOG_MODE_NUM] = {
191+
{
192+
.name = "dirty-log",
193+
.collect_dirty_pages = dirty_log_collect_dirty_pages,
194+
},
195+
{
196+
.name = "clear-log",
197+
.supported = clear_log_supported,
198+
.create_vm_done = clear_log_create_vm_done,
199+
.collect_dirty_pages = clear_log_collect_dirty_pages,
200+
},
201+
};
202+
131203
/*
132204
* We use this bitmap to track some pages that should have its dirty
133205
* bit set in the _next_ iteration. For example, if we detected the
@@ -137,6 +209,44 @@ static uint64_t host_track_next_count;
137209
*/
138210
static unsigned long *host_bmap_track;
139211

212+
static void log_modes_dump(void)
213+
{
214+
int i;
215+
216+
printf("all");
217+
for (i = 0; i < LOG_MODE_NUM; i++)
218+
printf(", %s", log_modes[i].name);
219+
printf("\n");
220+
}
221+
222+
static bool log_mode_supported(void)
223+
{
224+
struct log_mode *mode = &log_modes[host_log_mode];
225+
226+
if (mode->supported)
227+
return mode->supported();
228+
229+
return true;
230+
}
231+
232+
static void log_mode_create_vm_done(struct kvm_vm *vm)
233+
{
234+
struct log_mode *mode = &log_modes[host_log_mode];
235+
236+
if (mode->create_vm_done)
237+
mode->create_vm_done(vm);
238+
}
239+
240+
static void log_mode_collect_dirty_pages(struct kvm_vm *vm, int slot,
241+
void *bitmap, uint32_t num_pages)
242+
{
243+
struct log_mode *mode = &log_modes[host_log_mode];
244+
245+
TEST_ASSERT(mode->collect_dirty_pages != NULL,
246+
"collect_dirty_pages() is required for any log mode!");
247+
mode->collect_dirty_pages(vm, slot, bitmap, num_pages);
248+
}
249+
140250
static void generate_random_array(uint64_t *guest_array, uint64_t size)
141251
{
142252
uint64_t i;
@@ -257,24 +367,27 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid,
257367
#ifdef __x86_64__
258368
vm_create_irqchip(vm);
259369
#endif
370+
log_mode_create_vm_done(vm);
260371
vm_vcpu_add_default(vm, vcpuid, guest_code);
261372
return vm;
262373
}
263374

264375
#define DIRTY_MEM_BITS 30 /* 1G */
265376
#define PAGE_SHIFT_4K 12
266377

267-
#ifdef USE_CLEAR_DIRTY_LOG
268-
static u64 dirty_log_manual_caps;
269-
#endif
270-
271378
static void run_test(enum vm_guest_mode mode, unsigned long iterations,
272379
unsigned long interval, uint64_t phys_offset)
273380
{
274381
pthread_t vcpu_thread;
275382
struct kvm_vm *vm;
276383
unsigned long *bmap;
277384

385+
if (!log_mode_supported()) {
386+
print_skip("Log mode '%s' not supported",
387+
log_modes[host_log_mode].name);
388+
return;
389+
}
390+
278391
/*
279392
* We reserve page table for 2 times of extra dirty mem which
280393
* will definitely cover the original (1G+) test range. Here
@@ -317,14 +430,6 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
317430
bmap = bitmap_alloc(host_num_pages);
318431
host_bmap_track = bitmap_alloc(host_num_pages);
319432

320-
#ifdef USE_CLEAR_DIRTY_LOG
321-
struct kvm_enable_cap cap = {};
322-
323-
cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2;
324-
cap.args[0] = dirty_log_manual_caps;
325-
vm_enable_cap(vm, &cap);
326-
#endif
327-
328433
/* Add an extra memory slot for testing dirty logging */
329434
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
330435
guest_test_phys_mem,
@@ -362,11 +467,8 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
362467
while (iteration < iterations) {
363468
/* Give the vcpu thread some time to dirty some pages */
364469
usleep(interval * 1000);
365-
kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
366-
#ifdef USE_CLEAR_DIRTY_LOG
367-
kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
368-
host_num_pages);
369-
#endif
470+
log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX,
471+
bmap, host_num_pages);
370472
vm_dirty_log_verify(mode, bmap);
371473
iteration++;
372474
sync_global_to_guest(vm, iteration);
@@ -410,6 +512,9 @@ static void help(char *name)
410512
TEST_HOST_LOOP_INTERVAL);
411513
printf(" -p: specify guest physical test memory offset\n"
412514
" Warning: a low offset can conflict with the loaded test code.\n");
515+
printf(" -M: specify the host logging mode "
516+
"(default: run all log modes). Supported modes: \n\t");
517+
log_modes_dump();
413518
printf(" -m: specify the guest mode ID to test "
414519
"(default: test all supported modes)\n"
415520
" This option may be used multiple times.\n"
@@ -429,18 +534,7 @@ int main(int argc, char *argv[])
429534
bool mode_selected = false;
430535
uint64_t phys_offset = 0;
431536
unsigned int mode;
432-
int opt, i;
433-
434-
#ifdef USE_CLEAR_DIRTY_LOG
435-
dirty_log_manual_caps =
436-
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
437-
if (!dirty_log_manual_caps) {
438-
print_skip("KVM_CLEAR_DIRTY_LOG not available");
439-
exit(KSFT_SKIP);
440-
}
441-
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
442-
KVM_DIRTY_LOG_INITIALLY_SET);
443-
#endif
537+
int opt, i, j;
444538

445539
#ifdef __x86_64__
446540
guest_mode_init(VM_MODE_PXXV48_4K, true, true);
@@ -464,7 +558,7 @@ int main(int argc, char *argv[])
464558
guest_mode_init(VM_MODE_P40V48_4K, true, true);
465559
#endif
466560

467-
while ((opt = getopt(argc, argv, "hi:I:p:m:")) != -1) {
561+
while ((opt = getopt(argc, argv, "hi:I:p:m:M:")) != -1) {
468562
switch (opt) {
469563
case 'i':
470564
iterations = strtol(optarg, NULL, 10);
@@ -486,6 +580,26 @@ int main(int argc, char *argv[])
486580
"Guest mode ID %d too big", mode);
487581
guest_modes[mode].enabled = true;
488582
break;
583+
case 'M':
584+
if (!strcmp(optarg, "all")) {
585+
host_log_mode_option = LOG_MODE_ALL;
586+
break;
587+
}
588+
for (i = 0; i < LOG_MODE_NUM; i++) {
589+
if (!strcmp(optarg, log_modes[i].name)) {
590+
pr_info("Setting log mode to: '%s'\n",
591+
optarg);
592+
host_log_mode_option = i;
593+
break;
594+
}
595+
}
596+
if (i == LOG_MODE_NUM) {
597+
printf("Log mode '%s' invalid. Please choose "
598+
"from: ", optarg);
599+
log_modes_dump();
600+
exit(1);
601+
}
602+
break;
489603
case 'h':
490604
default:
491605
help(argv[0]);
@@ -507,7 +621,18 @@ int main(int argc, char *argv[])
507621
TEST_ASSERT(guest_modes[i].supported,
508622
"Guest mode ID %d (%s) not supported.",
509623
i, vm_guest_mode_string(i));
510-
run_test(i, iterations, interval, phys_offset);
624+
if (host_log_mode_option == LOG_MODE_ALL) {
625+
/* Run each log mode */
626+
for (j = 0; j < LOG_MODE_NUM; j++) {
627+
pr_info("Testing Log Mode '%s'\n",
628+
log_modes[j].name);
629+
host_log_mode = j;
630+
run_test(i, iterations, interval, phys_offset);
631+
}
632+
} else {
633+
host_log_mode = host_log_mode_option;
634+
run_test(i, iterations, interval, phys_offset);
635+
}
511636
}
512637

513638
return 0;

0 commit comments

Comments
 (0)