Merge tag 'kvm-3.10-1' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[firefly-linux-kernel-4.4.55.git] / drivers / tty / tty_ioctl.c
index d58b92cc187cc21b05229ee9cdd7e936519fffb8..3500d41141472394f364b51952f599f31990bd19 100644 (file)
@@ -106,6 +106,7 @@ void tty_throttle(struct tty_struct *tty)
        if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
            tty->ops->throttle)
                tty->ops->throttle(tty);
+       tty->flow_change = 0;
        mutex_unlock(&tty->termios_mutex);
 }
 EXPORT_SYMBOL(tty_throttle);
@@ -129,10 +130,73 @@ void tty_unthrottle(struct tty_struct *tty)
        if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
            tty->ops->unthrottle)
                tty->ops->unthrottle(tty);
+       tty->flow_change = 0;
        mutex_unlock(&tty->termios_mutex);
 }
 EXPORT_SYMBOL(tty_unthrottle);
 
+/**
+ *     tty_throttle_safe       -       flow control
+ *     @tty: terminal
+ *
+ *     Similar to tty_throttle() but will only attempt throttle
+ *     if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental
+ *     throttle due to race conditions when throttling is conditional
+ *     on factors evaluated prior to throttling.
+ *
+ *     Returns 0 if tty is throttled (or was already throttled)
+ */
+
+int tty_throttle_safe(struct tty_struct *tty)
+{
+       int ret = 0;
+
+       mutex_lock(&tty->termios_mutex);
+       if (!test_bit(TTY_THROTTLED, &tty->flags)) {
+               if (tty->flow_change != TTY_THROTTLE_SAFE)
+                       ret = 1;
+               else {
+                       set_bit(TTY_THROTTLED, &tty->flags);
+                       if (tty->ops->throttle)
+                               tty->ops->throttle(tty);
+               }
+       }
+       mutex_unlock(&tty->termios_mutex);
+
+       return ret;
+}
+
+/**
+ *     tty_unthrottle_safe     -       flow control
+ *     @tty: terminal
+ *
+ *     Similar to tty_unthrottle() but will only attempt unthrottle
+ *     if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental
+ *     unthrottle due to race conditions when unthrottling is conditional
+ *     on factors evaluated prior to unthrottling.
+ *
+ *     Returns 0 if tty is unthrottled (or was already unthrottled)
+ */
+
+int tty_unthrottle_safe(struct tty_struct *tty)
+{
+       int ret = 0;
+
+       mutex_lock(&tty->termios_mutex);
+       if (test_bit(TTY_THROTTLED, &tty->flags)) {
+               if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
+                       ret = 1;
+               else {
+                       clear_bit(TTY_THROTTLED, &tty->flags);
+                       if (tty->ops->unthrottle)
+                               tty->ops->unthrottle(tty);
+               }
+       }
+       mutex_unlock(&tty->termios_mutex);
+
+       return ret;
+}
+
 /**
  *     tty_wait_until_sent     -       wait for I/O to finish
  *     @tty: tty we are waiting for
@@ -414,34 +478,6 @@ void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
 }
 EXPORT_SYMBOL_GPL(tty_encode_baud_rate);
 
-/**
- *     tty_get_baud_rate       -       get tty bit rates
- *     @tty: tty to query
- *
- *     Returns the baud rate as an integer for this terminal. The
- *     termios lock must be held by the caller and the terminal bit
- *     flags may be updated.
- *
- *     Locking: none
- */
-
-speed_t tty_get_baud_rate(struct tty_struct *tty)
-{
-       speed_t baud = tty_termios_baud_rate(&tty->termios);
-
-       if (baud == 38400 && tty->alt_speed) {
-               if (!tty->warned) {
-                       printk(KERN_WARNING "Use of setserial/setrocket to "
-                                           "set SPD_* flags is deprecated\n");
-                       tty->warned = 1;
-               }
-               baud = tty->alt_speed;
-       }
-
-       return baud;
-}
-EXPORT_SYMBOL(tty_get_baud_rate);
-
 /**
  *     tty_termios_copy_hw     -       copy hardware settings
  *     @new: New termios
@@ -1086,14 +1122,12 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
 }
 EXPORT_SYMBOL_GPL(tty_mode_ioctl);
 
-int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
+
+/* Caller guarantees ldisc reference is held */
+static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg)
 {
-       struct tty_ldisc *ld;
-       int retval = tty_check_change(tty);
-       if (retval)
-               return retval;
+       struct tty_ldisc *ld = tty->ldisc;
 
-       ld = tty_ldisc_ref_wait(tty);
        switch (arg) {
        case TCIFLUSH:
                if (ld && ld->ops->flush_buffer) {
@@ -1111,12 +1145,24 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
                tty_driver_flush_buffer(tty);
                break;
        default:
-               tty_ldisc_deref(ld);
                return -EINVAL;
        }
-       tty_ldisc_deref(ld);
        return 0;
 }
+
+int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
+{
+       struct tty_ldisc *ld;
+       int retval = tty_check_change(tty);
+       if (retval)
+               return retval;
+
+       ld = tty_ldisc_ref_wait(tty);
+       retval = __tty_perform_flush(tty, arg);
+       if (ld)
+               tty_ldisc_deref(ld);
+       return retval;
+}
 EXPORT_SYMBOL_GPL(tty_perform_flush);
 
 int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
@@ -1155,7 +1201,7 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
                }
                return 0;
        case TCFLSH:
-               return tty_perform_flush(tty, arg);
+               return __tty_perform_flush(tty, arg);
        default:
                /* Try the mode commands */
                return tty_mode_ioctl(tty, file, cmd, arg);