X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Ftty%2Ftty_ldisc.c;h=3737f55272d2c1184463edc3714f89fb2724730e;hb=28023d2a8e9121b1b06c3b46e523c3c7a3e8b530;hp=49001fa2ea2f0084480c2d09ba4c1cd325b12851;hpb=3ff51a199f9e85aed843471bc10dae9e94dbb0fc;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 49001fa2ea2f..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); - 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); + __tty_ldisc_unlock(tty); 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); @@ -756,18 +762,17 @@ static void tty_ldisc_kill(struct tty_struct *tty) /** * tty_ldisc_release - release line discipline - * @tty: tty being shut down - * @o_tty: pair tty for pty/tty pairs - * - * Called during the final close of a tty/pty pair in order to shut down - * the line discpline layer. On exit the ldisc assigned is N_TTY and the - * ldisc has not been opened. + * @tty: tty being shut down (or one end of pty pair) * - * Holding ldisc_sem write lock serializes tty->ldisc changes. + * Called during the final close of a tty or a pty pair in order to shut + * down the line discpline layer. On exit, each ldisc assigned is N_TTY and + * each ldisc has not been opened. */ -void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) +void tty_ldisc_release(struct tty_struct *tty) { + struct tty_struct *o_tty = tty->link; + /* * Shutdown this line discipline. As this is the final close, * it does not race with the set_ldisc code path.