aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Neukum <oliver@neukum.org>2008-07-29 09:26:15 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-10-17 17:40:51 -0400
commit55b447bf79ad25591437d24b78caa9d0ae4fec82 (patch)
treef8b84be61cf96a993664f35f572d8ddf6e618d39
parent49b707b90c7f7260beb8691fc5d99d71a5549ec0 (diff)
USB: kill URBs permanently
looking at usb_kill_urb() it seems to me that it is unnecessarily lenient. In the use case of disconnect() you never want to use the URB again (for the same device) But leaving urb->reject elevated will make it easier to avoid races between read/write and disconnect. Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/core/urb.c56
-rw-r--r--include/linux/usb.h2
2 files changed, 52 insertions, 6 deletions
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 47111e88f791..a7945ab208c2 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -522,6 +522,7 @@ int usb_unlink_urb(struct urb *urb)
522} 522}
523EXPORT_SYMBOL_GPL(usb_unlink_urb); 523EXPORT_SYMBOL_GPL(usb_unlink_urb);
524 524
525static DEFINE_MUTEX(usb_reject_mutex);
525/** 526/**
526 * usb_kill_urb - cancel a transfer request and wait for it to finish 527 * usb_kill_urb - cancel a transfer request and wait for it to finish
527 * @urb: pointer to URB describing a previously submitted request, 528 * @urb: pointer to URB describing a previously submitted request,
@@ -544,25 +545,68 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb);
544 */ 545 */
545void usb_kill_urb(struct urb *urb) 546void usb_kill_urb(struct urb *urb)
546{ 547{
547 static DEFINE_MUTEX(reject_mutex);
548
549 might_sleep(); 548 might_sleep();
550 if (!(urb && urb->dev && urb->ep)) 549 if (!(urb && urb->dev && urb->ep))
551 return; 550 return;
552 mutex_lock(&reject_mutex); 551 mutex_lock(&usb_reject_mutex);
553 ++urb->reject; 552 ++urb->reject;
554 mutex_unlock(&reject_mutex); 553 mutex_unlock(&usb_reject_mutex);
555 554
556 usb_hcd_unlink_urb(urb, -ENOENT); 555 usb_hcd_unlink_urb(urb, -ENOENT);
557 wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); 556 wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
558 557
559 mutex_lock(&reject_mutex); 558 mutex_lock(&usb_reject_mutex);
560 --urb->reject; 559 --urb->reject;
561 mutex_unlock(&reject_mutex); 560 mutex_unlock(&usb_reject_mutex);
562} 561}
563EXPORT_SYMBOL_GPL(usb_kill_urb); 562EXPORT_SYMBOL_GPL(usb_kill_urb);
564 563
565/** 564/**
565 * usb_poison_urb - reliably kill a transfer and prevent further use of an URB
566 * @urb: pointer to URB describing a previously submitted request,
567 * may be NULL
568 *
569 * This routine cancels an in-progress request. It is guaranteed that
570 * upon return all completion handlers will have finished and the URB
571 * will be totally idle and cannot be reused. These features make
572 * this an ideal way to stop I/O in a disconnect() callback.
573 * If the request has not already finished or been unlinked
574 * the completion handler will see urb->status == -ENOENT.
575 *
576 * After and while the routine runs, attempts to resubmit the URB will fail
577 * with error -EPERM. Thus even if the URB's completion handler always
578 * tries to resubmit, it will not succeed and the URB will become idle.
579 *
580 * This routine may not be used in an interrupt context (such as a bottom
581 * half or a completion handler), or when holding a spinlock, or in other
582 * situations where the caller can't schedule().
583 */
584void usb_poison_urb(struct urb *urb)
585{
586 might_sleep();
587 if (!(urb && urb->dev && urb->ep))
588 return;
589 mutex_lock(&usb_reject_mutex);
590 ++urb->reject;
591 mutex_unlock(&usb_reject_mutex);
592
593 usb_hcd_unlink_urb(urb, -ENOENT);
594 wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
595}
596EXPORT_SYMBOL_GPL(usb_poison_urb);
597
598void usb_unpoison_urb(struct urb *urb)
599{
600 if (!urb)
601 return;
602
603 mutex_lock(&usb_reject_mutex);
604 --urb->reject;
605 mutex_unlock(&usb_reject_mutex);
606}
607EXPORT_SYMBOL_GPL(usb_unpoison_urb);
608
609/**
566 * usb_kill_anchored_urbs - cancel transfer requests en masse 610 * usb_kill_anchored_urbs - cancel transfer requests en masse
567 * @anchor: anchor the requests are bound to 611 * @anchor: anchor the requests are bound to
568 * 612 *
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 94ac74aba6b6..3371c91e7ff4 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1459,6 +1459,8 @@ extern struct urb *usb_get_urb(struct urb *urb);
1459extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags); 1459extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
1460extern int usb_unlink_urb(struct urb *urb); 1460extern int usb_unlink_urb(struct urb *urb);
1461extern void usb_kill_urb(struct urb *urb); 1461extern void usb_kill_urb(struct urb *urb);
1462extern void usb_poison_urb(struct urb *urb);
1463extern void usb_unpoison_urb(struct urb *urb);
1462extern void usb_kill_anchored_urbs(struct usb_anchor *anchor); 1464extern void usb_kill_anchored_urbs(struct usb_anchor *anchor);
1463extern void usb_unlink_anchored_urbs(struct usb_anchor *anchor); 1465extern void usb_unlink_anchored_urbs(struct usb_anchor *anchor);
1464extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor); 1466extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);