random: credit architectural init the exact amount
[linux-2.6-microblaze.git] / drivers / char / random.c
index c1763bc..2906259 100644 (file)
  *********************************************************************/
 
 /*
- * crng_init =  0 --> Uninitialized
- *             1 --> Initialized
- *             2 --> Initialized from input_pool
- *
  * crng_init is protected by base_crng->lock, and only increases
- * its value (from 0->1->2).
+ * its value (from empty->early->ready).
  */
-static int crng_init = 0;
-#define crng_ready() (likely(crng_init > 1))
-/* Various types of waiters for crng_init->2 transition. */
+static enum {
+       CRNG_EMPTY = 0, /* Little to no entropy collected */
+       CRNG_EARLY = 1, /* At least POOL_EARLY_BITS collected */
+       CRNG_READY = 2  /* Fully initialized with POOL_READY_BITS collected */
+} crng_init = CRNG_EMPTY;
+#define crng_ready() (likely(crng_init >= CRNG_READY))
+/* Various types of waiters for crng_init->CRNG_READY transition. */
 static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait);
 static struct fasync_struct *fasync;
 static DEFINE_SPINLOCK(random_ready_chain_lock);
 static RAW_NOTIFIER_HEAD(random_ready_chain);
 
 /* Control how we warn userspace. */
-static struct ratelimit_state unseeded_warning =
-       RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3);
 static struct ratelimit_state urandom_warning =
        RATELIMIT_STATE_INIT("warn_urandom_randomness", HZ, 3);
-static int ratelimit_disable __read_mostly;
+static int ratelimit_disable __read_mostly =
+       IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM);
 module_param_named(ratelimit_disable, ratelimit_disable, int, 0644);
 MODULE_PARM_DESC(ratelimit_disable, "Disable random ratelimit suppression");
 
@@ -182,27 +181,15 @@ static void process_random_ready_list(void)
        spin_unlock_irqrestore(&random_ready_chain_lock, flags);
 }
 
-#define warn_unseeded_randomness(previous) \
-       _warn_unseeded_randomness(__func__, (void *)_RET_IP_, (previous))
+#define warn_unseeded_randomness() \
+       _warn_unseeded_randomness(__func__, (void *)_RET_IP_)
 
-static void _warn_unseeded_randomness(const char *func_name, void *caller, void **previous)
+static void _warn_unseeded_randomness(const char *func_name, void *caller)
 {
-#ifdef CONFIG_WARN_ALL_UNSEEDED_RANDOM
-       const bool print_once = false;
-#else
-       static bool print_once __read_mostly;
-#endif
-
-       if (print_once || crng_ready() ||
-           (previous && (caller == READ_ONCE(*previous))))
+       if (!IS_ENABLED(CONFIG_WARN_ALL_UNSEEDED_RANDOM) || crng_ready())
                return;
-       WRITE_ONCE(*previous, caller);
-#ifndef CONFIG_WARN_ALL_UNSEEDED_RANDOM
-       print_once = true;
-#endif
-       if (__ratelimit(&unseeded_warning))
-               printk_deferred(KERN_NOTICE "random: %s called from %pS with crng_init=%d\n",
-                               func_name, caller, crng_init);
+       printk_deferred(KERN_NOTICE "random: %s called from %pS with crng_init=%d\n",
+                       func_name, caller, crng_init);
 }
 
 
@@ -265,7 +252,6 @@ static void crng_reseed(void)
        unsigned long flags;
        unsigned long next_gen;
        u8 key[CHACHA_KEY_SIZE];
-       bool finalize_init = false;
 
        extract_entropy(key, sizeof(key));
 
@@ -282,28 +268,10 @@ static void crng_reseed(void)
                ++next_gen;
        WRITE_ONCE(base_crng.generation, next_gen);
        WRITE_ONCE(base_crng.birth, jiffies);
-       if (!crng_ready()) {
-               crng_init = 2;
-               finalize_init = true;
-       }
+       if (!crng_ready())
+               crng_init = CRNG_READY;
        spin_unlock_irqrestore(&base_crng.lock, flags);
        memzero_explicit(key, sizeof(key));
-       if (finalize_init) {
-               process_random_ready_list();
-               wake_up_interruptible(&crng_init_wait);
-               kill_fasync(&fasync, SIGIO, POLL_IN);
-               pr_notice("crng init done\n");
-               if (unseeded_warning.missed) {
-                       pr_notice("%d get_random_xx warning(s) missed due to ratelimiting\n",
-                                 unseeded_warning.missed);
-                       unseeded_warning.missed = 0;
-               }
-               if (urandom_warning.missed) {
-                       pr_notice("%d urandom warning(s) missed due to ratelimiting\n",
-                                 urandom_warning.missed);
-                       urandom_warning.missed = 0;
-               }
-       }
 }
 
 /*
@@ -357,7 +325,7 @@ static bool crng_has_old_seed(void)
                        interval = max_t(unsigned int, CRNG_RESEED_START_INTERVAL,
                                         (unsigned int)uptime / 2 * HZ);
        }
-       return time_after(jiffies, READ_ONCE(base_crng.birth) + interval);
+       return time_is_before_jiffies(READ_ONCE(base_crng.birth) + interval);
 }
 
 /*
@@ -377,7 +345,7 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS],
         * For the fast path, we check whether we're ready, unlocked first, and
         * then re-check once locked later. In the case where we're really not
         * ready, we do fast key erasure with the base_crng directly, extracting
-        * when crng_init==0.
+        * when crng_init is CRNG_EMPTY.
         */
        if (!crng_ready()) {
                bool ready;
@@ -385,7 +353,7 @@ static void crng_make_state(u32 chacha_state[CHACHA_STATE_WORDS],
                spin_lock_irqsave(&base_crng.lock, flags);
                ready = crng_ready();
                if (!ready) {
-                       if (crng_init == 0)
+                       if (crng_init == CRNG_EMPTY)
                                extract_entropy(base_crng.key, sizeof(base_crng.key));
                        crng_fast_key_erasure(base_crng.key, chacha_state,
                                              random_data, random_data_len);
@@ -474,9 +442,7 @@ static void _get_random_bytes(void *buf, size_t nbytes)
  */
 void get_random_bytes(void *buf, size_t nbytes)
 {
-       static void *previous;
-
-       warn_unseeded_randomness(&previous);
+       warn_unseeded_randomness();
        _get_random_bytes(buf, nbytes);
 }
 EXPORT_SYMBOL(get_random_bytes);
@@ -572,10 +538,9 @@ u64 get_random_u64(void)
        u64 ret;
        unsigned long flags;
        struct batched_entropy *batch;
-       static void *previous;
        unsigned long next_gen;
 
-       warn_unseeded_randomness(&previous);
+       warn_unseeded_randomness();
 
        if  (!crng_ready()) {
                _get_random_bytes(&ret, sizeof(ret));
@@ -611,10 +576,9 @@ u32 get_random_u32(void)
        u32 ret;
        unsigned long flags;
        struct batched_entropy *batch;
-       static void *previous;
        unsigned long next_gen;
 
-       warn_unseeded_randomness(&previous);
+       warn_unseeded_randomness();
 
        if  (!crng_ready()) {
                _get_random_bytes(&ret, sizeof(ret));
@@ -739,8 +703,8 @@ EXPORT_SYMBOL(get_random_bytes_arch);
 
 enum {
        POOL_BITS = BLAKE2S_HASH_SIZE * 8,
-       POOL_INIT_BITS = POOL_BITS, /* No point in settling for less. */
-       POOL_FAST_INIT_BITS = POOL_INIT_BITS / 2
+       POOL_READY_BITS = POOL_BITS, /* When crng_init->CRNG_READY */
+       POOL_EARLY_BITS = POOL_READY_BITS / 2 /* When crng_init->CRNG_EARLY */
 };
 
 static struct {
@@ -822,7 +786,7 @@ static void extract_entropy(void *buf, size_t nbytes)
 
 static void credit_init_bits(size_t nbits)
 {
-       unsigned int init_bits, orig, add;
+       unsigned int new, orig, add;
        unsigned long flags;
 
        if (crng_ready() || !nbits)
@@ -832,16 +796,24 @@ static void credit_init_bits(size_t nbits)
 
        do {
                orig = READ_ONCE(input_pool.init_bits);
-               init_bits = min_t(unsigned int, POOL_BITS, orig + add);
-       } while (cmpxchg(&input_pool.init_bits, orig, init_bits) != orig);
+               new = min_t(unsigned int, POOL_BITS, orig + add);
+       } while (cmpxchg(&input_pool.init_bits, orig, new) != orig);
 
-       if (!crng_ready() && init_bits >= POOL_INIT_BITS)
-               crng_reseed();
-       else if (unlikely(crng_init == 0 && init_bits >= POOL_FAST_INIT_BITS)) {
+       if (orig < POOL_READY_BITS && new >= POOL_READY_BITS) {
+               crng_reseed(); /* Sets crng_init to CRNG_READY under base_crng.lock. */
+               process_random_ready_list();
+               wake_up_interruptible(&crng_init_wait);
+               kill_fasync(&fasync, SIGIO, POLL_IN);
+               pr_notice("crng init done\n");
+               if (urandom_warning.missed)
+                       pr_notice("%d urandom warning(s) missed due to ratelimiting\n",
+                                 urandom_warning.missed);
+       } else if (orig < POOL_EARLY_BITS && new >= POOL_EARLY_BITS) {
                spin_lock_irqsave(&base_crng.lock, flags);
-               if (crng_init == 0) {
+               /* Check if crng_init is CRNG_EMPTY, to avoid race with crng_reseed(). */
+               if (crng_init == CRNG_EMPTY) {
                        extract_entropy(base_crng.key, sizeof(base_crng.key));
-                       crng_init = 1;
+                       crng_init = CRNG_EARLY;
                }
                spin_unlock_irqrestore(&base_crng.lock, flags);
        }
@@ -947,16 +919,16 @@ static struct notifier_block pm_notifier = { .notifier_call = random_pm_notifica
 
 /*
  * The first collection of entropy occurs at system boot while interrupts
- * are still turned off. Here we push in RDSEED, a timestamp, and utsname().
- * Depending on the above configuration knob, RDSEED may be considered
- * sufficient for initialization. Note that much earlier setup may already
- * have pushed entropy into the input pool by the time we get here.
+ * are still turned off. Here we push in latent entropy, RDSEED, a timestamp,
+ * utsname(), and the command line. Depending on the above configuration knob,
+ * RDSEED may be considered sufficient for initialization. Note that much
+ * earlier setup may already have pushed entropy into the input pool by the
+ * time we get here.
  */
-int __init rand_initialize(void)
+int __init random_init(const char *command_line)
 {
-       size_t i;
        ktime_t now = ktime_get_real();
-       bool arch_init = true;
+       unsigned int i, arch_bytes;
        unsigned long rv;
 
 #if defined(LATENT_ENTROPY_PLUGIN)
@@ -964,26 +936,24 @@ int __init rand_initialize(void)
        _mix_pool_bytes(compiletime_seed, sizeof(compiletime_seed));
 #endif
 
-       for (i = 0; i < BLAKE2S_BLOCK_SIZE; i += sizeof(rv)) {
+       for (i = 0, arch_bytes = BLAKE2S_BLOCK_SIZE;
+            i < BLAKE2S_BLOCK_SIZE; i += sizeof(rv)) {
                if (!arch_get_random_seed_long_early(&rv) &&
                    !arch_get_random_long_early(&rv)) {
                        rv = random_get_entropy();
-                       arch_init = false;
+                       arch_bytes -= sizeof(rv);
                }
                _mix_pool_bytes(&rv, sizeof(rv));
        }
        _mix_pool_bytes(&now, sizeof(now));
        _mix_pool_bytes(utsname(), sizeof(*(utsname())));
+       _mix_pool_bytes(command_line, strlen(command_line));
+       add_latent_entropy();
 
        if (crng_ready())
                crng_reseed();
-       else if (arch_init && trust_cpu)
-               credit_init_bits(BLAKE2S_BLOCK_SIZE * 8);
-
-       if (ratelimit_disable) {
-               urandom_warning.interval = 0;
-               unseeded_warning.interval = 0;
-       }
+       else if (trust_cpu)
+               credit_init_bits(arch_bytes * 8);
 
        WARN_ON(register_pm_notifier(&pm_notifier));
 
@@ -1491,11 +1461,14 @@ static ssize_t urandom_read(struct file *file, char __user *buf, size_t nbytes,
        if (!crng_ready())
                try_to_generate_entropy();
 
-       if (!crng_ready() && maxwarn > 0) {
-               maxwarn--;
-               if (__ratelimit(&urandom_warning))
+       if (!crng_ready()) {
+               if (!ratelimit_disable && maxwarn <= 0)
+                       ++urandom_warning.missed;
+               else if (ratelimit_disable || __ratelimit(&urandom_warning)) {
+                       --maxwarn;
                        pr_notice("%s: uninitialized urandom read (%zd bytes read)\n",
                                  current->comm, nbytes);
+               }
        }
 
        return get_random_bytes_user(buf, nbytes);
@@ -1610,7 +1583,7 @@ const struct file_operations urandom_fops = {
  *
  * - write_wakeup_threshold - the amount of entropy in the input pool
  *   below which write polls to /dev/random will unblock, requesting
- *   more entropy, tied to the POOL_INIT_BITS constant. It is writable
+ *   more entropy, tied to the POOL_READY_BITS constant. It is writable
  *   to avoid breaking old userspaces, but writing to it does not
  *   change any behavior of the RNG.
  *
@@ -1625,7 +1598,7 @@ const struct file_operations urandom_fops = {
 #include <linux/sysctl.h>
 
 static int sysctl_random_min_urandom_seed = CRNG_RESEED_INTERVAL / HZ;
-static int sysctl_random_write_wakeup_bits = POOL_INIT_BITS;
+static int sysctl_random_write_wakeup_bits = POOL_READY_BITS;
 static int sysctl_poolsize = POOL_BITS;
 static u8 sysctl_bootid[UUID_SIZE];
 
@@ -1715,8 +1688,8 @@ static struct ctl_table random_table[] = {
 };
 
 /*
- * rand_initialize() is called before sysctl_init(),
- * so we cannot call register_sysctl_init() in rand_initialize()
+ * random_init() is called before sysctl_init(),
+ * so we cannot call register_sysctl_init() in random_init()
  */
 static int __init random_sysctls_init(void)
 {