diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2014-11-05 20:12:50 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-11-06 05:24:10 +0300 |
commit | 52494eeb993ef6865f7817c8b473f798771371fc (patch) | |
tree | 91cc87744fe28fdc04b968d6c671a84f3cfcfa1a /drivers/tty/tty_io.c | |
parent | 55199ea3bd2e53007715d544fb9094cbbdda1597 (diff) | |
download | linux-52494eeb993ef6865f7817c8b473f798771371fc.tar.xz |
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: Alan Cox <alan@linux.intel.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/tty_io.c')
-rw-r--r-- | drivers/tty/tty_io.c | 53 |
1 files changed, 27 insertions, 26 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 0eaf632ad39e..2e166a1be2b7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -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 @@ retry_open: 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,21 +2079,21 @@ retry_open: retval = PTR_ERR(tty); goto err_unlock; } - } - if (tty) { - tty_lock(tty); - retval = tty_reopen(tty); - if (retval < 0) { - tty_unlock(tty); - tty = ERR_PTR(retval); - } - } else /* Returns with the tty_lock held for now */ - tty = tty_init_dev(driver, index); + if (tty) { + tty_lock(tty); + retval = tty_reopen(tty); + if (retval < 0) { + tty_unlock(tty); + tty = ERR_PTR(retval); + } + } else /* Returns with the tty_lock held for now */ + tty = tty_init_dev(driver, index); - mutex_unlock(&tty_mutex); - if (driver) + mutex_unlock(&tty_mutex); tty_driver_kref_put(driver); + } + if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto err_file; |