From 9372b35e11149c5314f56f939775e67d83057604 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Dec 2014 16:50:35 +0800 Subject: hwrng: place mutex around read functions and buffers. There's currently a big lock around everything, and it means that we can't query sysfs (eg /sys/devices/virtual/misc/hw_random/rng_current) while the rng is reading. This is a real problem when the rng is slow, or blocked (eg. virtio_rng with qemu's default /dev/random backend) This doesn't help (it leaves the current lock untouched), just adds a lock to protect the read function and the static buffers, in preparation for transition. Signed-off-by: Rusty Russell Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 1500cfd..2dd3144 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -53,7 +53,10 @@ static struct hwrng *current_rng; static struct task_struct *hwrng_fill; static LIST_HEAD(rng_list); +/* Protects rng_list and current_rng */ static DEFINE_MUTEX(rng_mutex); +/* Protects rng read functions, data_avail, rng_buffer and rng_fillbuf */ +static DEFINE_MUTEX(reading_mutex); static int data_avail; static u8 *rng_buffer, *rng_fillbuf; static unsigned short current_quality; @@ -81,7 +84,9 @@ static void add_early_randomness(struct hwrng *rng) unsigned char bytes[16]; int bytes_read; + mutex_lock(&reading_mutex); bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); + mutex_unlock(&reading_mutex); if (bytes_read > 0) add_device_randomness(bytes, bytes_read); } @@ -128,6 +133,7 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, int wait) { int present; + BUG_ON(!mutex_is_locked(&reading_mutex)); if (rng->read) return rng->read(rng, (void *)buffer, size, wait); @@ -160,13 +166,14 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, goto out_unlock; } + mutex_lock(&reading_mutex); if (!data_avail) { bytes_read = rng_get_data(current_rng, rng_buffer, rng_buffer_size(), !(filp->f_flags & O_NONBLOCK)); if (bytes_read < 0) { err = bytes_read; - goto out_unlock; + goto out_unlock_reading; } data_avail = bytes_read; } @@ -174,7 +181,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, if (!data_avail) { if (filp->f_flags & O_NONBLOCK) { err = -EAGAIN; - goto out_unlock; + goto out_unlock_reading; } } else { len = data_avail; @@ -186,7 +193,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, if (copy_to_user(buf + ret, rng_buffer + data_avail, len)) { err = -EFAULT; - goto out_unlock; + goto out_unlock_reading; } size -= len; @@ -194,6 +201,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, } mutex_unlock(&rng_mutex); + mutex_unlock(&reading_mutex); if (need_resched()) schedule_timeout_interruptible(1); @@ -208,6 +216,9 @@ out: out_unlock: mutex_unlock(&rng_mutex); goto out; +out_unlock_reading: + mutex_unlock(&reading_mutex); + goto out_unlock; } @@ -344,13 +355,16 @@ static int hwrng_fillfn(void *unused) while (!kthread_should_stop()) { if (!current_rng) break; + mutex_lock(&reading_mutex); rc = rng_get_data(current_rng, rng_fillbuf, rng_buffer_size(), 1); + mutex_unlock(&reading_mutex); if (rc <= 0) { pr_warn("hwrng: no data available\n"); msleep_interruptible(10000); continue; } + /* Outside lock, sure, but y'know: randomness. */ add_hwgenerator_randomness((void *)rng_fillbuf, rc, rc * current_quality * 8 >> 10); } -- cgit v1.1 From 1dacb395d68a14825ee48c0843335e3181aea675 Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Mon, 8 Dec 2014 16:50:36 +0800 Subject: hwrng: move some code out mutex_lock for avoiding underlying deadlock In next patch, we use reference counting for each struct hwrng, changing reference count also needs to take mutex_lock. Before releasing the lock, if we try to stop a kthread that waits to take the lock to reduce the referencing count, deadlock will occur. Signed-off-by: Amos Kong Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 2dd3144..b4c0e87 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -470,12 +470,12 @@ void hwrng_unregister(struct hwrng *rng) } } if (list_empty(&rng_list)) { + mutex_unlock(&rng_mutex); unregister_miscdev(); if (hwrng_fill) kthread_stop(hwrng_fill); - } - - mutex_unlock(&rng_mutex); + } else + mutex_unlock(&rng_mutex); } EXPORT_SYMBOL_GPL(hwrng_unregister); -- cgit v1.1 From 3a2c0ba5ad00c018c0bef39a2224aca950aa33f2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Dec 2014 16:50:37 +0800 Subject: hwrng: use reference counts on each struct hwrng. current_rng holds one reference, and we bump it every time we want to do a read from it. This means we only hold the rng_mutex to grab or drop a reference, so accessing /sys/devices/virtual/misc/hw_random/rng_current doesn't block on read of /dev/hwrng. Using a kref is overkill (we're always under the rng_mutex), but a standard pattern. This also solves the problem that the hwrng_fillfn thread was accessing current_rng without a lock, which could change (eg. to NULL) underneath it. Signed-off-by: Rusty Russell Signed-off-by: Amos Kong Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 135 ++++++++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 43 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index b4c0e87..089c18d 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -91,6 +92,60 @@ static void add_early_randomness(struct hwrng *rng) add_device_randomness(bytes, bytes_read); } +static inline void cleanup_rng(struct kref *kref) +{ + struct hwrng *rng = container_of(kref, struct hwrng, ref); + + if (rng->cleanup) + rng->cleanup(rng); +} + +static void set_current_rng(struct hwrng *rng) +{ + BUG_ON(!mutex_is_locked(&rng_mutex)); + kref_get(&rng->ref); + current_rng = rng; +} + +static void drop_current_rng(void) +{ + BUG_ON(!mutex_is_locked(&rng_mutex)); + if (!current_rng) + return; + + /* decrease last reference for triggering the cleanup */ + kref_put(¤t_rng->ref, cleanup_rng); + current_rng = NULL; +} + +/* Returns ERR_PTR(), NULL or refcounted hwrng */ +static struct hwrng *get_current_rng(void) +{ + struct hwrng *rng; + + if (mutex_lock_interruptible(&rng_mutex)) + return ERR_PTR(-ERESTARTSYS); + + rng = current_rng; + if (rng) + kref_get(&rng->ref); + + mutex_unlock(&rng_mutex); + return rng; +} + +static void put_rng(struct hwrng *rng) +{ + /* + * Hold rng_mutex here so we serialize in case they set_current_rng + * on rng again immediately. + */ + mutex_lock(&rng_mutex); + if (rng) + kref_put(&rng->ref, cleanup_rng); + mutex_unlock(&rng_mutex); +} + static inline int hwrng_init(struct hwrng *rng) { if (rng->init) { @@ -113,12 +168,6 @@ static inline int hwrng_init(struct hwrng *rng) return 0; } -static inline void hwrng_cleanup(struct hwrng *rng) -{ - if (rng && rng->cleanup) - rng->cleanup(rng); -} - static int rng_dev_open(struct inode *inode, struct file *filp) { /* enforce read-only access to this chrdev */ @@ -154,21 +203,22 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, ssize_t ret = 0; int err = 0; int bytes_read, len; + struct hwrng *rng; while (size) { - if (mutex_lock_interruptible(&rng_mutex)) { - err = -ERESTARTSYS; + rng = get_current_rng(); + if (IS_ERR(rng)) { + err = PTR_ERR(rng); goto out; } - - if (!current_rng) { + if (!rng) { err = -ENODEV; - goto out_unlock; + goto out; } mutex_lock(&reading_mutex); if (!data_avail) { - bytes_read = rng_get_data(current_rng, rng_buffer, + bytes_read = rng_get_data(rng, rng_buffer, rng_buffer_size(), !(filp->f_flags & O_NONBLOCK)); if (bytes_read < 0) { @@ -200,8 +250,8 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, ret += len; } - mutex_unlock(&rng_mutex); mutex_unlock(&reading_mutex); + put_rng(rng); if (need_resched()) schedule_timeout_interruptible(1); @@ -213,12 +263,11 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, } out: return ret ? : err; -out_unlock: - mutex_unlock(&rng_mutex); - goto out; + out_unlock_reading: mutex_unlock(&reading_mutex); - goto out_unlock; + put_rng(rng); + goto out; } @@ -257,8 +306,8 @@ static ssize_t hwrng_attr_current_store(struct device *dev, err = hwrng_init(rng); if (err) break; - hwrng_cleanup(current_rng); - current_rng = rng; + drop_current_rng(); + set_current_rng(rng); err = 0; break; } @@ -272,17 +321,15 @@ static ssize_t hwrng_attr_current_show(struct device *dev, struct device_attribute *attr, char *buf) { - int err; ssize_t ret; - const char *name = "none"; + struct hwrng *rng; - err = mutex_lock_interruptible(&rng_mutex); - if (err) - return -ERESTARTSYS; - if (current_rng) - name = current_rng->name; - ret = snprintf(buf, PAGE_SIZE, "%s\n", name); - mutex_unlock(&rng_mutex); + 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); return ret; } @@ -353,12 +400,16 @@ static int hwrng_fillfn(void *unused) long rc; while (!kthread_should_stop()) { - if (!current_rng) + struct hwrng *rng; + + rng = get_current_rng(); + if (IS_ERR(rng) || !rng) break; mutex_lock(&reading_mutex); - rc = rng_get_data(current_rng, rng_fillbuf, + rc = rng_get_data(rng, rng_fillbuf, rng_buffer_size(), 1); mutex_unlock(&reading_mutex); + put_rng(rng); if (rc <= 0) { pr_warn("hwrng: no data available\n"); msleep_interruptible(10000); @@ -419,14 +470,13 @@ int hwrng_register(struct hwrng *rng) err = hwrng_init(rng); if (err) goto out_unlock; - current_rng = rng; + set_current_rng(rng); } err = 0; if (!old_rng) { err = register_miscdev(); if (err) { - hwrng_cleanup(rng); - current_rng = NULL; + drop_current_rng(); goto out_unlock; } } @@ -453,22 +503,21 @@ EXPORT_SYMBOL_GPL(hwrng_register); void hwrng_unregister(struct hwrng *rng) { - int err; - mutex_lock(&rng_mutex); list_del(&rng->list); if (current_rng == rng) { - hwrng_cleanup(rng); - if (list_empty(&rng_list)) { - current_rng = NULL; - } else { - current_rng = list_entry(rng_list.prev, struct hwrng, list); - err = hwrng_init(current_rng); - if (err) - current_rng = NULL; + drop_current_rng(); + if (!list_empty(&rng_list)) { + struct hwrng *tail; + + tail = list_entry(rng_list.prev, struct hwrng, list); + + if (hwrng_init(tail) == 0) + set_current_rng(tail); } } + if (list_empty(&rng_list)) { mutex_unlock(&rng_mutex); unregister_miscdev(); -- cgit v1.1 From a027f30d72f2c4d27d6dd9bd053205d3102de7d1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Dec 2014 16:50:38 +0800 Subject: hwrng: fix unregister race. The previous patch added one potential problem: we can still be reading from a hwrng when it's unregistered. Add a wait for zero in the hwrng_unregister path. Signed-off-by: Rusty Russell Signed-off-by: Amos Kong Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 089c18d..8d609a0 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -60,6 +60,7 @@ static DEFINE_MUTEX(rng_mutex); static DEFINE_MUTEX(reading_mutex); static int data_avail; static u8 *rng_buffer, *rng_fillbuf; +static DECLARE_WAIT_QUEUE_HEAD(rng_done); static unsigned short current_quality; static unsigned short default_quality; /* = 0; default to "off" */ @@ -98,6 +99,11 @@ static inline void cleanup_rng(struct kref *kref) if (rng->cleanup) rng->cleanup(rng); + + /* cleanup_done should be updated after cleanup finishes */ + smp_wmb(); + rng->cleanup_done = true; + wake_up_all(&rng_done); } static void set_current_rng(struct hwrng *rng) @@ -494,6 +500,8 @@ int hwrng_register(struct hwrng *rng) add_early_randomness(rng); } + rng->cleanup_done = false; + out_unlock: mutex_unlock(&rng_mutex); out: @@ -525,6 +533,10 @@ void hwrng_unregister(struct hwrng *rng) kthread_stop(hwrng_fill); } else mutex_unlock(&rng_mutex); + + /* Just in case rng is reading right now, wait. */ + wait_event(rng_done, rng->cleanup_done && + atomic_read(&rng->ref.refcount) == 0); } EXPORT_SYMBOL_GPL(hwrng_unregister); -- cgit v1.1 From ebbbfa248389b176e2e62d8cf91814253849ccc9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Dec 2014 16:50:39 +0800 Subject: hwrng: don't double-check old_rng. Interesting anti-pattern. Signed-off-by: Rusty Russell Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 8d609a0..e384ee3 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -472,14 +472,13 @@ int hwrng_register(struct hwrng *rng) } old_rng = current_rng; + err = 0; if (!old_rng) { err = hwrng_init(rng); if (err) goto out_unlock; set_current_rng(rng); - } - err = 0; - if (!old_rng) { + err = register_miscdev(); if (err) { drop_current_rng(); -- cgit v1.1 From 2d2ec0642a85966b6a299bbcd94707982327ace8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Dec 2014 16:50:40 +0800 Subject: hwrng: don't init list element we're about to add to list. Another interesting anti-pattern. Signed-off-by: Rusty Russell Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index e384ee3..6ec4225 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -485,7 +485,6 @@ int hwrng_register(struct hwrng *rng) goto out_unlock; } } - INIT_LIST_HEAD(&rng->list); list_add_tail(&rng->list, &rng_list); if (old_rng && !rng->init) { -- cgit v1.1 From 77584ee57434813b50fc85cde995a6271a5081b7 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 23 Dec 2014 16:40:17 +1100 Subject: hwrng: core - Use struct completion for cleanup_done There is no point in doing a manual completion for cleanup_done when struct completion fits in perfectly. Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 6ec4225..3dba2cf 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -60,7 +60,6 @@ static DEFINE_MUTEX(rng_mutex); static DEFINE_MUTEX(reading_mutex); static int data_avail; static u8 *rng_buffer, *rng_fillbuf; -static DECLARE_WAIT_QUEUE_HEAD(rng_done); static unsigned short current_quality; static unsigned short default_quality; /* = 0; default to "off" */ @@ -100,10 +99,7 @@ static inline void cleanup_rng(struct kref *kref) if (rng->cleanup) rng->cleanup(rng); - /* cleanup_done should be updated after cleanup finishes */ - smp_wmb(); - rng->cleanup_done = true; - wake_up_all(&rng_done); + complete(&rng->cleanup_done); } static void set_current_rng(struct hwrng *rng) @@ -498,7 +494,7 @@ int hwrng_register(struct hwrng *rng) add_early_randomness(rng); } - rng->cleanup_done = false; + init_completion(&rng->cleanup_done); out_unlock: mutex_unlock(&rng_mutex); @@ -532,9 +528,7 @@ void hwrng_unregister(struct hwrng *rng) } else mutex_unlock(&rng_mutex); - /* Just in case rng is reading right now, wait. */ - wait_event(rng_done, rng->cleanup_done && - atomic_read(&rng->ref.refcount) == 0); + wait_for_completion(&rng->cleanup_done); } EXPORT_SYMBOL_GPL(hwrng_unregister); -- cgit v1.1 From 15b66cd54291186011f733cc750263f320b8a0a4 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 23 Dec 2014 16:40:18 +1100 Subject: hwrng: core - Fix current_rng init/cleanup race yet again The kref solution is still buggy because we were only focusing on the register/unregister race. The same race affects the setting of current_rng through sysfs. This patch fixes it by using kref_get_unless_zero. Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 3dba2cf..42827fd 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -105,7 +105,6 @@ static inline void cleanup_rng(struct kref *kref) static void set_current_rng(struct hwrng *rng) { BUG_ON(!mutex_is_locked(&rng_mutex)); - kref_get(&rng->ref); current_rng = rng; } @@ -150,6 +149,9 @@ static void put_rng(struct hwrng *rng) static inline int hwrng_init(struct hwrng *rng) { + if (kref_get_unless_zero(&rng->ref)) + goto skip_init; + if (rng->init) { int ret; @@ -157,6 +159,11 @@ static inline int hwrng_init(struct hwrng *rng) if (ret) return ret; } + + kref_init(&rng->ref); + reinit_completion(&rng->cleanup_done); + +skip_init: add_early_randomness(rng); current_quality = rng->quality ? : default_quality; @@ -467,6 +474,9 @@ int hwrng_register(struct hwrng *rng) goto out_unlock; } + init_completion(&rng->cleanup_done); + complete(&rng->cleanup_done); + old_rng = current_rng; err = 0; if (!old_rng) { @@ -494,8 +504,6 @@ int hwrng_register(struct hwrng *rng) add_early_randomness(rng); } - init_completion(&rng->cleanup_done); - out_unlock: mutex_unlock(&rng_mutex); out: -- cgit v1.1 From ac3a497f13e42a99ed6fe188ebf2dcc39eb7ac20 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 23 Dec 2014 16:40:19 +1100 Subject: hwrng: core - Do not register device opportunistically Currently we only register the device when a valid RNG is added. However the way it's done is buggy because we test whether there is a current RNG to determine whether we need to register. As the current RNG may be missing due to a reinitialisation error this can lead to a reregistration of the device. As the device already has to handle a NULL current RNG anyway, let's just register the device always and remove the complexity. Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 42827fd..1d342f0 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -372,14 +372,14 @@ static DEVICE_ATTR(rng_available, S_IRUGO, NULL); -static void unregister_miscdev(void) +static void __exit unregister_miscdev(void) { device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available); device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current); misc_deregister(&rng_miscdev); } -static int register_miscdev(void) +static int __init register_miscdev(void) { int err; @@ -484,12 +484,6 @@ int hwrng_register(struct hwrng *rng) if (err) goto out_unlock; set_current_rng(rng); - - err = register_miscdev(); - if (err) { - drop_current_rng(); - goto out_unlock; - } } list_add_tail(&rng->list, &rng_list); @@ -530,7 +524,6 @@ void hwrng_unregister(struct hwrng *rng) if (list_empty(&rng_list)) { mutex_unlock(&rng_mutex); - unregister_miscdev(); if (hwrng_fill) kthread_stop(hwrng_fill); } else @@ -540,16 +533,24 @@ void hwrng_unregister(struct hwrng *rng) } EXPORT_SYMBOL_GPL(hwrng_unregister); -static void __exit hwrng_exit(void) +static int __init hwrng_modinit(void) +{ + return register_miscdev(); +} + +static void __exit hwrng_modexit(void) { mutex_lock(&rng_mutex); BUG_ON(current_rng); kfree(rng_buffer); kfree(rng_fillbuf); mutex_unlock(&rng_mutex); + + unregister_miscdev(); } -module_exit(hwrng_exit); +module_init(hwrng_modinit); +module_exit(hwrng_modexit); MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); MODULE_LICENSE("GPL"); -- cgit v1.1 From ff77c150f71b761dcf29b9d1947df3165d2dc83e Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 23 Dec 2014 16:40:21 +1100 Subject: hwrng: core - Drop current rng in set_current_rng Rather than having callers of set_current_rng call drop_current_rng, we can do it directly in set_current_rng. Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 1d342f0..787ef42 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -70,6 +70,7 @@ 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 void start_khwrngd(void); static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, @@ -105,6 +106,7 @@ static inline void cleanup_rng(struct kref *kref) static void set_current_rng(struct hwrng *rng) { BUG_ON(!mutex_is_locked(&rng_mutex)); + drop_current_rng(); current_rng = rng; } @@ -315,7 +317,6 @@ static ssize_t hwrng_attr_current_store(struct device *dev, err = hwrng_init(rng); if (err) break; - drop_current_rng(); set_current_rng(rng); err = 0; break; -- cgit v1.1 From 90ac41bd40ad0571a10826eb26d53c84bd791f29 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 23 Dec 2014 16:40:22 +1100 Subject: hwrng: core - Move hwrng_init call into set_current_rng We always do hwrng_init in set_current_rng. In fact, our current reference count system relies on this. So make this explicit by moving hwrng_init into set_current_rng. Signed-off-by: Herbert Xu --- drivers/char/hw_random/core.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 787ef42..32a8a86 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -71,6 +71,7 @@ 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); static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, @@ -103,11 +104,20 @@ static inline void cleanup_rng(struct kref *kref) complete(&rng->cleanup_done); } -static void set_current_rng(struct hwrng *rng) +static int set_current_rng(struct hwrng *rng) { + int err; + BUG_ON(!mutex_is_locked(&rng_mutex)); + + err = hwrng_init(rng); + if (err) + return err; + drop_current_rng(); current_rng = rng; + + return 0; } static void drop_current_rng(void) @@ -149,7 +159,7 @@ static void put_rng(struct hwrng *rng) mutex_unlock(&rng_mutex); } -static inline int hwrng_init(struct hwrng *rng) +static int hwrng_init(struct hwrng *rng) { if (kref_get_unless_zero(&rng->ref)) goto skip_init; @@ -310,15 +320,9 @@ static ssize_t hwrng_attr_current_store(struct device *dev, err = -ENODEV; list_for_each_entry(rng, &rng_list, list) { if (strcmp(rng->name, buf) == 0) { - if (rng == current_rng) { - err = 0; - break; - } - err = hwrng_init(rng); - if (err) - break; - set_current_rng(rng); err = 0; + if (rng != current_rng) + err = set_current_rng(rng); break; } } @@ -481,10 +485,9 @@ int hwrng_register(struct hwrng *rng) old_rng = current_rng; err = 0; if (!old_rng) { - err = hwrng_init(rng); + err = set_current_rng(rng); if (err) goto out_unlock; - set_current_rng(rng); } list_add_tail(&rng->list, &rng_list); @@ -518,8 +521,7 @@ void hwrng_unregister(struct hwrng *rng) tail = list_entry(rng_list.prev, struct hwrng, list); - if (hwrng_init(tail) == 0) - set_current_rng(tail); + set_current_rng(tail); } } -- cgit v1.1 From e20016a9c68cebeec7d7dddb005656c8134295c4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 20 Jan 2015 14:35:15 +0200 Subject: hwrng: virtio - drop extra empty line makes code look a bit prettier. Cc: linux-crypto@vger.kernel.org. Signed-off-by: Michael S. Tsirkin Signed-off-by: Herbert Xu --- drivers/char/hw_random/virtio-rng.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 72295ea..3fa2f8a 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -39,7 +39,6 @@ struct virtrng_info { bool hwrng_removed; }; - static void random_recv_done(struct virtqueue *vq) { struct virtrng_info *vi = vq->vdev->priv; -- cgit v1.1