Commit c9b3976e authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

tty: Fix PPP hang under load

Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d0eafc7d
...@@ -316,8 +316,7 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) ...@@ -316,8 +316,7 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{ {
/* wait_event is a macro */ /* wait_event is a macro */
wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
if (tty->ldisc.refcount == 0) WARN_ON(tty->ldisc.refcount == 0);
printk(KERN_ERR "tty_ldisc_ref_wait\n");
return &tty->ldisc; return &tty->ldisc;
} }
...@@ -376,15 +375,17 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref); ...@@ -376,15 +375,17 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref);
* @tty: terminal to activate ldisc on * @tty: terminal to activate ldisc on
* *
* Set the TTY_LDISC flag when the line discipline can be called * Set the TTY_LDISC flag when the line discipline can be called
* again. Do necessary wakeups for existing sleepers. * again. Do necessary wakeups for existing sleepers. Clear the LDISC
* changing flag to indicate any ldisc change is now over.
* *
* Note: nobody should set this bit except via this function. Clearing * Note: nobody should set the TTY_LDISC bit except via this function.
* directly is allowed. * Clearing directly is allowed.
*/ */
void tty_ldisc_enable(struct tty_struct *tty) void tty_ldisc_enable(struct tty_struct *tty)
{ {
set_bit(TTY_LDISC, &tty->flags); set_bit(TTY_LDISC, &tty->flags);
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
wake_up(&tty_ldisc_wait); wake_up(&tty_ldisc_wait);
} }
...@@ -496,7 +497,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) ...@@ -496,7 +497,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
* reference to the line discipline. The TTY_LDISC bit * reference to the line discipline. The TTY_LDISC bit
* prevents anyone taking a reference once it is clear. * prevents anyone taking a reference once it is clear.
* We need the lock to avoid racing reference takers. * We need the lock to avoid racing reference takers.
*
* We must clear the TTY_LDISC bit here to avoid a livelock
* with a userspace app continually trying to use the tty in
* parallel to the change and re-referencing the tty.
*/ */
clear_bit(TTY_LDISC, &tty->flags);
if (o_tty)
clear_bit(TTY_LDISC, &o_tty->flags);
spin_lock_irqsave(&tty_ldisc_lock, flags); spin_lock_irqsave(&tty_ldisc_lock, flags);
if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) { if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
...@@ -528,7 +536,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) ...@@ -528,7 +536,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
* If the TTY_LDISC bit is set, then we are racing against * If the TTY_LDISC bit is set, then we are racing against
* another ldisc change * another ldisc change
*/ */
if (!test_bit(TTY_LDISC, &tty->flags)) { if (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
struct tty_ldisc *ld; struct tty_ldisc *ld;
spin_unlock_irqrestore(&tty_ldisc_lock, flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags);
tty_ldisc_put(new_ldisc.ops); tty_ldisc_put(new_ldisc.ops);
...@@ -536,10 +544,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) ...@@ -536,10 +544,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty_ldisc_deref(ld); tty_ldisc_deref(ld);
goto restart; goto restart;
} }
/*
clear_bit(TTY_LDISC, &tty->flags); * This flag is used to avoid two parallel ldisc changes. Once
* open and close are fine grained locked this may work better
* as a mutex shared with the open/close/hup paths
*/
set_bit(TTY_LDISC_CHANGING, &tty->flags);
if (o_tty) if (o_tty)
clear_bit(TTY_LDISC, &o_tty->flags); set_bit(TTY_LDISC_CHANGING, &o_tty->flags);
spin_unlock_irqrestore(&tty_ldisc_lock, flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags);
/* /*
......
...@@ -301,6 +301,7 @@ struct tty_struct { ...@@ -301,6 +301,7 @@ struct tty_struct {
#define TTY_PUSH 6 /* n_tty private */ #define TTY_PUSH 6 /* n_tty private */
#define TTY_CLOSING 7 /* ->close() in progress */ #define TTY_CLOSING 7 /* ->close() in progress */
#define TTY_LDISC 9 /* Line discipline attached */ #define TTY_LDISC 9 /* Line discipline attached */
#define TTY_LDISC_CHANGING 10 /* Line discipline changing */
#define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */ #define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */
#define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */ #define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */
#define TTY_PTY_LOCK 16 /* pty private */ #define TTY_PTY_LOCK 16 /* pty private */
......
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