1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2020, Google LLC.
7 * This work is licensed under the terms of the GNU GPL, version 2.
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.
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.
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.
30 #include "processor.h"
31 #include "test_util.h"
34 /* Default running time for the test */
35 #define DEFAULT_RUN_SECS 3
37 /* Default delay between migrate_pages calls (microseconds) */
38 #define DEFAULT_DELAY_USECS 500000
41 * Vector for IPI from sender vCPU to halting vCPU.
42 * Value is arbitrary and was chosen for the alternating bit pattern. Any
45 #define IPI_VECTOR 0xa5
48 * Incremented in the IPI handler. Provides evidence to the sender that the IPI
49 * arrived at the destination
51 static volatile uint64_t ipis_rcvd
;
53 /* Data struct shared between host main thread and vCPUs */
54 struct test_data_page
{
55 uint32_t halter_apic_id
;
56 volatile uint64_t hlt_count
;
57 volatile uint64_t wake_count
;
59 uint64_t migrations_attempted
;
60 uint64_t migrations_completed
;
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.
75 struct thread_params
{
76 struct test_data_page
*data
;
77 struct kvm_vcpu
*vcpu
;
78 uint64_t *pipis_rcvd
; /* host address of ipis_rcvd global */
81 void verify_apic_base_addr(void)
83 uint64_t msr
= rdmsr(MSR_IA32_APICBASE
);
84 uint64_t base
= GET_APIC_BASE(msr
);
86 GUEST_ASSERT(base
== APIC_DEFAULT_GPA
);
89 static void halter_guest_code(struct test_data_page
*data
)
91 verify_apic_base_addr();
94 data
->halter_apic_id
= GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID
));
95 data
->halter_lvr
= xapic_read_reg(APIC_LVR
);
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.
106 data
->halter_tpr
= xapic_read_reg(APIC_TASKPRI
);
107 data
->halter_ppr
= xapic_read_reg(APIC_PROCPRI
);
109 asm volatile("sti; hlt; cli");
115 * Runs on halter vCPU when IPI arrives. Write an arbitrary non-zero value to
116 * enable diagnosing errant writes to the APIC access address backing page in
117 * case of test failure.
119 static void guest_ipi_handler(struct ex_regs
*regs
)
122 xapic_write_reg(APIC_EOI
, 77);
125 static void sender_guest_code(struct test_data_page
*data
)
127 uint64_t last_wake_count
;
128 uint64_t last_hlt_count
;
129 uint64_t last_ipis_rcvd_count
;
134 verify_apic_base_addr();
138 * Init interrupt command register for sending IPIs
140 * Delivery mode=fixed, per SDM:
141 * "Delivers the interrupt specified in the vector field to the target
144 * Destination mode=physical i.e. specify target by its local APIC
145 * ID. This vCPU assumes that the halter vCPU has already started and
146 * set data->halter_apic_id.
148 icr_val
= (APIC_DEST_PHYSICAL
| APIC_DM_FIXED
| IPI_VECTOR
);
149 icr2_val
= SET_APIC_DEST_FIELD(data
->halter_apic_id
);
151 data
->icr2
= icr2_val
;
153 last_wake_count
= data
->wake_count
;
154 last_hlt_count
= data
->hlt_count
;
155 last_ipis_rcvd_count
= ipis_rcvd
;
158 * Send IPI to halter vCPU.
159 * First IPI can be sent unconditionally because halter vCPU
162 xapic_write_reg(APIC_ICR2
, icr2_val
);
163 xapic_write_reg(APIC_ICR
, icr_val
);
167 * Wait up to ~1 sec for halter to indicate that it has:
168 * 1. Received the IPI
169 * 2. Woken up from the halt
170 * 3. Gone back into halt
171 * Current CPUs typically run at 2.x Ghz which is ~2
172 * billion ticks per second.
175 while (rdtsc() - tsc_start
< 2000000000) {
176 if ((ipis_rcvd
!= last_ipis_rcvd_count
) &&
177 (data
->wake_count
!= last_wake_count
) &&
178 (data
->hlt_count
!= last_hlt_count
))
182 GUEST_ASSERT((ipis_rcvd
!= last_ipis_rcvd_count
) &&
183 (data
->wake_count
!= last_wake_count
) &&
184 (data
->hlt_count
!= last_hlt_count
));
186 last_wake_count
= data
->wake_count
;
187 last_hlt_count
= data
->hlt_count
;
188 last_ipis_rcvd_count
= ipis_rcvd
;
192 static void *vcpu_thread(void *arg
)
194 struct thread_params
*params
= (struct thread_params
*)arg
;
195 struct kvm_vcpu
*vcpu
= params
->vcpu
;
200 r
= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, &old
);
202 "pthread_setcanceltype failed on vcpu_id=%u with errno=%d",
205 fprintf(stderr
, "vCPU thread running vCPU %u\n", vcpu
->id
);
208 TEST_ASSERT_KVM_EXIT_REASON(vcpu
, KVM_EXIT_IO
);
210 if (get_ucall(vcpu
, &uc
) == UCALL_ABORT
) {
212 "vCPU %u exited with error: %s.\n"
213 "Sending vCPU sent %lu IPIs to halting vCPU\n"
214 "Halting vCPU halted %lu times, woke %lu times, received %lu IPIs.\n"
215 "Halter TPR=%#x PPR=%#x LVR=%#x\n"
216 "Migrations attempted: %lu\n"
217 "Migrations completed: %lu",
218 vcpu
->id
, (const char *)uc
.args
[0],
219 params
->data
->ipis_sent
, params
->data
->hlt_count
,
220 params
->data
->wake_count
,
221 *params
->pipis_rcvd
, params
->data
->halter_tpr
,
222 params
->data
->halter_ppr
, params
->data
->halter_lvr
,
223 params
->data
->migrations_attempted
,
224 params
->data
->migrations_completed
);
230 static void cancel_join_vcpu_thread(pthread_t thread
, struct kvm_vcpu
*vcpu
)
235 r
= pthread_cancel(thread
);
237 "pthread_cancel on vcpu_id=%d failed with errno=%d",
240 r
= pthread_join(thread
, &retval
);
242 "pthread_join on vcpu_id=%d failed with errno=%d",
244 TEST_ASSERT(retval
== PTHREAD_CANCELED
,
245 "expected retval=%p, got %p", PTHREAD_CANCELED
,
249 void do_migrations(struct test_data_page
*data
, int run_secs
, int delay_usecs
,
250 uint64_t *pipis_rcvd
)
252 long pages_not_moved
;
253 unsigned long nodemask
= 0;
254 unsigned long nodemasks
[sizeof(nodemask
) * 8];
256 time_t start_time
, last_update
, now
;
257 time_t interval_secs
= 1;
265 fprintf(stderr
, "Calling migrate_pages every %d microseconds\n",
268 /* Get set of first 64 numa nodes available */
269 r
= get_mempolicy(NULL
, &nodemask
, sizeof(nodemask
) * 8,
270 0, MPOL_F_MEMS_ALLOWED
);
271 TEST_ASSERT(r
== 0, "get_mempolicy failed errno=%d", errno
);
273 fprintf(stderr
, "Numa nodes found amongst first %lu possible nodes "
274 "(each 1-bit indicates node is present): %#lx\n",
275 sizeof(nodemask
) * 8, nodemask
);
277 /* Init array of masks containing a single-bit in each, one for each
278 * available node. migrate_pages called below requires specifying nodes
281 for (i
= 0, bit
= 1; i
< sizeof(nodemask
) * 8; i
++, bit
<<= 1) {
282 if (nodemask
& bit
) {
283 nodemasks
[nodes
] = nodemask
& bit
;
288 TEST_ASSERT(nodes
> 1,
289 "Did not find at least 2 numa nodes. Can't do migration");
291 fprintf(stderr
, "Migrating amongst %d nodes found\n", nodes
);
295 start_time
= time(NULL
);
296 last_update
= start_time
;
298 ipis_sent
= data
->ipis_sent
;
299 hlt_count
= data
->hlt_count
;
300 wake_count
= data
->wake_count
;
302 while ((int)(time(NULL
) - start_time
) < run_secs
) {
303 data
->migrations_attempted
++;
306 * migrate_pages with PID=0 will migrate all pages of this
307 * process between the nodes specified as bitmasks. The page
308 * backing the APIC access address belongs to this process
309 * because it is allocated by KVM in the context of the
310 * KVM_CREATE_VCPU ioctl. If that assumption ever changes this
311 * test may break or give a false positive signal.
313 pages_not_moved
= migrate_pages(0, sizeof(nodemasks
[from
]),
316 if (pages_not_moved
< 0)
318 "migrate_pages failed, errno=%d\n", errno
);
319 else if (pages_not_moved
> 0)
321 "migrate_pages could not move %ld pages\n",
324 data
->migrations_completed
++;
332 if (((now
- start_time
) % interval_secs
== 0) &&
333 (now
!= last_update
)) {
336 "%lu seconds: Migrations attempted=%lu completed=%lu, "
337 "IPIs sent=%lu received=%lu, HLTs=%lu wakes=%lu\n",
338 now
- start_time
, data
->migrations_attempted
,
339 data
->migrations_completed
,
340 data
->ipis_sent
, *pipis_rcvd
,
341 data
->hlt_count
, data
->wake_count
);
343 TEST_ASSERT(ipis_sent
!= data
->ipis_sent
&&
344 hlt_count
!= data
->hlt_count
&&
345 wake_count
!= data
->wake_count
,
346 "IPI, HLT and wake count have not increased "
347 "in the last %lu seconds. "
348 "HLTer is likely hung.", interval_secs
);
350 ipis_sent
= data
->ipis_sent
;
351 hlt_count
= data
->hlt_count
;
352 wake_count
= data
->wake_count
;
358 void get_cmdline_args(int argc
, char *argv
[], int *run_secs
,
359 bool *migrate
, int *delay_usecs
)
362 int opt
= getopt(argc
, argv
, "s:d:m");
368 *run_secs
= parse_size(optarg
);
374 *delay_usecs
= parse_size(optarg
);
378 "Usage: -s <runtime seconds>. Default is %d seconds.\n"
379 "-m adds calls to migrate_pages while vCPUs are running."
380 " Default is no migrations.\n"
381 "-d <delay microseconds> - delay between migrate_pages() calls."
382 " Default is %d microseconds.",
383 DEFAULT_RUN_SECS
, DEFAULT_DELAY_USECS
);
388 int main(int argc
, char *argv
[])
392 const int max_halter_wait
= 10;
395 struct test_data_page
*data
;
396 vm_vaddr_t test_data_page_vaddr
;
397 bool migrate
= false;
398 pthread_t threads
[2];
399 struct thread_params params
[2];
401 uint64_t *pipis_rcvd
;
403 get_cmdline_args(argc
, argv
, &run_secs
, &migrate
, &delay_usecs
);
405 run_secs
= DEFAULT_RUN_SECS
;
406 if (delay_usecs
<= 0)
407 delay_usecs
= DEFAULT_DELAY_USECS
;
409 vm
= vm_create_with_one_vcpu(¶ms
[0].vcpu
, halter_guest_code
);
411 vm_install_exception_handler(vm
, IPI_VECTOR
, guest_ipi_handler
);
413 virt_pg_map(vm
, APIC_DEFAULT_GPA
, APIC_DEFAULT_GPA
);
415 params
[1].vcpu
= vm_vcpu_add(vm
, 1, sender_guest_code
);
417 test_data_page_vaddr
= vm_vaddr_alloc_page(vm
);
418 data
= addr_gva2hva(vm
, test_data_page_vaddr
);
419 memset(data
, 0, sizeof(*data
));
420 params
[0].data
= data
;
421 params
[1].data
= data
;
423 vcpu_args_set(params
[0].vcpu
, 1, test_data_page_vaddr
);
424 vcpu_args_set(params
[1].vcpu
, 1, test_data_page_vaddr
);
426 pipis_rcvd
= (uint64_t *)addr_gva2hva(vm
, (uint64_t)&ipis_rcvd
);
427 params
[0].pipis_rcvd
= pipis_rcvd
;
428 params
[1].pipis_rcvd
= pipis_rcvd
;
430 /* Start halter vCPU thread and wait for it to execute first HLT. */
431 r
= pthread_create(&threads
[0], NULL
, vcpu_thread
, ¶ms
[0]);
433 "pthread_create halter failed errno=%d", errno
);
434 fprintf(stderr
, "Halter vCPU thread started\n");
437 while ((wait_secs
< max_halter_wait
) && !data
->hlt_count
) {
442 TEST_ASSERT(data
->hlt_count
,
443 "Halter vCPU did not execute first HLT within %d seconds",
447 "Halter vCPU thread reported its APIC ID: %u after %d seconds.\n",
448 data
->halter_apic_id
, wait_secs
);
450 r
= pthread_create(&threads
[1], NULL
, vcpu_thread
, ¶ms
[1]);
451 TEST_ASSERT(r
== 0, "pthread_create sender failed errno=%d", errno
);
454 "IPI sender vCPU thread started. Letting vCPUs run for %d seconds.\n",
460 do_migrations(data
, run_secs
, delay_usecs
, pipis_rcvd
);
463 * Cancel threads and wait for them to stop.
465 cancel_join_vcpu_thread(threads
[0], params
[0].vcpu
);
466 cancel_join_vcpu_thread(threads
[1], params
[1].vcpu
);
469 "Test successful after running for %d seconds.\n"
470 "Sending vCPU sent %lu IPIs to halting vCPU\n"
471 "Halting vCPU halted %lu times, woke %lu times, received %lu IPIs.\n"
472 "Halter APIC ID=%#x\n"
473 "Sender ICR value=%#x ICR2 value=%#x\n"
474 "Halter TPR=%#x PPR=%#x LVR=%#x\n"
475 "Migrations attempted: %lu\n"
476 "Migrations completed: %lu\n",
477 run_secs
, data
->ipis_sent
,
478 data
->hlt_count
, data
->wake_count
, *pipis_rcvd
,
479 data
->halter_apic_id
,
480 data
->icr
, data
->icr2
,
481 data
->halter_tpr
, data
->halter_ppr
, data
->halter_lvr
,
482 data
->migrations_attempted
, data
->migrations_completed
);