Commit a88a69c9 authored by Joe Peterson's avatar Joe Peterson Committed by Linus Torvalds

n_tty: Fix loss of echoed characters and remove bkl from n_tty

Fixes the loss of echoed (and other ldisc-generated characters) when
the tty is stopped or when the driver output buffer is full (happens
frequently for input during continuous program output, such as ^C)
and removes the Big Kernel Lock from the N_TTY line discipline.

Adds an "echo buffer" to the N_TTY line discipline that handles all
ldisc-generated output (including echoed characters).  Along with the
loss of characters, this also fixes the associated loss of sync between
tty output and the ldisc state when characters cannot be immediately
written to the tty driver.

The echo buffer stores (in addition to characters) state operations that need
to be done at the time of character output (like management of the column
position).  This allows echo to cooperate correctly with program output,
since the ldisc state remains consistent with actual characters written.

Since the echo buffer code now isolates the tty column state code
to the process_out* and process_echoes functions, we can remove the
Big Kernel Lock (BKL) and replace it with mutex locks.

Highlights are:

* Handles echo (and other ldisc output) when tty driver buffer is full
  - continuous program output can block echo
* Saves echo when tty is in stopped state (e.g. ^S)
  - (e.g.: ^Q will correctly cause held characters to be released for output)
* Control character pairs (e.g. "^C") are treated atomically and not
  split up by interleaved program output
* Line discipline state is kept consistent with characters sent to
  the tty driver
* Remove the big kernel lock (BKL) from N_TTY line discipline
Signed-off-by: default avatarJoe Peterson <joe@skyrush.com>
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e482a237
This diff is collapsed.
...@@ -1111,9 +1111,7 @@ void tty_write_message(struct tty_struct *tty, char *msg) ...@@ -1111,9 +1111,7 @@ void tty_write_message(struct tty_struct *tty, char *msg)
* Locks the line discipline as required * Locks the line discipline as required
* Writes to the tty driver are serialized by the atomic_write_lock * Writes to the tty driver are serialized by the atomic_write_lock
* and are then processed in chunks to the device. The line discipline * and are then processed in chunks to the device. The line discipline
* write method will not be involked in parallel for each device * write method will not be invoked in parallel for each device.
* The line discipline write method is called under the big
* kernel lock for historical reasons. New code should not rely on this.
*/ */
static ssize_t tty_write(struct file *file, const char __user *buf, static ssize_t tty_write(struct file *file, const char __user *buf,
...@@ -2785,6 +2783,8 @@ void initialize_tty_struct(struct tty_struct *tty, ...@@ -2785,6 +2783,8 @@ void initialize_tty_struct(struct tty_struct *tty,
INIT_WORK(&tty->hangup_work, do_tty_hangup); INIT_WORK(&tty->hangup_work, do_tty_hangup);
mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_read_lock);
mutex_init(&tty->atomic_write_lock); mutex_init(&tty->atomic_write_lock);
mutex_init(&tty->output_lock);
mutex_init(&tty->echo_lock);
spin_lock_init(&tty->read_lock); spin_lock_init(&tty->read_lock);
spin_lock_init(&tty->ctrl_lock); spin_lock_init(&tty->ctrl_lock);
INIT_LIST_HEAD(&tty->tty_files); INIT_LIST_HEAD(&tty->tty_files);
......
...@@ -2679,7 +2679,7 @@ static int con_write_room(struct tty_struct *tty) ...@@ -2679,7 +2679,7 @@ static int con_write_room(struct tty_struct *tty)
{ {
if (tty->stopped) if (tty->stopped)
return 0; return 0;
return 4096; /* No limit, really; we're not buffering */ return 32768; /* No limit, really; we're not buffering */
} }
static int con_chars_in_buffer(struct tty_struct *tty) static int con_chars_in_buffer(struct tty_struct *tty)
......
...@@ -253,6 +253,7 @@ struct tty_struct { ...@@ -253,6 +253,7 @@ struct tty_struct {
unsigned int column; unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1; unsigned char closing:1;
unsigned char echo_overrun:1;
unsigned short minimum_to_wake; unsigned short minimum_to_wake;
unsigned long overrun_time; unsigned long overrun_time;
int num_overrun; int num_overrun;
...@@ -262,11 +263,16 @@ struct tty_struct { ...@@ -262,11 +263,16 @@ struct tty_struct {
int read_tail; int read_tail;
int read_cnt; int read_cnt;
unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
unsigned char *echo_buf;
unsigned int echo_pos;
unsigned int echo_cnt;
int canon_data; int canon_data;
unsigned long canon_head; unsigned long canon_head;
unsigned int canon_column; unsigned int canon_column;
struct mutex atomic_read_lock; struct mutex atomic_read_lock;
struct mutex atomic_write_lock; struct mutex atomic_write_lock;
struct mutex output_lock;
struct mutex echo_lock;
unsigned char *write_buf; unsigned char *write_buf;
int write_cnt; int write_cnt;
spinlock_t read_lock; spinlock_t read_lock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment