diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 08:16:24 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 08:16:24 -0700 |
commit | f4f142ed4ef835709c7e6d12eaca10d190bcebed (patch) | |
tree | a0bc6850239fe3551bb67f5707bfef153ac437fe /drivers/char/hw_random | |
parent | bb2cbf5e9367d8598fecd0c48dead69560750223 (diff) | |
parent | e02b876597777ab26288dd2611a97b597d14d661 (diff) |
Merge tag 'random_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/random
Pull randomness updates from Ted Ts'o:
"Cleanups and bug fixes to /dev/random, add a new getrandom(2) system
call, which is a superset of OpenBSD's getentropy(2) call, for use
with userspace crypto libraries such as LibreSSL.
Also add the ability to have a kernel thread to pull entropy from
hardware rng devices into /dev/random"
* tag 'random_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/random:
hwrng: Pass entropy to add_hwgenerator_randomness() in bits, not bytes
random: limit the contribution of the hw rng to at most half
random: introduce getrandom(2) system call
hw_random: fix sparse warning (NULL vs 0 for pointer)
random: use registers from interrupted code for CPU's w/o a cycle counter
hwrng: add per-device entropy derating
hwrng: create filler thread
random: add_hwgenerator_randomness() for feeding entropy from devices
random: use an improved fast_mix() function
random: clean up interrupt entropy accounting for archs w/o cycle counters
random: only update the last_pulled time if we actually transferred entropy
random: remove unneeded hash of a portion of the entropy pool
random: always update the entropy pool under the spinlock
Diffstat (limited to 'drivers/char/hw_random')
-rw-r--r-- | drivers/char/hw_random/core.c | 67 |
1 files changed, 65 insertions, 2 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index c4419ea1ab07..6e02ec103cc7 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -38,6 +38,7 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/miscdevice.h> +#include <linux/kthread.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/random.h> @@ -50,10 +51,22 @@ static struct hwrng *current_rng; +static struct task_struct *hwrng_fill; static LIST_HEAD(rng_list); static DEFINE_MUTEX(rng_mutex); static int data_avail; -static u8 *rng_buffer; +static u8 *rng_buffer, *rng_fillbuf; +static unsigned short current_quality; +static unsigned short default_quality; /* = 0; default to "off" */ + +module_param(current_quality, ushort, 0644); +MODULE_PARM_DESC(current_quality, + "current hwrng entropy estimation per mill"); +module_param(default_quality, ushort, 0644); +MODULE_PARM_DESC(default_quality, + "default entropy content of hwrng per mill"); + +static void start_khwrngd(void); static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, int wait); @@ -89,6 +102,15 @@ static inline int hwrng_init(struct hwrng *rng) return ret; } add_early_randomness(rng); + + current_quality = rng->quality ? : default_quality; + current_quality &= 1023; + + if (current_quality == 0 && hwrng_fill) + kthread_stop(hwrng_fill); + if (current_quality > 0 && !hwrng_fill) + start_khwrngd(); + return 0; } @@ -325,6 +347,36 @@ err_misc_dereg: goto out; } +static int hwrng_fillfn(void *unused) +{ + long rc; + + while (!kthread_should_stop()) { + if (!current_rng) + break; + rc = rng_get_data(current_rng, rng_fillbuf, + rng_buffer_size(), 1); + if (rc <= 0) { + pr_warn("hwrng: no data available\n"); + msleep_interruptible(10000); + continue; + } + add_hwgenerator_randomness((void *)rng_fillbuf, rc, + rc * current_quality * 8 >> 10); + } + hwrng_fill = NULL; + return 0; +} + +static void start_khwrngd(void) +{ + hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng"); + if (hwrng_fill == ERR_PTR(-ENOMEM)) { + pr_err("hwrng_fill thread creation failed"); + hwrng_fill = NULL; + } +} + int hwrng_register(struct hwrng *rng) { int err = -EINVAL; @@ -343,6 +395,13 @@ int hwrng_register(struct hwrng *rng) if (!rng_buffer) goto out_unlock; } + if (!rng_fillbuf) { + rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL); + if (!rng_fillbuf) { + kfree(rng_buffer); + goto out_unlock; + } + } /* Must not register two RNGs with the same name. */ err = -EEXIST; @@ -406,8 +465,11 @@ void hwrng_unregister(struct hwrng *rng) current_rng = NULL; } } - if (list_empty(&rng_list)) + if (list_empty(&rng_list)) { unregister_miscdev(); + if (hwrng_fill) + kthread_stop(hwrng_fill); + } mutex_unlock(&rng_mutex); } @@ -418,6 +480,7 @@ static void __exit hwrng_exit(void) mutex_lock(&rng_mutex); BUG_ON(current_rng); kfree(rng_buffer); + kfree(rng_fillbuf); mutex_unlock(&rng_mutex); } |