diff options
Diffstat (limited to 'arch/um/drivers/line.c')
| -rw-r--r-- | arch/um/drivers/line.c | 61 | 
1 files changed, 36 insertions, 25 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index d51c404239a..364c8a15c4c 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -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);  |