[PATCH] hwrng: core - Allow for multiple simultaneous active hwrng devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Instead of having only one hwrng feeding /dev/random at a time, maintain
a list of devices and cycle between them when filling the entropy pool.

Signed-off-by: Keith Packard <keithp@xxxxxxxxxx>
---
 drivers/char/hw_random/core.c | 155 +++++++++++++++++++++++++++---------------
 include/linux/hw_random.h     |   2 +
 2 files changed, 104 insertions(+), 53 deletions(-)

diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 9203f2d..97fd4a7 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -51,10 +51,10 @@
 #define RNG_MISCDEV_MINOR	183 /* official */
 
 
-static struct hwrng *current_rng;
+static LIST_HEAD(active_rng);
 static struct task_struct *hwrng_fill;
 static LIST_HEAD(rng_list);
-/* Protects rng_list and current_rng */
+/* Protects rng_list and active_rng */
 static DEFINE_MUTEX(rng_mutex);
 /* Protects rng read functions, data_avail, rng_buffer and rng_fillbuf */
 static DEFINE_MUTEX(reading_mutex);
@@ -70,7 +70,6 @@ module_param(default_quality, ushort, 0644);
 MODULE_PARM_DESC(default_quality,
 		 "default entropy content of hwrng per mill");
 
-static void drop_current_rng(void);
 static int hwrng_init(struct hwrng *rng);
 static void start_khwrngd(void);
 
@@ -104,31 +103,65 @@ static inline void cleanup_rng(struct kref *kref)
 	complete(&rng->cleanup_done);
 }
 
-static int set_current_rng(struct hwrng *rng)
+static unsigned short rng_quality(struct hwrng *rng)
+{
+	unsigned short quality;
+
+	quality = current_quality;
+	if (!quality) {
+		quality = rng->quality;
+		if (!quality)
+			quality = default_quality;
+	}
+	if (quality > 1024)
+		quality = 1024;
+	return quality;
+}
+
+static int add_active_rng(struct hwrng *rng)
 {
 	int err;
 
 	BUG_ON(!mutex_is_locked(&rng_mutex));
 
+	if (rng->is_active)
+		return 0;
+
 	err = hwrng_init(rng);
 	if (err)
 		return err;
 
-	drop_current_rng();
-	current_rng = rng;
+	if (rng_quality(rng) != 0) {
+		rng->is_active = true;
+		list_add(&rng->active, &active_rng);
+		if (!hwrng_fill)
+			start_khwrngd();
+	}
 
 	return 0;
 }
 
-static void drop_current_rng(void)
+static int drop_active_rng(struct hwrng *rng)
 {
 	BUG_ON(!mutex_is_locked(&rng_mutex));
-	if (!current_rng)
-		return;
+	if (!rng->is_active)
+		return 0;
+
+	list_del(&rng->active);
+	rng->is_active = false;
 
 	/* decrease last reference for triggering the cleanup */
-	kref_put(&current_rng->ref, cleanup_rng);
-	current_rng = NULL;
+	kref_put(&rng->ref, cleanup_rng);
+
+	if (list_empty(&active_rng) && hwrng_fill)
+		kthread_stop(hwrng_fill);
+
+	return 0;
+}
+
+static struct hwrng *first_active_rng(void)
+{
+	return list_first_entry_or_null(&active_rng, struct hwrng, active);
 }
 
 /* Returns ERR_PTR(), NULL or refcounted hwrng */
@@ -139,18 +172,32 @@ static struct hwrng *get_current_rng(void)
 	if (mutex_lock_interruptible(&rng_mutex))
 		return ERR_PTR(-ERESTARTSYS);
 
-	rng = current_rng;
+	rng = first_active_rng();
 	if (rng)
 		kref_get(&rng->ref);
 
 	mutex_unlock(&rng_mutex);
+
 	return rng;
 }
 
+static int next_rng(void)
+{
+	if (mutex_lock_interruptible(&rng_mutex))
+		return -ERESTARTSYS;
+
+	if (!list_empty(&active_rng) && !list_is_singular(&active_rng))
+		list_rotate_left(&active_rng);
+
+	mutex_unlock(&rng_mutex);
+
+	return 0;
+}
+
 static void put_rng(struct hwrng *rng)
 {
 	/*
-	 * Hold rng_mutex here so we serialize in case they set_current_rng
+	 * Hold rng_mutex here so we serialize in case they add_active_rng
 	 * on rng again immediately.
 	 */
 	mutex_lock(&rng_mutex);
@@ -178,15 +225,6 @@ static int hwrng_init(struct hwrng *rng)
 skip_init:
 	add_early_randomness(rng);
 
-	current_quality = rng->quality ? : default_quality;
-	if (current_quality > 1024)
-		current_quality = 1024;
-
-	if (current_quality == 0 && hwrng_fill)
-		kthread_stop(hwrng_fill);
-	if (current_quality > 0 && !hwrng_fill)
-		start_khwrngd();
-
 	return 0;
 }
 
@@ -246,6 +284,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
 			bytes_read = rng_get_data(rng, rng_buffer,
 				rng_buffer_size(),
 				!(filp->f_flags & O_NONBLOCK));
+			next_rng();
 			if (bytes_read < 0) {
 				err = bytes_read;
 				goto out_unlock_reading;
@@ -320,20 +359,30 @@ static ssize_t hwrng_attr_current_store(struct device *dev,
 					const char *buf, size_t len)
 {
 	int err;
+	int add = 1;
 	struct hwrng *rng;
 
 	err = mutex_lock_interruptible(&rng_mutex);
 	if (err)
 		return -ERESTARTSYS;
+	if (*buf == '-') {
+		add = 0;
+		buf++;
+	} else if (*buf == '+') {
+		add = 1;
+		buf++;
+	}
 	err = -ENODEV;
 	list_for_each_entry(rng, &rng_list, list) {
 		if (sysfs_streq(rng->name, buf)) {
-			err = 0;
-			if (rng != current_rng)
-				err = set_current_rng(rng);
+			if (add)
+				err = add_active_rng(rng);
+			else
+				err = drop_active_rng(rng);
 			break;
 		}
 	}
+
 	mutex_unlock(&rng_mutex);
 
 	return err ? : len;
@@ -343,17 +392,26 @@ static ssize_t hwrng_attr_current_show(struct device *dev,
 				       struct device_attribute *attr,
 				       char *buf)
 {
-	ssize_t ret;
+	int err;
 	struct hwrng *rng;
 
-	rng = get_current_rng();
-	if (IS_ERR(rng))
-		return PTR_ERR(rng);
-
-	ret = snprintf(buf, PAGE_SIZE, "%s\n", rng ? rng->name : "none");
-	put_rng(rng);
+	err = mutex_lock_interruptible(&rng_mutex);
+	if (err)
+		return -ERESTARTSYS;
+	buf[0] = '\0';
+	if (list_empty(&active_rng)) {
+		strlcat(buf, "none", PAGE_SIZE);
+	} else {
+		list_for_each_entry(rng, &active_rng, active) {
+			if (rng != first_active_rng())
+				strlcat(buf, " ", PAGE_SIZE);
+			strlcat(buf, rng->name, PAGE_SIZE);
+		}
+	}
+	strlcat(buf, "\n", PAGE_SIZE);
+	mutex_unlock(&rng_mutex);
 
-	return ret;
+	return strlen(buf);
 }
 
 static ssize_t hwrng_attr_available_show(struct device *dev,
@@ -412,6 +470,7 @@ static int hwrng_fillfn(void *unused)
 		rng = get_current_rng();
 		if (IS_ERR(rng) || !rng)
 			break;
+		next_rng();
 		mutex_lock(&reading_mutex);
 		rc = rng_get_data(rng, rng_fillbuf,
 				  rng_buffer_size(), 1);
@@ -442,7 +501,7 @@ static void start_khwrngd(void)
 int hwrng_register(struct hwrng *rng)
 {
 	int err = -EINVAL;
-	struct hwrng *old_rng, *tmp;
+	struct hwrng *tmp;
 
 	if (rng->name == NULL ||
 	    (rng->data_read == NULL && rng->read == NULL))
@@ -472,19 +531,18 @@ int hwrng_register(struct hwrng *rng)
 			goto out_unlock;
 	}
 
+	rng->is_active = false;
+
 	init_completion(&rng->cleanup_done);
 	complete(&rng->cleanup_done);
 
-	old_rng = current_rng;
-	err = 0;
-	if (!old_rng) {
-		err = set_current_rng(rng);
-		if (err)
-			goto out_unlock;
-	}
+	err = add_active_rng(rng);
+	if (err)
+		goto out_unlock;
+
 	list_add_tail(&rng->list, &rng_list);
 
-	if (old_rng && !rng->init) {
+	if (!rng->init) {
 		/*
 		 * Use a new device's input to add some randomness to
 		 * the system.  If this rng device isn't going to be
@@ -507,16 +565,7 @@ void hwrng_unregister(struct hwrng *rng)
 	mutex_lock(&rng_mutex);
 
 	list_del(&rng->list);
-	if (current_rng == rng) {
-		drop_current_rng();
-		if (!list_empty(&rng_list)) {
-			struct hwrng *tail;
-
-			tail = list_entry(rng_list.prev, struct hwrng, list);
-
-			set_current_rng(tail);
-		}
-	}
+	drop_active_rng(rng);
 
 	if (list_empty(&rng_list)) {
 		mutex_unlock(&rng_mutex);
@@ -579,7 +628,7 @@ static int __init hwrng_modinit(void)
 static void __exit hwrng_modexit(void)
 {
 	mutex_lock(&rng_mutex);
-	BUG_ON(current_rng);
+	WARN_ON(!list_empty(&active_rng));
 	kfree(rng_buffer);
 	kfree(rng_fillbuf);
 	mutex_unlock(&rng_mutex);
diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h
index 4f7d8f4..c655563 100644
--- a/include/linux/hw_random.h
+++ b/include/linux/hw_random.h
@@ -45,7 +45,9 @@ struct hwrng {
 	unsigned short quality;
 
 	/* internal. */
+	bool is_active;
 	struct list_head list;
+	struct list_head active;
 	struct kref ref;
 	struct completion cleanup_done;
 };
-- 
2.8.1

--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux