diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-08-02 15:05:45 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-10-12 17:55:01 -0400 |
commit | 9439eb94b5c374d5b02699f8897fc43aa3603701 (patch) | |
tree | 87e4629b1c9e0f14da4562cd1cb38a809777c311 | |
parent | d617bc83ff48ebf0df253605529d8b3bef15773a (diff) |
USB: update spinlock usage for root-hub URBs
This patch (as952) adjusts the spinlock usage in the root-hub
emulation part of usbcore, to make it match more closely the pattern
used by regular host controller drivers. To wit: The private lock
(usb_hcd_root_hub_lock) is held throughout the important parts, and it
is dropped temporarily without re-enabling interrupts around the call
to usb_hcd_giveback_urb().
A nice side effect is that the code now avoids calling
local_irq_save(), thereby becoming more RT-friendly.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/hcd.c | 52 |
1 files changed, 29 insertions, 23 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 47a055a2acf5..f8e7deb03ee9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -356,10 +356,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) | |||
356 | const u8 *bufp = tbuf; | 356 | const u8 *bufp = tbuf; |
357 | int len = 0; | 357 | int len = 0; |
358 | int patch_wakeup = 0; | 358 | int patch_wakeup = 0; |
359 | unsigned long flags; | ||
360 | int status = 0; | 359 | int status = 0; |
361 | int n; | 360 | int n; |
362 | 361 | ||
362 | might_sleep(); | ||
363 | |||
363 | cmd = (struct usb_ctrlrequest *) urb->setup_packet; | 364 | cmd = (struct usb_ctrlrequest *) urb->setup_packet; |
364 | typeReq = (cmd->bRequestType << 8) | cmd->bRequest; | 365 | typeReq = (cmd->bRequestType << 8) | cmd->bRequest; |
365 | wValue = le16_to_cpu (cmd->wValue); | 366 | wValue = le16_to_cpu (cmd->wValue); |
@@ -523,13 +524,21 @@ error: | |||
523 | } | 524 | } |
524 | 525 | ||
525 | /* any errors get returned through the urb completion */ | 526 | /* any errors get returned through the urb completion */ |
526 | local_irq_save (flags); | 527 | spin_lock_irq(&hcd_root_hub_lock); |
527 | spin_lock (&urb->lock); | 528 | spin_lock(&urb->lock); |
528 | if (urb->status == -EINPROGRESS) | 529 | if (urb->status == -EINPROGRESS) |
529 | urb->status = status; | 530 | urb->status = status; |
530 | spin_unlock (&urb->lock); | 531 | spin_unlock(&urb->lock); |
531 | usb_hcd_giveback_urb (hcd, urb); | 532 | |
532 | local_irq_restore (flags); | 533 | /* This peculiar use of spinlocks echoes what real HC drivers do. |
534 | * Avoiding calls to local_irq_disable/enable makes the code | ||
535 | * RT-friendly. | ||
536 | */ | ||
537 | spin_unlock(&hcd_root_hub_lock); | ||
538 | usb_hcd_giveback_urb(hcd, urb); | ||
539 | spin_lock(&hcd_root_hub_lock); | ||
540 | |||
541 | spin_unlock_irq(&hcd_root_hub_lock); | ||
533 | return 0; | 542 | return 0; |
534 | } | 543 | } |
535 | 544 | ||
@@ -559,8 +568,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) | |||
559 | if (length > 0) { | 568 | if (length > 0) { |
560 | 569 | ||
561 | /* try to complete the status urb */ | 570 | /* try to complete the status urb */ |
562 | local_irq_save (flags); | 571 | spin_lock_irqsave(&hcd_root_hub_lock, flags); |
563 | spin_lock(&hcd_root_hub_lock); | ||
564 | urb = hcd->status_urb; | 572 | urb = hcd->status_urb; |
565 | if (urb) { | 573 | if (urb) { |
566 | spin_lock(&urb->lock); | 574 | spin_lock(&urb->lock); |
@@ -574,16 +582,16 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) | |||
574 | } else /* urb has been unlinked */ | 582 | } else /* urb has been unlinked */ |
575 | length = 0; | 583 | length = 0; |
576 | spin_unlock(&urb->lock); | 584 | spin_unlock(&urb->lock); |
585 | |||
586 | spin_unlock(&hcd_root_hub_lock); | ||
587 | usb_hcd_giveback_urb(hcd, urb); | ||
588 | spin_lock(&hcd_root_hub_lock); | ||
577 | } else | 589 | } else |
578 | length = 0; | 590 | length = 0; |
579 | spin_unlock(&hcd_root_hub_lock); | ||
580 | 591 | ||
581 | /* local irqs are always blocked in completions */ | 592 | if (length <= 0) |
582 | if (length > 0) | ||
583 | usb_hcd_giveback_urb (hcd, urb); | ||
584 | else | ||
585 | hcd->poll_pending = 1; | 593 | hcd->poll_pending = 1; |
586 | local_irq_restore (flags); | 594 | spin_unlock_irqrestore(&hcd_root_hub_lock, flags); |
587 | } | 595 | } |
588 | 596 | ||
589 | /* The USB 2.0 spec says 256 ms. This is close enough and won't | 597 | /* The USB 2.0 spec says 256 ms. This is close enough and won't |
@@ -651,25 +659,23 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) | |||
651 | { | 659 | { |
652 | unsigned long flags; | 660 | unsigned long flags; |
653 | 661 | ||
662 | spin_lock_irqsave(&hcd_root_hub_lock, flags); | ||
654 | if (usb_endpoint_num(&urb->ep->desc) == 0) { /* Control URB */ | 663 | if (usb_endpoint_num(&urb->ep->desc) == 0) { /* Control URB */ |
655 | ; /* Do nothing */ | 664 | ; /* Do nothing */ |
656 | 665 | ||
657 | } else { /* Status URB */ | 666 | } else { /* Status URB */ |
658 | if (!hcd->uses_new_polling) | 667 | if (!hcd->uses_new_polling) |
659 | del_timer (&hcd->rh_timer); | 668 | del_timer (&hcd->rh_timer); |
660 | local_irq_save (flags); | ||
661 | spin_lock (&hcd_root_hub_lock); | ||
662 | if (urb == hcd->status_urb) { | 669 | if (urb == hcd->status_urb) { |
663 | hcd->status_urb = NULL; | 670 | hcd->status_urb = NULL; |
664 | urb->hcpriv = NULL; | 671 | urb->hcpriv = NULL; |
665 | } else | ||
666 | urb = NULL; /* wasn't fully queued */ | ||
667 | spin_unlock (&hcd_root_hub_lock); | ||
668 | if (urb) | ||
669 | usb_hcd_giveback_urb (hcd, urb); | ||
670 | local_irq_restore (flags); | ||
671 | } | ||
672 | 672 | ||
673 | spin_unlock(&hcd_root_hub_lock); | ||
674 | usb_hcd_giveback_urb(hcd, urb); | ||
675 | spin_lock(&hcd_root_hub_lock); | ||
676 | } | ||
677 | } | ||
678 | spin_unlock_irqrestore(&hcd_root_hub_lock, flags); | ||
673 | return 0; | 679 | return 0; |
674 | } | 680 | } |
675 | 681 | ||