aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2008-05-12 17:01:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-05-13 11:02:22 -0400
commit5d33e4d7fd9a52d2673e5c730eab81856e100a74 (patch)
treec4d5014fa21ebde900441b4a5b51092a09c47823
parent60a2988aea701a6424809a5432bf068667aac177 (diff)
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 <jdike@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/um/drivers/random.c41
-rw-r--r--arch/um/include/os.h1
-rw-r--r--arch/um/include/process.h16
-rw-r--r--arch/um/os-Linux/sigio.c35
-rw-r--r--include/asm-um/irq.h3
5 files changed, 65 insertions, 31 deletions
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 @@
8#include <linux/sched.h> 8#include <linux/sched.h>
9#include <linux/module.h> 9#include <linux/module.h>
10#include <linux/fs.h> 10#include <linux/fs.h>
11#include <linux/interrupt.h>
11#include <linux/miscdevice.h> 12#include <linux/miscdevice.h>
12#include <linux/delay.h> 13#include <linux/delay.h>
13#include <asm/uaccess.h> 14#include <asm/uaccess.h>
15#include "irq_kern.h"
14#include "os.h" 16#include "os.h"
15 17
16/* 18/*
17 * core module and version information 19 * core module and version information
18 */ 20 */
19#define RNG_VERSION "1.0.0" 21#define RNG_VERSION "1.0.0"
20#define RNG_MODULE_NAME "random" 22#define RNG_MODULE_NAME "hw_random"
21 23
22#define RNG_MISCDEV_MINOR 183 /* official */ 24#define RNG_MISCDEV_MINOR 183 /* official */
23 25
@@ -26,6 +28,7 @@
26 * protects against a module being loaded twice at the same time. 28 * protects against a module being loaded twice at the same time.
27 */ 29 */
28static int random_fd = -1; 30static int random_fd = -1;
31static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);
29 32
30static int rng_dev_open (struct inode *inode, struct file *filp) 33static int rng_dev_open (struct inode *inode, struct file *filp)
31{ 34{
@@ -38,6 +41,8 @@ static int rng_dev_open (struct inode *inode, struct file *filp)
38 return 0; 41 return 0;
39} 42}
40 43
44static atomic_t host_sleep_count = ATOMIC_INIT(0);
45
41static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, 46static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
42 loff_t * offp) 47 loff_t * offp)
43{ 48{
@@ -60,11 +65,26 @@ static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
60 } 65 }
61 } 66 }
62 else if(n == -EAGAIN){ 67 else if(n == -EAGAIN){
68 DECLARE_WAITQUEUE(wait, current);
69
63 if (filp->f_flags & O_NONBLOCK) 70 if (filp->f_flags & O_NONBLOCK)
64 return ret ? : -EAGAIN; 71 return ret ? : -EAGAIN;
65 72
66 if(need_resched()) 73 atomic_inc(&host_sleep_count);
67 schedule_timeout_interruptible(1); 74 reactivate_fd(random_fd, RANDOM_IRQ);
75 add_sigio_fd(random_fd);
76
77 add_wait_queue(&host_read_wait, &wait);
78 set_task_state(current, TASK_INTERRUPTIBLE);
79
80 schedule();
81 set_task_state(current, TASK_RUNNING);
82 remove_wait_queue(&host_read_wait, &wait);
83
84 if (atomic_dec_and_test(&host_sleep_count)) {
85 ignore_sigio_fd(random_fd);
86 deactivate_fd(random_fd, RANDOM_IRQ);
87 }
68 } 88 }
69 else return n; 89 else return n;
70 if (signal_pending (current)) 90 if (signal_pending (current))
@@ -86,6 +106,13 @@ static struct miscdevice rng_miscdev = {
86 &rng_chrdev_ops, 106 &rng_chrdev_ops,
87}; 107};
88 108
109static irqreturn_t random_interrupt(int irq, void *data)
110{
111 wake_up(&host_read_wait);
112
113 return IRQ_HANDLED;
114}
115
89/* 116/*
90 * rng_init - initialize RNG module 117 * rng_init - initialize RNG module
91 */ 118 */
@@ -99,10 +126,14 @@ static int __init rng_init (void)
99 126
100 random_fd = err; 127 random_fd = err;
101 128
102 err = os_set_fd_block(random_fd, 0); 129 err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
130 IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "random",
131 NULL);
103 if(err) 132 if(err)
104 goto err_out_cleanup_hw; 133 goto err_out_cleanup_hw;
105 134
135 sigio_broken(random_fd, 1);
136
106 err = misc_register (&rng_miscdev); 137 err = misc_register (&rng_miscdev);
107 if (err) { 138 if (err) {
108 printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n"); 139 printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n");
@@ -113,6 +144,7 @@ static int __init rng_init (void)
113 return err; 144 return err;
114 145
115 err_out_cleanup_hw: 146 err_out_cleanup_hw:
147 os_close_file(random_fd);
116 random_fd = -1; 148 random_fd = -1;
117 goto out; 149 goto out;
118} 150}
@@ -122,6 +154,7 @@ static int __init rng_init (void)
122 */ 154 */
123static void __exit rng_cleanup (void) 155static void __exit rng_cleanup (void)
124{ 156{
157 os_close_file(random_fd);
125 misc_deregister (&rng_miscdev); 158 misc_deregister (&rng_miscdev);
126} 159}
127 160
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 32c799e3a495..e2716ac8889a 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -290,6 +290,7 @@ extern void os_set_ioignore(void);
290extern int add_sigio_fd(int fd); 290extern int add_sigio_fd(int fd);
291extern int ignore_sigio_fd(int fd); 291extern int ignore_sigio_fd(int fd);
292extern void maybe_sigio_broken(int fd, int read); 292extern void maybe_sigio_broken(int fd, int read);
293extern void sigio_broken(int fd, int read);
293 294
294/* sys-x86_64/prctl.c */ 295/* sys-x86_64/prctl.c */
295extern int os_arch_prctl(int pid, int code, unsigned long *addr); 296extern int os_arch_prctl(int pid, int code, unsigned long *addr);
diff --git a/arch/um/include/process.h b/arch/um/include/process.h
index 5af9157ff54f..838b4802ce53 100644
--- a/arch/um/include/process.h
+++ b/arch/um/include/process.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) 2 * Copyright (C) 2000 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Licensed under the GPL 3 * Licensed under the GPL
4 */ 4 */
5 5
@@ -8,18 +8,10 @@
8 8
9#include <signal.h> 9#include <signal.h>
10 10
11/* Copied from linux/compiler-gcc.h since we can't include it directly */
12#define barrier() __asm__ __volatile__("": : :"memory")
13
11extern void sig_handler(int sig, struct sigcontext sc); 14extern void sig_handler(int sig, struct sigcontext sc);
12extern void alarm_handler(int sig, struct sigcontext sc); 15extern void alarm_handler(int sig, struct sigcontext sc);
13 16
14#endif 17#endif
15
16/*
17 * Overrides for Emacs so that we follow Linus's tabbing style.
18 * Emacs will notice this stuff at the end of the file and automatically
19 * adjust the settings for this buffer only. This must remain at the end
20 * of the file.
21 * ---------------------------------------------------------------------------
22 * Local variables:
23 * c-file-style: "linux"
24 * End:
25 */
diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c
index 0578481983c4..eb8f2e4be192 100644
--- a/arch/um/os-Linux/sigio.c
+++ b/arch/um/os-Linux/sigio.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 2 * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Licensed under the GPL 3 * Licensed under the GPL
4 */ 4 */
5 5
@@ -15,6 +15,7 @@
15#include "kern_util.h" 15#include "kern_util.h"
16#include "init.h" 16#include "init.h"
17#include "os.h" 17#include "os.h"
18#include "process.h"
18#include "sigio.h" 19#include "sigio.h"
19#include "um_malloc.h" 20#include "um_malloc.h"
20#include "user.h" 21#include "user.h"
@@ -338,20 +339,10 @@ out_close1:
338 close(l_write_sigio_fds[1]); 339 close(l_write_sigio_fds[1]);
339} 340}
340 341
341/* Changed during early boot */ 342void sigio_broken(int fd, int read)
342static int pty_output_sigio = 0;
343static int pty_close_sigio = 0;
344
345void maybe_sigio_broken(int fd, int read)
346{ 343{
347 int err; 344 int err;
348 345
349 if (!isatty(fd))
350 return;
351
352 if ((read || pty_output_sigio) && (!read || pty_close_sigio))
353 return;
354
355 write_sigio_workaround(); 346 write_sigio_workaround();
356 347
357 sigio_lock(); 348 sigio_lock();
@@ -370,6 +361,21 @@ out:
370 sigio_unlock(); 361 sigio_unlock();
371} 362}
372 363
364/* Changed during early boot */
365static int pty_output_sigio;
366static int pty_close_sigio;
367
368void maybe_sigio_broken(int fd, int read)
369{
370 if (!isatty(fd))
371 return;
372
373 if ((read || pty_output_sigio) && (!read || pty_close_sigio))
374 return;
375
376 sigio_broken(fd, read);
377}
378
373static void sigio_cleanup(void) 379static void sigio_cleanup(void)
374{ 380{
375 if (write_sigio_pid == -1) 381 if (write_sigio_pid == -1)
@@ -383,7 +389,7 @@ static void sigio_cleanup(void)
383__uml_exitcall(sigio_cleanup); 389__uml_exitcall(sigio_cleanup);
384 390
385/* Used as a flag during SIGIO testing early in boot */ 391/* Used as a flag during SIGIO testing early in boot */
386static volatile int got_sigio = 0; 392static int got_sigio;
387 393
388static void __init handler(int sig) 394static void __init handler(int sig)
389{ 395{
@@ -498,7 +504,8 @@ static void tty_output(int master, int slave)
498 if (errno != EAGAIN) 504 if (errno != EAGAIN)
499 printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n", 505 printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n",
500 errno); 506 errno);
501 while (((n = read(slave, buf, sizeof(buf))) > 0) && !got_sigio) 507 while (((n = read(slave, buf, sizeof(buf))) > 0) &&
508 !({ barrier(); got_sigio; }))
502 ; 509 ;
503 510
504 if (got_sigio) { 511 if (got_sigio) {
diff --git a/include/asm-um/irq.h b/include/asm-um/irq.h
index de389a477cdd..4a2037f8204b 100644
--- a/include/asm-um/irq.h
+++ b/include/asm-um/irq.h
@@ -15,8 +15,9 @@
15#define SIGIO_WRITE_IRQ 11 15#define SIGIO_WRITE_IRQ 11
16#define TELNETD_IRQ 12 16#define TELNETD_IRQ 12
17#define XTERM_IRQ 13 17#define XTERM_IRQ 13
18#define RANDOM_IRQ 14
18 19
19#define LAST_IRQ XTERM_IRQ 20#define LAST_IRQ RANDOM_IRQ
20#define NR_IRQS (LAST_IRQ + 1) 21#define NR_IRQS (LAST_IRQ + 1)
21 22
22#endif 23#endif