diff options
author | Torsten Duwe <duwe@lst.de> | 2014-06-14 23:46:03 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2014-07-15 04:49:40 -0400 |
commit | be4000bc4644d027c519b6361f5ae3bbfc52c347 (patch) | |
tree | 4ce9e61ae36a8261d41d8bae1a4c8a78b3d04416 /drivers/char/hw_random | |
parent | c84dbf61a7b322188d2a7fddc0cc6317ac6713e2 (diff) |
hwrng: create filler thread
This can be viewed as the in-kernel equivalent of hwrngd;
like FUSE it is a good thing to have a mechanism in user land,
but for some reasons (simplicity, secrecy, integrity, speed)
it may be better to have it in kernel space.
This patch creates a thread once a hwrng registers, and uses
the previously established add_hwgenerator_randomness() to feed
its data to the input pool as long as needed. A derating factor
is used to bias the entropy estimation and to disable this
mechanism entirely when set to zero.
Signed-off-by: Torsten Duwe <duwe@suse.de>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Acked-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'drivers/char/hw_random')
-rw-r--r-- | drivers/char/hw_random/core.c | 69 |
1 files changed, 64 insertions, 5 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 334601cc81cf..48b3c812b9ec 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/fs.h> | 38 | #include <linux/fs.h> |
39 | #include <linux/sched.h> | 39 | #include <linux/sched.h> |
40 | #include <linux/miscdevice.h> | 40 | #include <linux/miscdevice.h> |
41 | #include <linux/kthread.h> | ||
41 | #include <linux/delay.h> | 42 | #include <linux/delay.h> |
42 | #include <linux/slab.h> | 43 | #include <linux/slab.h> |
43 | #include <linux/random.h> | 44 | #include <linux/random.h> |
@@ -50,10 +51,18 @@ | |||
50 | 51 | ||
51 | 52 | ||
52 | static struct hwrng *current_rng; | 53 | static struct hwrng *current_rng; |
54 | static struct task_struct *hwrng_fill; | ||
53 | static LIST_HEAD(rng_list); | 55 | static LIST_HEAD(rng_list); |
54 | static DEFINE_MUTEX(rng_mutex); | 56 | static DEFINE_MUTEX(rng_mutex); |
55 | static int data_avail; | 57 | static int data_avail; |
56 | static u8 *rng_buffer; | 58 | static u8 *rng_buffer, *rng_fillbuf; |
59 | static unsigned short current_quality = 700; /* an arbitrary 70% */ | ||
60 | |||
61 | module_param(current_quality, ushort, 0644); | ||
62 | MODULE_PARM_DESC(current_quality, | ||
63 | "current hwrng entropy estimation per mill"); | ||
64 | |||
65 | static void start_khwrngd(void); | ||
57 | 66 | ||
58 | static size_t rng_buffer_size(void) | 67 | static size_t rng_buffer_size(void) |
59 | { | 68 | { |
@@ -62,9 +71,18 @@ static size_t rng_buffer_size(void) | |||
62 | 71 | ||
63 | static inline int hwrng_init(struct hwrng *rng) | 72 | static inline int hwrng_init(struct hwrng *rng) |
64 | { | 73 | { |
65 | if (!rng->init) | 74 | int err; |
66 | return 0; | 75 | |
67 | return rng->init(rng); | 76 | if (rng->init) { |
77 | err = rng->init(rng); | ||
78 | if (err) | ||
79 | return err; | ||
80 | } | ||
81 | |||
82 | if (current_quality > 0 && !hwrng_fill) | ||
83 | start_khwrngd(); | ||
84 | |||
85 | return 0; | ||
68 | } | 86 | } |
69 | 87 | ||
70 | static inline void hwrng_cleanup(struct hwrng *rng) | 88 | static inline void hwrng_cleanup(struct hwrng *rng) |
@@ -300,6 +318,36 @@ err_misc_dereg: | |||
300 | goto out; | 318 | goto out; |
301 | } | 319 | } |
302 | 320 | ||
321 | static int hwrng_fillfn(void *unused) | ||
322 | { | ||
323 | long rc; | ||
324 | |||
325 | while (!kthread_should_stop()) { | ||
326 | if (!current_rng) | ||
327 | break; | ||
328 | rc = rng_get_data(current_rng, rng_fillbuf, | ||
329 | rng_buffer_size(), 1); | ||
330 | if (rc <= 0) { | ||
331 | pr_warn("hwrng: no data available\n"); | ||
332 | msleep_interruptible(10000); | ||
333 | continue; | ||
334 | } | ||
335 | add_hwgenerator_randomness((void *)rng_fillbuf, rc, | ||
336 | (rc*current_quality)>>10); | ||
337 | } | ||
338 | hwrng_fill = 0; | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static void start_khwrngd(void) | ||
343 | { | ||
344 | hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng"); | ||
345 | if (hwrng_fill == ERR_PTR(-ENOMEM)) { | ||
346 | pr_err("hwrng_fill thread creation failed"); | ||
347 | hwrng_fill = NULL; | ||
348 | } | ||
349 | } | ||
350 | |||
303 | int hwrng_register(struct hwrng *rng) | 351 | int hwrng_register(struct hwrng *rng) |
304 | { | 352 | { |
305 | int err = -EINVAL; | 353 | int err = -EINVAL; |
@@ -320,6 +368,13 @@ int hwrng_register(struct hwrng *rng) | |||
320 | if (!rng_buffer) | 368 | if (!rng_buffer) |
321 | goto out_unlock; | 369 | goto out_unlock; |
322 | } | 370 | } |
371 | if (!rng_fillbuf) { | ||
372 | rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL); | ||
373 | if (!rng_fillbuf) { | ||
374 | kfree(rng_buffer); | ||
375 | goto out_unlock; | ||
376 | } | ||
377 | } | ||
323 | 378 | ||
324 | /* Must not register two RNGs with the same name. */ | 379 | /* Must not register two RNGs with the same name. */ |
325 | err = -EEXIST; | 380 | err = -EEXIST; |
@@ -375,8 +430,11 @@ void hwrng_unregister(struct hwrng *rng) | |||
375 | current_rng = NULL; | 430 | current_rng = NULL; |
376 | } | 431 | } |
377 | } | 432 | } |
378 | if (list_empty(&rng_list)) | 433 | if (list_empty(&rng_list)) { |
379 | unregister_miscdev(); | 434 | unregister_miscdev(); |
435 | if (hwrng_fill) | ||
436 | kthread_stop(hwrng_fill); | ||
437 | } | ||
380 | 438 | ||
381 | mutex_unlock(&rng_mutex); | 439 | mutex_unlock(&rng_mutex); |
382 | } | 440 | } |
@@ -387,6 +445,7 @@ static void __exit hwrng_exit(void) | |||
387 | mutex_lock(&rng_mutex); | 445 | mutex_lock(&rng_mutex); |
388 | BUG_ON(current_rng); | 446 | BUG_ON(current_rng); |
389 | kfree(rng_buffer); | 447 | kfree(rng_buffer); |
448 | kfree(rng_fillbuf); | ||
390 | mutex_unlock(&rng_mutex); | 449 | mutex_unlock(&rng_mutex); |
391 | } | 450 | } |
392 | 451 | ||