Skip to content

Commit c20dd9e

Browse files
xiaobo55xavpatel
authored andcommitted
KVM: arm64: selftests: Split arch_timer test code
Split the arch-neutral test code out of aarch64/arch_timer.c and put them into a common arch_timer.c. This is a preparation to share timer test codes in riscv. Suggested-by: Andrew Jones <[email protected]> Signed-off-by: Haibo Xu <[email protected]> Reviewed-by: Andrew Jones <[email protected]> Signed-off-by: Anup Patel <[email protected]>
1 parent d1dafd0 commit c20dd9e

File tree

5 files changed

+311
-280
lines changed

5 files changed

+311
-280
lines changed

tools/testing/selftests/kvm/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ TEST_GEN_PROGS_x86_64 += system_counter_offset_test
143143
TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test
144144

145145
TEST_GEN_PROGS_aarch64 += aarch64/aarch32_id_regs
146-
TEST_GEN_PROGS_aarch64 += aarch64/arch_timer
147146
TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
148147
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
149148
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
@@ -155,6 +154,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
155154
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
156155
TEST_GEN_PROGS_aarch64 += aarch64/vpmu_counter_access
157156
TEST_GEN_PROGS_aarch64 += access_tracking_perf_test
157+
TEST_GEN_PROGS_aarch64 += arch_timer
158158
TEST_GEN_PROGS_aarch64 += demand_paging_test
159159
TEST_GEN_PROGS_aarch64 += dirty_log_test
160160
TEST_GEN_PROGS_aarch64 += dirty_log_perf_test
@@ -194,6 +194,7 @@ TEST_GEN_PROGS_riscv += kvm_page_table_test
194194
TEST_GEN_PROGS_riscv += set_memory_region_test
195195
TEST_GEN_PROGS_riscv += steal_time
196196

197+
SPLIT_TESTS += arch_timer
197198
SPLIT_TESTS += get-reg-list
198199

199200
TEST_PROGS += $(TEST_PROGS_$(ARCH_DIR))
Lines changed: 6 additions & 279 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,19 @@
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-
8828
static int vtimer_irq, ptimer_irq;
8929

90-
static unsigned long *vcpu_done_map;
91-
static pthread_mutex_t vcpu_done_map_lock;
92-
9330
static void
9431
guest_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-
364170
static 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

378184
static 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

Comments
 (0)