summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2009-04-30 10:06:19 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-05-18 16:34:34 -0700
commitc16199fcd341f10147eb2e29a60aed41ceca55b4 (patch)
tree788cfa212a260cf02d547ec1009ccf3141cad2de
parent192966f9f34028e04437e29afc0d936be2fc7052 (diff)
usb-serial: ftdi_sio: fix reference counting of ftdi_private
commit c45d63202fbaccef7ef7946c03f27f72c809b1cc upstream. This patch (as1238) adds proper reference counting for ftdi_sio's private data structure. Without it, the driver will free the structure while it is still in use if the user unplugs the serial device before closing the device file. The patch also replaces a slightly dangerous cancel_delayed_work/flush_scheduled_work pair with cancel_delayed_work_sync, which is always safer. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: Daniel Mack <daniel@caiaq.de> Tested-by: Daniel Mack <daniel@caiaq.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/serial/ftdi_sio.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index bb3143eb7ffd..5daa51703d5a 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -56,6 +56,7 @@ static __u16 vendor = FTDI_VID;
static __u16 product;
struct ftdi_private {
+ struct kref kref;
ftdi_chip_type_t chip_type;
/* type of device, either SIO or FT8U232AM */
int baud_base; /* baud base clock for divisor setting */
@@ -1352,6 +1353,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
return -ENOMEM;
}
+ kref_init(&priv->kref);
spin_lock_init(&priv->rx_lock);
spin_lock_init(&priv->tx_lock);
init_waitqueue_head(&priv->delta_msr_wait);
@@ -1468,6 +1470,13 @@ static void ftdi_shutdown(struct usb_serial *serial)
dbg("%s", __func__);
}
+static void ftdi_sio_priv_release(struct kref *k)
+{
+ struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
+
+ kfree(priv);
+}
+
static int ftdi_sio_port_remove(struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -1482,7 +1491,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
if (priv) {
usb_set_serial_port_data(port, NULL);
- kfree(priv);
+ kref_put(&priv->kref, ftdi_sio_priv_release);
}
return 0;
@@ -1547,7 +1556,8 @@ static int ftdi_open(struct tty_struct *tty,
dev_err(&port->dev,
"%s - failed submitting read urb, error %d\n",
__func__, result);
-
+ else
+ kref_get(&priv->kref);
return result;
} /* ftdi_open */
@@ -1589,11 +1599,11 @@ static void ftdi_close(struct tty_struct *tty,
mutex_unlock(&port->serial->disc_mutex);
/* cancel any scheduled reading */
- cancel_delayed_work(&priv->rx_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&priv->rx_work);
/* shutdown our bulk read */
usb_kill_urb(port->read_urb);
+ kref_put(&priv->kref, ftdi_sio_priv_release);
} /* ftdi_close */