Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -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) {
+20 −4
Original line number Original line Diff line number Diff line
@@ -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);
	/*
	/*