11// SPDX-License-Identifier: GPL-2.0-only
22/*
3- * arch_timer.c - Tests the aarch64 timer IRQ functionality
4- *
53 * 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.
215 *
226 * Copyright (c) 2021, Google LLC.
237 */
248#define _GNU_SOURCE
259
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"
3610#include "arch_timer.h"
11+ #include "delay.h"
3712#include "gic.h"
13+ #include "processor.h"
14+ #include "timer_test.h"
3815#include "vgic.h"
3916
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-
6617#define GICD_BASE_GPA 0x8000000ULL
6718#define GICR_BASE_GPA 0x80A0000ULL
6819
@@ -74,22 +25,8 @@ enum guest_stage {
7425 GUEST_STAGE_MAX ,
7526};
7627
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-
8828static int vtimer_irq , ptimer_irq ;
8929
90- static unsigned long * vcpu_done_map ;
91- static pthread_mutex_t vcpu_done_map_lock ;
92-
9330static void
9431guest_configure_timer_action (struct test_vcpu_shared_data * shared_data )
9532{
@@ -230,137 +167,6 @@ static void guest_code(void)
230167 GUEST_DONE ();
231168}
232169
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-
364170static void test_init_timer_irq (struct kvm_vm * vm )
365171{
366172 /* 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)
377183
378184static int gic_fd ;
379185
380- static struct kvm_vm * test_vm_create (void )
186+ struct kvm_vm * test_vm_create (void )
381187{
382188 struct kvm_vm * vm ;
383189 unsigned int i ;
@@ -408,87 +214,8 @@ static struct kvm_vm *test_vm_create(void)
408214 return vm ;
409215}
410216
411- static void test_vm_cleanup (struct kvm_vm * vm )
217+ void test_vm_cleanup (struct kvm_vm * vm )
412218{
413219 close (gic_fd );
414220 kvm_vm_free (vm );
415221}
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