aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-06-05 19:46:26 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:34:30 -0400
commit59c2afa072506aae10ef93126aab651142e0c908 (patch)
tree9e6d87cc767cd6f7b2e882a50db7c4bb1cd29290
parent4365831dadfeeeb4c9f8c4944e48ccf78c27f845 (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.c20
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)
446static void option_outdat_callback(struct urb *urb) 449static 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
457static void option_instat_callback(struct urb *urb) 471static 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);