2022-04-19 15:39:33

by Jason A. Donenfeld

[permalink] [raw]
Subject: [PATCH v5 00/11] archs/random: fallback to best raw ktime when no cycle counter

Hi folks,

The RNG uses a function called random_get_entropy() basically anytime
that it needs to timestamp an event. For example, an interrupt comes in,
and we mix a random_get_entropy() into the entropy pool somehow.
Somebody mashes their keyboard or moves their mouse around? We mix a
random_get_entropy() into the entropy pool. It's one of the main
varieties of input.

Unfortunately, it's always 0 on a few platforms. The RNG has accumulated
various hacks to deal with this, but in general it's not great. Surely
we can do better than 0. In fact, *anything* that's not the same exact
value all the time would be better than 0. Even a counter that
increments once per hour would be better than 0! I think you get the
idea.

On most platforms, random_get_entropy() is aliased to get_cycles(),
which makes sense for platforms where get_cycles() is defined. RDTSC,
for example, has all the characteristics we care about for this
function: it's fast to acquire (i.e. acceptable in an irq handler),
pretty high precision, available, forms a 2-monotone distribution, etc.
But for platforms without that, what is the next best thing?

Sometimes the next best thing is architecture-defined. For example,
really old MIPS has the C0 random register, which isn't quite a cycle
counter, but is at least something. However, some platforms don't even
have an architecture-defined fallback.

Fortunately, the timekeeping subsystem has already solved this problem
of trying to determine what the least bad clock is on constrained
systems, falling back to jiffies in the worst case. By exporting the raw
clock, we can get a decent fallback function for when there's no cycle
counter or architecture-specific function.

This series makes the RNG more useful on: m68k, RISC-V, MIPS, ARM32,
NIOS II, SPARC32, Xtensa, and Usermode Linux. Previously these platforms
would, in certain circumstances, but out of luck with regards to having
any type of event timestamping source in the RNG.

Finally, note that this series isn't about "jitter entropy" or other
ways of initializing the RNG. That's a different topic for a different
thread. Please don't let this discussion veer off into that. Here, I'm
just trying to find a good fallback counter/timer for platforms without
get_cycles(), a question with limited scope.

If this (or a future revision) looks good to you all and receives the
requisite acks, my plan was to take these through the random.git tree
for 5.19, so that I can then build on top of it.

Thanks,
Jason

Changes v4->v5:
- Do not prototype symbol with 'extern', according to style guide.
- On MIPS, combine random_get_entropy_fallback() with the c0 random
register in a way that matches the format of the c0 random value, so
that we get the best of a high precision cycle counter and of larger
period timer, joined together. As a result, Thomas Bogendoerfer's
ack on v4 of patch 4 has been dropped, since this is a substantial
change.

Changes v3->v4:
- Use EXPORT_SYMBOL_GPL instead of EXPORT_SYMBOL.

Changes v2->v3:
- Name the fallback function random_get_entropy_fallback(), so that it
can be changed out as needed.
- Include header with prototype in timekeeping.c to avoid compiler
warning.
- Export fallback function symbol.

Changes v1->v2:
- Use ktime_read_raw_clock() instead of sched_clock(), per Thomas'
suggestion.
- Drop arm64 change.
- Cleanup header inclusion ordering problem.

Cc: Thomas Gleixner <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Theodore Ts'o <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Russell King <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Geert Uytterhoeven <[email protected]>
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Paul Walmsley <[email protected]>
Cc: Palmer Dabbelt <[email protected]>
Cc: Albert Ou <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Richard Weinberger <[email protected]>
Cc: Anton Ivanov <[email protected]>
Cc: Johannes Berg <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: H. Peter Anvin <[email protected]>
Cc: Chris Zankel <[email protected]>
Cc: Max Filippov <[email protected]>
Cc: Stephen Boyd <[email protected]>
Cc: Dinh Nguyen <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]

Jason A. Donenfeld (11):
timekeeping: add raw clock fallback for random_get_entropy()
m68k: use fallback for random_get_entropy() instead of zero
riscv: use fallback for random_get_entropy() instead of zero
mips: use fallback for random_get_entropy() instead of just c0 random
arm: use fallback for random_get_entropy() instead of zero
nios2: use fallback for random_get_entropy() instead of zero
x86: use fallback for random_get_entropy() instead of zero
um: use fallback for random_get_entropy() instead of zero
sparc: use fallback for random_get_entropy() instead of zero
xtensa: use fallback for random_get_entropy() instead of zero
random: insist on random_get_entropy() existing in order to simplify

arch/arm/include/asm/timex.h | 1 +
arch/m68k/include/asm/timex.h | 2 +-
arch/mips/include/asm/timex.h | 16 +++---
arch/nios2/include/asm/timex.h | 2 +
arch/riscv/include/asm/timex.h | 2 +-
arch/sparc/include/asm/timex_32.h | 4 +-
arch/um/include/asm/timex.h | 9 +---
arch/x86/include/asm/timex.h | 10 ++++
arch/xtensa/include/asm/timex.h | 6 +--
drivers/char/random.c | 89 ++++++++++---------------------
include/linux/timex.h | 8 +++
kernel/time/timekeeping.c | 10 ++++
12 files changed, 74 insertions(+), 85 deletions(-)

--
2.35.1


2022-04-19 23:49:51

by Jason A. Donenfeld

[permalink] [raw]
Subject: [PATCH v5 02/11] m68k: use fallback for random_get_entropy() instead of zero

In the event that random_get_entropy() can't access a cycle counter or
similar, falling back to returning 0 is really not the best we can do.
Instead, at least calling random_get_entropy_fallback() would be
preferable, because that always needs to return _something_, even
falling back to jiffies eventually. It's not as though
random_get_entropy_fallback() is super high precision or guaranteed to
be entropic, but basically anything that's not zero all the time is
better than returning zero all the time.

Cc: Thomas Gleixner <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Acked-by: Geert Uytterhoeven <[email protected]>
Signed-off-by: Jason A. Donenfeld <[email protected]>
---
arch/m68k/include/asm/timex.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/m68k/include/asm/timex.h b/arch/m68k/include/asm/timex.h
index 6a21d9358280..f4a7a340f4ca 100644
--- a/arch/m68k/include/asm/timex.h
+++ b/arch/m68k/include/asm/timex.h
@@ -35,7 +35,7 @@ static inline unsigned long random_get_entropy(void)
{
if (mach_random_get_entropy)
return mach_random_get_entropy();
- return 0;
+ return random_get_entropy_fallback();
}
#define random_get_entropy random_get_entropy

--
2.35.1

2022-04-20 02:55:27

by Jason A. Donenfeld

[permalink] [raw]
Subject: [PATCH v5 10/11] xtensa: use fallback for random_get_entropy() instead of zero

In the event that random_get_entropy() can't access a cycle counter or
similar, falling back to returning 0 is really not the best we can do.
Instead, at least calling random_get_entropy_fallback() would be
preferable, because that always needs to return _something_, even
falling back to jiffies eventually. It's not as though
random_get_entropy_fallback() is super high precision or guaranteed to
be entropic, but basically anything that's not zero all the time is
better than returning zero all the time.

This is accomplished by just including the asm-generic code like on
other architectures, which means we can get rid of the empty stub
function here.

Cc: Thomas Gleixner <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Max Filippov <[email protected]>
Signed-off-by: Jason A. Donenfeld <[email protected]>
---
arch/xtensa/include/asm/timex.h | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/arch/xtensa/include/asm/timex.h b/arch/xtensa/include/asm/timex.h
index 233ec75e60c6..3f2462f2d027 100644
--- a/arch/xtensa/include/asm/timex.h
+++ b/arch/xtensa/include/asm/timex.h
@@ -29,10 +29,6 @@

extern unsigned long ccount_freq;

-typedef unsigned long long cycles_t;
-
-#define get_cycles() (0)
-
void local_timer_setup(unsigned cpu);

/*
@@ -59,4 +55,6 @@ static inline void set_linux_timer (unsigned long ccompare)
xtensa_set_sr(ccompare, SREG_CCOMPARE + LINUX_TIMER);
}

+#include <asm-generic/timex.h>
+
#endif /* _XTENSA_TIMEX_H */
--
2.35.1

2022-04-21 07:43:40

by Jason A. Donenfeld

[permalink] [raw]
Subject: [PATCH v5 08/11] um: use fallback for random_get_entropy() instead of zero

In the event that random_get_entropy() can't access a cycle counter or
similar, falling back to returning 0 is really not the best we can do.
Instead, at least calling random_get_entropy_fallback() would be
preferable, because that always needs to return _something_, even
falling back to jiffies eventually. It's not as though
random_get_entropy_fallback() is super high precision or guaranteed to
be entropic, but basically anything that's not zero all the time is
better than returning zero all the time.

This is accomplished by just including the asm-generic code like on
other architectures, which means we can get rid of the empty stub
function here.

Cc: Thomas Gleixner <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Richard Weinberger <[email protected]>
Cc: Anton Ivanov <[email protected]>
Cc: Johannes Berg <[email protected]>
Signed-off-by: Jason A. Donenfeld <[email protected]>
---
arch/um/include/asm/timex.h | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/arch/um/include/asm/timex.h b/arch/um/include/asm/timex.h
index e392a9a5bc9b..9f27176adb26 100644
--- a/arch/um/include/asm/timex.h
+++ b/arch/um/include/asm/timex.h
@@ -2,13 +2,8 @@
#ifndef __UM_TIMEX_H
#define __UM_TIMEX_H

-typedef unsigned long cycles_t;
-
-static inline cycles_t get_cycles (void)
-{
- return 0;
-}
-
#define CLOCK_TICK_RATE (HZ)

+#include <asm-generic/timex.h>
+
#endif
--
2.35.1

2022-04-22 18:03:27

by Max Filippov

[permalink] [raw]
Subject: Re: [PATCH v5 10/11] xtensa: use fallback for random_get_entropy() instead of zero

On Tue, Apr 19, 2022 at 4:18 AM Jason A. Donenfeld <[email protected]> wrote:
>
> In the event that random_get_entropy() can't access a cycle counter or
> similar, falling back to returning 0 is really not the best we can do.
> Instead, at least calling random_get_entropy_fallback() would be
> preferable, because that always needs to return _something_, even
> falling back to jiffies eventually. It's not as though
> random_get_entropy_fallback() is super high precision or guaranteed to
> be entropic, but basically anything that's not zero all the time is
> better than returning zero all the time.
>
> This is accomplished by just including the asm-generic code like on
> other architectures, which means we can get rid of the empty stub
> function here.
>
> Cc: Thomas Gleixner <[email protected]>
> Cc: Arnd Bergmann <[email protected]>
> Cc: Max Filippov <[email protected]>
> Signed-off-by: Jason A. Donenfeld <[email protected]>
> ---
> arch/xtensa/include/asm/timex.h | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)

Acked-by: Max Filippov <[email protected]>

--
Thanks.
-- Max

2022-04-22 20:17:13

by Jason A. Donenfeld

[permalink] [raw]
Subject: [PATCH v5 04/11] mips: use fallback for random_get_entropy() instead of just c0 random

For situations in which we don't have a c0 counter register available,
we've been falling back to reading the c0 "random" register, which is
usually bounded by the amount of TLB entries and changes every other
cycle or so. This means it wraps extremely often. We can do better by
combining this fast-changing counter with a potentially slower-changing
counter from random_get_entropy_fallback() in the more significant bits.
This commit combines the two, taking into account that the changing bits
are in a different bit position depending on the CPU model. In addition,
we previously were falling back to 0 for ancient CPUs that Linux does
not support anyway; remove that dead path entirely.

Cc: Thomas Gleixner <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Thomas Bogendoerfer <[email protected]>
Cc: Maciej W. Rozycki <[email protected]>
Signed-off-by: Jason A. Donenfeld <[email protected]>
---
ThomasB - I dropped your Ack from v4, because this is pretty different
from v4 now.

Maciej - you mentioned you had a test rig. Think you could provide a
"Tested-by" if this approach works?

arch/mips/include/asm/timex.h | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/arch/mips/include/asm/timex.h b/arch/mips/include/asm/timex.h
index b05bb70a2e46..e3f5460a923b 100644
--- a/arch/mips/include/asm/timex.h
+++ b/arch/mips/include/asm/timex.h
@@ -80,21 +80,19 @@ static inline cycles_t get_cycles(void)
/*
* Like get_cycles - but where c0_count is not available we desperately
* use c0_random in an attempt to get at least a little bit of entropy.
- *
- * R6000 and R6000A neither have a count register nor a random register.
- * That leaves no entropy source in the CPU itself.
*/
static inline unsigned long random_get_entropy(void)
{
- unsigned int prid = read_c0_prid();
- unsigned int imp = prid & PRID_IMP_MASK;
+ unsigned int c0_random;

- if (can_use_mips_counter(prid))
+ if (can_use_mips_counter(read_c0_prid()))
return read_c0_count();
- else if (likely(imp != PRID_IMP_R6000 && imp != PRID_IMP_R6000A))
- return read_c0_random();
+
+ if (cpu_has_3kex)
+ c0_random = (read_c0_random() >> 8) & 0x3f;
else
- return 0; /* no usable register */
+ c0_random = read_c0_random() & 0x3f;
+ return (random_get_entropy_fallback() << 6) | (0x3f - c0_random);
}
#define random_get_entropy random_get_entropy

--
2.35.1