Re: [PATCH v5 2/2] KVM: arm64: selftests: Add arch_timer_edge_cases selftest

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> +#include "kvm_util.h"
> +#include "processor.h"
> +#include "delay.h"
nit: Are we using any of the functions from delay.h?

> +#include "arch_timer.h"
> +#include "gic.h"
> +#include "vgic.h"
> +
> +const uint64_t CVAL_MAX = ~0ULL;
> +/* tval is a signed 32-bit int. */
> +const int32_t TVAL_MAX = INT_MAX;
> +const int32_t TVAL_MIN = INT_MIN;
nit: To be super clear, use INT32_{MIN|MAX}

> +
> +/* After how much time we say there is no IRQ. */
> +const uint32_t TIMEOUT_NO_IRQ_US = 50000;
> +
> +/* A nice counter value to use as the starting one for most tests. */
> +const uint64_t DEF_CNT = (CVAL_MAX / 2);
> +
> +/* Number of runs. */
> +const uint32_t NR_TEST_ITERS_DEF = 5;
> +
> +/* Default wait test time in ms. */
> +const uint32_t WAIT_TEST_MS = 10;
> +
> +/* Default "long" wait test time in ms. */
> +const uint32_t LONG_WAIT_TEST_MS = 100;
> +
Define all the above variables as 'static const'.

> +static int vtimer_irq, ptimer_irq;
> +
> +enum sync_cmd {
> +       SET_COUNTER_VALUE = 100001,
nit: Any specific reason to start with this value?

> +       USERSPACE_USLEEP,
> +       USERSPACE_SCHED_YIELD,
> +       USERSPACE_MIGRATE_SELF,
> +       NO_USERSPACE_CMD,
> +};

> +static void guest_irq_handler(struct ex_regs *regs)
> +{
> +       unsigned int intid = gic_get_and_ack_irq();
> +       enum arch_timer timer;
> +       uint64_t cnt, cval;
> +       uint32_t ctl;
> +       bool timer_condition, istatus;
> +
> +       if (intid == IAR_SPURIOUS) {
> +               atomic_inc(&shared_data.spurious);
Any reason why we are accounting for the spurious interrupts? I don't
see the test reading this anywhere.

> +               goto out;
> +       }
> +
> +       if (intid == ptimer_irq)
> +               timer = PHYSICAL;
> +       else if (intid == vtimer_irq)
> +               timer = VIRTUAL;
> +       else
> +               goto out;
> +
> +       ctl = timer_get_ctl(timer);
> +       cval = timer_get_cval(timer);
> +       cnt = timer_get_cntct(timer);
> +       timer_condition = cnt >= cval;
> +       istatus = (ctl & CTL_ISTATUS) && (ctl & CTL_ENABLE);
> +       GUEST_ASSERT_EQ(timer_condition, istatus);
What if both are false? I think checking these independently would be
a better way to go, even in terms of readability.

> +
> +       /* Disable and mask the timer. */
> +       timer_set_ctl(timer, CTL_IMASK);
> +
> +       atomic_inc(&shared_data.handled);
> +
> +out:
> +       gic_set_eoi(intid);
> +}
> +
> +static void set_cval_irq(enum arch_timer timer, uint64_t cval_cycles,
> +                        uint32_t ctl)
> +{
> +       atomic_set(&shared_data.handled, 0);
> +       atomic_set(&shared_data.spurious, 0);
> +       timer_set_cval(timer, cval_cycles);
> +       timer_set_ctl(timer, ctl);
> +}
> +
> +static void set_tval_irq(enum arch_timer timer, uint64_t tval_cycles,
> +                        uint32_t ctl)
> +{
> +       atomic_set(&shared_data.handled, 0);
> +       atomic_set(&shared_data.spurious, 0);
> +       timer_set_tval(timer, tval_cycles);
> +       timer_set_ctl(timer, ctl);
Maybe it's a good idea to set the ctl before the tval/cval. This is
just to avoid narrow races such as, when an IRQ is not needed but,
programming the tval/cval could generate an IRQ before we get a chance
to set ctl. The comment applies to the order in set_cval_irq() as
well.

> +
> +/*
> + * Should be called with IRQs masked.
nit: It might be easy to miss this. Can we add a safety check? Same
goes for other wait_* functions.

> +static void test_xval_check_no_irq(enum arch_timer timer, uint64_t xval,
> +                                  uint64_t usec, enum timer_view timer_view,
> +                                  sleep_method_t guest_sleep)
> +{
> +       local_irq_disable();
> +
> +       set_xval_irq(timer, xval, CTL_ENABLE | CTL_IMASK, timer_view);
> +       guest_sleep(timer, usec);
> +
> +       local_irq_enable();
> +       isb();
> +
> +       /* Assume success (no IRQ) after waiting usec microseconds */
> +       assert_irqs_handled(0);
The test cases calling into this expect no IRQ for the given
conditions. However, since you set CTL_IMASK above, we wouldn't be
getting an IRQ regardless of the fact that the timer condition is met
(erroneously) or not. Should we leave it unmasked and/or check
CTL_ISTATUS?

> +/* Test masking/unmasking a timer using the timer mask (not the IRQ mask). */
> +static void test_timer_control_mask_then_unmask(enum arch_timer timer)
> +{
> +       reset_timer_state(timer, DEF_CNT);
> +       set_tval_irq(timer, -1, CTL_ENABLE | CTL_IMASK);
> +
> +       /* Unmask the timer, and then get an IRQ. */
> +       local_irq_disable();
> +       timer_set_ctl(timer, CTL_ENABLE);
> +       wait_for_non_spurious_irq();
Do you want to loop through @irq_wait_method array like other test
routines or is there a specific reason to call only
wait_for_non_spurious_irq()?

> +
> +       assert_irqs_handled(1);
> +       local_irq_enable();
> +}
> +
> +/* Check that timer control masks actually mask a timer being fired. */
> +static void test_timer_control_masks(enum arch_timer timer)
> +{
> +       reset_timer_state(timer, DEF_CNT);
> +
> +       /* Local IRQs are not masked at this point. */
> +
> +       set_tval_irq(timer, -1, CTL_ENABLE | CTL_IMASK);
> +
> +       /* Assume no IRQ after waiting TIMEOUT_NO_IRQ_US microseconds */
> +       sleep_poll(timer, TIMEOUT_NO_IRQ_US);
Again, do we want to loop through @sleep_method array like other routines?

> +static void test_reprogram_timers(enum arch_timer timer)
> +{
> +       int i;
> +       uint64_t base_wait = test_args.wait_ms;
> +
> +       for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) {
> +               /*
> +                * Ensure reprogramming works whether going from a
> +                * longer time to a shorter or vice versa.
> +                */
> +               test_reprogramming_timer(timer, irq_wait_method[i], 2 * base_wait,
> +                                        base_wait);
> +               test_reprogramming_timer(timer, irq_wait_method[i], base_wait,
> +                                        2 * base_wait);
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(sleep_method); i++) {
> +               test_reprogramming_timer_with_timeout(timer, sleep_method[i],
> +                                                     2 * base_wait, base_wait);
> +               test_reprogramming_timer_with_timeout(timer, sleep_method[i],
> +                                                     base_wait, 2 * base_wait);
Apart from the timeout part, is there anything else that's different
from the above (non-timeout) ones that I might be missing? If not,
would just having the timeout one give the coverage that we are
looking for?

> +       }
> +}
> +
> +static void test_basic_functionality(enum arch_timer timer)
> +{
> +       int32_t tval = (int32_t) msec_to_cycles(test_args.wait_ms);
> +       uint64_t cval;
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) {
> +               irq_wait_method_t wm = irq_wait_method[i];
> +
> +               cval = DEF_CNT + msec_to_cycles(test_args.wait_ms);
> +
nit: Similar to @tval, since the @cval variable doesn't change over
the course of the loop, may be set it outside?

> +               test_timer_cval(timer, cval, wm, true, DEF_CNT);
> +               test_timer_tval(timer, tval, wm, true, DEF_CNT);
> +       }
> +}
> +

> +
> +static void test_timers_sanity_checks(enum arch_timer timer)
> +{
> +       timers_sanity_checks(timer, false);
> +       /* Check how KVM saves/restores these edge-case values. */
> +       timers_sanity_checks(timer, true);
> +}
> +
> +static void test_set_cnt_after_tval_max(enum arch_timer timer, irq_wait_method_t wm)
> +{
> +       local_irq_disable();
> +       reset_timer_state(timer, DEF_CNT);
> +
> +       set_cval_irq(timer,
> +                    (uint64_t) TVAL_MAX +
> +                    msec_to_cycles(test_args.wait_ms) / 2, CTL_ENABLE);
> +
> +       set_counter(timer, TVAL_MAX);
> +       wm();
> +
> +       assert_irqs_handled(1);
When I first read this, I was thinking, "how will this assert pass if
the IRQs are disabled?". But then, I looked into wm() and saw that we
enable the IRQs there. Can we make this part easily readable or at
least add appropriate comments? The comment applies to
test_timer_xval(), test_set_cnt_after_xval(), and any other places
where this approach is taken,

> +       local_irq_enable();
> +}
> +
> +/* Test timers set for: cval = now + TVAL_MAX + wait_ms / 2 */
> +static void test_timers_above_tval_max(enum arch_timer timer)
> +{
> +       uint64_t cval;
> +       int i;
> +
> +       /*
> +        * Test that the system is not implementing cval in terms of
> +        * tval.  If that was the case, setting a cval to "cval = now
> +        * + TVAL_MAX + wait_ms" would wrap to "cval = now +
wait_ms / 2? Also, why /2? Is there a significance?

> +        * wait_ms / 2", and the timer would fire immediately. Test that it
> +        * doesn't.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(sleep_method); i++) {
> +               reset_timer_state(timer, DEF_CNT);
> +               cval =
> +                   timer_get_cntct(timer) + TVAL_MAX +
> +                   msec_to_cycles(test_args.wait_ms) / 2;
> +               test_cval_no_irq(timer, cval,
> +                                msecs_to_usecs(test_args.wait_ms) / 2 +
> +                                TIMEOUT_NO_IRQ_US, sleep_method[i]);
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) {
> +               /* Get the IRQ by moving the counter forward. */
> +               test_set_cnt_after_tval_max(timer, irq_wait_method[i]);
> +       }
> +}
> +
> +/*
> + * Template function to be used by the test_move_counter_ahead_* tests.  It
> + * sets the counter to cnt_1, the [c|t]val, the counter to cnt_2, and
> + * then waits for an IRQ.
> + */
> +static void test_set_cnt_after_xval(enum arch_timer timer, uint64_t cnt_1,
> +                                   uint64_t xval, uint64_t cnt_2,
> +                                   irq_wait_method_t wm, enum timer_view tv)
> +{
> +       local_irq_disable();
> +
> +       set_counter(timer, cnt_1);
> +       timer_set_ctl(timer, CTL_IMASK);
Is the expectation that with cnt_1 programmed, we should not be seeing
an IRQ? If that's the case, should we unmask the interrupts and
assert_irqs_handled(0) and/or check CTL_ISTATUS?

> +
> +       set_xval_irq(timer, xval, CTL_ENABLE, tv);
> +       set_counter(timer, cnt_2);
> +       wm();
> +
> +       assert_irqs_handled(1);
> +       local_irq_enable();
> +}
> +
> +/*
> + * Template function to be used by the test_move_counter_ahead_* tests.  It
> + * sets the counter to cnt_1, the [c|t]val, the counter to cnt_2, and
> + * then waits for an IRQ.
"waits for an IRQ to not show up"?

> + */
> +static void test_set_cnt_after_xval_no_irq(enum arch_timer timer,
> +                                          uint64_t cnt_1, uint64_t xval,
> +                                          uint64_t cnt_2,
> +                                          sleep_method_t guest_sleep,
> +                                          enum timer_view tv)
> +{
> +       local_irq_disable();
> +
> +       set_counter(timer, cnt_1);
> +       timer_set_ctl(timer, CTL_IMASK);
> +
> +       set_xval_irq(timer, xval, CTL_ENABLE, tv);
> +       set_counter(timer, cnt_2);
> +       guest_sleep(timer, TIMEOUT_NO_IRQ_US);
> +
> +       local_irq_enable();
> +       isb();
> +
> +       /* Assume no IRQ after waiting TIMEOUT_NO_IRQ_US microseconds */
> +       assert_irqs_handled(0);
> +       timer_set_ctl(timer, CTL_IMASK);
> +}
> +

> +static void test_timers_in_the_past(enum arch_timer timer)
> +{
> +       int32_t tval = -1 * (int32_t) msec_to_cycles(test_args.wait_ms);
> +       uint64_t cval;
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) {
> +               irq_wait_method_t wm = irq_wait_method[i];
> +
> +               /* set a timer wait_ms the past. */
> +               cval = DEF_CNT - msec_to_cycles(test_args.wait_ms);
> +               test_timer_cval(timer, cval, wm, true, DEF_CNT);
> +               test_timer_tval(timer, tval, wm, true, DEF_CNT);
> +
> +               /* Set a timer to counter=0 (in the past) */
> +               test_timer_cval(timer, 0, wm, true, DEF_CNT);
> +
> +               /* Set a time for tval=0 (now) */
> +               test_timer_tval(timer, 0, wm, true, DEF_CNT);
> +
> +               /* Set a timer to as far in the past as possible */
> +               test_timer_tval(timer, TVAL_MIN, wm, true, DEF_CNT);
Is this part of the test any different from the above call,
test_timer_tval(timer, tval, wm, true, DEF_CNT)? I'm guessing both
would wrap around and meet the timer condition, with the only
difference of 'when'.

> +       }
> +
> +       /*
> +        * Set the counter to wait_ms, and a tval to -wait_ms. There should be no
> +        * timer as that tval means cval=CVAL_MAX-wait_ms.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(sleep_method); i++) {
> +               sleep_method_t sm = sleep_method[i];
> +
> +               set_counter(timer, msec_to_cycles(test_args.wait_ms));
> +               test_tval_no_irq(timer, tval, TIMEOUT_NO_IRQ_US, sm);
> +       }
I'm not sure if I understand what we are trying to test here. Do you
mind re-framing the comment for better clarity?

> +}
> +
> +static void test_long_timer_delays(enum arch_timer timer)
> +{
> +       int32_t tval = (int32_t) msec_to_cycles(test_args.long_wait_ms);
> +       uint64_t cval;
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(irq_wait_method); i++) {
> +               irq_wait_method_t wm = irq_wait_method[i];
> +
> +               cval = DEF_CNT + msec_to_cycles(test_args.long_wait_ms);
nit: Maybe set this outside the loop?

> +               test_timer_cval(timer, cval, wm, true, DEF_CNT);
> +               test_timer_tval(timer, tval, wm, true, DEF_CNT);
> +       }
> +}
> +

Thank you.
Raghavendra



>
> +
> +static void test_vm_create(struct kvm_vm **vm, struct kvm_vcpu **vcpu,
> +                          enum arch_timer timer)
> +{
> +       *vm = vm_create_with_one_vcpu(vcpu, guest_code);
> +       TEST_ASSERT(*vm, "Failed to create the test VM\n");
> +
> +       vm_init_descriptor_tables(*vm);
> +       vm_install_exception_handler(*vm, VECTOR_IRQ_CURRENT,
> +                                    guest_irq_handler);
> +
> +       vcpu_init_descriptor_tables(*vcpu);
> +       vcpu_args_set(*vcpu, 1, timer);
> +
> +       test_init_timer_irq(*vm, *vcpu);
> +       vgic_v3_setup(*vm, 1, 64);
> +       sync_global_to_guest(*vm, test_args);
> +}
> +
> +static void test_print_help(char *name)
> +{
> +       pr_info("Usage: %s [-h] [-b] [-i iterations] [-l long_wait_ms] [-p] [-v]\n"
> +               , name);
> +       pr_info("\t-i: Number of iterations (default: %u)\n",
> +               NR_TEST_ITERS_DEF);
> +       pr_info("\t-b: Test both physical and virtual timers (default: true)\n");
> +       pr_info("\t-l: Delta (in ms) used for long wait time test (default: %u)\n",
> +            LONG_WAIT_TEST_MS);
> +       pr_info("\t-l: Delta (in ms) used for wait times (default: %u)\n",
> +               WAIT_TEST_MS);
> +       pr_info("\t-p: Test physical timer (default: true)\n");
> +       pr_info("\t-v: Test virtual timer (default: true)\n");
> +       pr_info("\t-h: Print this help message\n");
> +}
> +
> +static bool parse_args(int argc, char *argv[])
> +{
> +       int opt;
> +
> +       while ((opt = getopt(argc, argv, "bhi:l:pvw:")) != -1) {
> +               switch (opt) {
> +               case 'b':
> +                       test_args.test_physical = true;
> +                       test_args.test_virtual = true;
> +                       break;
> +               case 'i':
> +                       test_args.iterations =
> +                           atoi_positive("Number of iterations", optarg);
> +                       break;
> +               case 'l':
> +                       test_args.long_wait_ms =
> +                           atoi_positive("Long wait time", optarg);
> +                       break;
> +               case 'p':
> +                       test_args.test_physical = true;
> +                       test_args.test_virtual = false;
> +                       break;
> +               case 'v':
> +                       test_args.test_virtual = true;
> +                       test_args.test_physical = false;
> +                       break;
> +               case 'w':
> +                       test_args.wait_ms = atoi_positive("Wait time", optarg);
> +                       break;
> +               case 'h':
> +               default:
> +                       goto err;
> +               }
> +       }
> +
> +       return true;
> +
> + err:
> +       test_print_help(argv[0]);
> +       return false;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       struct kvm_vcpu *vcpu;
> +       struct kvm_vm *vm;
> +
> +       /* Tell stdout not to buffer its content */
> +       setbuf(stdout, NULL);
> +
> +       if (!parse_args(argc, argv))
> +               exit(KSFT_SKIP);
> +
> +       if (test_args.test_virtual) {
> +               test_vm_create(&vm, &vcpu, VIRTUAL);
> +               test_run(vm, vcpu);
> +               kvm_vm_free(vm);
> +       }
> +
> +       if (test_args.test_physical) {
> +               test_vm_create(&vm, &vcpu, PHYSICAL);
> +               test_run(vm, vcpu);
> +               kvm_vm_free(vm);
> +       }
> +
> +       return 0;
> +}
> diff --git a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h
> index b3e97525cb55..bf461de34785 100644
> --- a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h
> +++ b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h
> @@ -79,7 +79,7 @@ static inline uint64_t timer_get_cval(enum arch_timer timer)
>         return 0;
>  }
>
> -static inline void timer_set_tval(enum arch_timer timer, uint32_t tval)
> +static inline void timer_set_tval(enum arch_timer timer, int32_t tval)
>  {
>         switch (timer) {
>         case VIRTUAL:
> @@ -95,6 +95,22 @@ static inline void timer_set_tval(enum arch_timer timer, uint32_t tval)
>         isb();
>  }
>
> +static inline int32_t timer_get_tval(enum arch_timer timer)
> +{
> +       isb();
> +       switch (timer) {
> +       case VIRTUAL:
> +               return read_sysreg(cntv_tval_el0);
> +       case PHYSICAL:
> +               return read_sysreg(cntp_tval_el0);
> +       default:
> +               GUEST_FAIL("Could not get timer %d\n", timer);
> +       }
> +
> +       /* We should not reach here */
> +       return 0;
> +}
> +
>  static inline void timer_set_ctl(enum arch_timer timer, uint32_t ctl)
>  {
>         switch (timer) {
> --
> 2.46.0.76.ge559c4bf1a-goog
>




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux