diff options
Diffstat (limited to 'drivers/tty/n_tty.c')
| -rw-r--r-- | drivers/tty/n_tty.c | 172 | 
1 files changed, 85 insertions, 87 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 05e72bea9b0..d655416087b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -153,6 +153,12 @@ static void n_tty_set_room(struct tty_struct *tty)  	if (left && !old_left) {  		WARN_RATELIMIT(tty->port->itty == NULL,  				"scheduling with invalid itty\n"); +		/* see if ldisc has been killed - if so, this means that +		 * even though the ldisc has been halted and ->buf.work +		 * cancelled, ->buf.work is about to be rescheduled +		 */ +		WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), +			       "scheduling buffer work for halted ldisc\n");  		schedule_work(&tty->port->buf.work);  	}  } @@ -189,34 +195,17 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)  }  /** - *	check_unthrottle	-	allow new receive data - *	@tty; tty device - * - *	Check whether to call the driver unthrottle functions - * - *	Can sleep, may be called under the atomic_read_lock mutex but - *	this is not guaranteed. - */ -static void check_unthrottle(struct tty_struct *tty) -{ -	if (tty->count) -		tty_unthrottle(tty); -} - -/**   *	reset_buffer_flags	-	reset buffer state   *	@tty: terminal to reset   * - *	Reset the read buffer counters, clear the flags, - *	and make sure the driver is unthrottled. Called - *	from n_tty_open() and n_tty_flush_buffer(). + *	Reset the read buffer counters and clear the flags. + *	Called from n_tty_open() and n_tty_flush_buffer().   *   *	Locking: tty_read_lock for read fields.   */ -static void reset_buffer_flags(struct tty_struct *tty) +static void reset_buffer_flags(struct n_tty_data *ldata)  { -	struct n_tty_data *ldata = tty->disc_data;  	unsigned long flags;  	raw_spin_lock_irqsave(&ldata->read_lock, flags); @@ -229,36 +218,38 @@ static void reset_buffer_flags(struct tty_struct *tty)  	ldata->canon_head = ldata->canon_data = ldata->erasing = 0;  	bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); -	n_tty_set_room(tty); +} + +static void n_tty_packet_mode_flush(struct tty_struct *tty) +{ +	unsigned long flags; + +	spin_lock_irqsave(&tty->ctrl_lock, flags); +	if (tty->link->packet) { +		tty->ctrl_status |= TIOCPKT_FLUSHREAD; +		wake_up_interruptible(&tty->link->read_wait); +	} +	spin_unlock_irqrestore(&tty->ctrl_lock, flags);  }  /**   *	n_tty_flush_buffer	-	clean input queue   *	@tty:	terminal device   * - *	Flush the input buffer. Called when the line discipline is - *	being closed, when the tty layer wants the buffer flushed (eg - *	at hangup) or when the N_TTY line discipline internally has to - *	clean the pending queue (for example some signals). + *	Flush the input buffer. Called when the tty layer wants the + *	buffer flushed (eg at hangup) or when the N_TTY line discipline + *	internally has to clean the pending queue (for example some signals).   *   *	Locking: ctrl_lock, read_lock.   */  static void n_tty_flush_buffer(struct tty_struct *tty)  { -	unsigned long flags; -	/* clear everything and unthrottle the driver */ -	reset_buffer_flags(tty); - -	if (!tty->link) -		return; +	reset_buffer_flags(tty->disc_data); +	n_tty_set_room(tty); -	spin_lock_irqsave(&tty->ctrl_lock, flags); -	if (tty->link->packet) { -		tty->ctrl_status |= TIOCPKT_FLUSHREAD; -		wake_up_interruptible(&tty->link->read_wait); -	} -	spin_unlock_irqrestore(&tty->ctrl_lock, flags); +	if (tty->link) +		n_tty_packet_mode_flush(tty);  }  /** @@ -1032,23 +1023,19 @@ static void eraser(unsigned char c, struct tty_struct *tty)   *	isig		-	handle the ISIG optio   *	@sig: signal   *	@tty: terminal - *	@flush: force flush   * - *	Called when a signal is being sent due to terminal input. This - *	may caus terminal flushing to take place according to the termios - *	settings and character used. Called from the driver receive_buf - *	path so serialized. + *	Called when a signal is being sent due to terminal input. + *	Called from the driver receive_buf path so serialized.   * - *	Locking: ctrl_lock, read_lock (both via flush buffer) + *	Locking: ctrl_lock   */ -static inline void isig(int sig, struct tty_struct *tty, int flush) +static inline void isig(int sig, struct tty_struct *tty)  { -	if (tty->pgrp) -		kill_pgrp(tty->pgrp, sig, 1); -	if (flush || !L_NOFLSH(tty)) { -		n_tty_flush_buffer(tty); -		tty_driver_flush_buffer(tty); +	struct pid *tty_pgrp = tty_get_pgrp(tty); +	if (tty_pgrp) { +		kill_pgrp(tty_pgrp, sig, 1); +		put_pid(tty_pgrp);  	}  } @@ -1069,7 +1056,11 @@ static inline void n_tty_receive_break(struct tty_struct *tty)  	if (I_IGNBRK(tty))  		return;  	if (I_BRKINT(tty)) { -		isig(SIGINT, tty, 1); +		isig(SIGINT, tty); +		if (!L_NOFLSH(tty)) { +			n_tty_flush_buffer(tty); +			tty_driver_flush_buffer(tty); +		}  		return;  	}  	if (I_PARMRK(tty)) { @@ -1236,11 +1227,6 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)  		signal = SIGTSTP;  		if (c == SUSP_CHAR(tty)) {  send_signal: -			/* -			 * Note that we do not use isig() here because we want -			 * the order to be: -			 * 1) flush, 2) echo, 3) signal -			 */  			if (!L_NOFLSH(tty)) {  				n_tty_flush_buffer(tty);  				tty_driver_flush_buffer(tty); @@ -1251,8 +1237,7 @@ send_signal:  				echo_char(c, tty);  				process_echoes(tty);  			} -			if (tty->pgrp) -				kill_pgrp(tty->pgrp, signal, 1); +			isig(signal, tty);  			return;  		}  	} @@ -1483,14 +1468,14 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,  	 * mode.  We don't want to throttle the driver if we're in  	 * canonical mode and don't have a newline yet!  	 */ -	if (tty->receive_room < TTY_THRESHOLD_THROTTLE) -		tty_throttle(tty); - -        /* FIXME: there is a tiny race here if the receive room check runs -           before the other work executes and empties the buffer (upping -           the receiving room and unthrottling. We then throttle and get -           stuck. This has been observed and traced down by Vincent Pillet/ -           We need to address this when we sort out out the rx path locking */ +	while (1) { +		tty_set_flow_change(tty, TTY_THROTTLE_SAFE); +		if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) +			break; +		if (!tty_throttle_safe(tty)) +			break; +	} +	__tty_set_flow_change(tty, 0);  }  int is_ignored(int sig) @@ -1607,7 +1592,9 @@ static void n_tty_close(struct tty_struct *tty)  {  	struct n_tty_data *ldata = tty->disc_data; -	n_tty_flush_buffer(tty); +	if (tty->link) +		n_tty_packet_mode_flush(tty); +  	kfree(ldata->read_buf);  	kfree(ldata->echo_buf);  	kfree(ldata); @@ -1645,12 +1632,14 @@ static int n_tty_open(struct tty_struct *tty)  		goto err_free_bufs;  	tty->disc_data = ldata; -	reset_buffer_flags(tty); -	tty_unthrottle(tty); +	reset_buffer_flags(tty->disc_data);  	ldata->column = 0; -	n_tty_set_termios(tty, NULL);  	tty->minimum_to_wake = 1;  	tty->closing = 0; +	/* indicate buffer work may resume */ +	clear_bit(TTY_LDISC_HALTED, &tty->flags); +	n_tty_set_termios(tty, NULL); +	tty_unthrottle(tty);  	return 0;  err_free_bufs: @@ -1740,10 +1729,9 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *,   *	and if appropriate send any needed signals and return a negative   *	error code if action should be taken.   * - *	FIXME: - *	Locking: None - redirected write test is safe, testing - *	current->signal should possibly lock current->sighand - *	pgrp locking ? + *	Locking: redirected write test is safe + *		 current->signal->tty check is safe + *		 ctrl_lock to safely reference tty->pgrp   */  static int job_control(struct tty_struct *tty, struct file *file) @@ -1753,19 +1741,22 @@ static int job_control(struct tty_struct *tty, struct file *file)  	/* NOTE: not yet done after every sleep pending a thorough  	   check of the logic of this change. -- jlc */  	/* don't stop on /dev/console */ -	if (file->f_op->write != redirected_tty_write && -	    current->signal->tty == tty) { -		if (!tty->pgrp) -			printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); -		else if (task_pgrp(current) != tty->pgrp) { -			if (is_ignored(SIGTTIN) || -			    is_current_pgrp_orphaned()) -				return -EIO; -			kill_pgrp(task_pgrp(current), SIGTTIN, 1); -			set_thread_flag(TIF_SIGPENDING); -			return -ERESTARTSYS; -		} +	if (file->f_op->write == redirected_tty_write || +	    current->signal->tty != tty) +		return 0; + +	spin_lock_irq(&tty->ctrl_lock); +	if (!tty->pgrp) +		printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); +	else if (task_pgrp(current) != tty->pgrp) { +		spin_unlock_irq(&tty->ctrl_lock); +		if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) +			return -EIO; +		kill_pgrp(task_pgrp(current), SIGTTIN, 1); +		set_thread_flag(TIF_SIGPENDING); +		return -ERESTARTSYS;  	} +	spin_unlock_irq(&tty->ctrl_lock);  	return 0;  } @@ -1959,10 +1950,17 @@ do_it_again:  		 * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,  		 * we won't get any more characters.  		 */ -		if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { +		while (1) { +			tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); +			if (n_tty_chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) +				break; +			if (!tty->count) +				break;  			n_tty_set_room(tty); -			check_unthrottle(tty); +			if (!tty_unthrottle_safe(tty)) +				break;  		} +		__tty_set_flow_change(tty, 0);  		if (b - buf >= minimum)  			break;  |