Commit 60af22d2 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Greg Kroah-Hartman

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: default avatarArnd Bergmann <arnd@arndb.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent be1bc288
...@@ -319,8 +319,13 @@ int paste_selection(struct tty_struct *tty) ...@@ -319,8 +319,13 @@ int paste_selection(struct tty_struct *tty)
poke_blanked_console(); poke_blanked_console();
release_console_sem(); release_console_sem();
ld = tty_ldisc_ref_wait(tty); ld = tty_ldisc_ref(tty);
if (!ld) {
tty_unlock();
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) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
......
...@@ -582,6 +582,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment