summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-timer.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2016-01-25 15:45:25 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-02-03 13:14:52 -0800
commit87d61912c23a746ee9a8a8d2fe17af217c87f761 (patch)
tree3f2239d651d7c2cf44df93095d34ad9cf6acd358 /drivers/usb/host/ehci-timer.c
parentf96fba0dbf8f6b0eaa313b4c230f93c9bb0dd759 (diff)
downloadblackbird-obmc-linux-87d61912c23a746ee9a8a8d2fe17af217c87f761.tar.gz
blackbird-obmc-linux-87d61912c23a746ee9a8a8d2fe17af217c87f761.zip
USB: EHCI: add a delay when unlinking an active QH
Michael Reutman reports that an AMD/ATI EHCI host controller on one of his computers does not stop transferring data when an active bulk QH is unlinked from the async schedule. Apparently that host controller fails to implement the IAA mechanism correctly when an active QH is unlinked. This leads to data corruption, because the controller continues to update the QH in memory when the driver doesn't expect it. As a result, the next URB submitted for that QH can hang, because the link pointers for the TD queue have been messed up. This misbehavior is observed quite regularly. To be fair, the EHCI spec (section 4.8.2) says that active QHs should not be unlinked. It goes on to recommend a procedure that involves waiting for the QH to go inactive before unlinking it. In the real world this is impractical, not least because the QH may _never_ go inactive. (What were they thinking?) Sometimes we have no choice but to unlink an active QH. In an attempt to avoid the problems that can ensue, this patch changes how the driver decides when the unlink is complete. In addition to waiting through two IAA cycles, in cases where the QH was not known to be inactive beforehand we now wait until a 2-ms period has elapsed with the host controller making no change to the QH data structure (the hw_current and hw_token fields in particular). The intuition here is that after such a long period, the endpoint must be NAKing and hopefully the QH has been dropped from the host controller's internal cache. There's no way to know if this reasoning is really valid -- the spec is no help in this regard -- but at least this approach fixes Michael's problem. The test for whether the QH is already known to be inactive involves the reason for unlinking the QH originally. If it was unlinked because it had halted, or it stopped in response to a short read, or it overlaid a dummy TD (a silicon bug), then it certainly is inactive. If it was unlinked because the TD queue was empty and no TDs have been added to the queue in the meantime, then it must be inactive. Or if the hardware status indicates that the QH is currently halted (even if that wasn't the reason for unlinking it), then it is inactive. Otherwise, if none of those checks apply, we go through the 2-ms delay. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: Michael Reutman <mreutman@epiqsolutions.com> Tested-by: Michael Reutman <mreutman@epiqsolutions.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ehci-timer.c')
-rw-r--r--drivers/usb/host/ehci-timer.c2
1 files changed, 2 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index 6bea4d553ec5..69f50e6533a6 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */
+ 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ACTIVE_UNLINK */
5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */
6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */
@@ -395,6 +396,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */
+ end_unlink_async, /* EHCI_HRTIMER_ACTIVE_UNLINK */
ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */
unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */
ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */
OpenPOWER on IntegriCloud