1
1
// SPDX-License-Identifier: GPL-2.0-only
2
2
/*
3
- * arch_timer.c - Tests the aarch64 timer IRQ functionality
4
- *
5
3
* The test validates both the virtual and physical timer IRQs using
6
- * CVAL and TVAL registers. This consitutes the four stages in the test.
7
- * The guest's main thread configures the timer interrupt for a stage
8
- * and waits for it to fire, with a timeout equal to the timer period.
9
- * It asserts that the timeout doesn't exceed the timer period plus
10
- * a user configurable error margin(default to 100us).
11
- *
12
- * On the other hand, upon receipt of an interrupt, the guest's interrupt
13
- * handler validates the interrupt by checking if the architectural state
14
- * is in compliance with the specifications.
15
- *
16
- * The test provides command-line options to configure the timer's
17
- * period (-p), number of vCPUs (-n), iterations per stage (-i) and timer
18
- * interrupt arrival error margin (-e). To stress-test the timer stack
19
- * even more, an option to migrate the vCPUs across pCPUs (-m), at a
20
- * particular rate, is also provided.
4
+ * CVAL and TVAL registers.
21
5
*
22
6
* Copyright (c) 2021, Google LLC.
23
7
*/
24
8
#define _GNU_SOURCE
25
9
26
- #include <stdlib.h>
27
- #include <pthread.h>
28
- #include <linux/kvm.h>
29
- #include <linux/sizes.h>
30
- #include <linux/bitmap.h>
31
- #include <sys/sysinfo.h>
32
-
33
- #include "kvm_util.h"
34
- #include "processor.h"
35
- #include "delay.h"
36
10
#include "arch_timer.h"
11
+ #include "delay.h"
37
12
#include "gic.h"
13
+ #include "processor.h"
14
+ #include "timer_test.h"
38
15
#include "vgic.h"
39
16
40
- #define NR_VCPUS_DEF 4
41
- #define NR_TEST_ITERS_DEF 5
42
- #define TIMER_TEST_PERIOD_MS_DEF 10
43
- #define TIMER_TEST_ERR_MARGIN_US 100
44
- #define TIMER_TEST_MIGRATION_FREQ_MS 2
45
-
46
- struct test_args {
47
- uint32_t nr_vcpus ;
48
- uint32_t nr_iter ;
49
- uint32_t timer_period_ms ;
50
- uint32_t migration_freq_ms ;
51
- uint32_t timer_err_margin_us ;
52
- struct kvm_arm_counter_offset offset ;
53
- };
54
-
55
- static struct test_args test_args = {
56
- .nr_vcpus = NR_VCPUS_DEF ,
57
- .nr_iter = NR_TEST_ITERS_DEF ,
58
- .timer_period_ms = TIMER_TEST_PERIOD_MS_DEF ,
59
- .migration_freq_ms = TIMER_TEST_MIGRATION_FREQ_MS ,
60
- .timer_err_margin_us = TIMER_TEST_ERR_MARGIN_US ,
61
- .offset = { .reserved = 1 },
62
- };
63
-
64
- #define msecs_to_usecs (msec ) ((msec) * 1000ULL)
65
-
66
17
#define GICD_BASE_GPA 0x8000000ULL
67
18
#define GICR_BASE_GPA 0x80A0000ULL
68
19
@@ -74,22 +25,8 @@ enum guest_stage {
74
25
GUEST_STAGE_MAX ,
75
26
};
76
27
77
- /* Shared variables between host and guest */
78
- struct test_vcpu_shared_data {
79
- uint32_t nr_iter ;
80
- enum guest_stage guest_stage ;
81
- uint64_t xcnt ;
82
- };
83
-
84
- static struct kvm_vcpu * vcpus [KVM_MAX_VCPUS ];
85
- static pthread_t pt_vcpu_run [KVM_MAX_VCPUS ];
86
- static struct test_vcpu_shared_data vcpu_shared_data [KVM_MAX_VCPUS ];
87
-
88
28
static int vtimer_irq , ptimer_irq ;
89
29
90
- static unsigned long * vcpu_done_map ;
91
- static pthread_mutex_t vcpu_done_map_lock ;
92
-
93
30
static void
94
31
guest_configure_timer_action (struct test_vcpu_shared_data * shared_data )
95
32
{
@@ -230,137 +167,6 @@ static void guest_code(void)
230
167
GUEST_DONE ();
231
168
}
232
169
233
- static void * test_vcpu_run (void * arg )
234
- {
235
- unsigned int vcpu_idx = (unsigned long )arg ;
236
- struct ucall uc ;
237
- struct kvm_vcpu * vcpu = vcpus [vcpu_idx ];
238
- struct kvm_vm * vm = vcpu -> vm ;
239
- struct test_vcpu_shared_data * shared_data = & vcpu_shared_data [vcpu_idx ];
240
-
241
- vcpu_run (vcpu );
242
-
243
- /* Currently, any exit from guest is an indication of completion */
244
- pthread_mutex_lock (& vcpu_done_map_lock );
245
- __set_bit (vcpu_idx , vcpu_done_map );
246
- pthread_mutex_unlock (& vcpu_done_map_lock );
247
-
248
- switch (get_ucall (vcpu , & uc )) {
249
- case UCALL_SYNC :
250
- case UCALL_DONE :
251
- break ;
252
- case UCALL_ABORT :
253
- sync_global_from_guest (vm , * shared_data );
254
- fprintf (stderr , "Guest assert failed, vcpu %u; stage; %u; iter: %u\n" ,
255
- vcpu_idx , shared_data -> guest_stage , shared_data -> nr_iter );
256
- REPORT_GUEST_ASSERT (uc );
257
- break ;
258
- default :
259
- TEST_FAIL ("Unexpected guest exit" );
260
- }
261
-
262
- return NULL ;
263
- }
264
-
265
- static uint32_t test_get_pcpu (void )
266
- {
267
- uint32_t pcpu ;
268
- unsigned int nproc_conf ;
269
- cpu_set_t online_cpuset ;
270
-
271
- nproc_conf = get_nprocs_conf ();
272
- sched_getaffinity (0 , sizeof (cpu_set_t ), & online_cpuset );
273
-
274
- /* Randomly find an available pCPU to place a vCPU on */
275
- do {
276
- pcpu = rand () % nproc_conf ;
277
- } while (!CPU_ISSET (pcpu , & online_cpuset ));
278
-
279
- return pcpu ;
280
- }
281
-
282
- static int test_migrate_vcpu (unsigned int vcpu_idx )
283
- {
284
- int ret ;
285
- cpu_set_t cpuset ;
286
- uint32_t new_pcpu = test_get_pcpu ();
287
-
288
- CPU_ZERO (& cpuset );
289
- CPU_SET (new_pcpu , & cpuset );
290
-
291
- pr_debug ("Migrating vCPU: %u to pCPU: %u\n" , vcpu_idx , new_pcpu );
292
-
293
- ret = pthread_setaffinity_np (pt_vcpu_run [vcpu_idx ],
294
- sizeof (cpuset ), & cpuset );
295
-
296
- /* Allow the error where the vCPU thread is already finished */
297
- TEST_ASSERT (ret == 0 || ret == ESRCH ,
298
- "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d" ,
299
- vcpu_idx , new_pcpu , ret );
300
-
301
- return ret ;
302
- }
303
-
304
- static void * test_vcpu_migration (void * arg )
305
- {
306
- unsigned int i , n_done ;
307
- bool vcpu_done ;
308
-
309
- do {
310
- usleep (msecs_to_usecs (test_args .migration_freq_ms ));
311
-
312
- for (n_done = 0 , i = 0 ; i < test_args .nr_vcpus ; i ++ ) {
313
- pthread_mutex_lock (& vcpu_done_map_lock );
314
- vcpu_done = test_bit (i , vcpu_done_map );
315
- pthread_mutex_unlock (& vcpu_done_map_lock );
316
-
317
- if (vcpu_done ) {
318
- n_done ++ ;
319
- continue ;
320
- }
321
-
322
- test_migrate_vcpu (i );
323
- }
324
- } while (test_args .nr_vcpus != n_done );
325
-
326
- return NULL ;
327
- }
328
-
329
- static void test_run (struct kvm_vm * vm )
330
- {
331
- pthread_t pt_vcpu_migration ;
332
- unsigned int i ;
333
- int ret ;
334
-
335
- pthread_mutex_init (& vcpu_done_map_lock , NULL );
336
- vcpu_done_map = bitmap_zalloc (test_args .nr_vcpus );
337
- TEST_ASSERT (vcpu_done_map , "Failed to allocate vcpu done bitmap" );
338
-
339
- for (i = 0 ; i < (unsigned long )test_args .nr_vcpus ; i ++ ) {
340
- ret = pthread_create (& pt_vcpu_run [i ], NULL , test_vcpu_run ,
341
- (void * )(unsigned long )i );
342
- TEST_ASSERT (!ret , "Failed to create vCPU-%d pthread" , i );
343
- }
344
-
345
- /* Spawn a thread to control the vCPU migrations */
346
- if (test_args .migration_freq_ms ) {
347
- srand (time (NULL ));
348
-
349
- ret = pthread_create (& pt_vcpu_migration , NULL ,
350
- test_vcpu_migration , NULL );
351
- TEST_ASSERT (!ret , "Failed to create the migration pthread" );
352
- }
353
-
354
-
355
- for (i = 0 ; i < test_args .nr_vcpus ; i ++ )
356
- pthread_join (pt_vcpu_run [i ], NULL );
357
-
358
- if (test_args .migration_freq_ms )
359
- pthread_join (pt_vcpu_migration , NULL );
360
-
361
- bitmap_free (vcpu_done_map );
362
- }
363
-
364
170
static void test_init_timer_irq (struct kvm_vm * vm )
365
171
{
366
172
/* Timer initid should be same for all the vCPUs, so query only vCPU-0 */
@@ -377,7 +183,7 @@ static void test_init_timer_irq(struct kvm_vm *vm)
377
183
378
184
static int gic_fd ;
379
185
380
- static struct kvm_vm * test_vm_create (void )
186
+ struct kvm_vm * test_vm_create (void )
381
187
{
382
188
struct kvm_vm * vm ;
383
189
unsigned int i ;
@@ -408,87 +214,8 @@ static struct kvm_vm *test_vm_create(void)
408
214
return vm ;
409
215
}
410
216
411
- static void test_vm_cleanup (struct kvm_vm * vm )
217
+ void test_vm_cleanup (struct kvm_vm * vm )
412
218
{
413
219
close (gic_fd );
414
220
kvm_vm_free (vm );
415
221
}
416
-
417
- static void test_print_help (char * name )
418
- {
419
- pr_info ("Usage: %s [-h] [-n nr_vcpus] [-i iterations] [-p timer_period_ms]\n"
420
- "\t\t [-m migration_freq_ms] [-o counter_offset]\n"
421
- "\t\t [-e timer_err_margin_us]\n" , name );
422
- pr_info ("\t-n: Number of vCPUs to configure (default: %u; max: %u)\n" ,
423
- NR_VCPUS_DEF , KVM_MAX_VCPUS );
424
- pr_info ("\t-i: Number of iterations per stage (default: %u)\n" ,
425
- NR_TEST_ITERS_DEF );
426
- pr_info ("\t-p: Periodicity (in ms) of the guest timer (default: %u)\n" ,
427
- TIMER_TEST_PERIOD_MS_DEF );
428
- pr_info ("\t-m: Frequency (in ms) of vCPUs to migrate to different pCPU. 0 to turn off (default: %u)\n" ,
429
- TIMER_TEST_MIGRATION_FREQ_MS );
430
- pr_info ("\t-o: Counter offset (in counter cycles, default: 0)\n" );
431
- pr_info ("\t-e: Interrupt arrival error margin (in us) of the guest timer (default: %u)\n" ,
432
- TIMER_TEST_ERR_MARGIN_US );
433
- pr_info ("\t-h: print this help screen\n" );
434
- }
435
-
436
- static bool parse_args (int argc , char * argv [])
437
- {
438
- int opt ;
439
-
440
- while ((opt = getopt (argc , argv , "hn:i:p:m:o:e:" )) != -1 ) {
441
- switch (opt ) {
442
- case 'n' :
443
- test_args .nr_vcpus = atoi_positive ("Number of vCPUs" , optarg );
444
- if (test_args .nr_vcpus > KVM_MAX_VCPUS ) {
445
- pr_info ("Max allowed vCPUs: %u\n" ,
446
- KVM_MAX_VCPUS );
447
- goto err ;
448
- }
449
- break ;
450
- case 'i' :
451
- test_args .nr_iter = atoi_positive ("Number of iterations" , optarg );
452
- break ;
453
- case 'p' :
454
- test_args .timer_period_ms = atoi_positive ("Periodicity" , optarg );
455
- break ;
456
- case 'm' :
457
- test_args .migration_freq_ms = atoi_non_negative ("Frequency" , optarg );
458
- break ;
459
- case 'e' :
460
- test_args .timer_err_margin_us = atoi_non_negative ("Error Margin" , optarg );
461
- break ;
462
- case 'o' :
463
- test_args .offset .counter_offset = strtol (optarg , NULL , 0 );
464
- test_args .offset .reserved = 0 ;
465
- break ;
466
- case 'h' :
467
- default :
468
- goto err ;
469
- }
470
- }
471
-
472
- return true;
473
-
474
- err :
475
- test_print_help (argv [0 ]);
476
- return false;
477
- }
478
-
479
- int main (int argc , char * argv [])
480
- {
481
- struct kvm_vm * vm ;
482
-
483
- if (!parse_args (argc , argv ))
484
- exit (KSFT_SKIP );
485
-
486
- __TEST_REQUIRE (!test_args .migration_freq_ms || get_nprocs () >= 2 ,
487
- "At least two physical CPUs needed for vCPU migration" );
488
-
489
- vm = test_vm_create ();
490
- test_run (vm );
491
- test_vm_cleanup (vm );
492
-
493
- return 0 ;
494
- }
0 commit comments