tty: Serialize tcflow() with other tty flow control changes
authorPeter Hurley <peter@hurleysoftware.com>
Wed, 10 Sep 2014 19:06:33 +0000 (15:06 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Sep 2014 04:19:35 +0000 (21:19 -0700)
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>
drivers/tty/tty_ioctl.c
include/linux/tty.h

index cd1285c3bfe9829b7d4d2ed8d8c3c14272152dbe..dcf5c0af7de9adc8e3c46e20ee4c55aaa7926fe0 100644 (file)
@@ -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)
index fd4148d3a261dbc96f2ccc4f1a0541319f50b691..d80b53642f2cb7f7e79b86154b59d1f012596ab7 100644 (file)
@@ -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 */