diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2013-03-06 07:20:57 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-03-15 17:02:33 -0400 |
commit | 25fdf2435139542759df2eeb59e4998923c13403 (patch) | |
tree | 8ae08d45b06959acbcb0dca62f59e322af0322d3 /drivers/tty | |
parent | f91e2590410bd992e3f065d17c55329bdaa51b1d (diff) |
tty: Signal SIGHUP before hanging up ldisc
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>
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/tty_io.c | 10 |
1 files changed, 5 insertions, 5 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3feca406dc36..d3ddb31e363e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c | |||
@@ -651,17 +651,17 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) | |||
651 | } | 651 | } |
652 | spin_unlock(&tty_files_lock); | 652 | spin_unlock(&tty_files_lock); |
653 | 653 | ||
654 | refs = tty_signal_session_leader(tty, exit_session); | ||
655 | /* Account for the p->signal references we killed */ | ||
656 | while (refs--) | ||
657 | tty_kref_put(tty); | ||
658 | |||
654 | /* | 659 | /* |
655 | * it drops BTM and thus races with reopen | 660 | * it drops BTM and thus races with reopen |
656 | * we protect the race by TTY_HUPPING | 661 | * we protect the race by TTY_HUPPING |
657 | */ | 662 | */ |
658 | tty_ldisc_hangup(tty); | 663 | tty_ldisc_hangup(tty); |
659 | 664 | ||
660 | refs = tty_signal_session_leader(tty, exit_session); | ||
661 | /* Account for the p->signal references we killed */ | ||
662 | while (refs--) | ||
663 | tty_kref_put(tty); | ||
664 | |||
665 | spin_lock_irq(&tty->ctrl_lock); | 665 | spin_lock_irq(&tty->ctrl_lock); |
666 | clear_bit(TTY_THROTTLED, &tty->flags); | 666 | clear_bit(TTY_THROTTLED, &tty->flags); |
667 | clear_bit(TTY_PUSH, &tty->flags); | 667 | clear_bit(TTY_PUSH, &tty->flags); |