Commit df84cef5 authored by Sean Christopherson's avatar Sean Christopherson Committed by Paolo Bonzini

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: default avatarSean Christopherson <seanjc@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 376851f8
...@@ -74,7 +74,7 @@ struct test_params { ...@@ -74,7 +74,7 @@ struct test_params {
uint64_t vcpu_memory_bytes; uint64_t vcpu_memory_bytes;
/* The number of vCPUs to create in the VM. */ /* The number of vCPUs to create in the VM. */
int vcpus; int nr_vcpus;
}; };
static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) static uint64_t pread_uint64(int fd, const char *filename, uint64_t index)
...@@ -127,10 +127,12 @@ static void mark_page_idle(int page_idle_fd, uint64_t pfn) ...@@ -127,10 +127,12 @@ static void mark_page_idle(int page_idle_fd, uint64_t pfn)
"Set page_idle bits for PFN 0x%" PRIx64, pfn); "Set page_idle bits for PFN 0x%" PRIx64, pfn);
} }
static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) static void mark_vcpu_memory_idle(struct kvm_vm *vm,
struct perf_test_vcpu_args *vcpu_args)
{ {
uint64_t base_gva = perf_test_args.vcpu_args[vcpu_id].gva; int vcpu_idx = vcpu_args->vcpu_idx;
uint64_t pages = perf_test_args.vcpu_args[vcpu_id].pages; uint64_t base_gva = vcpu_args->gva;
uint64_t pages = vcpu_args->pages;
uint64_t page; uint64_t page;
uint64_t still_idle = 0; uint64_t still_idle = 0;
uint64_t no_pfn = 0; uint64_t no_pfn = 0;
...@@ -138,7 +140,7 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) ...@@ -138,7 +140,7 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id)
int pagemap_fd; int pagemap_fd;
/* If vCPUs are using an overlapping region, let vCPU 0 mark it idle. */ /* If vCPUs are using an overlapping region, let vCPU 0 mark it idle. */
if (overlap_memory_access && vcpu_id) if (overlap_memory_access && vcpu_idx)
return; return;
page_idle_fd = open("/sys/kernel/mm/page_idle/bitmap", O_RDWR); page_idle_fd = open("/sys/kernel/mm/page_idle/bitmap", O_RDWR);
...@@ -170,7 +172,7 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) ...@@ -170,7 +172,7 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id)
*/ */
TEST_ASSERT(no_pfn < pages / 100, TEST_ASSERT(no_pfn < pages / 100,
"vCPU %d: No PFN for %" PRIu64 " out of %" PRIu64 " pages.", "vCPU %d: No PFN for %" PRIu64 " out of %" PRIu64 " pages.",
vcpu_id, no_pfn, pages); vcpu_idx, no_pfn, pages);
/* /*
* Test that at least 90% of memory has been marked idle (the rest might * Test that at least 90% of memory has been marked idle (the rest might
...@@ -183,17 +185,16 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) ...@@ -183,17 +185,16 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id)
TEST_ASSERT(still_idle < pages / 10, TEST_ASSERT(still_idle < pages / 10,
"vCPU%d: Too many pages still idle (%"PRIu64 " out of %" "vCPU%d: Too many pages still idle (%"PRIu64 " out of %"
PRIu64 ").\n", PRIu64 ").\n",
vcpu_id, still_idle, pages); vcpu_idx, still_idle, pages);
close(page_idle_fd); close(page_idle_fd);
close(pagemap_fd); close(pagemap_fd);
} }
static void assert_ucall(struct kvm_vm *vm, uint32_t vcpu_id, static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_ucall)
uint64_t expected_ucall)
{ {
struct ucall uc; struct ucall uc;
uint64_t actual_ucall = get_ucall(vm, vcpu_id, &uc); uint64_t actual_ucall = get_ucall(vcpu->vm, vcpu->id, &uc);
TEST_ASSERT(expected_ucall == actual_ucall, TEST_ASSERT(expected_ucall == actual_ucall,
"Guest exited unexpectedly (expected ucall %" PRIu64 "Guest exited unexpectedly (expected ucall %" PRIu64
...@@ -217,28 +218,29 @@ static bool spin_wait_for_next_iteration(int *current_iteration) ...@@ -217,28 +218,29 @@ static bool spin_wait_for_next_iteration(int *current_iteration)
static void vcpu_thread_main(struct perf_test_vcpu_args *vcpu_args) static void vcpu_thread_main(struct perf_test_vcpu_args *vcpu_args)
{ {
struct kvm_vcpu *vcpu = vcpu_args->vcpu;
struct kvm_vm *vm = perf_test_args.vm; struct kvm_vm *vm = perf_test_args.vm;
int vcpu_id = vcpu_args->vcpu_id; int vcpu_idx = vcpu_args->vcpu_idx;
int current_iteration = 0; int current_iteration = 0;
while (spin_wait_for_next_iteration(&current_iteration)) { while (spin_wait_for_next_iteration(&current_iteration)) {
switch (READ_ONCE(iteration_work)) { switch (READ_ONCE(iteration_work)) {
case ITERATION_ACCESS_MEMORY: case ITERATION_ACCESS_MEMORY:
vcpu_run(vm, vcpu_id); vcpu_run(vm, vcpu->id);
assert_ucall(vm, vcpu_id, UCALL_SYNC); assert_ucall(vcpu, UCALL_SYNC);
break; break;
case ITERATION_MARK_IDLE: case ITERATION_MARK_IDLE:
mark_vcpu_memory_idle(vm, vcpu_id); mark_vcpu_memory_idle(vm, vcpu_args);
break; break;
}; };
vcpu_last_completed_iteration[vcpu_id] = current_iteration; vcpu_last_completed_iteration[vcpu_idx] = current_iteration;
} }
} }
static void spin_wait_for_vcpu(int vcpu_id, int target_iteration) static void spin_wait_for_vcpu(int vcpu_idx, int target_iteration)
{ {
while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != while (READ_ONCE(vcpu_last_completed_iteration[vcpu_idx]) !=
target_iteration) { target_iteration) {
continue; continue;
} }
...@@ -250,12 +252,11 @@ enum access_type { ...@@ -250,12 +252,11 @@ enum access_type {
ACCESS_WRITE, ACCESS_WRITE,
}; };
static void run_iteration(struct kvm_vm *vm, int vcpus, const char *description) static void run_iteration(struct kvm_vm *vm, int nr_vcpus, const char *description)
{ {
struct timespec ts_start; struct timespec ts_start;
struct timespec ts_elapsed; struct timespec ts_elapsed;
int next_iteration; int next_iteration, i;
int vcpu_id;
/* Kick off the vCPUs by incrementing iteration. */ /* Kick off the vCPUs by incrementing iteration. */
next_iteration = ++iteration; next_iteration = ++iteration;
...@@ -263,23 +264,23 @@ static void run_iteration(struct kvm_vm *vm, int vcpus, const char *description) ...@@ -263,23 +264,23 @@ static void run_iteration(struct kvm_vm *vm, int vcpus, const char *description)
clock_gettime(CLOCK_MONOTONIC, &ts_start); clock_gettime(CLOCK_MONOTONIC, &ts_start);
/* Wait for all vCPUs to finish the iteration. */ /* Wait for all vCPUs to finish the iteration. */
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) for (i = 0; i < nr_vcpus; i++)
spin_wait_for_vcpu(vcpu_id, next_iteration); spin_wait_for_vcpu(i, next_iteration);
ts_elapsed = timespec_elapsed(ts_start); ts_elapsed = timespec_elapsed(ts_start);
pr_info("%-30s: %ld.%09lds\n", pr_info("%-30s: %ld.%09lds\n",
description, ts_elapsed.tv_sec, ts_elapsed.tv_nsec); description, ts_elapsed.tv_sec, ts_elapsed.tv_nsec);
} }
static void access_memory(struct kvm_vm *vm, int vcpus, enum access_type access, static void access_memory(struct kvm_vm *vm, int nr_vcpus,
const char *description) enum access_type access, const char *description)
{ {
perf_test_set_wr_fract(vm, (access == ACCESS_READ) ? INT_MAX : 1); perf_test_set_wr_fract(vm, (access == ACCESS_READ) ? INT_MAX : 1);
iteration_work = ITERATION_ACCESS_MEMORY; iteration_work = ITERATION_ACCESS_MEMORY;
run_iteration(vm, vcpus, description); run_iteration(vm, nr_vcpus, description);
} }
static void mark_memory_idle(struct kvm_vm *vm, int vcpus) static void mark_memory_idle(struct kvm_vm *vm, int nr_vcpus)
{ {
/* /*
* Even though this parallelizes the work across vCPUs, this is still a * Even though this parallelizes the work across vCPUs, this is still a
...@@ -289,37 +290,37 @@ static void mark_memory_idle(struct kvm_vm *vm, int vcpus) ...@@ -289,37 +290,37 @@ static void mark_memory_idle(struct kvm_vm *vm, int vcpus)
*/ */
pr_debug("Marking VM memory idle (slow)...\n"); pr_debug("Marking VM memory idle (slow)...\n");
iteration_work = ITERATION_MARK_IDLE; iteration_work = ITERATION_MARK_IDLE;
run_iteration(vm, vcpus, "Mark memory idle"); run_iteration(vm, nr_vcpus, "Mark memory idle");
} }
static void run_test(enum vm_guest_mode mode, void *arg) static void run_test(enum vm_guest_mode mode, void *arg)
{ {
struct test_params *params = arg; struct test_params *params = arg;
struct kvm_vm *vm; struct kvm_vm *vm;
int vcpus = params->vcpus; int nr_vcpus = params->nr_vcpus;
vm = perf_test_create_vm(mode, vcpus, params->vcpu_memory_bytes, 1, vm = perf_test_create_vm(mode, nr_vcpus, params->vcpu_memory_bytes, 1,
params->backing_src, !overlap_memory_access); params->backing_src, !overlap_memory_access);
perf_test_start_vcpu_threads(vcpus, vcpu_thread_main); perf_test_start_vcpu_threads(nr_vcpus, vcpu_thread_main);
pr_info("\n"); pr_info("\n");
access_memory(vm, vcpus, ACCESS_WRITE, "Populating memory"); access_memory(vm, nr_vcpus, ACCESS_WRITE, "Populating memory");
/* As a control, read and write to the populated memory first. */ /* As a control, read and write to the populated memory first. */
access_memory(vm, vcpus, ACCESS_WRITE, "Writing to populated memory"); access_memory(vm, nr_vcpus, ACCESS_WRITE, "Writing to populated memory");
access_memory(vm, vcpus, ACCESS_READ, "Reading from populated memory"); access_memory(vm, nr_vcpus, ACCESS_READ, "Reading from populated memory");
/* Repeat on memory that has been marked as idle. */ /* Repeat on memory that has been marked as idle. */
mark_memory_idle(vm, vcpus); mark_memory_idle(vm, nr_vcpus);
access_memory(vm, vcpus, ACCESS_WRITE, "Writing to idle memory"); access_memory(vm, nr_vcpus, ACCESS_WRITE, "Writing to idle memory");
mark_memory_idle(vm, vcpus); mark_memory_idle(vm, nr_vcpus);
access_memory(vm, vcpus, ACCESS_READ, "Reading from idle memory"); access_memory(vm, nr_vcpus, ACCESS_READ, "Reading from idle memory");
/* Set done to signal the vCPU threads to exit */ /* Set done to signal the vCPU threads to exit */
done = true; done = true;
perf_test_join_vcpu_threads(vcpus); perf_test_join_vcpu_threads(nr_vcpus);
perf_test_destroy_vm(vm); perf_test_destroy_vm(vm);
} }
...@@ -347,7 +348,7 @@ int main(int argc, char *argv[]) ...@@ -347,7 +348,7 @@ int main(int argc, char *argv[])
struct test_params params = { struct test_params params = {
.backing_src = DEFAULT_VM_MEM_SRC, .backing_src = DEFAULT_VM_MEM_SRC,
.vcpu_memory_bytes = DEFAULT_PER_VCPU_MEM_SIZE, .vcpu_memory_bytes = DEFAULT_PER_VCPU_MEM_SIZE,
.vcpus = 1, .nr_vcpus = 1,
}; };
int page_idle_fd; int page_idle_fd;
int opt; int opt;
...@@ -363,7 +364,7 @@ int main(int argc, char *argv[]) ...@@ -363,7 +364,7 @@ int main(int argc, char *argv[])
params.vcpu_memory_bytes = parse_size(optarg); params.vcpu_memory_bytes = parse_size(optarg);
break; break;
case 'v': case 'v':
params.vcpus = atoi(optarg); params.nr_vcpus = atoi(optarg);
break; break;
case 'o': case 'o':
overlap_memory_access = true; overlap_memory_access = true;
......
...@@ -44,28 +44,27 @@ static char *guest_data_prototype; ...@@ -44,28 +44,27 @@ static char *guest_data_prototype;
static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args)
{ {
int ret; struct kvm_vcpu *vcpu = vcpu_args->vcpu;
int vcpu_id = vcpu_args->vcpu_id;
struct kvm_vm *vm = perf_test_args.vm; struct kvm_vm *vm = perf_test_args.vm;
struct kvm_run *run; int vcpu_idx = vcpu_args->vcpu_idx;
struct kvm_run *run = vcpu->run;
struct timespec start; struct timespec start;
struct timespec ts_diff; struct timespec ts_diff;
int ret;
run = vcpu_state(vm, vcpu_id);
clock_gettime(CLOCK_MONOTONIC, &start); clock_gettime(CLOCK_MONOTONIC, &start);
/* Let the guest access its memory */ /* Let the guest access its memory */
ret = _vcpu_run(vm, vcpu_id); ret = _vcpu_run(vm, vcpu->id);
TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
if (get_ucall(vm, vcpu_id, NULL) != UCALL_SYNC) { if (get_ucall(vm, vcpu->id, NULL) != UCALL_SYNC) {
TEST_ASSERT(false, TEST_ASSERT(false,
"Invalid guest sync status: exit_reason=%s\n", "Invalid guest sync status: exit_reason=%s\n",
exit_reason_str(run->exit_reason)); exit_reason_str(run->exit_reason));
} }
ts_diff = timespec_elapsed(start); ts_diff = timespec_elapsed(start);
PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_id, PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_idx,
ts_diff.tv_sec, ts_diff.tv_nsec); ts_diff.tv_sec, ts_diff.tv_nsec);
} }
...@@ -285,8 +284,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -285,8 +284,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
struct timespec ts_diff; struct timespec ts_diff;
int *pipefds = NULL; int *pipefds = NULL;
struct kvm_vm *vm; struct kvm_vm *vm;
int vcpu_id; int r, i;
int r;
vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 1, vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 1,
p->src_type, p->partition_vcpu_memory_access); p->src_type, p->partition_vcpu_memory_access);
...@@ -309,12 +307,12 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -309,12 +307,12 @@ static void run_test(enum vm_guest_mode mode, void *arg)
pipefds = malloc(sizeof(int) * nr_vcpus * 2); pipefds = malloc(sizeof(int) * nr_vcpus * 2);
TEST_ASSERT(pipefds, "Unable to allocate memory for pipefd"); TEST_ASSERT(pipefds, "Unable to allocate memory for pipefd");
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { for (i = 0; i < nr_vcpus; i++) {
struct perf_test_vcpu_args *vcpu_args; struct perf_test_vcpu_args *vcpu_args;
void *vcpu_hva; void *vcpu_hva;
void *vcpu_alias; void *vcpu_alias;
vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; vcpu_args = &perf_test_args.vcpu_args[i];
/* Cache the host addresses of the region */ /* Cache the host addresses of the region */
vcpu_hva = addr_gpa2hva(vm, vcpu_args->gpa); vcpu_hva = addr_gpa2hva(vm, vcpu_args->gpa);
...@@ -324,13 +322,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -324,13 +322,13 @@ static void run_test(enum vm_guest_mode mode, void *arg)
* Set up user fault fd to handle demand paging * Set up user fault fd to handle demand paging
* requests. * requests.
*/ */
r = pipe2(&pipefds[vcpu_id * 2], r = pipe2(&pipefds[i * 2],
O_CLOEXEC | O_NONBLOCK); O_CLOEXEC | O_NONBLOCK);
TEST_ASSERT(!r, "Failed to set up pipefd"); TEST_ASSERT(!r, "Failed to set up pipefd");
setup_demand_paging(vm, &uffd_handler_threads[vcpu_id], setup_demand_paging(vm, &uffd_handler_threads[i],
pipefds[vcpu_id * 2], p->uffd_mode, pipefds[i * 2], p->uffd_mode,
p->uffd_delay, &uffd_args[vcpu_id], p->uffd_delay, &uffd_args[i],
vcpu_hva, vcpu_alias, vcpu_hva, vcpu_alias,
vcpu_args->pages * perf_test_args.guest_page_size); vcpu_args->pages * perf_test_args.guest_page_size);
} }
...@@ -350,11 +348,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -350,11 +348,11 @@ static void run_test(enum vm_guest_mode mode, void *arg)
char c; char c;
/* Tell the user fault fd handler threads to quit */ /* Tell the user fault fd handler threads to quit */
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { for (i = 0; i < nr_vcpus; i++) {
r = write(pipefds[vcpu_id * 2 + 1], &c, 1); r = write(pipefds[i * 2 + 1], &c, 1);
TEST_ASSERT(r == 1, "Unable to write to pipefd"); TEST_ASSERT(r == 1, "Unable to write to pipefd");
pthread_join(uffd_handler_threads[vcpu_id], NULL); pthread_join(uffd_handler_threads[i], NULL);
} }
} }
......
...@@ -68,44 +68,45 @@ static int vcpu_last_completed_iteration[KVM_MAX_VCPUS]; ...@@ -68,44 +68,45 @@ static int vcpu_last_completed_iteration[KVM_MAX_VCPUS];
static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args)
{ {
int ret; struct kvm_vcpu *vcpu = vcpu_args->vcpu;
struct kvm_vm *vm = perf_test_args.vm; struct kvm_vm *vm = perf_test_args.vm;
int vcpu_idx = vcpu_args->vcpu_idx;
uint64_t pages_count = 0; uint64_t pages_count = 0;
struct kvm_run *run; struct kvm_run *run;
struct timespec start; struct timespec start;
struct timespec ts_diff; struct timespec ts_diff;
struct timespec total = (struct timespec){0}; struct timespec total = (struct timespec){0};
struct timespec avg; struct timespec avg;
int vcpu_id = vcpu_args->vcpu_id; int ret;
run = vcpu_state(vm, vcpu_id); run = vcpu->run;
while (!READ_ONCE(host_quit)) { while (!READ_ONCE(host_quit)) {
int current_iteration = READ_ONCE(iteration); int current_iteration = READ_ONCE(iteration);
clock_gettime(CLOCK_MONOTONIC, &start); clock_gettime(CLOCK_MONOTONIC, &start);
ret = _vcpu_run(vm, vcpu_id); ret = _vcpu_run(vm, vcpu->id);
ts_diff = timespec_elapsed(start); ts_diff = timespec_elapsed(start);
TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
TEST_ASSERT(get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC, TEST_ASSERT(get_ucall(vm, vcpu->id, NULL) == UCALL_SYNC,
"Invalid guest sync status: exit_reason=%s\n", "Invalid guest sync status: exit_reason=%s\n",
exit_reason_str(run->exit_reason)); exit_reason_str(run->exit_reason));
pr_debug("Got sync event from vCPU %d\n", vcpu_id); pr_debug("Got sync event from vCPU %d\n", vcpu_idx);
vcpu_last_completed_iteration[vcpu_id] = current_iteration; vcpu_last_completed_iteration[vcpu_idx] = current_iteration;
pr_debug("vCPU %d updated last completed iteration to %d\n", pr_debug("vCPU %d updated last completed iteration to %d\n",
vcpu_id, vcpu_last_completed_iteration[vcpu_id]); vcpu->id, vcpu_last_completed_iteration[vcpu_idx]);
if (current_iteration) { if (current_iteration) {
pages_count += vcpu_args->pages; pages_count += vcpu_args->pages;
total = timespec_add(total, ts_diff); total = timespec_add(total, ts_diff);
pr_debug("vCPU %d iteration %d dirty memory time: %ld.%.9lds\n", pr_debug("vCPU %d iteration %d dirty memory time: %ld.%.9lds\n",
vcpu_id, current_iteration, ts_diff.tv_sec, vcpu_idx, current_iteration, ts_diff.tv_sec,
ts_diff.tv_nsec); ts_diff.tv_nsec);
} else { } else {
pr_debug("vCPU %d iteration %d populate memory time: %ld.%.9lds\n", pr_debug("vCPU %d iteration %d populate memory time: %ld.%.9lds\n",
vcpu_id, current_iteration, ts_diff.tv_sec, vcpu_idx, current_iteration, ts_diff.tv_sec,
ts_diff.tv_nsec); ts_diff.tv_nsec);
} }
...@@ -113,9 +114,9 @@ static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) ...@@ -113,9 +114,9 @@ static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args)
!READ_ONCE(host_quit)) {} !READ_ONCE(host_quit)) {}
} }
avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_id]); avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_idx]);
pr_debug("\nvCPU %d dirtied 0x%lx pages over %d iterations in %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n", pr_debug("\nvCPU %d dirtied 0x%lx pages over %d iterations in %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
vcpu_id, pages_count, vcpu_last_completed_iteration[vcpu_id], vcpu_idx, pages_count, vcpu_last_completed_iteration[vcpu_idx],
total.tv_sec, total.tv_nsec, avg.tv_sec, avg.tv_nsec); total.tv_sec, total.tv_nsec, avg.tv_sec, avg.tv_nsec);
} }
...@@ -207,13 +208,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -207,13 +208,13 @@ static void run_test(enum vm_guest_mode mode, void *arg)
uint64_t guest_num_pages; uint64_t guest_num_pages;
uint64_t host_num_pages; uint64_t host_num_pages;
uint64_t pages_per_slot; uint64_t pages_per_slot;
int vcpu_id;
struct timespec start; struct timespec start;
struct timespec ts_diff; struct timespec ts_diff;
struct timespec get_dirty_log_total = (struct timespec){0}; struct timespec get_dirty_log_total = (struct timespec){0};
struct timespec vcpu_dirty_total = (struct timespec){0}; struct timespec vcpu_dirty_total = (struct timespec){0};
struct timespec avg; struct timespec avg;
struct timespec clear_dirty_log_total = (struct timespec){0}; struct timespec clear_dirty_log_total = (struct timespec){0};
int i;
vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size,
p->slots, p->backing_src, p->slots, p->backing_src,
...@@ -239,15 +240,15 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -239,15 +240,15 @@ static void run_test(enum vm_guest_mode mode, void *arg)
host_quit = false; host_quit = false;
clock_gettime(CLOCK_MONOTONIC, &start); clock_gettime(CLOCK_MONOTONIC, &start);
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) for (i = 0; i < nr_vcpus; i++)
vcpu_last_completed_iteration[vcpu_id] = -1; vcpu_last_completed_iteration[i] = -1;
perf_test_start_vcpu_threads(nr_vcpus, vcpu_worker); perf_test_start_vcpu_threads(nr_vcpus, vcpu_worker);
/* Allow the vCPUs to populate memory */ /* Allow the vCPUs to populate memory */
pr_debug("Starting iteration %d - Populating\n", iteration); pr_debug("Starting iteration %d - Populating\n", iteration);
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { for (i = 0; i < nr_vcpus; i++) {
while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != while (READ_ONCE(vcpu_last_completed_iteration[i]) !=
iteration) iteration)
; ;
} }
...@@ -272,8 +273,8 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -272,8 +273,8 @@ static void run_test(enum vm_guest_mode mode, void *arg)
iteration++; iteration++;
pr_debug("Starting iteration %d\n", iteration); pr_debug("Starting iteration %d\n", iteration);
for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { for (i = 0; i < nr_vcpus; i++) {
while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) while (READ_ONCE(vcpu_last_completed_iteration[i])
!= iteration) != iteration)
; ;
} }
......
...@@ -25,7 +25,8 @@ struct perf_test_vcpu_args { ...@@ -25,7 +25,8 @@ struct perf_test_vcpu_args {
uint64_t pages; uint64_t pages;
/* Only used by the host userspace part of the vCPU thread */ /* Only used by the host userspace part of the vCPU thread */
int vcpu_id; struct kvm_vcpu *vcpu;
int vcpu_idx;
}; };
struct perf_test_args { struct perf_test_args {
...@@ -44,7 +45,7 @@ struct perf_test_args { ...@@ -44,7 +45,7 @@ struct perf_test_args {
extern struct perf_test_args perf_test_args; extern struct perf_test_args perf_test_args;
struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus,
uint64_t vcpu_memory_bytes, int slots, uint64_t vcpu_memory_bytes, int slots,
enum vm_mem_backing_src_type backing_src, enum vm_mem_backing_src_type backing_src,
bool partition_vcpu_memory_access); bool partition_vcpu_memory_access);
...@@ -57,6 +58,6 @@ void perf_test_join_vcpu_threads(int vcpus); ...@@ -57,6 +58,6 @@ void perf_test_join_vcpu_threads(int vcpus);
void perf_test_guest_code(uint32_t vcpu_id); void perf_test_guest_code(uint32_t vcpu_id);
uint64_t perf_test_nested_pages(int nr_vcpus); uint64_t perf_test_nested_pages(int nr_vcpus);
void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus); void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]);
#endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */ #endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
...@@ -17,8 +17,8 @@ struct perf_test_args perf_test_args; ...@@ -17,8 +17,8 @@ struct perf_test_args perf_test_args;
static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
struct vcpu_thread { struct vcpu_thread {
/* The id of the vCPU. */ /* The index of the vCPU. */
int vcpu_id; int vcpu_idx;
/* The pthread backing the vCPU. */ /* The pthread backing the vCPU. */
pthread_t thread; pthread_t thread;
...@@ -36,24 +36,26 @@ static void (*vcpu_thread_fn)(struct perf_test_vcpu_args *); ...@@ -36,24 +36,26 @@ static void (*vcpu_thread_fn)(struct perf_test_vcpu_args *);
/* Set to true once all vCPU threads are up and running. */ /* Set to true once all vCPU threads are up and running. */
static bool all_vcpu_threads_running; static bool all_vcpu_threads_running;
static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
/* /*
* Continuously write to the first 8 bytes of each page in the * Continuously write to the first 8 bytes of each page in the
* specified region. * specified region.
*/ */
void perf_test_guest_code(uint32_t vcpu_id) void perf_test_guest_code(uint32_t vcpu_idx)
{ {
struct perf_test_args *pta = &perf_test_args; struct perf_test_args *pta = &perf_test_args;
struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_id]; struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_idx];
uint64_t gva; uint64_t gva;
uint64_t pages; uint64_t pages;
int i; int i;
/* Make sure vCPU args data structure is not corrupt. */
GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id);
gva = vcpu_args->gva; gva = vcpu_args->gva;
pages = vcpu_args->pages; pages = vcpu_args->pages;
/* Make sure vCPU args data structure is not corrupt. */
GUEST_ASSERT(vcpu_args->vcpu_idx == vcpu_idx);
while (true) { while (true) {
for (i = 0; i < pages; i++) { for (i = 0; i < pages; i++) {
uint64_t addr = gva + (i * pta->guest_page_size); uint64_t addr = gva + (i * pta->guest_page_size);
...@@ -68,40 +70,43 @@ void perf_test_guest_code(uint32_t vcpu_id) ...@@ -68,40 +70,43 @@ void perf_test_guest_code(uint32_t vcpu_id)
} }
} }
void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, void perf_test_setup_vcpus(struct kvm_vm *vm, int nr_vcpus,
struct kvm_vcpu *vcpus[],
uint64_t vcpu_memory_bytes, uint64_t vcpu_memory_bytes,
bool partition_vcpu_memory_access) bool partition_vcpu_memory_access)
{ {
struct perf_test_args *pta = &perf_test_args; struct perf_test_args *pta = &perf_test_args;
struct perf_test_vcpu_args *vcpu_args; struct perf_test_vcpu_args *vcpu_args;
int vcpu_id; int i;
for (i = 0; i < nr_vcpus; i++) {
vcpu_args = &pta->vcpu_args[i];
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { vcpu_args->vcpu = vcpus[i];
vcpu_args = &pta->vcpu_args[vcpu_id]; vcpu_args->vcpu_idx = i;
vcpu_args->vcpu_id = vcpu_id;
if (partition_vcpu_memory_access) { if (partition_vcpu_memory_access) {
vcpu_args->gva = guest_test_virt_mem + vcpu_args->gva = guest_test_virt_mem +
(vcpu_id * vcpu_memory_bytes); (i * vcpu_memory_bytes);
vcpu_args->pages = vcpu_memory_bytes / vcpu_args->pages = vcpu_memory_bytes /
pta->guest_page_size; pta->guest_page_size;
vcpu_args->gpa = pta->gpa + (vcpu_id * vcpu_memory_bytes); vcpu_args->gpa = pta->gpa + (i * vcpu_memory_bytes);
} else { } else {
vcpu_args->gva = guest_test_virt_mem; vcpu_args->gva = guest_test_virt_mem;
vcpu_args->pages = (vcpus * vcpu_memory_bytes) / vcpu_args->pages = (nr_vcpus * vcpu_memory_bytes) /
pta->guest_page_size; pta->guest_page_size;
vcpu_args->gpa = pta->gpa; vcpu_args->gpa = pta->gpa;
} }
vcpu_args_set(vm, vcpu_id, 1, vcpu_id); vcpu_args_set(vm, vcpus[i]->id, 1, i);
pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n", pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
vcpu_id, vcpu_args->gpa, vcpu_args->gpa + i, vcpu_args->gpa, vcpu_args->gpa +
(vcpu_args->pages * pta->guest_page_size)); (vcpu_args->pages * pta->guest_page_size));
} }
} }
struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus,
uint64_t vcpu_memory_bytes, int slots, uint64_t vcpu_memory_bytes, int slots,
enum vm_mem_backing_src_type backing_src, enum vm_mem_backing_src_type backing_src,
bool partition_vcpu_memory_access) bool partition_vcpu_memory_access)
...@@ -125,7 +130,7 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, ...@@ -125,7 +130,7 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
pta->guest_page_size = vm_guest_mode_params[mode].page_size; pta->guest_page_size = vm_guest_mode_params[mode].page_size;
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages = vm_adjust_num_guest_pages(mode,
(vcpus * vcpu_memory_bytes) / pta->guest_page_size); (nr_vcpus * vcpu_memory_bytes) / pta->guest_page_size);
TEST_ASSERT(vcpu_memory_bytes % getpagesize() == 0, TEST_ASSERT(vcpu_memory_bytes % getpagesize() == 0,
"Guest memory size is not host page size aligned."); "Guest memory size is not host page size aligned.");
...@@ -140,16 +145,16 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, ...@@ -140,16 +145,16 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
* in-memory data structures. * in-memory data structures.
*/ */
if (pta->nested) if (pta->nested)
slot0_pages += perf_test_nested_pages(vcpus); slot0_pages += perf_test_nested_pages(nr_vcpus);
/* /*
* Pass guest_num_pages to populate the page tables for test memory. * Pass guest_num_pages to populate the page tables for test memory.
* The memory is also added to memslot 0, but that's a benign side * The memory is also added to memslot 0, but that's a benign side
* effect as KVM allows aliasing HVAs in meslots. * effect as KVM allows aliasing HVAs in meslots.
*/ */
vm = __vm_create_with_vcpus(mode, vcpus, DEFAULT_GUEST_PHY_PAGES, vm = __vm_create_with_vcpus(mode, nr_vcpus, DEFAULT_GUEST_PHY_PAGES,
slot0_pages + guest_num_pages, 0, slot0_pages + guest_num_pages, 0,
perf_test_guest_code, NULL); perf_test_guest_code, vcpus);
pta->vm = vm; pta->vm = vm;
...@@ -171,11 +176,10 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, ...@@ -171,11 +176,10 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
TEST_ASSERT(guest_num_pages < region_end_gfn, TEST_ASSERT(guest_num_pages < region_end_gfn,
"Requested more guest memory than address space allows.\n" "Requested more guest memory than address space allows.\n"
" guest pages: %" PRIx64 " max gfn: %" PRIx64 " guest pages: %" PRIx64 " max gfn: %" PRIx64
" vcpus: %d wss: %" PRIx64 "]\n", " nr_vcpus: %d wss: %" PRIx64 "]\n",
guest_num_pages, region_end_gfn - 1, vcpus, guest_num_pages, region_end_gfn - 1, nr_vcpus, vcpu_memory_bytes);
vcpu_memory_bytes);
pta->gpa = (region_end_gfn - guest_num_pages) * pta->guest_page_size; pta->gpa = (region_end_gfn - guest_num_pages - 1) * pta->guest_page_size;
pta->gpa = align_down(pta->gpa, backing_src_pagesz); pta->gpa = align_down(pta->gpa, backing_src_pagesz);
#ifdef __s390x__ #ifdef __s390x__
/* Align to 1M (segment size) */ /* Align to 1M (segment size) */
...@@ -198,11 +202,12 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, ...@@ -198,11 +202,12 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
/* Do mapping for the demand paging memory slot */ /* Do mapping for the demand paging memory slot */
virt_map(vm, guest_test_virt_mem, pta->gpa, guest_num_pages); virt_map(vm, guest_test_virt_mem, pta->gpa, guest_num_pages);
perf_test_setup_vcpus(vm, vcpus, vcpu_memory_bytes, partition_vcpu_memory_access); perf_test_setup_vcpus(vm, nr_vcpus, vcpus, vcpu_memory_bytes,
partition_vcpu_memory_access);
if (pta->nested) { if (pta->nested) {
pr_info("Configuring vCPUs to run in L2 (nested).\n"); pr_info("Configuring vCPUs to run in L2 (nested).\n");
perf_test_setup_nested(vm, vcpus); perf_test_setup_nested(vm, nr_vcpus, vcpus);
} }
ucall_init(vm, NULL); ucall_init(vm, NULL);
...@@ -230,7 +235,7 @@ uint64_t __weak perf_test_nested_pages(int nr_vcpus) ...@@ -230,7 +235,7 @@ uint64_t __weak perf_test_nested_pages(int nr_vcpus)
return 0; return 0;
} }
void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu **vcpus)
{ {
pr_info("%s() not support on this architecture, skipping.\n", __func__); pr_info("%s() not support on this architecture, skipping.\n", __func__);
exit(KSFT_SKIP); exit(KSFT_SKIP);
...@@ -251,39 +256,40 @@ static void *vcpu_thread_main(void *data) ...@@ -251,39 +256,40 @@ static void *vcpu_thread_main(void *data)
while (!READ_ONCE(all_vcpu_threads_running)) while (!READ_ONCE(all_vcpu_threads_running))
; ;
vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_id]); vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_idx]);
return NULL; return NULL;
} }
void perf_test_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct perf_test_vcpu_args *)) void perf_test_start_vcpu_threads(int nr_vcpus,
void (*vcpu_fn)(struct perf_test_vcpu_args *))
{ {
int vcpu_id; int i;
vcpu_thread_fn = vcpu_fn; vcpu_thread_fn = vcpu_fn;
WRITE_ONCE(all_vcpu_threads_running, false); WRITE_ONCE(all_vcpu_threads_running, false);
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { for (i = 0; i < nr_vcpus; i++) {
struct vcpu_thread *vcpu = &vcpu_threads[vcpu_id]; struct vcpu_thread *vcpu = &vcpu_threads[i];
vcpu->vcpu_id = vcpu_id; vcpu->vcpu_idx = i;
WRITE_ONCE(vcpu->running, false); WRITE_ONCE(vcpu->running, false);
pthread_create(&vcpu->thread, NULL, vcpu_thread_main, vcpu); pthread_create(&vcpu->thread, NULL, vcpu_thread_main, vcpu);
} }
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { for (i = 0; i < nr_vcpus; i++) {
while (!READ_ONCE(vcpu_threads[vcpu_id].running)) while (!READ_ONCE(vcpu_threads[i].running))
; ;
} }
WRITE_ONCE(all_vcpu_threads_running, true); WRITE_ONCE(all_vcpu_threads_running, true);
} }
void perf_test_join_vcpu_threads(int vcpus) void perf_test_join_vcpu_threads(int nr_vcpus)
{ {
int vcpu_id; int i;
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) for (i = 0; i < nr_vcpus; i++)
pthread_join(vcpu_threads[vcpu_id].thread, NULL); pthread_join(vcpu_threads[i].thread, NULL);
} }
...@@ -77,7 +77,7 @@ void perf_test_setup_ept(struct vmx_pages *vmx, struct kvm_vm *vm) ...@@ -77,7 +77,7 @@ void perf_test_setup_ept(struct vmx_pages *vmx, struct kvm_vm *vm)
nested_identity_map_1g(vmx, vm, start, end - start); nested_identity_map_1g(vmx, vm, start, end - start);
} }
void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[])
{ {
struct vmx_pages *vmx, *vmx0 = NULL; struct vmx_pages *vmx, *vmx0 = NULL;
struct kvm_regs regs; struct kvm_regs regs;
...@@ -103,9 +103,9 @@ void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) ...@@ -103,9 +103,9 @@ void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus)
* Override the vCPU to run perf_test_l1_guest_code() which will * Override the vCPU to run perf_test_l1_guest_code() which will
* bounce it into L2 before calling perf_test_guest_code(). * bounce it into L2 before calling perf_test_guest_code().
*/ */
vcpu_regs_get(vm, vcpu_id, &regs); vcpu_regs_get(vm, vcpus[vcpu_id]->id, &regs);
regs.rip = (unsigned long) perf_test_l1_guest_code; regs.rip = (unsigned long) perf_test_l1_guest_code;
vcpu_regs_set(vm, vcpu_id, &regs); vcpu_regs_set(vm, vcpus[vcpu_id]->id, &regs);
vcpu_args_set(vm, vcpu_id, 2, vmx_gva, vcpu_id); vcpu_args_set(vm, vcpus[vcpu_id]->id, 2, vmx_gva, vcpu_id);
} }
} }
...@@ -38,19 +38,19 @@ static bool run_vcpus = true; ...@@ -38,19 +38,19 @@ static bool run_vcpus = true;
static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args)
{ {
int ret; struct kvm_vcpu *vcpu = vcpu_args->vcpu;
int vcpu_id = vcpu_args->vcpu_id;
struct kvm_vm *vm = perf_test_args.vm; struct kvm_vm *vm = perf_test_args.vm;
struct kvm_run *run; struct kvm_run *run;
int ret;
run = vcpu_state(vm, vcpu_id); run = vcpu->run;
/* Let the guest access its memory until a stop signal is received */ /* Let the guest access its memory until a stop signal is received */
while (READ_ONCE(run_vcpus)) { while (READ_ONCE(run_vcpus)) {
ret = _vcpu_run(vm, vcpu_id); ret = _vcpu_run(vm, vcpu->id);
TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret);
if (get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC) if (get_ucall(vm, vcpu->id, NULL) == UCALL_SYNC)
continue; continue;
TEST_ASSERT(false, TEST_ASSERT(false,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment