aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-sched.c
diff options
context:
space:
mode:
authorKarsten Wiese <fzu@wemgehoertderstaat.de>2009-02-08 19:07:58 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-02-27 17:40:50 -0500
commit9aa09d2f8f4bc440d6db1c3414d4009642875240 (patch)
tree11b8735a5ddb24fcb4b810b9af3959a8aae6a1ae /drivers/usb/host/ehci-sched.c
parent9a6e184c804b33a2c2ea974efcd3c9798d30cb39 (diff)
USB: EHCI: slow down ITD reuse
Currently ITDs are immediately recycled whenever their URB completes. However, EHCI hardware can sometimes remember some ITD state. This means that when the ITD is reused before end-of-frame it may sometimes cause the hardware to reference bogus state. This patch defers reusing such ITDs by moving them into a new ehci member cached_itd_list. ITDs resting in cached_itd_list are moved back into their stream's free_list once scan_periodic() detects that the active frame has elapsed. This makes the snd_usb_us122l driver (in kernel since .28) work right when it's hooked up through EHCI. [ dbrownell@users.sourceforge.net: comment fixups ] Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de> Tested-by: Philippe Carriere <philippe-f.carriere@wanadoo.fr> Tested-by: Federico Briata <federicobriata@gmail.com> Cc: stable <stable@kernel.org> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> 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.c56
1 files changed, 48 insertions, 8 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index a081ee65bde..07bcb931021 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
1004 1004
1005 is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; 1005 is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
1006 stream->bEndpointAddress &= 0x0f; 1006 stream->bEndpointAddress &= 0x0f;
1007 stream->ep->hcpriv = NULL; 1007 if (stream->ep)
1008 stream->ep->hcpriv = NULL;
1008 1009
1009 if (stream->rescheduled) { 1010 if (stream->rescheduled) {
1010 ehci_info (ehci, "ep%d%s-iso rescheduled " 1011 ehci_info (ehci, "ep%d%s-iso rescheduled "
@@ -1653,14 +1654,28 @@ itd_complete (
1653 (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); 1654 (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
1654 } 1655 }
1655 iso_stream_put (ehci, stream); 1656 iso_stream_put (ehci, stream);
1656 /* OK to recycle this ITD now that its completion callback ran. */ 1657
1657done: 1658done:
1658 usb_put_urb(urb); 1659 usb_put_urb(urb);
1659 itd->urb = NULL; 1660 itd->urb = NULL;
1660 itd->stream = NULL; 1661 if (ehci->clock_frame != itd->frame || itd->index[7] != -1) {
1661 list_move(&itd->itd_list, &stream->free_list); 1662 /* OK to recycle this ITD now. */
1662 iso_stream_put(ehci, stream); 1663 itd->stream = NULL;
1663 1664 list_move(&itd->itd_list, &stream->free_list);
1665 iso_stream_put(ehci, stream);
1666 } else {
1667 /* HW might remember this ITD, so we can't recycle it yet.
1668 * Move it to a safe place until a new frame starts.
1669 */
1670 list_move(&itd->itd_list, &ehci->cached_itd_list);
1671 if (stream->refcount == 2) {
1672 /* If iso_stream_put() were called here, stream
1673 * would be freed. Instead, just prevent reuse.
1674 */
1675 stream->ep->hcpriv = NULL;
1676 stream->ep = NULL;
1677 }
1678 }
1664 return retval; 1679 return retval;
1665} 1680}
1666 1681
@@ -2101,6 +2116,20 @@ done:
2101 2116
2102/*-------------------------------------------------------------------------*/ 2117/*-------------------------------------------------------------------------*/
2103 2118
2119static void free_cached_itd_list(struct ehci_hcd *ehci)
2120{
2121 struct ehci_itd *itd, *n;
2122
2123 list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
2124 struct ehci_iso_stream *stream = itd->stream;
2125 itd->stream = NULL;
2126 list_move(&itd->itd_list, &stream->free_list);
2127 iso_stream_put(ehci, stream);
2128 }
2129}
2130
2131/*-------------------------------------------------------------------------*/
2132
2104static void 2133static void
2105scan_periodic (struct ehci_hcd *ehci) 2134scan_periodic (struct ehci_hcd *ehci)
2106{ 2135{
@@ -2115,10 +2144,17 @@ scan_periodic (struct ehci_hcd *ehci)
2115 * Touches as few pages as possible: cache-friendly. 2144 * Touches as few pages as possible: cache-friendly.
2116 */ 2145 */
2117 now_uframe = ehci->next_uframe; 2146 now_uframe = ehci->next_uframe;
2118 if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) 2147 if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
2119 clock = ehci_readl(ehci, &ehci->regs->frame_index); 2148 clock = ehci_readl(ehci, &ehci->regs->frame_index);
2120 else 2149 clock_frame = (clock >> 3) % ehci->periodic_size;
2150 } else {
2121 clock = now_uframe + mod - 1; 2151 clock = now_uframe + mod - 1;
2152 clock_frame = -1;
2153 }
2154 if (ehci->clock_frame != clock_frame) {
2155 free_cached_itd_list(ehci);
2156 ehci->clock_frame = clock_frame;
2157 }
2122 clock %= mod; 2158 clock %= mod;
2123 clock_frame = clock >> 3; 2159 clock_frame = clock >> 3;
2124 2160
@@ -2277,6 +2313,10 @@ restart:
2277 /* rescan the rest of this frame, then ... */ 2313 /* rescan the rest of this frame, then ... */
2278 clock = now; 2314 clock = now;
2279 clock_frame = clock >> 3; 2315 clock_frame = clock >> 3;
2316 if (ehci->clock_frame != clock_frame) {
2317 free_cached_itd_list(ehci);
2318 ehci->clock_frame = clock_frame;
2319 }
2280 } else { 2320 } else {
2281 now_uframe++; 2321 now_uframe++;
2282 now_uframe %= mod; 2322 now_uframe %= mod;