hwrng: use rng source with best quality
authorHarald Freudenberger <freude@linux.vnet.ibm.com>
Tue, 11 Jul 2017 07:36:22 +0000 (09:36 +0200)
committerHerbert Xu <herbert@gondor.apana.org.au>
Tue, 18 Jul 2017 10:16:08 +0000 (18:16 +0800)
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 <freude@linux.vnet.ibm.com>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/char/hw_random/core.c

index 503a41dfa1936b09d9857b1042cbec48d7dfb4a6..e9dda162d4d2a376772e8bbe928bd19defa98f0c 100644 (file)
@@ -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);
                }
        }