diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-06-05 19:46:26 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-12 19:34:30 -0400 |
commit | 59c2afa072506aae10ef93126aab651142e0c908 (patch) | |
tree | 9e6d87cc767cd6f7b2e882a50db7c4bb1cd29290 | |
parent | 4365831dadfeeeb4c9f8c4944e48ccf78c27f845 (diff) |
USB: option: fix usage of urb->status abuse
Might fix bug 8561
On Mon, 4 Jun 2007, Paulo Pereira wrote:
> The patch that you send is not resolving the problem... :(
> I stil have Kernel panic after 45/60 min of work with Ktorrent/Amule...
>
> The Drump is:
>
> Call Trace:
> [<c055fb36>] usb_hcd_submit+0xb1/0x763
> [<f9276488>] ipt_do_table+0x2c7/0x2ef [ip_tables]
> [<f929a6d7>] nf_ct_deliver_cached_events+0x41/0x96 [nf_conntrak]
> [<f9288254>] ipv4_confirm+0x36/0c3b [nf_conntrack_ipv4]
> [<c05ce7c2>] tcp_v4_rcv+0x827/0x899
> [<c05afcc0>] nf_hook_slow+0x4d/0xb5
> [<c042826f>] irq_enter+0x19/0x23
> [<c042826f>] irq_enter+0x19/0x23
> [<c040794c>] do_IRQ+0xbd/0xd1
> [<f90893c9>] option_write+0xa7/0xef [option]
Okay, from this it looks like there's a problem in the option.c serial
driver. Glancing at the code, it's obvious why: The thing totally
abuses the USB API.
Try applying this patch; it should help.
From: Alan Stern <stern@rowland.harvard.edu>
Cc: Paulo Pereira <pfmp.404@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/serial/option.c | 20 |
1 files changed, 17 insertions, 3 deletions
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5d3999e3ff61..b37d65fc8752 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/tty.h> | 38 | #include <linux/tty.h> |
39 | #include <linux/tty_flip.h> | 39 | #include <linux/tty_flip.h> |
40 | #include <linux/module.h> | 40 | #include <linux/module.h> |
41 | #include <linux/bitops.h> | ||
41 | #include <linux/usb.h> | 42 | #include <linux/usb.h> |
42 | #include <linux/usb/serial.h> | 43 | #include <linux/usb/serial.h> |
43 | 44 | ||
@@ -240,6 +241,7 @@ struct option_port_private { | |||
240 | /* Output endpoints and buffer for this port */ | 241 | /* Output endpoints and buffer for this port */ |
241 | struct urb *out_urbs[N_OUT_URB]; | 242 | struct urb *out_urbs[N_OUT_URB]; |
242 | char out_buffer[N_OUT_URB][OUT_BUFLEN]; | 243 | char out_buffer[N_OUT_URB][OUT_BUFLEN]; |
244 | unsigned long out_busy; /* Bit vector of URBs in use */ | ||
243 | 245 | ||
244 | /* Settings for the port */ | 246 | /* Settings for the port */ |
245 | int rts_state; /* Handshaking pins (outputs) */ | 247 | int rts_state; /* Handshaking pins (outputs) */ |
@@ -370,7 +372,7 @@ static int option_write(struct usb_serial_port *port, | |||
370 | todo = OUT_BUFLEN; | 372 | todo = OUT_BUFLEN; |
371 | 373 | ||
372 | this_urb = portdata->out_urbs[i]; | 374 | this_urb = portdata->out_urbs[i]; |
373 | if (this_urb->status == -EINPROGRESS) { | 375 | if (test_and_set_bit(i, &portdata->out_busy)) { |
374 | if (time_before(jiffies, | 376 | if (time_before(jiffies, |
375 | portdata->tx_start_time[i] + 10 * HZ)) | 377 | portdata->tx_start_time[i] + 10 * HZ)) |
376 | continue; | 378 | continue; |
@@ -394,6 +396,7 @@ static int option_write(struct usb_serial_port *port, | |||
394 | dbg("usb_submit_urb %p (write bulk) failed " | 396 | dbg("usb_submit_urb %p (write bulk) failed " |
395 | "(%d, has %d)", this_urb, | 397 | "(%d, has %d)", this_urb, |
396 | err, this_urb->status); | 398 | err, this_urb->status); |
399 | clear_bit(i, &portdata->out_busy); | ||
397 | continue; | 400 | continue; |
398 | } | 401 | } |
399 | portdata->tx_start_time[i] = jiffies; | 402 | portdata->tx_start_time[i] = jiffies; |
@@ -446,12 +449,23 @@ static void option_indat_callback(struct urb *urb) | |||
446 | static void option_outdat_callback(struct urb *urb) | 449 | static void option_outdat_callback(struct urb *urb) |
447 | { | 450 | { |
448 | struct usb_serial_port *port; | 451 | struct usb_serial_port *port; |
452 | struct option_port_private *portdata; | ||
453 | int i; | ||
449 | 454 | ||
450 | dbg("%s", __FUNCTION__); | 455 | dbg("%s", __FUNCTION__); |
451 | 456 | ||
452 | port = (struct usb_serial_port *) urb->context; | 457 | port = (struct usb_serial_port *) urb->context; |
453 | 458 | ||
454 | usb_serial_port_softint(port); | 459 | usb_serial_port_softint(port); |
460 | |||
461 | portdata = usb_get_serial_port_data(port); | ||
462 | for (i = 0; i < N_OUT_URB; ++i) { | ||
463 | if (portdata->out_urbs[i] == urb) { | ||
464 | smp_mb__before_clear_bit(); | ||
465 | clear_bit(i, &portdata->out_busy); | ||
466 | break; | ||
467 | } | ||
468 | } | ||
455 | } | 469 | } |
456 | 470 | ||
457 | static void option_instat_callback(struct urb *urb) | 471 | static void option_instat_callback(struct urb *urb) |
@@ -518,7 +532,7 @@ static int option_write_room(struct usb_serial_port *port) | |||
518 | 532 | ||
519 | for (i=0; i < N_OUT_URB; i++) { | 533 | for (i=0; i < N_OUT_URB; i++) { |
520 | this_urb = portdata->out_urbs[i]; | 534 | this_urb = portdata->out_urbs[i]; |
521 | if (this_urb && this_urb->status != -EINPROGRESS) | 535 | if (this_urb && !test_bit(i, &portdata->out_busy)) |
522 | data_len += OUT_BUFLEN; | 536 | data_len += OUT_BUFLEN; |
523 | } | 537 | } |
524 | 538 | ||
@@ -537,7 +551,7 @@ static int option_chars_in_buffer(struct usb_serial_port *port) | |||
537 | 551 | ||
538 | for (i=0; i < N_OUT_URB; i++) { | 552 | for (i=0; i < N_OUT_URB; i++) { |
539 | this_urb = portdata->out_urbs[i]; | 553 | this_urb = portdata->out_urbs[i]; |
540 | if (this_urb && this_urb->status == -EINPROGRESS) | 554 | if (this_urb && test_bit(i, &portdata->out_busy)) |
541 | data_len += this_urb->transfer_buffer_length; | 555 | data_len += this_urb->transfer_buffer_length; |
542 | } | 556 | } |
543 | dbg("%s: %d", __FUNCTION__, data_len); | 557 | dbg("%s: %d", __FUNCTION__, data_len); |