Skip to content

Commit

Permalink
KVM: selftests: Allowing running dirty_log_perf_test on specific CPUs
Browse files Browse the repository at this point in the history
Add a command line option, -c, to pin vCPUs to physical CPUs (pCPUs),
i.e.  to force vCPUs to run on specific pCPUs.

Requirement to implement this feature came in discussion on the patch
"Make page tables for eager page splitting NUMA aware"
https://lore.kernel.org/lkml/YuhPT2drgqL+osLl@google.com/

This feature is useful as it provides a way to analyze performance based
on the vCPUs and dirty log worker locations, like on the different NUMA
nodes or on the same NUMA nodes.

To keep things simple, implementation is intentionally very limited,
either all of the vCPUs will be pinned followed by an optional main
thread or nothing will be pinned.

Signed-off-by: Vipin Sharma <vipinsh@google.com>
Suggested-by: David Matlack <dmatlack@google.com>
Reviewed-by: Sean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/r/20221103191719.1559407-8-vipinsh@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
  • Loading branch information
Vipin Sharma authored and Sean Christopherson committed Nov 16, 2022
1 parent 0001725 commit d886724
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 3 deletions.
25 changes: 23 additions & 2 deletions tools/testing/selftests/kvm/dirty_log_perf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ static void help(char *name)
puts("");
printf("usage: %s [-h] [-i iterations] [-p offset] [-g] "
"[-m mode] [-n] [-b vcpu bytes] [-v vcpus] [-o] [-s mem type]"
"[-x memslots]\n", name);
"[-x memslots] [-c physical cpus to run test on]\n", name);
puts("");
printf(" -i: specify iteration counts (default: %"PRIu64")\n",
TEST_HOST_LOOP_N);
Expand Down Expand Up @@ -383,13 +383,25 @@ static void help(char *name)
backing_src_help("-s");
printf(" -x: Split the memory region into this number of memslots.\n"
" (default: 1)\n");
printf(" -c: Pin tasks to physical CPUs. Takes a list of comma separated\n"
" values (target pCPU), one for each vCPU, plus an optional\n"
" entry for the main application task (specified via entry\n"
" <nr_vcpus + 1>). If used, entries must be provided for all\n"
" vCPUs, i.e. pinning vCPUs is all or nothing.\n\n"
" E.g. to create 3 vCPUs, pin vCPU0=>pCPU22, vCPU1=>pCPU23,\n"
" vCPU2=>pCPU24, and pin the application task to pCPU50:\n\n"
" ./dirty_log_perf_test -v 3 -c 22,23,24,50\n\n"
" To leave the application task unpinned, drop the final entry:\n\n"
" ./dirty_log_perf_test -v 3 -c 22,23,24\n\n"
" (default: no pinning)\n");
puts("");
exit(0);
}

int main(int argc, char *argv[])
{
int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
const char *pcpu_list = NULL;
struct test_params p = {
.iterations = TEST_HOST_LOOP_N,
.wr_fract = 1,
Expand All @@ -406,11 +418,14 @@ int main(int argc, char *argv[])

guest_modes_append_default();

while ((opt = getopt(argc, argv, "b:ef:ghi:m:nop:s:v:x:")) != -1) {
while ((opt = getopt(argc, argv, "b:c:ef:ghi:m:nop:s:v:x:")) != -1) {
switch (opt) {
case 'b':
guest_percpu_mem_size = parse_size(optarg);
break;
case 'c':
pcpu_list = optarg;
break;
case 'e':
/* 'e' is for evil. */
run_vcpus_while_disabling_dirty_logging = true;
Expand Down Expand Up @@ -456,6 +471,12 @@ int main(int argc, char *argv[])
}
}

if (pcpu_list) {
kvm_parse_vcpu_pinning(pcpu_list, perf_test_args.vcpu_to_pcpu,
nr_vcpus);
perf_test_args.pin_vcpus = true;
}

TEST_ASSERT(p.iterations >= 2, "The test should have at least two iterations");

pr_info("Test iterations: %"PRIu64"\n", p.iterations);
Expand Down
4 changes: 4 additions & 0 deletions tools/testing/selftests/kvm/include/kvm_util_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,10 @@ static inline struct kvm_vm *vm_create_with_one_vcpu(struct kvm_vcpu **vcpu,

struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm);

void kvm_pin_this_task_to_pcpu(uint32_t pcpu);
void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
int nr_vcpus);

unsigned long vm_compute_max_gfn(struct kvm_vm *vm);
unsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size);
unsigned int vm_num_host_pages(enum vm_guest_mode mode, unsigned int num_guest_pages);
Expand Down
4 changes: 4 additions & 0 deletions tools/testing/selftests/kvm/include/perf_test_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ struct perf_test_args {

/* Run vCPUs in L2 instead of L1, if the architecture supports it. */
bool nested;
/* True if all vCPUs are pinned to pCPUs */
bool pin_vcpus;
/* The vCPU=>pCPU pinning map. Only valid if pin_vcpus is true. */
uint32_t vcpu_to_pcpu[KVM_MAX_VCPUS];

struct perf_test_vcpu_args vcpu_args[KVM_MAX_VCPUS];
};
Expand Down
54 changes: 54 additions & 0 deletions tools/testing/selftests/kvm/lib/kvm_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "processor.h"

#include <assert.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
Expand Down Expand Up @@ -443,6 +444,59 @@ struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm)
return vm_vcpu_recreate(vm, 0);
}

void kvm_pin_this_task_to_pcpu(uint32_t pcpu)
{
cpu_set_t mask;
int r;

CPU_ZERO(&mask);
CPU_SET(pcpu, &mask);
r = sched_setaffinity(0, sizeof(mask), &mask);
TEST_ASSERT(!r, "sched_setaffinity() failed for pCPU '%u'.\n", pcpu);
}

static uint32_t parse_pcpu(const char *cpu_str, const cpu_set_t *allowed_mask)
{
uint32_t pcpu = atoi_non_negative("CPU number", cpu_str);

TEST_ASSERT(CPU_ISSET(pcpu, allowed_mask),
"Not allowed to run on pCPU '%d', check cgroups?\n", pcpu);
return pcpu;
}

void kvm_parse_vcpu_pinning(const char *pcpus_string, uint32_t vcpu_to_pcpu[],
int nr_vcpus)
{
cpu_set_t allowed_mask;
char *cpu, *cpu_list;
char delim[2] = ",";
int i, r;

cpu_list = strdup(pcpus_string);
TEST_ASSERT(cpu_list, "strdup() allocation failed.\n");

r = sched_getaffinity(0, sizeof(allowed_mask), &allowed_mask);
TEST_ASSERT(!r, "sched_getaffinity() failed");

cpu = strtok(cpu_list, delim);

/* 1. Get all pcpus for vcpus. */
for (i = 0; i < nr_vcpus; i++) {
TEST_ASSERT(cpu, "pCPU not provided for vCPU '%d'\n", i);
vcpu_to_pcpu[i] = parse_pcpu(cpu, &allowed_mask);
cpu = strtok(NULL, delim);
}

/* 2. Check if the main worker needs to be pinned. */
if (cpu) {
kvm_pin_this_task_to_pcpu(parse_pcpu(cpu, &allowed_mask));
cpu = strtok(NULL, delim);
}

TEST_ASSERT(!cpu, "pCPU list contains trailing garbage characters '%s'", cpu);
free(cpu_list);
}

/*
* Userspace Memory Region Find
*
Expand Down
8 changes: 7 additions & 1 deletion tools/testing/selftests/kvm/lib/perf_test_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
/*
* Copyright (C) 2020, Google LLC.
*/
#define _GNU_SOURCE

#include <inttypes.h>

#include "kvm_util.h"
Expand Down Expand Up @@ -243,6 +245,10 @@ void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_v
static void *vcpu_thread_main(void *data)
{
struct vcpu_thread *vcpu = data;
int vcpu_idx = vcpu->vcpu_idx;

if (perf_test_args.pin_vcpus)
kvm_pin_this_task_to_pcpu(perf_test_args.vcpu_to_pcpu[vcpu_idx]);

WRITE_ONCE(vcpu->running, true);

Expand All @@ -255,7 +261,7 @@ static void *vcpu_thread_main(void *data)
while (!READ_ONCE(all_vcpu_threads_running))
;

vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_idx]);
vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu_idx]);

return NULL;
}
Expand Down

0 comments on commit d886724

Please sign in to comment.