summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorRoman Zippel2005-07-08 02:56:55 +0200
committerLinus Torvalds2005-07-08 03:23:45 +0200
commitd6afe27bfff30fbec2cca6ad5626c22f4094d770 (patch)
tree83c28d2c90fe720b5a315b89301cf3a519ffed88 /drivers/char
parent[PATCH] xtensa: remove old syscalls (diff)
downloadkernel-qcow2-linux-d6afe27bfff30fbec2cca6ad5626c22f4094d770.tar.gz
kernel-qcow2-linux-d6afe27bfff30fbec2cca6ad5626c22f4094d770.tar.xz
kernel-qcow2-linux-d6afe27bfff30fbec2cca6ad5626c22f4094d770.zip
[PATCH] tty output lossage fix
The patch fixes a few corner cases around tty line editing with very long input lines: - n_tty_receive_char(): don't simply drop eol characters, otherwise canon_data isn't increased and the reader isn't woken up. - n_tty_receive_room(): If there is no newline pending and the edit buffer is full, allow only a single character to be written (until eol is found and the line is flushed), so characters from the next line aren't dropped. - write_chan(): if an incomplete line was written, continue writing until write() returns 0, otherwise it might not write the eol character to flush the line and the writer goes to sleep without ever being woken up. BTW the core problem is that part of this should be handled in the receive_buf path, but for this it has to return the number of written characters, as the amount of written characters may not be the same as the amount of characters going into the write buffer, so the receive_room() usage in pty_write() is not really reliable. Alan said: The problem looks valid. The behaviour of 'traditional unix' appears to be the following If you exceed the line limit then beep and drop the character Always allow EOL to complete a canonical line input Always do signal/control processing if enabled Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/n_tty.c33
1 files changed, 15 insertions, 18 deletions
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index edba5a35bf21..09103b3d8f05 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -770,10 +770,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;
@@ -790,10 +788,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;
@@ -862,12 +858,9 @@ static int n_tty_receive_room(struct tty_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;
}
/**
@@ -1473,13 +1466,17 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file,
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;