Commit 6864c644 authored by Colton Lewis's avatar Colton Lewis Committed by Sean Christopherson

KVM: selftests: randomize which pages are written vs read

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: default avatarColton Lewis <coltonlewis@google.com>
Reviewed-by: default avatarDavid Matlack <dmatlack@google.com>
Link: https://lore.kernel.org/r/20221107182208.479157-4-coltonlewis@google.comSigned-off-by: default avatarSean Christopherson <seanjc@google.com>
parent f11aa24b
......@@ -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);
}
......
......@@ -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;
};
......@@ -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);
......@@ -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 */
......@@ -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
......@@ -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);
......@@ -376,10 +386,6 @@ 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");
......@@ -387,6 +393,11 @@ 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(" -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"
......@@ -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;
......@@ -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);
......@@ -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;
......@@ -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;
......
......@@ -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;
......@@ -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 *));
......
......@@ -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;
......@@ -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);
......@@ -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
......@@ -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)
......
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