diff options
-rw-r--r-- | drivers/tty/n_tty.c | 45 | ||||
-rw-r--r-- | drivers/tty/pty.c | 1 | ||||
-rw-r--r-- | include/linux/tty.h | 3 |
3 files changed, 34 insertions, 15 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 5793aa0d3bfb..cf6e0f2e1331 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1090,16 +1090,45 @@ static void eraser(unsigned char c, struct tty_struct *tty) * Called when a signal is being sent due to terminal input. * Called from the driver receive_buf path so serialized. * + * Performs input and output flush if !NOFLSH. In this context, the echo + * buffer is 'output'. The signal is processed first to alert any current + * readers or writers to discontinue and exit their i/o loops. + * * Locking: ctrl_lock */ static void isig(int sig, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; struct pid *tty_pgrp = tty_get_pgrp(tty); if (tty_pgrp) { kill_pgrp(tty_pgrp, sig, 1); put_pid(tty_pgrp); } + + if (!L_NOFLSH(tty)) { + up_read(&tty->termios_rwsem); + down_write(&tty->termios_rwsem); + + /* clear echo buffer */ + mutex_lock(&ldata->output_lock); + ldata->echo_head = ldata->echo_tail = 0; + ldata->echo_mark = ldata->echo_commit = 0; + mutex_unlock(&ldata->output_lock); + + /* clear output buffer */ + tty_driver_flush_buffer(tty); + + /* clear input buffer */ + reset_buffer_flags(tty->disc_data); + + /* notify pty master of flush */ + if (tty->link) + n_tty_packet_mode_flush(tty); + + up_write(&tty->termios_rwsem); + down_read(&tty->termios_rwsem); + } } /** @@ -1123,13 +1152,6 @@ static void n_tty_receive_break(struct tty_struct *tty) return; if (I_BRKINT(tty)) { isig(SIGINT, tty); - if (!L_NOFLSH(tty)) { - /* flushing needs exclusive termios_rwsem */ - up_read(&tty->termios_rwsem); - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); - down_read(&tty->termios_rwsem); - } return; } if (I_PARMRK(tty)) { @@ -1203,13 +1225,7 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) static void n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) { - if (!L_NOFLSH(tty)) { - /* flushing needs exclusive termios_rwsem */ - up_read(&tty->termios_rwsem); - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); - down_read(&tty->termios_rwsem); - } + isig(signal, tty); if (I_IXON(tty)) start_tty(tty); if (L_ECHO(tty)) { @@ -1217,7 +1233,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) commit_echoes(tty); } else process_echoes(tty); - isig(signal, tty); return; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 0e273158edac..e72ee629cead 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -395,6 +395,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, goto err_put_module; tty_set_lock_subclass(o_tty); + lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE); if (legacy) { /* We always use new tty termios data so we can do this diff --git a/include/linux/tty.h b/include/linux/tty.h index 55964b9490dd..fe5623c9af71 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -25,6 +25,9 @@ * * legacy_mutex - Nested tty locks are necessary for releasing pty pairs. * The stable lock order is master pty first, then slave pty. + * termios_rwsem - The stable lock order is tty_buffer lock->termios_rwsem. + * Subclassing this lock enables the slave pty to hold its + * termios_rwsem when claiming the master tty_buffer lock. * tty_buffer lock - slave ptys can claim nested buffer lock when handling * signal chars. The stable lock order is slave pty, then * master. |