tty: reorder ldisc locking

We need to release the BTM in paste_selection() when
sleeping in tty_ldisc_ref_wait to avoid deadlocks
with tty_ldisc_enable.

In tty_set_ldisc, we now always grab the BTM before
taking the ldisc_mutex in order to avoid AB-BA
deadlocks between the two.

tty_ldisc_halt potentially blocks on a workqueue
function that takes the BTM, so we must release
the BTM before calling it.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Arnd Bergmann
2010-06-01 22:53:06 +02:00
committed by Greg Kroah-Hartman
parent be1bc2889a
commit 60af22d2ed
2 changed files with 27 additions and 6 deletions

View File

@@ -319,7 +319,12 @@ int paste_selection(struct tty_struct *tty)
poke_blanked_console(); poke_blanked_console();
release_console_sem(); release_console_sem();
ld = tty_ldisc_ref(tty);
if (!ld) {
tty_unlock();
ld = tty_ldisc_ref_wait(tty); ld = tty_ldisc_ref_wait(tty);
tty_lock();
}
add_wait_queue(&vc->paste_wait, &wait); add_wait_queue(&vc->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) { while (sel_buffer && sel_buffer_lth > pasted) {

View File

@@ -582,6 +582,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty_wait_until_sent(tty, 0); tty_wait_until_sent(tty, 0);
tty_lock();
mutex_lock(&tty->ldisc_mutex); mutex_lock(&tty->ldisc_mutex);
/* /*
@@ -591,13 +592,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
mutex_unlock(&tty->ldisc_mutex); mutex_unlock(&tty->ldisc_mutex);
tty_unlock();
wait_event(tty_ldisc_wait, wait_event(tty_ldisc_wait,
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
tty_lock();
mutex_lock(&tty->ldisc_mutex); mutex_lock(&tty->ldisc_mutex);
} }
tty_lock();
set_bit(TTY_LDISC_CHANGING, &tty->flags); set_bit(TTY_LDISC_CHANGING, &tty->flags);
/* /*
@@ -634,8 +635,8 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
flush_scheduled_work(); flush_scheduled_work();
mutex_lock(&tty->ldisc_mutex);
tty_lock(); tty_lock();
mutex_lock(&tty->ldisc_mutex);
if (test_bit(TTY_HUPPED, &tty->flags)) { if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped /* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */ the ldisc data and closed the ldisc down */
@@ -782,7 +783,20 @@ void tty_ldisc_hangup(struct tty_struct *tty)
* Avoid racing set_ldisc or tty_ldisc_release * Avoid racing set_ldisc or tty_ldisc_release
*/ */
mutex_lock(&tty->ldisc_mutex); mutex_lock(&tty->ldisc_mutex);
tty_ldisc_halt(tty);
/*
* this is like tty_ldisc_halt, but we need to give up
* the BTM before calling cancel_delayed_work_sync,
* which may need to wait for another function taking the BTM
*/
clear_bit(TTY_LDISC, &tty->flags);
tty_unlock();
cancel_delayed_work_sync(&tty->buf.work);
mutex_unlock(&tty->ldisc_mutex);
tty_lock();
mutex_lock(&tty->ldisc_mutex);
/* At this point we have a closed ldisc and we want to /* At this point we have a closed ldisc and we want to
reopen it. We could defer this to the next open but reopen it. We could defer this to the next open but
it means auditing a lot of other paths so this is it means auditing a lot of other paths so this is
@@ -853,8 +867,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
* race with the set_ldisc code path. * race with the set_ldisc code path.
*/ */
tty_unlock();
tty_ldisc_halt(tty); tty_ldisc_halt(tty);
flush_scheduled_work(); flush_scheduled_work();
tty_lock();
mutex_lock(&tty->ldisc_mutex); mutex_lock(&tty->ldisc_mutex);
/* /*