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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
9a6e184c80
commit
9aa09d2f8f
@@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream)
|
||||
|
||||
is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0;
|
||||
stream->bEndpointAddress &= 0x0f;
|
||||
stream->ep->hcpriv = NULL;
|
||||
if (stream->ep)
|
||||
stream->ep->hcpriv = NULL;
|
||||
|
||||
if (stream->rescheduled) {
|
||||
ehci_info (ehci, "ep%d%s-iso rescheduled "
|
||||
@@ -1653,14 +1654,28 @@ itd_complete (
|
||||
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
|
||||
}
|
||||
iso_stream_put (ehci, stream);
|
||||
/* OK to recycle this ITD now that its completion callback ran. */
|
||||
|
||||
done:
|
||||
usb_put_urb(urb);
|
||||
itd->urb = NULL;
|
||||
itd->stream = NULL;
|
||||
list_move(&itd->itd_list, &stream->free_list);
|
||||
iso_stream_put(ehci, stream);
|
||||
|
||||
if (ehci->clock_frame != itd->frame || itd->index[7] != -1) {
|
||||
/* OK to recycle this ITD now. */
|
||||
itd->stream = NULL;
|
||||
list_move(&itd->itd_list, &stream->free_list);
|
||||
iso_stream_put(ehci, stream);
|
||||
} else {
|
||||
/* HW might remember this ITD, so we can't recycle it yet.
|
||||
* Move it to a safe place until a new frame starts.
|
||||
*/
|
||||
list_move(&itd->itd_list, &ehci->cached_itd_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;
|
||||
}
|
||||
|
||||
@@ -2101,6 +2116,20 @@ done:
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void free_cached_itd_list(struct ehci_hcd *ehci)
|
||||
{
|
||||
struct ehci_itd *itd, *n;
|
||||
|
||||
list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
|
||||
struct ehci_iso_stream *stream = itd->stream;
|
||||
itd->stream = NULL;
|
||||
list_move(&itd->itd_list, &stream->free_list);
|
||||
iso_stream_put(ehci, stream);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
scan_periodic (struct ehci_hcd *ehci)
|
||||
{
|
||||
@@ -2115,10 +2144,17 @@ scan_periodic (struct ehci_hcd *ehci)
|
||||
* Touches as few pages as possible: cache-friendly.
|
||||
*/
|
||||
now_uframe = ehci->next_uframe;
|
||||
if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
|
||||
if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
|
||||
clock = ehci_readl(ehci, &ehci->regs->frame_index);
|
||||
else
|
||||
clock_frame = (clock >> 3) % ehci->periodic_size;
|
||||
} else {
|
||||
clock = now_uframe + mod - 1;
|
||||
clock_frame = -1;
|
||||
}
|
||||
if (ehci->clock_frame != clock_frame) {
|
||||
free_cached_itd_list(ehci);
|
||||
ehci->clock_frame = clock_frame;
|
||||
}
|
||||
clock %= mod;
|
||||
clock_frame = clock >> 3;
|
||||
|
||||
@@ -2277,6 +2313,10 @@ restart:
|
||||
/* rescan the rest of this frame, then ... */
|
||||
clock = now;
|
||||
clock_frame = clock >> 3;
|
||||
if (ehci->clock_frame != clock_frame) {
|
||||
free_cached_itd_list(ehci);
|
||||
ehci->clock_frame = clock_frame;
|
||||
}
|
||||
} else {
|
||||
now_uframe++;
|
||||
now_uframe %= mod;
|
||||
|
Reference in New Issue
Block a user