summaryrefslogtreecommitdiffstats
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.c114
1 files changed, 74 insertions, 40 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 8fbad3410c75..0508a1d8e4cd 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -908,8 +908,7 @@ void no_tty(void)
* stop_tty - propagate flow control
* @tty: tty to stop
*
- * Perform flow control to the driver. For PTY/TTY pairs we
- * must also propagate the TIOCKPKT status. May be called
+ * Perform flow control to the driver. May be called
* on an already stopped device and will not re-call the driver
* method.
*
@@ -919,64 +918,58 @@ void no_tty(void)
* but not always.
*
* Locking:
- * Uses the tty control lock internally
+ * flow_lock
*/
-void stop_tty(struct tty_struct *tty)
+void __stop_tty(struct tty_struct *tty)
{
- unsigned long flags;
- spin_lock_irqsave(&tty->ctrl_lock, flags);
- if (tty->stopped) {
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ if (tty->stopped)
return;
- }
tty->stopped = 1;
- if (tty->link && tty->link->packet) {
- tty->ctrl_status &= ~TIOCPKT_START;
- tty->ctrl_status |= TIOCPKT_STOP;
- wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
- }
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty->ops->stop)
(tty->ops->stop)(tty);
}
+void stop_tty(struct tty_struct *tty)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tty->flow_lock, flags);
+ __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. Perform
- * any necessary wakeups and propagate the TIOCPKT status. If this
- * is the tty was previous stopped and is being started then the
- * driver start method is invoked and the line discipline woken.
+ * 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:
- * ctrl_lock
+ * flow_lock
*/
-void start_tty(struct tty_struct *tty)
+void __start_tty(struct tty_struct *tty)
{
- unsigned long flags;
- spin_lock_irqsave(&tty->ctrl_lock, flags);
- if (!tty->stopped || tty->flow_stopped) {
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ if (!tty->stopped || tty->flow_stopped)
return;
- }
tty->stopped = 0;
- if (tty->link && tty->link->packet) {
- tty->ctrl_status &= ~TIOCPKT_STOP;
- tty->ctrl_status |= TIOCPKT_START;
- wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
- }
- spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty->ops->start)
(tty->ops->start)(tty);
- /* If we have a running line discipline it may need kicking */
tty_wakeup(tty);
}
+void start_tty(struct tty_struct *tty)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tty->flow_lock, flags);
+ __start_tty(tty);
+ spin_unlock_irqrestore(&tty->flow_lock, flags);
+}
EXPORT_SYMBOL(start_tty);
/* We limit tty time update visibility to every 8 seconds or so. */
@@ -1030,14 +1023,14 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
return i;
}
-void tty_write_unlock(struct tty_struct *tty)
+static void tty_write_unlock(struct tty_struct *tty)
__releases(&tty->atomic_write_lock)
{
mutex_unlock(&tty->atomic_write_lock);
wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
}
-int tty_write_lock(struct tty_struct *tty, int ndelay)
+static int tty_write_lock(struct tty_struct *tty, int ndelay)
__acquires(&tty->atomic_write_lock)
{
if (!mutex_trylock(&tty->atomic_write_lock)) {
@@ -1224,6 +1217,35 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
return tty_write(file, buf, count, ppos);
}
+/**
+ * tty_send_xchar - send priority character
+ *
+ * Send a high priority character to the tty even if stopped
+ *
+ * Locking: none for xchar method, write ordering for write method.
+ */
+
+int tty_send_xchar(struct tty_struct *tty, char ch)
+{
+ int was_stopped = tty->stopped;
+
+ if (tty->ops->send_xchar) {
+ tty->ops->send_xchar(tty, ch);
+ return 0;
+ }
+
+ if (tty_write_lock(tty, 0) < 0)
+ return -ERESTARTSYS;
+
+ if (was_stopped)
+ start_tty(tty);
+ tty->ops->write(tty, &ch, 1);
+ if (was_stopped)
+ stop_tty(tty);
+ tty_write_unlock(tty);
+ return 0;
+}
+
static char ptychar[] = "pqrstuvwxyzabcde";
/**
@@ -1544,13 +1566,14 @@ static void release_one_tty(struct work_struct *work)
struct tty_struct *tty =
container_of(work, struct tty_struct, hangup_work);
struct tty_driver *driver = tty->driver;
+ struct module *owner = driver->owner;
if (tty->ops->cleanup)
tty->ops->cleanup(tty);
tty->magic = 0;
tty_driver_kref_put(driver);
- module_put(driver->owner);
+ module_put(owner);
spin_lock(&tty_files_lock);
list_del_init(&tty->tty_files);
@@ -1686,6 +1709,8 @@ int tty_release(struct inode *inode, struct file *filp)
int pty_master, tty_closing, o_tty_closing, do_sleep;
int idx;
char buf[64];
+ long timeout = 0;
+ int once = 1;
if (tty_paranoia_check(tty, inode, __func__))
return 0;
@@ -1766,11 +1791,18 @@ int tty_release(struct inode *inode, struct file *filp)
if (!do_sleep)
break;
- printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
- __func__, tty_name(tty, buf));
+ if (once) {
+ once = 0;
+ printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
+ __func__, tty_name(tty, buf));
+ }
tty_unlock_pair(tty, o_tty);
mutex_unlock(&tty_mutex);
- schedule();
+ schedule_timeout_killable(timeout);
+ if (timeout < 120 * HZ)
+ timeout = 2 * timeout + 1;
+ else
+ timeout = MAX_SCHEDULE_TIMEOUT;
}
/*
@@ -2163,8 +2195,9 @@ static int __tty_fasync(int fd, struct file *filp, int on)
}
get_pid(pid);
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
- retval = __f_setown(filp, pid, type, 0);
+ __f_setown(filp, pid, type, 0);
put_pid(pid);
+ retval = 0;
}
out:
return retval;
@@ -3018,6 +3051,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
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);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, do_SAK_work);