lkml.org 
[lkml]   [2014]   [Sep]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[RFC] tty: Override and virtualize flow control for tty_send_xchar()
Date
I'm offering this patch as an RFC rather than a PATCH because I think
it's added complexity is not worth the hassle, given that it is only
needed for drivers which don't support the send_xchar() method.

But it does complete the flow control patch series, by fixing a
problem with flow control where a driver that does not support the
send_xchar() method can accidentally restore a stopped terminal,
even though other flow control has restarted the terminal.

Regards,
Peter Hurley

--- >% ---
Subject: [RFC] tty: Override and virtualize flow control for
tty_send_xchar()

When sending START/STOP from tcflow(TCIxxx), if the tty driver does
not support the send_xchar() method, tty_send_xchar() uses the
normal driver write() routine. Because the tty may stopped,
tty_send_xchar() must override the flow control state and restore
it after START/STOP has been written.

Add file-scope helper, force_start_tty(), which saves the current
flow control state, and starts the tty if necessary (but not if
the output flow has been stopped by tcflow(TCOOFF)). While the
flow control state is overridden, stop_tty() and start_tty()
continue to track the virtual flow control state without notifying
the driver (so the actual flow state does not change).

If, while the override is on, tcflow(TCOOFF) turns off flow control,
the override is ended and the actual flow control is stopped.

Also, add file-scope helper, restore_tty_stopped(), which restores
the actual flow control state to the tracked virtual flow control
state, stopping the tty if necessary.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
---
drivers/tty/tty_io.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++------
include/linux/tty.h | 4 +++-
2 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 54e359b..11461ba 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -937,6 +937,13 @@ void no_tty(void)

void __stop_tty(struct tty_struct *tty)
{
+ if (tty->override_stopped) {
+ if (!tty->flow_stopped) {
+ tty->virt_stopped = 1;
+ return;
+ }
+ tty->override_stopped = 0;
+ }
if (tty->stopped)
return;
tty->stopped = 1;
@@ -968,6 +975,13 @@ EXPORT_SYMBOL(stop_tty);

void __start_tty(struct tty_struct *tty)
{
+ if (tty->override_stopped) {
+ if (!tty->flow_stopped) {
+ tty->virt_stopped = 0;
+ return;
+ }
+ tty->override_stopped = 0;
+ }
if (!tty->stopped || tty->flow_stopped)
return;
tty->stopped = 0;
@@ -986,6 +1000,35 @@ void start_tty(struct tty_struct *tty)
}
EXPORT_SYMBOL(start_tty);

+/* Used by tty_send_xchar() to force the tty to start */
+static void force_start_tty(struct tty_struct *tty)
+{
+ spin_lock_irq(&tty->flow_lock);
+ if (!tty->flow_stopped) {
+ tty->override_stopped = 1;
+ tty->virt_stopped = tty->stopped;
+ if (tty->stopped) {
+ tty->stopped = 0;
+ if (tty->ops->start)
+ tty->ops->start(tty);
+ tty_wakeup(tty);
+ }
+ }
+ spin_unlock_irq(&tty->flow_lock);
+}
+
+static void restore_tty_stopped(struct tty_struct *tty)
+{
+ spin_lock_irq(&tty->flow_lock);
+ if (tty->override_stopped) {
+ tty->override_stopped = 0;
+ tty->stopped = tty->virt_stopped;
+ if (tty->stopped && tty->ops->stop)
+ tty->ops->stop(tty);
+ }
+ spin_unlock_irq(&tty->flow_lock);
+}
+
/* We limit tty time update visibility to every 8 seconds or so. */
static void tty_update_time(struct timespec *time)
{
@@ -1241,8 +1284,6 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,

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;
@@ -1251,11 +1292,10 @@ int tty_send_xchar(struct tty_struct *tty, char ch)
if (tty_write_lock(tty, 0) < 0)
return -ERESTARTSYS;

- if (was_stopped)
- start_tty(tty);
+ force_start_tty(tty);
tty->ops->write(tty, &ch, 1);
- if (was_stopped)
- stop_tty(tty);
+ restore_tty_stopped(tty);
+
tty_write_unlock(tty);
return 0;
}
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 7a0a796..9c79497 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -264,7 +264,9 @@ struct tty_struct {
struct winsize winsize; /* winsize_mutex */
unsigned long stopped:1, /* flow_lock */
flow_stopped:1,
- unused:62;
+ virt_stopped:1,
+ override_stopped:1,
+ unused:60;
int hw_stopped;
unsigned long ctrl_status:8, /* ctrl_lock */
packet:1,
--
2.1.0


\
 
 \ /
  Last update: 2014-09-10 23:41    [W:0.018 / U:0.416 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site