|
@@ -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)
|
|
|
|
- printk(KERN_ERR "tty_ldisc_ref_wait\n");
|
|
|
|
|
|
+ WARN_ON(tty->ldisc.refcount == 0);
|
|
return &tty->ldisc;
|
|
return &tty->ldisc;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -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
|
|
|
|
- * directly is allowed.
|
|
|
|
|
|
+ * Note: nobody should set the TTY_LDISC bit except via this function.
|
|
|
|
+ * 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 @@ restart:
|
|
* 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 @@ restart:
|
|
* 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 @@ restart:
|
|
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);
|
|
|
|
|
|
/*
|
|
/*
|