Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: selftests: Stop conflating vCPU index and ID in perf tests

Track vCPUs by their 'struct kvm_vcpu' object, and stop assuming that a
vCPU's ID is the same as its index when referencing a vCPU's metadata.

Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Sean Christopherson and committed by
Paolo Bonzini
df84cef5 376851f8

+138 -131
+41 -40
tools/testing/selftests/kvm/access_tracking_perf_test.c
··· 74 74 uint64_t vcpu_memory_bytes; 75 75 76 76 /* The number of vCPUs to create in the VM. */ 77 - int vcpus; 77 + int nr_vcpus; 78 78 }; 79 79 80 80 static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) ··· 127 127 "Set page_idle bits for PFN 0x%" PRIx64, pfn); 128 128 } 129 129 130 - static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) 130 + static void mark_vcpu_memory_idle(struct kvm_vm *vm, 131 + struct perf_test_vcpu_args *vcpu_args) 131 132 { 132 - uint64_t base_gva = perf_test_args.vcpu_args[vcpu_id].gva; 133 - uint64_t pages = perf_test_args.vcpu_args[vcpu_id].pages; 133 + int vcpu_idx = vcpu_args->vcpu_idx; 134 + uint64_t base_gva = vcpu_args->gva; 135 + uint64_t pages = vcpu_args->pages; 134 136 uint64_t page; 135 137 uint64_t still_idle = 0; 136 138 uint64_t no_pfn = 0; ··· 140 138 int pagemap_fd; 141 139 142 140 /* If vCPUs are using an overlapping region, let vCPU 0 mark it idle. */ 143 - if (overlap_memory_access && vcpu_id) 141 + if (overlap_memory_access && vcpu_idx) 144 142 return; 145 143 146 144 page_idle_fd = open("/sys/kernel/mm/page_idle/bitmap", O_RDWR); ··· 172 170 */ 173 171 TEST_ASSERT(no_pfn < pages / 100, 174 172 "vCPU %d: No PFN for %" PRIu64 " out of %" PRIu64 " pages.", 175 - vcpu_id, no_pfn, pages); 173 + vcpu_idx, no_pfn, pages); 176 174 177 175 /* 178 176 * Test that at least 90% of memory has been marked idle (the rest might ··· 185 183 TEST_ASSERT(still_idle < pages / 10, 186 184 "vCPU%d: Too many pages still idle (%"PRIu64 " out of %" 187 185 PRIu64 ").\n", 188 - vcpu_id, still_idle, pages); 186 + vcpu_idx, still_idle, pages); 189 187 190 188 close(page_idle_fd); 191 189 close(pagemap_fd); 192 190 } 193 191 194 - static void assert_ucall(struct kvm_vm *vm, uint32_t vcpu_id, 195 - uint64_t expected_ucall) 192 + static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_ucall) 196 193 { 197 194 struct ucall uc; 198 - uint64_t actual_ucall = get_ucall(vm, vcpu_id, &uc); 195 + uint64_t actual_ucall = get_ucall(vcpu->vm, vcpu->id, &uc); 199 196 200 197 TEST_ASSERT(expected_ucall == actual_ucall, 201 198 "Guest exited unexpectedly (expected ucall %" PRIu64 ··· 218 217 219 218 static void vcpu_thread_main(struct perf_test_vcpu_args *vcpu_args) 220 219 { 220 + struct kvm_vcpu *vcpu = vcpu_args->vcpu; 221 221 struct kvm_vm *vm = perf_test_args.vm; 222 - int vcpu_id = vcpu_args->vcpu_id; 222 + int vcpu_idx = vcpu_args->vcpu_idx; 223 223 int current_iteration = 0; 224 224 225 225 while (spin_wait_for_next_iteration(&current_iteration)) { 226 226 switch (READ_ONCE(iteration_work)) { 227 227 case ITERATION_ACCESS_MEMORY: 228 - vcpu_run(vm, vcpu_id); 229 - assert_ucall(vm, vcpu_id, UCALL_SYNC); 228 + vcpu_run(vm, vcpu->id); 229 + assert_ucall(vcpu, UCALL_SYNC); 230 230 break; 231 231 case ITERATION_MARK_IDLE: 232 - mark_vcpu_memory_idle(vm, vcpu_id); 232 + mark_vcpu_memory_idle(vm, vcpu_args); 233 233 break; 234 234 }; 235 235 236 - vcpu_last_completed_iteration[vcpu_id] = current_iteration; 236 + vcpu_last_completed_iteration[vcpu_idx] = current_iteration; 237 237 } 238 238 } 239 239 240 - static void spin_wait_for_vcpu(int vcpu_id, int target_iteration) 240 + static void spin_wait_for_vcpu(int vcpu_idx, int target_iteration) 241 241 { 242 - while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != 242 + while (READ_ONCE(vcpu_last_completed_iteration[vcpu_idx]) != 243 243 target_iteration) { 244 244 continue; 245 245 } ··· 252 250 ACCESS_WRITE, 253 251 }; 254 252 255 - static void run_iteration(struct kvm_vm *vm, int vcpus, const char *description) 253 + static void run_iteration(struct kvm_vm *vm, int nr_vcpus, const char *description) 256 254 { 257 255 struct timespec ts_start; 258 256 struct timespec ts_elapsed; 259 - int next_iteration; 260 - int vcpu_id; 257 + int next_iteration, i; 261 258 262 259 /* Kick off the vCPUs by incrementing iteration. */ 263 260 next_iteration = ++iteration; ··· 264 263 clock_gettime(CLOCK_MONOTONIC, &ts_start); 265 264 266 265 /* Wait for all vCPUs to finish the iteration. */ 267 - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) 268 - spin_wait_for_vcpu(vcpu_id, next_iteration); 266 + for (i = 0; i < nr_vcpus; i++) 267 + spin_wait_for_vcpu(i, next_iteration); 269 268 270 269 ts_elapsed = timespec_elapsed(ts_start); 271 270 pr_info("%-30s: %ld.%09lds\n", 272 271 description, ts_elapsed.tv_sec, ts_elapsed.tv_nsec); 273 272 } 274 273 275 - static void access_memory(struct kvm_vm *vm, int vcpus, enum access_type access, 276 - const char *description) 274 + static void access_memory(struct kvm_vm *vm, int nr_vcpus, 275 + enum access_type access, const char *description) 277 276 { 278 277 perf_test_set_wr_fract(vm, (access == ACCESS_READ) ? INT_MAX : 1); 279 278 iteration_work = ITERATION_ACCESS_MEMORY; 280 - run_iteration(vm, vcpus, description); 279 + run_iteration(vm, nr_vcpus, description); 281 280 } 282 281 283 - static void mark_memory_idle(struct kvm_vm *vm, int vcpus) 282 + static void mark_memory_idle(struct kvm_vm *vm, int nr_vcpus) 284 283 { 285 284 /* 286 285 * Even though this parallelizes the work across vCPUs, this is still a ··· 290 289 */ 291 290 pr_debug("Marking VM memory idle (slow)...\n"); 292 291 iteration_work = ITERATION_MARK_IDLE; 293 - run_iteration(vm, vcpus, "Mark memory idle"); 292 + run_iteration(vm, nr_vcpus, "Mark memory idle"); 294 293 } 295 294 296 295 static void run_test(enum vm_guest_mode mode, void *arg) 297 296 { 298 297 struct test_params *params = arg; 299 298 struct kvm_vm *vm; 300 - int vcpus = params->vcpus; 299 + int nr_vcpus = params->nr_vcpus; 301 300 302 - vm = perf_test_create_vm(mode, vcpus, params->vcpu_memory_bytes, 1, 301 + vm = perf_test_create_vm(mode, nr_vcpus, params->vcpu_memory_bytes, 1, 303 302 params->backing_src, !overlap_memory_access); 304 303 305 - perf_test_start_vcpu_threads(vcpus, vcpu_thread_main); 304 + perf_test_start_vcpu_threads(nr_vcpus, vcpu_thread_main); 306 305 307 306 pr_info("\n"); 308 - access_memory(vm, vcpus, ACCESS_WRITE, "Populating memory"); 307 + access_memory(vm, nr_vcpus, ACCESS_WRITE, "Populating memory"); 309 308 310 309 /* As a control, read and write to the populated memory first. */ 311 - access_memory(vm, vcpus, ACCESS_WRITE, "Writing to populated memory"); 312 - access_memory(vm, vcpus, ACCESS_READ, "Reading from populated memory"); 310 + access_memory(vm, nr_vcpus, ACCESS_WRITE, "Writing to populated memory"); 311 + access_memory(vm, nr_vcpus, ACCESS_READ, "Reading from populated memory"); 313 312 314 313 /* Repeat on memory that has been marked as idle. */ 315 - mark_memory_idle(vm, vcpus); 316 - access_memory(vm, vcpus, ACCESS_WRITE, "Writing to idle memory"); 317 - mark_memory_idle(vm, vcpus); 318 - access_memory(vm, vcpus, ACCESS_READ, "Reading from idle memory"); 314 + mark_memory_idle(vm, nr_vcpus); 315 + access_memory(vm, nr_vcpus, ACCESS_WRITE, "Writing to idle memory"); 316 + mark_memory_idle(vm, nr_vcpus); 317 + access_memory(vm, nr_vcpus, ACCESS_READ, "Reading from idle memory"); 319 318 320 319 /* Set done to signal the vCPU threads to exit */ 321 320 done = true; 322 321 323 - perf_test_join_vcpu_threads(vcpus); 322 + perf_test_join_vcpu_threads(nr_vcpus); 324 323 perf_test_destroy_vm(vm); 325 324 } 326 325 ··· 348 347 struct test_params params = { 349 348 .backing_src = DEFAULT_VM_MEM_SRC, 350 349 .vcpu_memory_bytes = DEFAULT_PER_VCPU_MEM_SIZE, 351 - .vcpus = 1, 350 + .nr_vcpus = 1, 352 351 }; 353 352 int page_idle_fd; 354 353 int opt; ··· 364 363 params.vcpu_memory_bytes = parse_size(optarg); 365 364 break; 366 365 case 'v': 367 - params.vcpus = atoi(optarg); 366 + params.nr_vcpus = atoi(optarg); 368 367 break; 369 368 case 'o': 370 369 overlap_memory_access = true;
+17 -19
tools/testing/selftests/kvm/demand_paging_test.c
··· 44 44 45 45 static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) 46 46 { 47 - int ret; 48 - int vcpu_id = vcpu_args->vcpu_id; 47 + struct kvm_vcpu *vcpu = vcpu_args->vcpu; 49 48 struct kvm_vm *vm = perf_test_args.vm; 50 - struct kvm_run *run; 49 + int vcpu_idx = vcpu_args->vcpu_idx; 50 + struct kvm_run *run = vcpu->run; 51 51 struct timespec start; 52 52 struct timespec ts_diff; 53 - 54 - run = vcpu_state(vm, vcpu_id); 53 + int ret; 55 54 56 55 clock_gettime(CLOCK_MONOTONIC, &start); 57 56 58 57 /* Let the guest access its memory */ 59 - ret = _vcpu_run(vm, vcpu_id); 58 + ret = _vcpu_run(vm, vcpu->id); 60 59 TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); 61 - if (get_ucall(vm, vcpu_id, NULL) != UCALL_SYNC) { 60 + if (get_ucall(vm, vcpu->id, NULL) != UCALL_SYNC) { 62 61 TEST_ASSERT(false, 63 62 "Invalid guest sync status: exit_reason=%s\n", 64 63 exit_reason_str(run->exit_reason)); 65 64 } 66 65 67 66 ts_diff = timespec_elapsed(start); 68 - PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_id, 67 + PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_idx, 69 68 ts_diff.tv_sec, ts_diff.tv_nsec); 70 69 } 71 70 ··· 284 285 struct timespec ts_diff; 285 286 int *pipefds = NULL; 286 287 struct kvm_vm *vm; 287 - int vcpu_id; 288 - int r; 288 + int r, i; 289 289 290 290 vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 1, 291 291 p->src_type, p->partition_vcpu_memory_access); ··· 307 309 pipefds = malloc(sizeof(int) * nr_vcpus * 2); 308 310 TEST_ASSERT(pipefds, "Unable to allocate memory for pipefd"); 309 311 310 - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 312 + for (i = 0; i < nr_vcpus; i++) { 311 313 struct perf_test_vcpu_args *vcpu_args; 312 314 void *vcpu_hva; 313 315 void *vcpu_alias; 314 316 315 - vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; 317 + vcpu_args = &perf_test_args.vcpu_args[i]; 316 318 317 319 /* Cache the host addresses of the region */ 318 320 vcpu_hva = addr_gpa2hva(vm, vcpu_args->gpa); ··· 322 324 * Set up user fault fd to handle demand paging 323 325 * requests. 324 326 */ 325 - r = pipe2(&pipefds[vcpu_id * 2], 327 + r = pipe2(&pipefds[i * 2], 326 328 O_CLOEXEC | O_NONBLOCK); 327 329 TEST_ASSERT(!r, "Failed to set up pipefd"); 328 330 329 - setup_demand_paging(vm, &uffd_handler_threads[vcpu_id], 330 - pipefds[vcpu_id * 2], p->uffd_mode, 331 - p->uffd_delay, &uffd_args[vcpu_id], 331 + setup_demand_paging(vm, &uffd_handler_threads[i], 332 + pipefds[i * 2], p->uffd_mode, 333 + p->uffd_delay, &uffd_args[i], 332 334 vcpu_hva, vcpu_alias, 333 335 vcpu_args->pages * perf_test_args.guest_page_size); 334 336 } ··· 348 350 char c; 349 351 350 352 /* Tell the user fault fd handler threads to quit */ 351 - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 352 - r = write(pipefds[vcpu_id * 2 + 1], &c, 1); 353 + for (i = 0; i < nr_vcpus; i++) { 354 + r = write(pipefds[i * 2 + 1], &c, 1); 353 355 TEST_ASSERT(r == 1, "Unable to write to pipefd"); 354 356 355 - pthread_join(uffd_handler_threads[vcpu_id], NULL); 357 + pthread_join(uffd_handler_threads[i], NULL); 356 358 } 357 359 } 358 360
+20 -19
tools/testing/selftests/kvm/dirty_log_perf_test.c
··· 68 68 69 69 static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) 70 70 { 71 - int ret; 71 + struct kvm_vcpu *vcpu = vcpu_args->vcpu; 72 72 struct kvm_vm *vm = perf_test_args.vm; 73 + int vcpu_idx = vcpu_args->vcpu_idx; 73 74 uint64_t pages_count = 0; 74 75 struct kvm_run *run; 75 76 struct timespec start; 76 77 struct timespec ts_diff; 77 78 struct timespec total = (struct timespec){0}; 78 79 struct timespec avg; 79 - int vcpu_id = vcpu_args->vcpu_id; 80 + int ret; 80 81 81 - run = vcpu_state(vm, vcpu_id); 82 + run = vcpu->run; 82 83 83 84 while (!READ_ONCE(host_quit)) { 84 85 int current_iteration = READ_ONCE(iteration); 85 86 86 87 clock_gettime(CLOCK_MONOTONIC, &start); 87 - ret = _vcpu_run(vm, vcpu_id); 88 + ret = _vcpu_run(vm, vcpu->id); 88 89 ts_diff = timespec_elapsed(start); 89 90 90 91 TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); 91 - TEST_ASSERT(get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC, 92 + TEST_ASSERT(get_ucall(vm, vcpu->id, NULL) == UCALL_SYNC, 92 93 "Invalid guest sync status: exit_reason=%s\n", 93 94 exit_reason_str(run->exit_reason)); 94 95 95 - pr_debug("Got sync event from vCPU %d\n", vcpu_id); 96 - vcpu_last_completed_iteration[vcpu_id] = current_iteration; 96 + pr_debug("Got sync event from vCPU %d\n", vcpu_idx); 97 + vcpu_last_completed_iteration[vcpu_idx] = current_iteration; 97 98 pr_debug("vCPU %d updated last completed iteration to %d\n", 98 - vcpu_id, vcpu_last_completed_iteration[vcpu_id]); 99 + vcpu->id, vcpu_last_completed_iteration[vcpu_idx]); 99 100 100 101 if (current_iteration) { 101 102 pages_count += vcpu_args->pages; 102 103 total = timespec_add(total, ts_diff); 103 104 pr_debug("vCPU %d iteration %d dirty memory time: %ld.%.9lds\n", 104 - vcpu_id, current_iteration, ts_diff.tv_sec, 105 + vcpu_idx, current_iteration, ts_diff.tv_sec, 105 106 ts_diff.tv_nsec); 106 107 } else { 107 108 pr_debug("vCPU %d iteration %d populate memory time: %ld.%.9lds\n", 108 - vcpu_id, current_iteration, ts_diff.tv_sec, 109 + vcpu_idx, current_iteration, ts_diff.tv_sec, 109 110 ts_diff.tv_nsec); 110 111 } 111 112 ··· 114 113 !READ_ONCE(host_quit)) {} 115 114 } 116 115 117 - avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_id]); 116 + avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_idx]); 118 117 pr_debug("\nvCPU %d dirtied 0x%lx pages over %d iterations in %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n", 119 - vcpu_id, pages_count, vcpu_last_completed_iteration[vcpu_id], 118 + vcpu_idx, pages_count, vcpu_last_completed_iteration[vcpu_idx], 120 119 total.tv_sec, total.tv_nsec, avg.tv_sec, avg.tv_nsec); 121 120 } 122 121 ··· 208 207 uint64_t guest_num_pages; 209 208 uint64_t host_num_pages; 210 209 uint64_t pages_per_slot; 211 - int vcpu_id; 212 210 struct timespec start; 213 211 struct timespec ts_diff; 214 212 struct timespec get_dirty_log_total = (struct timespec){0}; 215 213 struct timespec vcpu_dirty_total = (struct timespec){0}; 216 214 struct timespec avg; 217 215 struct timespec clear_dirty_log_total = (struct timespec){0}; 216 + int i; 218 217 219 218 vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 220 219 p->slots, p->backing_src, ··· 240 239 host_quit = false; 241 240 242 241 clock_gettime(CLOCK_MONOTONIC, &start); 243 - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) 244 - vcpu_last_completed_iteration[vcpu_id] = -1; 242 + for (i = 0; i < nr_vcpus; i++) 243 + vcpu_last_completed_iteration[i] = -1; 245 244 246 245 perf_test_start_vcpu_threads(nr_vcpus, vcpu_worker); 247 246 248 247 /* Allow the vCPUs to populate memory */ 249 248 pr_debug("Starting iteration %d - Populating\n", iteration); 250 - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 251 - while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != 249 + for (i = 0; i < nr_vcpus; i++) { 250 + while (READ_ONCE(vcpu_last_completed_iteration[i]) != 252 251 iteration) 253 252 ; 254 253 } ··· 273 272 iteration++; 274 273 275 274 pr_debug("Starting iteration %d\n", iteration); 276 - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { 277 - while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) 275 + for (i = 0; i < nr_vcpus; i++) { 276 + while (READ_ONCE(vcpu_last_completed_iteration[i]) 278 277 != iteration) 279 278 ; 280 279 }
+4 -3
tools/testing/selftests/kvm/include/perf_test_util.h
··· 25 25 uint64_t pages; 26 26 27 27 /* Only used by the host userspace part of the vCPU thread */ 28 - int vcpu_id; 28 + struct kvm_vcpu *vcpu; 29 + int vcpu_idx; 29 30 }; 30 31 31 32 struct perf_test_args { ··· 45 44 46 45 extern struct perf_test_args perf_test_args; 47 46 48 - struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, 47 + struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus, 49 48 uint64_t vcpu_memory_bytes, int slots, 50 49 enum vm_mem_backing_src_type backing_src, 51 50 bool partition_vcpu_memory_access); ··· 58 57 void perf_test_guest_code(uint32_t vcpu_id); 59 58 60 59 uint64_t perf_test_nested_pages(int nr_vcpus); 61 - void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus); 60 + void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]); 62 61 63 62 #endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
+47 -41
tools/testing/selftests/kvm/lib/perf_test_util.c
··· 17 17 static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; 18 18 19 19 struct vcpu_thread { 20 - /* The id of the vCPU. */ 21 - int vcpu_id; 20 + /* The index of the vCPU. */ 21 + int vcpu_idx; 22 22 23 23 /* The pthread backing the vCPU. */ 24 24 pthread_t thread; ··· 36 36 /* Set to true once all vCPU threads are up and running. */ 37 37 static bool all_vcpu_threads_running; 38 38 39 + static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; 40 + 39 41 /* 40 42 * Continuously write to the first 8 bytes of each page in the 41 43 * specified region. 42 44 */ 43 - void perf_test_guest_code(uint32_t vcpu_id) 45 + void perf_test_guest_code(uint32_t vcpu_idx) 44 46 { 45 47 struct perf_test_args *pta = &perf_test_args; 46 - struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_id]; 48 + struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_idx]; 47 49 uint64_t gva; 48 50 uint64_t pages; 49 51 int i; 50 52 51 - /* Make sure vCPU args data structure is not corrupt. */ 52 - GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id); 53 - 54 53 gva = vcpu_args->gva; 55 54 pages = vcpu_args->pages; 55 + 56 + /* Make sure vCPU args data structure is not corrupt. */ 57 + GUEST_ASSERT(vcpu_args->vcpu_idx == vcpu_idx); 56 58 57 59 while (true) { 58 60 for (i = 0; i < pages; i++) { ··· 70 68 } 71 69 } 72 70 73 - void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, 71 + void perf_test_setup_vcpus(struct kvm_vm *vm, int nr_vcpus, 72 + struct kvm_vcpu *vcpus[], 74 73 uint64_t vcpu_memory_bytes, 75 74 bool partition_vcpu_memory_access) 76 75 { 77 76 struct perf_test_args *pta = &perf_test_args; 78 77 struct perf_test_vcpu_args *vcpu_args; 79 - int vcpu_id; 78 + int i; 80 79 81 - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { 82 - vcpu_args = &pta->vcpu_args[vcpu_id]; 80 + for (i = 0; i < nr_vcpus; i++) { 81 + vcpu_args = &pta->vcpu_args[i]; 83 82 84 - vcpu_args->vcpu_id = vcpu_id; 83 + vcpu_args->vcpu = vcpus[i]; 84 + vcpu_args->vcpu_idx = i; 85 + 85 86 if (partition_vcpu_memory_access) { 86 87 vcpu_args->gva = guest_test_virt_mem + 87 - (vcpu_id * vcpu_memory_bytes); 88 + (i * vcpu_memory_bytes); 88 89 vcpu_args->pages = vcpu_memory_bytes / 89 90 pta->guest_page_size; 90 - vcpu_args->gpa = pta->gpa + (vcpu_id * vcpu_memory_bytes); 91 + vcpu_args->gpa = pta->gpa + (i * vcpu_memory_bytes); 91 92 } else { 92 93 vcpu_args->gva = guest_test_virt_mem; 93 - vcpu_args->pages = (vcpus * vcpu_memory_bytes) / 94 + vcpu_args->pages = (nr_vcpus * vcpu_memory_bytes) / 94 95 pta->guest_page_size; 95 96 vcpu_args->gpa = pta->gpa; 96 97 } 97 98 98 - vcpu_args_set(vm, vcpu_id, 1, vcpu_id); 99 + vcpu_args_set(vm, vcpus[i]->id, 1, i); 99 100 100 101 pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n", 101 - vcpu_id, vcpu_args->gpa, vcpu_args->gpa + 102 + i, vcpu_args->gpa, vcpu_args->gpa + 102 103 (vcpu_args->pages * pta->guest_page_size)); 103 104 } 104 105 } 105 106 106 - struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, 107 + struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus, 107 108 uint64_t vcpu_memory_bytes, int slots, 108 109 enum vm_mem_backing_src_type backing_src, 109 110 bool partition_vcpu_memory_access) ··· 130 125 pta->guest_page_size = vm_guest_mode_params[mode].page_size; 131 126 132 127 guest_num_pages = vm_adjust_num_guest_pages(mode, 133 - (vcpus * vcpu_memory_bytes) / pta->guest_page_size); 128 + (nr_vcpus * vcpu_memory_bytes) / pta->guest_page_size); 134 129 135 130 TEST_ASSERT(vcpu_memory_bytes % getpagesize() == 0, 136 131 "Guest memory size is not host page size aligned."); ··· 145 140 * in-memory data structures. 146 141 */ 147 142 if (pta->nested) 148 - slot0_pages += perf_test_nested_pages(vcpus); 143 + slot0_pages += perf_test_nested_pages(nr_vcpus); 149 144 150 145 /* 151 146 * Pass guest_num_pages to populate the page tables for test memory. 152 147 * The memory is also added to memslot 0, but that's a benign side 153 148 * effect as KVM allows aliasing HVAs in meslots. 154 149 */ 155 - vm = __vm_create_with_vcpus(mode, vcpus, DEFAULT_GUEST_PHY_PAGES, 150 + vm = __vm_create_with_vcpus(mode, nr_vcpus, DEFAULT_GUEST_PHY_PAGES, 156 151 slot0_pages + guest_num_pages, 0, 157 - perf_test_guest_code, NULL); 152 + perf_test_guest_code, vcpus); 158 153 159 154 pta->vm = vm; 160 155 ··· 176 171 TEST_ASSERT(guest_num_pages < region_end_gfn, 177 172 "Requested more guest memory than address space allows.\n" 178 173 " guest pages: %" PRIx64 " max gfn: %" PRIx64 179 - " vcpus: %d wss: %" PRIx64 "]\n", 180 - guest_num_pages, region_end_gfn - 1, vcpus, 181 - vcpu_memory_bytes); 174 + " nr_vcpus: %d wss: %" PRIx64 "]\n", 175 + guest_num_pages, region_end_gfn - 1, nr_vcpus, vcpu_memory_bytes); 182 176 183 - pta->gpa = (region_end_gfn - guest_num_pages) * pta->guest_page_size; 177 + pta->gpa = (region_end_gfn - guest_num_pages - 1) * pta->guest_page_size; 184 178 pta->gpa = align_down(pta->gpa, backing_src_pagesz); 185 179 #ifdef __s390x__ 186 180 /* Align to 1M (segment size) */ ··· 202 198 /* Do mapping for the demand paging memory slot */ 203 199 virt_map(vm, guest_test_virt_mem, pta->gpa, guest_num_pages); 204 200 205 - perf_test_setup_vcpus(vm, vcpus, vcpu_memory_bytes, partition_vcpu_memory_access); 201 + perf_test_setup_vcpus(vm, nr_vcpus, vcpus, vcpu_memory_bytes, 202 + partition_vcpu_memory_access); 206 203 207 204 if (pta->nested) { 208 205 pr_info("Configuring vCPUs to run in L2 (nested).\n"); 209 - perf_test_setup_nested(vm, vcpus); 206 + perf_test_setup_nested(vm, nr_vcpus, vcpus); 210 207 } 211 208 212 209 ucall_init(vm, NULL); ··· 235 230 return 0; 236 231 } 237 232 238 - void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) 233 + void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu **vcpus) 239 234 { 240 235 pr_info("%s() not support on this architecture, skipping.\n", __func__); 241 236 exit(KSFT_SKIP); ··· 256 251 while (!READ_ONCE(all_vcpu_threads_running)) 257 252 ; 258 253 259 - vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_id]); 254 + vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_idx]); 260 255 261 256 return NULL; 262 257 } 263 258 264 - void perf_test_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct perf_test_vcpu_args *)) 259 + void perf_test_start_vcpu_threads(int nr_vcpus, 260 + void (*vcpu_fn)(struct perf_test_vcpu_args *)) 265 261 { 266 - int vcpu_id; 262 + int i; 267 263 268 264 vcpu_thread_fn = vcpu_fn; 269 265 WRITE_ONCE(all_vcpu_threads_running, false); 270 266 271 - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { 272 - struct vcpu_thread *vcpu = &vcpu_threads[vcpu_id]; 267 + for (i = 0; i < nr_vcpus; i++) { 268 + struct vcpu_thread *vcpu = &vcpu_threads[i]; 273 269 274 - vcpu->vcpu_id = vcpu_id; 270 + vcpu->vcpu_idx = i; 275 271 WRITE_ONCE(vcpu->running, false); 276 272 277 273 pthread_create(&vcpu->thread, NULL, vcpu_thread_main, vcpu); 278 274 } 279 275 280 - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { 281 - while (!READ_ONCE(vcpu_threads[vcpu_id].running)) 276 + for (i = 0; i < nr_vcpus; i++) { 277 + while (!READ_ONCE(vcpu_threads[i].running)) 282 278 ; 283 279 } 284 280 285 281 WRITE_ONCE(all_vcpu_threads_running, true); 286 282 } 287 283 288 - void perf_test_join_vcpu_threads(int vcpus) 284 + void perf_test_join_vcpu_threads(int nr_vcpus) 289 285 { 290 - int vcpu_id; 286 + int i; 291 287 292 - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) 293 - pthread_join(vcpu_threads[vcpu_id].thread, NULL); 288 + for (i = 0; i < nr_vcpus; i++) 289 + pthread_join(vcpu_threads[i].thread, NULL); 294 290 }
+4 -4
tools/testing/selftests/kvm/lib/x86_64/perf_test_util.c
··· 77 77 nested_identity_map_1g(vmx, vm, start, end - start); 78 78 } 79 79 80 - void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) 80 + void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]) 81 81 { 82 82 struct vmx_pages *vmx, *vmx0 = NULL; 83 83 struct kvm_regs regs; ··· 103 103 * Override the vCPU to run perf_test_l1_guest_code() which will 104 104 * bounce it into L2 before calling perf_test_guest_code(). 105 105 */ 106 - vcpu_regs_get(vm, vcpu_id, &regs); 106 + vcpu_regs_get(vm, vcpus[vcpu_id]->id, &regs); 107 107 regs.rip = (unsigned long) perf_test_l1_guest_code; 108 - vcpu_regs_set(vm, vcpu_id, &regs); 109 - vcpu_args_set(vm, vcpu_id, 2, vmx_gva, vcpu_id); 108 + vcpu_regs_set(vm, vcpus[vcpu_id]->id, &regs); 109 + vcpu_args_set(vm, vcpus[vcpu_id]->id, 2, vmx_gva, vcpu_id); 110 110 } 111 111 }
+5 -5
tools/testing/selftests/kvm/memslot_modification_stress_test.c
··· 38 38 39 39 static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) 40 40 { 41 - int ret; 42 - int vcpu_id = vcpu_args->vcpu_id; 41 + struct kvm_vcpu *vcpu = vcpu_args->vcpu; 43 42 struct kvm_vm *vm = perf_test_args.vm; 44 43 struct kvm_run *run; 44 + int ret; 45 45 46 - run = vcpu_state(vm, vcpu_id); 46 + run = vcpu->run; 47 47 48 48 /* Let the guest access its memory until a stop signal is received */ 49 49 while (READ_ONCE(run_vcpus)) { 50 - ret = _vcpu_run(vm, vcpu_id); 50 + ret = _vcpu_run(vm, vcpu->id); 51 51 TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); 52 52 53 - if (get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC) 53 + if (get_ucall(vm, vcpu->id, NULL) == UCALL_SYNC) 54 54 continue; 55 55 56 56 TEST_ASSERT(false,