USB: ftdi_sio: fix race condition in TIOCMIWAIT, and abort of TIOCMIWAIT when the...
[linux-flexiantxendom0.git] / drivers / usb / serial / ftdi_sio.c
index cfc8e59..c4cf3f3 100644 (file)
@@ -76,6 +76,7 @@ struct ftdi_private {
        struct async_icount     icount;
        wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
        char prev_status;        /* Used for TIOCMIWAIT */
+       bool dev_gone;        /* Used to abort TIOCMIWAIT */
        char transmit_empty;    /* If transmitter is empty or not */
        struct usb_serial_port *port;
        __u16 interface;        /* FT2232C, FT2232H or FT4232H port interface
@@ -1679,6 +1680,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
        init_waitqueue_head(&priv->delta_msr_wait);
 
        priv->flags = ASYNC_LOW_LATENCY;
+       priv->dev_gone = false;
 
        if (quirk && quirk->port_probe)
                quirk->port_probe(priv);
@@ -1836,6 +1838,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
 
        dbg("%s", __func__);
 
+       priv->dev_gone = true;
+       wake_up_interruptible_all(&priv->delta_msr_wait);
+
        remove_sysfs_attrs(port);
 
        kref_put(&priv->kref, ftdi_sio_priv_release);
@@ -2390,15 +2395,12 @@ static int ftdi_ioctl(struct tty_struct *tty,
         */
        case TIOCMIWAIT:
                cprev = priv->icount;
-               while (1) {
+               while (!priv->dev_gone) {
                        interruptible_sleep_on(&priv->delta_msr_wait);
                        /* see if a signal did it */
                        if (signal_pending(current))
                                return -ERESTARTSYS;
                        cnow = priv->icount;
-                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
-                           cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
-                               return -EIO; /* no change => error */
                        if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
                            ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
                            ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
@@ -2407,7 +2409,7 @@ static int ftdi_ioctl(struct tty_struct *tty,
                        }
                        cprev = cnow;
                }
-               /* not reached */
+               return -EIO;
                break;
        case TIOCSERGETLSR:
                return get_lsr_info(port, (struct serial_struct __user *)arg);