linux/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * xapic_ipi_test
   4 *
   5 * Copyright (C) 2020, Google LLC.
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2.
   8 *
   9 * Test that when the APIC is in xAPIC mode, a vCPU can send an IPI to wake
  10 * another vCPU that is halted when KVM's backing page for the APIC access
  11 * address has been moved by mm.
  12 *
  13 * The test starts two vCPUs: one that sends IPIs and one that continually
  14 * executes HLT. The sender checks that the halter has woken from the HLT and
  15 * has reentered HLT before sending the next IPI. While the vCPUs are running,
  16 * the host continually calls migrate_pages to move all of the process' pages
  17 * amongst the available numa nodes on the machine.
  18 *
  19 * Migration is a command line option. When used on non-numa machines will 
  20 * exit with error. Test is still usefull on non-numa for testing IPIs.
  21 */
  22
  23#define _GNU_SOURCE /* for program_invocation_short_name */
  24#include <getopt.h>
  25#include <pthread.h>
  26#include <inttypes.h>
  27#include <string.h>
  28#include <time.h>
  29
  30#include "kvm_util.h"
  31#include "numaif.h"
  32#include "processor.h"
  33#include "test_util.h"
  34#include "vmx.h"
  35
  36/* Default running time for the test */
  37#define DEFAULT_RUN_SECS 3
  38
  39/* Default delay between migrate_pages calls (microseconds) */
  40#define DEFAULT_DELAY_USECS 500000
  41
  42#define HALTER_VCPU_ID 0
  43#define SENDER_VCPU_ID 1
  44
  45/*
  46 * Vector for IPI from sender vCPU to halting vCPU.
  47 * Value is arbitrary and was chosen for the alternating bit pattern. Any
  48 * value should work.
  49 */
  50#define IPI_VECTOR       0xa5
  51
  52/*
  53 * Incremented in the IPI handler. Provides evidence to the sender that the IPI
  54 * arrived at the destination
  55 */
  56static volatile uint64_t ipis_rcvd;
  57
  58/* Data struct shared between host main thread and vCPUs */
  59struct test_data_page {
  60        uint32_t halter_apic_id;
  61        volatile uint64_t hlt_count;
  62        volatile uint64_t wake_count;
  63        uint64_t ipis_sent;
  64        uint64_t migrations_attempted;
  65        uint64_t migrations_completed;
  66        uint32_t icr;
  67        uint32_t icr2;
  68        uint32_t halter_tpr;
  69        uint32_t halter_ppr;
  70
  71        /*
  72         *  Record local version register as a cross-check that APIC access
  73         *  worked. Value should match what KVM reports (APIC_VERSION in
  74         *  arch/x86/kvm/lapic.c). If test is failing, check that values match
  75         *  to determine whether APIC access exits are working.
  76         */
  77        uint32_t halter_lvr;
  78};
  79
  80struct thread_params {
  81        struct test_data_page *data;
  82        struct kvm_vm *vm;
  83        uint32_t vcpu_id;
  84        uint64_t *pipis_rcvd; /* host address of ipis_rcvd global */
  85};
  86
  87void verify_apic_base_addr(void)
  88{
  89        uint64_t msr = rdmsr(MSR_IA32_APICBASE);
  90        uint64_t base = GET_APIC_BASE(msr);
  91
  92        GUEST_ASSERT(base == APIC_DEFAULT_GPA);
  93}
  94
  95static void halter_guest_code(struct test_data_page *data)
  96{
  97        verify_apic_base_addr();
  98        xapic_enable();
  99
 100        data->halter_apic_id = GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID));
 101        data->halter_lvr = xapic_read_reg(APIC_LVR);
 102
 103        /*
 104         * Loop forever HLTing and recording halts & wakes. Disable interrupts
 105         * each time around to minimize window between signaling the pending
 106         * halt to the sender vCPU and executing the halt. No need to disable on
 107         * first run as this vCPU executes first and the host waits for it to
 108         * signal going into first halt before starting the sender vCPU. Record
 109         * TPR and PPR for diagnostic purposes in case the test fails.
 110         */
 111        for (;;) {
 112                data->halter_tpr = xapic_read_reg(APIC_TASKPRI);
 113                data->halter_ppr = xapic_read_reg(APIC_PROCPRI);
 114                data->hlt_count++;
 115                asm volatile("sti; hlt; cli");
 116                data->wake_count++;
 117        }
 118}
 119
 120/*
 121 * Runs on halter vCPU when IPI arrives. Write an arbitrary non-zero value to
 122 * enable diagnosing errant writes to the APIC access address backing page in
 123 * case of test failure.
 124 */
 125static void guest_ipi_handler(struct ex_regs *regs)
 126{
 127        ipis_rcvd++;
 128        xapic_write_reg(APIC_EOI, 77);
 129}
 130
 131static void sender_guest_code(struct test_data_page *data)
 132{
 133        uint64_t last_wake_count;
 134        uint64_t last_hlt_count;
 135        uint64_t last_ipis_rcvd_count;
 136        uint32_t icr_val;
 137        uint32_t icr2_val;
 138        uint64_t tsc_start;
 139
 140        verify_apic_base_addr();
 141        xapic_enable();
 142
 143        /*
 144         * Init interrupt command register for sending IPIs
 145         *
 146         * Delivery mode=fixed, per SDM:
 147         *   "Delivers the interrupt specified in the vector field to the target
 148         *    processor."
 149         *
 150         * Destination mode=physical i.e. specify target by its local APIC
 151         * ID. This vCPU assumes that the halter vCPU has already started and
 152         * set data->halter_apic_id.
 153         */
 154        icr_val = (APIC_DEST_PHYSICAL | APIC_DM_FIXED | IPI_VECTOR);
 155        icr2_val = SET_APIC_DEST_FIELD(data->halter_apic_id);
 156        data->icr = icr_val;
 157        data->icr2 = icr2_val;
 158
 159        last_wake_count = data->wake_count;
 160        last_hlt_count = data->hlt_count;
 161        last_ipis_rcvd_count = ipis_rcvd;
 162        for (;;) {
 163                /*
 164                 * Send IPI to halter vCPU.
 165                 * First IPI can be sent unconditionally because halter vCPU
 166                 * starts earlier.
 167                 */
 168                xapic_write_reg(APIC_ICR2, icr2_val);
 169                xapic_write_reg(APIC_ICR, icr_val);
 170                data->ipis_sent++;
 171
 172                /*
 173                 * Wait up to ~1 sec for halter to indicate that it has:
 174                 * 1. Received the IPI
 175                 * 2. Woken up from the halt
 176                 * 3. Gone back into halt
 177                 * Current CPUs typically run at 2.x Ghz which is ~2
 178                 * billion ticks per second.
 179                 */
 180                tsc_start = rdtsc();
 181                while (rdtsc() - tsc_start < 2000000000) {
 182                        if ((ipis_rcvd != last_ipis_rcvd_count) &&
 183                            (data->wake_count != last_wake_count) &&
 184                            (data->hlt_count != last_hlt_count))
 185                                break;
 186                }
 187
 188                GUEST_ASSERT((ipis_rcvd != last_ipis_rcvd_count) &&
 189                             (data->wake_count != last_wake_count) &&
 190                             (data->hlt_count != last_hlt_count));
 191
 192                last_wake_count = data->wake_count;
 193                last_hlt_count = data->hlt_count;
 194                last_ipis_rcvd_count = ipis_rcvd;
 195        }
 196}
 197
 198static void *vcpu_thread(void *arg)
 199{
 200        struct thread_params *params = (struct thread_params *)arg;
 201        struct ucall uc;
 202        int old;
 203        int r;
 204        unsigned int exit_reason;
 205
 206        r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
 207        TEST_ASSERT(r == 0,
 208                    "pthread_setcanceltype failed on vcpu_id=%u with errno=%d",
 209                    params->vcpu_id, r);
 210
 211        fprintf(stderr, "vCPU thread running vCPU %u\n", params->vcpu_id);
 212        vcpu_run(params->vm, params->vcpu_id);
 213        exit_reason = vcpu_state(params->vm, params->vcpu_id)->exit_reason;
 214
 215        TEST_ASSERT(exit_reason == KVM_EXIT_IO,
 216                    "vCPU %u exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO",
 217                    params->vcpu_id, exit_reason, exit_reason_str(exit_reason));
 218
 219        if (get_ucall(params->vm, params->vcpu_id, &uc) == UCALL_ABORT) {
 220                TEST_ASSERT(false,
 221                            "vCPU %u exited with error: %s.\n"
 222                            "Sending vCPU sent %lu IPIs to halting vCPU\n"
 223                            "Halting vCPU halted %lu times, woke %lu times, received %lu IPIs.\n"
 224                            "Halter TPR=%#x PPR=%#x LVR=%#x\n"
 225                            "Migrations attempted: %lu\n"
 226                            "Migrations completed: %lu\n",
 227                            params->vcpu_id, (const char *)uc.args[0],
 228                            params->data->ipis_sent, params->data->hlt_count,
 229                            params->data->wake_count,
 230                            *params->pipis_rcvd, params->data->halter_tpr,
 231                            params->data->halter_ppr, params->data->halter_lvr,
 232                            params->data->migrations_attempted,
 233                            params->data->migrations_completed);
 234        }
 235
 236        return NULL;
 237}
 238
 239static void cancel_join_vcpu_thread(pthread_t thread, uint32_t vcpu_id)
 240{
 241        void *retval;
 242        int r;
 243
 244        r = pthread_cancel(thread);
 245        TEST_ASSERT(r == 0,
 246                    "pthread_cancel on vcpu_id=%d failed with errno=%d",
 247                    vcpu_id, r);
 248
 249        r = pthread_join(thread, &retval);
 250        TEST_ASSERT(r == 0,
 251                    "pthread_join on vcpu_id=%d failed with errno=%d",
 252                    vcpu_id, r);
 253        TEST_ASSERT(retval == PTHREAD_CANCELED,
 254                    "expected retval=%p, got %p", PTHREAD_CANCELED,
 255                    retval);
 256}
 257
 258void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs,
 259                   uint64_t *pipis_rcvd)
 260{
 261        long pages_not_moved;
 262        unsigned long nodemask = 0;
 263        unsigned long nodemasks[sizeof(nodemask) * 8];
 264        int nodes = 0;
 265        time_t start_time, last_update, now;
 266        time_t interval_secs = 1;
 267        int i, r;
 268        int from, to;
 269        unsigned long bit;
 270        uint64_t hlt_count;
 271        uint64_t wake_count;
 272        uint64_t ipis_sent;
 273
 274        fprintf(stderr, "Calling migrate_pages every %d microseconds\n",
 275                delay_usecs);
 276
 277        /* Get set of first 64 numa nodes available */
 278        r = get_mempolicy(NULL, &nodemask, sizeof(nodemask) * 8,
 279                          0, MPOL_F_MEMS_ALLOWED);
 280        TEST_ASSERT(r == 0, "get_mempolicy failed errno=%d", errno);
 281
 282        fprintf(stderr, "Numa nodes found amongst first %lu possible nodes "
 283                "(each 1-bit indicates node is present): %#lx\n",
 284                sizeof(nodemask) * 8, nodemask);
 285
 286        /* Init array of masks containing a single-bit in each, one for each
 287         * available node. migrate_pages called below requires specifying nodes
 288         * as bit masks.
 289         */
 290        for (i = 0, bit = 1; i < sizeof(nodemask) * 8; i++, bit <<= 1) {
 291                if (nodemask & bit) {
 292                        nodemasks[nodes] = nodemask & bit;
 293                        nodes++;
 294                }
 295        }
 296
 297        TEST_ASSERT(nodes > 1,
 298                    "Did not find at least 2 numa nodes. Can't do migration\n");
 299
 300        fprintf(stderr, "Migrating amongst %d nodes found\n", nodes);
 301
 302        from = 0;
 303        to = 1;
 304        start_time = time(NULL);
 305        last_update = start_time;
 306
 307        ipis_sent = data->ipis_sent;
 308        hlt_count = data->hlt_count;
 309        wake_count = data->wake_count;
 310
 311        while ((int)(time(NULL) - start_time) < run_secs) {
 312                data->migrations_attempted++;
 313
 314                /*
 315                 * migrate_pages with PID=0 will migrate all pages of this
 316                 * process between the nodes specified as bitmasks. The page
 317                 * backing the APIC access address belongs to this process
 318                 * because it is allocated by KVM in the context of the
 319                 * KVM_CREATE_VCPU ioctl. If that assumption ever changes this
 320                 * test may break or give a false positive signal.
 321                 */
 322                pages_not_moved = migrate_pages(0, sizeof(nodemasks[from]),
 323                                                &nodemasks[from],
 324                                                &nodemasks[to]);
 325                if (pages_not_moved < 0)
 326                        fprintf(stderr,
 327                                "migrate_pages failed, errno=%d\n", errno);
 328                else if (pages_not_moved > 0)
 329                        fprintf(stderr,
 330                                "migrate_pages could not move %ld pages\n",
 331                                pages_not_moved);
 332                else
 333                        data->migrations_completed++;
 334
 335                from = to;
 336                to++;
 337                if (to == nodes)
 338                        to = 0;
 339
 340                now = time(NULL);
 341                if (((now - start_time) % interval_secs == 0) &&
 342                    (now != last_update)) {
 343                        last_update = now;
 344                        fprintf(stderr,
 345                                "%lu seconds: Migrations attempted=%lu completed=%lu, "
 346                                "IPIs sent=%lu received=%lu, HLTs=%lu wakes=%lu\n",
 347                                now - start_time, data->migrations_attempted,
 348                                data->migrations_completed,
 349                                data->ipis_sent, *pipis_rcvd,
 350                                data->hlt_count, data->wake_count);
 351
 352                        TEST_ASSERT(ipis_sent != data->ipis_sent &&
 353                                    hlt_count != data->hlt_count &&
 354                                    wake_count != data->wake_count,
 355                                    "IPI, HLT and wake count have not increased "
 356                                    "in the last %lu seconds. "
 357                                    "HLTer is likely hung.\n", interval_secs);
 358
 359                        ipis_sent = data->ipis_sent;
 360                        hlt_count = data->hlt_count;
 361                        wake_count = data->wake_count;
 362                }
 363                usleep(delay_usecs);
 364        }
 365}
 366
 367void get_cmdline_args(int argc, char *argv[], int *run_secs,
 368                      bool *migrate, int *delay_usecs)
 369{
 370        for (;;) {
 371                int opt = getopt(argc, argv, "s:d:m");
 372
 373                if (opt == -1)
 374                        break;
 375                switch (opt) {
 376                case 's':
 377                        *run_secs = parse_size(optarg);
 378                        break;
 379                case 'm':
 380                        *migrate = true;
 381                        break;
 382                case 'd':
 383                        *delay_usecs = parse_size(optarg);
 384                        break;
 385                default:
 386                        TEST_ASSERT(false,
 387                                    "Usage: -s <runtime seconds>. Default is %d seconds.\n"
 388                                    "-m adds calls to migrate_pages while vCPUs are running."
 389                                    " Default is no migrations.\n"
 390                                    "-d <delay microseconds> - delay between migrate_pages() calls."
 391                                    " Default is %d microseconds.\n",
 392                                    DEFAULT_RUN_SECS, DEFAULT_DELAY_USECS);
 393                }
 394        }
 395}
 396
 397int main(int argc, char *argv[])
 398{
 399        int r;
 400        int wait_secs;
 401        const int max_halter_wait = 10;
 402        int run_secs = 0;
 403        int delay_usecs = 0;
 404        struct test_data_page *data;
 405        vm_vaddr_t test_data_page_vaddr;
 406        bool migrate = false;
 407        pthread_t threads[2];
 408        struct thread_params params[2];
 409        struct kvm_vm *vm;
 410        uint64_t *pipis_rcvd;
 411
 412        get_cmdline_args(argc, argv, &run_secs, &migrate, &delay_usecs);
 413        if (run_secs <= 0)
 414                run_secs = DEFAULT_RUN_SECS;
 415        if (delay_usecs <= 0)
 416                delay_usecs = DEFAULT_DELAY_USECS;
 417
 418        vm = vm_create_default(HALTER_VCPU_ID, 0, halter_guest_code);
 419        params[0].vm = vm;
 420        params[1].vm = vm;
 421
 422        vm_init_descriptor_tables(vm);
 423        vcpu_init_descriptor_tables(vm, HALTER_VCPU_ID);
 424        vm_install_exception_handler(vm, IPI_VECTOR, guest_ipi_handler);
 425
 426        virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
 427
 428        vm_vcpu_add_default(vm, SENDER_VCPU_ID, sender_guest_code);
 429
 430        test_data_page_vaddr = vm_vaddr_alloc_page(vm);
 431        data =
 432           (struct test_data_page *)addr_gva2hva(vm, test_data_page_vaddr);
 433        memset(data, 0, sizeof(*data));
 434        params[0].data = data;
 435        params[1].data = data;
 436
 437        vcpu_args_set(vm, HALTER_VCPU_ID, 1, test_data_page_vaddr);
 438        vcpu_args_set(vm, SENDER_VCPU_ID, 1, test_data_page_vaddr);
 439
 440        pipis_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ipis_rcvd);
 441        params[0].pipis_rcvd = pipis_rcvd;
 442        params[1].pipis_rcvd = pipis_rcvd;
 443
 444        /* Start halter vCPU thread and wait for it to execute first HLT. */
 445        params[0].vcpu_id = HALTER_VCPU_ID;
 446        r = pthread_create(&threads[0], NULL, vcpu_thread, &params[0]);
 447        TEST_ASSERT(r == 0,
 448                    "pthread_create halter failed errno=%d", errno);
 449        fprintf(stderr, "Halter vCPU thread started\n");
 450
 451        wait_secs = 0;
 452        while ((wait_secs < max_halter_wait) && !data->hlt_count) {
 453                sleep(1);
 454                wait_secs++;
 455        }
 456
 457        TEST_ASSERT(data->hlt_count,
 458                    "Halter vCPU did not execute first HLT within %d seconds",
 459                    max_halter_wait);
 460
 461        fprintf(stderr,
 462                "Halter vCPU thread reported its APIC ID: %u after %d seconds.\n",
 463                data->halter_apic_id, wait_secs);
 464
 465        params[1].vcpu_id = SENDER_VCPU_ID;
 466        r = pthread_create(&threads[1], NULL, vcpu_thread, &params[1]);
 467        TEST_ASSERT(r == 0, "pthread_create sender failed errno=%d", errno);
 468
 469        fprintf(stderr,
 470                "IPI sender vCPU thread started. Letting vCPUs run for %d seconds.\n",
 471                run_secs);
 472
 473        if (!migrate)
 474                sleep(run_secs);
 475        else
 476                do_migrations(data, run_secs, delay_usecs, pipis_rcvd);
 477
 478        /*
 479         * Cancel threads and wait for them to stop.
 480         */
 481        cancel_join_vcpu_thread(threads[0], HALTER_VCPU_ID);
 482        cancel_join_vcpu_thread(threads[1], SENDER_VCPU_ID);
 483
 484        fprintf(stderr,
 485                "Test successful after running for %d seconds.\n"
 486                "Sending vCPU sent %lu IPIs to halting vCPU\n"
 487                "Halting vCPU halted %lu times, woke %lu times, received %lu IPIs.\n"
 488                "Halter APIC ID=%#x\n"
 489                "Sender ICR value=%#x ICR2 value=%#x\n"
 490                "Halter TPR=%#x PPR=%#x LVR=%#x\n"
 491                "Migrations attempted: %lu\n"
 492                "Migrations completed: %lu\n",
 493                run_secs, data->ipis_sent,
 494                data->hlt_count, data->wake_count, *pipis_rcvd,
 495                data->halter_apic_id,
 496                data->icr, data->icr2,
 497                data->halter_tpr, data->halter_ppr, data->halter_lvr,
 498                data->migrations_attempted, data->migrations_completed);
 499
 500        kvm_vm_free(vm);
 501
 502        return 0;
 503}
 504