tty: Signal SIGHUP before hanging up ldisc
authorPeter Hurley <peter@hurleysoftware.com>
Wed, 6 Mar 2013 12:20:57 +0000 (07:20 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Mar 2013 21:02:33 +0000 (14:02 -0700)
An exiting session leader can hang if a foreground process is
blocking for line discipline i/o, eg. in n_tty_read(). This happens
because the blocking reader is holding an ldisc reference (indicating
the line discipline is in-use) which prevents __tty_hangup() from
recycling the line discipline. Although waiters are woken before
attempting to gain exclusive access for changing the ldisc, the
blocking reader in this case will not exit the i/o loop since it
has not yet received SIGHUP (because it has not been sent).

Instead, perform signalling first, then recycle the line discipline.

Fixes:

INFO: task init:1 blocked for more than 120 seconds.
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
init            D 00000000001d7180  2688     1      0 0x00000002
 ffff8800b9acfba8 0000000000000002 00000000001d7180 ffff8800b9b10048
 ffff8800b94cb000 ffff8800b9b10000 00000000001d7180 00000000001d7180
 ffff8800b9b10000 ffff8800b9acffd8 00000000001d7180 00000000001d7180
Call Trace:
 [<ffffffff83db9909>] __schedule+0x2e9/0x3b0
 [<ffffffff83db9b35>] schedule+0x55/0x60
 [<ffffffff83db74ba>] schedule_timeout+0x3a/0x370
 [<ffffffff81182349>] ? mark_held_locks+0xf9/0x130
 [<ffffffff83dbab38>] ? down_failed+0x108/0x200
 [<ffffffff83dbb7ab>] ? _raw_spin_unlock_irq+0x2b/0x80
 [<ffffffff81182608>] ? trace_hardirqs_on_caller+0x128/0x160
 [<ffffffff83dbab61>] down_failed+0x131/0x200
 [<ffffffff83dbbfad>] ? tty_ldisc_lock_pair_timeout+0xcd/0x120
 [<ffffffff83dbae03>] ldsem_down_write+0xd3/0x113
 [<ffffffff83dbbfad>] ? tty_ldisc_lock_pair_timeout+0xcd/0x120
 [<ffffffff8118264d>] ? trace_hardirqs_on+0xd/0x10
 [<ffffffff83dbbfad>] tty_ldisc_lock_pair_timeout+0xcd/0x120
 [<ffffffff81c3df60>] tty_ldisc_hangup+0xd0/0x220
 [<ffffffff81c35bd7>] __tty_hangup+0x137/0x4f0
 [<ffffffff81c37c7c>] disassociate_ctty+0x6c/0x230
 [<ffffffff8111290c>] do_exit+0x41c/0x590
 [<ffffffff8107ad34>] ? syscall_trace_enter+0x24/0x2e0
 [<ffffffff81112b4a>] do_group_exit+0x8a/0xc0
 [<ffffffff81112b92>] sys_exit_group+0x12/0x20
 [<ffffffff83dc49d8>] tracesys+0xe1/0xe6
1 lock held by init/1:
 #0: (&tty->ldisc_sem){++++++}, at: [<ffffffff83dbbfad>] tty_ldisc_lock_pair_timeout+0xcd/0x120

Reported-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/tty_io.c

index 3feca406dc369ea18693d11a62caf58526971b9f..d3ddb31e363e1181594e5ce0d768537a6ae7e5ee 100644 (file)
@@ -651,17 +651,17 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
        }
        spin_unlock(&tty_files_lock);
 
+       refs = tty_signal_session_leader(tty, exit_session);
+       /* Account for the p->signal references we killed */
+       while (refs--)
+               tty_kref_put(tty);
+
        /*
         * it drops BTM and thus races with reopen
         * we protect the race by TTY_HUPPING
         */
        tty_ldisc_hangup(tty);
 
-       refs = tty_signal_session_leader(tty, exit_session);
-       /* Account for the p->signal references we killed */
-       while (refs--)
-               tty_kref_put(tty);
-
        spin_lock_irq(&tty->ctrl_lock);
        clear_bit(TTY_THROTTLED, &tty->flags);
        clear_bit(TTY_PUSH, &tty->flags);