aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_tty.c
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-01-17 15:42:06 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-02-02 13:11:27 -0500
commitd2b6f44779d3be22d32a5697bd30b59367fd2b33 (patch)
treed7d607053156ff19c610ed1e9c976156c3ac1050 /drivers/tty/n_tty.c
parent1d1d14da12e79a6c05fbe1a975401f0f56c93316 (diff)
n_tty: Fix signal handling flushes
BRKINT and ISIG requires input and output flush when a signal char is received. However, the order of operations is significant since parallel i/o may be ongoing. Merge the signal handling for BRKINT with ISIG handling. Process the signal first. This ensures any ongoing i/o is aborted; without this, a waiting writer may continue writing after the flush occurs and after the signal char has been echoed. Write lock the termios_rwsem, which excludes parallel writers from pushing new i/o until after the output buffers are flushed; claiming the write lock is necessary anyway to exclude parallel readers while the read buffer is flushed. Subclass the termios_rwsem for ptys since the slave pty performing the flush may appear to reorder the termios_rwsem->tty buffer lock lock order; adding annotation clarifies that slave tty_buffer lock-> slave termios_rwsem -> master tty_buffer lock is a valid lock order. Flush the echo buffer. In this context, the echo buffer is 'output'. Otherwise, the output will appear discontinuous because the output buffer was cleared which contains older output than the echo buffer. Open-code the read buffer flush since the input worker does not need kicking (this is the input worker). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_tty.c')
-rw-r--r--drivers/tty/n_tty.c45
1 files changed, 30 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)
1090 * Called when a signal is being sent due to terminal input. 1090 * Called when a signal is being sent due to terminal input.
1091 * Called from the driver receive_buf path so serialized. 1091 * Called from the driver receive_buf path so serialized.
1092 * 1092 *
1093 * Performs input and output flush if !NOFLSH. In this context, the echo
1094 * buffer is 'output'. The signal is processed first to alert any current
1095 * readers or writers to discontinue and exit their i/o loops.
1096 *
1093 * Locking: ctrl_lock 1097 * Locking: ctrl_lock
1094 */ 1098 */
1095 1099
1096static void isig(int sig, struct tty_struct *tty) 1100static void isig(int sig, struct tty_struct *tty)
1097{ 1101{
1102 struct n_tty_data *ldata = tty->disc_data;
1098 struct pid *tty_pgrp = tty_get_pgrp(tty); 1103 struct pid *tty_pgrp = tty_get_pgrp(tty);
1099 if (tty_pgrp) { 1104 if (tty_pgrp) {
1100 kill_pgrp(tty_pgrp, sig, 1); 1105 kill_pgrp(tty_pgrp, sig, 1);
1101 put_pid(tty_pgrp); 1106 put_pid(tty_pgrp);
1102 } 1107 }
1108
1109 if (!L_NOFLSH(tty)) {
1110 up_read(&tty->termios_rwsem);
1111 down_write(&tty->termios_rwsem);
1112
1113 /* clear echo buffer */
1114 mutex_lock(&ldata->output_lock);
1115 ldata->echo_head = ldata->echo_tail = 0;
1116 ldata->echo_mark = ldata->echo_commit = 0;
1117 mutex_unlock(&ldata->output_lock);
1118
1119 /* clear output buffer */
1120 tty_driver_flush_buffer(tty);
1121
1122 /* clear input buffer */
1123 reset_buffer_flags(tty->disc_data);
1124
1125 /* notify pty master of flush */
1126 if (tty->link)
1127 n_tty_packet_mode_flush(tty);
1128
1129 up_write(&tty->termios_rwsem);
1130 down_read(&tty->termios_rwsem);
1131 }
1103} 1132}
1104 1133
1105/** 1134/**
@@ -1123,13 +1152,6 @@ static void n_tty_receive_break(struct tty_struct *tty)
1123 return; 1152 return;
1124 if (I_BRKINT(tty)) { 1153 if (I_BRKINT(tty)) {
1125 isig(SIGINT, tty); 1154 isig(SIGINT, tty);
1126 if (!L_NOFLSH(tty)) {
1127 /* flushing needs exclusive termios_rwsem */
1128 up_read(&tty->termios_rwsem);
1129 n_tty_flush_buffer(tty);
1130 tty_driver_flush_buffer(tty);
1131 down_read(&tty->termios_rwsem);
1132 }
1133 return; 1155 return;
1134 } 1156 }
1135 if (I_PARMRK(tty)) { 1157 if (I_PARMRK(tty)) {
@@ -1203,13 +1225,7 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
1203static void 1225static void
1204n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) 1226n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
1205{ 1227{
1206 if (!L_NOFLSH(tty)) { 1228 isig(signal, tty);
1207 /* flushing needs exclusive termios_rwsem */
1208 up_read(&tty->termios_rwsem);
1209 n_tty_flush_buffer(tty);
1210 tty_driver_flush_buffer(tty);
1211 down_read(&tty->termios_rwsem);
1212 }
1213 if (I_IXON(tty)) 1229 if (I_IXON(tty))
1214 start_tty(tty); 1230 start_tty(tty);
1215 if (L_ECHO(tty)) { 1231 if (L_ECHO(tty)) {
@@ -1217,7 +1233,6 @@ n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
1217 commit_echoes(tty); 1233 commit_echoes(tty);
1218 } else 1234 } else
1219 process_echoes(tty); 1235 process_echoes(tty);
1220 isig(signal, tty);
1221 return; 1236 return;
1222} 1237}
1223 1238