diff options
Diffstat (limited to 'drivers/char/hw_random/core.c')
| -rw-r--r-- | drivers/char/hw_random/core.c | 55 | 
1 files changed, 44 insertions, 11 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index a0f7724852e..c4419ea1ab0 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -37,10 +37,10 @@  #include <linux/kernel.h>  #include <linux/fs.h>  #include <linux/sched.h> -#include <linux/init.h>  #include <linux/miscdevice.h>  #include <linux/delay.h>  #include <linux/slab.h> +#include <linux/random.h>  #include <asm/uaccess.h> @@ -55,16 +55,41 @@ static DEFINE_MUTEX(rng_mutex);  static int data_avail;  static u8 *rng_buffer; +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, +			       int wait); +  static size_t rng_buffer_size(void)  {  	return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;  } +static void add_early_randomness(struct hwrng *rng) +{ +	unsigned char bytes[16]; +	int bytes_read; + +	/* +	 * Currently only virtio-rng cannot return data during device +	 * probe, and that's handled in virtio-rng.c itself.  If there +	 * are more such devices, this call to rng_get_data can be +	 * made conditional here instead of doing it per-device. +	 */ +	bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); +	if (bytes_read > 0) +		add_device_randomness(bytes, bytes_read); +} +  static inline int hwrng_init(struct hwrng *rng)  { -	if (!rng->init) -		return 0; -	return rng->init(rng); +	if (rng->init) { +		int ret; + +		ret =  rng->init(rng); +		if (ret) +			return ret; +	} +	add_early_randomness(rng); +	return 0;  }  static inline void hwrng_cleanup(struct hwrng *rng) @@ -302,7 +327,6 @@ err_misc_dereg:  int hwrng_register(struct hwrng *rng)  { -	int must_register_misc;  	int err = -EINVAL;  	struct hwrng *old_rng, *tmp; @@ -327,7 +351,6 @@ int hwrng_register(struct hwrng *rng)  			goto out_unlock;  	} -	must_register_misc = (current_rng == NULL);  	old_rng = current_rng;  	if (!old_rng) {  		err = hwrng_init(rng); @@ -336,18 +359,28 @@ int hwrng_register(struct hwrng *rng)  		current_rng = rng;  	}  	err = 0; -	if (must_register_misc) { +	if (!old_rng) {  		err = register_miscdev();  		if (err) { -			if (!old_rng) { -				hwrng_cleanup(rng); -				current_rng = NULL; -			} +			hwrng_cleanup(rng); +			current_rng = NULL;  			goto out_unlock;  		}  	}  	INIT_LIST_HEAD(&rng->list);  	list_add_tail(&rng->list, &rng_list); + +	if (old_rng && !rng->init) { +		/* +		 * Use a new device's input to add some randomness to +		 * the system.  If this rng device isn't going to be +		 * used right away, its init function hasn't been +		 * called yet; so only use the randomness from devices +		 * that don't need an init callback. +		 */ +		add_early_randomness(rng); +	} +  out_unlock:  	mutex_unlock(&rng_mutex);  out:  | 
