This patch rewoks the hwrng to always use the
rng source with best entropy quality.

On registation and unregistration the hwrng now
tries to choose the best (= highest quality value)
rng source. The handling of the internal list
of registered rng sources is now always sorted
by quality and the top most rng chosen.

Signed-off-by: Harald Freudenberger <fre...@linux.vnet.ibm.com>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmku...@gmail.com>
---
 drivers/char/hw_random/core.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 503a41d..e9dda16 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -29,6 +29,7 @@
 
 static struct hwrng *current_rng;
 static struct task_struct *hwrng_fill;
+/* list of registered rngs, sorted decending by quality */
 static LIST_HEAD(rng_list);
 /* Protects rng_list and current_rng */
 static DEFINE_MUTEX(rng_mutex);
@@ -417,6 +418,7 @@ int hwrng_register(struct hwrng *rng)
 {
        int err = -EINVAL;
        struct hwrng *old_rng, *tmp;
+       struct list_head *rng_list_ptr;
 
        if (!rng->name || (!rng->data_read && !rng->read))
                goto out;
@@ -432,14 +434,25 @@ int hwrng_register(struct hwrng *rng)
        init_completion(&rng->cleanup_done);
        complete(&rng->cleanup_done);
 
+       /* rng_list is sorted by decreasing quality */
+       list_for_each(rng_list_ptr, &rng_list) {
+               tmp = list_entry(rng_list_ptr, struct hwrng, list);
+               if (tmp->quality < rng->quality)
+                       break;
+       }
+       list_add_tail(&rng->list, rng_list_ptr);
+
        old_rng = current_rng;
        err = 0;
-       if (!old_rng) {
+       if (!old_rng || (rng->quality > old_rng->quality)) {
+               /*
+                * Set new rng as current as the new rng source
+                * provides better entropy quality.
+                */
                err = set_current_rng(rng);
                if (err)
                        goto out_unlock;
        }
-       list_add_tail(&rng->list, &rng_list);
 
        if (old_rng && !rng->init) {
                /*
@@ -466,12 +479,12 @@ void hwrng_unregister(struct hwrng *rng)
        list_del(&rng->list);
        if (current_rng == rng) {
                drop_current_rng();
+               /* rng_list is sorted by quality, use the best (=first) one */
                if (!list_empty(&rng_list)) {
-                       struct hwrng *tail;
-
-                       tail = list_entry(rng_list.prev, struct hwrng, list);
+                       struct hwrng *new_rng;
 
-                       set_current_rng(tail);
+                       new_rng = list_entry(rng_list.next, struct hwrng, list);
+                       set_current_rng(new_rng);
                }
        }
 
-- 
2.7.4

Reply via email to