hwrng: core - start hwrng kthread also for untrusted sources
[linux-2.6-microblaze.git] / drivers / char / hw_random / core.c
index 16f227b..cc002b0 100644 (file)
@@ -52,7 +52,7 @@ MODULE_PARM_DESC(default_quality,
 
 static void drop_current_rng(void);
 static int hwrng_init(struct hwrng *rng);
-static void hwrng_manage_rngd(struct hwrng *rng);
+static int hwrng_fillfn(void *unused);
 
 static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
                               int wait);
@@ -96,6 +96,15 @@ static int set_current_rng(struct hwrng *rng)
        drop_current_rng();
        current_rng = rng;
 
+       /* if necessary, start hwrng thread */
+       if (!hwrng_fill) {
+               hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng");
+               if (IS_ERR(hwrng_fill)) {
+                       pr_err("hwrng_fill thread creation failed\n");
+                       hwrng_fill = NULL;
+               }
+       }
+
        return 0;
 }
 
@@ -167,8 +176,6 @@ skip_init:
                rng->quality = 1024;
        current_quality = rng->quality; /* obsolete */
 
-       hwrng_manage_rngd(rng);
-
        return 0;
 }
 
@@ -454,10 +461,6 @@ static ssize_t rng_quality_store(struct device *dev,
        /* the best available RNG may have changed */
        ret = enable_best_rng();
 
-       /* start/stop rngd if necessary */
-       if (current_rng)
-               hwrng_manage_rngd(current_rng);
-
 out:
        mutex_unlock(&rng_mutex);
        return ret ? ret : len;
@@ -507,16 +510,14 @@ static int hwrng_fillfn(void *unused)
                        rng->quality = current_quality; /* obsolete */
                quality = rng->quality;
                mutex_unlock(&reading_mutex);
-               put_rng(rng);
 
-               if (!quality)
-                       break;
+               if (rc <= 0)
+                       hwrng_msleep(rng, 10000);
 
-               if (rc <= 0) {
-                       pr_warn("hwrng: no data available\n");
-                       msleep_interruptible(10000);
+               put_rng(rng);
+
+               if (rc <= 0)
                        continue;
-               }
 
                /* If we cannot credit at least one bit of entropy,
                 * keep track of the remainder for the next iteration
@@ -533,22 +534,6 @@ static int hwrng_fillfn(void *unused)
        return 0;
 }
 
-static void hwrng_manage_rngd(struct hwrng *rng)
-{
-       if (WARN_ON(!mutex_is_locked(&rng_mutex)))
-               return;
-
-       if (rng->quality == 0 && hwrng_fill)
-               kthread_stop(hwrng_fill);
-       if (rng->quality > 0 && !hwrng_fill) {
-               hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng");
-               if (IS_ERR(hwrng_fill)) {
-                       pr_err("hwrng_fill thread creation failed\n");
-                       hwrng_fill = NULL;
-               }
-       }
-}
-
 int hwrng_register(struct hwrng *rng)
 {
        int err = -EINVAL;
@@ -570,6 +555,7 @@ int hwrng_register(struct hwrng *rng)
 
        init_completion(&rng->cleanup_done);
        complete(&rng->cleanup_done);
+       init_completion(&rng->dying);
 
        if (!current_rng ||
            (!cur_rng_set_by_user && rng->quality > current_rng->quality)) {
@@ -617,6 +603,7 @@ void hwrng_unregister(struct hwrng *rng)
 
        old_rng = current_rng;
        list_del(&rng->list);
+       complete_all(&rng->dying);
        if (current_rng == rng) {
                err = enable_best_rng();
                if (err) {
@@ -685,6 +672,14 @@ void devm_hwrng_unregister(struct device *dev, struct hwrng *rng)
 }
 EXPORT_SYMBOL_GPL(devm_hwrng_unregister);
 
+long hwrng_msleep(struct hwrng *rng, unsigned int msecs)
+{
+       unsigned long timeout = msecs_to_jiffies(msecs) + 1;
+
+       return wait_for_completion_interruptible_timeout(&rng->dying, timeout);
+}
+EXPORT_SYMBOL_GPL(hwrng_msleep);
+
 static int __init hwrng_modinit(void)
 {
        int ret;