Merge remote-tracking branch 'spi/fix/hspi' into spi-linus
[firefly-linux-kernel-4.4.55.git] / drivers / char / random.c
index cd9a6211dcadd61243905335a87d1e75bd91940e..35487e8ded59f106e3a7b157aa3d5bf46f9579e8 100644 (file)
@@ -865,16 +865,24 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
        if (r->entropy_count / 8 < min + reserved) {
                nbytes = 0;
        } else {
+               int entropy_count, orig;
+retry:
+               entropy_count = orig = ACCESS_ONCE(r->entropy_count);
                /* If limited, never pull more than available */
-               if (r->limit && nbytes + reserved >= r->entropy_count / 8)
-                       nbytes = r->entropy_count/8 - reserved;
-
-               if (r->entropy_count / 8 >= nbytes + reserved)
-                       r->entropy_count -= nbytes*8;
-               else
-                       r->entropy_count = reserved;
+               if (r->limit && nbytes + reserved >= entropy_count / 8)
+                       nbytes = entropy_count/8 - reserved;
+
+               if (entropy_count / 8 >= nbytes + reserved) {
+                       entropy_count -= nbytes*8;
+                       if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+                               goto retry;
+               } else {
+                       entropy_count = reserved;
+                       if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+                               goto retry;
+               }
 
-               if (r->entropy_count < random_write_wakeup_thresh)
+               if (entropy_count < random_write_wakeup_thresh)
                        wakeup_write = 1;
        }
 
@@ -957,10 +965,23 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
 {
        ssize_t ret = 0, i;
        __u8 tmp[EXTRACT_SIZE];
+       unsigned long flags;
 
        /* if last_data isn't primed, we need EXTRACT_SIZE extra bytes */
-       if (fips_enabled && !r->last_data_init)
-               nbytes += EXTRACT_SIZE;
+       if (fips_enabled) {
+               spin_lock_irqsave(&r->lock, flags);
+               if (!r->last_data_init) {
+                       r->last_data_init = true;
+                       spin_unlock_irqrestore(&r->lock, flags);
+                       trace_extract_entropy(r->name, EXTRACT_SIZE,
+                                             r->entropy_count, _RET_IP_);
+                       xfer_secondary_pool(r, EXTRACT_SIZE);
+                       extract_buf(r, tmp);
+                       spin_lock_irqsave(&r->lock, flags);
+                       memcpy(r->last_data, tmp, EXTRACT_SIZE);
+               }
+               spin_unlock_irqrestore(&r->lock, flags);
+       }
 
        trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
        xfer_secondary_pool(r, nbytes);
@@ -970,19 +991,6 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
                extract_buf(r, tmp);
 
                if (fips_enabled) {
-                       unsigned long flags;
-
-
-                       /* prime last_data value if need be, per fips 140-2 */
-                       if (!r->last_data_init) {
-                               spin_lock_irqsave(&r->lock, flags);
-                               memcpy(r->last_data, tmp, EXTRACT_SIZE);
-                               r->last_data_init = true;
-                               nbytes -= EXTRACT_SIZE;
-                               spin_unlock_irqrestore(&r->lock, flags);
-                               extract_buf(r, tmp);
-                       }
-
                        spin_lock_irqsave(&r->lock, flags);
                        if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
                                panic("Hardware RNG duplicated output!\n");