diff options
author | Alan Cox <alan@linux.intel.com> | 2009-07-27 13:58:08 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-07-27 23:23:22 +0400 |
commit | b68f2fb9e73f46037fbeca5fbd4ae8a7ddd8ef6b (patch) | |
tree | 1149afd6c48dcb408f01e75ad250165522693188 /drivers/usb/serial/usb-serial.c | |
parent | 6a31d4aeab85a02f9a57ca37b935054393daa794 (diff) | |
download | linux-b68f2fb9e73f46037fbeca5fbd4ae8a7ddd8ef6b.tar.xz |
tty: Fix a USB serial crash/scribble
The port lock is used to protect the port state. However the port structure
is freed on a hangup, then the lock taken on a close. The right fix is to
drop the port on tty->shutdown() but we can't yet do that due to sleep v
non-sleeping rules. Instead do the next best thing and fix it up when we are
not in -rc season.
Reported-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Tested-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/usb/serial/usb-serial.c')
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index bd7581b3a48a..3c8923f62ed1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -340,6 +340,22 @@ static void serial_close(struct tty_struct *tty, struct file *filp) dbg("%s - port %d", __func__, port->number); + /* FIXME: + This leaves a very narrow race. Really we should do the + serial_do_free() on tty->shutdown(), but tty->shutdown can + be called from IRQ context and serial_do_free can sleep. + + The right fix is probably to make the tty free (which is rare) + and thus tty->shutdown() occur via a work queue and simplify all + the drivers that use it. + */ + if (tty_hung_up_p(filp)) { + /* serial_hangup already called serial_down at this point. + Another user may have already reopened the port but + serial_do_free is refcounted */ + serial_do_free(port); + return; + } if (tty_port_close_start(&port->port, tty, filp) == 0) return; @@ -355,7 +371,8 @@ static void serial_hangup(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; serial_do_down(port); tty_port_hangup(&port->port); - serial_do_free(port); + /* We must not free port yet - the USB serial layer depends on it's + continued existence */ } static int serial_write(struct tty_struct *tty, const unsigned char *buf, |