Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2c43890 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman
Browse files

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: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 137084bb
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
@@ -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