diff options
author | Theodore Ts'o <tytso@mit.edu> | 2016-05-02 02:04:41 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2016-07-03 00:57:58 -0400 |
commit | 1e7f583af67be4ff091d0aeb863c649efd7a9112 (patch) | |
tree | 2ffd7732681f0cd243ba0e2cb934cc37f59bd2ca /drivers/char/random.c | |
parent | e192be9d9a30555aae2ca1dc3aad37cba484cd4a (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.c | 62 |
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)) |
437 | static int crng_init_cnt = 0; | 437 | static 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) |
439 | static void _extract_crng(struct crng_state *crng, | ||
440 | __u8 out[CHACHA20_BLOCK_SIZE]); | ||
439 | static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]); | 441 | static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]); |
440 | static void process_random_ready_list(void); | 442 | static 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 | ||
757 | static DECLARE_WAIT_QUEUE_HEAD(crng_init_wait); | 759 | static 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 | */ | ||
768 | static struct crng_state **crng_node_pool __read_mostly; | ||
769 | #endif | ||
770 | |||
759 | static void crng_initialize(struct crng_state *crng) | 771 | static 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 | ||
850 | static 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 | |||
838 | static inline void crng_wait_ready(void) | 857 | static 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 | ||
843 | static void extract_crng(__u8 out[CHACHA20_BLOCK_SIZE]) | 862 | static 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 | ||
879 | static 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 | |||
860 | static ssize_t extract_crng_user(void __user *buf, size_t nbytes) | 892 | static 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 | */ |
1576 | static int rand_initialize(void) | 1608 | static 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 | } |
1583 | early_initcall(rand_initialize); | 1637 | early_initcall(rand_initialize); |