From: Greg Kroah-Hartman Date: Sat, 23 Feb 2013 01:18:09 +0000 (-0800) Subject: 3.8-stable patches X-Git-Tag: v3.7.10~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a5225c01445139956a90bbd41e6bfaea590915ba;p=thirdparty%2Fkernel%2Fstable-queue.git 3.8-stable patches added patches: serial_core-fix-type-definition-for-port_brcm_trumanage.patch serial-imx-fix-recursive-locking-bug.patch tty-prevent-deadlock-in-n_gsm-driver.patch tty-set_termios-set_termiox-should-not-return-eintr.patch usb-serial-fix-null-pointer-dereferences-on-disconnect.patch --- diff --git a/queue-3.8/serial-imx-fix-recursive-locking-bug.patch b/queue-3.8/serial-imx-fix-recursive-locking-bug.patch new file mode 100644 index 00000000000..326c5bba23a --- /dev/null +++ b/queue-3.8/serial-imx-fix-recursive-locking-bug.patch @@ -0,0 +1,118 @@ +From 677fe555cbfb188af58cce105f4dae9505e58c31 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Thu, 14 Feb 2013 21:01:06 +0100 +Subject: serial: imx: Fix recursive locking bug + +From: Thomas Gleixner + +commit 677fe555cbfb188af58cce105f4dae9505e58c31 upstream. + +commit 9ec1882df2 (tty: serial: imx: console write routing is unsafe +on SMP) introduced a recursive locking bug in imx_console_write(). + +The callchain is: + +imx_rxint() + spin_lock_irqsave(&sport->port.lock,flags); + ... + uart_handle_sysrq_char(); + sysrq_function(); + printk(); + imx_console_write(); + spin_lock_irqsave(&sport->port.lock,flags); <--- DEAD + +The bad news is that the kernel debugging facilities can dectect the +problem, but the printks never surface on the serial console for +obvious reasons. + +There is a similar issue with oops_in_progress. If the kernel crashes +we really don't want to be stuck on the lock and unable to tell what +happened. + +In general most UP originated drivers miss these checks and nobody +ever notices because CONFIG_PROVE_LOCKING seems to be still ignored by +a large number of developers. + +The solution is to avoid locking in the sysrq case and trylock in the +oops_in_progress case. + +This scheme is used in other drivers as well and it would be nice if +we could move this to a common place, so the usual copy/paste/modify +bugs can be avoided. + +Now there is another issue with this scheme: + +CPU0 CPU1 +printk() + rxint() + sysrq_detection() -> sets port->sysrq + return from interrupt + console_write() + if (port->sysrq) + avoid locking + +port->sysrq is reset with the next receive character. So as long as +the port->sysrq is not reset and this can take an endless amount of +time if after the break no futher receive character follows, all +console writes happen unlocked. + +While the current writer is protected against other console writers by +the console sem, it's unprotected against open/close or other +operations which fiddle with the port. That's what the above mentioned +commit tried to solve. + +That's an issue in all drivers which use that scheme and unfortunately +there is no easy workaround. The only solution is to have a separate +indicator port->sysrq_cpu. uart_handle_sysrq_char() then sets it to +smp_processor_id() before calling into handle_sysrq() and resets it to +-1 after that. Then change the locking check to: + + if (port->sysrq_cpu == smp_processor_id()) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock_irqsave(port->lock, flags); + else + spin_lock_irqsave(port->lock, flags); + +That would force all other cpus into the spin_lock path. Problem +solved, but that's way beyond the scope of this fix and really wants +to be implemented in a common function which calls the uart specific +write function to avoid another gazillion of hard to debug +copy/paste/modify bugs. + +Reported-and-tested-by: Tim Sander +Signed-off-by: Thomas Gleixner +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/tty/serial/imx.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/tty/serial/imx.c ++++ b/drivers/tty/serial/imx.c +@@ -1213,8 +1213,14 @@ imx_console_write(struct console *co, co + struct imx_port_ucrs old_ucr; + unsigned int ucr1; + unsigned long flags; ++ int locked = 1; + +- spin_lock_irqsave(&sport->port.lock, flags); ++ if (sport->port.sysrq) ++ locked = 0; ++ else if (oops_in_progress) ++ locked = spin_trylock_irqsave(&sport->port.lock, flags); ++ else ++ spin_lock_irqsave(&sport->port.lock, flags); + + /* + * First, save UCR1/2/3 and then disable interrupts +@@ -1241,7 +1247,8 @@ imx_console_write(struct console *co, co + + imx_port_ucrs_restore(&sport->port, &old_ucr); + +- spin_unlock_irqrestore(&sport->port.lock, flags); ++ if (locked) ++ spin_unlock_irqrestore(&sport->port.lock, flags); + } + + /* diff --git a/queue-3.8/serial_core-fix-type-definition-for-port_brcm_trumanage.patch b/queue-3.8/serial_core-fix-type-definition-for-port_brcm_trumanage.patch new file mode 100644 index 00000000000..10d44d15701 --- /dev/null +++ b/queue-3.8/serial_core-fix-type-definition-for-port_brcm_trumanage.patch @@ -0,0 +1,31 @@ +From 85f024401bf80746ae08b7fd5809a9b16accf0b1 Mon Sep 17 00:00:00 2001 +From: Michael Chan +Date: Tue, 29 Jan 2013 17:54:44 -0800 +Subject: serial_core: Fix type definition for PORT_BRCM_TRUMANAGE. + +From: Michael Chan + +commit 85f024401bf80746ae08b7fd5809a9b16accf0b1 upstream. + +It was mistakenly defined to be 24 instead of the next higher number 25. + +Reported-by: Alexander Shishkin +Cc: Stephen Hurd +Signed-off-by: Michael Chan +Signed-off-by: Greg Kroah-Hartman + +--- + include/uapi/linux/serial_core.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -50,7 +50,7 @@ + #define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */ + #define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */ + #define PORT_XR17V35X 24 /* Exar XR17V35x UARTs */ +-#define PORT_BRCM_TRUMANAGE 24 ++#define PORT_BRCM_TRUMANAGE 25 + #define PORT_MAX_8250 25 /* max port ID */ + + /* diff --git a/queue-3.8/series b/queue-3.8/series index a1587e63bf5..b2772513b15 100644 --- a/queue-3.8/series +++ b/queue-3.8/series @@ -12,3 +12,8 @@ workqueue-un-gpl-function-delayed_work_timer_fn.patch alsa-ali5451-remove-irq-enabling-in-pointer-callback.patch alsa-rme32.c-irq-enabling-after-spin_lock_irq.patch alsa-aloop-fix-oops-while-pm-resume.patch +tty-prevent-deadlock-in-n_gsm-driver.patch +tty-set_termios-set_termiox-should-not-return-eintr.patch +usb-serial-fix-null-pointer-dereferences-on-disconnect.patch +serial-imx-fix-recursive-locking-bug.patch +serial_core-fix-type-definition-for-port_brcm_trumanage.patch diff --git a/queue-3.8/tty-prevent-deadlock-in-n_gsm-driver.patch b/queue-3.8/tty-prevent-deadlock-in-n_gsm-driver.patch new file mode 100644 index 00000000000..8a30f3f9797 --- /dev/null +++ b/queue-3.8/tty-prevent-deadlock-in-n_gsm-driver.patch @@ -0,0 +1,195 @@ +From 4d9b109060f690f5c835130ff54165ae157b3087 Mon Sep 17 00:00:00 2001 +From: Dirkjan Bussink +Date: Wed, 30 Jan 2013 11:44:50 +0100 +Subject: tty: Prevent deadlock in n_gsm driver + +From: Dirkjan Bussink + +commit 4d9b109060f690f5c835130ff54165ae157b3087 upstream. + +This change fixes a deadlock when the multiplexer is closed while there +are still client side ports open. + +When the multiplexer is closed and there are active tty's it tries to +close them with tty_vhangup. This has a problem though, because +tty_vhangup needs the tty_lock. This patch changes it to unlock the +tty_lock before attempting the hangup and relocks afterwards. The +additional call to tty_port_tty_set is needed because otherwise the +port stays active because of the reference counter. + +This change also exposed another problem that other code paths don't +expect that the multiplexer could have been closed. This patch also adds +checks for these cases in the gsmtty_ class of function that could be +called. + +The documentation explicitly states that "first close all virtual ports +before closing the physical port" but we've found this to not always +reality in our field situations. The GPRS / UTMS modem sometimes crashes +and needs a power cycle in that case which means cleanly shutting down +everything is not always possible. This change makes it much more robust +for our situation where at least the system is recoverable with this patch +and doesn't hang in a deadlock situation inside the kernel. + +The patch is against the long term support kernel (3.4.27) and should +apply cleanly to more recent branches. Tested with a Telit GE864-QUADV2 +and Telit HE910 modem. + +Signed-off-by: Dirkjan Bussink +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/tty/n_gsm.c | 42 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 41 insertions(+), 1 deletion(-) + +--- a/drivers/tty/n_gsm.c ++++ b/drivers/tty/n_gsm.c +@@ -1689,6 +1689,8 @@ static inline void dlci_put(struct gsm_d + tty_port_put(&dlci->port); + } + ++static void gsm_destroy_network(struct gsm_dlci *dlci); ++ + /** + * gsm_dlci_release - release DLCI + * @dlci: DLCI to destroy +@@ -1702,9 +1704,19 @@ static void gsm_dlci_release(struct gsm_ + { + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { ++ mutex_lock(&dlci->mutex); ++ gsm_destroy_network(dlci); ++ mutex_unlock(&dlci->mutex); ++ ++ /* tty_vhangup needs the tty_lock, so unlock and ++ relock after doing the hangup. */ ++ tty_unlock(tty); + tty_vhangup(tty); ++ tty_lock(tty); ++ tty_port_tty_set(&dlci->port, NULL); + tty_kref_put(tty); + } ++ dlci->state = DLCI_CLOSED; + dlci_put(dlci); + } + +@@ -2947,6 +2959,8 @@ static void gsmtty_close(struct tty_stru + + if (dlci == NULL) + return; ++ if (dlci->state == DLCI_CLOSED) ++ return; + mutex_lock(&dlci->mutex); + gsm_destroy_network(dlci); + mutex_unlock(&dlci->mutex); +@@ -2965,6 +2979,8 @@ out: + static void gsmtty_hangup(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return; + tty_port_hangup(&dlci->port); + gsm_dlci_begin_close(dlci); + } +@@ -2972,9 +2988,12 @@ static void gsmtty_hangup(struct tty_str + static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, + int len) + { ++ int sent; + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + /* Stuff the bytes into the fifo queue */ +- int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); ++ sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); + /* Need to kick the channel */ + gsm_dlci_data_kick(dlci); + return sent; +@@ -2983,18 +3002,24 @@ static int gsmtty_write(struct tty_struc + static int gsmtty_write_room(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + return TX_SIZE - kfifo_len(dlci->fifo); + } + + static int gsmtty_chars_in_buffer(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + return kfifo_len(dlci->fifo); + } + + static void gsmtty_flush_buffer(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return; + /* Caution needed: If we implement reliable transport classes + then the data being transmitted can't simply be junked once + it has first hit the stack. Until then we can just blow it +@@ -3013,6 +3038,8 @@ static void gsmtty_wait_until_sent(struc + static int gsmtty_tiocmget(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + return dlci->modem_rx; + } + +@@ -3022,6 +3049,8 @@ static int gsmtty_tiocmset(struct tty_st + struct gsm_dlci *dlci = tty->driver_data; + unsigned int modem_tx = dlci->modem_tx; + ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + modem_tx &= ~clear; + modem_tx |= set; + +@@ -3040,6 +3069,8 @@ static int gsmtty_ioctl(struct tty_struc + struct gsm_netconfig nc; + int index; + ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + switch (cmd) { + case GSMIOC_ENABLE_NET: + if (copy_from_user(&nc, (void __user *)arg, sizeof(nc))) +@@ -3066,6 +3097,9 @@ static int gsmtty_ioctl(struct tty_struc + + static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) + { ++ struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return; + /* For the moment its fixed. In actual fact the speed information + for the virtual channel can be propogated in both directions by + the RPN control message. This however rapidly gets nasty as we +@@ -3077,6 +3111,8 @@ static void gsmtty_set_termios(struct tt + static void gsmtty_throttle(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return; + if (tty->termios.c_cflag & CRTSCTS) + dlci->modem_tx &= ~TIOCM_DTR; + dlci->throttled = 1; +@@ -3087,6 +3123,8 @@ static void gsmtty_throttle(struct tty_s + static void gsmtty_unthrottle(struct tty_struct *tty) + { + struct gsm_dlci *dlci = tty->driver_data; ++ if (dlci->state == DLCI_CLOSED) ++ return; + if (tty->termios.c_cflag & CRTSCTS) + dlci->modem_tx |= TIOCM_DTR; + dlci->throttled = 0; +@@ -3098,6 +3136,8 @@ static int gsmtty_break_ctl(struct tty_s + { + struct gsm_dlci *dlci = tty->driver_data; + int encode = 0; /* Off */ ++ if (dlci->state == DLCI_CLOSED) ++ return -EINVAL; + + if (state == -1) /* "On indefinitely" - we can't encode this + properly */ diff --git a/queue-3.8/tty-set_termios-set_termiox-should-not-return-eintr.patch b/queue-3.8/tty-set_termios-set_termiox-should-not-return-eintr.patch new file mode 100644 index 00000000000..aaeef6669b0 --- /dev/null +++ b/queue-3.8/tty-set_termios-set_termiox-should-not-return-eintr.patch @@ -0,0 +1,77 @@ +From 183d95cdd834381c594d3aa801c1f9f9c0c54fa9 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +Date: Tue, 29 Jan 2013 20:07:41 +0100 +Subject: tty: set_termios/set_termiox should not return -EINTR + +From: Oleg Nesterov + +commit 183d95cdd834381c594d3aa801c1f9f9c0c54fa9 upstream. + +See https://bugzilla.redhat.com/show_bug.cgi?id=904907 +read command causes bash to abort with double free or corruption (out). + +A simple test-case from Roman: + + // Compile the reproducer and send sigchld ti that process. + // EINTR occurs even if SA_RESTART flag is set. + + void handler(int sig) + { + } + + main() + { + struct sigaction act; + act.sa_handler = handler; + act.sa_flags = SA_RESTART; + sigaction (SIGCHLD, &act, 0); + struct termio ttp; + ioctl(0, TCGETA, &ttp); + while(1) + { + if (ioctl(0, TCSETAW, ttp) < 0) + { + if (errno == EINTR) + { + fprintf(stderr, "BUG!"); return(1); + } + } + } + } + +Change set_termios/set_termiox to return -ERESTARTSYS to fix this +particular problem. + +I didn't dare to change other EINTR's in drivers/tty/, but they look +equally wrong. + +Reported-by: Roman Rakus +Reported-by: Lingzhu Xiang +Signed-off-by: Oleg Nesterov +Cc: Jiri Slaby +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/tty/tty_ioctl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/tty/tty_ioctl.c ++++ b/drivers/tty/tty_ioctl.c +@@ -617,7 +617,7 @@ static int set_termios(struct tty_struct + if (opt & TERMIOS_WAIT) { + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) +- return -EINTR; ++ return -ERESTARTSYS; + } + + tty_set_termios(tty, &tmp_termios); +@@ -684,7 +684,7 @@ static int set_termiox(struct tty_struct + if (opt & TERMIOS_WAIT) { + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) +- return -EINTR; ++ return -ERESTARTSYS; + } + + mutex_lock(&tty->termios_mutex); diff --git a/queue-3.8/usb-serial-fix-null-pointer-dereferences-on-disconnect.patch b/queue-3.8/usb-serial-fix-null-pointer-dereferences-on-disconnect.patch new file mode 100644 index 00000000000..4f93ab28dc7 --- /dev/null +++ b/queue-3.8/usb-serial-fix-null-pointer-dereferences-on-disconnect.patch @@ -0,0 +1,238 @@ +From b2ca699076573c94fee9a73cb0d8645383b602a0 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Wed, 13 Feb 2013 17:53:28 +0100 +Subject: USB: serial: fix null-pointer dereferences on disconnect + +From: Johan Hovold + +commit b2ca699076573c94fee9a73cb0d8645383b602a0 upstream. + +Make sure serial-driver dtr_rts is called with disc_mutex held after +checking the disconnected flag. + +Due to a bug in the tty layer, dtr_rts may get called after a device has +been disconnected and the tty-device unregistered. Some drivers have had +individual checks for disconnect to make sure the disconnected interface +was not accessed, but this should really be handled in usb-serial core +(at least until the long-standing tty-bug has been fixed). + +Note that the problem has been made more acute with commit 0998d0631001 +("device-core: Ensure drvdata = NULL when no driver is bound") as the +port data is now also NULL when dtr_rts is called resulting in further +oopses. + +Reported-by: Chris Ruehl +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/serial/ftdi_sio.c | 20 +++++++++----------- + drivers/usb/serial/mct_u232.c | 22 +++++++++------------- + drivers/usb/serial/quatech2.c | 18 ++++++++---------- + drivers/usb/serial/sierra.c | 8 +------- + drivers/usb/serial/ssu100.c | 19 ++++++++----------- + drivers/usb/serial/usb-serial.c | 14 ++++++++++++-- + drivers/usb/serial/usb_wwan.c | 8 +++----- + 7 files changed, 50 insertions(+), 59 deletions(-) + +--- a/drivers/usb/serial/ftdi_sio.c ++++ b/drivers/usb/serial/ftdi_sio.c +@@ -1886,24 +1886,22 @@ static void ftdi_dtr_rts(struct usb_seri + { + struct ftdi_private *priv = usb_get_serial_port_data(port); + +- mutex_lock(&port->serial->disc_mutex); +- if (!port->serial->disconnected) { +- /* Disable flow control */ +- if (!on && usb_control_msg(port->serial->dev, ++ /* Disable flow control */ ++ if (!on) { ++ if (usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, priv->interface, NULL, 0, + WDR_TIMEOUT) < 0) { +- dev_err(&port->dev, "error from flowcontrol urb\n"); ++ dev_err(&port->dev, "error from flowcontrol urb\n"); + } +- /* drop RTS and DTR */ +- if (on) +- set_mctrl(port, TIOCM_DTR | TIOCM_RTS); +- else +- clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +- mutex_unlock(&port->serial->disc_mutex); ++ /* drop RTS and DTR */ ++ if (on) ++ set_mctrl(port, TIOCM_DTR | TIOCM_RTS); ++ else ++ clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } + + /* +--- a/drivers/usb/serial/mct_u232.c ++++ b/drivers/usb/serial/mct_u232.c +@@ -499,19 +499,15 @@ static void mct_u232_dtr_rts(struct usb_ + unsigned int control_state; + struct mct_u232_private *priv = usb_get_serial_port_data(port); + +- mutex_lock(&port->serial->disc_mutex); +- if (!port->serial->disconnected) { +- /* drop DTR and RTS */ +- spin_lock_irq(&priv->lock); +- if (on) +- priv->control_state |= TIOCM_DTR | TIOCM_RTS; +- else +- priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); +- control_state = priv->control_state; +- spin_unlock_irq(&priv->lock); +- mct_u232_set_modem_ctrl(port, control_state); +- } +- mutex_unlock(&port->serial->disc_mutex); ++ spin_lock_irq(&priv->lock); ++ if (on) ++ priv->control_state |= TIOCM_DTR | TIOCM_RTS; ++ else ++ priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); ++ control_state = priv->control_state; ++ spin_unlock_irq(&priv->lock); ++ ++ mct_u232_set_modem_ctrl(port, control_state); + } + + static void mct_u232_close(struct usb_serial_port *port) +--- a/drivers/usb/serial/quatech2.c ++++ b/drivers/usb/serial/quatech2.c +@@ -945,19 +945,17 @@ static void qt2_dtr_rts(struct usb_seria + struct usb_device *dev = port->serial->dev; + struct qt2_port_private *port_priv = usb_get_serial_port_data(port); + +- mutex_lock(&port->serial->disc_mutex); +- if (!port->serial->disconnected) { +- /* Disable flow control */ +- if (!on && qt2_setregister(dev, port_priv->device_port, ++ /* Disable flow control */ ++ if (!on) { ++ if (qt2_setregister(dev, port_priv->device_port, + UART_MCR, 0) < 0) + dev_warn(&port->dev, "error from flowcontrol urb\n"); +- /* drop RTS and DTR */ +- if (on) +- update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0); +- else +- update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS); + } +- mutex_unlock(&port->serial->disc_mutex); ++ /* drop RTS and DTR */ ++ if (on) ++ update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0); ++ else ++ update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS); + } + + static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch) +--- a/drivers/usb/serial/sierra.c ++++ b/drivers/usb/serial/sierra.c +@@ -861,19 +861,13 @@ static int sierra_open(struct tty_struct + + static void sierra_dtr_rts(struct usb_serial_port *port, int on) + { +- struct usb_serial *serial = port->serial; + struct sierra_port_private *portdata; + + portdata = usb_get_serial_port_data(port); + portdata->rts_state = on; + portdata->dtr_state = on; + +- if (serial->dev) { +- mutex_lock(&serial->disc_mutex); +- if (!serial->disconnected) +- sierra_send_setup(port); +- mutex_unlock(&serial->disc_mutex); +- } ++ sierra_send_setup(port); + } + + static int sierra_startup(struct usb_serial *serial) +--- a/drivers/usb/serial/ssu100.c ++++ b/drivers/usb/serial/ssu100.c +@@ -506,19 +506,16 @@ static void ssu100_dtr_rts(struct usb_se + { + struct usb_device *dev = port->serial->dev; + +- mutex_lock(&port->serial->disc_mutex); +- if (!port->serial->disconnected) { +- /* Disable flow control */ +- if (!on && +- ssu100_setregister(dev, 0, UART_MCR, 0) < 0) ++ /* Disable flow control */ ++ if (!on) { ++ if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0) + dev_err(&port->dev, "error from flowcontrol urb\n"); +- /* drop RTS and DTR */ +- if (on) +- set_mctrl(dev, TIOCM_DTR | TIOCM_RTS); +- else +- clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS); + } +- mutex_unlock(&port->serial->disc_mutex); ++ /* drop RTS and DTR */ ++ if (on) ++ set_mctrl(dev, TIOCM_DTR | TIOCM_RTS); ++ else ++ clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS); + } + + static void ssu100_update_msr(struct usb_serial_port *port, u8 msr) +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -688,10 +688,20 @@ static int serial_carrier_raised(struct + static void serial_dtr_rts(struct tty_port *port, int on) + { + struct usb_serial_port *p = container_of(port, struct usb_serial_port, port); +- struct usb_serial_driver *drv = p->serial->type; ++ struct usb_serial *serial = p->serial; ++ struct usb_serial_driver *drv = serial->type; + +- if (drv->dtr_rts) ++ if (!drv->dtr_rts) ++ return; ++ /* ++ * Work-around bug in the tty-layer which can result in dtr_rts ++ * being called after a disconnect (and tty_unregister_device ++ * has returned). Remove once bug has been squashed. ++ */ ++ mutex_lock(&serial->disc_mutex); ++ if (!serial->disconnected) + drv->dtr_rts(p, on); ++ mutex_unlock(&serial->disc_mutex); + } + + static const struct tty_port_operations serial_port_ops = { +--- a/drivers/usb/serial/usb_wwan.c ++++ b/drivers/usb/serial/usb_wwan.c +@@ -38,7 +38,6 @@ + + void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) + { +- struct usb_serial *serial = port->serial; + struct usb_wwan_port_private *portdata; + struct usb_wwan_intf_private *intfdata; + +@@ -48,12 +47,11 @@ void usb_wwan_dtr_rts(struct usb_serial_ + return; + + portdata = usb_get_serial_port_data(port); +- mutex_lock(&serial->disc_mutex); ++ /* FIXME: locking */ + portdata->rts_state = on; + portdata->dtr_state = on; +- if (serial->dev) +- intfdata->send_setup(port); +- mutex_unlock(&serial->disc_mutex); ++ ++ intfdata->send_setup(port); + } + EXPORT_SYMBOL(usb_wwan_dtr_rts); +