Skip to content

Commit

Permalink
KVM: selftests: randomize which pages are written vs read
Browse files Browse the repository at this point in the history
Randomize which pages are written vs read using the random number
generator.

Change the variable wr_fract and associated function calls to
write_percent that now operates as a percentage from 0 to 100 where X
means each page has an X% chance of being written. Change the -f
argument to -w to reflect the new variable semantics. Keep the same
default of 100% writes.

Population always uses 100% writes to ensure all memory is actually
populated and not just mapped to the zero page. The prevents expensive
copy-on-write faults from occurring during the dirty memory iterations
below, which would pollute the performance results.

Each vCPU calculates its own random seed by adding its index to the
seed provided.

Signed-off-by: Colton Lewis <coltonlewis@google.com>
Reviewed-by: David Matlack <dmatlack@google.com>
Link: https://lore.kernel.org/r/20221107182208.479157-4-coltonlewis@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
  • Loading branch information
Colton Lewis authored and Sean Christopherson committed Nov 16, 2022
1 parent f11aa24 commit 6864c64
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 20 deletions.
2 changes: 1 addition & 1 deletion tools/testing/selftests/kvm/access_tracking_perf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ static void run_iteration(struct kvm_vm *vm, int nr_vcpus, const char *descripti
static void access_memory(struct kvm_vm *vm, int nr_vcpus,
enum access_type access, const char *description)
{
perf_test_set_wr_fract(vm, (access == ACCESS_READ) ? INT_MAX : 1);
perf_test_set_write_percent(vm, (access == ACCESS_READ) ? 0 : 100);
iteration_work = ITERATION_ACCESS_MEMORY;
run_iteration(vm, nr_vcpus, description);
}
Expand Down
37 changes: 25 additions & 12 deletions tools/testing/selftests/kvm/dirty_log_perf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args)
struct test_params {
unsigned long iterations;
uint64_t phys_offset;
int wr_fract;
bool partition_vcpu_memory_access;
enum vm_mem_backing_src_type backing_src;
int slots;
uint32_t write_percent;
uint32_t random_seed;
};

Expand Down Expand Up @@ -228,7 +228,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)

pr_info("Random seed: %u\n", p->random_seed);
perf_test_set_random_seed(vm, p->random_seed);
perf_test_set_wr_fract(vm, p->wr_fract);
perf_test_set_write_percent(vm, p->write_percent);

guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm->page_shift;
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
Expand All @@ -251,6 +251,14 @@ static void run_test(enum vm_guest_mode mode, void *arg)
for (i = 0; i < nr_vcpus; i++)
vcpu_last_completed_iteration[i] = -1;

/*
* Use 100% writes during the population phase to ensure all
* memory is actually populated and not just mapped to the zero
* page. The prevents expensive copy-on-write faults from
* occurring during the dirty memory iterations below, which
* would pollute the performance results.
*/
perf_test_set_write_percent(vm, 100);
perf_test_start_vcpu_threads(nr_vcpus, vcpu_worker);

/* Allow the vCPUs to populate memory */
Expand All @@ -272,6 +280,8 @@ static void run_test(enum vm_guest_mode mode, void *arg)
pr_info("Enabling dirty logging time: %ld.%.9lds\n\n",
ts_diff.tv_sec, ts_diff.tv_nsec);

perf_test_set_write_percent(vm, p->write_percent);

while (iteration < p->iterations) {
/*
* Incrementing the iteration number will start the vCPUs
Expand Down Expand Up @@ -356,7 +366,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] [-r random seed ] [-s mem type]"
"[-x memslots] [-c physical cpus to run test on]\n", name);
"[-x memslots] [-w percentage] [-c physical cpus to run test on]\n", name);
puts("");
printf(" -i: specify iteration counts (default: %"PRIu64")\n",
TEST_HOST_LOOP_N);
Expand All @@ -376,17 +386,18 @@ static void help(char *name)
printf(" -b: specify the size of the memory region which should be\n"
" dirtied by each vCPU. e.g. 10M or 3G.\n"
" (default: 1G)\n");
printf(" -f: specify the fraction of pages which should be written to\n"
" as opposed to simply read, in the form\n"
" 1/<fraction of pages to write>.\n"
" (default: 1 i.e. all pages are written to.)\n");
printf(" -v: specify the number of vCPUs to run.\n");
printf(" -o: Overlap guest memory accesses instead of partitioning\n"
" them into a separate region of memory for each vCPU.\n");
printf(" -r: specify the starting random seed.\n");
backing_src_help("-s");
printf(" -x: Split the memory region into this number of memslots.\n"
" (default: 1)\n");
printf(" -w: specify the percentage of pages which should be written to\n"
" as an integer from 0-100 inclusive. This is probabalistic,\n"
" so -w X means each page has an X%% chance of writing\n"
" and a (100-X)%% chance of reading.\n"
" (default: 100 i.e. all pages are written to.)\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"
Expand All @@ -408,11 +419,11 @@ int main(int argc, char *argv[])
const char *pcpu_list = NULL;
struct test_params p = {
.iterations = TEST_HOST_LOOP_N,
.wr_fract = 1,
.partition_vcpu_memory_access = true,
.backing_src = DEFAULT_VM_MEM_SRC,
.slots = 1,
.random_seed = 1,
.write_percent = 100,
};
int opt;

Expand All @@ -423,7 +434,7 @@ int main(int argc, char *argv[])

guest_modes_append_default();

while ((opt = getopt(argc, argv, "b:c:ef:ghi:m:nop:r:s:v:x:")) != -1) {
while ((opt = getopt(argc, argv, "b:c:eghi:m:nop:r:s:v:x:w:")) != -1) {
switch (opt) {
case 'b':
guest_percpu_mem_size = parse_size(optarg);
Expand All @@ -435,9 +446,6 @@ int main(int argc, char *argv[])
/* 'e' is for evil. */
run_vcpus_while_disabling_dirty_logging = true;
break;
case 'f':
p.wr_fract = atoi_positive("Write fraction", optarg);
break;
case 'g':
dirty_log_manual_caps = 0;
break;
Expand Down Expand Up @@ -470,6 +478,11 @@ int main(int argc, char *argv[])
TEST_ASSERT(nr_vcpus <= max_vcpus,
"Invalid number of vcpus, must be between 1 and %d", max_vcpus);
break;
case 'w':
p.write_percent = atoi_non_negative("Write percentage", optarg);
TEST_ASSERT(p.write_percent <= 100,
"Write percentage must be between 0 and 100");
break;
case 'x':
p.slots = atoi_positive("Number of slots", optarg);
break;
Expand Down
4 changes: 2 additions & 2 deletions tools/testing/selftests/kvm/include/perf_test_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct perf_test_args {
uint64_t size;
uint64_t guest_page_size;
uint32_t random_seed;
int wr_fract;
uint32_t write_percent;

/* Run vCPUs in L2 instead of L1, if the architecture supports it. */
bool nested;
Expand All @@ -56,7 +56,7 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus,
bool partition_vcpu_memory_access);
void perf_test_destroy_vm(struct kvm_vm *vm);

void perf_test_set_wr_fract(struct kvm_vm *vm, int wr_fract);
void perf_test_set_write_percent(struct kvm_vm *vm, uint32_t write_percent);
void perf_test_set_random_seed(struct kvm_vm *vm, uint32_t random_seed);

void perf_test_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct perf_test_vcpu_args *));
Expand Down
13 changes: 8 additions & 5 deletions tools/testing/selftests/kvm/lib/perf_test_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,13 @@ void perf_test_guest_code(uint32_t vcpu_idx)
{
struct perf_test_args *pta = &perf_test_args;
struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_idx];
struct guest_random_state rand_state;
uint64_t gva;
uint64_t pages;
int i;

rand_state = new_guest_random_state(pta->random_seed + vcpu_idx);

gva = vcpu_args->gva;
pages = vcpu_args->pages;

Expand All @@ -62,7 +65,7 @@ void perf_test_guest_code(uint32_t vcpu_idx)
for (i = 0; i < pages; i++) {
uint64_t addr = gva + (i * pta->guest_page_size);

if (i % pta->wr_fract == 0)
if (guest_random_u32(&rand_state) % 100 < pta->write_percent)
*(uint64_t *)addr = 0x0123456789ABCDEF;
else
READ_ONCE(*(uint64_t *)addr);
Expand Down Expand Up @@ -123,7 +126,7 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus,
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));

/* By default vCPUs will write to memory. */
pta->wr_fract = 1;
pta->write_percent = 100;

/*
* Snapshot the non-huge page size. This is used by the guest code to
Expand Down Expand Up @@ -225,10 +228,10 @@ void perf_test_destroy_vm(struct kvm_vm *vm)
kvm_vm_free(vm);
}

void perf_test_set_wr_fract(struct kvm_vm *vm, int wr_fract)
void perf_test_set_write_percent(struct kvm_vm *vm, uint32_t write_percent)
{
perf_test_args.wr_fract = wr_fract;
sync_global_to_guest(vm, perf_test_args);
perf_test_args.write_percent = write_percent;
sync_global_to_guest(vm, perf_test_args.write_percent);
}

void perf_test_set_random_seed(struct kvm_vm *vm, uint32_t random_seed)
Expand Down

0 comments on commit 6864c64

Please sign in to comment.