This commit was generated by cvs2svn to compensate for changes in r1129,
[linux-2.6.git] / arch / um / kernel / sigio_user.c
index 82d19b4..f7b18e1 100644 (file)
@@ -18,7 +18,6 @@
 #include "kern_util.h"
 #include "user_util.h"
 #include "sigio.h"
-#include "helper.h"
 #include "os.h"
 
 /* Changed during early boot */
@@ -108,12 +107,14 @@ static void tty_output(int master, int slave)
                panic("check_sigio : write failed, errno = %d\n", errno);
        while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ;
 
-       if(got_sigio){
+       if (got_sigio) {
                printk("Yes\n");
                pty_output_sigio = 1;
+       } else if (n == -EAGAIN) {
+               printk("No, enabling workaround\n");
+       } else {
+               panic("check_sigio : read failed, err = %d\n", n);
        }
-       else if(n == -EAGAIN) printk("No, enabling workaround\n");
-       else panic("check_sigio : read failed, err = %d\n", n);
 }
 
 static void tty_close(int master, int slave)
@@ -180,6 +181,7 @@ static int write_sigio_thread(void *unused)
        int i, n, respond_fd;
        char c;
 
+        signal(SIGWINCH, SIG_IGN);
        fds = &current_poll;
        while(1){
                n = poll(fds->poll, fds->used, -1);
@@ -214,6 +216,8 @@ static int write_sigio_thread(void *unused)
                                       "err = %d\n", -n);
                }
        }
+
+       return 0;
 }
 
 static int need_poll(int n)
@@ -222,7 +226,7 @@ static int need_poll(int n)
                next_poll.used = n;
                return(0);
        }
-       if(next_poll.poll != NULL) kfree(next_poll.poll);
+       kfree(next_poll.poll);
        next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
        if(next_poll.poll == NULL){
                printk("need_poll : failed to allocate new pollfds\n");
@@ -235,6 +239,8 @@ static int need_poll(int n)
        return(0);
 }
 
+/* Must be called with sigio_lock held, because it's needed by the marked
+ * critical section. */
 static void update_thread(void)
 {
        unsigned long flags;
@@ -257,7 +263,7 @@ static void update_thread(void)
        set_signals(flags);
        return;
  fail:
-       sigio_lock();
+       /* Critical section start */
        if(write_sigio_pid != -1) 
                os_kill_process(write_sigio_pid, 1);
        write_sigio_pid = -1;
@@ -265,7 +271,7 @@ static void update_thread(void)
        os_close_file(sigio_private[1]);
        os_close_file(write_sigio_fds[0]);
        os_close_file(write_sigio_fds[1]);
-       sigio_unlock();
+       /* Critical section end */
        set_signals(flags);
 }
 
@@ -331,70 +337,103 @@ int ignore_sigio_fd(int fd)
        return(err);
 }
 
-static int setup_initial_poll(int fd)
+static struct pollfd* setup_initial_poll(int fd)
 {
        struct pollfd *p;
 
        p = um_kmalloc(sizeof(struct pollfd));
-       if(p == NULL){
+       if (p == NULL) {
                printk("setup_initial_poll : failed to allocate poll\n");
-               return(-1);
+               return NULL;
        }
        *p = ((struct pollfd) { .fd     = fd,
                                .events         = POLLIN,
                                .revents        = 0 });
-       current_poll = ((struct pollfds) { .poll        = p,
-                                          .used        = 1,
-                                          .size        = 1 });
-       return(0);
+       return p;
 }
 
 void write_sigio_workaround(void)
 {
        unsigned long stack;
+       struct pollfd *p;
        int err;
+       int l_write_sigio_fds[2];
+       int l_sigio_private[2];
+       int l_write_sigio_pid;
 
+       /* We call this *tons* of times - and most ones we must just fail. */
        sigio_lock();
-       if(write_sigio_pid != -1)
-               goto out;
+       l_write_sigio_pid = write_sigio_pid;
+       sigio_unlock();
+
+       if (l_write_sigio_pid != -1)
+               return;
 
-       err = os_pipe(write_sigio_fds, 1, 1);
+       err = os_pipe(l_write_sigio_fds, 1, 1);
        if(err < 0){
                printk("write_sigio_workaround - os_pipe 1 failed, "
                       "err = %d\n", -err);
-               goto out;
+               return;
        }
-       err = os_pipe(sigio_private, 1, 1);
+       err = os_pipe(l_sigio_private, 1, 1);
        if(err < 0){
-               printk("write_sigio_workaround - os_pipe 2 failed, "
+               printk("write_sigio_workaround - os_pipe 1 failed, "
                       "err = %d\n", -err);
                goto out_close1;
        }
-       if(setup_initial_poll(sigio_private[1]))
+
+       p = setup_initial_poll(l_sigio_private[1]);
+       if(!p)
                goto out_close2;
 
-       write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, 
+       sigio_lock();
+
+       /* Did we race? Don't try to optimize this, please, it's not so likely
+        * to happen, and no more than once at the boot. */
+       if(write_sigio_pid != -1)
+               goto out_unlock;
+
+       write_sigio_pid = run_helper_thread(write_sigio_thread, NULL,
                                            CLONE_FILES | CLONE_VM, &stack, 0);
 
-       if(write_sigio_pid < 0) goto out_close2;
+       if (write_sigio_pid < 0)
+               goto out_clear;
 
-       if(write_sigio_irq(write_sigio_fds[0])) 
+       if (write_sigio_irq(l_write_sigio_fds[0]))
                goto out_kill;
 
- out:
+       /* Success, finally. */
+       memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds));
+       memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private));
+
+       current_poll = ((struct pollfds) { .poll        = p,
+                                          .used        = 1,
+                                          .size        = 1 });
+
        sigio_unlock();
        return;
 
  out_kill:
-       os_kill_process(write_sigio_pid, 1);
+       l_write_sigio_pid = write_sigio_pid;
        write_sigio_pid = -1;
+       sigio_unlock();
+       /* Going to call waitpid, avoid holding the lock. */
+       os_kill_process(l_write_sigio_pid, 1);
+       goto out_free;
+
+ out_clear:
+       write_sigio_pid = -1;
+ out_unlock:
+       sigio_unlock();
+ out_free:
+       kfree(p);
  out_close2:
-       os_close_file(sigio_private[0]);
-       os_close_file(sigio_private[1]);
+       os_close_file(l_sigio_private[0]);
+       os_close_file(l_sigio_private[1]);
  out_close1:
-       os_close_file(write_sigio_fds[0]);
-       os_close_file(write_sigio_fds[1]);
-       sigio_unlock();
+       os_close_file(l_write_sigio_fds[0]);
+       os_close_file(l_write_sigio_fds[1]);
+       return;
 }
 
 int read_sigio_fd(int fd)
@@ -418,19 +457,10 @@ int read_sigio_fd(int fd)
 
 static void sigio_cleanup(void)
 {
-       if(write_sigio_pid != -1)
+       if (write_sigio_pid != -1) {
                os_kill_process(write_sigio_pid, 1);
+               write_sigio_pid = -1;
+       }
 }
 
 __uml_exitcall(sigio_cleanup);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */