From 5d33e4d7fd9a52d2673e5c730eab81856e100a74 Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Mon, 12 May 2008 14:01:58 -0700 Subject: uml: random driver fixes The random driver would essentially hang if the host's /dev/random returned -EAGAIN. There was a test of need_resched followed by a schedule inside the loop, but that didn't help and it's the wrong way to work anyway. The right way is to ask for an interrupt when there is input available from the host and handle it then rather than polling. Now, when the host's /dev/random returns -EAGAIN, the driver asks for a wakeup when there's randomness available again and sleeps. The interrupt routine just wakes up whatever processes are sleeping on host_read_wait. There is an atomic_t, host_sleep_count, which counts the number of processes waiting for randomness. When this reaches zero, the interrupt is disabled. An added complication is that async I/O notification was only recently added to /dev/random (by me), so essentially all hosts will lack it. So, we use the sigio workaround here, which is to have a separate thread poll on the descriptor and send an interrupt when there is input on it. This mechanism is activated when a process gets -EAGAIN (activating this multiple times is harmless, if a bit wasteful) and deactivated by the last process still waiting. The module name was changed from "random" to "hw_random" in order for udev to recognize it. The sigio workaround needed some changes. sigio_broken was added for cases when we know that async notification doesn't work. This is now called from maybe_sigio_broken, which deals with pts devices. Signed-off-by: Jeff Dike Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/drivers/random.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'arch/um/drivers/random.c') diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c index 71f0959c1535..f92b7c81eb00 100644 --- a/arch/um/drivers/random.c +++ b/arch/um/drivers/random.c @@ -8,16 +8,18 @@ #include #include #include +#include #include #include #include +#include "irq_kern.h" #include "os.h" /* * core module and version information */ #define RNG_VERSION "1.0.0" -#define RNG_MODULE_NAME "random" +#define RNG_MODULE_NAME "hw_random" #define RNG_MISCDEV_MINOR 183 /* official */ @@ -26,6 +28,7 @@ * protects against a module being loaded twice at the same time. */ static int random_fd = -1; +static DECLARE_WAIT_QUEUE_HEAD(host_read_wait); static int rng_dev_open (struct inode *inode, struct file *filp) { @@ -38,6 +41,8 @@ static int rng_dev_open (struct inode *inode, struct file *filp) return 0; } +static atomic_t host_sleep_count = ATOMIC_INIT(0); + static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, loff_t * offp) { @@ -60,11 +65,26 @@ static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, } } else if(n == -EAGAIN){ + DECLARE_WAITQUEUE(wait, current); + if (filp->f_flags & O_NONBLOCK) return ret ? : -EAGAIN; - if(need_resched()) - schedule_timeout_interruptible(1); + atomic_inc(&host_sleep_count); + reactivate_fd(random_fd, RANDOM_IRQ); + add_sigio_fd(random_fd); + + add_wait_queue(&host_read_wait, &wait); + set_task_state(current, TASK_INTERRUPTIBLE); + + schedule(); + set_task_state(current, TASK_RUNNING); + remove_wait_queue(&host_read_wait, &wait); + + if (atomic_dec_and_test(&host_sleep_count)) { + ignore_sigio_fd(random_fd); + deactivate_fd(random_fd, RANDOM_IRQ); + } } else return n; if (signal_pending (current)) @@ -86,6 +106,13 @@ static struct miscdevice rng_miscdev = { &rng_chrdev_ops, }; +static irqreturn_t random_interrupt(int irq, void *data) +{ + wake_up(&host_read_wait); + + return IRQ_HANDLED; +} + /* * rng_init - initialize RNG module */ @@ -99,10 +126,14 @@ static int __init rng_init (void) random_fd = err; - err = os_set_fd_block(random_fd, 0); + err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "random", + NULL); if(err) goto err_out_cleanup_hw; + sigio_broken(random_fd, 1); + err = misc_register (&rng_miscdev); if (err) { printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n"); @@ -113,6 +144,7 @@ static int __init rng_init (void) return err; err_out_cleanup_hw: + os_close_file(random_fd); random_fd = -1; goto out; } @@ -122,6 +154,7 @@ static int __init rng_init (void) */ static void __exit rng_cleanup (void) { + os_close_file(random_fd); misc_deregister (&rng_miscdev); } -- cgit v1.2.3 From 3d88958e01e71bb14a367db75f12f7a59c068f02 Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Mon, 12 May 2008 14:01:59 -0700 Subject: uml: style fixes in the random driver Give random.c a style workover while I'm changing it. Signed-off-by: Jeff Dike Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/drivers/random.c | 79 +++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 38 deletions(-) (limited to 'arch/um/drivers/random.c') diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c index f92b7c81eb00..4949044773ba 100644 --- a/arch/um/drivers/random.c +++ b/arch/um/drivers/random.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2005 Jeff Dike */ +/* Copyright (C) 2005 - 2008 Jeff Dike */ + /* Much of this ripped from drivers/char/hw_random.c, see there for other * copyright. * @@ -35,7 +36,7 @@ static int rng_dev_open (struct inode *inode, struct file *filp) /* enforce read-only access to this chrdev */ if ((filp->f_mode & FMODE_READ) == 0) return -EINVAL; - if (filp->f_mode & FMODE_WRITE) + if ((filp->f_mode & FMODE_WRITE) != 0) return -EINVAL; return 0; @@ -44,31 +45,31 @@ static int rng_dev_open (struct inode *inode, struct file *filp) static atomic_t host_sleep_count = ATOMIC_INIT(0); static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, - loff_t * offp) + loff_t *offp) { - u32 data; - int n, ret = 0, have_data; - - while(size){ - n = os_read_file(random_fd, &data, sizeof(data)); - if(n > 0){ - have_data = n; - while (have_data && size) { - if (put_user((u8)data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - have_data--; - data>>=8; - } - } - else if(n == -EAGAIN){ + u32 data; + int n, ret = 0, have_data; + + while (size) { + n = os_read_file(random_fd, &data, sizeof(data)); + if (n > 0) { + have_data = n; + while (have_data && size) { + if (put_user((u8) data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data >>= 8; + } + } + else if (n == -EAGAIN) { DECLARE_WAITQUEUE(wait, current); - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; atomic_inc(&host_sleep_count); reactivate_fd(random_fd, RANDOM_IRQ); @@ -85,8 +86,10 @@ static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, ignore_sigio_fd(random_fd); deactivate_fd(random_fd, RANDOM_IRQ); } - } - else return n; + } + else + return n; + if (signal_pending (current)) return ret ? : -ERESTARTSYS; } @@ -120,33 +123,33 @@ static int __init rng_init (void) { int err; - err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); - if(err < 0) - goto out; + err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); + if (err < 0) + goto out; - random_fd = err; + random_fd = err; err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "random", NULL); - if(err) + if (err) goto err_out_cleanup_hw; sigio_broken(random_fd, 1); err = misc_register (&rng_miscdev); if (err) { - printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n"); + printk (KERN_ERR RNG_MODULE_NAME ": misc device register " + "failed\n"); goto err_out_cleanup_hw; } +out: + return err; - out: - return err; - - err_out_cleanup_hw: +err_out_cleanup_hw: os_close_file(random_fd); - random_fd = -1; - goto out; + random_fd = -1; + goto out; } /* -- cgit v1.2.3