Skip to content

Commit

Permalink
USB: serial: keyspan_pda: fix write unthrottling
Browse files Browse the repository at this point in the history
commit 320f902 upstream.

The driver did not update its view of the available device buffer space
until write() was called in task context. This meant that write_room()
would return 0 even after the device had sent a write-unthrottle
notification, something which could lead to blocked writers not being
woken up (e.g. when using OPOST).

Note that we must also request an unthrottle notification is case a
write() request fills the device buffer exactly.

Fixes: 1da177e ("Linux-2.6.12-rc2")
Cc: stable <stable@vger.kernel.org>
Acked-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
jhovold authored and gregkh committed Dec 30, 2020
1 parent 28a9c26 commit ed5e251
Showing 1 changed file with 20 additions and 9 deletions.
29 changes: 20 additions & 9 deletions drivers/usb/serial/keyspan_pda.c
Expand Up @@ -40,6 +40,8 @@
#define DRIVER_AUTHOR "Brian Warner <warner@lothar.com>"
#define DRIVER_DESC "USB Keyspan PDA Converter driver"

#define KEYSPAN_TX_THRESHOLD 16

struct keyspan_pda_private {
int tx_room;
int tx_throttled;
Expand Down Expand Up @@ -110,7 +112,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work)
7, /* request_unthrottle */
USB_TYPE_VENDOR | USB_RECIP_INTERFACE
| USB_DIR_OUT,
16, /* value: threshold */
KEYSPAN_TX_THRESHOLD,
0, /* index */
NULL,
0,
Expand All @@ -129,6 +131,8 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
int retval;
int status = urb->status;
struct keyspan_pda_private *priv;
unsigned long flags;

priv = usb_get_serial_port_data(port);

switch (status) {
Expand Down Expand Up @@ -171,7 +175,10 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
case 1: /* modemline change */
break;
case 2: /* tx unthrottle interrupt */
spin_lock_irqsave(&port->lock, flags);
priv->tx_throttled = 0;
priv->tx_room = max(priv->tx_room, KEYSPAN_TX_THRESHOLD);
spin_unlock_irqrestore(&port->lock, flags);
/* queue up a wakeup at scheduler time */
usb_serial_port_softint(port);
break;
Expand Down Expand Up @@ -505,7 +512,8 @@ static int keyspan_pda_write(struct tty_struct *tty,
goto exit;
}
}
if (count > priv->tx_room) {

if (count >= priv->tx_room) {
/* we're about to completely fill the Tx buffer, so
we'll be throttled afterwards. */
count = priv->tx_room;
Expand Down Expand Up @@ -560,14 +568,17 @@ static void keyspan_pda_write_bulk_callback(struct urb *urb)
static int keyspan_pda_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
struct keyspan_pda_private *priv;
priv = usb_get_serial_port_data(port);
/* used by n_tty.c for processing of tabs and such. Giving it our
conservative guess is probably good enough, but needs testing by
running a console through the device. */
return priv->tx_room;
}
struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int room = 0;

spin_lock_irqsave(&port->lock, flags);
if (test_bit(0, &port->write_urbs_free) && !priv->tx_throttled)
room = priv->tx_room;
spin_unlock_irqrestore(&port->lock, flags);

return room;
}

static int keyspan_pda_chars_in_buffer(struct tty_struct *tty)
{
Expand Down

0 comments on commit ed5e251

Please sign in to comment.