summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ehci-sched.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-04-08 16:56:37 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-05-12 15:02:29 -0700
commit4c9e0f8db0f379ba360594dc057efc8202155697 (patch)
treea465d24c32c1da019b2c7be9cbad57f41bcec2ba /drivers/usb/host/ehci-sched.c
parent3e7ad8ed979ce9e3ad897dd87c50bf577966f89c (diff)
USB: EHCI: defer reclamation of siTDs
commit 0e5f231bc16ff9910882fa5b9d64d80e7691cfab upstream. This patch (as1369) fixes a problem in ehci-hcd. Some controllers occasionally run into trouble when the driver reclaims siTDs too quickly. This can happen while streaming audio; it causes the controller to crash. The patch changes siTD reclamation to work the same way as iTD reclamation: Completed siTDs are stored on a list and not reused until at least one frame has passed. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Tested-by: Nate Case <ncase@xes-inc.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-sched.c')
-rw-r--r--drivers/usb/host/ehci-sched.c38
1 files changed, 30 insertions, 8 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index df533cec054b..2064045d1aef 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -2137,13 +2137,27 @@ sitd_complete (
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
}
iso_stream_put (ehci, stream);
- /* OK to recycle this SITD now that its completion callback ran. */
+
done:
sitd->urb = NULL;
- sitd->stream = NULL;
- list_move(&sitd->sitd_list, &stream->free_list);
- iso_stream_put(ehci, stream);
-
+ if (ehci->clock_frame != sitd->frame) {
+ /* OK to recycle this SITD now. */
+ sitd->stream = NULL;
+ list_move(&sitd->sitd_list, &stream->free_list);
+ iso_stream_put(ehci, stream);
+ } else {
+ /* HW might remember this SITD, so we can't recycle it yet.
+ * Move it to a safe place until a new frame starts.
+ */
+ list_move(&sitd->sitd_list, &ehci->cached_sitd_list);
+ if (stream->refcount == 2) {
+ /* If iso_stream_put() were called here, stream
+ * would be freed. Instead, just prevent reuse.
+ */
+ stream->ep->hcpriv = NULL;
+ stream->ep = NULL;
+ }
+ }
return retval;
}
@@ -2209,9 +2223,10 @@ done:
/*-------------------------------------------------------------------------*/
-static void free_cached_itd_list(struct ehci_hcd *ehci)
+static void free_cached_lists(struct ehci_hcd *ehci)
{
struct ehci_itd *itd, *n;
+ struct ehci_sitd *sitd, *sn;
list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
struct ehci_iso_stream *stream = itd->stream;
@@ -2219,6 +2234,13 @@ static void free_cached_itd_list(struct ehci_hcd *ehci)
list_move(&itd->itd_list, &stream->free_list);
iso_stream_put(ehci, stream);
}
+
+ list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
+ struct ehci_iso_stream *stream = sitd->stream;
+ sitd->stream = NULL;
+ list_move(&sitd->sitd_list, &stream->free_list);
+ iso_stream_put(ehci, stream);
+ }
}
/*-------------------------------------------------------------------------*/
@@ -2245,7 +2267,7 @@ scan_periodic (struct ehci_hcd *ehci)
clock_frame = -1;
}
if (ehci->clock_frame != clock_frame) {
- free_cached_itd_list(ehci);
+ free_cached_lists(ehci);
ehci->clock_frame = clock_frame;
}
clock %= mod;
@@ -2408,7 +2430,7 @@ restart:
clock = now;
clock_frame = clock >> 3;
if (ehci->clock_frame != clock_frame) {
- free_cached_itd_list(ehci);
+ free_cached_lists(ehci);
ehci->clock_frame = clock_frame;
}
} else {