X-Git-Url: http://git.cascardo.info/?a=blobdiff_plain;f=drivers%2Ftty%2Ftty_ldisc.c;h=3737f55272d2c1184463edc3714f89fb2724730e;hb=64bb1b944b554a751b518b09c3d596f6b6c0ce31;hp=1c4d7b6d8a76f06d7615222b7a353f7c077e8836;hpb=62462aefeb5aff092fc97037d9c12a4afe95a3ff;p=cascardo%2Flinux.git diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 1c4d7b6d8a76..3737f55272d2 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -308,23 +308,41 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref); static inline int __lockfunc -tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +__tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { return ldsem_down_write(&tty->ldisc_sem, timeout); } static inline int __lockfunc -tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) +__tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) { return ldsem_down_write_nested(&tty->ldisc_sem, LDISC_SEM_OTHER, timeout); } -static inline void tty_ldisc_unlock(struct tty_struct *tty) +static inline void __tty_ldisc_unlock(struct tty_struct *tty) { return ldsem_up_write(&tty->ldisc_sem); } +static int __lockfunc +tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) +{ + int ret; + + ret = __tty_ldisc_lock(tty, timeout); + if (!ret) + return -EBUSY; + set_bit(TTY_LDISC_HALTED, &tty->flags); + return 0; +} + +static void tty_ldisc_unlock(struct tty_struct *tty) +{ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + __tty_ldisc_unlock(tty); +} + static int __lockfunc tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, unsigned long timeout) @@ -332,24 +350,24 @@ tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, int ret; if (tty < tty2) { - ret = tty_ldisc_lock(tty, timeout); + ret = __tty_ldisc_lock(tty, timeout); if (ret) { - ret = tty_ldisc_lock_nested(tty2, timeout); + ret = __tty_ldisc_lock_nested(tty2, timeout); if (!ret) - tty_ldisc_unlock(tty); + __tty_ldisc_unlock(tty); } } else { /* if this is possible, it has lots of implications */ WARN_ON_ONCE(tty == tty2); if (tty2 && tty != tty2) { - ret = tty_ldisc_lock(tty2, timeout); + ret = __tty_ldisc_lock(tty2, timeout); if (ret) { - ret = tty_ldisc_lock_nested(tty, timeout); + ret = __tty_ldisc_lock_nested(tty, timeout); if (!ret) - tty_ldisc_unlock(tty2); + __tty_ldisc_unlock(tty2); } } else - ret = tty_ldisc_lock(tty, timeout); + ret = __tty_ldisc_lock(tty, timeout); } if (!ret) @@ -370,38 +388,26 @@ tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, struct tty_struct *tty2) { - tty_ldisc_unlock(tty); + __tty_ldisc_unlock(tty); if (tty2) - tty_ldisc_unlock(tty2); -} - -static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, - struct tty_struct *tty2) -{ - clear_bit(TTY_LDISC_HALTED, &tty->flags); - if (tty2) - clear_bit(TTY_LDISC_HALTED, &tty2->flags); - - tty_ldisc_unlock_pair(tty, tty2); + __tty_ldisc_unlock(tty2); } /** * tty_ldisc_flush - flush line discipline queue * @tty: tty * - * Flush the line discipline queue (if any) for this tty. If there - * is no line discipline active this is a no-op. + * Flush the line discipline queue (if any) and the tty flip buffers + * for this tty. */ void tty_ldisc_flush(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); + + tty_buffer_flush(tty, ld); + if (ld) tty_ldisc_deref(ld); - } - tty_buffer_flush(tty); } EXPORT_SYMBOL_GPL(tty_ldisc_flush); @@ -517,14 +523,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; struct tty_ldisc *old_ldisc, *new_ldisc; - struct tty_struct *o_tty = tty->link; new_ldisc = tty_ldisc_get(tty, ldisc); if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); tty_lock(tty); - retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); + retval = tty_ldisc_lock(tty, 5 * HZ); if (retval) { tty_ldisc_put(new_ldisc); tty_unlock(tty); @@ -536,7 +541,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_ldisc_enable_pair(tty, o_tty); + tty_ldisc_unlock(tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return 0; @@ -547,7 +552,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (test_bit(TTY_HUPPED, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ - tty_ldisc_enable_pair(tty, o_tty); + tty_ldisc_unlock(tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return -EIO; @@ -567,8 +572,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_ldisc_restore(tty, old_ldisc); } - if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) + if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { + down_read(&tty->termios_rwsem); tty->ops->set_ldisc(tty); + up_read(&tty->termios_rwsem); + } /* At this point we hold a reference to the new ldisc and a reference to the old ldisc, or we hold two references to @@ -581,13 +589,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) /* * Allow ldisc referencing to occur again */ - tty_ldisc_enable_pair(tty, o_tty); + tty_ldisc_unlock(tty); /* Restart the work queue in case no characters kick it off. Safe if already running */ schedule_work(&tty->port->buf.work); - if (o_tty) - schedule_work(&o_tty->port->buf.work); tty_unlock(tty); return retval; @@ -682,7 +688,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) * * Avoid racing set_ldisc or tty_ldisc_release */ - tty_ldisc_lock_pair(tty, tty->link); + tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT); if (tty->ldisc) { @@ -704,7 +710,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) WARN_ON(tty_ldisc_open(tty, tty->ldisc)); } } - tty_ldisc_enable_pair(tty, tty->link); + tty_ldisc_unlock(tty); if (reset) tty_reset_termios(tty);