Re: [PATCH] KVM: selftests: Test IPI to halted vCPU in xAPIC while backing page moves

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

 



On Thu, Nov 5, 2020 at 2:38 PM Peter Shier <pshier@xxxxxxxxxx> wrote:
>
> When a guest is using xAPIC KVM allocates a backing page for the required
> EPT entry for the APIC access address set in the VMCS. If mm decides to
> move that page the KVM mmu notifier will update the VMCS with the new
> HPA. This test induces a page move to test that APIC access continues to
> work correctly. It is a directed test for
> commit e649b3f0188f "KVM: x86: Fix APIC page invalidation race".
>
> Tested: ran for 1 hour on a skylake, migrating backing page every 1ms
>
> Depends on patch "selftests: kvm: Add exception handling to selftests"
> from aaronlewis@xxxxxxxxxx that has not yet been queued.
>
> Signed-off-by: Peter Shier <pshier@xxxxxxxxxx>
> Reviewed-by: Jim Mattson <jmattson@xxxxxxxxxx>
> Reviewed-by: Ricardo Koller <ricarkol@xxxxxxxxxx>
> ---
>  tools/testing/selftests/kvm/.gitignore        |   1 +
>  tools/testing/selftests/kvm/Makefile          |   1 +
>  tools/testing/selftests/kvm/include/numaif.h  |  55 ++
>  .../selftests/kvm/include/x86_64/processor.h  |  20 +
>  .../selftests/kvm/x86_64/xapic_ipi_test.c     | 544 ++++++++++++++++++
>  5 files changed, 621 insertions(+)
>  create mode 100644 tools/testing/selftests/kvm/include/numaif.h
>  create mode 100644 tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
>
> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index 307ceaadbbb9..56f8367e2ace 100644
> --- a/tools/testing/selftests/kvm/.gitignore
> +++ b/tools/testing/selftests/kvm/.gitignore
> @@ -19,6 +19,7 @@
>  /x86_64/vmx_dirty_log_test
>  /x86_64/vmx_set_nested_state_test
>  /x86_64/vmx_tsc_adjust_test
> +/x86_64/xapic_ipi_test
>  /x86_64/xss_msr_test
>  /clear_dirty_log_test
>  /demand_paging_test
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index aaaf992faf87..19283554ef5e 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -53,6 +53,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test
>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_dirty_log_test
>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test
>  TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
> +TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test
>  TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
>  TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
>  TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
> diff --git a/tools/testing/selftests/kvm/include/numaif.h b/tools/testing/selftests/kvm/include/numaif.h
> new file mode 100644
> index 000000000000..b020547403fd
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/include/numaif.h
> @@ -0,0 +1,55 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * tools/testing/selftests/kvm/include/numaif.h
> + *
> + * Copyright (C) 2020, Google LLC.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.
> + *
> + * Header file that provides access to NUMA API functions not explicitly
> + * exported to user space.
> + */
> +
> +#ifndef SELFTEST_KVM_NUMAIF_H
> +#define SELFTEST_KVM_NUMAIF_H
> +
> +#define __NR_get_mempolicy 239
> +#define __NR_migrate_pages 256
> +
> +/* System calls */
> +long get_mempolicy(int *policy, const unsigned long *nmask,
> +                  unsigned long maxnode, void *addr, int flags)
> +{
> +       return syscall(__NR_get_mempolicy, policy, nmask,
> +                      maxnode, addr, flags);
> +}
> +
> +long migrate_pages(int pid, unsigned long maxnode,
> +                  const unsigned long *frommask,
> +                  const unsigned long *tomask)
> +{
> +       return syscall(__NR_migrate_pages, pid, maxnode, frommask, tomask);
> +}
> +
> +/* Policies */
> +#define MPOL_DEFAULT    0
> +#define MPOL_PREFERRED  1
> +#define MPOL_BIND       2
> +#define MPOL_INTERLEAVE         3
> +
> +#define MPOL_MAX MPOL_INTERLEAVE
> +
> +/* Flags for get_mem_policy */
> +#define MPOL_F_NODE        (1<<0)  /* return next il node or node of address */
> +                                   /* Warning: MPOL_F_NODE is unsupported and
> +                                    * subject to change. Don't use.
> +                                    */
> +#define MPOL_F_ADDR        (1<<1)  /* look up vma using address */
> +#define MPOL_F_MEMS_ALLOWED (1<<2)  /* query nodes allowed in cpuset */
> +
> +/* Flags for mbind */
> +#define MPOL_MF_STRICT      (1<<0) /* Verify existing pages in the mapping */
> +#define MPOL_MF_MOVE        (1<<1) /* Move pages owned by this process to conform to mapping */
> +#define MPOL_MF_MOVE_ALL     (1<<2) /* Move every page to conform to mapping */
> +
> +#endif /* SELFTEST_KVM_NUMAIF_H */
> diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
> index 02530dc6339b..313ec00b1f7c 100644
> --- a/tools/testing/selftests/kvm/include/x86_64/processor.h
> +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
> @@ -377,8 +377,27 @@ void vm_handle_exception(struct kvm_vm *vm, int vector,
>  #define X86_CR0_CD          (1UL<<30) /* Cache Disable */
>  #define X86_CR0_PG          (1UL<<31) /* Paging */
>
> +#define APIC_DEFAULT_GPA               0xfee00000ULL
> +
> +/* APIC base address MSR and fields */
> +#define MSR_IA32_APICBASE              0x0000001b
> +#define MSR_IA32_APICBASE_BSP          (1<<8)
> +#define MSR_IA32_APICBASE_EXTD         (1<<10)
> +#define MSR_IA32_APICBASE_ENABLE       (1<<11)
> +#define MSR_IA32_APICBASE_BASE         (0xfffff<<12)
> +#define                GET_APIC_BASE(x)        (((x) >> 12) << 12)
> +
>  #define APIC_BASE_MSR  0x800
>  #define X2APIC_ENABLE  (1UL << 10)
> +#define        APIC_ID         0x20
> +#define        APIC_LVR        0x30
> +#define                GET_APIC_ID_FIELD(x)    (((x) >> 24) & 0xFF)
> +#define        APIC_TASKPRI    0x80
> +#define        APIC_PROCPRI    0xA0
> +#define        APIC_EOI        0xB0
> +#define        APIC_SPIV       0xF0
> +#define                APIC_SPIV_FOCUS_DISABLED        (1 << 9)
> +#define                APIC_SPIV_APIC_ENABLED          (1 << 8)
>  #define        APIC_ICR        0x300
>  #define                APIC_DEST_SELF          0x40000
>  #define                APIC_DEST_ALLINC        0x80000
> @@ -403,6 +422,7 @@ void vm_handle_exception(struct kvm_vm *vm, int vector,
>  #define                APIC_DM_EXTINT          0x00700
>  #define                APIC_VECTOR_MASK        0x000FF
>  #define        APIC_ICR2       0x310
> +#define                SET_APIC_DEST_FIELD(x)  ((x) << 24)
>
>  /* VMX_EPT_VPID_CAP bits */
>  #define VMX_EPT_VPID_CAP_AD_BITS       (1ULL << 21)
> diff --git a/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
> new file mode 100644
> index 000000000000..47c0ec975330
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
> @@ -0,0 +1,544 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * xapic_ipi_test
> + *
> + * Copyright (C) 2020, Google LLC.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2.
> + *
> + * Test that when the APIC is in xAPIC mode, a vCPU can send an IPI to wake
> + * another vCPU that is halted when KVM's backing page for the APIC access
> + * address has been moved by mm.
> + *
> + * The test starts two vCPUs: one that sends IPIs and one that continually
> + * executes HLT. The sender checks that the halter has woken from the HLT and
> + * has reentered HLT before sending the next IPI. While the vCPUs are running,
> + * the host continually calls migrate_pages to move all of the process' pages
> + * amongst the available numa nodes on the machine.
> + *
> + * Migration is a command line option. When used on non-numa machines will
> + * exit with error. Test is still usefull on non-numa for testing IPIs.
> + */
> +
> +#define _GNU_SOURCE /* for program_invocation_short_name */
> +#include <getopt.h>
> +#include <pthread.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <time.h>
> +
> +#include "kvm_util.h"
> +#include "numaif.h"
> +#include "processor.h"
> +#include "test_util.h"
> +#include "vmx.h"
> +
> +/* Default running time for the test */
> +#define DEFAULT_RUN_SECS 3
> +
> +/* Default delay between migrate_pages calls (microseconds) */
> +#define DEFAULT_DELAY_USECS 500000
> +
> +#define HALTER_VCPU_ID 0
> +#define SENDER_VCPU_ID 1
> +
> +volatile uint32_t *apic_base = (volatile uint32_t *)APIC_DEFAULT_GPA;
> +
> +/*
> + * Vector for IPI from sender vCPU to halting vCPU.
> + * Value is arbitrary and was chosen for the alternating bit pattern. Any
> + * value should work.
> + */
> +#define IPI_VECTOR      0xa5
> +
> +/*
> + * Incremented in the IPI handler. Provides evidence to the sender that the IPI
> + * arrived at the destination
> + */
> +static volatile uint64_t ipis_rcvd;
> +
> +/* Data struct shared between host main thread and vCPUs */
> +struct test_data_page {
> +       uint32_t halter_apic_id;
> +       volatile uint64_t hlt_count;
> +       volatile uint64_t wake_count;
> +       uint64_t ipis_sent;
> +       uint64_t migrations_attempted;
> +       uint64_t migrations_completed;
> +       uint32_t icr;
> +       uint32_t icr2;
> +       uint32_t halter_tpr;
> +       uint32_t halter_ppr;
> +
> +       /*
> +        *  Record local version register as a cross-check that APIC access
> +        *  worked. Value should match what KVM reports (APIC_VERSION in
> +        *  arch/x86/kvm/lapic.c). If test is failing, check that values match
> +        *  to determine whether APIC access exits are working.
> +        */
> +       uint32_t halter_lvr;
> +};
> +
> +struct thread_params {
> +       struct test_data_page *data;
> +       struct kvm_vm *vm;
> +       uint32_t vcpu_id;
> +       uint64_t *pipis_rcvd; /* host address of ipis_rcvd global */
> +};
> +
> +uint32_t read_apic_reg(uint reg)
> +{
> +       return apic_base[reg >> 2];
> +}
> +
> +void write_apic_reg(uint reg, uint32_t val)
> +{
> +       apic_base[reg >> 2] = val;
> +}
> +
> +void disable_apic(void)
> +{
> +       wrmsr(MSR_IA32_APICBASE,
> +             rdmsr(MSR_IA32_APICBASE) &
> +               ~(MSR_IA32_APICBASE_ENABLE | MSR_IA32_APICBASE_EXTD));
> +}
> +
> +void enable_xapic(void)
> +{
> +       uint64_t val = rdmsr(MSR_IA32_APICBASE);
> +
> +       /* Per SDM: to enable xAPIC when in x2APIC must first disable APIC */
> +       if (val & MSR_IA32_APICBASE_EXTD) {
> +               disable_apic();
> +               wrmsr(MSR_IA32_APICBASE,
> +                     rdmsr(MSR_IA32_APICBASE) | MSR_IA32_APICBASE_ENABLE);
> +       } else if (!(val & MSR_IA32_APICBASE_ENABLE)) {
> +               wrmsr(MSR_IA32_APICBASE, val | MSR_IA32_APICBASE_ENABLE);
> +       }
> +
> +       /*
> +        * Per SDM: reset value of spurious interrupt vector register has the
> +        * APIC software enabled bit=0. It must be enabled in addition to the
> +        * enable bit in the MSR.
> +        */
> +       val = read_apic_reg(APIC_SPIV) | APIC_SPIV_APIC_ENABLED;
> +       write_apic_reg(APIC_SPIV, val);
> +}
> +
> +void verify_apic_base_addr(void)
> +{
> +       uint64_t msr = rdmsr(MSR_IA32_APICBASE);
> +       uint64_t base = GET_APIC_BASE(msr);
> +
> +       GUEST_ASSERT(base == APIC_DEFAULT_GPA);
> +}
> +
> +static void halter_guest_code(struct test_data_page *data)
> +{
> +       verify_apic_base_addr();
> +       enable_xapic();
> +
> +       data->halter_apic_id = GET_APIC_ID_FIELD(read_apic_reg(APIC_ID));
> +       data->halter_lvr = read_apic_reg(APIC_LVR);
> +
> +       /*
> +        * Loop forever HLTing and recording halts & wakes. Disable interrupts
> +        * each time around to minimize window between signaling the pending
> +        * halt to the sender vCPU and executing the halt. No need to disable on
> +        * first run as this vCPU executes first and the host waits for it to
> +        * signal going into first halt before starting the sender vCPU. Record
> +        * TPR and PPR for diagnostic purposes in case the test fails.
> +        */
> +       for (;;) {
> +               data->halter_tpr = read_apic_reg(APIC_TASKPRI);
> +               data->halter_ppr = read_apic_reg(APIC_PROCPRI);
> +               data->hlt_count++;
> +               asm volatile("sti; hlt; cli");
> +               data->wake_count++;
> +       }
> +}
> +
> +/*
> + * Runs on halter vCPU when IPI arrives. Write an arbitrary non-zero value to
> + * enable diagnosing errant writes to the APIC access address backing page in
> + * case of test failure.
> + */
> +static void guest_ipi_handler(struct ex_regs *regs)
> +{
> +       ipis_rcvd++;
> +       write_apic_reg(APIC_EOI, 77);
> +}
> +
> +static void sender_guest_code(struct test_data_page *data)
> +{
> +       uint64_t last_wake_count;
> +       uint64_t last_hlt_count;
> +       uint64_t last_ipis_rcvd_count;
> +       uint32_t icr_val;
> +       uint32_t icr2_val;
> +       uint64_t tsc_start;
> +
> +       verify_apic_base_addr();
> +       enable_xapic();
> +
> +       /*
> +        * Init interrupt command register for sending IPIs
> +        *
> +        * Delivery mode=fixed, per SDM:
> +        *   "Delivers the interrupt specified in the vector field to the target
> +        *    processor."
> +        *
> +        * Destination mode=physical i.e. specify target by its local APIC
> +        * ID. This vCPU assumes that the halter vCPU has already started and
> +        * set data->halter_apic_id.
> +        */
> +       icr_val = (APIC_DEST_PHYSICAL | APIC_DM_FIXED | IPI_VECTOR);
> +       icr2_val = SET_APIC_DEST_FIELD(data->halter_apic_id);
> +       data->icr = icr_val;
> +       data->icr2 = icr2_val;
> +
> +       last_wake_count = data->wake_count;
> +       last_hlt_count = data->hlt_count;
> +       last_ipis_rcvd_count = ipis_rcvd;
> +       for (;;) {
> +               /*
> +                * Send IPI to halter vCPU.
> +                * First IPI can be sent unconditionally because halter vCPU
> +                * starts earlier.
> +                */
> +               write_apic_reg(APIC_ICR2, icr2_val);
> +               write_apic_reg(APIC_ICR, icr_val);
> +               data->ipis_sent++;
> +
> +               /*
> +                * Wait up to ~1 sec for halter to indicate that it has:
> +                * 1. Received the IPI
> +                * 2. Woken up from the halt
> +                * 3. Gone back into halt
> +                * Current CPUs typically run at 2.x Ghz which is ~2
> +                * billion ticks per second.
> +                */
> +               tsc_start = rdtsc();
> +               while (rdtsc() - tsc_start < 2000000000) {
> +                       if ((ipis_rcvd != last_ipis_rcvd_count) &&
> +                           (data->wake_count != last_wake_count) &&
> +                           (data->hlt_count != last_hlt_count))
> +                               break;
> +               }
> +
> +               GUEST_ASSERT((ipis_rcvd != last_ipis_rcvd_count) &&
> +                            (data->wake_count != last_wake_count) &&
> +                            (data->hlt_count != last_hlt_count));
> +
> +               last_wake_count = data->wake_count;
> +               last_hlt_count = data->hlt_count;
> +               last_ipis_rcvd_count = ipis_rcvd;
> +       }
> +}
> +
> +static void *vcpu_thread(void *arg)
> +{
> +       struct thread_params *params = (struct thread_params *)arg;
> +       struct ucall uc;
> +       int old;
> +       int r;
> +       unsigned int exit_reason;
> +
> +       r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
> +       TEST_ASSERT(r == 0,
> +                   "pthread_setcanceltype failed on vcpu_id=%u with errno=%d",
> +                   params->vcpu_id, r);
> +
> +       fprintf(stderr, "vCPU thread running vCPU %u\n", params->vcpu_id);
> +       vcpu_run(params->vm, params->vcpu_id);
> +       exit_reason = vcpu_state(params->vm, params->vcpu_id)->exit_reason;
> +
> +       TEST_ASSERT(exit_reason == KVM_EXIT_IO,
> +                   "vCPU %u exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO",
> +                   params->vcpu_id, exit_reason, exit_reason_str(exit_reason));
> +
> +       if (get_ucall(params->vm, params->vcpu_id, &uc) == UCALL_ABORT) {
> +               TEST_ASSERT(false,
> +                           "vCPU %u exited with error: %s.\n"
> +                           "Sending vCPU sent %lu IPIs to halting vCPU\n"
> +                           "Halting vCPU halted %lu times, woke %lu times, received %lu IPIs.\n"
> +                           "Halter TPR=%#x PPR=%#x LVR=%#x\n"
> +                           "Migrations attempted: %lu\n"
> +                           "Migrations completed: %lu\n",
> +                           params->vcpu_id, (const char *)uc.args[0],
> +                           params->data->ipis_sent, params->data->hlt_count,
> +                           params->data->wake_count,
> +                           *params->pipis_rcvd, params->data->halter_tpr,
> +                           params->data->halter_ppr, params->data->halter_lvr,
> +                           params->data->migrations_attempted,
> +                           params->data->migrations_completed);
> +       }
> +
> +       return NULL;
> +}
> +
> +static void cancel_join_vcpu_thread(pthread_t thread, uint32_t vcpu_id)
> +{
> +       void *retval;
> +       int r;
> +
> +       r = pthread_cancel(thread);
> +       TEST_ASSERT(r == 0,
> +                   "pthread_cancel on vcpu_id=%d failed with errno=%d",
> +                   vcpu_id, r);
> +
> +       r = pthread_join(thread, &retval);
> +       TEST_ASSERT(r == 0,
> +                   "pthread_join on vcpu_id=%d failed with errno=%d",
> +                   vcpu_id, r);
> +       TEST_ASSERT(retval == PTHREAD_CANCELED,
> +                   "expected retval=%p, got %p", PTHREAD_CANCELED,
> +                   retval);
> +}
> +
> +void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs,
> +                  uint64_t *pipis_rcvd)
> +{
> +       long pages_not_moved;
> +       unsigned long nodemask = 0;
> +       unsigned long nodemasks[sizeof(nodemask) * 8];
> +       int nodes = 0;
> +       time_t start_time, last_update, now;
> +       time_t interval_secs = 1;
> +       int i, r;
> +       int from, to;
> +       unsigned long bit;
> +       uint64_t hlt_count;
> +       uint64_t wake_count;
> +       uint64_t ipis_sent;
> +
> +       fprintf(stderr, "Calling migrate_pages every %d microseconds\n",
> +               delay_usecs);
> +
> +       /* Get set of first 64 numa nodes available */
> +       r = get_mempolicy(NULL, &nodemask, sizeof(nodemask) * 8,
> +                         0, MPOL_F_MEMS_ALLOWED);
> +       TEST_ASSERT(r == 0, "get_mempolicy failed errno=%d", errno);
> +
> +       fprintf(stderr, "Numa nodes found amongst first %lu possible nodes "
> +               "(each 1-bit indicates node is present): %#lx\n",
> +               sizeof(nodemask) * 8, nodemask);
> +
> +       /* Init array of masks containing a single-bit in each, one for each
> +        * available node. migrate_pages called below requires specifying nodes
> +        * as bit masks.
> +        */
> +       for (i = 0, bit = 1; i < sizeof(nodemask) * 8; i++, bit <<= 1) {
> +               if (nodemask & bit) {
> +                       nodemasks[nodes] = nodemask & bit;
> +                       nodes++;
> +               }
> +       }
> +
> +       TEST_ASSERT(nodes > 1,
> +                   "Did not find at least 2 numa nodes. Can't do migration\n");
> +
> +       fprintf(stderr, "Migrating amongst %d nodes found\n", nodes);
> +
> +       from = 0;
> +       to = 1;
> +       start_time = time(NULL);
> +       last_update = start_time;
> +
> +       ipis_sent = data->ipis_sent;
> +       hlt_count = data->hlt_count;
> +       wake_count = data->wake_count;
> +
> +       while ((int)(time(NULL) - start_time) < run_secs) {
> +               data->migrations_attempted++;
> +
> +               /*
> +                * migrate_pages with PID=0 will migrate all pages of this
> +                * process between the nodes specified as bitmasks. The page
> +                * backing the APIC access address belongs to this process
> +                * because it is allocated by KVM in the context of the
> +                * KVM_CREATE_VCPU ioctl. If that assumption ever changes this
> +                * test may break or give a false positive signal.
> +                */
> +               pages_not_moved = migrate_pages(0, sizeof(nodemasks[from]),
> +                                               &nodemasks[from],
> +                                               &nodemasks[to]);
> +               if (pages_not_moved < 0)
> +                       fprintf(stderr,
> +                               "migrate_pages failed, errno=%d\n", errno);
> +               else if (pages_not_moved > 0)
> +                       fprintf(stderr,
> +                               "migrate_pages could not move %ld pages\n",
> +                               pages_not_moved);
> +               else
> +                       data->migrations_completed++;
> +
> +               from = to;
> +               to++;
> +               if (to == nodes)
> +                       to = 0;
> +
> +               now = time(NULL);
> +               if (((now - start_time) % interval_secs == 0) &&
> +                   (now != last_update)) {
> +                       last_update = now;
> +                       fprintf(stderr,
> +                               "%lu seconds: Migrations attempted=%lu completed=%lu, "
> +                               "IPIs sent=%lu received=%lu, HLTs=%lu wakes=%lu\n",
> +                               now - start_time, data->migrations_attempted,
> +                               data->migrations_completed,
> +                               data->ipis_sent, *pipis_rcvd,
> +                               data->hlt_count, data->wake_count);
> +
> +                       TEST_ASSERT(ipis_sent != data->ipis_sent &&
> +                                   hlt_count != data->hlt_count &&
> +                                   wake_count != data->wake_count,
> +                                   "IPI, HLT and wake count have not increased "
> +                                   "in the last %lu seconds. "
> +                                   "HLTer is likely hung.\n", interval_secs);
> +
> +                       ipis_sent = data->ipis_sent;
> +                       hlt_count = data->hlt_count;
> +                       wake_count = data->wake_count;
> +               }
> +               usleep(delay_usecs);
> +       }
> +}
> +
> +void get_cmdline_args(int argc, char *argv[], int *run_secs,
> +                     bool *migrate, int *delay_usecs)
> +{
> +       for (;;) {
> +               int opt = getopt(argc, argv, "s:d:m");
> +
> +               if (opt == -1)
> +                       break;
> +               switch (opt) {
> +               case 's':
> +                       *run_secs = parse_size(optarg);
> +                       break;
> +               case 'm':
> +                       *migrate = true;
> +                       break;
> +               case 'd':
> +                       *delay_usecs = parse_size(optarg);
> +                       break;
> +               default:
> +                       TEST_ASSERT(false,
> +                                   "Usage: -s <runtime seconds>. Default is %d seconds.\n"
> +                                   "-m adds calls to migrate_pages while vCPUs are running."
> +                                   " Default is no migrations.\n"
> +                                   "-d <delay microseconds> - delay between migrate_pages() calls."
> +                                   " Default is %d microseconds.\n",
> +                                   DEFAULT_RUN_SECS, DEFAULT_DELAY_USECS);
> +               }
> +       }
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       int r;
> +       int wait_secs;
> +       const int max_halter_wait = 10;
> +       int run_secs = 0;
> +       int delay_usecs = 0;
> +       struct test_data_page *data;
> +       vm_vaddr_t test_data_page_vaddr;
> +       bool migrate = false;
> +       pthread_t threads[2];
> +       struct thread_params params[2];
> +       struct kvm_vm *vm;
> +       uint64_t *pipis_rcvd;
> +
> +       get_cmdline_args(argc, argv, &run_secs, &migrate, &delay_usecs);
> +       if (run_secs <= 0)
> +               run_secs = DEFAULT_RUN_SECS;
> +       if (delay_usecs <= 0)
> +               delay_usecs = DEFAULT_DELAY_USECS;
> +
> +       vm = vm_create_default(HALTER_VCPU_ID, 0, halter_guest_code);
> +       params[0].vm = vm;
> +       params[1].vm = vm;
> +
> +       vm_init_descriptor_tables(vm);
> +       vcpu_init_descriptor_tables(vm, HALTER_VCPU_ID);
> +       vm_handle_exception(vm, IPI_VECTOR, guest_ipi_handler);
> +
> +       virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA, 0);
> +
> +       vm_vcpu_add_default(vm, SENDER_VCPU_ID, sender_guest_code);
> +
> +       test_data_page_vaddr = vm_vaddr_alloc(vm, 0x1000, 0x1000, 0, 0);
> +       data =
> +          (struct test_data_page *)addr_gva2hva(vm, test_data_page_vaddr);
> +       memset(data, 0, sizeof(*data));
> +       params[0].data = data;
> +       params[1].data = data;
> +
> +       vcpu_args_set(vm, HALTER_VCPU_ID, 1, test_data_page_vaddr);
> +       vcpu_args_set(vm, SENDER_VCPU_ID, 1, test_data_page_vaddr);
> +
> +       pipis_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ipis_rcvd);
> +       params[0].pipis_rcvd = pipis_rcvd;
> +       params[1].pipis_rcvd = pipis_rcvd;
> +
> +       /* Start halter vCPU thread and wait for it to execute first HLT. */
> +       params[0].vcpu_id = HALTER_VCPU_ID;
> +       r = pthread_create(&threads[0], NULL, vcpu_thread, &params[0]);
> +       TEST_ASSERT(r == 0,
> +                   "pthread_create halter failed errno=%d", errno);
> +       fprintf(stderr, "Halter vCPU thread started\n");
> +
> +       wait_secs = 0;
> +       while ((wait_secs < max_halter_wait) && !data->hlt_count) {
> +               sleep(1);
> +               wait_secs++;
> +       }
> +
> +       TEST_ASSERT(data->hlt_count,
> +                   "Halter vCPU did not execute first HLT within %d seconds",
> +                   max_halter_wait);
> +
> +       fprintf(stderr,
> +               "Halter vCPU thread reported its APIC ID: %u after %d seconds.\n",
> +               data->halter_apic_id, wait_secs);
> +
> +       params[1].vcpu_id = SENDER_VCPU_ID;
> +       r = pthread_create(&threads[1], NULL, vcpu_thread, &params[1]);
> +       TEST_ASSERT(r == 0, "pthread_create sender failed errno=%d", errno);
> +
> +       fprintf(stderr,
> +               "IPI sender vCPU thread started. Letting vCPUs run for %d seconds.\n",
> +               run_secs);
> +
> +       if (!migrate)
> +               sleep(run_secs);
> +       else
> +               do_migrations(data, run_secs, delay_usecs, pipis_rcvd);
> +
> +       /*
> +        * Cancel threads and wait for them to stop.
> +        */
> +       cancel_join_vcpu_thread(threads[0], HALTER_VCPU_ID);
> +       cancel_join_vcpu_thread(threads[1], SENDER_VCPU_ID);
> +
> +       fprintf(stderr,
> +               "Test successful after running for %d seconds.\n"
> +               "Sending vCPU sent %lu IPIs to halting vCPU\n"
> +               "Halting vCPU halted %lu times, woke %lu times, received %lu IPIs.\n"
> +               "Halter APIC ID=%#x\n"
> +               "Sender ICR value=%#x ICR2 value=%#x\n"
> +               "Halter TPR=%#x PPR=%#x LVR=%#x\n"
> +               "Migrations attempted: %lu\n"
> +               "Migrations completed: %lu\n",
> +               run_secs, data->ipis_sent,
> +               data->hlt_count, data->wake_count, *pipis_rcvd,
> +               data->halter_apic_id,
> +               data->icr, data->icr2,
> +               data->halter_tpr, data->halter_ppr, data->halter_lvr,
> +               data->migrations_attempted, data->migrations_completed);
> +
> +       kvm_vm_free(vm);
> +
> +       return 0;
> +}
> --
>
>

Ping on this patch. Thx



[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