From d2c438905f9f718b3d9f5d89ce163fc22bd33995 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:04:47 -0400 Subject: tty: Add lock/unlock ldisc pair functions Just as the tty pair must be locked in a stable sequence (ie, independent of which is consider the 'other' tty), so must the ldisc pair be locked in a stable sequence as well. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'drivers/tty/tty_ldisc.c') diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 8166260aa839..418c9f64a9fd 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -31,6 +31,13 @@ #define tty_ldisc_debug(tty, f, args...) #endif +/* lockdep nested classes for tty->ldisc_sem */ +enum { + LDISC_SEM_NORMAL, + LDISC_SEM_OTHER, +}; + + /* * This guards the refcounted line discipline lists. The lock * must be taken with irqs off because there are hangup path @@ -351,6 +358,86 @@ void tty_ldisc_deref(struct tty_ldisc *ld) } EXPORT_SYMBOL_GPL(tty_ldisc_deref); + +static inline int __lockfunc +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) +{ + return ldsem_down_write_nested(&tty->ldisc_sem, + LDISC_SEM_OTHER, timeout); +} + +static inline void tty_ldisc_unlock(struct tty_struct *tty) +{ + return ldsem_up_write(&tty->ldisc_sem); +} + +static int __lockfunc +tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, + unsigned long timeout) +{ + int ret; + + if (tty < tty2) { + ret = tty_ldisc_lock(tty, timeout); + if (ret) { + ret = tty_ldisc_lock_nested(tty2, timeout); + if (!ret) + 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); + if (ret) { + ret = tty_ldisc_lock_nested(tty, timeout); + if (!ret) + tty_ldisc_unlock(tty2); + } + } else + ret = tty_ldisc_lock(tty, timeout); + } + + if (!ret) + return -EBUSY; + + set_bit(TTY_LDISC_HALTED, &tty->flags); + if (tty2) + set_bit(TTY_LDISC_HALTED, &tty2->flags); + return 0; +} + +static void __lockfunc +tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) +{ + tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT); +} + +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); + if (tty2) + clear_bit(TTY_LDISC_HALTED, &tty2->flags); + + tty_ldisc_unlock_pair(tty, tty2); +} + + /** * tty_ldisc_enable - allow ldisc use * @tty: terminal to activate ldisc on -- cgit v1.2.3-59-g8ed1b