n_tty: Fix signal handling flushes
[firefly-linux-kernel-4.4.55.git] / drivers / tty / n_tty.c
index 5793aa0d3bfb0e21445d721eb64f925fd55b1606..cf6e0f2e1331fd46310a6834d99896e2b943ea19 100644 (file)
@@ -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;
 }