USB: serial: digi_acceleport: fix write-wakeup deadlocks
The driver must not call tty_wakeup() while holding its private lock as line disciplines are allowed to call back into write() from write_wakeup(), leading to a deadlock. Also remove the unneeded work struct that was used to defer wakeup in order to work around a possible race in ancient times (see comment about n_tty write_chan() in commit14b54e39b4
("USB: serial: remove changelogs and old todo entries")). Fixes:1da177e4c3
("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
parent
66c32e4833
commit
5098e77962
@ -19,7 +19,6 @@
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/wait.h>
|
||||
@ -198,14 +197,12 @@ struct digi_port {
|
||||
int dp_throttle_restart;
|
||||
wait_queue_head_t dp_flush_wait;
|
||||
wait_queue_head_t dp_close_wait; /* wait queue for close */
|
||||
struct work_struct dp_wakeup_work;
|
||||
struct usb_serial_port *dp_port;
|
||||
};
|
||||
|
||||
|
||||
/* Local Function Declarations */
|
||||
|
||||
static void digi_wakeup_write_lock(struct work_struct *work);
|
||||
static int digi_write_oob_command(struct usb_serial_port *port,
|
||||
unsigned char *buf, int count, int interruptible);
|
||||
static int digi_write_inb_command(struct usb_serial_port *port,
|
||||
@ -356,26 +353,6 @@ __releases(lock)
|
||||
return timeout;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Digi Wakeup Write
|
||||
*
|
||||
* Wake up port, line discipline, and tty processes sleeping
|
||||
* on writes.
|
||||
*/
|
||||
|
||||
static void digi_wakeup_write_lock(struct work_struct *work)
|
||||
{
|
||||
struct digi_port *priv =
|
||||
container_of(work, struct digi_port, dp_wakeup_work);
|
||||
struct usb_serial_port *port = priv->dp_port;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
tty_port_tty_wakeup(&port->port);
|
||||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Digi Write OOB Command
|
||||
*
|
||||
@ -985,6 +962,7 @@ static void digi_write_bulk_callback(struct urb *urb)
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
int status = urb->status;
|
||||
bool wakeup;
|
||||
|
||||
/* port and serial sanity check */
|
||||
if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
|
||||
@ -1011,6 +989,7 @@ static void digi_write_bulk_callback(struct urb *urb)
|
||||
}
|
||||
|
||||
/* try to send any buffered data on this port */
|
||||
wakeup = true;
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
priv->dp_write_urb_in_use = 0;
|
||||
if (priv->dp_out_buf_len > 0) {
|
||||
@ -1026,19 +1005,18 @@ static void digi_write_bulk_callback(struct urb *urb)
|
||||
if (ret == 0) {
|
||||
priv->dp_write_urb_in_use = 1;
|
||||
priv->dp_out_buf_len = 0;
|
||||
wakeup = false;
|
||||
}
|
||||
}
|
||||
/* wake up processes sleeping on writes immediately */
|
||||
tty_port_tty_wakeup(&port->port);
|
||||
/* also queue up a wakeup at scheduler time, in case we */
|
||||
/* lost the race in write_chan(). */
|
||||
schedule_work(&priv->dp_wakeup_work);
|
||||
|
||||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||||
|
||||
if (ret && ret != -EPERM)
|
||||
dev_err_console(port,
|
||||
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
|
||||
__func__, ret, priv->dp_port_num);
|
||||
|
||||
if (wakeup)
|
||||
tty_port_tty_wakeup(&port->port);
|
||||
}
|
||||
|
||||
static int digi_write_room(struct tty_struct *tty)
|
||||
@ -1238,7 +1216,6 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
|
||||
init_waitqueue_head(&priv->dp_transmit_idle_wait);
|
||||
init_waitqueue_head(&priv->dp_flush_wait);
|
||||
init_waitqueue_head(&priv->dp_close_wait);
|
||||
INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
|
||||
priv->dp_port = port;
|
||||
|
||||
init_waitqueue_head(&port->write_wait);
|
||||
@ -1507,13 +1484,14 @@ static int digi_read_oob_callback(struct urb *urb)
|
||||
rts = C_CRTSCTS(tty);
|
||||
|
||||
if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
|
||||
bool wakeup = false;
|
||||
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
/* convert from digi flags to termiox flags */
|
||||
if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
|
||||
priv->dp_modem_signals |= TIOCM_CTS;
|
||||
/* port must be open to use tty struct */
|
||||
if (rts)
|
||||
tty_port_tty_wakeup(&port->port);
|
||||
wakeup = true;
|
||||
} else {
|
||||
priv->dp_modem_signals &= ~TIOCM_CTS;
|
||||
/* port must be open to use tty struct */
|
||||
@ -1532,6 +1510,9 @@ static int digi_read_oob_callback(struct urb *urb)
|
||||
priv->dp_modem_signals &= ~TIOCM_CD;
|
||||
|
||||
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
|
||||
|
||||
if (wakeup)
|
||||
tty_port_tty_wakeup(&port->port);
|
||||
} else if (opcode == DIGI_CMD_TRANSMIT_IDLE) {
|
||||
spin_lock_irqsave(&priv->dp_port_lock, flags);
|
||||
priv->dp_transmit_idle = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user