Skip to content

Commit 876ae50

Browse files
nomisgregkh
authored andcommitted
USB: ftdi_sio: fix race condition in TIOCMIWAIT, and abort of TIOCMIWAIT when the device is removed
There are two issues here, one is that the device is generating spurious very fast modem status line changes somewhere: CTS becomes high then low 18µs later: [121226.924373] ftdi_process_packet: prev rng=0 dsr=10 dcd=0 cts=6 [121226.924378] ftdi_process_packet: status=10 prev=00 diff=10 [121226.924382] ftdi_process_packet: now rng=0 dsr=10 dcd=0 cts=7 (wake_up_interruptible is called) [121226.924391] ftdi_process_packet: prev rng=0 dsr=10 dcd=0 cts=7 [121226.924394] ftdi_process_packet: status=00 prev=10 diff=10 [121226.924397] ftdi_process_packet: now rng=0 dsr=10 dcd=0 cts=8 (wake_up_interruptible is called) This wakes up the task in TIOCMIWAIT: [121226.924405] ftdi_ioctl: 19451 rng=0->0 dsr=10->10 dcd=0->0 cts=6->8 (wait from 20:51:46 returns and observes both changes) Which then calls TIOCMIWAIT again: 20:51:46.400239 ioctl(3, TIOCMIWAIT, 0x20) = 0 22:11:09.441818 ioctl(3, TIOCMGET, [TIOCM_DTR|TIOCM_RTS]) = 0 22:11:09.442812 ioctl(3, TIOCMIWAIT, 0x20) = -1 EIO (Input/output error) (the second wake_up_interruptible takes effect and an I/O error occurs) The other issue is that TIOCMIWAIT will wait forever (unless the task is interrupted) if the device is removed. This change removes the -EIO return that occurs if the counts don't appear to have changed. Multiple counts may have been processed as one or the waiting task may have started waiting after recording the current count. It adds a bool to indicate that the device has been removed so that TIOCMIWAIT doesn't wait forever, and wakes up any tasks so that they can return -EIO. Signed-off-by: Simon Arlott <[email protected]> Cc: stable <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent fca5430 commit 876ae50

File tree

1 file changed

+7
-5
lines changed

1 file changed

+7
-5
lines changed

drivers/usb/serial/ftdi_sio.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct ftdi_private {
7676
struct async_icount icount;
7777
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
7878
char prev_status; /* Used for TIOCMIWAIT */
79+
bool dev_gone; /* Used to abort TIOCMIWAIT */
7980
char transmit_empty; /* If transmitter is empty or not */
8081
struct usb_serial_port *port;
8182
__u16 interface; /* FT2232C, FT2232H or FT4232H port interface
@@ -1681,6 +1682,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
16811682
init_waitqueue_head(&priv->delta_msr_wait);
16821683

16831684
priv->flags = ASYNC_LOW_LATENCY;
1685+
priv->dev_gone = false;
16841686

16851687
if (quirk && quirk->port_probe)
16861688
quirk->port_probe(priv);
@@ -1839,6 +1841,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
18391841

18401842
dbg("%s", __func__);
18411843

1844+
priv->dev_gone = true;
1845+
wake_up_interruptible_all(&priv->delta_msr_wait);
1846+
18421847
remove_sysfs_attrs(port);
18431848

18441849
kref_put(&priv->kref, ftdi_sio_priv_release);
@@ -2397,15 +2402,12 @@ static int ftdi_ioctl(struct tty_struct *tty,
23972402
*/
23982403
case TIOCMIWAIT:
23992404
cprev = priv->icount;
2400-
while (1) {
2405+
while (!priv->dev_gone) {
24012406
interruptible_sleep_on(&priv->delta_msr_wait);
24022407
/* see if a signal did it */
24032408
if (signal_pending(current))
24042409
return -ERESTARTSYS;
24052410
cnow = priv->icount;
2406-
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
2407-
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
2408-
return -EIO; /* no change => error */
24092411
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
24102412
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
24112413
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
@@ -2414,7 +2416,7 @@ static int ftdi_ioctl(struct tty_struct *tty,
24142416
}
24152417
cprev = cnow;
24162418
}
2417-
/* not reached */
2419+
return -EIO;
24182420
break;
24192421
case TIOCSERGETLSR:
24202422
return get_lsr_info(port, (struct serial_struct __user *)arg);

0 commit comments

Comments
 (0)