diff options
Diffstat (limited to 'arch/um/drivers/line.c')
-rw-r--r-- | arch/um/drivers/line.c | 37 |
1 files changed, 24 insertions, 13 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 2bb4c4f5dec4..e0fdffa2d542 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c | |||
@@ -663,11 +663,15 @@ struct tty_driver *line_register_devfs(struct lines *set, | |||
663 | return driver; | 663 | return driver; |
664 | } | 664 | } |
665 | 665 | ||
666 | static spinlock_t winch_handler_lock; | ||
667 | LIST_HEAD(winch_handlers); | ||
668 | |||
666 | void lines_init(struct line *lines, int nlines) | 669 | void lines_init(struct line *lines, int nlines) |
667 | { | 670 | { |
668 | struct line *line; | 671 | struct line *line; |
669 | int i; | 672 | int i; |
670 | 673 | ||
674 | spin_lock_init(&winch_handler_lock); | ||
671 | for(i = 0; i < nlines; i++){ | 675 | for(i = 0; i < nlines; i++){ |
672 | line = &lines[i]; | 676 | line = &lines[i]; |
673 | INIT_LIST_HEAD(&line->chan_list); | 677 | INIT_LIST_HEAD(&line->chan_list); |
@@ -724,31 +728,30 @@ irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) | |||
724 | return IRQ_HANDLED; | 728 | return IRQ_HANDLED; |
725 | } | 729 | } |
726 | 730 | ||
727 | DECLARE_MUTEX(winch_handler_sem); | ||
728 | LIST_HEAD(winch_handlers); | ||
729 | |||
730 | void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) | 731 | void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) |
731 | { | 732 | { |
732 | struct winch *winch; | 733 | struct winch *winch; |
733 | 734 | ||
734 | down(&winch_handler_sem); | ||
735 | winch = kmalloc(sizeof(*winch), GFP_KERNEL); | 735 | winch = kmalloc(sizeof(*winch), GFP_KERNEL); |
736 | if (winch == NULL) { | 736 | if (winch == NULL) { |
737 | printk("register_winch_irq - kmalloc failed\n"); | 737 | printk("register_winch_irq - kmalloc failed\n"); |
738 | goto out; | 738 | return; |
739 | } | 739 | } |
740 | |||
740 | *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), | 741 | *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), |
741 | .fd = fd, | 742 | .fd = fd, |
742 | .tty_fd = tty_fd, | 743 | .tty_fd = tty_fd, |
743 | .pid = pid, | 744 | .pid = pid, |
744 | .tty = tty }); | 745 | .tty = tty }); |
746 | |||
747 | spin_lock(&winch_handler_lock); | ||
745 | list_add(&winch->list, &winch_handlers); | 748 | list_add(&winch->list, &winch_handlers); |
749 | spin_unlock(&winch_handler_lock); | ||
750 | |||
746 | if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, | 751 | if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, |
747 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, | 752 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, |
748 | "winch", winch) < 0) | 753 | "winch", winch) < 0) |
749 | printk("register_winch_irq - failed to register IRQ\n"); | 754 | printk("register_winch_irq - failed to register IRQ\n"); |
750 | out: | ||
751 | up(&winch_handler_sem); | ||
752 | } | 755 | } |
753 | 756 | ||
754 | static void unregister_winch(struct tty_struct *tty) | 757 | static void unregister_winch(struct tty_struct *tty) |
@@ -756,7 +759,7 @@ static void unregister_winch(struct tty_struct *tty) | |||
756 | struct list_head *ele; | 759 | struct list_head *ele; |
757 | struct winch *winch, *found = NULL; | 760 | struct winch *winch, *found = NULL; |
758 | 761 | ||
759 | down(&winch_handler_sem); | 762 | spin_lock(&winch_handler_lock); |
760 | list_for_each(ele, &winch_handlers){ | 763 | list_for_each(ele, &winch_handlers){ |
761 | winch = list_entry(ele, struct winch, list); | 764 | winch = list_entry(ele, struct winch, list); |
762 | if(winch->tty == tty){ | 765 | if(winch->tty == tty){ |
@@ -764,20 +767,25 @@ static void unregister_winch(struct tty_struct *tty) | |||
764 | break; | 767 | break; |
765 | } | 768 | } |
766 | } | 769 | } |
767 | |||
768 | if(found == NULL) | 770 | if(found == NULL) |
769 | goto out; | 771 | goto err; |
772 | |||
773 | list_del(&winch->list); | ||
774 | spin_unlock(&winch_handler_lock); | ||
770 | 775 | ||
771 | if(winch->pid != -1) | 776 | if(winch->pid != -1) |
772 | os_kill_process(winch->pid, 1); | 777 | os_kill_process(winch->pid, 1); |
773 | 778 | ||
774 | free_irq(WINCH_IRQ, winch); | 779 | free_irq(WINCH_IRQ, winch); |
775 | list_del(&winch->list); | ||
776 | kfree(winch); | 780 | kfree(winch); |
777 | out: | 781 | |
778 | up(&winch_handler_sem); | 782 | return; |
783 | err: | ||
784 | spin_unlock(&winch_handler_lock); | ||
779 | } | 785 | } |
780 | 786 | ||
787 | /* XXX: No lock as it's an exitcall... is this valid? Depending on cleanup | ||
788 | * order... are we sure that nothing else is done on the list? */ | ||
781 | static void winch_cleanup(void) | 789 | static void winch_cleanup(void) |
782 | { | 790 | { |
783 | struct list_head *ele; | 791 | struct list_head *ele; |
@@ -786,6 +794,9 @@ static void winch_cleanup(void) | |||
786 | list_for_each(ele, &winch_handlers){ | 794 | list_for_each(ele, &winch_handlers){ |
787 | winch = list_entry(ele, struct winch, list); | 795 | winch = list_entry(ele, struct winch, list); |
788 | if(winch->fd != -1){ | 796 | if(winch->fd != -1){ |
797 | /* Why is this different from the above free_irq(), | ||
798 | * which deactivates SIGIO? This searches the FD | ||
799 | * somewhere else and removes it from the list... */ | ||
789 | deactivate_fd(winch->fd, WINCH_IRQ); | 800 | deactivate_fd(winch->fd, WINCH_IRQ); |
790 | os_close_file(winch->fd); | 801 | os_close_file(winch->fd); |
791 | } | 802 | } |