@@ -128,6 +128,78 @@ static uint64_t host_dirty_count;
128128static uint64_t host_clear_count ;
129129static 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 */
138210static 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+
140250static 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-
271378static 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