n_tty: Fully initialize ldisc before restarting buffer work
authorPeter Hurley <peter@hurleysoftware.com>
Mon, 11 Mar 2013 20:44:32 +0000 (16:44 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 18 Mar 2013 23:44:01 +0000 (16:44 -0700)
Buffer work may already be pending when the n_tty ldisc is re-opened,
eg., when setting the ldisc (via TIOCSETD ioctl) and when hanging up
the tty. Since n_tty_set_room() may restart buffer work, first ensure
the ldisc is completely initialized.

Factor n_tty_set_room() out of reset_buffer_flags() (only 2 callers)
and reorganize n_tty_open() to set termios last; buffer work will
be restarted there if necessary, after the char_map is properly
initialized.

Fixes this WARNING:

[  549.561769] ------------[ cut here ]------------
[  549.598755] WARNING: at drivers/tty/n_tty.c:160 n_tty_set_room+0xff/0x130()
[  549.604058] scheduling buffer work for halted ldisc
[  549.607741] Pid: 9417, comm: trinity-child28 Tainted: G      D W 3.7.0-next-20121217-sasha-00023-g8689ef9 #219
[  549.652580] Call Trace:
[  549.662754]  [<ffffffff81c432cf>] ? n_tty_set_room+0xff/0x130
[  549.665458]  [<ffffffff8110cae7>] warn_slowpath_common+0x87/0xb0
[  549.668257]  [<ffffffff8110cb71>] warn_slowpath_fmt+0x41/0x50
[  549.671007]  [<ffffffff81c432cf>] n_tty_set_room+0xff/0x130
[  549.673268]  [<ffffffff81c44597>] reset_buffer_flags+0x137/0x150
[  549.675607]  [<ffffffff81c45b71>] n_tty_open+0x131/0x1c0
[  549.677699]  [<ffffffff81c47824>] tty_ldisc_open.isra.5+0x54/0x70
[  549.680147]  [<ffffffff81c482bf>] tty_ldisc_hangup+0x11f/0x1e0
[  549.682409]  [<ffffffff81c3fa17>] __tty_hangup+0x137/0x440
[  549.684634]  [<ffffffff81c3fd49>] tty_vhangup+0x9/0x10
[  549.686443]  [<ffffffff81c4a42c>] pty_close+0x14c/0x160
[  549.688446]  [<ffffffff81c41225>] tty_release+0xd5/0x490
[  549.690460]  [<ffffffff8127d8a2>] __fput+0x122/0x250
[  549.692577]  [<ffffffff8127d9d9>] ____fput+0x9/0x10
[  549.694534]  [<ffffffff811348c2>] task_work_run+0xb2/0xf0
[  549.696349]  [<ffffffff81113c6d>] do_exit+0x36d/0x580
[  549.698286]  [<ffffffff8107d964>] ? syscall_trace_enter+0x24/0x2e0
[  549.702729]  [<ffffffff81113f4a>] do_group_exit+0x8a/0xc0
[  549.706775]  [<ffffffff81113f92>] sys_exit_group+0x12/0x20
[  549.711088]  [<ffffffff83cfab18>] tracesys+0xe1/0xe6
[  549.728001] ---[ end trace 73eb41728f11f87e ]---

Reported-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/n_tty.c

index 0d3f715de7d2c990416ebcdf7850977ce5a6b914..d655416087b7099d7639923c7c0a478c72a5afbe 100644 (file)
@@ -204,9 +204,8 @@ static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
  *     Locking: tty_read_lock for read fields.
  */
 
-static void reset_buffer_flags(struct tty_struct *tty)
+static void reset_buffer_flags(struct n_tty_data *ldata)
 {
-       struct n_tty_data *ldata = tty->disc_data;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&ldata->read_lock, flags);
@@ -219,7 +218,6 @@ static void reset_buffer_flags(struct tty_struct *tty)
 
        ldata->canon_head = ldata->canon_data = ldata->erasing = 0;
        bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
-       n_tty_set_room(tty);
 }
 
 static void n_tty_packet_mode_flush(struct tty_struct *tty)
@@ -247,7 +245,8 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty)
 
 static void n_tty_flush_buffer(struct tty_struct *tty)
 {
-       reset_buffer_flags(tty);
+       reset_buffer_flags(tty->disc_data);
+       n_tty_set_room(tty);
 
        if (tty->link)
                n_tty_packet_mode_flush(tty);
@@ -1633,14 +1632,14 @@ static int n_tty_open(struct tty_struct *tty)
                goto err_free_bufs;
 
        tty->disc_data = ldata;
-       /* indicate buffer work may resume */
-       clear_bit(TTY_LDISC_HALTED, &tty->flags);
-       reset_buffer_flags(tty);
-       tty_unthrottle(tty);
+       reset_buffer_flags(tty->disc_data);
        ldata->column = 0;
-       n_tty_set_termios(tty, NULL);
        tty->minimum_to_wake = 1;
        tty->closing = 0;
+       /* indicate buffer work may resume */
+       clear_bit(TTY_LDISC_HALTED, &tty->flags);
+       n_tty_set_termios(tty, NULL);
+       tty_unthrottle(tty);
 
        return 0;
 err_free_bufs: