diff options
author | Johan Hovold <jhovold@gmail.com> | 2010-08-04 09:45:57 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 17:35:45 -0400 |
commit | b58af4066d240b18b43f202e07b9ec7461d90b17 (patch) | |
tree | a6f04122c5849383b0981d0a130bdf787788f1fa /drivers/usb/serial/generic.c | |
parent | b409214c683ed06c26e2cdad0be546ad11463354 (diff) |
USB: serial: fix stalled writes
As David VomLehn points out, it was possible to receive an interrupt
before clearing the free-urb flag which could lead to the urb being
incorrectly marked as busy.
For the same reason, move tx_bytes accounting so that it will never be
negative.
Note that the free-flags set and clear operations do not need any
additional locking as they are manipulated while USB_SERIAL_WRITE_BUSY
is set.
Reported-by: David VomLehn <dvomlehn@cisco.com>
Tested-by: David VomLehn <dvomlehn@cisco.com>
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial/generic.c')
-rw-r--r-- | drivers/usb/serial/generic.c | 15 |
1 files changed, 10 insertions, 5 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index a817ced82835..ca92f67747cc 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c | |||
@@ -208,18 +208,23 @@ retry: | |||
208 | urb->transfer_buffer_length = count; | 208 | urb->transfer_buffer_length = count; |
209 | usb_serial_debug_data(debug, &port->dev, __func__, count, | 209 | usb_serial_debug_data(debug, &port->dev, __func__, count, |
210 | urb->transfer_buffer); | 210 | urb->transfer_buffer); |
211 | spin_lock_irqsave(&port->lock, flags); | ||
212 | port->tx_bytes += count; | ||
213 | spin_unlock_irqrestore(&port->lock, flags); | ||
214 | |||
215 | clear_bit(i, &port->write_urbs_free); | ||
211 | result = usb_submit_urb(urb, GFP_ATOMIC); | 216 | result = usb_submit_urb(urb, GFP_ATOMIC); |
212 | if (result) { | 217 | if (result) { |
213 | dev_err(&port->dev, "%s - error submitting urb: %d\n", | 218 | dev_err(&port->dev, "%s - error submitting urb: %d\n", |
214 | __func__, result); | 219 | __func__, result); |
220 | set_bit(i, &port->write_urbs_free); | ||
221 | spin_lock_irqsave(&port->lock, flags); | ||
222 | port->tx_bytes -= count; | ||
223 | spin_unlock_irqrestore(&port->lock, flags); | ||
224 | |||
215 | clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); | 225 | clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); |
216 | return result; | 226 | return result; |
217 | } | 227 | } |
218 | clear_bit(i, &port->write_urbs_free); | ||
219 | |||
220 | spin_lock_irqsave(&port->lock, flags); | ||
221 | port->tx_bytes += count; | ||
222 | spin_unlock_irqrestore(&port->lock, flags); | ||
223 | 228 | ||
224 | /* Try sending off another urb, unless in irq context (in which case | 229 | /* Try sending off another urb, unless in irq context (in which case |
225 | * there will be no free urb). */ | 230 | * there will be no free urb). */ |