summaryrefslogtreecommitdiff
path: root/drivers/tty/pty.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/pty.c')
-rw-r--r--drivers/tty/pty.c464
1 files changed, 282 insertions, 182 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index abfd99089781..6120d827a797 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1991, 1992 Linus Torvalds
*
@@ -7,13 +8,12 @@
*/
#include <linux/module.h>
-
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/fcntl.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/mm.h>
@@ -24,7 +24,19 @@
#include <linux/devpts_fs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
-
+#include <linux/poll.h>
+#include <linux/mount.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/compat.h>
+#include "tty.h"
+
+#undef TTY_DEBUG_HANGUP
+#ifdef TTY_DEBUG_HANGUP
+# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
+#else
+# define tty_debug_hangup(tty, f, args...) do {} while (0)
+#endif
#ifdef CONFIG_UNIX98_PTYS
static struct tty_driver *ptm_driver;
@@ -34,11 +46,10 @@ static DEFINE_MUTEX(devpts_mutex);
static void pty_close(struct tty_struct *tty, struct file *filp)
{
- BUG_ON(!tty);
if (tty->driver->subtype == PTY_TYPE_MASTER)
WARN_ON(tty->count > 1);
else {
- if (test_bit(TTY_IO_ERROR, &tty->flags))
+ if (tty_io_error(tty))
return;
if (tty->count > 2)
return;
@@ -46,7 +57,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
set_bit(TTY_IO_ERROR, &tty->flags);
wake_up_interruptible(&tty->read_wait);
wake_up_interruptible(&tty->write_wait);
- tty->packet = 0;
+ scoped_guard(spinlock_irq, &tty->ctrl.lock)
+ tty->ctrl.packet = false;
/* Review - krefs on tty_link ?? */
if (!tty->link)
return;
@@ -57,15 +69,12 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
set_bit(TTY_OTHER_CLOSED, &tty->flags);
#ifdef CONFIG_UNIX98_PTYS
if (tty->driver == ptm_driver) {
- mutex_lock(&devpts_mutex);
+ guard(mutex)(&devpts_mutex);
if (tty->link->driver_data)
devpts_pty_kill(tty->link->driver_data);
- mutex_unlock(&devpts_mutex);
}
#endif
- tty_unlock(tty);
tty_vhangup(tty->link);
- tty_lock(tty);
}
}
@@ -86,27 +95,10 @@ static void pty_unthrottle(struct tty_struct *tty)
}
/**
- * pty_space - report space left for writing
- * @to: tty we are writing into
- *
- * The tty buffers allow 64K but we sneak a peak and clip at 8K this
- * allows a lot of overspill room for echo and other fun messes to
- * be handled properly
- */
-
-static int pty_space(struct tty_struct *to)
-{
- int n = 8192 - to->port->buf.memory_used;
- if (n < 0)
- return 0;
- return n;
-}
-
-/**
* pty_write - write to a pty
* @tty: the tty we write from
* @buf: kernel buffer of data
- * @count: bytes to write
+ * @c: bytes to write
*
* Our "hardware" write method. Data is coming from the ldisc which
* may be in a non sleeping state. We simply throw this at the other
@@ -114,23 +106,14 @@ static int pty_space(struct tty_struct *to)
* the other side of the pty/tty pair.
*/
-static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
+static ssize_t pty_write(struct tty_struct *tty, const u8 *buf, size_t c)
{
struct tty_struct *to = tty->link;
- if (tty->stopped)
+ if (tty->flow.stopped || !c)
return 0;
- if (c > 0) {
- /* Stuff the data into the input queue of the other end */
- c = tty_insert_flip_string(to->port, buf, c);
- /* And shovel */
- if (c) {
- tty_flip_buffer_push(to->port);
- tty_wakeup(tty);
- }
- }
- return c;
+ return tty_insert_flip_string_and_push_buffer(to->port, buf, c);
}
/**
@@ -141,30 +124,18 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
* the other device.
*/
-static int pty_write_room(struct tty_struct *tty)
+static unsigned int pty_write_room(struct tty_struct *tty)
{
- if (tty->stopped)
+ if (tty->flow.stopped)
return 0;
- return pty_space(tty->link);
-}
-
-/**
- * pty_chars_in_buffer - characters currently in our tx queue
- * @tty: our tty
- *
- * Report how much we have in the transmit queue. As everything is
- * instantly at the other end this is easy to implement.
- */
-
-static int pty_chars_in_buffer(struct tty_struct *tty)
-{
- return 0;
+ return tty_buffer_space_avail(tty->link->port);
}
/* Set the lock flag on a pty */
static int pty_set_lock(struct tty_struct *tty, int __user *arg)
{
int val;
+
if (get_user(val, arg))
return -EFAULT;
if (val)
@@ -177,27 +148,30 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg)
static int pty_get_lock(struct tty_struct *tty, int __user *arg)
{
int locked = test_bit(TTY_PTY_LOCK, &tty->flags);
+
return put_user(locked, arg);
}
/* Set the packet mode on a pty */
static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
{
- unsigned long flags;
- int pktmode;
+ int want_pktmode;
- if (get_user(pktmode, arg))
+ if (get_user(want_pktmode, arg))
return -EFAULT;
- spin_lock_irqsave(&tty->ctrl_lock, flags);
- if (pktmode) {
- if (!tty->packet) {
- tty->packet = 1;
- tty->link->ctrl_status = 0;
- }
- } else
- tty->packet = 0;
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ guard(spinlock_irq)(&tty->ctrl.lock);
+ if (!want_pktmode) {
+ tty->ctrl.packet = false;
+ return 0;
+ }
+
+ if (tty->ctrl.packet)
+ return 0;
+
+ tty->link->ctrl.pktstatus = 0;
+ smp_mb();
+ tty->ctrl.packet = true;
return 0;
}
@@ -205,22 +179,23 @@ static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
/* Get the packet mode of a pty */
static int pty_get_pktmode(struct tty_struct *tty, int __user *arg)
{
- int pktmode = tty->packet;
+ int pktmode = tty->ctrl.packet;
+
return put_user(pktmode, arg);
}
/* Send a signal to the slave */
static int pty_signal(struct tty_struct *tty, int sig)
{
- unsigned long flags;
struct pid *pgrp;
- if (tty->link) {
- spin_lock_irqsave(&tty->link->ctrl_lock, flags);
- pgrp = get_pid(tty->link->pgrp);
- spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
+ if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
+ return -EINVAL;
- kill_pgrp(pgrp, sig, 1);
+ if (tty->link) {
+ pgrp = tty_get_pgrp(tty->link);
+ if (pgrp)
+ kill_pgrp(pgrp, sig, 1);
put_pid(pgrp);
}
return 0;
@@ -229,16 +204,15 @@ static int pty_signal(struct tty_struct *tty, int sig)
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
- unsigned long flags;
if (!to)
return;
- /* tty_buffer_flush(to); FIXME */
- if (to->packet) {
- spin_lock_irqsave(&tty->ctrl_lock, flags);
- tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
+
+ tty_buffer_flush(to, NULL);
+ if (to->ctrl.packet) {
+ guard(spinlock_irq)(&tty->ctrl.lock);
+ tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&to->read_wait);
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
}
}
@@ -265,14 +239,39 @@ out:
}
static void pty_set_termios(struct tty_struct *tty,
- struct ktermios *old_termios)
+ const struct ktermios *old_termios)
{
+ /* See if packet mode change of state. */
+ if (tty->link && tty->link->ctrl.packet) {
+ int extproc = (old_termios->c_lflag & EXTPROC) | L_EXTPROC(tty);
+ int old_flow = ((old_termios->c_iflag & IXON) &&
+ (old_termios->c_cc[VSTOP] == '\023') &&
+ (old_termios->c_cc[VSTART] == '\021'));
+ int new_flow = (I_IXON(tty) &&
+ STOP_CHAR(tty) == '\023' &&
+ START_CHAR(tty) == '\021');
+ if ((old_flow != new_flow) || extproc) {
+ scoped_guard(spinlock_irq, &tty->ctrl.lock) {
+ if (old_flow != new_flow) {
+ tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+ if (new_flow)
+ tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
+ else
+ tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
+ }
+ if (extproc)
+ tty->ctrl.pktstatus |= TIOCPKT_IOCTL;
+ }
+ wake_up_interruptible(&tty->link->read_wait);
+ }
+ }
+
tty->termios.c_cflag &= ~(CSIZE | PARENB);
tty->termios.c_cflag |= (CS8 | CREAD);
}
/**
- * pty_do_resize - resize event
+ * pty_resize - resize event
* @tty: tty being resized
* @ws: window size being set.
*
@@ -283,25 +282,16 @@ static void pty_set_termios(struct tty_struct *tty,
static int pty_resize(struct tty_struct *tty, struct winsize *ws)
{
struct pid *pgrp, *rpgrp;
- unsigned long flags;
struct tty_struct *pty = tty->link;
/* For a PTY we need to lock the tty side */
- mutex_lock(&tty->termios_mutex);
+ guard(mutex)(&tty->winsize_mutex);
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
- goto done;
-
- /* Get the PID values and reference them so we can
- avoid holding the tty ctrl lock while sending signals.
- We need to lock these individually however. */
-
- spin_lock_irqsave(&tty->ctrl_lock, flags);
- pgrp = get_pid(tty->pgrp);
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ return 0;
- spin_lock_irqsave(&pty->ctrl_lock, flags);
- rpgrp = get_pid(pty->pgrp);
- spin_unlock_irqrestore(&pty->ctrl_lock, flags);
+ /* Signal the foreground process group of both ptys */
+ pgrp = tty_get_pgrp(tty);
+ rpgrp = tty_get_pgrp(pty);
if (pgrp)
kill_pgrp(pgrp, SIGWINCH, 1);
@@ -313,16 +303,49 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws)
tty->winsize = *ws;
pty->winsize = *ws; /* Never used so will go away soon */
-done:
- mutex_unlock(&tty->termios_mutex);
+
return 0;
}
/**
+ * pty_start - start() handler
+ * pty_stop - stop() handler
+ * @tty: tty being flow-controlled
+ *
+ * Propagates the TIOCPKT status to the master pty.
+ *
+ * NB: only the master pty can be in packet mode so only the slave
+ * needs start()/stop() handlers
+ */
+static void pty_start(struct tty_struct *tty)
+{
+ if (!tty->link || !tty->link->ctrl.packet)
+ return;
+
+ scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {
+ tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
+ tty->ctrl.pktstatus |= TIOCPKT_START;
+ }
+ wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
+}
+
+static void pty_stop(struct tty_struct *tty)
+{
+ if (!tty->link || !tty->link->ctrl.packet)
+ return;
+
+ scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {
+ tty->ctrl.pktstatus &= ~TIOCPKT_START;
+ tty->ctrl.pktstatus |= TIOCPKT_STOP;
+ }
+ wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
+}
+
+/**
* pty_common_install - set up the pty pair
* @driver: the pty driver
* @tty: the tty being instantiated
- * @bool: legacy, true if this is BSD style
+ * @legacy: true if this is BSD style
*
* Perform the initial set up for the tty/pty pair. Called from the
* tty layer when the port is first opened.
@@ -337,29 +360,30 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
int idx = tty->index;
int retval = -ENOMEM;
- o_tty = alloc_tty_struct();
- if (!o_tty)
- goto err;
+ /* Opening the slave first has always returned -EIO */
+ if (driver->subtype != PTY_TYPE_MASTER)
+ return -EIO;
+
ports[0] = kmalloc(sizeof **ports, GFP_KERNEL);
ports[1] = kmalloc(sizeof **ports, GFP_KERNEL);
if (!ports[0] || !ports[1])
- goto err_free_tty;
+ goto err;
if (!try_module_get(driver->other->owner)) {
/* This cannot in fact currently happen */
- goto err_free_tty;
+ goto err;
}
- initialize_tty_struct(o_tty, driver->other, idx);
+ o_tty = alloc_tty_struct(driver->other, idx);
+ if (!o_tty)
+ goto err_put_module;
+
+ tty_set_lock_subclass(o_tty);
+ lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
if (legacy) {
/* We always use new tty termios data so we can do this
the easy way .. */
- retval = tty_init_termios(tty);
- if (retval)
- goto err_deinit_tty;
-
- retval = tty_init_termios(o_tty);
- if (retval)
- goto err_free_termios;
+ tty_init_termios(tty);
+ tty_init_termios(o_tty);
driver->other->ttys[idx] = o_tty;
driver->ttys[idx] = tty;
@@ -374,31 +398,29 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
* Everything allocated ... set up the o_tty structure.
*/
tty_driver_kref_get(driver->other);
- if (driver->subtype == PTY_TYPE_MASTER)
- o_tty->count++;
/* Establish the links in both directions */
tty->link = o_tty;
o_tty->link = tty;
tty_port_init(ports[0]);
tty_port_init(ports[1]);
+ tty_buffer_set_limit(ports[0], 8192);
+ tty_buffer_set_limit(ports[1], 8192);
o_tty->port = ports[0];
tty->port = ports[1];
o_tty->port->itty = o_tty;
+ tty_buffer_set_lock_subclass(o_tty->port);
+
tty_driver_kref_get(driver);
tty->count++;
+ o_tty->count++;
return 0;
-err_free_termios:
- if (legacy)
- tty_free_termios(tty);
-err_deinit_tty:
- deinitialize_tty_struct(o_tty);
- module_put(o_tty->driver->owner);
-err_free_tty:
+
+err_put_module:
+ module_put(driver->other->owner);
+err:
kfree(ports[0]);
kfree(ports[1]);
- free_tty_struct(o_tty);
-err:
return retval;
}
@@ -418,6 +440,7 @@ static int pty_install(struct tty_driver *driver, struct tty_struct *tty)
static void pty_remove(struct tty_driver *driver, struct tty_struct *tty)
{
struct tty_struct *pair = tty->link;
+
driver->ttys[tty->index] = NULL;
if (pair)
pair->driver->ttys[pair->index] = NULL;
@@ -443,7 +466,25 @@ static int pty_bsd_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}
+#ifdef CONFIG_COMPAT
+static long pty_bsd_compat_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ /*
+ * PTY ioctls don't require any special translation between 32-bit and
+ * 64-bit userspace, they are already compatible.
+ */
+ return pty_bsd_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
+}
+#else
+#define pty_bsd_compat_ioctl NULL
+#endif
+
static int legacy_count = CONFIG_LEGACY_PTY_COUNT;
+/*
+ * not really modular, but the easiest way to keep compat with existing
+ * bootargs behaviour is to continue using module_param here.
+ */
module_param(legacy_count, int, 0);
/*
@@ -457,10 +498,9 @@ static const struct tty_operations master_pty_ops_bsd = {
.write = pty_write,
.write_room = pty_write_room,
.flush_buffer = pty_flush_buffer,
- .chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
- .set_termios = pty_set_termios,
.ioctl = pty_bsd_ioctl,
+ .compat_ioctl = pty_bsd_compat_ioctl,
.cleanup = pty_cleanup,
.resize = pty_resize,
.remove = pty_remove
@@ -473,11 +513,12 @@ static const struct tty_operations slave_pty_ops_bsd = {
.write = pty_write,
.write_room = pty_write_room,
.flush_buffer = pty_flush_buffer,
- .chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
.set_termios = pty_set_termios,
.cleanup = pty_cleanup,
.resize = pty_resize,
+ .start = pty_start,
+ .stop = pty_stop,
.remove = pty_remove
};
@@ -542,9 +583,43 @@ static inline void legacy_pty_init(void) { }
/* Unix98 devices */
#ifdef CONFIG_UNIX98_PTYS
-
static struct cdev ptmx_cdev;
+static struct file *ptm_open_peer_file(struct file *master,
+ struct tty_struct *tty, int flags)
+{
+ struct path path;
+ struct file *file;
+
+ /* Compute the slave's path */
+ path.mnt = devpts_mntget(master, tty->driver_data);
+ if (IS_ERR(path.mnt))
+ return ERR_CAST(path.mnt);
+ path.dentry = tty->link->driver_data;
+
+ file = dentry_open(&path, flags, current_cred());
+ mntput(path.mnt);
+ return file;
+}
+
+/**
+ * ptm_open_peer - open the peer of a pty
+ * @master: the open struct file of the ptmx device node
+ * @tty: the master of the pty being opened
+ * @flags: the flags for open
+ *
+ * Provide a race free way for userspace to open the slave end of a pty
+ * (where they have the master fd and cannot access or trust the mount
+ * namespace /dev/pts was mounted inside).
+ */
+int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
+{
+ if (tty->driver != ptm_driver)
+ return -EIO;
+
+ return FD_ADD(flags, ptm_open_peer_file(master, tty, flags));
+}
+
static int pty_unix98_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -566,9 +641,25 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}
+#ifdef CONFIG_COMPAT
+static long pty_unix98_compat_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ /*
+ * PTY ioctls don't require any special translation between 32-bit and
+ * 64-bit userspace, they are already compatible.
+ */
+ return pty_unix98_ioctl(tty, cmd,
+ cmd == TIOCSIG ? arg : (unsigned long)compat_ptr(arg));
+}
+#else
+#define pty_unix98_compat_ioctl NULL
+#endif
+
/**
* ptm_unix98_lookup - find a pty master
* @driver: ptm driver
+ * @file: unused
* @idx: tty index
*
* Look up a pty master device. Called under the tty_mutex for now.
@@ -576,7 +667,7 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
*/
static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
- struct inode *ptm_inode, int idx)
+ struct file *file, int idx)
{
/* Master must be open via /dev/ptmx */
return ERR_PTR(-EIO);
@@ -585,6 +676,7 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
/**
* pts_unix98_lookup - find a pty slave
* @driver: pts driver
+ * @file: file pointer to tty
* @idx: tty index
*
* Look up a pty master device. Called under the tty_mutex for now.
@@ -592,35 +684,37 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
*/
static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
- struct inode *pts_inode, int idx)
+ struct file *file, int idx)
{
- struct tty_struct *tty;
-
- mutex_lock(&devpts_mutex);
- tty = devpts_get_priv(pts_inode);
- mutex_unlock(&devpts_mutex);
+ guard(mutex)(&devpts_mutex);
/* Master must be open before slave */
- if (!tty)
- return ERR_PTR(-EIO);
- return tty;
+ return devpts_get_priv(file->f_path.dentry) ? : ERR_PTR(-EIO);
}
-/* We have no need to install and remove our tty objects as devpts does all
- the work for us */
-
static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
{
return pty_common_install(driver, tty, false);
}
+/* this is called once with whichever end is closed last */
static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
{
+ struct pts_fs_info *fsi;
+
+ if (tty->driver->subtype == PTY_TYPE_MASTER)
+ fsi = tty->driver_data;
+ else
+ fsi = tty->link->driver_data;
+
+ if (fsi) {
+ devpts_kill_index(fsi, tty->index);
+ devpts_release(fsi);
+ }
}
-/* this is called once with whichever end is closed last */
-static void pty_unix98_shutdown(struct tty_struct *tty)
+static void pty_show_fdinfo(struct tty_struct *tty, struct seq_file *m)
{
- devpts_kill_index(tty->driver_data, tty->index);
+ seq_printf(m, "tty-index:\t%d\n", tty->index);
}
static const struct tty_operations ptm_unix98_ops = {
@@ -632,13 +726,12 @@ static const struct tty_operations ptm_unix98_ops = {
.write = pty_write,
.write_room = pty_write_room,
.flush_buffer = pty_flush_buffer,
- .chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
- .set_termios = pty_set_termios,
.ioctl = pty_unix98_ioctl,
+ .compat_ioctl = pty_unix98_compat_ioctl,
.resize = pty_resize,
- .shutdown = pty_unix98_shutdown,
- .cleanup = pty_cleanup
+ .cleanup = pty_cleanup,
+ .show_fdinfo = pty_show_fdinfo,
};
static const struct tty_operations pty_unix98_ops = {
@@ -650,10 +743,10 @@ static const struct tty_operations pty_unix98_ops = {
.write = pty_write,
.write_room = pty_write_room,
.flush_buffer = pty_flush_buffer,
- .chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
.set_termios = pty_set_termios,
- .shutdown = pty_unix98_shutdown,
+ .start = pty_start,
+ .stop = pty_stop,
.cleanup = pty_cleanup,
};
@@ -671,76 +764,83 @@ static const struct tty_operations pty_unix98_ops = {
static int ptmx_open(struct inode *inode, struct file *filp)
{
+ struct pts_fs_info *fsi;
struct tty_struct *tty;
- struct inode *slave_inode;
+ struct dentry *dentry;
int retval;
int index;
nonseekable_open(inode, filp);
/* We refuse fsnotify events on ptmx, since it's a shared resource */
- filp->f_mode |= FMODE_NONOTIFY;
+ file_set_fsnotify_mode(filp, FMODE_NONOTIFY);
retval = tty_alloc_file(filp);
if (retval)
return retval;
- /* find a device that is not in use. */
- mutex_lock(&devpts_mutex);
- index = devpts_new_index(inode);
- if (index < 0) {
- retval = index;
- mutex_unlock(&devpts_mutex);
- goto err_file;
+ fsi = devpts_acquire(filp);
+ if (IS_ERR(fsi)) {
+ retval = PTR_ERR(fsi);
+ goto out_free_file;
}
- mutex_unlock(&devpts_mutex);
+ /* find a device that is not in use. */
+ scoped_guard(mutex, &devpts_mutex)
+ index = devpts_new_index(fsi);
- mutex_lock(&tty_mutex);
- tty = tty_init_dev(ptm_driver, index);
+ retval = index;
+ if (index < 0)
+ goto out_put_fsi;
- if (IS_ERR(tty)) {
- retval = PTR_ERR(tty);
- goto out;
- }
- /* The tty returned here is locked so we can safely
- drop the mutex */
- mutex_unlock(&tty_mutex);
+ /* The tty returned here is locked so we can safely drop the mutex */
+ scoped_guard(mutex, &tty_mutex)
+ tty = tty_init_dev(ptm_driver, index);
+
+ retval = PTR_ERR(tty);
+ if (IS_ERR(tty))
+ goto out;
+ /*
+ * From here on out, the tty is "live", and the index and
+ * fsi will be killed/put by the tty_release()
+ */
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
- tty->driver_data = inode;
+ tty->driver_data = fsi;
tty_add_file(tty, filp);
- slave_inode = devpts_pty_new(inode,
- MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index,
- tty->link);
- if (IS_ERR(slave_inode)) {
- retval = PTR_ERR(slave_inode);
+ dentry = devpts_pty_new(fsi, index, tty->link);
+ if (IS_ERR(dentry)) {
+ retval = PTR_ERR(dentry);
goto err_release;
}
- tty->link->driver_data = slave_inode;
+ tty->link->driver_data = dentry;
retval = ptm_driver->ops->open(tty, filp);
if (retval)
goto err_release;
+ tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
+
tty_unlock(tty);
return 0;
err_release:
tty_unlock(tty);
+ // This will also put-ref the fsi
tty_release(inode, filp);
return retval;
out:
- mutex_unlock(&tty_mutex);
- devpts_kill_index(inode, index);
-err_file:
+ devpts_kill_index(fsi, index);
+out_put_fsi:
+ devpts_release(fsi);
+out_free_file:
tty_free_file(filp);
return retval;
}
-static struct file_operations ptmx_fops;
+static struct file_operations ptmx_fops __ro_after_init;
static void __init unix98_pty_init(void)
{
@@ -803,7 +903,7 @@ static void __init unix98_pty_init(void)
if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
panic("Couldn't register /dev/ptmx driver");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
+ device_create(&tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
}
#else
@@ -816,4 +916,4 @@ static int __init pty_init(void)
unix98_pty_init();
return 0;
}
-module_init(pty_init);
+device_initcall(pty_init);