ACK: [SRU][Bionic][PATCH 1/1] s390/archrandom: Rework arch random implementation.
Stefan Bader
stefan.bader at canonical.com
Fri Jun 15 14:50:06 UTC 2018
On 13.06.2018 18:04, Joseph Salisbury wrote:
> From: Harald Freudenberger <freude at de.ibm.com>
>
> BugLink: http://bugs.launchpad.net/bugs/1775391
>
> The arch_get_random_seed_long() invocation done by the random device
> driver is done in interrupt context and may be invoked very very
> frequently. The existing s390 arch_get_random_seed*() implementation
> uses the PRNO(TRNG) instruction which produces excellent high quality
> entropy but is relatively slow and thus expensive.
>
> This fix reworks the arch_get_random_seed* implementation. It
> introduces a buffer concept to decouple the delivery of random data
> via arch_get_random_seed*() from the generation of new random
> bytes. The buffer of random data is filled asynchronously by a
> workqueue thread.
> If there are enough bytes in the buffer the s390_arch_random_generate()
> just delivers these bytes. Otherwise false is returned until the worker
> thread refills the buffer.
> The worker fills the rng buffer by pulling fresh entropy from the
> high quality (but slow) true hardware random generator. This entropy
> is then spread over the buffer with an pseudo random generator.
> As the arch_get_random_seed_long() fetches 8 bytes and the calling
> function add_interrupt_randomness() counts this as 1 bit entropy the
> distribution needs to make sure there is in fact 1 bit entropy
> contained in 8 bytes of the buffer. The current values pull 32 byte
> entropy and scatter this into a 2048 byte buffer. So 8 byte in the
> buffer will contain 1 bit of entropy.
> The worker thread is rescheduled based on the charge level of the
> buffer but at least with 500 ms delay to avoid too much cpu consumption.
> So the max. amount of rng data delivered via arch_get_random_seed is
> limited to 4Kb per second.
>
> Signed-off-by: Harald Freudenberger <freude at de.ibm.com>
> Reviewed-by: Patrick Steuer <patrick.steuer at de.ibm.com>
> Signed-off-by: Martin Schwidefsky <schwidefsky at de.ibm.com>
> (cherry picked from commit 966f53e750aedc5f59f9ccae6bbfb8f671c7c842)
> Signed-off-by: Joseph Salisbury <joseph.salisbury at canonical.com>
Acked-by: Stefan Bader <stefan.bader at canonical.com>
> ---
> arch/s390/crypto/arch_random.c | 103 +++++++++++++++++++++++++++++++++++--
> arch/s390/include/asm/archrandom.h | 13 ++---
> 2 files changed, 102 insertions(+), 14 deletions(-)
>
> diff --git a/arch/s390/crypto/arch_random.c b/arch/s390/crypto/arch_random.c
> index 8720e92..dd95cdbd 100644
> --- a/arch/s390/crypto/arch_random.c
> +++ b/arch/s390/crypto/arch_random.c
> @@ -2,14 +2,37 @@
> /*
> * s390 arch random implementation.
> *
> - * Copyright IBM Corp. 2017
> - * Author(s): Harald Freudenberger <freude at de.ibm.com>
> + * Copyright IBM Corp. 2017, 2018
> + * Author(s): Harald Freudenberger
> + *
> + * The s390_arch_random_generate() function may be called from random.c
> + * in interrupt context. So this implementation does the best to be very
> + * fast. There is a buffer of random data which is asynchronously checked
> + * and filled by a workqueue thread.
> + * If there are enough bytes in the buffer the s390_arch_random_generate()
> + * just delivers these bytes. Otherwise false is returned until the
> + * worker thread refills the buffer.
> + * The worker fills the rng buffer by pulling fresh entropy from the
> + * high quality (but slow) true hardware random generator. This entropy
> + * is then spread over the buffer with an pseudo random generator PRNG.
> + * As the arch_get_random_seed_long() fetches 8 bytes and the calling
> + * function add_interrupt_randomness() counts this as 1 bit entropy the
> + * distribution needs to make sure there is in fact 1 bit entropy contained
> + * in 8 bytes of the buffer. The current values pull 32 byte entropy
> + * and scatter this into a 2048 byte buffer. So 8 byte in the buffer
> + * will contain 1 bit of entropy.
> + * The worker thread is rescheduled based on the charge level of the
> + * buffer but at least with 500 ms delay to avoid too much CPU consumption.
> + * So the max. amount of rng data delivered via arch_get_random_seed is
> + * limited to 4k bytes per second.
> */
>
> #include <linux/kernel.h>
> #include <linux/atomic.h>
> #include <linux/random.h>
> +#include <linux/slab.h>
> #include <linux/static_key.h>
> +#include <linux/workqueue.h>
> #include <asm/cpacf.h>
>
> DEFINE_STATIC_KEY_FALSE(s390_arch_random_available);
> @@ -17,11 +40,83 @@ DEFINE_STATIC_KEY_FALSE(s390_arch_random_available);
> atomic64_t s390_arch_random_counter = ATOMIC64_INIT(0);
> EXPORT_SYMBOL(s390_arch_random_counter);
>
> +#define ARCH_REFILL_TICKS (HZ/2)
> +#define ARCH_PRNG_SEED_SIZE 32
> +#define ARCH_RNG_BUF_SIZE 2048
> +
> +static DEFINE_SPINLOCK(arch_rng_lock);
> +static u8 *arch_rng_buf;
> +static unsigned int arch_rng_buf_idx;
> +
> +static void arch_rng_refill_buffer(struct work_struct *);
> +static DECLARE_DELAYED_WORK(arch_rng_work, arch_rng_refill_buffer);
> +
> +bool s390_arch_random_generate(u8 *buf, unsigned int nbytes)
> +{
> + /* lock rng buffer */
> + if (!spin_trylock(&arch_rng_lock))
> + return false;
> +
> + /* try to resolve the requested amount of bytes from the buffer */
> + arch_rng_buf_idx -= nbytes;
> + if (arch_rng_buf_idx < ARCH_RNG_BUF_SIZE) {
> + memcpy(buf, arch_rng_buf + arch_rng_buf_idx, nbytes);
> + atomic64_add(nbytes, &s390_arch_random_counter);
> + spin_unlock(&arch_rng_lock);
> + return true;
> + }
> +
> + /* not enough bytes in rng buffer, refill is done asynchronously */
> + spin_unlock(&arch_rng_lock);
> +
> + return false;
> +}
> +EXPORT_SYMBOL(s390_arch_random_generate);
> +
> +static void arch_rng_refill_buffer(struct work_struct *unused)
> +{
> + unsigned int delay = ARCH_REFILL_TICKS;
> +
> + spin_lock(&arch_rng_lock);
> + if (arch_rng_buf_idx > ARCH_RNG_BUF_SIZE) {
> + /* buffer is exhausted and needs refill */
> + u8 seed[ARCH_PRNG_SEED_SIZE];
> + u8 prng_wa[240];
> + /* fetch ARCH_PRNG_SEED_SIZE bytes of entropy */
> + cpacf_trng(NULL, 0, seed, sizeof(seed));
> + /* blow this entropy up to ARCH_RNG_BUF_SIZE with PRNG */
> + memset(prng_wa, 0, sizeof(prng_wa));
> + cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED,
> + &prng_wa, NULL, 0, seed, sizeof(seed));
> + cpacf_prno(CPACF_PRNO_SHA512_DRNG_GEN,
> + &prng_wa, arch_rng_buf, ARCH_RNG_BUF_SIZE, NULL, 0);
> + arch_rng_buf_idx = ARCH_RNG_BUF_SIZE;
> + }
> + delay += (ARCH_REFILL_TICKS * arch_rng_buf_idx) / ARCH_RNG_BUF_SIZE;
> + spin_unlock(&arch_rng_lock);
> +
> + /* kick next check */
> + queue_delayed_work(system_long_wq, &arch_rng_work, delay);
> +}
> +
> static int __init s390_arch_random_init(void)
> {
> - /* check if subfunction CPACF_PRNO_TRNG is available */
> - if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG))
> + /* all the needed PRNO subfunctions available ? */
> + if (cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG) &&
> + cpacf_query_func(CPACF_PRNO, CPACF_PRNO_SHA512_DRNG_GEN)) {
> +
> + /* alloc arch random working buffer */
> + arch_rng_buf = kmalloc(ARCH_RNG_BUF_SIZE, GFP_KERNEL);
> + if (!arch_rng_buf)
> + return -ENOMEM;
> +
> + /* kick worker queue job to fill the random buffer */
> + queue_delayed_work(system_long_wq,
> + &arch_rng_work, ARCH_REFILL_TICKS);
> +
> + /* enable arch random to the outside world */
> static_branch_enable(&s390_arch_random_available);
> + }
>
> return 0;
> }
> diff --git a/arch/s390/include/asm/archrandom.h b/arch/s390/include/asm/archrandom.h
> index 09aed10..c67b82d 100644
> --- a/arch/s390/include/asm/archrandom.h
> +++ b/arch/s390/include/asm/archrandom.h
> @@ -15,16 +15,11 @@
>
> #include <linux/static_key.h>
> #include <linux/atomic.h>
> -#include <asm/cpacf.h>
>
> DECLARE_STATIC_KEY_FALSE(s390_arch_random_available);
> extern atomic64_t s390_arch_random_counter;
>
> -static void s390_arch_random_generate(u8 *buf, unsigned int nbytes)
> -{
> - cpacf_trng(NULL, 0, buf, nbytes);
> - atomic64_add(nbytes, &s390_arch_random_counter);
> -}
> +bool s390_arch_random_generate(u8 *buf, unsigned int nbytes);
>
> static inline bool arch_has_random(void)
> {
> @@ -51,8 +46,7 @@ static inline bool arch_get_random_int(unsigned int *v)
> static inline bool arch_get_random_seed_long(unsigned long *v)
> {
> if (static_branch_likely(&s390_arch_random_available)) {
> - s390_arch_random_generate((u8 *)v, sizeof(*v));
> - return true;
> + return s390_arch_random_generate((u8 *)v, sizeof(*v));
> }
> return false;
> }
> @@ -60,8 +54,7 @@ static inline bool arch_get_random_seed_long(unsigned long *v)
> static inline bool arch_get_random_seed_int(unsigned int *v)
> {
> if (static_branch_likely(&s390_arch_random_available)) {
> - s390_arch_random_generate((u8 *)v, sizeof(*v));
> - return true;
> + return s390_arch_random_generate((u8 *)v, sizeof(*v));
> }
> return false;
> }
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <https://lists.ubuntu.com/archives/kernel-team/attachments/20180615/d04b8001/attachment.sig>
More information about the kernel-team
mailing list