diff options
Diffstat (limited to 'drivers/usb/serial/generic.c')
-rw-r--r-- | drivers/usb/serial/generic.c | 76 |
1 files changed, 41 insertions, 35 deletions
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 2274d9625f63..1be8bea372a2 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c | |||
@@ -106,12 +106,8 @@ void usb_serial_generic_deregister(void) | |||
106 | int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) | 106 | int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) |
107 | { | 107 | { |
108 | int result = 0; | 108 | int result = 0; |
109 | unsigned long flags; | ||
110 | 109 | ||
111 | spin_lock_irqsave(&port->lock, flags); | 110 | clear_bit(USB_SERIAL_THROTTLED, &port->flags); |
112 | port->throttled = 0; | ||
113 | port->throttle_req = 0; | ||
114 | spin_unlock_irqrestore(&port->lock, flags); | ||
115 | 111 | ||
116 | if (port->bulk_in_size) | 112 | if (port->bulk_in_size) |
117 | result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); | 113 | result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); |
@@ -375,7 +371,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) | |||
375 | { | 371 | { |
376 | struct usb_serial_port *port = urb->context; | 372 | struct usb_serial_port *port = urb->context; |
377 | unsigned char *data = urb->transfer_buffer; | 373 | unsigned char *data = urb->transfer_buffer; |
378 | unsigned long flags; | 374 | bool stopped = false; |
379 | int status = urb->status; | 375 | int status = urb->status; |
380 | int i; | 376 | int i; |
381 | 377 | ||
@@ -383,42 +379,55 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) | |||
383 | if (urb == port->read_urbs[i]) | 379 | if (urb == port->read_urbs[i]) |
384 | break; | 380 | break; |
385 | } | 381 | } |
386 | set_bit(i, &port->read_urbs_free); | ||
387 | 382 | ||
388 | dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, | 383 | dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, |
389 | urb->actual_length); | 384 | urb->actual_length); |
390 | switch (status) { | 385 | switch (status) { |
391 | case 0: | 386 | case 0: |
387 | usb_serial_debug_data(&port->dev, __func__, urb->actual_length, | ||
388 | data); | ||
389 | port->serial->type->process_read_urb(urb); | ||
392 | break; | 390 | break; |
393 | case -ENOENT: | 391 | case -ENOENT: |
394 | case -ECONNRESET: | 392 | case -ECONNRESET: |
395 | case -ESHUTDOWN: | 393 | case -ESHUTDOWN: |
396 | dev_dbg(&port->dev, "%s - urb stopped: %d\n", | 394 | dev_dbg(&port->dev, "%s - urb stopped: %d\n", |
397 | __func__, status); | 395 | __func__, status); |
398 | return; | 396 | stopped = true; |
397 | break; | ||
399 | case -EPIPE: | 398 | case -EPIPE: |
400 | dev_err(&port->dev, "%s - urb stopped: %d\n", | 399 | dev_err(&port->dev, "%s - urb stopped: %d\n", |
401 | __func__, status); | 400 | __func__, status); |
402 | return; | 401 | stopped = true; |
402 | break; | ||
403 | default: | 403 | default: |
404 | dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", | 404 | dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", |
405 | __func__, status); | 405 | __func__, status); |
406 | goto resubmit; | 406 | break; |
407 | } | 407 | } |
408 | 408 | ||
409 | usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); | 409 | /* |
410 | port->serial->type->process_read_urb(urb); | 410 | * Make sure URB processing is done before marking as free to avoid |
411 | * racing with unthrottle() on another CPU. Matches the barriers | ||
412 | * implied by the test_and_clear_bit() in | ||
413 | * usb_serial_generic_submit_read_urb(). | ||
414 | */ | ||
415 | smp_mb__before_atomic(); | ||
416 | set_bit(i, &port->read_urbs_free); | ||
417 | /* | ||
418 | * Make sure URB is marked as free before checking the throttled flag | ||
419 | * to avoid racing with unthrottle() on another CPU. Matches the | ||
420 | * smp_mb() in unthrottle(). | ||
421 | */ | ||
422 | smp_mb__after_atomic(); | ||
411 | 423 | ||
412 | resubmit: | 424 | if (stopped) |
413 | /* Throttle the device if requested by tty */ | 425 | return; |
414 | spin_lock_irqsave(&port->lock, flags); | 426 | |
415 | port->throttled = port->throttle_req; | 427 | if (test_bit(USB_SERIAL_THROTTLED, &port->flags)) |
416 | if (!port->throttled) { | 428 | return; |
417 | spin_unlock_irqrestore(&port->lock, flags); | 429 | |
418 | usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); | 430 | usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); |
419 | } else { | ||
420 | spin_unlock_irqrestore(&port->lock, flags); | ||
421 | } | ||
422 | } | 431 | } |
423 | EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); | 432 | EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); |
424 | 433 | ||
@@ -454,10 +463,9 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) | |||
454 | default: | 463 | default: |
455 | dev_err_console(port, "%s - nonzero urb status: %d\n", | 464 | dev_err_console(port, "%s - nonzero urb status: %d\n", |
456 | __func__, status); | 465 | __func__, status); |
457 | goto resubmit; | 466 | break; |
458 | } | 467 | } |
459 | 468 | ||
460 | resubmit: | ||
461 | usb_serial_generic_write_start(port, GFP_ATOMIC); | 469 | usb_serial_generic_write_start(port, GFP_ATOMIC); |
462 | usb_serial_port_softint(port); | 470 | usb_serial_port_softint(port); |
463 | } | 471 | } |
@@ -466,26 +474,24 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); | |||
466 | void usb_serial_generic_throttle(struct tty_struct *tty) | 474 | void usb_serial_generic_throttle(struct tty_struct *tty) |
467 | { | 475 | { |
468 | struct usb_serial_port *port = tty->driver_data; | 476 | struct usb_serial_port *port = tty->driver_data; |
469 | unsigned long flags; | ||
470 | 477 | ||
471 | spin_lock_irqsave(&port->lock, flags); | 478 | set_bit(USB_SERIAL_THROTTLED, &port->flags); |
472 | port->throttle_req = 1; | ||
473 | spin_unlock_irqrestore(&port->lock, flags); | ||
474 | } | 479 | } |
475 | EXPORT_SYMBOL_GPL(usb_serial_generic_throttle); | 480 | EXPORT_SYMBOL_GPL(usb_serial_generic_throttle); |
476 | 481 | ||
477 | void usb_serial_generic_unthrottle(struct tty_struct *tty) | 482 | void usb_serial_generic_unthrottle(struct tty_struct *tty) |
478 | { | 483 | { |
479 | struct usb_serial_port *port = tty->driver_data; | 484 | struct usb_serial_port *port = tty->driver_data; |
480 | int was_throttled; | ||
481 | 485 | ||
482 | spin_lock_irq(&port->lock); | 486 | clear_bit(USB_SERIAL_THROTTLED, &port->flags); |
483 | was_throttled = port->throttled; | 487 | |
484 | port->throttled = port->throttle_req = 0; | 488 | /* |
485 | spin_unlock_irq(&port->lock); | 489 | * Matches the smp_mb__after_atomic() in |
490 | * usb_serial_generic_read_bulk_callback(). | ||
491 | */ | ||
492 | smp_mb(); | ||
486 | 493 | ||
487 | if (was_throttled) | 494 | usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); |
488 | usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); | ||
489 | } | 495 | } |
490 | EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); | 496 | EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); |
491 | 497 | ||