mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
USB: serial: keyspan_pda: fix data loss on receive throttling
Killing the interrupt-in urb when the line disciple requests throttling
may lead to data loss if an ongoing transfer is cancelled.
Instead set a flag to prevent the completion handler from resubmitting
the urb until the port is unthrottled.
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
parent
a13c140cc2
commit
42a97c0480
|
|
@ -33,6 +33,8 @@ struct keyspan_pda_private {
|
|||
struct work_struct unthrottle_work;
|
||||
struct usb_serial *serial;
|
||||
struct usb_serial_port *port;
|
||||
bool throttled;
|
||||
bool throttle_req;
|
||||
};
|
||||
|
||||
static int keyspan_pda_write_start(struct usb_serial_port *port);
|
||||
|
|
@ -148,6 +150,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
|
|||
int retval;
|
||||
int status = urb->status;
|
||||
struct keyspan_pda_private *priv;
|
||||
bool throttled = false;
|
||||
unsigned long flags;
|
||||
|
||||
priv = usb_get_serial_port_data(port);
|
||||
|
|
@ -209,16 +212,24 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
|
|||
}
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&port->dev,
|
||||
"%s - usb_submit_urb failed with result %d\n",
|
||||
__func__, retval);
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
if (priv->throttle_req) {
|
||||
priv->throttled = true;
|
||||
throttled = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
if (!throttled) {
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyspan_pda_rx_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
/*
|
||||
* Stop receiving characters. We just turn off the URB request, and
|
||||
|
|
@ -228,16 +239,29 @@ static void keyspan_pda_rx_throttle(struct tty_struct *tty)
|
|||
* send an XOFF, although it might make sense to foist that off upon
|
||||
* the device too.
|
||||
*/
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
spin_lock_irq(&port->lock);
|
||||
priv->throttle_req = true;
|
||||
spin_unlock_irq(&port->lock);
|
||||
}
|
||||
|
||||
static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
|
||||
bool throttled;
|
||||
int ret;
|
||||
|
||||
/* just restart the receive interrupt URB */
|
||||
if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
|
||||
dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
|
||||
spin_lock_irq(&port->lock);
|
||||
throttled = priv->throttled;
|
||||
priv->throttled = false;
|
||||
priv->throttle_req = false;
|
||||
spin_unlock_irq(&port->lock);
|
||||
|
||||
if (throttled) {
|
||||
ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
if (ret)
|
||||
dev_err(&port->dev, "failed to submit in urb: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
|
||||
|
|
@ -577,6 +601,8 @@ static int keyspan_pda_open(struct tty_struct *tty,
|
|||
|
||||
spin_lock_irq(&port->lock);
|
||||
priv->tx_room = rc;
|
||||
priv->throttled = false;
|
||||
priv->throttle_req = false;
|
||||
spin_unlock_irq(&port->lock);
|
||||
|
||||
rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user