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

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

tty: Re-open /dev/tty without tty_mutex



Opening /dev/tty (ie., the controlling tty for the current task)
is always a re-open of the underlying tty. Because holding the
tty_lock is sufficient for safely re-opening a tty, and because
having a tty kref is sufficient for safely acquiring the tty_lock [1],
tty_open_current_tty() does not require holding tty_mutex.

Repurpose tty_open_current_tty() to perform the re-open itself and
refactor tty_open().

[1] Analysis of safely re-opening the current tty w/o tty_mutex

get_current_tty() gets a tty kref from the already kref'ed tty value of
current->signal->tty while holding the sighand lock for the current
task. This guarantees that the tty pointer returned from
get_current_tty() points to a tty which remains referenceable
while holding the kref.

Although release_tty() may run concurrently, and thus the driver
reference may be removed, release_one_tty() cannot have run, and
won't while holding the tty kref.

This, in turn, guarantees the tty_lock() can safely be acquired
(since tty->magic and tty->legacy_mutex are still a valid dereferences).
The tty_lock() also gets a tty kref to prevent the tty_unlock() from
dereferencing a released tty. Thus, the kref returned from
get_current_tty() can be released.

Lastly, the first operation of tty_reopen() is to check the tty count.
If non-zero, this ensures release_tty() is not running concurrently,
and the driver references have not been removed.

Reviewed-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 55199ea3
Loading
Loading
Loading
Loading
+27 −26
Original line number Diff line number Diff line
@@ -1935,20 +1935,20 @@ int tty_release(struct inode *inode, struct file *filp)
}

/**
 *	tty_open_current_tty - get tty of current task for open
 *	tty_open_current_tty - get locked tty of current task
 *	@device: device number
 *	@filp: file pointer to tty
 *	@return: tty of the current task iff @device is /dev/tty
 *	@return: locked tty of the current task iff @device is /dev/tty
 *
 *	Performs a re-open of the current task's controlling tty.
 *
 *	We cannot return driver and index like for the other nodes because
 *	devpts will not work then. It expects inodes to be from devpts FS.
 *
 *	We need to move to returning a refcounted object from all the lookup
 *	paths including this one.
 */
static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
	struct tty_struct *tty;
	int retval;

	if (device != MKDEV(TTYAUX_MAJOR, 0))
		return NULL;
@@ -1959,9 +1959,14 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)

	filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
	/* noctty = 1; */
	tty_kref_put(tty);
	/* FIXME: we put a reference and return a TTY! */
	/* This is only safe because the caller holds tty_mutex */
	tty_lock(tty);
	tty_kref_put(tty);	/* safe to drop the kref now */

	retval = tty_reopen(tty);
	if (retval < 0) {
		tty_unlock(tty);
		tty = ERR_PTR(retval);
	}
	return tty;
}

@@ -2059,13 +2064,9 @@ static int tty_open(struct inode *inode, struct file *filp)
	index  = -1;
	retval = 0;

	mutex_lock(&tty_mutex);
	/* This is protected by the tty_mutex */
	tty = tty_open_current_tty(device, filp);
	if (IS_ERR(tty)) {
		retval = PTR_ERR(tty);
		goto err_unlock;
	} else if (!tty) {
	if (!tty) {
		mutex_lock(&tty_mutex);
		driver = tty_lookup_driver(device, filp, &noctty, &index);
		if (IS_ERR(driver)) {
			retval = PTR_ERR(driver);
@@ -2078,7 +2079,6 @@ static int tty_open(struct inode *inode, struct file *filp)
			retval = PTR_ERR(tty);
			goto err_unlock;
		}
	}

		if (tty) {
			tty_lock(tty);
@@ -2091,8 +2091,9 @@ static int tty_open(struct inode *inode, struct file *filp)
			tty = tty_init_dev(driver, index);

		mutex_unlock(&tty_mutex);
	if (driver)
		tty_driver_kref_put(driver);
	}

	if (IS_ERR(tty)) {
		retval = PTR_ERR(tty);
		goto err_file;