mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Simon Arlott <simon@fire.lp0.eu>
To: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Greg KH <gregkh@linuxfoundation.org>
Cc: USB list <linux-usb@vger.kernel.org>
Subject: ftdi_sio: fix race condition in TIOCMIWAIT, and abort of TIOCMIWAIT when the device is removed
Date: Mon, 26 Mar 2012 23:27:59 +0100	[thread overview]
Message-ID: <4F70ED6F.6070005@simon.arlott.org.uk> (raw)

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 <simon@fire.lp0.eu>
---
 drivers/usb/serial/ftdi_sio.c |   12 +++++++-----
 1 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index d260b79..fc9a901 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -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
@@ -1663,6 +1664,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);
@@ -1819,6 +1821,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);
@@ -2376,15 +2381,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)) ||
@@ -2393,7 +2395,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);
-- 
1.7.4.1

-- 
Simon Arlott

                 reply	other threads:[~2012-03-26 22:28 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4F70ED6F.6070005@simon.arlott.org.uk \
    --to=simon@fire.lp0.eu \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®