diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-08-19 12:22:44 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:37 -0400 |
commit | a448c9d8c58ff7d3f8cc2a8f835065460099b22d (patch) | |
tree | 418610891b8dc271bbd798ff5c47d921be5f806d /drivers/usb/host | |
parent | 3a44494e233c0fdd818d485cfea8998500543589 (diff) |
USB: EHCI: change deschedule logic for interrupt QHs
This patch (as1281) changes the way ehci-hcd deschedules interrupt
QHs, copying the approach used for async QHs. The caller is no longer
responsible for rescheduling the QH if its queue is non-empty; instead
the reschedule is done directly by intr_deschedule(), after calling
qh_completions(). This is exactly the same as how end_unlink_async()
works.
ehci_urb_dequeue() and intr_deschedule() now correctly handle the case
where they are called while another interrupt URB for the same QH is
being given back. This was a surprisingly large blind spot. And
scan_periodic() now respects the new needs_rescan flag.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 26 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 36 |
3 files changed, 40 insertions, 34 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index d7e85b6231b3..4f89d7ffd53a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -934,8 +934,9 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |||
934 | break; | 934 | break; |
935 | switch (qh->qh_state) { | 935 | switch (qh->qh_state) { |
936 | case QH_STATE_LINKED: | 936 | case QH_STATE_LINKED: |
937 | case QH_STATE_COMPLETING: | ||
937 | intr_deschedule (ehci, qh); | 938 | intr_deschedule (ehci, qh); |
938 | /* FALL THROUGH */ | 939 | break; |
939 | case QH_STATE_IDLE: | 940 | case QH_STATE_IDLE: |
940 | qh_completions (ehci, qh); | 941 | qh_completions (ehci, qh); |
941 | break; | 942 | break; |
@@ -944,23 +945,6 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |||
944 | qh, qh->qh_state); | 945 | qh, qh->qh_state); |
945 | goto done; | 946 | goto done; |
946 | } | 947 | } |
947 | |||
948 | /* reschedule QH iff another request is queued */ | ||
949 | if (!list_empty (&qh->qtd_list) | ||
950 | && HC_IS_RUNNING (hcd->state)) { | ||
951 | rc = qh_schedule(ehci, qh); | ||
952 | |||
953 | /* An error here likely indicates handshake failure | ||
954 | * or no space left in the schedule. Neither fault | ||
955 | * should happen often ... | ||
956 | * | ||
957 | * FIXME kill the now-dysfunctional queued urbs | ||
958 | */ | ||
959 | if (rc != 0) | ||
960 | ehci_err(ehci, | ||
961 | "can't reschedule qh %p, err %d", | ||
962 | qh, rc); | ||
963 | } | ||
964 | break; | 948 | break; |
965 | 949 | ||
966 | case PIPE_ISOCHRONOUS: | 950 | case PIPE_ISOCHRONOUS: |
@@ -1079,12 +1063,10 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) | |||
1079 | * while the QH is active. Unlink it now; | 1063 | * while the QH is active. Unlink it now; |
1080 | * re-linking will call qh_refresh(). | 1064 | * re-linking will call qh_refresh(). |
1081 | */ | 1065 | */ |
1082 | if (eptype == USB_ENDPOINT_XFER_BULK) { | 1066 | if (eptype == USB_ENDPOINT_XFER_BULK) |
1083 | unlink_async(ehci, qh); | 1067 | unlink_async(ehci, qh); |
1084 | } else { | 1068 | else |
1085 | intr_deschedule(ehci, qh); | 1069 | intr_deschedule(ehci, qh); |
1086 | (void) qh_schedule(ehci, qh); | ||
1087 | } | ||
1088 | } | 1070 | } |
1089 | } | 1071 | } |
1090 | spin_unlock_irqrestore(&ehci->lock, flags); | 1072 | spin_unlock_irqrestore(&ehci->lock, flags); |
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 57a84795c43f..00ad9ce392ed 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -299,7 +299,6 @@ __acquires(ehci->lock) | |||
299 | static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); | 299 | static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); |
300 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); | 300 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); |
301 | 301 | ||
302 | static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh); | ||
303 | static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); | 302 | static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); |
304 | 303 | ||
305 | /* | 304 | /* |
@@ -555,14 +554,9 @@ halt: | |||
555 | * That should be rare for interrupt transfers, | 554 | * That should be rare for interrupt transfers, |
556 | * except maybe high bandwidth ... | 555 | * except maybe high bandwidth ... |
557 | */ | 556 | */ |
558 | if ((cpu_to_hc32(ehci, QH_SMASK) | 557 | |
559 | & hw->hw_info2) != 0) { | 558 | /* Tell the caller to start an unlink */ |
560 | intr_deschedule (ehci, qh); | 559 | qh->needs_rescan = 1; |
561 | (void) qh_schedule (ehci, qh); | ||
562 | } else { | ||
563 | /* Tell the caller to start an unlink */ | ||
564 | qh->needs_rescan = 1; | ||
565 | } | ||
566 | break; | 560 | break; |
567 | /* otherwise, unlink already started */ | 561 | /* otherwise, unlink already started */ |
568 | } | 562 | } |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 327437af2122..3ea05936851f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -615,8 +615,19 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
615 | 615 | ||
616 | static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) | 616 | static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) |
617 | { | 617 | { |
618 | unsigned wait; | 618 | unsigned wait; |
619 | struct ehci_qh_hw *hw = qh->hw; | 619 | struct ehci_qh_hw *hw = qh->hw; |
620 | int rc; | ||
621 | |||
622 | /* If the QH isn't linked then there's nothing we can do | ||
623 | * unless we were called during a giveback, in which case | ||
624 | * qh_completions() has to deal with it. | ||
625 | */ | ||
626 | if (qh->qh_state != QH_STATE_LINKED) { | ||
627 | if (qh->qh_state == QH_STATE_COMPLETING) | ||
628 | qh->needs_rescan = 1; | ||
629 | return; | ||
630 | } | ||
620 | 631 | ||
621 | qh_unlink_periodic (ehci, qh); | 632 | qh_unlink_periodic (ehci, qh); |
622 | 633 | ||
@@ -636,6 +647,24 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
636 | qh->qh_state = QH_STATE_IDLE; | 647 | qh->qh_state = QH_STATE_IDLE; |
637 | hw->hw_next = EHCI_LIST_END(ehci); | 648 | hw->hw_next = EHCI_LIST_END(ehci); |
638 | wmb (); | 649 | wmb (); |
650 | |||
651 | qh_completions(ehci, qh); | ||
652 | |||
653 | /* reschedule QH iff another request is queued */ | ||
654 | if (!list_empty(&qh->qtd_list) && | ||
655 | HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { | ||
656 | rc = qh_schedule(ehci, qh); | ||
657 | |||
658 | /* An error here likely indicates handshake failure | ||
659 | * or no space left in the schedule. Neither fault | ||
660 | * should happen often ... | ||
661 | * | ||
662 | * FIXME kill the now-dysfunctional queued urbs | ||
663 | */ | ||
664 | if (rc != 0) | ||
665 | ehci_err(ehci, "can't reschedule qh %p, err %d\n", | ||
666 | qh, rc); | ||
667 | } | ||
639 | } | 668 | } |
640 | 669 | ||
641 | /*-------------------------------------------------------------------------*/ | 670 | /*-------------------------------------------------------------------------*/ |
@@ -2213,7 +2242,8 @@ restart: | |||
2213 | type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); | 2242 | type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); |
2214 | q = q.qh->qh_next; | 2243 | q = q.qh->qh_next; |
2215 | modified = qh_completions (ehci, temp.qh); | 2244 | modified = qh_completions (ehci, temp.qh); |
2216 | if (unlikely (list_empty (&temp.qh->qtd_list))) | 2245 | if (unlikely(list_empty(&temp.qh->qtd_list) || |
2246 | temp.qh->needs_rescan)) | ||
2217 | intr_deschedule (ehci, temp.qh); | 2247 | intr_deschedule (ehci, temp.qh); |
2218 | qh_put (temp.qh); | 2248 | qh_put (temp.qh); |
2219 | break; | 2249 | break; |