[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1322074412-10873-1-git-send-email-hskinnemoen@google.com>
Date: Wed, 23 Nov 2011 10:53:32 -0800
From: Havard Skinnemoen <hskinnemoen@...gle.com>
To: Jiri Slaby <jslaby@...e.cz>
Cc: Dave Jones <davej@...hat.com>,
Linux Kernel <linux-kernel@...r.kernel.org>,
Greg Kroah-Hartman <gregkh@...e.de>,
Jiri Slaby <jirislaby@...il.com>,
Alan Cox <alan@...ux.intel.com>,
Havard Skinnemoen <hskinnemoen@...gle.com>
Subject: [RFC] cdc-acm: Fix potential deadlock (lockdep warning)
We can't hold open_lock while calling tty_port_close_start, which takes
big_tty_mutex, because open_lock and big_tty_mutex are taken in the
opposite order when opening the device.
This means we need some other way of preventing the device state from
being freed before we're done cleaning up. Using the existing kref in
tty_port, we can let the TTY side and the USB side indenpendently signal
that they're done with the object, and free it when both are done.
Signed-off-by: Havard Skinnemoen <hskinnemoen@...gle.com>
---
How about something along these lines? I haven't tested it yet, and in fact I'm
a bit worried about the possible lack of symmetry between the tty_port_get() in
acm_tty_open() and the tty_port_put() in acm_tty_cleanup(). Is there any better
way to do this?
drivers/usb/class/cdc-acm.c | 42 ++++++++++++++++++++++--------------------
1 files changed, 22 insertions(+), 20 deletions(-)
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e8c564a..c09f3f7 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -62,9 +62,6 @@ static DEFINE_MUTEX(open_mutex);
#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
-static const struct tty_port_operations acm_port_ops = {
-};
-
/*
* Functions for ACM control messages.
*/
@@ -501,6 +498,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
if (acm_submit_read_urbs(acm, GFP_KERNEL))
goto bail_out;
+ /* Get a reference for the TTY device. Released on the last close */
+ tty_port_get(&acm->port);
+
set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
rv = tty_port_block_til_ready(&acm->port, tty, filp);
@@ -519,8 +519,9 @@ early_bail:
return -EIO;
}
-static void acm_tty_unregister(struct acm *acm)
+static void acm_port_destruct(struct tty_port *port)
{
+ struct acm *acm = container_of(port, struct acm, port);
int i;
tty_unregister_device(acm_tty_driver, acm->minor);
@@ -552,6 +553,12 @@ static void acm_port_down(struct acm *acm)
}
}
+static void acm_tty_cleanup(struct tty_struct *tty)
+{
+ struct acm *acm = tty->driver_data;
+ tty_port_put(&acm->port);
+}
+
static void acm_tty_hangup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
@@ -567,19 +574,10 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
/* Perform the closing process and see if we need to do the hardware
shutdown */
- if (!acm)
- return;
-
- mutex_lock(&open_mutex);
if (tty_port_close_start(&acm->port, tty, filp) == 0) {
- if (!acm->dev) {
- tty_port_tty_set(&acm->port, NULL);
- acm_tty_unregister(acm);
- tty->driver_data = NULL;
- }
- mutex_unlock(&open_mutex);
return;
}
+ mutex_lock(&open_mutex);
acm_port_down(acm);
tty_port_close_end(&acm->port, tty);
tty_port_tty_set(&acm->port, NULL);
@@ -788,6 +786,10 @@ static void acm_tty_set_termios(struct tty_struct *tty,
}
}
+static const struct tty_port_operations acm_port_ops = {
+ .destruct = acm_port_destruct,
+};
+
/*
* USB probe and disconnect routines.
*/
@@ -1206,6 +1208,9 @@ skip_countries:
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
+ /* Grab a reference for the USB device. Released on disconnect */
+ tty_port_get(&acm->port);
+
acm_set_control(acm, acm->ctrlout);
acm->line.dwDTERate = cpu_to_le32(9600);
@@ -1287,18 +1292,14 @@ static void acm_disconnect(struct usb_interface *intf)
usb_driver_release_interface(&acm_driver, intf == acm->control ?
acm->data : acm->control);
- if (acm->port.count == 0) {
- acm_tty_unregister(acm);
- mutex_unlock(&open_mutex);
- return;
- }
-
mutex_unlock(&open_mutex);
tty = tty_port_tty_get(&acm->port);
if (tty) {
tty_hangup(tty);
tty_kref_put(tty);
}
+
+ tty_port_put(&acm->port);
}
#ifdef CONFIG_PM
@@ -1596,6 +1597,7 @@ static struct usb_driver acm_driver = {
static const struct tty_operations acm_ops = {
.open = acm_tty_open,
.close = acm_tty_close,
+ .cleanup = acm_tty_cleanup,
.hangup = acm_tty_hangup,
.write = acm_tty_write,
.write_room = acm_tty_write_room,
--
1.7.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists