diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2010-04-08 16:56:37 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-04-22 18:18:28 -0400 |
commit | 0e5f231bc16ff9910882fa5b9d64d80e7691cfab (patch) | |
tree | 6312287dcfdd99634ce9027f6ba08e087c124b0e /drivers/usb/host/ehci-sched.c | |
parent | 5f677f1d45b2bf08085bbba7394392dfa586fa8e (diff) |
USB: EHCI: defer reclamation of siTDs
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>
CC: <stable@kernel.org>
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.c | 40 |
1 files changed, 31 insertions, 9 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a0aaaaff2560..805ec633a652 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -510,7 +510,7 @@ static int disable_periodic (struct ehci_hcd *ehci) | |||
510 | ehci_writel(ehci, cmd, &ehci->regs->command); | 510 | ehci_writel(ehci, cmd, &ehci->regs->command); |
511 | /* posted write ... */ | 511 | /* posted write ... */ |
512 | 512 | ||
513 | free_cached_itd_list(ehci); | 513 | free_cached_lists(ehci); |
514 | 514 | ||
515 | ehci->next_uframe = -1; | 515 | ehci->next_uframe = -1; |
516 | return 0; | 516 | return 0; |
@@ -2139,13 +2139,27 @@ sitd_complete ( | |||
2139 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); | 2139 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); |
2140 | } | 2140 | } |
2141 | iso_stream_put (ehci, stream); | 2141 | iso_stream_put (ehci, stream); |
2142 | /* OK to recycle this SITD now that its completion callback ran. */ | 2142 | |
2143 | done: | 2143 | done: |
2144 | sitd->urb = NULL; | 2144 | sitd->urb = NULL; |
2145 | sitd->stream = NULL; | 2145 | if (ehci->clock_frame != sitd->frame) { |
2146 | list_move(&sitd->sitd_list, &stream->free_list); | 2146 | /* OK to recycle this SITD now. */ |
2147 | iso_stream_put(ehci, stream); | 2147 | sitd->stream = NULL; |
2148 | 2148 | list_move(&sitd->sitd_list, &stream->free_list); | |
2149 | iso_stream_put(ehci, stream); | ||
2150 | } else { | ||
2151 | /* HW might remember this SITD, so we can't recycle it yet. | ||
2152 | * Move it to a safe place until a new frame starts. | ||
2153 | */ | ||
2154 | list_move(&sitd->sitd_list, &ehci->cached_sitd_list); | ||
2155 | if (stream->refcount == 2) { | ||
2156 | /* If iso_stream_put() were called here, stream | ||
2157 | * would be freed. Instead, just prevent reuse. | ||
2158 | */ | ||
2159 | stream->ep->hcpriv = NULL; | ||
2160 | stream->ep = NULL; | ||
2161 | } | ||
2162 | } | ||
2149 | return retval; | 2163 | return retval; |
2150 | } | 2164 | } |
2151 | 2165 | ||
@@ -2211,9 +2225,10 @@ done: | |||
2211 | 2225 | ||
2212 | /*-------------------------------------------------------------------------*/ | 2226 | /*-------------------------------------------------------------------------*/ |
2213 | 2227 | ||
2214 | static void free_cached_itd_list(struct ehci_hcd *ehci) | 2228 | static void free_cached_lists(struct ehci_hcd *ehci) |
2215 | { | 2229 | { |
2216 | struct ehci_itd *itd, *n; | 2230 | struct ehci_itd *itd, *n; |
2231 | struct ehci_sitd *sitd, *sn; | ||
2217 | 2232 | ||
2218 | list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { | 2233 | list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { |
2219 | struct ehci_iso_stream *stream = itd->stream; | 2234 | struct ehci_iso_stream *stream = itd->stream; |
@@ -2221,6 +2236,13 @@ static void free_cached_itd_list(struct ehci_hcd *ehci) | |||
2221 | list_move(&itd->itd_list, &stream->free_list); | 2236 | list_move(&itd->itd_list, &stream->free_list); |
2222 | iso_stream_put(ehci, stream); | 2237 | iso_stream_put(ehci, stream); |
2223 | } | 2238 | } |
2239 | |||
2240 | list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) { | ||
2241 | struct ehci_iso_stream *stream = sitd->stream; | ||
2242 | sitd->stream = NULL; | ||
2243 | list_move(&sitd->sitd_list, &stream->free_list); | ||
2244 | iso_stream_put(ehci, stream); | ||
2245 | } | ||
2224 | } | 2246 | } |
2225 | 2247 | ||
2226 | /*-------------------------------------------------------------------------*/ | 2248 | /*-------------------------------------------------------------------------*/ |
@@ -2247,7 +2269,7 @@ scan_periodic (struct ehci_hcd *ehci) | |||
2247 | clock_frame = -1; | 2269 | clock_frame = -1; |
2248 | } | 2270 | } |
2249 | if (ehci->clock_frame != clock_frame) { | 2271 | if (ehci->clock_frame != clock_frame) { |
2250 | free_cached_itd_list(ehci); | 2272 | free_cached_lists(ehci); |
2251 | ehci->clock_frame = clock_frame; | 2273 | ehci->clock_frame = clock_frame; |
2252 | } | 2274 | } |
2253 | clock %= mod; | 2275 | clock %= mod; |
@@ -2414,7 +2436,7 @@ restart: | |||
2414 | clock = now; | 2436 | clock = now; |
2415 | clock_frame = clock >> 3; | 2437 | clock_frame = clock >> 3; |
2416 | if (ehci->clock_frame != clock_frame) { | 2438 | if (ehci->clock_frame != clock_frame) { |
2417 | free_cached_itd_list(ehci); | 2439 | free_cached_lists(ehci); |
2418 | ehci->clock_frame = clock_frame; | 2440 | ehci->clock_frame = clock_frame; |
2419 | } | 2441 | } |
2420 | } else { | 2442 | } else { |