Merge branch 'upstream-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
[pandora-kernel.git] / arch / um / os-Linux / sigio.c
index 00e9388..925a652 100644 (file)
@@ -19,6 +19,7 @@
 #include "user_util.h"
 #include "sigio.h"
 #include "os.h"
+#include "um_malloc.h"
 
 /* Protected by sigio_lock(), also used by sigio_cleanup, which is an
  * exitcall.
@@ -43,17 +44,9 @@ struct pollfds {
 /* Protected by sigio_lock().  Used by the sigio thread, but the UML thread
  * synchronizes with it.
  */
-struct pollfds current_poll = {
-       .poll           = NULL,
-       .size           = 0,
-       .used           = 0
-};
-
-struct pollfds next_poll = {
-       .poll           = NULL,
-       .size           = 0,
-       .used           = 0
-};
+static struct pollfds current_poll;
+static struct pollfds next_poll;
+static struct pollfds all_sigio_fds;
 
 static int write_sigio_thread(void *unused)
 {
@@ -78,7 +71,8 @@ static int write_sigio_thread(void *unused)
                                n = os_read_file(sigio_private[1], &c, sizeof(c));
                                if(n != sizeof(c))
                                        printk("write_sigio_thread : "
-                                              "read failed, err = %d\n", -n);
+                                              "read on socket failed, "
+                                              "err = %d\n", -n);
                                tmp = current_poll;
                                current_poll = next_poll;
                                next_poll = tmp;
@@ -93,35 +87,36 @@ static int write_sigio_thread(void *unused)
 
                        n = os_write_file(respond_fd, &c, sizeof(c));
                        if(n != sizeof(c))
-                               printk("write_sigio_thread : write failed, "
-                                      "err = %d\n", -n);
+                               printk("write_sigio_thread : write on socket "
+                                      "failed, err = %d\n", -n);
                }
        }
 
        return 0;
 }
 
-static int need_poll(int n)
+static int need_poll(struct pollfds *polls, int n)
 {
-       if(n <= next_poll.size){
-               next_poll.used = n;
-               return(0);
+       if(n <= polls->size){
+               polls->used = n;
+               return 0;
        }
-       kfree(next_poll.poll);
-       next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
-       if(next_poll.poll == NULL){
+       kfree(polls->poll);
+       polls->poll = um_kmalloc_atomic(n * sizeof(struct pollfd));
+       if(polls->poll == NULL){
                printk("need_poll : failed to allocate new pollfds\n");
-               next_poll.size = 0;
-               next_poll.used = 0;
-               return(-1);
+               polls->size = 0;
+               polls->used = 0;
+               return -ENOMEM;
        }
-       next_poll.size = n;
-       next_poll.used = n;
-       return(0);
+       polls->size = n;
+       polls->used = n;
+       return 0;
 }
 
 /* Must be called with sigio_lock held, because it's needed by the marked
- * critical section. */
+ * critical section.
+ */
 static void update_thread(void)
 {
        unsigned long flags;
@@ -156,34 +151,39 @@ static void update_thread(void)
        set_signals(flags);
 }
 
-int add_sigio_fd(int fd, int read)
+int add_sigio_fd(int fd)
 {
-       int err = 0, i, n, events;
+       struct pollfd *p;
+       int err = 0, i, n;
 
        sigio_lock();
+       for(i = 0; i < all_sigio_fds.used; i++){
+               if(all_sigio_fds.poll[i].fd == fd)
+                       break;
+       }
+       if(i == all_sigio_fds.used)
+               goto out;
+
+       p = &all_sigio_fds.poll[i];
+
        for(i = 0; i < current_poll.used; i++){
                if(current_poll.poll[i].fd == fd)
                        goto out;
        }
 
        n = current_poll.used + 1;
-       err = need_poll(n);
+       err = need_poll(&next_poll, n);
        if(err)
                goto out;
 
        for(i = 0; i < current_poll.used; i++)
                next_poll.poll[i] = current_poll.poll[i];
 
-       if(read) events = POLLIN;
-       else events = POLLOUT;
-
-       next_poll.poll[n - 1] = ((struct pollfd) { .fd          = fd,
-                                                  .events      = events,
-                                                  .revents     = 0 });
+       next_poll.poll[n - 1] = *p;
        update_thread();
  out:
        sigio_unlock();
-       return(err);
+       return err;
 }
 
 int ignore_sigio_fd(int fd)
@@ -191,6 +191,13 @@ int ignore_sigio_fd(int fd)
        struct pollfd *p;
        int err = 0, i, n = 0;
 
+       /* This is called from exitcalls elsewhere in UML - if
+        * sigio_cleanup has already run, then update_thread will hang
+        * or fail because the thread is no longer running.
+        */
+       if(write_sigio_pid == -1)
+               return -EIO;
+
        sigio_lock();
        for(i = 0; i < current_poll.used; i++){
                if(current_poll.poll[i].fd == fd) break;
@@ -198,24 +205,20 @@ int ignore_sigio_fd(int fd)
        if(i == current_poll.used)
                goto out;
 
-       err = need_poll(current_poll.used - 1);
+       err = need_poll(&next_poll, current_poll.used - 1);
        if(err)
                goto out;
 
        for(i = 0; i < current_poll.used; i++){
                p = &current_poll.poll[i];
-               if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i];
-       }
-       if(n == i){
-               printk("ignore_sigio_fd : fd %d not found\n", fd);
-               err = -1;
-               goto out;
+               if(p->fd != fd)
+                       next_poll.poll[n++] = *p;
        }
 
        update_thread();
  out:
        sigio_unlock();
-       return(err);
+       return err;
 }
 
 static struct pollfd *setup_initial_poll(int fd)
@@ -227,13 +230,13 @@ static struct pollfd *setup_initial_poll(int fd)
                printk("setup_initial_poll : failed to allocate poll\n");
                return NULL;
        }
-       *p = ((struct pollfd) { .fd     = fd,
+       *p = ((struct pollfd) { .fd             = fd,
                                .events         = POLLIN,
                                .revents        = 0 });
        return p;
 }
 
-void write_sigio_workaround(void)
+static void write_sigio_workaround(void)
 {
        unsigned long stack;
        struct pollfd *p;
@@ -314,10 +317,38 @@ out_close1:
        close(l_write_sigio_fds[1]);
 }
 
-void sigio_cleanup(void)
+void maybe_sigio_broken(int fd, int read)
+{
+       int err;
+
+       if(!isatty(fd))
+               return;
+
+       if((read || pty_output_sigio) && (!read || pty_close_sigio))
+               return;
+
+       write_sigio_workaround();
+
+       sigio_lock();
+       err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1);
+       if(err){
+               printk("maybe_sigio_broken - failed to add pollfd\n");
+               goto out;
+       }
+       all_sigio_fds.poll[all_sigio_fds.used++] =
+               ((struct pollfd) { .fd          = fd,
+                                  .events      = read ? POLLIN : POLLOUT,
+                                  .revents     = 0 });
+out:
+       sigio_unlock();
+}
+
+static void sigio_cleanup(void)
 {
        if(write_sigio_pid != -1){
                os_kill_process(write_sigio_pid, 1);
                write_sigio_pid = -1;
        }
 }
+
+__uml_exitcall(sigio_cleanup);