Merge branch 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / arch / um / drivers / line.c
index d51c404..364c8a1 100644 (file)
@@ -399,8 +399,8 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
  * is done under a spinlock.  Checking whether the device is in use is
  * line->tty->count > 1, also under the spinlock.
  *
- * tty->count serves to decide whether the device should be enabled or
- * disabled on the host.  If it's equal to 1, then we are doing the
+ * line->count serves to decide whether the device should be enabled or
+ * disabled on the host.  If it's equal to 0, then we are doing the
  * first open or last close.  Otherwise, open and close just return.
  */
 
@@ -414,16 +414,16 @@ int line_open(struct line *lines, struct tty_struct *tty)
                goto out_unlock;
 
        err = 0;
-       if (tty->count > 1)
+       if (line->count++)
                goto out_unlock;
 
-       spin_unlock(&line->count_lock);
-
+       BUG_ON(tty->driver_data);
        tty->driver_data = line;
        line->tty = tty;
 
+       spin_unlock(&line->count_lock);
        err = enable_chan(line);
-       if (err)
+       if (err) /* line_close() will be called by our caller */
                return err;
 
        INIT_DELAYED_WORK(&line->task, line_timer_cb);
@@ -436,7 +436,7 @@ int line_open(struct line *lines, struct tty_struct *tty)
        chan_window_size(&line->chan_list, &tty->winsize.ws_row,
                         &tty->winsize.ws_col);
 
-       return err;
+       return 0;
 
 out_unlock:
        spin_unlock(&line->count_lock);
@@ -460,17 +460,16 @@ void line_close(struct tty_struct *tty, struct file * filp)
        flush_buffer(line);
 
        spin_lock(&line->count_lock);
-       if (!line->valid)
-               goto out_unlock;
+       BUG_ON(!line->valid);
 
-       if (tty->count > 1)
+       if (--line->count)
                goto out_unlock;
 
-       spin_unlock(&line->count_lock);
-
        line->tty = NULL;
        tty->driver_data = NULL;
 
+       spin_unlock(&line->count_lock);
+
        if (line->sigio) {
                unregister_winch(tty);
                line->sigio = 0;
@@ -498,7 +497,7 @@ static int setup_one_line(struct line *lines, int n, char *init, int init_prio,
 
        spin_lock(&line->count_lock);
 
-       if (line->tty != NULL) {
+       if (line->count) {
                *error_out = "Device is already open";
                goto out;
        }
@@ -722,41 +721,53 @@ struct winch {
        int pid;
        struct tty_struct *tty;
        unsigned long stack;
+       struct work_struct work;
 };
 
-static void free_winch(struct winch *winch, int free_irq_ok)
+static void __free_winch(struct work_struct *work)
 {
-       if (free_irq_ok)
-               free_irq(WINCH_IRQ, winch);
-
-       list_del(&winch->list);
+       struct winch *winch = container_of(work, struct winch, work);
+       free_irq(WINCH_IRQ, winch);
 
        if (winch->pid != -1)
                os_kill_process(winch->pid, 1);
-       if (winch->fd != -1)
-               os_close_file(winch->fd);
        if (winch->stack != 0)
                free_stack(winch->stack, 0);
        kfree(winch);
 }
 
+static void free_winch(struct winch *winch)
+{
+       int fd = winch->fd;
+       winch->fd = -1;
+       if (fd != -1)
+               os_close_file(fd);
+       list_del(&winch->list);
+       __free_winch(&winch->work);
+}
+
 static irqreturn_t winch_interrupt(int irq, void *data)
 {
        struct winch *winch = data;
        struct tty_struct *tty;
        struct line *line;
+       int fd = winch->fd;
        int err;
        char c;
 
-       if (winch->fd != -1) {
-               err = generic_read(winch->fd, &c, NULL);
+       if (fd != -1) {
+               err = generic_read(fd, &c, NULL);
                if (err < 0) {
                        if (err != -EAGAIN) {
+                               winch->fd = -1;
+                               list_del(&winch->list);
+                               os_close_file(fd);
                                printk(KERN_ERR "winch_interrupt : "
                                       "read failed, errno = %d\n", -err);
                                printk(KERN_ERR "fd %d is losing SIGWINCH "
                                       "support\n", winch->tty_fd);
-                               free_winch(winch, 0);
+                               INIT_WORK(&winch->work, __free_winch);
+                               schedule_work(&winch->work);
                                return IRQ_HANDLED;
                        }
                        goto out;
@@ -828,7 +839,7 @@ static void unregister_winch(struct tty_struct *tty)
        list_for_each_safe(ele, next, &winch_handlers) {
                winch = list_entry(ele, struct winch, list);
                if (winch->tty == tty) {
-                       free_winch(winch, 1);
+                       free_winch(winch);
                        break;
                }
        }
@@ -844,7 +855,7 @@ static void winch_cleanup(void)
 
        list_for_each_safe(ele, next, &winch_handlers) {
                winch = list_entry(ele, struct winch, list);
-               free_winch(winch, 1);
+               free_winch(winch);
        }
 
        spin_unlock(&winch_handler_lock);