
From: Roman Zippel <zippel@linux-m68k.org>

> Does that make sense to you? Maybe the "input full, but no canon_data" 
> special case would be better handled in the read path, rather than the 
> write path?

There might be no reader at this time. The basic problem is that this 
should be handled in the receive_buf path, but for that it had to return 
the numbers of characters written (or dropped in the no canon_data case). 
Relying on receive_room value instead is rather bogus.

Below is a new patch, which also fixes problems with very long lines.

1. if receive_room() returns a small number, write_chan() has to
   continue writing, otherwise it will sleep with nobody waking it up.

2. we mustn't simply drop eol characters in n_tty_receive_char
   otherwise canon_data isn't increased and the reader isn't woken up.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/char/n_tty.c |   33 +++++++++++++++------------------
 1 files changed, 15 insertions(+), 18 deletions(-)

diff -puN drivers/char/n_tty.c~tty-output-lossage-fix drivers/char/n_tty.c
--- 25/drivers/char/n_tty.c~tty-output-lossage-fix	Wed Feb 16 14:51:23 2005
+++ 25-akpm/drivers/char/n_tty.c	Wed Feb 16 14:51:23 2005
@@ -768,10 +768,8 @@ send_signal:
 		}
 		if (c == '\n') {
 			if (L_ECHO(tty) || L_ECHONL(tty)) {
-				if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+				if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
 					put_char('\a', tty);
-					return;
-				}
 				opost('\n', tty);
 			}
 			goto handle_newline;
@@ -788,10 +786,8 @@ send_signal:
 			 * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
 			 */
 			if (L_ECHO(tty)) {
-				if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
+				if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
 					put_char('\a', tty);
-					return;
-				}
 				/* Record the column of first canon char. */
 				if (tty->canon_head == tty->read_head)
 					tty->canon_column = tty->column;
@@ -860,12 +856,9 @@ static int n_tty_receive_room(struct tty
 	 * that erase characters will be handled.  Other excess
 	 * characters will be beeped.
 	 */
-	if (tty->icanon && !tty->canon_data)
-		return N_TTY_BUF_SIZE;
-
-	if (left > 0)
-		return left;
-	return 0;
+	if (left <= 0)
+		left = tty->icanon && !tty->canon_data;
+	return left;
 }
 
 /**
@@ -1471,13 +1464,17 @@ static ssize_t write_chan(struct tty_str
 			if (tty->driver->flush_chars)
 				tty->driver->flush_chars(tty);
 		} else {
-			c = tty->driver->write(tty, b, nr);
-			if (c < 0) {
-				retval = c;
-				goto break_out;
+			while (nr > 0) {
+				c = tty->driver->write(tty, b, nr);
+				if (c < 0) {
+					retval = c;
+					goto break_out;
+				}
+				if (!c)
+					break;
+				b += c;
+				nr -= c;
 			}
-			b += c;
-			nr -= c;
 		}
 		if (!nr)
 			break;
_
