summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2014-09-10 15:06:33 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-09-23 21:19:35 -0700
commitc545b66c6922b002b5fe224a6eaec58c913650b5 (patch)
tree6a979922aeb32f1100d0f6074b2c2d652d6bf931
parent01adc80706f80a583948db6768c5571204cd5f99 (diff)
downloadlinux-c545b66c6922b002b5fe224a6eaec58c913650b5.tar.bz2
tty: Serialize tcflow() with other tty flow control changes
Use newly-introduced tty->flow_lock to serialize updates to tty->flow_stopped (via tcflow()) and with concurrent tty flow control changes from other sources. Merge the storage for ->stopped and ->flow_stopped, now that both flags are serialized by ->flow_lock. The padding bits are necessary to force the compiler to allocate the type specified; otherwise, gcc will ignore the type specifier and allocate the minimum number of bytes necessary to store the bitfield. In turn, this would allow Alpha EV4 and EV5 cpus to corrupt adjacent byte storage because those cpus use RMW to store byte and short data. gcc versions < 4.7.2 will also corrupt storage adjacent to bitfields smaller than unsigned long on ia64, ppc64, hppa64 and sparc64, thus requiring more than unsigned int storage (which would otherwise be sufficient to workaround the Alpha non-atomic byte/short storage problem). Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/tty_ioctl.c8
-rw-r--r--include/linux/tty.h5
2 files changed, 9 insertions, 4 deletions
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index cd1285c3bfe9..dcf5c0af7de9 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -1177,16 +1177,20 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
return retval;
switch (arg) {
case TCOOFF:
+ spin_lock_irq(&tty->flow_lock);
if (!tty->flow_stopped) {
tty->flow_stopped = 1;
- stop_tty(tty);
+ __stop_tty(tty);
}
+ spin_unlock_irq(&tty->flow_lock);
break;
case TCOON:
+ spin_lock_irq(&tty->flow_lock);
if (tty->flow_stopped) {
tty->flow_stopped = 0;
- start_tty(tty);
+ __start_tty(tty);
}
+ spin_unlock_irq(&tty->flow_lock);
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
diff --git a/include/linux/tty.h b/include/linux/tty.h
index fd4148d3a261..d80b53642f2c 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -262,8 +262,9 @@ struct tty_struct {
unsigned long flags;
int count;
struct winsize winsize; /* winsize_mutex */
- int stopped; /* flow_lock */
- int flow_stopped;
+ unsigned long stopped:1, /* flow_lock */
+ flow_stopped:1,
+ unused:62;
int hw_stopped;
int packet;
unsigned char ctrl_status; /* ctrl_lock */