summaryrefslogtreecommitdiffstats
path: root/drivers/char/random.c
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2016-05-02 02:04:41 -0400
committerTheodore Ts'o <tytso@mit.edu>2016-07-03 00:57:58 -0400
commit1e7f583af67be4ff091d0aeb863c649efd7a9112 (patch)
tree2ffd7732681f0cd243ba0e2cb934cc37f59bd2ca /drivers/char/random.c
parente192be9d9a30555aae2ca1dc3aad37cba484cd4a (diff)
random: make /dev/urandom scalable for silly userspace programs
On a system with a 4 socket (NUMA) system where a large number of application threads were all trying to read from /dev/urandom, this can result in the system spending 80% of its time contending on the global urandom spinlock. The application should have used its own PRNG, but let's try to help it from running, lemming-like, straight over the locking cliff. Reported-by: Andi Kleen <ak@linux.intel.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'drivers/char/random.c')
-rw-r--r--drivers/char/random.c62
1 files changed, 58 insertions, 4 deletions
diff --git a/drivers/char/random.c b/drivers/char/random.c
index dc2a9c2d8dcf..2a30d9718a1b 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -436,6 +436,8 @@ static int crng_init = 0;
436#define crng_ready() (likely(crng_init > 0)) 436#define crng_ready() (likely(crng_init > 0))
437static int crng_init_cnt = 0; 437static int crng_init_cnt = 0;
438#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE) 438#define CRNG_INIT_CNT_THRESH (2*CHACHA20_KEY_SIZE)
439static void _extract_crng(struct crng_state *crng,
440 __u8 out[CHACHA20_BLOCK_SIZE]);
439static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]); 441static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]);
440static void process_random_ready_list(void); 442static void process_random_ready_list(void);
441 443
@@ -756,6 +758,16 @@ static void credit_entropy_bits_safe(struct entropy_store *r, int nbits)
756 758
757static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait); 759static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait);
758 760
761#ifdef CONFIG_NUMA
762/*
763 * Hack to deal with crazy userspace progams when they are all trying
764 * to access /dev/urandom in parallel. The programs are almost
765 * certainly doing something terribly wrong, but we'll work around
766 * their brain damage.
767 */
768static struct crng_state **crng_node_pool __read_mostly;
769#endif
770
759static void crng_initialize(struct crng_state *crng) 771static void crng_initialize(struct crng_state *crng)
760{ 772{
761 int i; 773 int i;
@@ -815,7 +827,7 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
815 if (num == 0) 827 if (num == 0)
816 return; 828 return;
817 } else 829 } else
818 extract_crng(buf.block); 830 _extract_crng(&primary_crng, buf.block);
819 spin_lock_irqsave(&primary_crng.lock, flags); 831 spin_lock_irqsave(&primary_crng.lock, flags);
820 for (i = 0; i < 8; i++) { 832 for (i = 0; i < 8; i++) {
821 unsigned long rv; 833 unsigned long rv;
@@ -835,19 +847,26 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r)
835 spin_unlock_irqrestore(&primary_crng.lock, flags); 847 spin_unlock_irqrestore(&primary_crng.lock, flags);
836} 848}
837 849
850static inline void maybe_reseed_primary_crng(void)
851{
852 if (crng_init > 2 &&
853 time_after(jiffies, primary_crng.init_time + CRNG_RESEED_INTERVAL))
854 crng_reseed(&primary_crng, &input_pool);
855}
856
838static inline void crng_wait_ready(void) 857static inline void crng_wait_ready(void)
839{ 858{
840 wait_event_interruptible(crng_init_wait, crng_ready()); 859 wait_event_interruptible(crng_init_wait, crng_ready());
841} 860}
842 861
843static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]) 862static void _extract_crng(struct crng_state *crng,
863 __u8 out[CHACHA20_BLOCK_SIZE])
844{ 864{
845 unsigned long v, flags; 865 unsigned long v, flags;
846 struct crng_state *crng = &primary_crng;
847 866
848 if (crng_init > 1 && 867 if (crng_init > 1 &&
849 time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL)) 868 time_after(jiffies, crng->init_time + CRNG_RESEED_INTERVAL))
850 crng_reseed(crng, &input_pool); 869 crng_reseed(crng, crng == &primary_crng ? &input_pool : NULL);
851 spin_lock_irqsave(&crng->lock, flags); 870 spin_lock_irqsave(&crng->lock, flags);
852 if (arch_get_random_long(&v)) 871 if (arch_get_random_long(&v))
853 crng->state[14] ^= v; 872 crng->state[14] ^= v;
@@ -857,6 +876,19 @@ static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE])
857 spin_unlock_irqrestore(&crng->lock, flags); 876 spin_unlock_irqrestore(&crng->lock, flags);
858} 877}
859 878
879static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE])
880{
881 struct crng_state *crng = NULL;
882
883#ifdef CONFIG_NUMA
884 if (crng_node_pool)
885 crng = crng_node_pool[numa_node_id()];
886 if (crng == NULL)
887#endif
888 crng = &primary_crng;
889 _extract_crng(crng, out);
890}
891
860static ssize_t extract_crng_user(void __user *buf, size_t nbytes) 892static ssize_t extract_crng_user(void __user *buf, size_t nbytes)
861{ 893{
862 ssize_t ret = 0, i; 894 ssize_t ret = 0, i;
@@ -1575,9 +1607,31 @@ static void init_std_data(struct entropy_store *r)
1575 */ 1607 */
1576static int rand_initialize(void) 1608static int rand_initialize(void)
1577{ 1609{
1610#ifdef CONFIG_NUMA
1611 int i;
1612 int num_nodes = num_possible_nodes();
1613 struct crng_state *crng;
1614 struct crng_state **pool;
1615#endif
1616
1578 init_std_data(&input_pool); 1617 init_std_data(&input_pool);
1579 init_std_data(&blocking_pool); 1618 init_std_data(&blocking_pool);
1580 crng_initialize(&primary_crng); 1619 crng_initialize(&primary_crng);
1620
1621#ifdef CONFIG_NUMA
1622 pool = kmalloc(num_nodes * sizeof(void *),
1623 GFP_KERNEL|__GFP_NOFAIL|__GFP_ZERO);
1624 for (i=0; i < num_nodes; i++) {
1625 crng = kmalloc_node(sizeof(struct crng_state),
1626 GFP_KERNEL | __GFP_NOFAIL, i);
1627 spin_lock_init(&crng->lock);
1628 crng_initialize(crng);
1629 pool[i] = crng;
1630
1631 }
1632 mb();
1633 crng_node_pool = pool;
1634#endif
1581 return 0; 1635 return 0;
1582} 1636}
1583early_initcall(rand_initialize); 1637early_initcall(rand_initialize);