summaryrefslogtreecommitdiff
path: root/drivers/tty/tty_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/tty_io.c')
-rw-r--r--drivers/tty/tty_io.c2063
1 files changed, 1247 insertions, 816 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 974b13d24401..e2d92cf70eb7 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
@@ -32,7 +33,7 @@
* -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
*
* Rewrote canonical mode and added more termios flags.
- * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
+ * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
*
* Reorganized FASYNC support so mouse code can share it.
* -- ctm@ardi.com, 9Sep95
@@ -86,6 +87,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/poll.h>
+#include <linux/ppp-ioctl.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -96,8 +98,10 @@
#include <linux/seq_file.h>
#include <linux/serial.h>
#include <linux/ratelimit.h>
-
+#include <linux/compat.h>
#include <linux/uaccess.h>
+#include <linux/termios_internal.h>
+#include <linux/fs.h>
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
@@ -105,6 +109,7 @@
#include <linux/kmod.h>
#include <linux/nsproxy.h>
+#include "tty.h"
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
@@ -127,25 +132,22 @@ struct ktermios tty_std_termios = { /* for the benefit of tty drivers */
.c_ospeed = 38400,
/* .c_line = N_TTY, */
};
-
EXPORT_SYMBOL(tty_std_termios);
/* This list gets poked at by procfs and various bits of boot up code. This
- could do with some rationalisation such as pulling the tty proc function
- into this file */
+ * could do with some rationalisation such as pulling the tty proc function
+ * into this file.
+ */
LIST_HEAD(tty_drivers); /* linked list of tty drivers */
/* Mutex to protect creating and releasing a tty */
DEFINE_MUTEX(tty_mutex);
-static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
-static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
-ssize_t redirected_tty_write(struct file *, const char __user *,
- size_t, loff_t *);
-static unsigned int tty_poll(struct file *, poll_table *);
+static ssize_t tty_read(struct kiocb *, struct iov_iter *);
+static ssize_t tty_write(struct kiocb *, struct iov_iter *);
+static __poll_t tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
-long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
@@ -157,20 +159,18 @@ static int tty_fasync(int fd, struct file *filp, int on);
static void release_tty(struct tty_struct *tty, int idx);
/**
- * free_tty_struct - free a disused tty
- * @tty: tty struct to free
+ * free_tty_struct - free a disused tty
+ * @tty: tty struct to free
*
- * Free the write buffers, tty queue and tty memory itself.
+ * Free the write buffers, tty queue and tty memory itself.
*
- * Locking: none. Must be called after tty is definitely unused
+ * Locking: none. Must be called after tty is definitely unused
*/
-
static void free_tty_struct(struct tty_struct *tty)
{
tty_ldisc_deinit(tty);
put_device(tty->dev);
- kfree(tty->write_buf);
- tty->magic = 0xDEADDEAD;
+ kvfree(tty->write_buf);
kfree(tty);
}
@@ -207,6 +207,7 @@ void tty_add_file(struct tty_struct *tty, struct file *file)
/**
* tty_free_file - free file->private_data
+ * @file: to free private_data of
*
* This shall be used only for fail path handling when tty_add_file was not
* called yet.
@@ -232,22 +233,20 @@ static void tty_del_file(struct file *file)
}
/**
- * tty_name - return tty naming
- * @tty: tty structure
+ * tty_name - return tty naming
+ * @tty: tty structure
*
- * Convert a tty structure into a name. The name reflects the kernel
- * naming policy and if udev is in use may not reflect user space
+ * Convert a tty structure into a name. The name reflects the kernel naming
+ * policy and if udev is in use may not reflect user space
*
- * Locking: none
+ * Locking: none
*/
-
const char *tty_name(const struct tty_struct *tty)
{
if (!tty) /* Hmm. NULL pointer. That's fun. */
return "NULL tty";
return tty->name;
}
-
EXPORT_SYMBOL(tty_name);
const char *tty_driver_name(const struct tty_struct *tty)
@@ -266,57 +265,51 @@ static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
imajor(inode), iminor(inode), routine);
return 1;
}
- if (tty->magic != TTY_MAGIC) {
- pr_warn("(%d:%d): %s: bad magic number\n",
- imajor(inode), iminor(inode), routine);
- return 1;
- }
#endif
return 0;
}
/* Caller must hold tty_lock */
-static int check_tty_count(struct tty_struct *tty, const char *routine)
+static void check_tty_count(struct tty_struct *tty, const char *routine)
{
#ifdef CHECK_TTY_COUNT
struct list_head *p;
- int count = 0;
+ int count = 0, kopen_count = 0;
+
+ scoped_guard(spinlock, &tty->files_lock)
+ list_for_each(p, &tty->tty_files)
+ count++;
- spin_lock(&tty->files_lock);
- list_for_each(p, &tty->tty_files) {
- count++;
- }
- spin_unlock(&tty->files_lock);
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_SLAVE &&
tty->link && tty->link->count)
count++;
- if (tty->count != count) {
- tty_warn(tty, "%s: tty->count(%d) != #fd's(%d)\n",
- routine, tty->count, count);
- return count;
+ if (tty_port_kopened(tty->port))
+ kopen_count++;
+ if (tty->count != (count + kopen_count)) {
+ tty_warn(tty, "%s: tty->count(%d) != (#fd's(%d) + #kopen's(%d))\n",
+ routine, tty->count, count, kopen_count);
}
#endif
- return 0;
}
/**
- * get_tty_driver - find device of a tty
- * @dev_t: device identifier
- * @index: returns the index of the tty
+ * get_tty_driver - find device of a tty
+ * @device: device identifier
+ * @index: returns the index of the tty
*
- * This routine returns a tty driver structure, given a device number
- * and also passes back the index number.
+ * This routine returns a tty driver structure, given a device number and also
+ * passes back the index number.
*
- * Locking: caller must hold tty_mutex
+ * Locking: caller must hold tty_mutex
*/
-
static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;
list_for_each_entry(p, &tty_drivers, tty_drivers) {
dev_t base = MKDEV(p->major, p->minor_start);
+
if (device < base || device >= base + p->num)
continue;
*index = device - base;
@@ -326,17 +319,17 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index)
}
/**
- * tty_dev_name_to_number - return dev_t for device name
- * @name: user space name of device under /dev
- * @number: pointer to dev_t that this function will populate
+ * tty_dev_name_to_number - return dev_t for device name
+ * @name: user space name of device under /dev
+ * @number: pointer to dev_t that this function will populate
*
- * This function converts device names like ttyS0 or ttyUSB1 into dev_t
- * like (4, 64) or (188, 1). If no corresponding driver is registered then
- * the function returns -ENODEV.
+ * This function converts device names like ttyS0 or ttyUSB1 into dev_t like
+ * (4, 64) or (188, 1). If no corresponding driver is registered then the
+ * function returns -%ENODEV.
*
- * Locking: this acquires tty_mutex to protect the tty_drivers list from
- * being modified while we are traversing it, and makes sure to
- * release it before exiting.
+ * Locking: this acquires tty_mutex to protect the tty_drivers list from
+ * being modified while we are traversing it, and makes sure to
+ * release it before exiting.
*/
int tty_dev_name_to_number(const char *name, dev_t *number)
{
@@ -356,39 +349,35 @@ int tty_dev_name_to_number(const char *name, dev_t *number)
return ret;
prefix_length = str - name;
- mutex_lock(&tty_mutex);
+
+ guard(mutex)(&tty_mutex);
list_for_each_entry(p, &tty_drivers, tty_drivers)
if (prefix_length == strlen(p->name) && strncmp(name,
p->name, prefix_length) == 0) {
if (index < p->num) {
*number = MKDEV(p->major, p->minor_start + index);
- goto out;
+ return 0;
}
}
- /* if here then driver wasn't found */
- ret = -ENODEV;
-out:
- mutex_unlock(&tty_mutex);
- return ret;
+ return -ENODEV;
}
EXPORT_SYMBOL_GPL(tty_dev_name_to_number);
#ifdef CONFIG_CONSOLE_POLL
/**
- * tty_find_polling_driver - find device of a polled tty
- * @name: name string to match
- * @line: pointer to resulting tty line nr
+ * tty_find_polling_driver - find device of a polled tty
+ * @name: name string to match
+ * @line: pointer to resulting tty line nr
*
- * This routine returns a tty driver structure, given a name
- * and the condition that the tty driver is capable of polled
- * operation.
+ * This routine returns a tty driver structure, given a name and the condition
+ * that the tty driver is capable of polled operation.
*/
struct tty_driver *tty_find_polling_driver(char *name, int *line)
{
- struct tty_driver *p, *res = NULL;
+ struct tty_driver *p;
int tty_line = 0;
int len;
char *str, *stp;
@@ -402,10 +391,11 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
len = str - name;
tty_line = simple_strtoul(str, &str, 10);
- mutex_lock(&tty_mutex);
+ guard(mutex)(&tty_mutex);
+
/* Search through the tty devices to look for a match */
list_for_each_entry(p, &tty_drivers, tty_drivers) {
- if (strncmp(name, p->name, len) != 0)
+ if (!len || strncmp(name, p->name, len) != 0)
continue;
stp = str;
if (*stp == ',')
@@ -415,34 +405,30 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
if (tty_line >= 0 && tty_line < p->num && p->ops &&
p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
- res = tty_driver_kref_get(p);
*line = tty_line;
- break;
+ return tty_driver_kref_get(p);
}
}
- mutex_unlock(&tty_mutex);
- return res;
+ return NULL;
}
EXPORT_SYMBOL_GPL(tty_find_polling_driver);
#endif
-static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t hung_up_tty_read(struct kiocb *iocb, struct iov_iter *to)
{
return 0;
}
-static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from)
{
return -EIO;
}
/* No kernel lock held - none needed ;) */
-static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
+static __poll_t hung_up_tty_poll(struct file *filp, poll_table *wait)
{
- return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
+ return EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | EPOLLWRNORM;
}
static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
@@ -462,22 +448,33 @@ static int hung_up_tty_fasync(int fd, struct file *file, int on)
return -ENOTTY;
}
+static void tty_show_fdinfo(struct seq_file *m, struct file *file)
+{
+ struct tty_struct *tty = file_tty(file);
+
+ if (tty && tty->ops && tty->ops->show_fdinfo)
+ tty->ops->show_fdinfo(tty, m);
+}
+
static const struct file_operations tty_fops = {
- .llseek = no_llseek,
- .read = tty_read,
- .write = tty_write,
+ .read_iter = tty_read,
+ .write_iter = tty_write,
+ .splice_read = copy_splice_read,
+ .splice_write = iter_file_splice_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
+ .show_fdinfo = tty_show_fdinfo,
};
static const struct file_operations console_fops = {
- .llseek = no_llseek,
- .read = tty_read,
- .write = redirected_tty_write,
+ .read_iter = tty_read,
+ .write_iter = redirected_tty_write,
+ .splice_read = copy_splice_read,
+ .splice_write = iter_file_splice_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
@@ -487,9 +484,8 @@ static const struct file_operations console_fops = {
};
static const struct file_operations hung_up_tty_fops = {
- .llseek = no_llseek,
- .read = hung_up_tty_read,
- .write = hung_up_tty_write,
+ .read_iter = hung_up_tty_read,
+ .write_iter = hung_up_tty_write,
.poll = hung_up_tty_poll,
.unlocked_ioctl = hung_up_tty_ioctl,
.compat_ioctl = hung_up_tty_compat_ioctl,
@@ -501,14 +497,13 @@ static DEFINE_SPINLOCK(redirect_lock);
static struct file *redirect;
/**
- * tty_wakeup - request more data
- * @tty: terminal
+ * tty_wakeup - request more data
+ * @tty: terminal
*
- * Internal and external helper for wakeups of tty. This function
- * informs the line discipline if present that the driver is ready
- * to receive more output data.
+ * Internal and external helper for wakeups of tty. This function informs the
+ * line discipline if present that the driver is ready to receive more output
+ * data.
*/
-
void tty_wakeup(struct tty_struct *tty)
{
struct tty_ldisc *ld;
@@ -521,37 +516,59 @@ void tty_wakeup(struct tty_struct *tty)
tty_ldisc_deref(ld);
}
}
- wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+ wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT);
}
-
EXPORT_SYMBOL_GPL(tty_wakeup);
/**
- * __tty_hangup - actual handler for hangup events
- * @work: tty device
+ * tty_release_redirect - Release a redirect on a pty if present
+ * @tty: tty device
+ *
+ * This is available to the pty code so if the master closes, if the slave is a
+ * redirect it can release the redirect.
+ */
+static struct file *tty_release_redirect(struct tty_struct *tty)
+{
+ guard(spinlock)(&redirect_lock);
+
+ if (redirect && file_tty(redirect) == tty) {
+ struct file *f = redirect;
+ redirect = NULL;
+ return f;
+ }
+
+ return NULL;
+}
+
+/**
+ * __tty_hangup - actual handler for hangup events
+ * @tty: tty device
+ * @exit_session: if non-zero, signal all foreground group processes
+ *
+ * This can be called by a "kworker" kernel thread. That is process synchronous
+ * but doesn't hold any locks, so we need to make sure we have the appropriate
+ * locks for what we're doing.
+ *
+ * The hangup event clears any pending redirections onto the hung up device. It
+ * ensures future writes will error and it does the needed line discipline
+ * hangup and signal delivery. The tty object itself remains intact.
+ *
+ * Locking:
+ * * BTM
*
- * This can be called by a "kworker" kernel thread. That is process
- * synchronous but doesn't hold any locks, so we need to make sure we
- * have the appropriate locks for what we're doing.
+ * * redirect lock for undoing redirection
+ * * file list lock for manipulating list of ttys
+ * * tty_ldiscs_lock from called functions
+ * * termios_rwsem resetting termios data
+ * * tasklist_lock to walk task list for hangup event
*
- * The hangup event clears any pending redirections onto the hung up
- * device. It ensures future writes will error and it does the needed
- * line discipline hangup and signal delivery. The tty object itself
- * remains intact.
+ * * ->siglock to protect ->signal/->sighand
*
- * Locking:
- * BTM
- * redirect lock for undoing redirection
- * file list lock for manipulating list of ttys
- * tty_ldiscs_lock from called functions
- * termios_rwsem resetting termios data
- * tasklist_lock to walk task list for hangup event
- * ->siglock to protect ->signal/->sighand
*/
static void __tty_hangup(struct tty_struct *tty, int exit_session)
{
struct file *cons_filp = NULL;
- struct file *filp, *f = NULL;
+ struct file *filp, *f;
struct tty_file_private *priv;
int closecount = 0, n;
int refs;
@@ -559,13 +576,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
if (!tty)
return;
-
- spin_lock(&redirect_lock);
- if (redirect && file_tty(redirect) == tty) {
- f = redirect;
- redirect = NULL;
- }
- spin_unlock(&redirect_lock);
+ f = tty_release_redirect(tty);
tty_lock(tty);
@@ -574,18 +585,27 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
return;
}
+ /*
+ * Some console devices aren't actually hung up for technical and
+ * historical reasons, which can lead to indefinite interruptible
+ * sleep in n_tty_read(). The following explicitly tells
+ * n_tty_read() to abort readers.
+ */
+ set_bit(TTY_HUPPING, &tty->flags);
+
/* inuse_filps is protected by the single tty lock,
- this really needs to change if we want to flush the
- workqueue with the lock held */
+ * this really needs to change if we want to flush the
+ * workqueue with the lock held.
+ */
check_tty_count(tty, "tty_hangup");
spin_lock(&tty->files_lock);
/* This breaks for file handles being sent over AF_UNIX sockets ? */
list_for_each_entry(priv, &tty->tty_files, list) {
filp = priv->file;
- if (filp->f_op->write == redirected_tty_write)
+ if (filp->f_op->write_iter == redirected_tty_write)
cons_filp = filp;
- if (filp->f_op->write != tty_write)
+ if (filp->f_op->write_iter != tty_write)
continue;
closecount++;
__tty_fasync(-1, filp, 0); /* can't block */
@@ -600,15 +620,15 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
tty_ldisc_hangup(tty, cons_filp != NULL);
- spin_lock_irq(&tty->ctrl_lock);
+ spin_lock_irq(&tty->ctrl.lock);
clear_bit(TTY_THROTTLED, &tty->flags);
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- put_pid(tty->session);
- put_pid(tty->pgrp);
- tty->session = NULL;
- tty->pgrp = NULL;
- tty->ctrl_status = 0;
- spin_unlock_irq(&tty->ctrl_lock);
+ put_pid(tty->ctrl.session);
+ put_pid(tty->ctrl.pgrp);
+ tty->ctrl.session = NULL;
+ tty->ctrl.pgrp = NULL;
+ tty->ctrl.pktstatus = 0;
+ spin_unlock_irq(&tty->ctrl.lock);
/*
* If one of the devices matches a console pointer, we
@@ -628,6 +648,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
* from the ldisc side, which is now guaranteed.
*/
set_bit(TTY_HUPPED, &tty->flags);
+ clear_bit(TTY_HUPPING, &tty->flags);
tty_unlock(tty);
if (f)
@@ -643,45 +664,40 @@ static void do_tty_hangup(struct work_struct *work)
}
/**
- * tty_hangup - trigger a hangup event
- * @tty: tty to hangup
+ * tty_hangup - trigger a hangup event
+ * @tty: tty to hangup
*
- * A carrier loss (virtual or otherwise) has occurred on this like
- * schedule a hangup sequence to run after this event.
+ * A carrier loss (virtual or otherwise) has occurred on @tty. Schedule a
+ * hangup sequence to run after this event.
*/
-
void tty_hangup(struct tty_struct *tty)
{
tty_debug_hangup(tty, "hangup\n");
schedule_work(&tty->hangup_work);
}
-
EXPORT_SYMBOL(tty_hangup);
/**
- * tty_vhangup - process vhangup
- * @tty: tty to hangup
+ * tty_vhangup - process vhangup
+ * @tty: tty to hangup
*
- * The user has asked via system call for the terminal to be hung up.
- * We do this synchronously so that when the syscall returns the process
- * is complete. That guarantee is necessary for security reasons.
+ * The user has asked via system call for the terminal to be hung up. We do
+ * this synchronously so that when the syscall returns the process is complete.
+ * That guarantee is necessary for security reasons.
*/
-
void tty_vhangup(struct tty_struct *tty)
{
tty_debug_hangup(tty, "vhangup\n");
__tty_hangup(tty, 0);
}
-
EXPORT_SYMBOL(tty_vhangup);
/**
- * tty_vhangup_self - process vhangup for own ctty
+ * tty_vhangup_self - process vhangup for own ctty
*
- * Perform a vhangup on the current controlling tty
+ * Perform a vhangup on the current controlling tty
*/
-
void tty_vhangup_self(void)
{
struct tty_struct *tty;
@@ -694,16 +710,15 @@ void tty_vhangup_self(void)
}
/**
- * tty_vhangup_session - hangup session leader exit
- * @tty: tty to hangup
+ * tty_vhangup_session - hangup session leader exit
+ * @tty: tty to hangup
*
- * The session leader is exiting and hanging up its controlling terminal.
- * Every process in the foreground process group is signalled SIGHUP.
+ * The session leader is exiting and hanging up its controlling terminal.
+ * Every process in the foreground process group is signalled %SIGHUP.
*
- * We do this synchronously so that when the syscall returns the process
- * is complete. That guarantee is necessary for security reasons.
+ * We do this synchronously so that when the syscall returns the process is
+ * complete. That guarantee is necessary for security reasons.
*/
-
void tty_vhangup_session(struct tty_struct *tty)
{
tty_debug_hangup(tty, "session hangup\n");
@@ -711,124 +726,185 @@ void tty_vhangup_session(struct tty_struct *tty)
}
/**
- * tty_hung_up_p - was tty hung up
- * @filp: file pointer of tty
+ * tty_hung_up_p - was tty hung up
+ * @filp: file pointer of tty
*
- * Return true if the tty has been subject to a vhangup or a carrier
- * loss
+ * Return: true if the tty has been subject to a vhangup or a carrier loss
*/
-
int tty_hung_up_p(struct file *filp)
{
return (filp && filp->f_op == &hung_up_tty_fops);
}
-
EXPORT_SYMBOL(tty_hung_up_p);
-/**
- * stop_tty - propagate flow control
- * @tty: tty to stop
- *
- * Perform flow control to the driver. May be called
- * on an already stopped device and will not re-call the driver
- * method.
- *
- * This functionality is used by both the line disciplines for
- * halting incoming flow and by the driver. It may therefore be
- * called from any context, may be under the tty atomic_write_lock
- * but not always.
- *
- * Locking:
- * flow_lock
- */
-
void __stop_tty(struct tty_struct *tty)
{
- if (tty->stopped)
+ if (tty->flow.stopped)
return;
- tty->stopped = 1;
+ tty->flow.stopped = true;
if (tty->ops->stop)
tty->ops->stop(tty);
}
+/**
+ * stop_tty - propagate flow control
+ * @tty: tty to stop
+ *
+ * Perform flow control to the driver. May be called on an already stopped
+ * device and will not re-call the &tty_driver->stop() method.
+ *
+ * This functionality is used by both the line disciplines for halting incoming
+ * flow and by the driver. It may therefore be called from any context, may be
+ * under the tty %atomic_write_lock but not always.
+ *
+ * Locking:
+ * flow.lock
+ */
void stop_tty(struct tty_struct *tty)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tty->flow_lock, flags);
+ guard(spinlock_irqsave)(&tty->flow.lock);
__stop_tty(tty);
- spin_unlock_irqrestore(&tty->flow_lock, flags);
}
EXPORT_SYMBOL(stop_tty);
-/**
- * start_tty - propagate flow control
- * @tty: tty to start
- *
- * Start a tty that has been stopped if at all possible. If this
- * tty was previous stopped and is now being started, the driver
- * start method is invoked and the line discipline woken.
- *
- * Locking:
- * flow_lock
- */
-
void __start_tty(struct tty_struct *tty)
{
- if (!tty->stopped || tty->flow_stopped)
+ if (!tty->flow.stopped || tty->flow.tco_stopped)
return;
- tty->stopped = 0;
+ tty->flow.stopped = false;
if (tty->ops->start)
tty->ops->start(tty);
tty_wakeup(tty);
}
+/**
+ * start_tty - propagate flow control
+ * @tty: tty to start
+ *
+ * Start a tty that has been stopped if at all possible. If @tty was previously
+ * stopped and is now being started, the &tty_driver->start() method is invoked
+ * and the line discipline woken.
+ *
+ * Locking:
+ * flow.lock
+ */
void start_tty(struct tty_struct *tty)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tty->flow_lock, flags);
+ guard(spinlock_irqsave)(&tty->flow.lock);
__start_tty(tty);
- spin_unlock_irqrestore(&tty->flow_lock, flags);
}
EXPORT_SYMBOL(start_tty);
-static void tty_update_time(struct timespec *time)
+static void tty_update_time(struct tty_struct *tty, bool mtime)
{
- unsigned long sec = get_seconds();
+ time64_t sec = ktime_get_real_seconds();
+ struct tty_file_private *priv;
- /*
- * We only care if the two values differ in anything other than the
- * lower three bits (i.e every 8 seconds). If so, then we can update
- * the time of the tty device, otherwise it could be construded as a
- * security leak to let userspace know the exact timing of the tty.
- */
- if ((sec ^ time->tv_sec) & ~7)
- time->tv_sec = sec;
+ guard(spinlock)(&tty->files_lock);
+
+ list_for_each_entry(priv, &tty->tty_files, list) {
+ struct inode *inode = file_inode(priv->file);
+ struct timespec64 time = mtime ? inode_get_mtime(inode) : inode_get_atime(inode);
+
+ /*
+ * We only care if the two values differ in anything other than the
+ * lower three bits (i.e every 8 seconds). If so, then we can update
+ * the time of the tty device, otherwise it could be construded as a
+ * security leak to let userspace know the exact timing of the tty.
+ */
+ if ((sec ^ time.tv_sec) & ~7) {
+ if (mtime)
+ inode_set_mtime(inode, sec, 0);
+ else
+ inode_set_atime(inode, sec, 0);
+ }
+ }
}
-/**
- * tty_read - read method for tty device files
- * @file: pointer to tty file
- * @buf: user buffer
- * @count: size of user buffer
- * @ppos: unused
+/*
+ * Iterate on the ldisc ->read() function until we've gotten all
+ * the data the ldisc has for us.
*
- * Perform the read system call function on this terminal device. Checks
- * for hung up devices before calling the line discipline method.
+ * The "cookie" is something that the ldisc read function can fill
+ * in to let us know that there is more data to be had.
*
- * Locking:
- * Locks the line discipline internally while needed. Multiple
- * read calls may be outstanding in parallel.
+ * We promise to continue to call the ldisc until it stops returning
+ * data or clears the cookie. The cookie may be something that the
+ * ldisc maintains state for and needs to free.
*/
+static ssize_t iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty,
+ struct file *file, struct iov_iter *to)
+{
+ void *cookie = NULL;
+ unsigned long offset = 0;
+ ssize_t retval = 0;
+ size_t copied, count = iov_iter_count(to);
+ u8 kernel_buf[64];
+
+ do {
+ ssize_t size = min(count, sizeof(kernel_buf));
-static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
- loff_t *ppos)
+ size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset);
+ if (!size)
+ break;
+
+ if (size < 0) {
+ /* Did we have an earlier error (ie -EFAULT)? */
+ if (retval)
+ break;
+ retval = size;
+
+ /*
+ * -EOVERFLOW means we didn't have enough space
+ * for a whole packet, and we shouldn't return
+ * a partial result.
+ */
+ if (retval == -EOVERFLOW)
+ offset = 0;
+ break;
+ }
+
+ copied = copy_to_iter(kernel_buf, size, to);
+ offset += copied;
+ count -= copied;
+
+ /*
+ * If the user copy failed, we still need to do another ->read()
+ * call if we had a cookie to let the ldisc clear up.
+ *
+ * But make sure size is zeroed.
+ */
+ if (unlikely(copied != size)) {
+ count = 0;
+ retval = -EFAULT;
+ }
+ } while (cookie);
+
+ /* We always clear tty buffer in case they contained passwords */
+ memzero_explicit(kernel_buf, sizeof(kernel_buf));
+ return offset ? offset : retval;
+}
+
+
+/**
+ * tty_read - read method for tty device files
+ * @iocb: kernel I/O control block
+ * @to: destination for the data read
+ *
+ * Perform the read system call function on this terminal device. Checks
+ * for hung up devices before calling the line discipline method.
+ *
+ * Locking:
+ * Locks the line discipline internally while needed. Multiple read calls
+ * may be outstanding in parallel.
+ */
+static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to)
{
- int i;
+ struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
+ ssize_t ret;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
@@ -836,29 +912,29 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
return -EIO;
/* We want to wait for the line discipline to sort out in this
- situation */
+ * situation.
+ */
ld = tty_ldisc_ref_wait(tty);
if (!ld)
- return hung_up_tty_read(file, buf, count, ppos);
+ return hung_up_tty_read(iocb, to);
+ ret = -EIO;
if (ld->ops->read)
- i = ld->ops->read(tty, file, buf, count);
- else
- i = -EIO;
+ ret = iterate_tty_read(ld, tty, file, to);
tty_ldisc_deref(ld);
- if (i > 0)
- tty_update_time(&inode->i_atime);
+ if (ret > 0)
+ tty_update_time(tty, false);
- return i;
+ return ret;
}
-static void tty_write_unlock(struct tty_struct *tty)
+void tty_write_unlock(struct tty_struct *tty)
{
mutex_unlock(&tty->atomic_write_lock);
- wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
+ wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT);
}
-static int tty_write_lock(struct tty_struct *tty, int ndelay)
+int tty_write_lock(struct tty_struct *tty, bool ndelay)
{
if (!mutex_trylock(&tty->atomic_write_lock)) {
if (ndelay)
@@ -873,15 +949,11 @@ static int tty_write_lock(struct tty_struct *tty, int ndelay)
* Split writes up in sane blocksizes to avoid
* denial-of-service type attacks
*/
-static inline ssize_t do_tty_write(
- ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
- struct tty_struct *tty,
- struct file *file,
- const char __user *buf,
- size_t count)
+static ssize_t iterate_tty_write(struct tty_ldisc *ld, struct tty_struct *tty,
+ struct file *file, struct iov_iter *from)
{
+ size_t chunk, count = iov_iter_count(from);
ssize_t ret, written = 0;
- unsigned int chunk;
ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
if (ret < 0)
@@ -899,9 +971,6 @@ static inline ssize_t do_tty_write(
* layer has problems with bigger chunks. It will
* claim to be able to handle more characters than
* it actually does.
- *
- * FIXME: This can probably go away now except that 64K chunks
- * are too likely to fail unless switched to vmalloc...
*/
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
@@ -911,34 +980,41 @@ static inline ssize_t do_tty_write(
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
- unsigned char *buf_chunk;
+ u8 *buf_chunk;
if (chunk < 1024)
chunk = 1024;
- buf_chunk = kmalloc(chunk, GFP_KERNEL);
+ buf_chunk = kvmalloc(chunk, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
- kfree(tty->write_buf);
+ kvfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/* Do the write .. */
for (;;) {
- size_t size = count;
- if (size > chunk)
- size = chunk;
+ size_t size = min(chunk, count);
+
ret = -EFAULT;
- if (copy_from_user(tty->write_buf, buf, size))
+ if (copy_from_iter(tty->write_buf, size, from) != size)
break;
- ret = write(tty, file, tty->write_buf, size);
+
+ ret = ld->ops->write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
+
written += ret;
- buf += ret;
+ if (ret > size)
+ break;
+
+ /* FIXME! Have Al check this! */
+ if (ret != size)
+ iov_iter_revert(from, size-ret);
+
count -= ret;
if (!count)
break;
@@ -948,7 +1024,7 @@ static inline ssize_t do_tty_write(
cond_resched();
}
if (written) {
- tty_update_time(&file_inode(file)->i_mtime);
+ tty_update_time(tty, true);
ret = written;
}
out:
@@ -956,18 +1032,20 @@ out:
return ret;
}
+#ifdef CONFIG_PRINT_QUOTA_WARNING
/**
* tty_write_message - write a message to a certain tty, not just the console.
* @tty: the destination tty_struct
* @msg: the message to write
*
- * This is used for messages that need to be redirected to a specific tty.
- * We don't put it into the syslog queue right now maybe in the future if
- * really needed.
+ * This is used for messages that need to be redirected to a specific tty. We
+ * don't put it into the syslog queue right now maybe in the future if really
+ * needed.
*
* We must still hold the BTM and test the CLOSING flag for the moment.
+ *
+ * This function is DEPRECATED, do not use in new code.
*/
-
void tty_write_message(struct tty_struct *tty, char *msg)
{
if (tty) {
@@ -978,53 +1056,53 @@ void tty_write_message(struct tty_struct *tty, char *msg)
tty_unlock(tty);
tty_write_unlock(tty);
}
- return;
}
+#endif
-
-/**
- * tty_write - write method for tty device file
- * @file: tty file pointer
- * @buf: user data to write
- * @count: bytes to write
- * @ppos: unused
- *
- * Write data to a tty device via the line discipline.
- *
- * Locking:
- * Locks the line discipline as required
- * Writes to the tty driver are serialized by the atomic_write_lock
- * and are then processed in chunks to the device. The line discipline
- * write method will not be invoked in parallel for each device.
- */
-
-static ssize_t tty_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t file_tty_write(struct file *file, struct kiocb *iocb, struct iov_iter *from)
{
struct tty_struct *tty = file_tty(file);
- struct tty_ldisc *ld;
+ struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, file_inode(file), "tty_write"))
return -EIO;
if (!tty || !tty->ops->write || tty_io_error(tty))
- return -EIO;
+ return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
tty_err(tty, "missing write_room method\n");
ld = tty_ldisc_ref_wait(tty);
if (!ld)
- return hung_up_tty_write(file, buf, count, ppos);
+ return hung_up_tty_write(iocb, from);
if (!ld->ops->write)
ret = -EIO;
else
- ret = do_tty_write(ld->ops->write, tty, file, buf, count);
+ ret = iterate_tty_write(ld, tty, file, from);
tty_ldisc_deref(ld);
return ret;
}
-ssize_t redirected_tty_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+/**
+ * tty_write - write method for tty device file
+ * @iocb: kernel I/O control block
+ * @from: iov_iter with data to write
+ *
+ * Write data to a tty device via the line discipline.
+ *
+ * Locking:
+ * Locks the line discipline as required
+ * Writes to the tty driver are serialized by the atomic_write_lock
+ * and are then processed in chunks to the device. The line
+ * discipline write method will not be invoked in parallel for
+ * each device.
+ */
+static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
+{
+ return file_tty_write(iocb->ki_filp, iocb, from);
+}
+
+ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *p = NULL;
@@ -1033,26 +1111,32 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
p = get_file(redirect);
spin_unlock(&redirect_lock);
+ /*
+ * We know the redirected tty is just another tty, we can
+ * call file_tty_write() directly with that file pointer.
+ */
if (p) {
ssize_t res;
- res = vfs_write(p, buf, count, &p->f_pos);
+
+ res = file_tty_write(p, iocb, iter);
fput(p);
return res;
}
- return tty_write(file, buf, count, ppos);
+ return tty_write(iocb, iter);
}
/**
- * tty_send_xchar - send priority character
+ * tty_send_xchar - send priority character
+ * @tty: the tty to send to
+ * @ch: xchar to send
*
- * Send a high priority character to the tty even if stopped
+ * Send a high priority character to the tty even if stopped.
*
- * Locking: none for xchar method, write ordering for write method.
+ * Locking: none for xchar method, write ordering for write method.
*/
-
-int tty_send_xchar(struct tty_struct *tty, char ch)
+int tty_send_xchar(struct tty_struct *tty, u8 ch)
{
- int was_stopped = tty->stopped;
+ bool was_stopped = tty->flow.stopped;
if (tty->ops->send_xchar) {
down_read(&tty->termios_rwsem);
@@ -1061,7 +1145,7 @@ int tty_send_xchar(struct tty_struct *tty, char ch)
return 0;
}
- if (tty_write_lock(tty, 0) < 0)
+ if (tty_write_lock(tty, false) < 0)
return -ERESTARTSYS;
down_read(&tty->termios_rwsem);
@@ -1075,21 +1159,20 @@ int tty_send_xchar(struct tty_struct *tty, char ch)
return 0;
}
-static char ptychar[] = "pqrstuvwxyzabcde";
-
/**
- * pty_line_name - generate name for a pty
- * @driver: the tty driver in use
- * @index: the minor number
- * @p: output buffer of at least 6 bytes
+ * pty_line_name - generate name for a pty
+ * @driver: the tty driver in use
+ * @index: the minor number
+ * @p: output buffer of at least 6 bytes
*
- * Generate a name from a driver reference and write it to the output
- * buffer.
+ * Generate a name from a @driver reference and write it to the output buffer
+ * @p.
*
- * Locking: None
+ * Locking: None
*/
static void pty_line_name(struct tty_driver *driver, int index, char *p)
{
+ static const char ptychar[] = "pqrstuvwxyzabcde";
int i = index + driver->name_base;
/* ->name is initialized to "ttyp", but "tty" is expected */
sprintf(p, "%s%c%x",
@@ -1098,15 +1181,15 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p)
}
/**
- * tty_line_name - generate name for a tty
- * @driver: the tty driver in use
- * @index: the minor number
- * @p: output buffer of at least 7 bytes
+ * tty_line_name - generate name for a tty
+ * @driver: the tty driver in use
+ * @index: the minor number
+ * @p: output buffer of at least 7 bytes
*
- * Generate a name from a driver reference and write it to the output
- * buffer.
+ * Generate a name from a @driver reference and write it to the output buffer
+ * @p.
*
- * Locking: None
+ * Locking: None
*/
static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
{
@@ -1118,41 +1201,43 @@ static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
}
/**
- * tty_driver_lookup_tty() - find an existing tty, if any
- * @driver: the driver for the tty
- * @idx: the minor number
+ * tty_driver_lookup_tty() - find an existing tty, if any
+ * @driver: the driver for the tty
+ * @file: file object
+ * @idx: the minor number
*
- * Return the tty, if found. If not found, return NULL or ERR_PTR() if the
- * driver lookup() method returns an error.
+ * Return: the tty, if found. If not found, return %NULL or ERR_PTR() if the
+ * driver lookup() method returns an error.
*
- * Locking: tty_mutex must be held. If the tty is found, bump the tty kref.
+ * Locking: tty_mutex must be held. If the tty is found, bump the tty kref.
*/
static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
struct file *file, int idx)
{
struct tty_struct *tty;
- if (driver->ops->lookup)
+ if (driver->ops->lookup) {
if (!file)
tty = ERR_PTR(-EIO);
else
tty = driver->ops->lookup(driver, file, idx);
- else
+ } else {
+ if (idx >= driver->num)
+ return ERR_PTR(-EINVAL);
tty = driver->ttys[idx];
-
+ }
if (!IS_ERR(tty))
tty_kref_get(tty);
return tty;
}
/**
- * tty_init_termios - helper for termios setup
- * @tty: the tty to set up
+ * tty_init_termios - helper for termios setup
+ * @tty: the tty to set up
*
- * Initialise the termios structures for this tty. Thus runs under
- * the tty_mutex currently so we can be relaxed about ordering.
+ * Initialise the termios structure for this tty. This runs under the
+ * %tty_mutex currently so we can be relaxed about ordering.
*/
-
void tty_init_termios(struct tty_struct *tty)
{
struct ktermios *tp;
@@ -1175,6 +1260,14 @@ void tty_init_termios(struct tty_struct *tty)
}
EXPORT_SYMBOL_GPL(tty_init_termios);
+/**
+ * tty_standard_install - usual tty->ops->install
+ * @driver: the driver for the tty
+ * @tty: the tty
+ *
+ * If the @driver overrides @tty->ops->install, it still can call this function
+ * to perform the standard install operations.
+ */
int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
{
tty_init_termios(tty);
@@ -1186,16 +1279,15 @@ int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
EXPORT_SYMBOL_GPL(tty_standard_install);
/**
- * tty_driver_install_tty() - install a tty entry in the driver
- * @driver: the driver for the tty
- * @tty: the tty
+ * tty_driver_install_tty() - install a tty entry in the driver
+ * @driver: the driver for the tty
+ * @tty: the tty
*
- * Install a tty object into the driver tables. The tty->index field
- * will be set by the time this is called. This method is responsible
- * for ensuring any need additional structures are allocated and
- * configured.
+ * Install a tty object into the driver tables. The @tty->index field will be
+ * set by the time this is called. This method is responsible for ensuring any
+ * need additional structures are allocated and configured.
*
- * Locking: tty_mutex for now
+ * Locking: tty_mutex for now
*/
static int tty_driver_install_tty(struct tty_driver *driver,
struct tty_struct *tty)
@@ -1205,14 +1297,14 @@ static int tty_driver_install_tty(struct tty_driver *driver,
}
/**
- * tty_driver_remove_tty() - remove a tty from the driver tables
- * @driver: the driver for the tty
- * @idx: the minor number
+ * tty_driver_remove_tty() - remove a tty from the driver tables
+ * @driver: the driver for the tty
+ * @tty: tty to remove
*
- * Remvoe a tty object from the driver tables. The tty->index field
- * will be set by the time this is called.
+ * Remove a tty object from the driver tables. The tty->index field will be set
+ * by the time this is called.
*
- * Locking: tty_mutex for now
+ * Locking: tty_mutex for now
*/
static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
{
@@ -1222,18 +1314,20 @@ static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *
driver->ttys[tty->index] = NULL;
}
-/*
- * tty_reopen() - fast re-open of an open tty
- * @tty - the tty to open
+/**
+ * tty_reopen() - fast re-open of an open tty
+ * @tty: the tty to open
*
- * Return 0 on success, -errno on error.
- * Re-opens on master ptys are not allowed and return -EIO.
+ * Re-opens on master ptys are not allowed and return -%EIO.
*
- * Locking: Caller must hold tty_lock
+ * Locking: Caller must hold tty_lock
+ * Return: 0 on success, -errno on error.
*/
static int tty_reopen(struct tty_struct *tty)
{
struct tty_driver *driver = tty->driver;
+ struct tty_ldisc *ld;
+ int retval = 0;
if (driver->type == TTY_DRIVER_TYPE_PTY &&
driver->subtype == PTY_TYPE_MASTER)
@@ -1245,38 +1339,48 @@ static int tty_reopen(struct tty_struct *tty)
if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
return -EBUSY;
- tty->count++;
+ ld = tty_ldisc_ref_wait(tty);
+ if (ld) {
+ tty_ldisc_deref(ld);
+ } else {
+ retval = tty_ldisc_lock(tty, 5 * HZ);
+ if (retval)
+ return retval;
+
+ if (!tty->ldisc)
+ retval = tty_ldisc_reinit(tty, tty->termios.c_line);
+ tty_ldisc_unlock(tty);
+ }
- if (!tty->ldisc)
- return tty_ldisc_reinit(tty, tty->termios.c_line);
+ if (retval == 0)
+ tty->count++;
- return 0;
+ return retval;
}
/**
- * tty_init_dev - initialise a tty device
- * @driver: tty driver we are opening a device on
- * @idx: device index
- * @ret_tty: returned tty structure
+ * tty_init_dev - initialise a tty device
+ * @driver: tty driver we are opening a device on
+ * @idx: device index
+ *
+ * Prepare a tty device. This may not be a "new" clean device but could also be
+ * an active device. The pty drivers require special handling because of this.
*
- * Prepare a tty device. This may not be a "new" clean device but
- * could also be an active device. The pty drivers require special
- * handling because of this.
+ * Locking:
+ * The function is called under the tty_mutex, which protects us from the
+ * tty struct or driver itself going away.
*
- * Locking:
- * The function is called under the tty_mutex, which
- * protects us from the tty struct or driver itself going away.
+ * On exit the tty device has the line discipline attached and a reference
+ * count of 1. If a pair was created for pty/tty use and the other was a pty
+ * master then it too has a reference count of 1.
*
- * On exit the tty device has the line discipline attached and
- * a reference count of 1. If a pair was created for pty/tty use
- * and the other was a pty master then it too has a reference count of 1.
+ * WSH 06/09/97: Rewritten to remove races and properly clean up after a failed
+ * open. The new code protects the open with a mutex, so it's really quite
+ * straightforward. The mutex locking can probably be relaxed for the (most
+ * common) case of reopening a tty.
*
- * WSH 06/09/97: Rewritten to remove races and properly clean up after a
- * failed open. The new code protects the open with a mutex, so it's
- * really quite straightforward. The mutex locking can probably be
- * relaxed for the (most common) case of reopening a tty.
+ * Return: new tty structure
*/
-
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
@@ -1307,10 +1411,16 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
if (!tty->port)
tty->port = driver->ports[idx];
- WARN_RATELIMIT(!tty->port,
- "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
- __func__, tty->driver->name);
+ if (WARN_RATELIMIT(!tty->port,
+ "%s: %s driver does not set tty->port. This would crash the kernel. Fix the driver!\n",
+ __func__, tty->driver->name)) {
+ retval = -EINVAL;
+ goto err_release_lock;
+ }
+ retval = tty_ldisc_lock(tty, 5 * HZ);
+ if (retval)
+ goto err_release_lock;
tty->port->itty = tty;
/*
@@ -1321,6 +1431,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto err_release_tty;
+ tty_ldisc_unlock(tty);
/* Return the tty locked so that it cannot vanish under the caller */
return tty;
@@ -1333,14 +1444,22 @@ err_module_put:
/* call the tty release_tty routine to clean out this slot */
err_release_tty:
- tty_unlock(tty);
+ tty_ldisc_unlock(tty);
tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
retval, idx);
+err_release_lock:
+ tty_unlock(tty);
release_tty(tty, idx);
return ERR_PTR(retval);
}
-static void tty_free_termios(struct tty_struct *tty)
+/**
+ * tty_save_termios() - save tty termios data in driver table
+ * @tty: tty whose termios data to save
+ *
+ * Locking: Caller guarantees serialisation with tty_init_termios().
+ */
+void tty_save_termios(struct tty_struct *tty)
{
struct ktermios *tp;
int idx = tty->index;
@@ -1352,19 +1471,20 @@ static void tty_free_termios(struct tty_struct *tty)
/* Stash the termios data */
tp = tty->driver->termios[idx];
if (tp == NULL) {
- tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
+ tp = kmalloc(sizeof(*tp), GFP_KERNEL);
if (tp == NULL)
return;
tty->driver->termios[idx] = tp;
}
*tp = tty->termios;
}
+EXPORT_SYMBOL_GPL(tty_save_termios);
/**
- * tty_flush_works - flush all works of a tty/pty pair
- * @tty: tty device to flush works for (or either end of a pty pair)
+ * tty_flush_works - flush all works of a tty/pty pair
+ * @tty: tty device to flush works for (or either end of a pty pair)
*
- * Sync flush all works belonging to @tty (and the 'other' tty).
+ * Sync flush all works belonging to @tty (and the 'other' tty).
*/
static void tty_flush_works(struct tty_struct *tty)
{
@@ -1377,19 +1497,19 @@ static void tty_flush_works(struct tty_struct *tty)
}
/**
- * release_one_tty - release tty structure memory
- * @kref: kref of tty we are obliterating
+ * release_one_tty - release tty structure memory
+ * @work: work of tty we are obliterating
*
- * Releases memory associated with a tty structure, and clears out the
- * driver table slots. This function is called when a device is no longer
- * in use. It also gets called when setup of a device fails.
+ * Releases memory associated with a tty structure, and clears out the
+ * driver table slots. This function is called when a device is no longer
+ * in use. It also gets called when setup of a device fails.
*
- * Locking:
- * takes the file list lock internally when working on the list
- * of ttys that the driver keeps.
+ * Locking:
+ * takes the file list lock internally when working on the list of ttys
+ * that the driver keeps.
*
- * This method gets called from a work queue so that the driver private
- * cleanup ops can sleep (needed for USB at least)
+ * This method gets called from a work queue so that the driver private
+ * cleanup ops can sleep (needed for USB at least)
*/
static void release_one_tty(struct work_struct *work)
{
@@ -1401,7 +1521,6 @@ static void release_one_tty(struct work_struct *work)
if (tty->ops->cleanup)
tty->ops->cleanup(tty);
- tty->magic = 0;
tty_driver_kref_put(driver);
module_put(owner);
@@ -1409,8 +1528,8 @@ static void release_one_tty(struct work_struct *work)
list_del_init(&tty->tty_files);
spin_unlock(&tty->files_lock);
- put_pid(tty->pgrp);
- put_pid(tty->session);
+ put_pid(tty->ctrl.pgrp);
+ put_pid(tty->ctrl.session);
free_tty_struct(tty);
}
@@ -1419,19 +1538,19 @@ static void queue_release_one_tty(struct kref *kref)
struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
/* The hangup queue is now free so we can reuse it rather than
- waste a chunk of memory for each port */
+ * waste a chunk of memory for each port.
+ */
INIT_WORK(&tty->hangup_work, release_one_tty);
schedule_work(&tty->hangup_work);
}
/**
- * tty_kref_put - release a tty kref
- * @tty: tty device
+ * tty_kref_put - release a tty kref
+ * @tty: tty device
*
- * Release a reference to a tty device and if need be let the kref
- * layer destruct the object for us
+ * Release a reference to the @tty device and if need be let the kref layer
+ * destruct the object for us.
*/
-
void tty_kref_put(struct tty_struct *tty)
{
if (tty)
@@ -1440,16 +1559,17 @@ void tty_kref_put(struct tty_struct *tty)
EXPORT_SYMBOL(tty_kref_put);
/**
- * release_tty - release tty structure memory
- *
- * Release both @tty and a possible linked partner (think pty pair),
- * and decrement the refcount of the backing module.
+ * release_tty - release tty structure memory
+ * @tty: tty device release
+ * @idx: index of the tty device release
*
- * Locking:
- * tty_mutex
- * takes the file list lock internally when working on the list
- * of ttys that the driver keeps.
+ * Release both @tty and a possible linked partner (think pty pair),
+ * and decrement the refcount of the backing module.
*
+ * Locking:
+ * tty_mutex
+ * takes the file list lock internally when working on the list of ttys
+ * that the driver keeps.
*/
static void release_tty(struct tty_struct *tty, int idx)
{
@@ -1458,25 +1578,28 @@ static void release_tty(struct tty_struct *tty, int idx)
WARN_ON(!mutex_is_locked(&tty_mutex));
if (tty->ops->shutdown)
tty->ops->shutdown(tty);
- tty_free_termios(tty);
+ tty_save_termios(tty);
tty_driver_remove_tty(tty->driver, tty);
- tty->port->itty = NULL;
+ if (tty->port)
+ tty->port->itty = NULL;
if (tty->link)
tty->link->port->itty = NULL;
- tty_buffer_cancel_work(tty->port);
+ if (tty->port)
+ tty_buffer_cancel_work(tty->port);
+ if (tty->link)
+ tty_buffer_cancel_work(tty->link->port);
tty_kref_put(tty->link);
tty_kref_put(tty);
}
/**
- * tty_release_checks - check a tty before real release
- * @tty: tty to check
- * @o_tty: link of @tty (if any)
- * @idx: index of the tty
+ * tty_release_checks - check a tty before real release
+ * @tty: tty to check
+ * @idx: index of the tty
*
- * Performs some paranoid checking before true release of the @tty.
- * This is a no-op unless TTY_PARANOIA_CHECK is defined.
+ * Performs some paranoid checking before true release of the @tty. This is a
+ * no-op unless %TTY_PARANOIA_CHECK is defined.
*/
static int tty_release_checks(struct tty_struct *tty, int idx)
{
@@ -1513,12 +1636,42 @@ static int tty_release_checks(struct tty_struct *tty, int idx)
}
/**
- * tty_release_struct - release a tty struct
- * @tty: tty device
- * @idx: index of the tty
+ * tty_kclose - closes tty opened by tty_kopen
+ * @tty: tty device
+ *
+ * Performs the final steps to release and free a tty device. It is the same as
+ * tty_release_struct() except that it also resets %TTY_PORT_KOPENED flag on
+ * @tty->port.
+ */
+void tty_kclose(struct tty_struct *tty)
+{
+ /*
+ * Ask the line discipline code to release its structures
+ */
+ tty_ldisc_release(tty);
+
+ /* Wait for pending work before tty destruction commences */
+ tty_flush_works(tty);
+
+ tty_debug_hangup(tty, "freeing structure\n");
+ /*
+ * The release_tty function takes care of the details of clearing
+ * the slots and preserving the termios structure.
+ */
+ mutex_lock(&tty_mutex);
+ tty_port_set_kopened(tty->port, 0);
+ release_tty(tty, tty->index);
+ mutex_unlock(&tty_mutex);
+}
+EXPORT_SYMBOL_GPL(tty_kclose);
+
+/**
+ * tty_release_struct - release a tty struct
+ * @tty: tty device
+ * @idx: index of the tty
*
- * Performs the final steps to release and free a tty device. It is
- * roughly the reverse of tty_init_dev.
+ * Performs the final steps to release and free a tty device. It is roughly the
+ * reverse of tty_init_dev().
*/
void tty_release_struct(struct tty_struct *tty, int idx)
{
@@ -1533,9 +1686,7 @@ void tty_release_struct(struct tty_struct *tty, int idx)
tty_debug_hangup(tty, "freeing structure\n");
/*
* The release_tty function takes care of the details of clearing
- * the slots and preserving the termios structure. The tty_unlock_pair
- * should be safe as we keep a kref while the tty is locked (so the
- * unlock never unlocks a freed tty).
+ * the slots and preserving the termios structure.
*/
mutex_lock(&tty_mutex);
release_tty(tty, idx);
@@ -1544,24 +1695,23 @@ void tty_release_struct(struct tty_struct *tty, int idx)
EXPORT_SYMBOL_GPL(tty_release_struct);
/**
- * tty_release - vfs callback for close
- * @inode: inode of tty
- * @filp: file pointer for handle to tty
+ * tty_release - vfs callback for close
+ * @inode: inode of tty
+ * @filp: file pointer for handle to tty
*
- * Called the last time each file handle is closed that references
- * this tty. There may however be several such references.
+ * Called the last time each file handle is closed that references this tty.
+ * There may however be several such references.
*
- * Locking:
- * Takes bkl. See tty_release_dev
+ * Locking:
+ * Takes BKL. See tty_release_dev().
*
- * Even releasing the tty structures is a tricky business.. We have
- * to be very careful that the structures are all released at the
- * same time, as interrupts might otherwise get the wrong pointers.
+ * Even releasing the tty structures is a tricky business. We have to be very
+ * careful that the structures are all released at the same time, as interrupts
+ * might otherwise get the wrong pointers.
*
* WSH 09/09/97: rewritten to avoid some nasty race conditions that could
* lead to double frees or releasing memory still in use.
*/
-
int tty_release(struct inode *inode, struct file *filp)
{
struct tty_struct *tty = file_tty(filp);
@@ -1615,21 +1765,21 @@ int tty_release(struct inode *inode, struct file *filp)
if (tty->count <= 1) {
if (waitqueue_active(&tty->read_wait)) {
- wake_up_poll(&tty->read_wait, POLLIN);
+ wake_up_poll(&tty->read_wait, EPOLLIN);
do_sleep++;
}
if (waitqueue_active(&tty->write_wait)) {
- wake_up_poll(&tty->write_wait, POLLOUT);
+ wake_up_poll(&tty->write_wait, EPOLLOUT);
do_sleep++;
}
}
if (o_tty && o_tty->count <= 1) {
if (waitqueue_active(&o_tty->read_wait)) {
- wake_up_poll(&o_tty->read_wait, POLLIN);
+ wake_up_poll(&o_tty->read_wait, EPOLLIN);
do_sleep++;
}
if (waitqueue_active(&o_tty->write_wait)) {
- wake_up_poll(&o_tty->write_wait, POLLOUT);
+ wake_up_poll(&o_tty->write_wait, EPOLLOUT);
do_sleep++;
}
}
@@ -1678,9 +1828,9 @@ int tty_release(struct inode *inode, struct file *filp)
*/
if (!tty->count) {
read_lock(&tasklist_lock);
- session_clear_tty(tty->session);
+ session_clear_tty(tty->ctrl.session);
if (o_tty)
- session_clear_tty(o_tty->session);
+ session_clear_tty(o_tty->ctrl.session);
read_unlock(&tasklist_lock);
}
@@ -1691,7 +1841,8 @@ int tty_release(struct inode *inode, struct file *filp)
tty_unlock(tty);
/* At this point, the tty->count == 0 should ensure a dead tty
- cannot be re-opened by a racing opener */
+ * cannot be re-opened by a racing opener.
+ */
if (!final)
return 0;
@@ -1703,15 +1854,15 @@ int tty_release(struct inode *inode, struct file *filp)
}
/**
- * tty_open_current_tty - get locked tty of current task
- * @device: device number
- * @filp: file pointer to tty
- * @return: locked tty of the current task iff @device is /dev/tty
+ * tty_open_current_tty - get locked tty of current task
+ * @device: device number
+ * @filp: file pointer to tty
+ * @return: locked tty of the current task iff @device is /dev/tty
*
- * Performs a re-open of the current task's controlling 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 cannot return driver and index like for the other nodes because devpts
+ * will not work then. It expects inodes to be from devpts FS.
*/
static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
@@ -1739,26 +1890,28 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
}
/**
- * tty_lookup_driver - lookup a tty driver for a given device file
- * @device: device number
- * @filp: file pointer to tty
- * @index: index for the device in the @return driver
- * @return: driver for this inode (with increased refcount)
+ * tty_lookup_driver - lookup a tty driver for a given device file
+ * @device: device number
+ * @filp: file pointer to tty
+ * @index: index for the device in the @return driver
*
- * If @return is not erroneous, the caller is responsible to decrement the
- * refcount by tty_driver_kref_put.
+ * If returned value is not erroneous, the caller is responsible to decrement
+ * the refcount by tty_driver_kref_put().
*
- * Locking: tty_mutex protects get_tty_driver
+ * Locking: %tty_mutex protects get_tty_driver()
+ *
+ * Return: driver for this inode (with increased refcount)
*/
static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
int *index)
{
- struct tty_driver *driver;
+ struct tty_driver *driver = NULL;
switch (device) {
#ifdef CONFIG_VT
case MKDEV(TTY_MAJOR, 0): {
extern struct tty_driver *console_driver;
+
driver = tty_driver_kref_get(console_driver);
*index = fg_console;
break;
@@ -1766,6 +1919,7 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
#endif
case MKDEV(TTYAUX_MAJOR, 1): {
struct tty_driver *console_driver = console_device(index);
+
if (console_driver) {
driver = tty_driver_kref_get(console_driver);
if (driver && filp) {
@@ -1774,6 +1928,8 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
break;
}
}
+ if (driver)
+ tty_driver_kref_put(driver);
return ERR_PTR(-ENODEV);
}
default:
@@ -1785,23 +1941,92 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
return driver;
}
+static struct tty_struct *tty_kopen(dev_t device, int shared)
+{
+ struct tty_struct *tty;
+ struct tty_driver *driver;
+ int index = -1;
+
+ mutex_lock(&tty_mutex);
+ driver = tty_lookup_driver(device, NULL, &index);
+ if (IS_ERR(driver)) {
+ mutex_unlock(&tty_mutex);
+ return ERR_CAST(driver);
+ }
+
+ /* check whether we're reopening an existing tty */
+ tty = tty_driver_lookup_tty(driver, NULL, index);
+ if (IS_ERR(tty) || shared)
+ goto out;
+
+ if (tty) {
+ /* drop kref from tty_driver_lookup_tty() */
+ tty_kref_put(tty);
+ tty = ERR_PTR(-EBUSY);
+ } else { /* tty_init_dev returns tty with the tty_lock held */
+ tty = tty_init_dev(driver, index);
+ if (IS_ERR(tty))
+ goto out;
+ tty_port_set_kopened(tty->port, 1);
+ }
+out:
+ mutex_unlock(&tty_mutex);
+ tty_driver_kref_put(driver);
+ return tty;
+}
+
/**
- * tty_open_by_driver - open a tty device
- * @device: dev_t of device to open
- * @inode: inode of device file
- * @filp: file pointer to tty
+ * tty_kopen_exclusive - open a tty device for kernel
+ * @device: dev_t of device to open
+ *
+ * Opens tty exclusively for kernel. Performs the driver lookup, makes sure
+ * it's not already opened and performs the first-time tty initialization.
+ *
+ * Claims the global %tty_mutex to serialize:
+ * * concurrent first-time tty initialization
+ * * concurrent tty driver removal w/ lookup
+ * * concurrent tty removal from driver table
*
- * Performs the driver lookup, checks for a reopen, or otherwise
- * performs the first-time tty initialization.
+ * Return: the locked initialized &tty_struct
+ */
+struct tty_struct *tty_kopen_exclusive(dev_t device)
+{
+ return tty_kopen(device, 0);
+}
+EXPORT_SYMBOL_GPL(tty_kopen_exclusive);
+
+/**
+ * tty_kopen_shared - open a tty device for shared in-kernel use
+ * @device: dev_t of device to open
*
- * Returns the locked initialized or re-opened &tty_struct
+ * Opens an already existing tty for in-kernel use. Compared to
+ * tty_kopen_exclusive() above it doesn't ensure to be the only user.
*
- * Claims the global tty_mutex to serialize:
- * - concurrent first-time tty initialization
- * - concurrent tty driver removal w/ lookup
- * - concurrent tty removal from driver table
+ * Locking: identical to tty_kopen() above.
*/
-struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
+struct tty_struct *tty_kopen_shared(dev_t device)
+{
+ return tty_kopen(device, 1);
+}
+EXPORT_SYMBOL_GPL(tty_kopen_shared);
+
+/**
+ * tty_open_by_driver - open a tty device
+ * @device: dev_t of device to open
+ * @filp: file pointer to tty
+ *
+ * Performs the driver lookup, checks for a reopen, or otherwise performs the
+ * first-time tty initialization.
+ *
+ *
+ * Claims the global tty_mutex to serialize:
+ * * concurrent first-time tty initialization
+ * * concurrent tty driver removal w/ lookup
+ * * concurrent tty removal from driver table
+ *
+ * Return: the locked initialized or re-opened &tty_struct
+ */
+static struct tty_struct *tty_open_by_driver(dev_t device,
struct file *filp)
{
struct tty_struct *tty;
@@ -1824,6 +2049,12 @@ struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
}
if (tty) {
+ if (tty_port_kopened(tty->port)) {
+ tty_kref_put(tty);
+ mutex_unlock(&tty_mutex);
+ tty = ERR_PTR(-EBUSY);
+ goto out;
+ }
mutex_unlock(&tty_mutex);
retval = tty_lock_interruptible(tty);
tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */
@@ -1846,32 +2077,30 @@ out:
tty_driver_kref_put(driver);
return tty;
}
-EXPORT_SYMBOL_GPL(tty_open_by_driver);
/**
- * tty_open - open a tty device
- * @inode: inode of device file
- * @filp: file pointer to tty
+ * tty_open - open a tty device
+ * @inode: inode of device file
+ * @filp: file pointer to tty
*
- * tty_open and tty_release keep up the tty count that contains the
- * number of opens done on a tty. We cannot use the inode-count, as
- * different inodes might point to the same tty.
+ * tty_open() and tty_release() keep up the tty count that contains the number
+ * of opens done on a tty. We cannot use the inode-count, as different inodes
+ * might point to the same tty.
*
- * Open-counting is needed for pty masters, as well as for keeping
- * track of serial lines: DTR is dropped when the last close happens.
- * (This is not done solely through tty->count, now. - Ted 1/27/92)
+ * Open-counting is needed for pty masters, as well as for keeping track of
+ * serial lines: DTR is dropped when the last close happens.
+ * (This is not done solely through tty->count, now. - Ted 1/27/92)
*
- * The termios state of a pty is reset on first open so that
- * settings don't persist across reuse.
+ * The termios state of a pty is reset on the first open so that settings don't
+ * persist across reuse.
*
- * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.
- * tty->count should protect the rest.
- * ->siglock protects ->signal/->sighand
+ * Locking:
+ * * %tty_mutex protects tty, tty_lookup_driver() and tty_init_dev().
+ * * @tty->count should protect the rest.
+ * * ->siglock protects ->signal/->sighand
*
- * Note: the tty_unlock/lock cases without a ref are only safe due to
- * tty_mutex
+ * Note: the tty_unlock/lock cases without a ref are only safe due to %tty_mutex
*/
-
static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
@@ -1888,7 +2117,7 @@ retry_open:
tty = tty_open_current_tty(device, filp);
if (!tty)
- tty = tty_open_by_driver(device, inode, filp);
+ tty = tty_open_by_driver(device, filp);
if (IS_ERR(tty)) {
tty_free_file(filp);
@@ -1943,24 +2172,22 @@ retry_open:
}
-
/**
- * tty_poll - check tty status
- * @filp: file being polled
- * @wait: poll wait structures to update
+ * tty_poll - check tty status
+ * @filp: file being polled
+ * @wait: poll wait structures to update
*
- * Call the line discipline polling method to obtain the poll
- * status of the device.
+ * Call the line discipline polling method to obtain the poll status of the
+ * device.
*
- * Locking: locks called line discipline but ldisc poll method
- * may be re-entered freely by other callers.
+ * Locking: locks called line discipline but ldisc poll method may be
+ * re-entered freely by other callers.
*/
-
-static unsigned int tty_poll(struct file *filp, poll_table *wait)
+static __poll_t tty_poll(struct file *filp, poll_table *wait)
{
struct tty_struct *tty = file_tty(filp);
struct tty_ldisc *ld;
- int ret = 0;
+ __poll_t ret = 0;
if (tty_paranoia_check(tty, file_inode(filp), "tty_poll"))
return 0;
@@ -1983,6 +2210,12 @@ static int __tty_fasync(int fd, struct file *filp, int on)
if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
goto out;
+ if (on) {
+ retval = file_f_owner_allocate(filp);
+ if (retval)
+ goto out;
+ }
+
retval = fasync_helper(fd, filp, on, &tty->fasync);
if (retval <= 0)
goto out;
@@ -1991,16 +2224,16 @@ static int __tty_fasync(int fd, struct file *filp, int on)
enum pid_type type;
struct pid *pid;
- spin_lock_irqsave(&tty->ctrl_lock, flags);
- if (tty->pgrp) {
- pid = tty->pgrp;
+ spin_lock_irqsave(&tty->ctrl.lock, flags);
+ if (tty->ctrl.pgrp) {
+ pid = tty->ctrl.pgrp;
type = PIDTYPE_PGID;
} else {
pid = task_pid(current);
- type = PIDTYPE_PID;
+ type = PIDTYPE_TGID;
}
get_pid(pid);
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ spin_unlock_irqrestore(&tty->ctrl.lock, flags);
__f_setown(filp, pid, type, 0);
put_pid(pid);
retval = 0;
@@ -2022,27 +2255,27 @@ static int tty_fasync(int fd, struct file *filp, int on)
return retval;
}
+static bool tty_legacy_tiocsti __read_mostly = IS_ENABLED(CONFIG_LEGACY_TIOCSTI);
/**
- * tiocsti - fake input character
- * @tty: tty to fake input into
- * @p: pointer to character
- *
- * Fake input to a tty device. Does the necessary locking and
- * input management.
+ * tiocsti - fake input character
+ * @tty: tty to fake input into
+ * @p: pointer to character
*
- * FIXME: does not honour flow control ??
+ * Fake input to a tty device. Does the necessary locking and input management.
*
- * Locking:
- * Called functions take tty_ldiscs_lock
- * current->signal->tty check is safe without locks
+ * FIXME: does not honour flow control ??
*
- * FIXME: may race normal receive processing
+ * Locking:
+ * * Called functions take tty_ldiscs_lock
+ * * current->signal->tty check is safe without locks
*/
-
-static int tiocsti(struct tty_struct *tty, char __user *p)
+static int tiocsti(struct tty_struct *tty, u8 __user *p)
{
- char ch, mbz = 0;
struct tty_ldisc *ld;
+ u8 ch;
+
+ if (!tty_legacy_tiocsti && !capable(CAP_SYS_ADMIN))
+ return -EIO;
if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -2052,51 +2285,50 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
ld = tty_ldisc_ref_wait(tty);
if (!ld)
return -EIO;
- ld->ops->receive_buf(tty, &ch, &mbz, 1);
+ tty_buffer_lock_exclusive(tty->port);
+ if (ld->ops->receive_buf)
+ ld->ops->receive_buf(tty, &ch, NULL, 1);
+ tty_buffer_unlock_exclusive(tty->port);
tty_ldisc_deref(ld);
return 0;
}
/**
- * tiocgwinsz - implement window query ioctl
- * @tty; tty
- * @arg: user buffer for result
+ * tiocgwinsz - implement window query ioctl
+ * @tty: tty
+ * @arg: user buffer for result
*
- * Copies the kernel idea of the window size into the user buffer.
+ * Copies the kernel idea of the window size into the user buffer.
*
- * Locking: tty->winsize_mutex is taken to ensure the winsize data
- * is consistent.
+ * Locking: @tty->winsize_mutex is taken to ensure the winsize data is
+ * consistent.
*/
-
static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
{
- int err;
+ guard(mutex)(&tty->winsize_mutex);
- mutex_lock(&tty->winsize_mutex);
- err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
- mutex_unlock(&tty->winsize_mutex);
+ if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
+ return -EFAULT;
- return err ? -EFAULT: 0;
+ return 0;
}
/**
- * tty_do_resize - resize event
- * @tty: tty being resized
- * @rows: rows (character)
- * @cols: cols (character)
+ * tty_do_resize - resize event
+ * @tty: tty being resized
+ * @ws: new dimensions
*
- * Update the termios variables and send the necessary signals to
- * peform a terminal resize correctly
+ * Update the termios variables and send the necessary signals to peform a
+ * terminal resize correctly.
*/
-
int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
{
struct pid *pgrp;
- /* Lock the tty */
- mutex_lock(&tty->winsize_mutex);
+ guard(mutex)(&tty->winsize_mutex);
+
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
- goto done;
+ return 0;
/* Signal the foreground process group */
pgrp = tty_get_pgrp(tty);
@@ -2105,30 +2337,29 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
put_pid(pgrp);
tty->winsize = *ws;
-done:
- mutex_unlock(&tty->winsize_mutex);
+
return 0;
}
EXPORT_SYMBOL(tty_do_resize);
/**
- * tiocswinsz - implement window size set ioctl
- * @tty; tty side of tty
- * @arg: user buffer for result
+ * tiocswinsz - implement window size set ioctl
+ * @tty: tty side of tty
+ * @arg: user buffer for result
*
- * Copies the user idea of the window size to the kernel. Traditionally
- * this is just advisory information but for the Linux console it
- * actually has driver level meaning and triggers a VC resize.
+ * Copies the user idea of the window size to the kernel. Traditionally this is
+ * just advisory information but for the Linux console it actually has driver
+ * level meaning and triggers a VC resize.
*
- * Locking:
- * Driver dependent. The default do_resize method takes the
- * tty termios mutex and ctrl_lock. The console takes its own lock
- * then calls into the default method.
+ * Locking:
+ * Driver dependent. The default do_resize method takes the tty termios
+ * mutex and ctrl.lock. The console takes its own lock then calls into the
+ * default method.
*/
-
static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
{
struct winsize tmp_ws;
+
if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
return -EFAULT;
@@ -2139,20 +2370,20 @@ static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
}
/**
- * tioccons - allow admin to move logical console
- * @file: the file to become console
+ * tioccons - allow admin to move logical console
+ * @file: the file to become console
*
- * Allow the administrator to move the redirected console device
+ * Allow the administrator to move the redirected console device.
*
- * Locking: uses redirect_lock to guard the redirect information
+ * Locking: uses redirect_lock to guard the redirect information
*/
-
static int tioccons(struct file *file)
{
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (file->f_op->write == redirected_tty_write) {
+ if (file->f_op->write_iter == redirected_tty_write) {
struct file *f;
+
spin_lock(&redirect_lock);
f = redirect;
redirect = NULL;
@@ -2161,54 +2392,32 @@ static int tioccons(struct file *file)
fput(f);
return 0;
}
- spin_lock(&redirect_lock);
- if (redirect) {
- spin_unlock(&redirect_lock);
- return -EBUSY;
- }
- redirect = get_file(file);
- spin_unlock(&redirect_lock);
- return 0;
-}
+ if (file->f_op->write_iter != tty_write)
+ return -ENOTTY;
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EBADF;
+ if (!(file->f_mode & FMODE_CAN_WRITE))
+ return -EINVAL;
-/**
- * fionbio - non blocking ioctl
- * @file: file to set blocking value
- * @p: user parameter
- *
- * Historical tty interfaces had a blocking control ioctl before
- * the generic functionality existed. This piece of history is preserved
- * in the expected tty API of posix OS's.
- *
- * Locking: none, the open file handle ensures it won't go away.
- */
+ guard(spinlock)(&redirect_lock);
-static int fionbio(struct file *file, int __user *p)
-{
- int nonblock;
+ if (redirect)
+ return -EBUSY;
- if (get_user(nonblock, p))
- return -EFAULT;
+ redirect = get_file(file);
- spin_lock(&file->f_lock);
- if (nonblock)
- file->f_flags |= O_NONBLOCK;
- else
- file->f_flags &= ~O_NONBLOCK;
- spin_unlock(&file->f_lock);
return 0;
}
/**
- * tiocsetd - set line discipline
- * @tty: tty device
- * @p: pointer to user data
+ * tiocsetd - set line discipline
+ * @tty: tty device
+ * @p: pointer to user data
*
- * Set the line discipline according to user request.
+ * Set the line discipline according to user request.
*
- * Locking: see tty_set_ldisc, this function is just a helper
+ * Locking: see tty_set_ldisc(), this function is just a helper
*/
-
static int tiocsetd(struct tty_struct *tty, int __user *p)
{
int disc;
@@ -2223,16 +2432,15 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
}
/**
- * tiocgetd - get line discipline
- * @tty: tty device
- * @p: pointer to user data
+ * tiocgetd - get line discipline
+ * @tty: tty device
+ * @p: pointer to user data
*
- * Retrieves the line discipline id directly from the ldisc.
+ * Retrieves the line discipline id directly from the ldisc.
*
- * Locking: waits for ldisc reference (in case the line discipline
- * is changing or the tty is being hungup)
+ * Locking: waits for ldisc reference (in case the line discipline is changing
+ * or the @tty is being hungup)
*/
-
static int tiocgetd(struct tty_struct *tty, int __user *p)
{
struct tty_ldisc *ld;
@@ -2247,18 +2455,16 @@ static int tiocgetd(struct tty_struct *tty, int __user *p)
}
/**
- * send_break - performed time break
- * @tty: device to break on
- * @duration: timeout in mS
- *
- * Perform a timed break on hardware that lacks its own driver level
- * timed break functionality.
+ * send_break - performed time break
+ * @tty: device to break on
+ * @duration: timeout in mS
*
- * Locking:
- * atomic_write_lock serializes
+ * Perform a timed break on hardware that lacks its own driver level timed
+ * break functionality.
*
+ * Locking:
+ * @tty->atomic_write_lock serializes
*/
-
static int send_break(struct tty_struct *tty, unsigned int duration)
{
int retval;
@@ -2267,62 +2473,78 @@ static int send_break(struct tty_struct *tty, unsigned int duration)
return 0;
if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
- retval = tty->ops->break_ctl(tty, duration);
- else {
- /* Do the work ourselves */
- if (tty_write_lock(tty, 0) < 0)
- return -EINTR;
- retval = tty->ops->break_ctl(tty, -1);
- if (retval)
- goto out;
- if (!signal_pending(current))
- msleep_interruptible(duration);
+ return tty->ops->break_ctl(tty, duration);
+
+ /* Do the work ourselves */
+ if (tty_write_lock(tty, false) < 0)
+ return -EINTR;
+
+ retval = tty->ops->break_ctl(tty, -1);
+ if (!retval) {
+ msleep_interruptible(duration);
retval = tty->ops->break_ctl(tty, 0);
-out:
- tty_write_unlock(tty);
- if (signal_pending(current))
- retval = -EINTR;
+ } else if (retval == -EOPNOTSUPP) {
+ /* some drivers can tell only dynamically */
+ retval = 0;
}
+ tty_write_unlock(tty);
+
+ if (signal_pending(current))
+ retval = -EINTR;
+
return retval;
}
/**
- * tty_tiocmget - get modem status
- * @tty: tty device
- * @file: user file pointer
- * @p: pointer to result
- *
- * Obtain the modem status bits from the tty driver if the feature
- * is supported. Return -EINVAL if it is not available.
+ * tty_get_tiocm - get tiocm status register
+ * @tty: tty device
*
- * Locking: none (up to the driver)
+ * Obtain the modem status bits from the tty driver if the feature
+ * is supported.
*/
-
-static int tty_tiocmget(struct tty_struct *tty, int __user *p)
+int tty_get_tiocm(struct tty_struct *tty)
{
- int retval = -EINVAL;
+ int retval = -ENOTTY;
- if (tty->ops->tiocmget) {
+ if (tty->ops->tiocmget)
retval = tty->ops->tiocmget(tty);
- if (retval >= 0)
- retval = put_user(retval, p);
- }
return retval;
}
+EXPORT_SYMBOL_GPL(tty_get_tiocm);
/**
- * tty_tiocmset - set modem status
- * @tty: tty device
- * @cmd: command - clear bits, set bits or set all
- * @p: pointer to desired bits
+ * tty_tiocmget - get modem status
+ * @tty: tty device
+ * @p: pointer to result
*
- * Set the modem status bits from the tty driver if the feature
- * is supported. Return -EINVAL if it is not available.
+ * Obtain the modem status bits from the tty driver if the feature is
+ * supported. Return -%ENOTTY if it is not available.
*
- * Locking: none (up to the driver)
+ * Locking: none (up to the driver)
*/
+static int tty_tiocmget(struct tty_struct *tty, int __user *p)
+{
+ int retval;
+
+ retval = tty_get_tiocm(tty);
+ if (retval >= 0)
+ retval = put_user(retval, p);
+
+ return retval;
+}
+/**
+ * tty_tiocmset - set modem status
+ * @tty: tty device
+ * @cmd: command - clear bits, set bits or set all
+ * @p: pointer to desired bits
+ *
+ * Set the modem status bits from the tty driver if the feature
+ * is supported. Return -%ENOTTY if it is not available.
+ *
+ * Locking: none (up to the driver)
+ */
static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd,
unsigned __user *p)
{
@@ -2330,7 +2552,7 @@ static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd,
unsigned int set, clear, val;
if (tty->ops->tiocmset == NULL)
- return -EINVAL;
+ return -ENOTTY;
retval = get_user(val, p);
if (retval)
@@ -2353,36 +2575,79 @@ static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd,
return tty->ops->tiocmset(tty, set, clear);
}
+/**
+ * tty_get_icount - get tty statistics
+ * @tty: tty device
+ * @icount: output parameter
+ *
+ * Gets a copy of the @tty's icount statistics.
+ *
+ * Locking: none (up to the driver)
+ */
+int tty_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ memset(icount, 0, sizeof(*icount));
+
+ if (tty->ops->get_icount)
+ return tty->ops->get_icount(tty, icount);
+ else
+ return -ENOTTY;
+}
+EXPORT_SYMBOL_GPL(tty_get_icount);
+
static int tty_tiocgicount(struct tty_struct *tty, void __user *arg)
{
- int retval = -EINVAL;
struct serial_icounter_struct icount;
- memset(&icount, 0, sizeof(icount));
- if (tty->ops->get_icount)
- retval = tty->ops->get_icount(tty, &icount);
+ int retval;
+
+ retval = tty_get_icount(tty, &icount);
if (retval != 0)
return retval;
+
if (copy_to_user(arg, &icount, sizeof(icount)))
return -EFAULT;
return 0;
}
-static void tty_warn_deprecated_flags(struct serial_struct __user *ss)
+static int tty_set_serial(struct tty_struct *tty, struct serial_struct *ss)
{
- static DEFINE_RATELIMIT_STATE(depr_flags,
- DEFAULT_RATELIMIT_INTERVAL,
- DEFAULT_RATELIMIT_BURST);
- char comm[TASK_COMM_LEN];
int flags;
- if (get_user(flags, &ss->flags))
- return;
+ flags = ss->flags & ASYNC_DEPRECATED;
+
+ if (flags)
+ pr_warn_ratelimited("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
+ __func__, current->comm, flags);
- flags &= ASYNC_DEPRECATED;
+ if (!tty->ops->set_serial)
+ return -ENOTTY;
- if (flags && __ratelimit(&depr_flags))
- pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
- __func__, get_task_comm(comm, current), flags);
+ return tty->ops->set_serial(tty, ss);
+}
+
+static int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss)
+{
+ struct serial_struct v;
+
+ if (copy_from_user(&v, ss, sizeof(*ss)))
+ return -EFAULT;
+
+ return tty_set_serial(tty, &v);
+}
+
+static int tty_tiocgserial(struct tty_struct *tty, struct serial_struct __user *ss)
+{
+ struct serial_struct v;
+ int err;
+
+ memset(&v, 0, sizeof(v));
+ if (!tty->ops->get_serial)
+ return -ENOTTY;
+ err = tty->ops->get_serial(tty, &v);
+ if (!err && copy_to_user(ss, &v, sizeof(v)))
+ err = -EFAULT;
+ return err;
}
/*
@@ -2445,8 +2710,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return tiocswinsz(real_tty, p);
case TIOCCONS:
return real_tty != tty ? -EINVAL : tioccons(file);
- case FIONBIO:
- return fionbio(file, p);
case TIOCEXCL:
set_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
@@ -2456,6 +2719,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case TIOCGEXCL:
{
int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
+
return put_user(excl, (int __user *)p);
}
case TIOCGETD:
@@ -2470,6 +2734,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case TIOCGDEV:
{
unsigned int ret = new_encode_dev(tty_devnum(real_tty));
+
return put_user(ret, (unsigned int __user *)p);
}
/*
@@ -2501,11 +2766,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case TIOCMBIS:
return tty_tiocmset(tty, cmd, p);
case TIOCGICOUNT:
- retval = tty_tiocgicount(tty, p);
- /* For the moment allow fall through to the old method */
- if (retval != -EINVAL)
- return retval;
- break;
+ return tty_tiocgicount(tty, p);
case TCFLSH:
switch (arg) {
case TCIFLUSH:
@@ -2516,8 +2777,12 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
break;
case TIOCSSERIAL:
- tty_warn_deprecated_flags(p);
- break;
+ return tty_tiocsserial(tty, p);
+ case TIOCGSERIAL:
+ return tty_tiocgserial(tty, p);
+ case TIOCGPTPEER:
+ /* Special because the struct file is needed */
+ return ptm_open_peer(file, tty, (int)arg);
default:
retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg);
if (retval != -ENOIOCTLCMD)
@@ -2533,7 +2798,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return hung_up_tty_ioctl(file, cmd, arg);
retval = -EINVAL;
if (ld->ops->ioctl) {
- retval = ld->ops->ioctl(tty, file, cmd, arg);
+ retval = ld->ops->ioctl(tty, cmd, arg);
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
}
@@ -2542,6 +2807,71 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
#ifdef CONFIG_COMPAT
+
+struct serial_struct32 {
+ compat_int_t type;
+ compat_int_t line;
+ compat_uint_t port;
+ compat_int_t irq;
+ compat_int_t flags;
+ compat_int_t xmit_fifo_size;
+ compat_int_t custom_divisor;
+ compat_int_t baud_base;
+ unsigned short close_delay;
+ char io_type;
+ char reserved_char;
+ compat_int_t hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ compat_uint_t iomem_base;
+ unsigned short iomem_reg_shift;
+ unsigned int port_high;
+ /* compat_ulong_t iomap_base FIXME */
+ compat_int_t reserved;
+};
+
+static int compat_tty_tiocsserial(struct tty_struct *tty,
+ struct serial_struct32 __user *ss)
+{
+ struct serial_struct32 v32;
+ struct serial_struct v;
+
+ if (copy_from_user(&v32, ss, sizeof(*ss)))
+ return -EFAULT;
+
+ memcpy(&v, &v32, offsetof(struct serial_struct32, iomem_base));
+ v.iomem_base = compat_ptr(v32.iomem_base);
+ v.iomem_reg_shift = v32.iomem_reg_shift;
+ v.port_high = v32.port_high;
+ v.iomap_base = 0;
+
+ return tty_set_serial(tty, &v);
+}
+
+static int compat_tty_tiocgserial(struct tty_struct *tty,
+ struct serial_struct32 __user *ss)
+{
+ struct serial_struct32 v32;
+ struct serial_struct v;
+ int err;
+
+ memset(&v, 0, sizeof(v));
+ memset(&v32, 0, sizeof(v32));
+
+ if (!tty->ops->get_serial)
+ return -ENOTTY;
+ err = tty->ops->get_serial(tty, &v);
+ if (!err) {
+ memcpy(&v32, &v, offsetof(struct serial_struct32, iomem_base));
+ v32.iomem_base = (unsigned long)v.iomem_base >> 32 ?
+ 0xfffffff : ptr_to_compat(v.iomem_base);
+ v32.iomem_reg_shift = v.iomem_reg_shift;
+ v32.port_high = v.port_high;
+ if (copy_to_user(ss, &v32, sizeof(v32)))
+ err = -EFAULT;
+ }
+ return err;
+}
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -2549,9 +2879,94 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
struct tty_ldisc *ld;
int retval = -ENOIOCTLCMD;
+ switch (cmd) {
+ case TIOCOUTQ:
+ case TIOCSTI:
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ case TIOCGEXCL:
+ case TIOCGETD:
+ case TIOCSETD:
+ case TIOCGDEV:
+ case TIOCMGET:
+ case TIOCMSET:
+ case TIOCMBIC:
+ case TIOCMBIS:
+ case TIOCGICOUNT:
+ case TIOCGPGRP:
+ case TIOCSPGRP:
+ case TIOCGSID:
+ case TIOCSERGETLSR:
+ case TIOCGRS485:
+ case TIOCSRS485:
+#ifdef TIOCGETP
+ case TIOCGETP:
+ case TIOCSETP:
+ case TIOCSETN:
+#endif
+#ifdef TIOCGETC
+ case TIOCGETC:
+ case TIOCSETC:
+#endif
+#ifdef TIOCGLTC
+ case TIOCGLTC:
+ case TIOCSLTC:
+#endif
+ case TCSETSF:
+ case TCSETSW:
+ case TCSETS:
+ case TCGETS:
+#ifdef TCGETS2
+ case TCGETS2:
+ case TCSETSF2:
+ case TCSETSW2:
+ case TCSETS2:
+#endif
+ case TCGETA:
+ case TCSETAF:
+ case TCSETAW:
+ case TCSETA:
+ case TIOCGLCKTRMIOS:
+ case TIOCSLCKTRMIOS:
+#ifdef TCGETX
+ case TCGETX:
+ case TCSETX:
+ case TCSETXW:
+ case TCSETXF:
+#endif
+ case TIOCGSOFTCAR:
+ case TIOCSSOFTCAR:
+
+ case PPPIOCGCHAN:
+ case PPPIOCGUNIT:
+ return tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+ case TIOCCONS:
+ case TIOCEXCL:
+ case TIOCNXCL:
+ case TIOCVHANGUP:
+ case TIOCSBRK:
+ case TIOCCBRK:
+ case TCSBRK:
+ case TCSBRKP:
+ case TCFLSH:
+ case TIOCGPTPEER:
+ case TIOCNOTTY:
+ case TIOCSCTTY:
+ case TCXONC:
+ case TIOCMIWAIT:
+ case TIOCSERCONFIG:
+ return tty_ioctl(file, cmd, arg);
+ }
+
if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
return -EINVAL;
+ switch (cmd) {
+ case TIOCSSERIAL:
+ return compat_tty_tiocsserial(tty, compat_ptr(arg));
+ case TIOCGSERIAL:
+ return compat_tty_tiocgserial(tty, compat_ptr(arg));
+ }
if (tty->ops->compat_ioctl) {
retval = tty->ops->compat_ioctl(tty, cmd, arg);
if (retval != -ENOIOCTLCMD)
@@ -2562,9 +2977,10 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
if (!ld)
return hung_up_tty_compat_ioctl(file, cmd, arg);
if (ld->ops->compat_ioctl)
- retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
- else
- retval = n_tty_compat_ioctl_helper(tty, file, cmd, arg);
+ retval = ld->ops->compat_ioctl(tty, cmd, arg);
+ if (retval == -ENOIOCTLCMD && ld->ops->ioctl)
+ retval = ld->ops->ioctl(tty, (unsigned long)compat_ptr(cmd),
+ arg);
tty_ldisc_deref(ld);
return retval;
@@ -2573,11 +2989,11 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
static int this_tty(const void *t, struct file *file, unsigned fd)
{
- if (likely(file->f_op->read != tty_read))
+ if (likely(file->f_op->read_iter != tty_read))
return 0;
return file_tty(file) != t ? 0 : fd + 1;
}
-
+
/*
* This implements the "Secure Attention Key" --- the idea is to
* prevent trojan horses by killing all processes associated with this
@@ -2599,16 +3015,12 @@ static int this_tty(const void *t, struct file *file, unsigned fd)
*/
void __do_SAK(struct tty_struct *tty)
{
-#ifdef TTY_SOFT_SAK
- tty_hangup(tty);
-#else
struct task_struct *g, *p;
struct pid *session;
- int i;
+ int i;
- if (!tty)
- return;
- session = tty->session;
+ scoped_guard(spinlock_irqsave, &tty->ctrl.lock)
+ session = get_pid(tty->ctrl.session);
tty_ldisc_flush(tty);
@@ -2619,28 +3031,29 @@ void __do_SAK(struct tty_struct *tty)
do_each_pid_task(session, PIDTYPE_SID, p) {
tty_notice(tty, "SAK: killed process %d (%s): by session\n",
task_pid_nr(p), p->comm);
- send_sig(SIGKILL, p, 1);
+ group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
} while_each_pid_task(session, PIDTYPE_SID, p);
/* Now kill any processes that happen to have the tty open */
- do_each_thread(g, p) {
+ for_each_process_thread(g, p) {
if (p->signal->tty == tty) {
tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n",
task_pid_nr(p), p->comm);
- send_sig(SIGKILL, p, 1);
+ group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p,
+ PIDTYPE_SID);
continue;
}
- task_lock(p);
+ guard(task_lock)(p);
i = iterate_fd(p->files, 0, this_tty, tty);
if (i != 0) {
tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n",
task_pid_nr(p), p->comm, i - 1);
- force_sig(SIGKILL, p);
+ group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p,
+ PIDTYPE_SID);
}
- task_unlock(p);
- } while_each_thread(g, p);
+ }
read_unlock(&tasklist_lock);
-#endif
+ put_pid(session);
}
static void do_SAK_work(struct work_struct *work)
@@ -2662,44 +3075,41 @@ void do_SAK(struct tty_struct *tty)
return;
schedule_work(&tty->SAK_work);
}
-
EXPORT_SYMBOL(do_SAK);
-static int dev_match_devt(struct device *dev, const void *data)
-{
- const dev_t *devt = data;
- return dev->devt == *devt;
-}
-
/* Must put_device() after it's unused! */
static struct device *tty_get_device(struct tty_struct *tty)
{
dev_t devt = tty_devnum(tty);
- return class_find_device(tty_class, NULL, &devt, dev_match_devt);
+
+ return class_find_device_by_devt(&tty_class, devt);
}
/**
- * alloc_tty_struct
+ * alloc_tty_struct - allocate a new tty
+ * @driver: driver which will handle the returned tty
+ * @idx: minor of the tty
*
- * This subroutine allocates and initializes a tty structure.
+ * This subroutine allocates and initializes a tty structure.
*
- * Locking: none - tty in question is not exposed at this point
+ * Locking: none - @tty in question is not exposed at this point
*/
-
struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
- tty = kzalloc(sizeof(*tty), GFP_KERNEL);
+ tty = kzalloc(sizeof(*tty), GFP_KERNEL_ACCOUNT);
if (!tty)
return NULL;
kref_init(&tty->kref);
- tty->magic = TTY_MAGIC;
- tty_ldisc_init(tty);
- tty->session = NULL;
- tty->pgrp = NULL;
+ if (tty_ldisc_init(tty)) {
+ kfree(tty);
+ return NULL;
+ }
+ tty->ctrl.session = NULL;
+ tty->ctrl.pgrp = NULL;
mutex_init(&tty->legacy_mutex);
mutex_init(&tty->throttle_mutex);
init_rwsem(&tty->termios_rwsem);
@@ -2709,8 +3119,8 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup);
mutex_init(&tty->atomic_write_lock);
- spin_lock_init(&tty->ctrl_lock);
- spin_lock_init(&tty->flow_lock);
+ spin_lock_init(&tty->ctrl.lock);
+ spin_lock_init(&tty->flow.lock);
spin_lock_init(&tty->files_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, do_SAK_work);
@@ -2725,18 +3135,19 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
}
/**
- * tty_put_char - write one character to a tty
- * @tty: tty
- * @ch: character
+ * tty_put_char - write one character to a tty
+ * @tty: tty
+ * @ch: character to write
*
- * Write one byte to the tty using the provided put_char method
- * if present. Returns the number of characters successfully output.
+ * Write one byte to the @tty using the provided @tty->ops->put_char() method
+ * if present.
*
- * Note: the specific put_char operation in the driver layer may go
- * away soon. Don't call it directly, use this method
+ * Note: the specific put_char operation in the driver layer may go
+ * away soon. Don't call it directly, use this method
+ *
+ * Return: the number of characters successfully output.
*/
-
-int tty_put_char(struct tty_struct *tty, unsigned char ch)
+int tty_put_char(struct tty_struct *tty, u8 ch)
{
if (tty->ops->put_char)
return tty->ops->put_char(tty, ch);
@@ -2744,8 +3155,6 @@ int tty_put_char(struct tty_struct *tty, unsigned char ch)
}
EXPORT_SYMBOL_GPL(tty_put_char);
-struct class *tty_class;
-
static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
unsigned int index, unsigned int count)
{
@@ -2764,24 +3173,23 @@ static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
}
/**
- * tty_register_device - register a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
- * @device: a struct device that is associated with this tty device.
- * This field is optional, if there is no known struct device
- * for this tty device it can be set to NULL safely.
+ * tty_register_device - register a tty device
+ * @driver: the tty driver that describes the tty device
+ * @index: the index in the tty driver for this tty device
+ * @device: a struct device that is associated with this tty device.
+ * This field is optional, if there is no known struct device
+ * for this tty device it can be set to NULL safely.
*
- * Returns a pointer to the struct device for this tty device
- * (or ERR_PTR(-EFOO) on error).
+ * This call is required to be made to register an individual tty device
+ * if the tty driver's flags have the %TTY_DRIVER_DYNAMIC_DEV bit set. If
+ * that bit is not set, this function should not be called by a tty
+ * driver.
*
- * This call is required to be made to register an individual tty device
- * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If
- * that bit is not set, this function should not be called by a tty
- * driver.
+ * Locking: ??
*
- * Locking: ??
+ * Return: A pointer to the struct device for this tty device (or
+ * ERR_PTR(-EFOO) on error).
*/
-
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
struct device *device)
{
@@ -2796,24 +3204,23 @@ static void tty_device_create_release(struct device *dev)
}
/**
- * tty_register_device_attr - register a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
- * @device: a struct device that is associated with this tty device.
- * This field is optional, if there is no known struct device
- * for this tty device it can be set to NULL safely.
- * @drvdata: Driver data to be set to device.
- * @attr_grp: Attribute group to be set on device.
+ * tty_register_device_attr - register a tty device
+ * @driver: the tty driver that describes the tty device
+ * @index: the index in the tty driver for this tty device
+ * @device: a struct device that is associated with this tty device.
+ * This field is optional, if there is no known struct device
+ * for this tty device it can be set to %NULL safely.
+ * @drvdata: Driver data to be set to device.
+ * @attr_grp: Attribute group to be set on device.
*
- * Returns a pointer to the struct device for this tty device
- * (or ERR_PTR(-EFOO) on error).
+ * This call is required to be made to register an individual tty device if the
+ * tty driver's flags have the %TTY_DRIVER_DYNAMIC_DEV bit set. If that bit is
+ * not set, this function should not be called by a tty driver.
*
- * This call is required to be made to register an individual tty device
- * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If
- * that bit is not set, this function should not be called by a tty
- * driver.
+ * Locking: ??
*
- * Locking: ??
+ * Return: A pointer to the struct device for this tty device (or
+ * ERR_PTR(-EFOO) on error).
*/
struct device *tty_register_device_attr(struct tty_driver *driver,
unsigned index, struct device *device,
@@ -2842,7 +3249,7 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
return ERR_PTR(-ENOMEM);
dev->devt = devt;
- dev->class = tty_class;
+ dev->class = &tty_class;
dev->parent = device;
dev->release = tty_device_create_release;
dev_set_name(dev, "%s", name);
@@ -2886,20 +3293,18 @@ err_put:
EXPORT_SYMBOL_GPL(tty_register_device_attr);
/**
- * tty_unregister_device - unregister a tty device
- * @driver: the tty driver that describes the tty device
- * @index: the index in the tty driver for this tty device
+ * tty_unregister_device - unregister a tty device
+ * @driver: the tty driver that describes the tty device
+ * @index: the index in the tty driver for this tty device
*
- * If a tty device is registered with a call to tty_register_device() then
- * this function must be called when the tty device is gone.
+ * If a tty device is registered with a call to tty_register_device() then
+ * this function must be called when the tty device is gone.
*
- * Locking: ??
+ * Locking: ??
*/
-
void tty_unregister_device(struct tty_driver *driver, unsigned index)
{
- device_destroy(tty_class,
- MKDEV(driver->major, driver->minor_start) + index);
+ device_destroy(&tty_class, MKDEV(driver->major, driver->minor_start) + index);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
cdev_del(driver->cdevs[index]);
driver->cdevs[index] = NULL;
@@ -2908,13 +3313,15 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index)
EXPORT_SYMBOL(tty_unregister_device);
/**
- * __tty_alloc_driver -- allocate tty driver
+ * __tty_alloc_driver - allocate tty driver
* @lines: count of lines this driver can handle at most
* @owner: module which is responsible for this driver
- * @flags: some of TTY_DRIVER_* flags, will be set in driver->flags
+ * @flags: some of enum tty_driver_flag, will be set in driver->flags
*
- * This should not be called directly, some of the provided macros should be
- * used instead. Use IS_ERR and friends on @retval.
+ * This should not be called directly, tty_alloc_driver() should be used
+ * instead.
+ *
+ * Returns: struct tty_driver or a PTR-encoded error (use IS_ERR() and friends).
*/
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
unsigned long flags)
@@ -2926,12 +3333,11 @@ struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
return ERR_PTR(-EINVAL);
- driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL);
if (!driver)
return ERR_PTR(-ENOMEM);
kref_init(&driver->kref);
- driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
driver->owner = owner;
driver->flags = flags;
@@ -3001,26 +3407,22 @@ static void destruct_tty_driver(struct kref *kref)
kfree(driver);
}
+/**
+ * tty_driver_kref_put - drop a reference to a tty driver
+ * @driver: driver of which to drop the reference
+ *
+ * The final put will destroy and free up the driver.
+ */
void tty_driver_kref_put(struct tty_driver *driver)
{
kref_put(&driver->kref, destruct_tty_driver);
}
EXPORT_SYMBOL(tty_driver_kref_put);
-void tty_set_operations(struct tty_driver *driver,
- const struct tty_operations *op)
-{
- driver->ops = op;
-};
-EXPORT_SYMBOL(tty_set_operations);
-
-void put_tty_driver(struct tty_driver *d)
-{
- tty_driver_kref_put(d);
-}
-EXPORT_SYMBOL(put_tty_driver);
-
-/*
+/**
+ * tty_register_driver - register a tty driver
+ * @driver: driver to register
+ *
* Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
@@ -3050,9 +3452,8 @@ int tty_register_driver(struct tty_driver *driver)
goto err_unreg_char;
}
- mutex_lock(&tty_mutex);
- list_add(&driver->tty_drivers, &tty_drivers);
- mutex_unlock(&tty_mutex);
+ scoped_guard(mutex, &tty_mutex)
+ list_add(&driver->tty_drivers, &tty_drivers);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
for (i = 0; i < driver->num; i++) {
@@ -3071,9 +3472,8 @@ err_unreg_devs:
for (i--; i >= 0; i--)
tty_unregister_device(driver, i);
- mutex_lock(&tty_mutex);
- list_del(&driver->tty_drivers);
- mutex_unlock(&tty_mutex);
+ scoped_guard(mutex, &tty_mutex)
+ list_del(&driver->tty_drivers);
err_unreg_char:
unregister_chrdev_region(dev, driver->num);
@@ -3082,24 +3482,19 @@ err:
}
EXPORT_SYMBOL(tty_register_driver);
-/*
+/**
+ * tty_unregister_driver - unregister a tty driver
+ * @driver: driver to unregister
+ *
* Called by a tty driver to unregister itself.
*/
-int tty_unregister_driver(struct tty_driver *driver)
+void tty_unregister_driver(struct tty_driver *driver)
{
-#if 0
- /* FIXME */
- if (driver->refcount)
- return -EBUSY;
-#endif
unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
driver->num);
- mutex_lock(&tty_mutex);
- list_del(&driver->tty_drivers);
- mutex_unlock(&tty_mutex);
- return 0;
+ scoped_guard(mutex, &tty_mutex)
+ list_del(&driver->tty_drivers);
}
-
EXPORT_SYMBOL(tty_unregister_driver);
dev_t tty_devnum(struct tty_struct *tty)
@@ -3113,7 +3508,7 @@ void tty_default_fops(struct file_operations *fops)
*fops = tty_fops;
}
-static char *tty_devnode(struct device *dev, umode_t *mode)
+static char *tty_devnode(const struct device *dev, umode_t *mode)
{
if (!mode)
return NULL;
@@ -3123,13 +3518,14 @@ static char *tty_devnode(struct device *dev, umode_t *mode)
return NULL;
}
+const struct class tty_class = {
+ .name = "tty",
+ .devnode = tty_devnode,
+};
+
static int __init tty_class_init(void)
{
- tty_class = class_create(THIS_MODULE, "tty");
- if (IS_ERR(tty_class))
- return PTR_ERR(tty_class);
- tty_class->devnode = tty_devnode;
- return 0;
+ return class_register(&tty_class);
}
postcore_initcall(tty_class_init);
@@ -3145,11 +3541,18 @@ static ssize_t show_cons_active(struct device *dev,
struct console *c;
ssize_t count = 0;
- console_lock();
+ /*
+ * Hold the console_list_lock to guarantee that no consoles are
+ * unregistered until all console processing is complete.
+ * This also allows safe traversal of the console list and
+ * race-free reading of @flags.
+ */
+ console_list_lock();
+
for_each_console(c) {
if (!c->device)
continue;
- if (!c->write)
+ if (!(c->flags & CON_NBCON) && !c->write)
continue;
if ((c->flags & CON_ENABLED) == 0)
continue;
@@ -3157,6 +3560,13 @@ static ssize_t show_cons_active(struct device *dev,
if (i >= ARRAY_SIZE(cs))
break;
}
+
+ /*
+ * Take console_lock to serialize device() callback with
+ * other console operations. For example, fg_console is
+ * modified under console_lock when switching vt.
+ */
+ console_lock();
while (i--) {
int index = cs[i]->index;
struct tty_driver *drv = cs[i]->device(cs[i], &index);
@@ -3172,6 +3582,8 @@ static ssize_t show_cons_active(struct device *dev,
}
console_unlock();
+ console_list_unlock();
+
return count;
}
static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
@@ -3191,23 +3603,43 @@ void console_sysfs_notify(void)
sysfs_notify(&consdev->kobj, NULL, "active");
}
+static const struct ctl_table tty_table[] = {
+ {
+ .procname = "legacy_tiocsti",
+ .data = &tty_legacy_tiocsti,
+ .maxlen = sizeof(tty_legacy_tiocsti),
+ .mode = 0644,
+ .proc_handler = proc_dobool,
+ },
+ {
+ .procname = "ldisc_autoload",
+ .data = &tty_ldisc_autoload,
+ .maxlen = sizeof(tty_ldisc_autoload),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
+};
+
/*
* Ok, now we can initialize the rest of the tty devices and can count
* on memory allocations, interrupts etc..
*/
int __init tty_init(void)
{
+ register_sysctl_init("dev/tty", tty_table);
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
+ device_create(&tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
- consdev = device_create_with_groups(tty_class, NULL,
+ consdev = device_create_with_groups(&tty_class, NULL,
MKDEV(TTYAUX_MAJOR, 1), NULL,
cons_dev_groups, "console");
if (IS_ERR(consdev))
@@ -3218,4 +3650,3 @@ int __init tty_init(void)
#endif
return 0;
}
-