14
14
*
15
15
* The test provides command-line options to configure the timer's
16
16
* period (-p), number of vCPUs (-n), and iterations per stage (-i).
17
+ * To stress-test the timer stack even more, an option to migrate the
18
+ * vCPUs across pCPUs (-m), at a particular rate, is also provided.
17
19
*
18
20
* Copyright (c) 2021, Google LLC.
19
21
*/
24
26
#include <pthread.h>
25
27
#include <linux/kvm.h>
26
28
#include <linux/sizes.h>
29
+ #include <linux/bitmap.h>
30
+ #include <sys/sysinfo.h>
27
31
28
32
#include "kvm_util.h"
29
33
#include "processor.h"
36
40
#define NR_TEST_ITERS_DEF 5
37
41
#define TIMER_TEST_PERIOD_MS_DEF 10
38
42
#define TIMER_TEST_ERR_MARGIN_US 100
43
+ #define TIMER_TEST_MIGRATION_FREQ_MS 2
39
44
40
45
struct test_args {
41
46
int nr_vcpus ;
42
47
int nr_iter ;
43
48
int timer_period_ms ;
49
+ int migration_freq_ms ;
44
50
};
45
51
46
52
static struct test_args test_args = {
47
53
.nr_vcpus = NR_VCPUS_DEF ,
48
54
.nr_iter = NR_TEST_ITERS_DEF ,
49
55
.timer_period_ms = TIMER_TEST_PERIOD_MS_DEF ,
56
+ .migration_freq_ms = TIMER_TEST_MIGRATION_FREQ_MS ,
50
57
};
51
58
52
59
#define msecs_to_usecs (msec ) ((msec) * 1000LL)
@@ -80,6 +87,9 @@ static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS];
80
87
81
88
static int vtimer_irq , ptimer_irq ;
82
89
90
+ static unsigned long * vcpu_done_map ;
91
+ static pthread_mutex_t vcpu_done_map_lock ;
92
+
83
93
static void
84
94
guest_configure_timer_action (struct test_vcpu_shared_data * shared_data )
85
95
{
@@ -215,6 +225,11 @@ static void *test_vcpu_run(void *arg)
215
225
216
226
vcpu_run (vm , vcpuid );
217
227
228
+ /* Currently, any exit from guest is an indication of completion */
229
+ pthread_mutex_lock (& vcpu_done_map_lock );
230
+ set_bit (vcpuid , vcpu_done_map );
231
+ pthread_mutex_unlock (& vcpu_done_map_lock );
232
+
218
233
switch (get_ucall (vm , vcpuid , & uc )) {
219
234
case UCALL_SYNC :
220
235
case UCALL_DONE :
@@ -233,18 +248,102 @@ static void *test_vcpu_run(void *arg)
233
248
return NULL ;
234
249
}
235
250
251
+ static uint32_t test_get_pcpu (void )
252
+ {
253
+ uint32_t pcpu ;
254
+ unsigned int nproc_conf ;
255
+ cpu_set_t online_cpuset ;
256
+
257
+ nproc_conf = get_nprocs_conf ();
258
+ sched_getaffinity (0 , sizeof (cpu_set_t ), & online_cpuset );
259
+
260
+ /* Randomly find an available pCPU to place a vCPU on */
261
+ do {
262
+ pcpu = rand () % nproc_conf ;
263
+ } while (!CPU_ISSET (pcpu , & online_cpuset ));
264
+
265
+ return pcpu ;
266
+ }
267
+
268
+ static int test_migrate_vcpu (struct test_vcpu * vcpu )
269
+ {
270
+ int ret ;
271
+ cpu_set_t cpuset ;
272
+ uint32_t new_pcpu = test_get_pcpu ();
273
+
274
+ CPU_ZERO (& cpuset );
275
+ CPU_SET (new_pcpu , & cpuset );
276
+
277
+ pr_debug ("Migrating vCPU: %u to pCPU: %u\n" , vcpu -> vcpuid , new_pcpu );
278
+
279
+ ret = pthread_setaffinity_np (vcpu -> pt_vcpu_run ,
280
+ sizeof (cpuset ), & cpuset );
281
+
282
+ /* Allow the error where the vCPU thread is already finished */
283
+ TEST_ASSERT (ret == 0 || ret == ESRCH ,
284
+ "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d\n" ,
285
+ vcpu -> vcpuid , new_pcpu , ret );
286
+
287
+ return ret ;
288
+ }
289
+
290
+ static void * test_vcpu_migration (void * arg )
291
+ {
292
+ unsigned int i , n_done ;
293
+ bool vcpu_done ;
294
+
295
+ do {
296
+ usleep (msecs_to_usecs (test_args .migration_freq_ms ));
297
+
298
+ for (n_done = 0 , i = 0 ; i < test_args .nr_vcpus ; i ++ ) {
299
+ pthread_mutex_lock (& vcpu_done_map_lock );
300
+ vcpu_done = test_bit (i , vcpu_done_map );
301
+ pthread_mutex_unlock (& vcpu_done_map_lock );
302
+
303
+ if (vcpu_done ) {
304
+ n_done ++ ;
305
+ continue ;
306
+ }
307
+
308
+ test_migrate_vcpu (& test_vcpu [i ]);
309
+ }
310
+ } while (test_args .nr_vcpus != n_done );
311
+
312
+ return NULL ;
313
+ }
314
+
236
315
static void test_run (struct kvm_vm * vm )
237
316
{
238
317
int i , ret ;
318
+ pthread_t pt_vcpu_migration ;
319
+
320
+ pthread_mutex_init (& vcpu_done_map_lock , NULL );
321
+ vcpu_done_map = bitmap_zalloc (test_args .nr_vcpus );
322
+ TEST_ASSERT (vcpu_done_map , "Failed to allocate vcpu done bitmap\n" );
239
323
240
324
for (i = 0 ; i < test_args .nr_vcpus ; i ++ ) {
241
325
ret = pthread_create (& test_vcpu [i ].pt_vcpu_run , NULL ,
242
326
test_vcpu_run , & test_vcpu [i ]);
243
327
TEST_ASSERT (!ret , "Failed to create vCPU-%d pthread\n" , i );
244
328
}
245
329
330
+ /* Spawn a thread to control the vCPU migrations */
331
+ if (test_args .migration_freq_ms ) {
332
+ srand (time (NULL ));
333
+
334
+ ret = pthread_create (& pt_vcpu_migration , NULL ,
335
+ test_vcpu_migration , NULL );
336
+ TEST_ASSERT (!ret , "Failed to create the migration pthread\n" );
337
+ }
338
+
339
+
246
340
for (i = 0 ; i < test_args .nr_vcpus ; i ++ )
247
341
pthread_join (test_vcpu [i ].pt_vcpu_run , NULL );
342
+
343
+ if (test_args .migration_freq_ms )
344
+ pthread_join (pt_vcpu_migration , NULL );
345
+
346
+ bitmap_free (vcpu_done_map );
248
347
}
249
348
250
349
static void test_init_timer_irq (struct kvm_vm * vm )
@@ -301,14 +400,16 @@ static void test_print_help(char *name)
301
400
NR_TEST_ITERS_DEF );
302
401
pr_info ("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n" ,
303
402
TIMER_TEST_PERIOD_MS_DEF );
403
+ pr_info ("\t-m: Frequency (in ms) of vCPUs to migrate to different pCPU. 0 to turn off (default: %u)\n" ,
404
+ TIMER_TEST_MIGRATION_FREQ_MS );
304
405
pr_info ("\t-h: print this help screen\n" );
305
406
}
306
407
307
408
static bool parse_args (int argc , char * argv [])
308
409
{
309
410
int opt ;
310
411
311
- while ((opt = getopt (argc , argv , "hn:i:p:" )) != -1 ) {
412
+ while ((opt = getopt (argc , argv , "hn:i:p:m: " )) != -1 ) {
312
413
switch (opt ) {
313
414
case 'n' :
314
415
test_args .nr_vcpus = atoi (optarg );
@@ -335,6 +436,13 @@ static bool parse_args(int argc, char *argv[])
335
436
goto err ;
336
437
}
337
438
break ;
439
+ case 'm' :
440
+ test_args .migration_freq_ms = atoi (optarg );
441
+ if (test_args .migration_freq_ms < 0 ) {
442
+ pr_info ("0 or positive value needed for -m\n" );
443
+ goto err ;
444
+ }
445
+ break ;
338
446
case 'h' :
339
447
default :
340
448
goto err ;
@@ -358,6 +466,11 @@ int main(int argc, char *argv[])
358
466
if (!parse_args (argc , argv ))
359
467
exit (KSFT_SKIP );
360
468
469
+ if (test_args .migration_freq_ms && get_nprocs () < 2 ) {
470
+ print_skip ("At least two physical CPUs needed for vCPU migration" );
471
+ exit (KSFT_SKIP );
472
+ }
473
+
361
474
vm = test_vm_create ();
362
475
test_run (vm );
363
476
kvm_vm_free (vm );
0 commit comments