tty: avoid recursive BTM in pty_close

When the console has been redirected, a hangup of the tty
will cause tty_release to be called under the big tty_mutex,
which leads to a deadlock because hangup is also called
under the BTM.

This moves the BTM deeper into the tty_hangup function so
we can close the redirected tty without holding the BTM.
In case of pty, we now need to drop the BTM before
calling tty_vhangup.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: John Kacur <jkacur@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Arnd Bergmann 2010-06-18 14:58:07 +02:00 committed by Greg Kroah-Hartman
parent a0821df6e5
commit 11dbf20392
2 changed files with 15 additions and 13 deletions

View File

@ -62,7 +62,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
if (tty->driver == ptm_driver) if (tty->driver == ptm_driver)
devpts_pty_kill(tty->link); devpts_pty_kill(tty->link);
#endif #endif
tty_vhangup_locked(tty->link); tty_unlock();
tty_vhangup(tty->link);
tty_lock();
} }
} }

View File

@ -471,7 +471,7 @@ void tty_wakeup(struct tty_struct *tty)
EXPORT_SYMBOL_GPL(tty_wakeup); EXPORT_SYMBOL_GPL(tty_wakeup);
/** /**
* do_tty_hangup - actual handler for hangup events * __tty_hangup - actual handler for hangup events
* @work: tty device * @work: tty device
* *
* This can be called by the "eventd" kernel thread. That is process * This can be called by the "eventd" kernel thread. That is process
@ -492,7 +492,7 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
* tasklist_lock to walk task list for hangup event * tasklist_lock to walk task list for hangup event
* ->siglock to protect ->signal/->sighand * ->siglock to protect ->signal/->sighand
*/ */
void tty_vhangup_locked(struct tty_struct *tty) void __tty_hangup(struct tty_struct *tty)
{ {
struct file *cons_filp = NULL; struct file *cons_filp = NULL;
struct file *filp, *f = NULL; struct file *filp, *f = NULL;
@ -512,10 +512,12 @@ void tty_vhangup_locked(struct tty_struct *tty)
} }
spin_unlock(&redirect_lock); spin_unlock(&redirect_lock);
tty_lock();
/* inuse_filps is protected by the single tty lock, /* inuse_filps is protected by the single tty lock,
this really needs to change if we want to flush the this really needs to change if we want to flush the
workqueue with the lock held */ workqueue with the lock held */
check_tty_count(tty, "do_tty_hangup"); check_tty_count(tty, "tty_hangup");
file_list_lock(); file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */ /* This breaks for file handles being sent over AF_UNIX sockets ? */
@ -594,6 +596,9 @@ void tty_vhangup_locked(struct tty_struct *tty)
*/ */
set_bit(TTY_HUPPED, &tty->flags); set_bit(TTY_HUPPED, &tty->flags);
tty_ldisc_enable(tty); tty_ldisc_enable(tty);
tty_unlock();
if (f) if (f)
fput(f); fput(f);
} }
@ -603,9 +608,7 @@ static void do_tty_hangup(struct work_struct *work)
struct tty_struct *tty = struct tty_struct *tty =
container_of(work, struct tty_struct, hangup_work); container_of(work, struct tty_struct, hangup_work);
tty_lock(); __tty_hangup(tty);
tty_vhangup_locked(tty);
tty_unlock();
} }
/** /**
@ -643,13 +646,12 @@ void tty_vhangup(struct tty_struct *tty)
printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
#endif #endif
tty_lock(); __tty_hangup(tty);
tty_vhangup_locked(tty);
tty_unlock();
} }
EXPORT_SYMBOL(tty_vhangup); EXPORT_SYMBOL(tty_vhangup);
/** /**
* tty_vhangup_self - process vhangup for own ctty * tty_vhangup_self - process vhangup for own ctty
* *
@ -727,10 +729,8 @@ void disassociate_ctty(int on_exit)
if (tty) { if (tty) {
tty_pgrp = get_pid(tty->pgrp); tty_pgrp = get_pid(tty->pgrp);
if (on_exit) { if (on_exit) {
tty_lock();
if (tty->driver->type != TTY_DRIVER_TYPE_PTY) if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup_locked(tty); tty_vhangup(tty);
tty_unlock();
} }
tty_kref_put(tty); tty_kref_put(tty);
} else if (on_exit) { } else if (on_exit) {