Commit 3092adce authored by Jason A. Donenfeld's avatar Jason A. Donenfeld

random: unify batched entropy implementations

There are currently two separate batched entropy implementations, for
u32 and u64, with nearly identical code, with the goal of avoiding
unaligned memory accesses and letting the buffers be used more
efficiently. Having to maintain these two functions independently is a
bit of a hassle though, considering that they always need to be kept in
sync.

This commit factors them out into a type-generic macro, so that the
expansion produces the same code as before, such that diffing the
assembly shows no differences. This will also make it easier in the
future to add u16 and u8 batches.

This was initially tested using an always_inline function and letting
gcc constant fold the type size in, but the code gen was less efficient,
and in general it was more verbose and harder to follow. So this patch
goes with the boring macro solution, similar to what's already done for
the _wait functions in random.h.

Cc: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: default avatarJason A. Donenfeld <Jason@zx2c4.com>
parent 5ad7dd88
...@@ -460,99 +460,62 @@ static ssize_t get_random_bytes_user(void __user *ubuf, size_t len) ...@@ -460,99 +460,62 @@ static ssize_t get_random_bytes_user(void __user *ubuf, size_t len)
* provided by this function is okay, the function wait_for_random_bytes() * provided by this function is okay, the function wait_for_random_bytes()
* should be called and return 0 at least once at any point prior. * should be called and return 0 at least once at any point prior.
*/ */
struct batched_entropy {
union {
/*
* We make this 1.5x a ChaCha block, so that we get the
* remaining 32 bytes from fast key erasure, plus one full
* block from the detached ChaCha state. We can increase
* the size of this later if needed so long as we keep the
* formula of (integer_blocks + 0.5) * CHACHA_BLOCK_SIZE.
*/
u64 entropy_u64[CHACHA_BLOCK_SIZE * 3 / (2 * sizeof(u64))];
u32 entropy_u32[CHACHA_BLOCK_SIZE * 3 / (2 * sizeof(u32))];
};
local_lock_t lock;
unsigned long generation;
unsigned int position;
};
static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u64) = {
.lock = INIT_LOCAL_LOCK(batched_entropy_u64.lock),
.position = UINT_MAX
};
u64 get_random_u64(void)
{
u64 ret;
unsigned long flags;
struct batched_entropy *batch;
unsigned long next_gen;
warn_unseeded_randomness();
if (!crng_ready()) {
_get_random_bytes(&ret, sizeof(ret));
return ret;
}
local_lock_irqsave(&batched_entropy_u64.lock, flags);
batch = raw_cpu_ptr(&batched_entropy_u64);
next_gen = READ_ONCE(base_crng.generation);
if (batch->position >= ARRAY_SIZE(batch->entropy_u64) ||
next_gen != batch->generation) {
_get_random_bytes(batch->entropy_u64, sizeof(batch->entropy_u64));
batch->position = 0;
batch->generation = next_gen;
}
ret = batch->entropy_u64[batch->position];
batch->entropy_u64[batch->position] = 0;
++batch->position;
local_unlock_irqrestore(&batched_entropy_u64.lock, flags);
return ret;
}
EXPORT_SYMBOL(get_random_u64);
static DEFINE_PER_CPU(struct batched_entropy, batched_entropy_u32) = {
.lock = INIT_LOCAL_LOCK(batched_entropy_u32.lock),
.position = UINT_MAX
};
u32 get_random_u32(void) #define DEFINE_BATCHED_ENTROPY(type) \
{ struct batch_ ##type { \
u32 ret; /* \
unsigned long flags; * We make this 1.5x a ChaCha block, so that we get the \
struct batched_entropy *batch; * remaining 32 bytes from fast key erasure, plus one full \
unsigned long next_gen; * block from the detached ChaCha state. We can increase \
* the size of this later if needed so long as we keep the \
warn_unseeded_randomness(); * formula of (integer_blocks + 0.5) * CHACHA_BLOCK_SIZE. \
*/ \
if (!crng_ready()) { type entropy[CHACHA_BLOCK_SIZE * 3 / (2 * sizeof(type))]; \
_get_random_bytes(&ret, sizeof(ret)); local_lock_t lock; \
return ret; unsigned long generation; \
} unsigned int position; \
}; \
local_lock_irqsave(&batched_entropy_u32.lock, flags); \
batch = raw_cpu_ptr(&batched_entropy_u32); static DEFINE_PER_CPU(struct batch_ ##type, batched_entropy_ ##type) = { \
.lock = INIT_LOCAL_LOCK(batched_entropy_ ##type.lock), \
next_gen = READ_ONCE(base_crng.generation); .position = UINT_MAX \
if (batch->position >= ARRAY_SIZE(batch->entropy_u32) || }; \
next_gen != batch->generation) { \
_get_random_bytes(batch->entropy_u32, sizeof(batch->entropy_u32)); type get_random_ ##type(void) \
batch->position = 0; { \
batch->generation = next_gen; type ret; \
} unsigned long flags; \
struct batch_ ##type *batch; \
ret = batch->entropy_u32[batch->position]; unsigned long next_gen; \
batch->entropy_u32[batch->position] = 0; \
++batch->position; warn_unseeded_randomness(); \
local_unlock_irqrestore(&batched_entropy_u32.lock, flags); \
return ret; if (!crng_ready()) { \
} _get_random_bytes(&ret, sizeof(ret)); \
EXPORT_SYMBOL(get_random_u32); return ret; \
} \
\
local_lock_irqsave(&batched_entropy_ ##type.lock, flags); \
batch = raw_cpu_ptr(&batched_entropy_##type); \
\
next_gen = READ_ONCE(base_crng.generation); \
if (batch->position >= ARRAY_SIZE(batch->entropy) || \
next_gen != batch->generation) { \
_get_random_bytes(batch->entropy, sizeof(batch->entropy)); \
batch->position = 0; \
batch->generation = next_gen; \
} \
\
ret = batch->entropy[batch->position]; \
batch->entropy[batch->position] = 0; \
++batch->position; \
local_unlock_irqrestore(&batched_entropy_ ##type.lock, flags); \
return ret; \
} \
EXPORT_SYMBOL(get_random_ ##type);
DEFINE_BATCHED_ENTROPY(u64)
DEFINE_BATCHED_ENTROPY(u32)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* /*
......
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