Commit cb6c6914 authored by Sean Christopherson's avatar Sean Christopherson

KVM: selftests: Provide a global pseudo-RNG instance for all tests

Add a global guest_random_state instance, i.e. a pseudo-RNG, so that an
RNG is available for *all* tests.  This will allow randomizing behavior
in core library code, e.g. x86 will utilize the pRNG to conditionally
force emulation of writes from within common guest code.

To allow for deterministic runs, and to be compatible with existing tests,
allow tests to override the seed used to initialize the pRNG.

Note, the seed *must* be overwritten before a VM is created in order for
the seed to take effect, though it's perfectly fine for a test to
initialize multiple VMs with different seeds.

And as evidenced by memstress_guest_code(), it's also a-ok to instantiate
more RNGs using the global seed (or a modified version of it).  The goal
of the global RNG is purely to ensure that _a_ source of random numbers is
available, it doesn't have to be the _only_ RNG.

Link: https://lore.kernel.org/r/20240314185459.2439072-2-seanjc@google.comSigned-off-by: default avatarSean Christopherson <seanjc@google.com>
parent 730cfa45
...@@ -132,7 +132,6 @@ struct test_params { ...@@ -132,7 +132,6 @@ struct test_params {
enum vm_mem_backing_src_type backing_src; enum vm_mem_backing_src_type backing_src;
int slots; int slots;
uint32_t write_percent; uint32_t write_percent;
uint32_t random_seed;
bool random_access; bool random_access;
}; };
...@@ -156,8 +155,6 @@ static void run_test(enum vm_guest_mode mode, void *arg) ...@@ -156,8 +155,6 @@ static void run_test(enum vm_guest_mode mode, void *arg)
p->slots, p->backing_src, p->slots, p->backing_src,
p->partition_vcpu_memory_access); p->partition_vcpu_memory_access);
pr_info("Random seed: %u\n", p->random_seed);
memstress_set_random_seed(vm, p->random_seed);
memstress_set_write_percent(vm, p->write_percent); memstress_set_write_percent(vm, p->write_percent);
guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm->page_shift; guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm->page_shift;
...@@ -346,11 +343,13 @@ int main(int argc, char *argv[]) ...@@ -346,11 +343,13 @@ int main(int argc, char *argv[])
.partition_vcpu_memory_access = true, .partition_vcpu_memory_access = true,
.backing_src = DEFAULT_VM_MEM_SRC, .backing_src = DEFAULT_VM_MEM_SRC,
.slots = 1, .slots = 1,
.random_seed = 1,
.write_percent = 100, .write_percent = 100,
}; };
int opt; int opt;
/* Override the seed to be deterministic by default. */
guest_random_seed = 1;
dirty_log_manual_caps = dirty_log_manual_caps =
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
...@@ -395,7 +394,7 @@ int main(int argc, char *argv[]) ...@@ -395,7 +394,7 @@ int main(int argc, char *argv[])
p.phys_offset = strtoull(optarg, NULL, 0); p.phys_offset = strtoull(optarg, NULL, 0);
break; break;
case 'r': case 'r':
p.random_seed = atoi_positive("Random seed", optarg); guest_random_seed = atoi_positive("Random seed", optarg);
break; break;
case 's': case 's':
p.backing_src = parse_backing_src_type(optarg); p.backing_src = parse_backing_src_type(optarg);
......
...@@ -73,7 +73,6 @@ ...@@ -73,7 +73,6 @@
static uint64_t host_page_size; static uint64_t host_page_size;
static uint64_t guest_page_size; static uint64_t guest_page_size;
static uint64_t guest_num_pages; static uint64_t guest_num_pages;
static uint64_t random_array[TEST_PAGES_PER_LOOP];
static uint64_t iteration; static uint64_t iteration;
/* /*
...@@ -112,13 +111,12 @@ static void guest_code(void) ...@@ -112,13 +111,12 @@ static void guest_code(void)
while (true) { while (true) {
for (i = 0; i < TEST_PAGES_PER_LOOP; i++) { for (i = 0; i < TEST_PAGES_PER_LOOP; i++) {
addr = guest_test_virt_mem; addr = guest_test_virt_mem;
addr += (READ_ONCE(random_array[i]) % guest_num_pages) addr += (guest_random_u64(&guest_rng) % guest_num_pages)
* guest_page_size; * guest_page_size;
addr = align_down(addr, host_page_size); addr = align_down(addr, host_page_size);
*(uint64_t *)addr = READ_ONCE(iteration); *(uint64_t *)addr = READ_ONCE(iteration);
} }
/* Tell the host that we need more random numbers */
GUEST_SYNC(1); GUEST_SYNC(1);
} }
} }
...@@ -505,20 +503,10 @@ static void log_mode_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) ...@@ -505,20 +503,10 @@ static void log_mode_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err)
mode->after_vcpu_run(vcpu, ret, err); mode->after_vcpu_run(vcpu, ret, err);
} }
static void generate_random_array(uint64_t *guest_array, uint64_t size)
{
uint64_t i;
for (i = 0; i < size; i++)
guest_array[i] = random();
}
static void *vcpu_worker(void *data) static void *vcpu_worker(void *data)
{ {
int ret; int ret;
struct kvm_vcpu *vcpu = data; struct kvm_vcpu *vcpu = data;
struct kvm_vm *vm = vcpu->vm;
uint64_t *guest_array;
uint64_t pages_count = 0; uint64_t pages_count = 0;
struct kvm_signal_mask *sigmask = alloca(offsetof(struct kvm_signal_mask, sigset) struct kvm_signal_mask *sigmask = alloca(offsetof(struct kvm_signal_mask, sigset)
+ sizeof(sigset_t)); + sizeof(sigset_t));
...@@ -537,11 +525,8 @@ static void *vcpu_worker(void *data) ...@@ -537,11 +525,8 @@ static void *vcpu_worker(void *data)
sigemptyset(sigset); sigemptyset(sigset);
sigaddset(sigset, SIG_IPI); sigaddset(sigset, SIG_IPI);
guest_array = addr_gva2hva(vm, (vm_vaddr_t)random_array);
while (!READ_ONCE(host_quit)) { while (!READ_ONCE(host_quit)) {
/* Clear any existing kick signals */ /* Clear any existing kick signals */
generate_random_array(guest_array, TEST_PAGES_PER_LOOP);
pages_count += TEST_PAGES_PER_LOOP; pages_count += TEST_PAGES_PER_LOOP;
/* Let the guest dirty the random pages */ /* Let the guest dirty the random pages */
ret = __vcpu_run(vcpu); ret = __vcpu_run(vcpu);
......
...@@ -62,7 +62,6 @@ struct kvm_vm *memstress_create_vm(enum vm_guest_mode mode, int nr_vcpus, ...@@ -62,7 +62,6 @@ struct kvm_vm *memstress_create_vm(enum vm_guest_mode mode, int nr_vcpus,
void memstress_destroy_vm(struct kvm_vm *vm); void memstress_destroy_vm(struct kvm_vm *vm);
void memstress_set_write_percent(struct kvm_vm *vm, uint32_t write_percent); void memstress_set_write_percent(struct kvm_vm *vm, uint32_t write_percent);
void memstress_set_random_seed(struct kvm_vm *vm, uint32_t random_seed);
void memstress_set_random_access(struct kvm_vm *vm, bool random_access); void memstress_set_random_access(struct kvm_vm *vm, bool random_access);
void memstress_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct memstress_vcpu_args *)); void memstress_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct memstress_vcpu_args *));
......
...@@ -91,9 +91,17 @@ struct guest_random_state { ...@@ -91,9 +91,17 @@ struct guest_random_state {
uint32_t seed; uint32_t seed;
}; };
extern uint32_t guest_random_seed;
extern struct guest_random_state guest_rng;
struct guest_random_state new_guest_random_state(uint32_t seed); struct guest_random_state new_guest_random_state(uint32_t seed);
uint32_t guest_random_u32(struct guest_random_state *state); uint32_t guest_random_u32(struct guest_random_state *state);
static inline uint64_t guest_random_u64(struct guest_random_state *state)
{
return ((uint64_t)guest_random_u32(state) << 32) | guest_random_u32(state);
}
enum vm_mem_backing_src_type { enum vm_mem_backing_src_type {
VM_MEM_SRC_ANONYMOUS, VM_MEM_SRC_ANONYMOUS,
VM_MEM_SRC_ANONYMOUS_THP, VM_MEM_SRC_ANONYMOUS_THP,
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#define KVM_UTIL_MIN_PFN 2 #define KVM_UTIL_MIN_PFN 2
uint32_t guest_random_seed;
struct guest_random_state guest_rng;
static int vcpu_mmap_sz(void); static int vcpu_mmap_sz(void);
int open_path_or_exit(const char *path, int flags) int open_path_or_exit(const char *path, int flags)
...@@ -430,6 +433,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus, ...@@ -430,6 +433,10 @@ struct kvm_vm *__vm_create(struct vm_shape shape, uint32_t nr_runnable_vcpus,
slot0 = memslot2region(vm, 0); slot0 = memslot2region(vm, 0);
ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size); ucall_init(vm, slot0->region.guest_phys_addr + slot0->region.memory_size);
pr_info("Random seed: 0x%x\n", guest_random_seed);
guest_rng = new_guest_random_state(guest_random_seed);
sync_global_to_guest(vm, guest_rng);
kvm_arch_vm_post_create(vm); kvm_arch_vm_post_create(vm);
return vm; return vm;
...@@ -2303,6 +2310,8 @@ void __attribute((constructor)) kvm_selftest_init(void) ...@@ -2303,6 +2310,8 @@ void __attribute((constructor)) kvm_selftest_init(void)
/* Tell stdout not to buffer its content. */ /* Tell stdout not to buffer its content. */
setbuf(stdout, NULL); setbuf(stdout, NULL);
guest_random_seed = random();
kvm_selftest_arch_init(); kvm_selftest_arch_init();
} }
......
...@@ -54,7 +54,7 @@ void memstress_guest_code(uint32_t vcpu_idx) ...@@ -54,7 +54,7 @@ void memstress_guest_code(uint32_t vcpu_idx)
uint64_t page; uint64_t page;
int i; int i;
rand_state = new_guest_random_state(args->random_seed + vcpu_idx); rand_state = new_guest_random_state(guest_random_seed + vcpu_idx);
gva = vcpu_args->gva; gva = vcpu_args->gva;
pages = vcpu_args->pages; pages = vcpu_args->pages;
...@@ -241,12 +241,6 @@ void memstress_set_write_percent(struct kvm_vm *vm, uint32_t write_percent) ...@@ -241,12 +241,6 @@ void memstress_set_write_percent(struct kvm_vm *vm, uint32_t write_percent)
sync_global_to_guest(vm, memstress_args.write_percent); sync_global_to_guest(vm, memstress_args.write_percent);
} }
void memstress_set_random_seed(struct kvm_vm *vm, uint32_t random_seed)
{
memstress_args.random_seed = random_seed;
sync_global_to_guest(vm, memstress_args.random_seed);
}
void memstress_set_random_access(struct kvm_vm *vm, bool random_access) void memstress_set_random_access(struct kvm_vm *vm, bool random_access)
{ {
memstress_args.random_access = random_access; memstress_args.random_access = random_access;
......
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