diff options
Diffstat (limited to 'arch/um/drivers/line.c')
-rw-r--r-- | arch/um/drivers/line.c | 64 |
1 files changed, 37 insertions, 27 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 35dd0b86401..364c8a15c4c 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c | |||
@@ -176,10 +176,9 @@ void line_flush_buffer(struct tty_struct *tty) | |||
176 | { | 176 | { |
177 | struct line *line = tty->driver_data; | 177 | struct line *line = tty->driver_data; |
178 | unsigned long flags; | 178 | unsigned long flags; |
179 | int err; | ||
180 | 179 | ||
181 | spin_lock_irqsave(&line->lock, flags); | 180 | spin_lock_irqsave(&line->lock, flags); |
182 | err = flush_buffer(line); | 181 | flush_buffer(line); |
183 | spin_unlock_irqrestore(&line->lock, flags); | 182 | spin_unlock_irqrestore(&line->lock, flags); |
184 | } | 183 | } |
185 | 184 | ||
@@ -400,8 +399,8 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data) | |||
400 | * is done under a spinlock. Checking whether the device is in use is | 399 | * is done under a spinlock. Checking whether the device is in use is |
401 | * line->tty->count > 1, also under the spinlock. | 400 | * line->tty->count > 1, also under the spinlock. |
402 | * | 401 | * |
403 | * tty->count serves to decide whether the device should be enabled or | 402 | * line->count serves to decide whether the device should be enabled or |
404 | * disabled on the host. If it's equal to 1, then we are doing the | 403 | * disabled on the host. If it's equal to 0, then we are doing the |
405 | * first open or last close. Otherwise, open and close just return. | 404 | * first open or last close. Otherwise, open and close just return. |
406 | */ | 405 | */ |
407 | 406 | ||
@@ -415,16 +414,16 @@ int line_open(struct line *lines, struct tty_struct *tty) | |||
415 | goto out_unlock; | 414 | goto out_unlock; |
416 | 415 | ||
417 | err = 0; | 416 | err = 0; |
418 | if (tty->count > 1) | 417 | if (line->count++) |
419 | goto out_unlock; | 418 | goto out_unlock; |
420 | 419 | ||
421 | spin_unlock(&line->count_lock); | 420 | BUG_ON(tty->driver_data); |
422 | |||
423 | tty->driver_data = line; | 421 | tty->driver_data = line; |
424 | line->tty = tty; | 422 | line->tty = tty; |
425 | 423 | ||
424 | spin_unlock(&line->count_lock); | ||
426 | err = enable_chan(line); | 425 | err = enable_chan(line); |
427 | if (err) | 426 | if (err) /* line_close() will be called by our caller */ |
428 | return err; | 427 | return err; |
429 | 428 | ||
430 | INIT_DELAYED_WORK(&line->task, line_timer_cb); | 429 | INIT_DELAYED_WORK(&line->task, line_timer_cb); |
@@ -437,7 +436,7 @@ int line_open(struct line *lines, struct tty_struct *tty) | |||
437 | chan_window_size(&line->chan_list, &tty->winsize.ws_row, | 436 | chan_window_size(&line->chan_list, &tty->winsize.ws_row, |
438 | &tty->winsize.ws_col); | 437 | &tty->winsize.ws_col); |
439 | 438 | ||
440 | return err; | 439 | return 0; |
441 | 440 | ||
442 | out_unlock: | 441 | out_unlock: |
443 | spin_unlock(&line->count_lock); | 442 | spin_unlock(&line->count_lock); |
@@ -461,17 +460,16 @@ void line_close(struct tty_struct *tty, struct file * filp) | |||
461 | flush_buffer(line); | 460 | flush_buffer(line); |
462 | 461 | ||
463 | spin_lock(&line->count_lock); | 462 | spin_lock(&line->count_lock); |
464 | if (!line->valid) | 463 | BUG_ON(!line->valid); |
465 | goto out_unlock; | ||
466 | 464 | ||
467 | if (tty->count > 1) | 465 | if (--line->count) |
468 | goto out_unlock; | 466 | goto out_unlock; |
469 | 467 | ||
470 | spin_unlock(&line->count_lock); | ||
471 | |||
472 | line->tty = NULL; | 468 | line->tty = NULL; |
473 | tty->driver_data = NULL; | 469 | tty->driver_data = NULL; |
474 | 470 | ||
471 | spin_unlock(&line->count_lock); | ||
472 | |||
475 | if (line->sigio) { | 473 | if (line->sigio) { |
476 | unregister_winch(tty); | 474 | unregister_winch(tty); |
477 | line->sigio = 0; | 475 | line->sigio = 0; |
@@ -499,7 +497,7 @@ static int setup_one_line(struct line *lines, int n, char *init, int init_prio, | |||
499 | 497 | ||
500 | spin_lock(&line->count_lock); | 498 | spin_lock(&line->count_lock); |
501 | 499 | ||
502 | if (line->tty != NULL) { | 500 | if (line->count) { |
503 | *error_out = "Device is already open"; | 501 | *error_out = "Device is already open"; |
504 | goto out; | 502 | goto out; |
505 | } | 503 | } |
@@ -723,41 +721,53 @@ struct winch { | |||
723 | int pid; | 721 | int pid; |
724 | struct tty_struct *tty; | 722 | struct tty_struct *tty; |
725 | unsigned long stack; | 723 | unsigned long stack; |
724 | struct work_struct work; | ||
726 | }; | 725 | }; |
727 | 726 | ||
728 | static void free_winch(struct winch *winch, int free_irq_ok) | 727 | static void __free_winch(struct work_struct *work) |
729 | { | 728 | { |
730 | if (free_irq_ok) | 729 | struct winch *winch = container_of(work, struct winch, work); |
731 | free_irq(WINCH_IRQ, winch); | 730 | free_irq(WINCH_IRQ, winch); |
732 | |||
733 | list_del(&winch->list); | ||
734 | 731 | ||
735 | if (winch->pid != -1) | 732 | if (winch->pid != -1) |
736 | os_kill_process(winch->pid, 1); | 733 | os_kill_process(winch->pid, 1); |
737 | if (winch->fd != -1) | ||
738 | os_close_file(winch->fd); | ||
739 | if (winch->stack != 0) | 734 | if (winch->stack != 0) |
740 | free_stack(winch->stack, 0); | 735 | free_stack(winch->stack, 0); |
741 | kfree(winch); | 736 | kfree(winch); |
742 | } | 737 | } |
743 | 738 | ||
739 | static void free_winch(struct winch *winch) | ||
740 | { | ||
741 | int fd = winch->fd; | ||
742 | winch->fd = -1; | ||
743 | if (fd != -1) | ||
744 | os_close_file(fd); | ||
745 | list_del(&winch->list); | ||
746 | __free_winch(&winch->work); | ||
747 | } | ||
748 | |||
744 | static irqreturn_t winch_interrupt(int irq, void *data) | 749 | static irqreturn_t winch_interrupt(int irq, void *data) |
745 | { | 750 | { |
746 | struct winch *winch = data; | 751 | struct winch *winch = data; |
747 | struct tty_struct *tty; | 752 | struct tty_struct *tty; |
748 | struct line *line; | 753 | struct line *line; |
754 | int fd = winch->fd; | ||
749 | int err; | 755 | int err; |
750 | char c; | 756 | char c; |
751 | 757 | ||
752 | if (winch->fd != -1) { | 758 | if (fd != -1) { |
753 | err = generic_read(winch->fd, &c, NULL); | 759 | err = generic_read(fd, &c, NULL); |
754 | if (err < 0) { | 760 | if (err < 0) { |
755 | if (err != -EAGAIN) { | 761 | if (err != -EAGAIN) { |
762 | winch->fd = -1; | ||
763 | list_del(&winch->list); | ||
764 | os_close_file(fd); | ||
756 | printk(KERN_ERR "winch_interrupt : " | 765 | printk(KERN_ERR "winch_interrupt : " |
757 | "read failed, errno = %d\n", -err); | 766 | "read failed, errno = %d\n", -err); |
758 | printk(KERN_ERR "fd %d is losing SIGWINCH " | 767 | printk(KERN_ERR "fd %d is losing SIGWINCH " |
759 | "support\n", winch->tty_fd); | 768 | "support\n", winch->tty_fd); |
760 | free_winch(winch, 0); | 769 | INIT_WORK(&winch->work, __free_winch); |
770 | schedule_work(&winch->work); | ||
761 | return IRQ_HANDLED; | 771 | return IRQ_HANDLED; |
762 | } | 772 | } |
763 | goto out; | 773 | goto out; |
@@ -829,7 +839,7 @@ static void unregister_winch(struct tty_struct *tty) | |||
829 | list_for_each_safe(ele, next, &winch_handlers) { | 839 | list_for_each_safe(ele, next, &winch_handlers) { |
830 | winch = list_entry(ele, struct winch, list); | 840 | winch = list_entry(ele, struct winch, list); |
831 | if (winch->tty == tty) { | 841 | if (winch->tty == tty) { |
832 | free_winch(winch, 1); | 842 | free_winch(winch); |
833 | break; | 843 | break; |
834 | } | 844 | } |
835 | } | 845 | } |
@@ -845,7 +855,7 @@ static void winch_cleanup(void) | |||
845 | 855 | ||
846 | list_for_each_safe(ele, next, &winch_handlers) { | 856 | list_for_each_safe(ele, next, &winch_handlers) { |
847 | winch = list_entry(ele, struct winch, list); | 857 | winch = list_entry(ele, struct winch, list); |
848 | free_winch(winch, 1); | 858 | free_winch(winch); |
849 | } | 859 | } |
850 | 860 | ||
851 | spin_unlock(&winch_handler_lock); | 861 | spin_unlock(&winch_handler_lock); |