Message ID | 20221122161152.293072-12-mlevitsk@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | kvm-unit-tests: set of fixes and new tests | expand |
On Tue, 22 Nov 2022 18:11:36 +0200 Maxim Levitsky <mlevitsk@redhat.com> wrote: > Add a simple pseudo random number generator which can be used > in the tests to add randomeness in a controlled manner. ahh, yes I have wanted something like this in the library for quite some time! thanks! I have some comments regarding the interfaces (see below), and also a request, if you could split the x86 part in a different patch, so we can have a "pure" lib patch, and then you can have an x86-only patch that uses the new interface > > For x86 add a wrapper which initializes the PRNG with RDRAND, > unless RANDOM_SEED env variable is set, in which case it is used > instead. > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> > --- > Makefile | 3 ++- > README.md | 1 + > lib/prng.c | 41 +++++++++++++++++++++++++++++++++++++++++ > lib/prng.h | 23 +++++++++++++++++++++++ > lib/x86/random.c | 33 +++++++++++++++++++++++++++++++++ > lib/x86/random.h | 17 +++++++++++++++++ > scripts/arch-run.bash | 2 +- > x86/Makefile.common | 1 + > 8 files changed, 119 insertions(+), 2 deletions(-) > create mode 100644 lib/prng.c > create mode 100644 lib/prng.h > create mode 100644 lib/x86/random.c > create mode 100644 lib/x86/random.h > > diff --git a/Makefile b/Makefile > index 6ed5deac..384b5acf 100644 > --- a/Makefile > +++ b/Makefile > @@ -29,7 +29,8 @@ cflatobjs := \ > lib/string.o \ > lib/abort.o \ > lib/report.o \ > - lib/stack.o > + lib/stack.o \ > + lib/prng.o > > # libfdt paths > LIBFDT_objdir = lib/libfdt > diff --git a/README.md b/README.md > index 6e82dc22..5a677a03 100644 > --- a/README.md > +++ b/README.md > @@ -91,6 +91,7 @@ the framework. The list of reserved environment variables is below > QEMU_ACCEL either kvm, hvf or tcg > QEMU_VERSION_STRING string of the form `qemu -h | head -1` > KERNEL_VERSION_STRING string of the form `uname -r` > + TEST_SEED integer to force a fixed seed for the prng > > Additionally these self-explanatory variables are reserved > > diff --git a/lib/prng.c b/lib/prng.c > new file mode 100644 > index 00000000..d9342eb3 > --- /dev/null > +++ b/lib/prng.c > @@ -0,0 +1,41 @@ > + > +/* > + * Random number generator that is usable from guest code. This is the > + * Park-Miller LCG using standard constants. > + */ > + > +#include "libcflat.h" > +#include "prng.h" > + > +struct random_state new_random_state(uint32_t seed) > +{ > + struct random_state s = {.seed = seed}; > + return s; > +} > + > +uint32_t random_u32(struct random_state *state) > +{ > + state->seed = (uint64_t)state->seed * 48271 % ((uint32_t)(1 << 31) - 1); why not: state->seed = state->seed * 48271ULL % (BIT_ULL(31) - 1); I think it's more readable > + return state->seed; > +} > + > + > +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max) > +{ > + uint32_t val = random_u32(state); > + > + return val % (max - min + 1) + min; what happens if max == UINT_MAX and min = 0 ? maybe: if (max - min == UINT_MAX) return val; > +} > + > +/* > + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, > + * it will return true in 70.0% of cases) > + */ > +bool random_decision(struct random_state *state, float percent_true) I'm not a fan of floats in the lib... > +{ > + if (percent_true == 0) > + return 0; > + if (percent_true == 100) > + return 1; > + return random_range(state, 1, 10000) < (uint32_t)(percent_true * 100); ...especially when you are only using 2 decimal places anyway can you rewrite it to take an unsigned int? e.g. if percent_true = 7123, it will return true in 71.23% of the cases then you can rewrite the last line like this: return random_range(state, 1, 10000) < percent_true; > +} > diff --git a/lib/prng.h b/lib/prng.h > new file mode 100644 > index 00000000..61d3a48b > --- /dev/null > +++ b/lib/prng.h > @@ -0,0 +1,23 @@ > + > +#ifndef SRC_LIB_PRNG_H_ > +#define SRC_LIB_PRNG_H_ > + > +struct random_state { > + uint32_t seed; > +}; > + > +struct random_state new_random_state(uint32_t seed); > +uint32_t random_u32(struct random_state *state); > + > +/* > + * return a random number from min to max (included) > + */ > +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max); > + > +/* > + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, > + * it will return true in 70.0% of cases) > + */ > +bool random_decision(struct random_state *state, float percent_true); > + > +#endif /* SRC_LIB_PRNG_H_ */ and then put the rest below in a new patch > diff --git a/lib/x86/random.c b/lib/x86/random.c > new file mode 100644 > index 00000000..fcdd5fe8 > --- /dev/null > +++ b/lib/x86/random.c > @@ -0,0 +1,33 @@ > + > +#include "libcflat.h" > +#include "processor.h" > +#include "prng.h" > +#include "smp.h" > +#include "asm/spinlock.h" > +#include "random.h" > + > +static u32 test_seed; > +static bool initialized; > + > +void init_prng(void) > +{ > + char *test_seed_str = getenv("TEST_SEED"); > + > + if (test_seed_str && strlen(test_seed_str)) > + test_seed = atol(test_seed_str); > + else > +#ifdef __x86_64__ > + test_seed = (u32)rdrand(); > +#else > + test_seed = (u32)(rdtsc() << 4); > +#endif > + initialized = true; > + > + printf("Test seed: %u\n", (unsigned int)test_seed); > +} > + > +struct random_state get_prng(void) > +{ > + assert(initialized); > + return new_random_state(test_seed + this_cpu_read_smp_id()); > +} > diff --git a/lib/x86/random.h b/lib/x86/random.h > new file mode 100644 > index 00000000..795b450b > --- /dev/null > +++ b/lib/x86/random.h > @@ -0,0 +1,17 @@ > +/* > + * prng.h > + * > + * Created on: Nov 9, 2022 > + * Author: mlevitsk > + */ > + > +#ifndef SRC_LIB_X86_RANDOM_H_ > +#define SRC_LIB_X86_RANDOM_H_ > + > +#include "libcflat.h" > +#include "prng.h" > + > +void init_prng(void); > +struct random_state get_prng(void); > + > +#endif /* SRC_LIB_X86_RANDOM_H_ */ > diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash > index 51e4b97b..238d19f8 100644 > --- a/scripts/arch-run.bash > +++ b/scripts/arch-run.bash > @@ -298,7 +298,7 @@ env_params () > KERNEL_EXTRAVERSION=${KERNEL_EXTRAVERSION%%[!0-9]*} > ! [[ $KERNEL_SUBLEVEL =~ ^[0-9]+$ ]] && unset $KERNEL_SUBLEVEL > ! [[ $KERNEL_EXTRAVERSION =~ ^[0-9]+$ ]] && unset $KERNEL_EXTRAVERSION > - env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION > + env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION TEST_SEED > } > > env_file () > diff --git a/x86/Makefile.common b/x86/Makefile.common > index 698a48ab..fa0a50e6 100644 > --- a/x86/Makefile.common > +++ b/x86/Makefile.common > @@ -23,6 +23,7 @@ cflatobjs += lib/x86/stack.o > cflatobjs += lib/x86/fault_test.o > cflatobjs += lib/x86/delay.o > cflatobjs += lib/x86/pmu.o > +cflatobjs += lib/x86/random.o > ifeq ($(CONFIG_EFI),y) > cflatobjs += lib/x86/amd_sev.o > cflatobjs += lib/efi.o
On Wed, Nov 23, 2022 at 10:28:50AM +0100, Claudio Imbrenda wrote: > On Tue, 22 Nov 2022 18:11:36 +0200 > Maxim Levitsky <mlevitsk@redhat.com> wrote: > > > Add a simple pseudo random number generator which can be used > > in the tests to add randomeness in a controlled manner. > > ahh, yes I have wanted something like this in the library for quite some > time! thanks! Here's another approach that we unfortunately never got merged. https://lore.kernel.org/all/20211202115352.951548-5-alex.bennee@linaro.org/ Thanks, drew
On Wed, 2022-11-23 at 13:54 +0100, Andrew Jones wrote: > On Wed, Nov 23, 2022 at 10:28:50AM +0100, Claudio Imbrenda wrote: > > On Tue, 22 Nov 2022 18:11:36 +0200 > > Maxim Levitsky <mlevitsk@redhat.com> wrote: > > > > > Add a simple pseudo random number generator which can be used > > > in the tests to add randomeness in a controlled manner. > > > > ahh, yes I have wanted something like this in the library for quite some > > time! thanks! > > Here's another approach that we unfortunately never got merged. > https://lore.kernel.org/all/20211202115352.951548-5-alex.bennee@linaro.org/ > > Thanks, > drew > Looks like a better version of the generator I took (I took the same as is proposed for in-kernel selftests). It is reallly pity that pathes are getting lost like that, we should not need to do double work. Best regards, Maxim Levitsky
On Wed, 2022-11-23 at 10:28 +0100, Claudio Imbrenda wrote: > On Tue, 22 Nov 2022 18:11:36 +0200 > Maxim Levitsky <mlevitsk@redhat.com> wrote: > > > Add a simple pseudo random number generator which can be used > > in the tests to add randomeness in a controlled manner. > > ahh, yes I have wanted something like this in the library for quite some > time! thanks! > > I have some comments regarding the interfaces (see below), and also a > request, if you could split the x86 part in a different patch, so we > can have a "pure" lib patch, and then you can have an x86-only patch > that uses the new interface > > > For x86 add a wrapper which initializes the PRNG with RDRAND, > > unless RANDOM_SEED env variable is set, in which case it is used > > instead. > > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> > > --- > > Makefile | 3 ++- > > README.md | 1 + > > lib/prng.c | 41 +++++++++++++++++++++++++++++++++++++++++ > > lib/prng.h | 23 +++++++++++++++++++++++ > > lib/x86/random.c | 33 +++++++++++++++++++++++++++++++++ > > lib/x86/random.h | 17 +++++++++++++++++ > > scripts/arch-run.bash | 2 +- > > x86/Makefile.common | 1 + > > 8 files changed, 119 insertions(+), 2 deletions(-) > > create mode 100644 lib/prng.c > > create mode 100644 lib/prng.h > > create mode 100644 lib/x86/random.c > > create mode 100644 lib/x86/random.h > > > > diff --git a/Makefile b/Makefile > > index 6ed5deac..384b5acf 100644 > > --- a/Makefile > > +++ b/Makefile > > @@ -29,7 +29,8 @@ cflatobjs := \ > > lib/string.o \ > > lib/abort.o \ > > lib/report.o \ > > - lib/stack.o > > + lib/stack.o \ > > + lib/prng.o > > > > # libfdt paths > > LIBFDT_objdir = lib/libfdt > > diff --git a/README.md b/README.md > > index 6e82dc22..5a677a03 100644 > > --- a/README.md > > +++ b/README.md > > @@ -91,6 +91,7 @@ the framework. The list of reserved environment variables is below > > QEMU_ACCEL either kvm, hvf or tcg > > QEMU_VERSION_STRING string of the form `qemu -h | head -1` > > KERNEL_VERSION_STRING string of the form `uname -r` > > + TEST_SEED integer to force a fixed seed for the prng > > > > Additionally these self-explanatory variables are reserved > > > > diff --git a/lib/prng.c b/lib/prng.c > > new file mode 100644 > > index 00000000..d9342eb3 > > --- /dev/null > > +++ b/lib/prng.c > > @@ -0,0 +1,41 @@ > > + > > +/* > > + * Random number generator that is usable from guest code. This is the > > + * Park-Miller LCG using standard constants. > > + */ > > + > > +#include "libcflat.h" > > +#include "prng.h" > > + > > +struct random_state new_random_state(uint32_t seed) > > +{ > > + struct random_state s = {.seed = seed}; > > + return s; > > +} > > + > > +uint32_t random_u32(struct random_state *state) > > +{ > > + state->seed = (uint64_t)state->seed * 48271 % ((uint32_t)(1 << 31) - 1); > > why not: > > state->seed = state->seed * 48271ULL % (BIT_ULL(31) - 1); > > I think it's more readable I copied this code vertabium from a patch that was send to in-kernel selftests as Sean suggested me to do. I to be honest would have picked some more complex random generator like the Mersenne Twister or something like that, since performance is not an issue here, and this generator is I think geared toward beeing as fast as possible. But againg I don't care much about this, any source of randomness is better that nothing. > > > + return state->seed; > > +} > > + > > + > > +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max) > > +{ > > + uint32_t val = random_u32(state); > > + > > + return val % (max - min + 1) + min; > > what happens if max == UINT_MAX and min = 0 ? > > maybe: > > if (max - min == UINT_MAX) > return val; Makes sense. > > > +} > > + > > +/* > > + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, > > + * it will return true in 70.0% of cases) > > + */ > > +bool random_decision(struct random_state *state, float percent_true) > > I'm not a fan of floats in the lib... > > > +{ > > + if (percent_true == 0) > > + return 0; > > + if (percent_true == 100) > > + return 1; > > + return random_range(state, 1, 10000) < (uint32_t)(percent_true * 100); > > ...especially when you are only using 2 decimal places anyway I was thinking the same about this, there are pros and cons, Using a fixed point integer is a bit less usable but overall I don't mind using it. > > can you rewrite it to take an unsigned int? > e.g. if percent_true = 7123, it will return true in 71.23% of the cases > > then you can rewrite the last line like this: > > return random_range(state, 1, 10000) < percent_true; > > > +} > > diff --git a/lib/prng.h b/lib/prng.h > > new file mode 100644 > > index 00000000..61d3a48b > > --- /dev/null > > +++ b/lib/prng.h > > @@ -0,0 +1,23 @@ > > + > > +#ifndef SRC_LIB_PRNG_H_ > > +#define SRC_LIB_PRNG_H_ > > + > > +struct random_state { > > + uint32_t seed; > > +}; > > + > > +struct random_state new_random_state(uint32_t seed); > > +uint32_t random_u32(struct random_state *state); > > + > > +/* > > + * return a random number from min to max (included) > > + */ > > +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max); > > + > > +/* > > + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, > > + * it will return true in 70.0% of cases) > > + */ > > +bool random_decision(struct random_state *state, float percent_true); > > + > > +#endif /* SRC_LIB_PRNG_H_ */ > > and then put the rest below in a new patch No problem. Note that x86 specific bits can be minimized but that requires plumbing in several things that you would take for granted, and in particular, env vars are x86 specific, and apic id is x86 specific. When a random number generator is wired to a new arch, this can be fixed. Best regards, Maxim Levitsky > > > diff --git a/lib/x86/random.c b/lib/x86/random.c > > new file mode 100644 > > index 00000000..fcdd5fe8 > > --- /dev/null > > +++ b/lib/x86/random.c > > @@ -0,0 +1,33 @@ > > + > > +#include "libcflat.h" > > +#include "processor.h" > > +#include "prng.h" > > +#include "smp.h" > > +#include "asm/spinlock.h" > > +#include "random.h" > > + > > +static u32 test_seed; > > +static bool initialized; > > + > > +void init_prng(void) > > +{ > > + char *test_seed_str = getenv("TEST_SEED"); > > + > > + if (test_seed_str && strlen(test_seed_str)) > > + test_seed = atol(test_seed_str); > > + else > > +#ifdef __x86_64__ > > + test_seed = (u32)rdrand(); > > +#else > > + test_seed = (u32)(rdtsc() << 4); > > +#endif > > + initialized = true; > > + > > + printf("Test seed: %u\n", (unsigned int)test_seed); > > +} > > + > > +struct random_state get_prng(void) > > +{ > > + assert(initialized); > > + return new_random_state(test_seed + this_cpu_read_smp_id()); > > +} > > diff --git a/lib/x86/random.h b/lib/x86/random.h > > new file mode 100644 > > index 00000000..795b450b > > --- /dev/null > > +++ b/lib/x86/random.h > > @@ -0,0 +1,17 @@ > > +/* > > + * prng.h > > + * > > + * Created on: Nov 9, 2022 > > + * Author: mlevitsk > > + */ > > + > > +#ifndef SRC_LIB_X86_RANDOM_H_ > > +#define SRC_LIB_X86_RANDOM_H_ > > + > > +#include "libcflat.h" > > +#include "prng.h" > > + > > +void init_prng(void); > > +struct random_state get_prng(void); > > + > > +#endif /* SRC_LIB_X86_RANDOM_H_ */ > > diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash > > index 51e4b97b..238d19f8 100644 > > --- a/scripts/arch-run.bash > > +++ b/scripts/arch-run.bash > > @@ -298,7 +298,7 @@ env_params () > > KERNEL_EXTRAVERSION=${KERNEL_EXTRAVERSION%%[!0-9]*} > > ! [[ $KERNEL_SUBLEVEL =~ ^[0-9]+$ ]] && unset $KERNEL_SUBLEVEL > > ! [[ $KERNEL_EXTRAVERSION =~ ^[0-9]+$ ]] && unset $KERNEL_EXTRAVERSION > > - env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION > > + env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION TEST_SEED > > } > > > > env_file () > > diff --git a/x86/Makefile.common b/x86/Makefile.common > > index 698a48ab..fa0a50e6 100644 > > --- a/x86/Makefile.common > > +++ b/x86/Makefile.common > > @@ -23,6 +23,7 @@ cflatobjs += lib/x86/stack.o > > cflatobjs += lib/x86/fault_test.o > > cflatobjs += lib/x86/delay.o > > cflatobjs += lib/x86/pmu.o > > +cflatobjs += lib/x86/random.o > > ifeq ($(CONFIG_EFI),y) > > cflatobjs += lib/x86/amd_sev.o > > cflatobjs += lib/efi.o
On Tue, 06 Dec 2022 16:07:39 +0200 Maxim Levitsky <mlevitsk@redhat.com> wrote: > On Wed, 2022-11-23 at 10:28 +0100, Claudio Imbrenda wrote: > > On Tue, 22 Nov 2022 18:11:36 +0200 > > Maxim Levitsky <mlevitsk@redhat.com> wrote: > > > > > Add a simple pseudo random number generator which can be used > > > in the tests to add randomeness in a controlled manner. > > > > ahh, yes I have wanted something like this in the library for quite some > > time! thanks! > > > > I have some comments regarding the interfaces (see below), and also a > > request, if you could split the x86 part in a different patch, so we > > can have a "pure" lib patch, and then you can have an x86-only patch > > that uses the new interface > > > > > For x86 add a wrapper which initializes the PRNG with RDRAND, > > > unless RANDOM_SEED env variable is set, in which case it is used > > > instead. > > > > > > Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> > > > --- > > > Makefile | 3 ++- > > > README.md | 1 + > > > lib/prng.c | 41 +++++++++++++++++++++++++++++++++++++++++ > > > lib/prng.h | 23 +++++++++++++++++++++++ > > > lib/x86/random.c | 33 +++++++++++++++++++++++++++++++++ > > > lib/x86/random.h | 17 +++++++++++++++++ > > > scripts/arch-run.bash | 2 +- > > > x86/Makefile.common | 1 + > > > 8 files changed, 119 insertions(+), 2 deletions(-) > > > create mode 100644 lib/prng.c > > > create mode 100644 lib/prng.h > > > create mode 100644 lib/x86/random.c > > > create mode 100644 lib/x86/random.h > > > > > > diff --git a/Makefile b/Makefile > > > index 6ed5deac..384b5acf 100644 > > > --- a/Makefile > > > +++ b/Makefile > > > @@ -29,7 +29,8 @@ cflatobjs := \ > > > lib/string.o \ > > > lib/abort.o \ > > > lib/report.o \ > > > - lib/stack.o > > > + lib/stack.o \ > > > + lib/prng.o > > > > > > # libfdt paths > > > LIBFDT_objdir = lib/libfdt > > > diff --git a/README.md b/README.md > > > index 6e82dc22..5a677a03 100644 > > > --- a/README.md > > > +++ b/README.md > > > @@ -91,6 +91,7 @@ the framework. The list of reserved environment variables is below > > > QEMU_ACCEL either kvm, hvf or tcg > > > QEMU_VERSION_STRING string of the form `qemu -h | head -1` > > > KERNEL_VERSION_STRING string of the form `uname -r` > > > + TEST_SEED integer to force a fixed seed for the prng > > > > > > Additionally these self-explanatory variables are reserved > > > > > > diff --git a/lib/prng.c b/lib/prng.c > > > new file mode 100644 > > > index 00000000..d9342eb3 > > > --- /dev/null > > > +++ b/lib/prng.c > > > @@ -0,0 +1,41 @@ > > > + > > > +/* > > > + * Random number generator that is usable from guest code. This is the > > > + * Park-Miller LCG using standard constants. > > > + */ > > > + > > > +#include "libcflat.h" > > > +#include "prng.h" > > > + > > > +struct random_state new_random_state(uint32_t seed) > > > +{ > > > + struct random_state s = {.seed = seed}; > > > + return s; > > > +} > > > + > > > +uint32_t random_u32(struct random_state *state) > > > +{ > > > + state->seed = (uint64_t)state->seed * 48271 % ((uint32_t)(1 << 31) - 1); > > > > why not: > > > > state->seed = state->seed * 48271ULL % (BIT_ULL(31) - 1); > > > > I think it's more readable > > I copied this code vertabium from a patch that was send to in-kernel selftests > as Sean suggested me to do. fair enough :) > > I to be honest would have picked some more complex random generator like the > Mersenne Twister or something like that, since performance is not an issue here, > and this generator is I think geared toward beeing as fast as possible. I think that the important thing is that the generator is random enough to confuse the various branch predictors and prefetchers. If the code is simple, it's even better, because it's easier to understand. > > But againg I don't care much about this, any source of randomness is better > that nothing. exactly > > > > > > + return state->seed; > > > +} > > > + > > > + > > > +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max) > > > +{ > > > + uint32_t val = random_u32(state); > > > + > > > + return val % (max - min + 1) + min; > > > > what happens if max == UINT_MAX and min = 0 ? > > > > maybe: > > > > if (max - min == UINT_MAX) > > return val; > > Makes sense. > > > > > +} > > > + > > > +/* > > > + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, > > > + * it will return true in 70.0% of cases) > > > + */ > > > +bool random_decision(struct random_state *state, float percent_true) > > > > I'm not a fan of floats in the lib... > > > > > +{ > > > + if (percent_true == 0) > > > + return 0; > > > + if (percent_true == 100) > > > + return 1; > > > + return random_range(state, 1, 10000) < (uint32_t)(percent_true * 100); > > > > ...especially when you are only using 2 decimal places anyway > > I was thinking the same about this, there are pros and cons, > Using a fixed point integer is a bit less usable but overall I don't mind > using it. maybe you can add a wrapper macro? #define RANDOM_DECISION_F(state, percent) \ random_decision((state), 100 * (percent)) would be functionally equivalent (I think), but only generate fp code when actually used. (feel free to chose a nicer name for it) > > > > > > > can you rewrite it to take an unsigned int? > > e.g. if percent_true = 7123, it will return true in 71.23% of the cases > > > > then you can rewrite the last line like this: > > > > return random_range(state, 1, 10000) < percent_true; > > > > > +} > > > diff --git a/lib/prng.h b/lib/prng.h > > > new file mode 100644 > > > index 00000000..61d3a48b > > > --- /dev/null > > > +++ b/lib/prng.h > > > @@ -0,0 +1,23 @@ > > > + > > > +#ifndef SRC_LIB_PRNG_H_ > > > +#define SRC_LIB_PRNG_H_ > > > + > > > +struct random_state { > > > + uint32_t seed; > > > +}; > > > + > > > +struct random_state new_random_state(uint32_t seed); > > > +uint32_t random_u32(struct random_state *state); > > > + > > > +/* > > > + * return a random number from min to max (included) > > > + */ > > > +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max); > > > + > > > +/* > > > + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, > > > + * it will return true in 70.0% of cases) > > > + */ > > > +bool random_decision(struct random_state *state, float percent_true); > > > + > > > +#endif /* SRC_LIB_PRNG_H_ */ > > > > and then put the rest below in a new patch > > No problem. Note that x86 specific bits can be minimized > but that requires plumbing in several things that you would > take for granted, and in particular, env vars > are x86 specific, and apic id is x86 specific. I'm not sure I understand your point. The lib code above does not depend on the arch code below, so you can split this into a non-arch patch, and an arch patch that uses the non-arch patch. I think it's cleaner to not mix common code and arch code. > > When a random number generator is wired to a new arch, > this can be fixed. > > Best regards, > Maxim Levitsky > > > > > > diff --git a/lib/x86/random.c b/lib/x86/random.c > > > new file mode 100644 > > > index 00000000..fcdd5fe8 > > > --- /dev/null > > > +++ b/lib/x86/random.c > > > @@ -0,0 +1,33 @@ > > > + > > > +#include "libcflat.h" > > > +#include "processor.h" > > > +#include "prng.h" > > > +#include "smp.h" > > > +#include "asm/spinlock.h" > > > +#include "random.h" > > > + > > > +static u32 test_seed; > > > +static bool initialized; > > > + > > > +void init_prng(void) > > > +{ > > > + char *test_seed_str = getenv("TEST_SEED"); > > > + > > > + if (test_seed_str && strlen(test_seed_str)) > > > + test_seed = atol(test_seed_str); > > > + else > > > +#ifdef __x86_64__ > > > + test_seed = (u32)rdrand(); > > > +#else > > > + test_seed = (u32)(rdtsc() << 4); > > > +#endif > > > + initialized = true; > > > + > > > + printf("Test seed: %u\n", (unsigned int)test_seed); > > > +} > > > + > > > +struct random_state get_prng(void) > > > +{ > > > + assert(initialized); > > > + return new_random_state(test_seed + this_cpu_read_smp_id()); > > > +} > > > diff --git a/lib/x86/random.h b/lib/x86/random.h > > > new file mode 100644 > > > index 00000000..795b450b > > > --- /dev/null > > > +++ b/lib/x86/random.h > > > @@ -0,0 +1,17 @@ > > > +/* > > > + * prng.h > > > + * > > > + * Created on: Nov 9, 2022 > > > + * Author: mlevitsk > > > + */ > > > + > > > +#ifndef SRC_LIB_X86_RANDOM_H_ > > > +#define SRC_LIB_X86_RANDOM_H_ > > > + > > > +#include "libcflat.h" > > > +#include "prng.h" > > > + > > > +void init_prng(void); > > > +struct random_state get_prng(void); > > > + > > > +#endif /* SRC_LIB_X86_RANDOM_H_ */ > > > diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash > > > index 51e4b97b..238d19f8 100644 > > > --- a/scripts/arch-run.bash > > > +++ b/scripts/arch-run.bash > > > @@ -298,7 +298,7 @@ env_params () > > > KERNEL_EXTRAVERSION=${KERNEL_EXTRAVERSION%%[!0-9]*} > > > ! [[ $KERNEL_SUBLEVEL =~ ^[0-9]+$ ]] && unset $KERNEL_SUBLEVEL > > > ! [[ $KERNEL_EXTRAVERSION =~ ^[0-9]+$ ]] && unset $KERNEL_EXTRAVERSION > > > - env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION > > > + env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION TEST_SEED > > > } > > > > > > env_file () > > > diff --git a/x86/Makefile.common b/x86/Makefile.common > > > index 698a48ab..fa0a50e6 100644 > > > --- a/x86/Makefile.common > > > +++ b/x86/Makefile.common > > > @@ -23,6 +23,7 @@ cflatobjs += lib/x86/stack.o > > > cflatobjs += lib/x86/fault_test.o > > > cflatobjs += lib/x86/delay.o > > > cflatobjs += lib/x86/pmu.o > > > +cflatobjs += lib/x86/random.o > > > ifeq ($(CONFIG_EFI),y) > > > cflatobjs += lib/x86/amd_sev.o > > > cflatobjs += lib/efi.o > >
diff --git a/Makefile b/Makefile index 6ed5deac..384b5acf 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,8 @@ cflatobjs := \ lib/string.o \ lib/abort.o \ lib/report.o \ - lib/stack.o + lib/stack.o \ + lib/prng.o # libfdt paths LIBFDT_objdir = lib/libfdt diff --git a/README.md b/README.md index 6e82dc22..5a677a03 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ the framework. The list of reserved environment variables is below QEMU_ACCEL either kvm, hvf or tcg QEMU_VERSION_STRING string of the form `qemu -h | head -1` KERNEL_VERSION_STRING string of the form `uname -r` + TEST_SEED integer to force a fixed seed for the prng Additionally these self-explanatory variables are reserved diff --git a/lib/prng.c b/lib/prng.c new file mode 100644 index 00000000..d9342eb3 --- /dev/null +++ b/lib/prng.c @@ -0,0 +1,41 @@ + +/* + * Random number generator that is usable from guest code. This is the + * Park-Miller LCG using standard constants. + */ + +#include "libcflat.h" +#include "prng.h" + +struct random_state new_random_state(uint32_t seed) +{ + struct random_state s = {.seed = seed}; + return s; +} + +uint32_t random_u32(struct random_state *state) +{ + state->seed = (uint64_t)state->seed * 48271 % ((uint32_t)(1 << 31) - 1); + return state->seed; +} + + +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max) +{ + uint32_t val = random_u32(state); + + return val % (max - min + 1) + min; +} + +/* + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, + * it will return true in 70.0% of cases) + */ +bool random_decision(struct random_state *state, float percent_true) +{ + if (percent_true == 0) + return 0; + if (percent_true == 100) + return 1; + return random_range(state, 1, 10000) < (uint32_t)(percent_true * 100); +} diff --git a/lib/prng.h b/lib/prng.h new file mode 100644 index 00000000..61d3a48b --- /dev/null +++ b/lib/prng.h @@ -0,0 +1,23 @@ + +#ifndef SRC_LIB_PRNG_H_ +#define SRC_LIB_PRNG_H_ + +struct random_state { + uint32_t seed; +}; + +struct random_state new_random_state(uint32_t seed); +uint32_t random_u32(struct random_state *state); + +/* + * return a random number from min to max (included) + */ +uint32_t random_range(struct random_state *state, uint32_t min, uint32_t max); + +/* + * Returns true randomly in 'percent_true' cases (e.g if percent_true = 70.0, + * it will return true in 70.0% of cases) + */ +bool random_decision(struct random_state *state, float percent_true); + +#endif /* SRC_LIB_PRNG_H_ */ diff --git a/lib/x86/random.c b/lib/x86/random.c new file mode 100644 index 00000000..fcdd5fe8 --- /dev/null +++ b/lib/x86/random.c @@ -0,0 +1,33 @@ + +#include "libcflat.h" +#include "processor.h" +#include "prng.h" +#include "smp.h" +#include "asm/spinlock.h" +#include "random.h" + +static u32 test_seed; +static bool initialized; + +void init_prng(void) +{ + char *test_seed_str = getenv("TEST_SEED"); + + if (test_seed_str && strlen(test_seed_str)) + test_seed = atol(test_seed_str); + else +#ifdef __x86_64__ + test_seed = (u32)rdrand(); +#else + test_seed = (u32)(rdtsc() << 4); +#endif + initialized = true; + + printf("Test seed: %u\n", (unsigned int)test_seed); +} + +struct random_state get_prng(void) +{ + assert(initialized); + return new_random_state(test_seed + this_cpu_read_smp_id()); +} diff --git a/lib/x86/random.h b/lib/x86/random.h new file mode 100644 index 00000000..795b450b --- /dev/null +++ b/lib/x86/random.h @@ -0,0 +1,17 @@ +/* + * prng.h + * + * Created on: Nov 9, 2022 + * Author: mlevitsk + */ + +#ifndef SRC_LIB_X86_RANDOM_H_ +#define SRC_LIB_X86_RANDOM_H_ + +#include "libcflat.h" +#include "prng.h" + +void init_prng(void); +struct random_state get_prng(void); + +#endif /* SRC_LIB_X86_RANDOM_H_ */ diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash index 51e4b97b..238d19f8 100644 --- a/scripts/arch-run.bash +++ b/scripts/arch-run.bash @@ -298,7 +298,7 @@ env_params () KERNEL_EXTRAVERSION=${KERNEL_EXTRAVERSION%%[!0-9]*} ! [[ $KERNEL_SUBLEVEL =~ ^[0-9]+$ ]] && unset $KERNEL_SUBLEVEL ! [[ $KERNEL_EXTRAVERSION =~ ^[0-9]+$ ]] && unset $KERNEL_EXTRAVERSION - env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION + env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION TEST_SEED } env_file () diff --git a/x86/Makefile.common b/x86/Makefile.common index 698a48ab..fa0a50e6 100644 --- a/x86/Makefile.common +++ b/x86/Makefile.common @@ -23,6 +23,7 @@ cflatobjs += lib/x86/stack.o cflatobjs += lib/x86/fault_test.o cflatobjs += lib/x86/delay.o cflatobjs += lib/x86/pmu.o +cflatobjs += lib/x86/random.o ifeq ($(CONFIG_EFI),y) cflatobjs += lib/x86/amd_sev.o cflatobjs += lib/efi.o
Add a simple pseudo random number generator which can be used in the tests to add randomeness in a controlled manner. For x86 add a wrapper which initializes the PRNG with RDRAND, unless RANDOM_SEED env variable is set, in which case it is used instead. Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> --- Makefile | 3 ++- README.md | 1 + lib/prng.c | 41 +++++++++++++++++++++++++++++++++++++++++ lib/prng.h | 23 +++++++++++++++++++++++ lib/x86/random.c | 33 +++++++++++++++++++++++++++++++++ lib/x86/random.h | 17 +++++++++++++++++ scripts/arch-run.bash | 2 +- x86/Makefile.common | 1 + 8 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 lib/prng.c create mode 100644 lib/prng.h create mode 100644 lib/x86/random.c create mode 100644 lib/x86/random.h