diff options
Diffstat (limited to 'drivers/tty/n_tty.c')
| -rw-r--r-- | drivers/tty/n_tty.c | 29 | 
1 files changed, 24 insertions, 5 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index ee1c268f5f9..8c0b7b42319 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -92,10 +92,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,  static void n_tty_set_room(struct tty_struct *tty)  { -	/* tty->read_cnt is not read locked ? */ -	int	left = N_TTY_BUF_SIZE - tty->read_cnt - 1; +	int left;  	int old_left; +	/* tty->read_cnt is not read locked ? */ +	if (I_PARMRK(tty)) { +		/* Multiply read_cnt by 3, since each byte might take up to +		 * three times as many spaces when PARMRK is set (depending on +		 * its flags, e.g. parity error). */ +		left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1; +	} else +		left = N_TTY_BUF_SIZE - tty->read_cnt - 1; +  	/*  	 * If we are doing input canonicalization, and there are no  	 * pending newlines, let characters through without limit, so @@ -1432,6 +1440,12 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,  	 */  	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 */  }  int is_ignored(int sig) @@ -1460,7 +1474,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)  	BUG_ON(!tty);  	if (old) -		canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; +		canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON;  	if (canon_change) {  		memset(&tty->read_flags, 0, sizeof tty->read_flags);  		tty->canon_head = tty->read_tail; @@ -1728,7 +1742,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,  do_it_again: -	BUG_ON(!tty->read_buf); +	if (WARN_ON(!tty->read_buf)) +		return -EAGAIN;  	c = job_control(tty, file);  	if (c < 0) @@ -1832,13 +1847,13 @@ do_it_again:  		if (tty->icanon && !L_EXTPROC(tty)) {  			/* N.B. avoid overrun if nr == 0 */ +			spin_lock_irqsave(&tty->read_lock, flags);  			while (nr && tty->read_cnt) {  				int eol;  				eol = test_and_clear_bit(tty->read_tail,  						tty->read_flags);  				c = tty->read_buf[tty->read_tail]; -				spin_lock_irqsave(&tty->read_lock, flags);  				tty->read_tail = ((tty->read_tail+1) &  						  (N_TTY_BUF_SIZE-1));  				tty->read_cnt--; @@ -1856,15 +1871,19 @@ do_it_again:  					if (tty_put_user(tty, c, b++)) {  						retval = -EFAULT;  						b--; +						spin_lock_irqsave(&tty->read_lock, flags);  						break;  					}  					nr--;  				}  				if (eol) {  					tty_audit_push(tty); +					spin_lock_irqsave(&tty->read_lock, flags);  					break;  				} +				spin_lock_irqsave(&tty->read_lock, flags);  			} +			spin_unlock_irqrestore(&tty->read_lock, flags);  			if (retval)  				break;  		} else {  |