1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2021, Huawei, Inc.
7 * Make sure that THP has been enabled or enough HUGETLB pages with specific
8 * page size have been pre-allocated on your system, if you are planning to
9 * use hugepages to back the guest memory for testing.
15 #include <semaphore.h>
17 #include "test_util.h"
19 #include "processor.h"
20 #include "guest_modes.h"
21 #include "ucall_common.h"
23 #define TEST_MEM_SLOT_INDEX 1
25 /* Default size(1GB) of the memory for testing */
26 #define DEFAULT_TEST_MEM_SIZE (1 << 30)
28 /* Default guest test virtual memory offset */
29 #define DEFAULT_GUEST_TEST_MEM 0xc0000000
31 /* Different guest memory accessing stages */
40 static const char * const test_stage_string
[] = {
41 "KVM_BEFORE_MAPPINGS",
42 "KVM_CREATE_MAPPINGS",
43 "KVM_UPDATE_MAPPINGS",
44 "KVM_ADJUST_MAPPINGS",
49 uint64_t guest_test_virt_mem
;
50 uint64_t host_page_size
;
51 uint64_t host_num_pages
;
52 uint64_t large_page_size
;
53 uint64_t large_num_pages
;
54 uint64_t host_pages_per_lpage
;
55 enum vm_mem_backing_src_type src_type
;
56 struct kvm_vcpu
*vcpus
[KVM_MAX_VCPUS
];
60 * Guest variables. Use addr_gva2hva() if these variables need
61 * to be changed in host.
63 static enum test_stage guest_test_stage
;
66 static uint32_t nr_vcpus
= 1;
67 static struct test_args test_args
;
68 static enum test_stage
*current_stage
;
69 static bool host_quit
;
71 /* Whether the test stage is updated, or completed */
72 static sem_t test_stage_updated
;
73 static sem_t test_stage_completed
;
76 * Guest physical memory offset of the testing memory slot.
77 * This will be set to the topmost valid physical address minus
78 * the test memory size.
80 static uint64_t guest_test_phys_mem
;
83 * Guest virtual memory offset of the testing memory slot.
84 * Must not conflict with identity mapped test code.
86 static uint64_t guest_test_virt_mem
= DEFAULT_GUEST_TEST_MEM
;
88 static void guest_code(bool do_write
)
90 struct test_args
*p
= &test_args
;
91 enum test_stage
*current_stage
= &guest_test_stage
;
96 addr
= p
->guest_test_virt_mem
;
98 switch (READ_ONCE(*current_stage
)) {
100 * All vCPU threads will be started in this stage,
101 * where guest code of each vCPU will do nothing.
103 case KVM_BEFORE_MAPPINGS
:
107 * Before dirty logging, vCPUs concurrently access the first
108 * 8 bytes of each page (host page/large page) within the same
109 * memory region with different accessing types (read/write).
110 * Then KVM will create normal page mappings or huge block
113 case KVM_CREATE_MAPPINGS
:
114 for (i
= 0; i
< p
->large_num_pages
; i
++) {
116 *(uint64_t *)addr
= 0x0123456789ABCDEF;
118 READ_ONCE(*(uint64_t *)addr
);
120 addr
+= p
->large_page_size
;
125 * During dirty logging, KVM will only update attributes of the
126 * normal page mappings from RO to RW if memory backing src type
127 * is anonymous. In other cases, KVM will split the huge block
128 * mappings into normal page mappings if memory backing src type
131 case KVM_UPDATE_MAPPINGS
:
132 if (p
->src_type
== VM_MEM_SRC_ANONYMOUS
) {
133 for (i
= 0; i
< p
->host_num_pages
; i
++) {
134 *(uint64_t *)addr
= 0x0123456789ABCDEF;
135 addr
+= p
->host_page_size
;
140 for (i
= 0; i
< p
->large_num_pages
; i
++) {
142 * Write to the first host page in each large
143 * page region, and triger break of large pages.
145 *(uint64_t *)addr
= 0x0123456789ABCDEF;
148 * Access the middle host pages in each large
149 * page region. Since dirty logging is enabled,
150 * this will create new mappings at the smallest
153 addr
+= p
->large_page_size
/ 2;
154 for (j
= 0; j
< p
->host_pages_per_lpage
/ 2; j
++) {
155 READ_ONCE(*(uint64_t *)addr
);
156 addr
+= p
->host_page_size
;
162 * After dirty logging is stopped, vCPUs concurrently read
163 * from every single host page. Then KVM will coalesce the
164 * split page mappings back to block mappings. And a TLB
165 * conflict abort could occur here if TLB entries of the
166 * page mappings are not fully invalidated.
168 case KVM_ADJUST_MAPPINGS
:
169 for (i
= 0; i
< p
->host_num_pages
; i
++) {
170 READ_ONCE(*(uint64_t *)addr
);
171 addr
+= p
->host_page_size
;
183 static void *vcpu_worker(void *data
)
185 struct kvm_vcpu
*vcpu
= data
;
186 bool do_write
= !(vcpu
->id
% 2);
187 struct timespec start
;
188 struct timespec ts_diff
;
189 enum test_stage stage
;
192 vcpu_args_set(vcpu
, 1, do_write
);
194 while (!READ_ONCE(host_quit
)) {
195 ret
= sem_wait(&test_stage_updated
);
196 TEST_ASSERT(ret
== 0, "Error in sem_wait");
198 if (READ_ONCE(host_quit
))
201 clock_gettime(CLOCK_MONOTONIC
, &start
);
202 ret
= _vcpu_run(vcpu
);
203 ts_diff
= timespec_elapsed(start
);
205 TEST_ASSERT(ret
== 0, "vcpu_run failed: %d", ret
);
206 TEST_ASSERT(get_ucall(vcpu
, NULL
) == UCALL_SYNC
,
207 "Invalid guest sync status: exit_reason=%s",
208 exit_reason_str(vcpu
->run
->exit_reason
));
210 pr_debug("Got sync event from vCPU %d\n", vcpu
->id
);
211 stage
= READ_ONCE(*current_stage
);
214 * Here we can know the execution time of every
215 * single vcpu running in different test stages.
217 pr_debug("vCPU %d has completed stage %s\n"
218 "execution time is: %ld.%.9lds\n\n",
219 vcpu
->id
, test_stage_string
[stage
],
220 ts_diff
.tv_sec
, ts_diff
.tv_nsec
);
222 ret
= sem_post(&test_stage_completed
);
223 TEST_ASSERT(ret
== 0, "Error in sem_post");
230 uint64_t phys_offset
;
231 uint64_t test_mem_size
;
232 enum vm_mem_backing_src_type src_type
;
235 static struct kvm_vm
*pre_init_before_test(enum vm_guest_mode mode
, void *arg
)
238 struct test_params
*p
= arg
;
239 enum vm_mem_backing_src_type src_type
= p
->src_type
;
240 uint64_t large_page_size
= get_backing_src_pagesz(src_type
);
241 uint64_t guest_page_size
= vm_guest_mode_params
[mode
].page_size
;
242 uint64_t host_page_size
= getpagesize();
243 uint64_t test_mem_size
= p
->test_mem_size
;
244 uint64_t guest_num_pages
;
249 /* Align up the test memory size */
250 alignment
= max(large_page_size
, guest_page_size
);
251 test_mem_size
= (test_mem_size
+ alignment
- 1) & ~(alignment
- 1);
253 /* Create a VM with enough guest pages */
254 guest_num_pages
= test_mem_size
/ guest_page_size
;
255 vm
= __vm_create_with_vcpus(VM_SHAPE(mode
), nr_vcpus
, guest_num_pages
,
256 guest_code
, test_args
.vcpus
);
258 /* Align down GPA of the testing memslot */
260 guest_test_phys_mem
= (vm
->max_gfn
- guest_num_pages
) *
263 guest_test_phys_mem
= p
->phys_offset
;
265 alignment
= max(0x100000UL
, alignment
);
267 guest_test_phys_mem
= align_down(guest_test_phys_mem
, alignment
);
269 /* Set up the shared data structure test_args */
271 test_args
.guest_test_virt_mem
= guest_test_virt_mem
;
272 test_args
.host_page_size
= host_page_size
;
273 test_args
.host_num_pages
= test_mem_size
/ host_page_size
;
274 test_args
.large_page_size
= large_page_size
;
275 test_args
.large_num_pages
= test_mem_size
/ large_page_size
;
276 test_args
.host_pages_per_lpage
= large_page_size
/ host_page_size
;
277 test_args
.src_type
= src_type
;
279 /* Add an extra memory slot with specified backing src type */
280 vm_userspace_mem_region_add(vm
, src_type
, guest_test_phys_mem
,
281 TEST_MEM_SLOT_INDEX
, guest_num_pages
, 0);
283 /* Do mapping(GVA->GPA) for the testing memory slot */
284 virt_map(vm
, guest_test_virt_mem
, guest_test_phys_mem
, guest_num_pages
);
286 /* Cache the HVA pointer of the region */
287 host_test_mem
= addr_gpa2hva(vm
, (vm_paddr_t
)guest_test_phys_mem
);
289 /* Export shared structure test_args to guest */
290 sync_global_to_guest(vm
, test_args
);
292 ret
= sem_init(&test_stage_updated
, 0, 0);
293 TEST_ASSERT(ret
== 0, "Error in sem_init");
295 ret
= sem_init(&test_stage_completed
, 0, 0);
296 TEST_ASSERT(ret
== 0, "Error in sem_init");
298 current_stage
= addr_gva2hva(vm
, (vm_vaddr_t
)(&guest_test_stage
));
299 *current_stage
= NUM_TEST_STAGES
;
301 pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode
));
302 pr_info("Testing memory backing src type: %s\n",
303 vm_mem_backing_src_alias(src_type
)->name
);
304 pr_info("Testing memory backing src granularity: 0x%lx\n",
306 pr_info("Testing memory size(aligned): 0x%lx\n", test_mem_size
);
307 pr_info("Guest physical test memory offset: 0x%lx\n",
308 guest_test_phys_mem
);
309 pr_info("Host virtual test memory offset: 0x%lx\n",
310 (uint64_t)host_test_mem
);
311 pr_info("Number of testing vCPUs: %d\n", nr_vcpus
);
316 static void vcpus_complete_new_stage(enum test_stage stage
)
321 /* Wake up all the vcpus to run new test stage */
322 for (vcpus
= 0; vcpus
< nr_vcpus
; vcpus
++) {
323 ret
= sem_post(&test_stage_updated
);
324 TEST_ASSERT(ret
== 0, "Error in sem_post");
326 pr_debug("All vcpus have been notified to continue\n");
328 /* Wait for all the vcpus to complete new test stage */
329 for (vcpus
= 0; vcpus
< nr_vcpus
; vcpus
++) {
330 ret
= sem_wait(&test_stage_completed
);
331 TEST_ASSERT(ret
== 0, "Error in sem_wait");
333 pr_debug("%d vcpus have completed stage %s\n",
334 vcpus
+ 1, test_stage_string
[stage
]);
337 pr_debug("All vcpus have completed stage %s\n",
338 test_stage_string
[stage
]);
341 static void run_test(enum vm_guest_mode mode
, void *arg
)
343 pthread_t
*vcpu_threads
;
345 struct timespec start
;
346 struct timespec ts_diff
;
349 /* Create VM with vCPUs and make some pre-initialization */
350 vm
= pre_init_before_test(mode
, arg
);
352 vcpu_threads
= malloc(nr_vcpus
* sizeof(*vcpu_threads
));
353 TEST_ASSERT(vcpu_threads
, "Memory allocation failed");
356 *current_stage
= KVM_BEFORE_MAPPINGS
;
358 for (i
= 0; i
< nr_vcpus
; i
++)
359 pthread_create(&vcpu_threads
[i
], NULL
, vcpu_worker
,
362 vcpus_complete_new_stage(*current_stage
);
363 pr_info("Started all vCPUs successfully\n");
365 /* Test the stage of KVM creating mappings */
366 *current_stage
= KVM_CREATE_MAPPINGS
;
368 clock_gettime(CLOCK_MONOTONIC
, &start
);
369 vcpus_complete_new_stage(*current_stage
);
370 ts_diff
= timespec_elapsed(start
);
372 pr_info("KVM_CREATE_MAPPINGS: total execution time: %ld.%.9lds\n\n",
373 ts_diff
.tv_sec
, ts_diff
.tv_nsec
);
375 /* Test the stage of KVM updating mappings */
376 vm_mem_region_set_flags(vm
, TEST_MEM_SLOT_INDEX
,
377 KVM_MEM_LOG_DIRTY_PAGES
);
379 *current_stage
= KVM_UPDATE_MAPPINGS
;
381 clock_gettime(CLOCK_MONOTONIC
, &start
);
382 vcpus_complete_new_stage(*current_stage
);
383 ts_diff
= timespec_elapsed(start
);
385 pr_info("KVM_UPDATE_MAPPINGS: total execution time: %ld.%.9lds\n\n",
386 ts_diff
.tv_sec
, ts_diff
.tv_nsec
);
388 /* Test the stage of KVM adjusting mappings */
389 vm_mem_region_set_flags(vm
, TEST_MEM_SLOT_INDEX
, 0);
391 *current_stage
= KVM_ADJUST_MAPPINGS
;
393 clock_gettime(CLOCK_MONOTONIC
, &start
);
394 vcpus_complete_new_stage(*current_stage
);
395 ts_diff
= timespec_elapsed(start
);
397 pr_info("KVM_ADJUST_MAPPINGS: total execution time: %ld.%.9lds\n\n",
398 ts_diff
.tv_sec
, ts_diff
.tv_nsec
);
400 /* Tell the vcpu thread to quit */
402 for (i
= 0; i
< nr_vcpus
; i
++) {
403 ret
= sem_post(&test_stage_updated
);
404 TEST_ASSERT(ret
== 0, "Error in sem_post");
407 for (i
= 0; i
< nr_vcpus
; i
++)
408 pthread_join(vcpu_threads
[i
], NULL
);
410 ret
= sem_destroy(&test_stage_updated
);
411 TEST_ASSERT(ret
== 0, "Error in sem_destroy");
413 ret
= sem_destroy(&test_stage_completed
);
414 TEST_ASSERT(ret
== 0, "Error in sem_destroy");
420 static void help(char *name
)
423 printf("usage: %s [-h] [-p offset] [-m mode] "
424 "[-b mem-size] [-v vcpus] [-s mem-type]\n", name
);
426 printf(" -p: specify guest physical test memory offset\n"
427 " Warning: a low offset can conflict with the loaded test code.\n");
429 printf(" -b: specify size of the memory region for testing. e.g. 10M or 3G.\n"
431 printf(" -v: specify the number of vCPUs to run\n"
433 backing_src_help("-s");
437 int main(int argc
, char *argv
[])
439 int max_vcpus
= kvm_check_cap(KVM_CAP_MAX_VCPUS
);
440 struct test_params p
= {
441 .test_mem_size
= DEFAULT_TEST_MEM_SIZE
,
442 .src_type
= DEFAULT_VM_MEM_SRC
,
446 guest_modes_append_default();
448 while ((opt
= getopt(argc
, argv
, "hp:m:b:v:s:")) != -1) {
451 p
.phys_offset
= strtoull(optarg
, NULL
, 0);
454 guest_modes_cmdline(optarg
);
457 p
.test_mem_size
= parse_size(optarg
);
460 nr_vcpus
= atoi_positive("Number of vCPUs", optarg
);
461 TEST_ASSERT(nr_vcpus
<= max_vcpus
,
462 "Invalid number of vcpus, must be between 1 and %d", max_vcpus
);
465 p
.src_type
= parse_backing_src_type(optarg
);
474 for_each_guest_mode(run_test
, &p
);