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