Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jeff Dike and committed by
Linus Torvalds
5d33e4d7 60a2988a

+65 -31
+37 -4
arch/um/drivers/random.c
··· 8 8 #include <linux/sched.h> 9 9 #include <linux/module.h> 10 10 #include <linux/fs.h> 11 + #include <linux/interrupt.h> 11 12 #include <linux/miscdevice.h> 12 13 #include <linux/delay.h> 13 14 #include <asm/uaccess.h> 15 + #include "irq_kern.h" 14 16 #include "os.h" 15 17 16 18 /* 17 19 * core module and version information 18 20 */ 19 21 #define RNG_VERSION "1.0.0" 20 - #define RNG_MODULE_NAME "random" 22 + #define RNG_MODULE_NAME "hw_random" 21 23 22 24 #define RNG_MISCDEV_MINOR 183 /* official */ 23 25 ··· 28 26 * protects against a module being loaded twice at the same time. 29 27 */ 30 28 static int random_fd = -1; 29 + static DECLARE_WAIT_QUEUE_HEAD(host_read_wait); 31 30 32 31 static int rng_dev_open (struct inode *inode, struct file *filp) 33 32 { ··· 40 37 41 38 return 0; 42 39 } 40 + 41 + static atomic_t host_sleep_count = ATOMIC_INIT(0); 43 42 44 43 static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, 45 44 loff_t * offp) ··· 65 60 } 66 61 } 67 62 else if(n == -EAGAIN){ 63 + DECLARE_WAITQUEUE(wait, current); 64 + 68 65 if (filp->f_flags & O_NONBLOCK) 69 66 return ret ? : -EAGAIN; 70 67 71 - if(need_resched()) 72 - schedule_timeout_interruptible(1); 68 + atomic_inc(&host_sleep_count); 69 + reactivate_fd(random_fd, RANDOM_IRQ); 70 + add_sigio_fd(random_fd); 71 + 72 + add_wait_queue(&host_read_wait, &wait); 73 + set_task_state(current, TASK_INTERRUPTIBLE); 74 + 75 + schedule(); 76 + set_task_state(current, TASK_RUNNING); 77 + remove_wait_queue(&host_read_wait, &wait); 78 + 79 + if (atomic_dec_and_test(&host_sleep_count)) { 80 + ignore_sigio_fd(random_fd); 81 + deactivate_fd(random_fd, RANDOM_IRQ); 82 + } 73 83 } 74 84 else return n; 75 85 if (signal_pending (current)) ··· 106 86 &rng_chrdev_ops, 107 87 }; 108 88 89 + static irqreturn_t random_interrupt(int irq, void *data) 90 + { 91 + wake_up(&host_read_wait); 92 + 93 + return IRQ_HANDLED; 94 + } 95 + 109 96 /* 110 97 * rng_init - initialize RNG module 111 98 */ ··· 126 99 127 100 random_fd = err; 128 101 129 - err = os_set_fd_block(random_fd, 0); 102 + err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, 103 + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "random", 104 + NULL); 130 105 if(err) 131 106 goto err_out_cleanup_hw; 107 + 108 + sigio_broken(random_fd, 1); 132 109 133 110 err = misc_register (&rng_miscdev); 134 111 if (err) { ··· 144 113 return err; 145 114 146 115 err_out_cleanup_hw: 116 + os_close_file(random_fd); 147 117 random_fd = -1; 148 118 goto out; 149 119 } ··· 154 122 */ 155 123 static void __exit rng_cleanup (void) 156 124 { 125 + os_close_file(random_fd); 157 126 misc_deregister (&rng_miscdev); 158 127 } 159 128
+1
arch/um/include/os.h
··· 290 290 extern int add_sigio_fd(int fd); 291 291 extern int ignore_sigio_fd(int fd); 292 292 extern void maybe_sigio_broken(int fd, int read); 293 + extern void sigio_broken(int fd, int read); 293 294 294 295 /* sys-x86_64/prctl.c */ 295 296 extern int os_arch_prctl(int pid, int code, unsigned long *addr);
+4 -12
arch/um/include/process.h
··· 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 3 * Licensed under the GPL 4 4 */ 5 5 ··· 8 8 9 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 + 11 14 extern void sig_handler(int sig, struct sigcontext sc); 12 15 extern void alarm_handler(int sig, struct sigcontext sc); 13 16 14 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 - */
+21 -14
arch/um/os-Linux/sigio.c
··· 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 3 * Licensed under the GPL 4 4 */ 5 5 ··· 15 15 #include "kern_util.h" 16 16 #include "init.h" 17 17 #include "os.h" 18 + #include "process.h" 18 19 #include "sigio.h" 19 20 #include "um_malloc.h" 20 21 #include "user.h" ··· 339 338 close(l_write_sigio_fds[1]); 340 339 } 341 340 342 - /* Changed during early boot */ 343 - static int pty_output_sigio = 0; 344 - static int pty_close_sigio = 0; 345 - 346 - void maybe_sigio_broken(int fd, int read) 341 + void sigio_broken(int fd, int read) 347 342 { 348 343 int err; 349 - 350 - if (!isatty(fd)) 351 - return; 352 - 353 - if ((read || pty_output_sigio) && (!read || pty_close_sigio)) 354 - return; 355 344 356 345 write_sigio_workaround(); 357 346 ··· 361 370 sigio_unlock(); 362 371 } 363 372 373 + /* Changed during early boot */ 374 + static int pty_output_sigio; 375 + static int pty_close_sigio; 376 + 377 + void maybe_sigio_broken(int fd, int read) 378 + { 379 + if (!isatty(fd)) 380 + return; 381 + 382 + if ((read || pty_output_sigio) && (!read || pty_close_sigio)) 383 + return; 384 + 385 + sigio_broken(fd, read); 386 + } 387 + 364 388 static void sigio_cleanup(void) 365 389 { 366 390 if (write_sigio_pid == -1) ··· 389 383 __uml_exitcall(sigio_cleanup); 390 384 391 385 /* Used as a flag during SIGIO testing early in boot */ 392 - static volatile int got_sigio = 0; 386 + static int got_sigio; 393 387 394 388 static void __init handler(int sig) 395 389 { ··· 504 498 if (errno != EAGAIN) 505 499 printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n", 506 500 errno); 507 - while (((n = read(slave, buf, sizeof(buf))) > 0) && !got_sigio) 501 + while (((n = read(slave, buf, sizeof(buf))) > 0) && 502 + !({ barrier(); got_sigio; })) 508 503 ; 509 504 510 505 if (got_sigio) {
+2 -1
include/asm-um/irq.h
··· 15 15 #define SIGIO_WRITE_IRQ 11 16 16 #define TELNETD_IRQ 12 17 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 21 #define NR_IRQS (LAST_IRQ + 1) 21 22 22 23 #endif