Browse Source

USB: EHCI: warn on unexpectedly active QH

This patch adds a new warning message to ehci-hcd.  The warning is
triggered whenever the driver finds that the hardware has set the
Active bit in a QH at a time when the driver expects the QH to be
completely idle.  Such bugs have been observed by users in the past,
and since they can lead to serious problems (such as inability to
unlink an URB that never completes), it would be good to know about
them when they occur.

This won't fix these bugs; that's a bigger job for a later patch.  But
success isn't guaranteed, since this depends on aspects of the
hardware which are not documented in the EHCI spec or for which the
spec's recommendations are clearly unworkable.  It therefore seems
worthwhile to check for these bugs proactively.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Michael Reutman <mreutman@epiqsolutions.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Alan Stern 9 years ago
parent
commit
fc0855f274
2 changed files with 8 additions and 2 deletions
  1. 7 2
      drivers/usb/host/ehci-q.c
  2. 1 0
      drivers/usb/host/ehci.h

+ 7 - 2
drivers/usb/host/ehci-q.c

@@ -132,10 +132,14 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
 	 * qtd is updated in qh_completions(). Update the QH
 	 * qtd is updated in qh_completions(). Update the QH
 	 * overlay here.
 	 * overlay here.
 	 */
 	 */
-	if (qh->hw->hw_token & ACTIVE_BIT(ehci))
+	if (qh->hw->hw_token & ACTIVE_BIT(ehci)) {
 		qh->hw->hw_qtd_next = qtd->hw_next;
 		qh->hw->hw_qtd_next = qtd->hw_next;
-	else
+		if (qh->should_be_inactive)
+			ehci_warn(ehci, "qh %p should be inactive!\n", qh);
+	} else {
 		qh_update(ehci, qh, qtd);
 		qh_update(ehci, qh, qtd);
+	}
+	qh->should_be_inactive = 0;
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
@@ -438,6 +442,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 					(hw->hw_token & ACTIVE_BIT(ehci))) {
 					(hw->hw_token & ACTIVE_BIT(ehci))) {
 				token = hc32_to_cpu(ehci, hw->hw_token);
 				token = hc32_to_cpu(ehci, hw->hw_token);
 				hw->hw_token &= ~ACTIVE_BIT(ehci);
 				hw->hw_token &= ~ACTIVE_BIT(ehci);
+				qh->should_be_inactive = 1;
 
 
 				/* An unlink may leave an incomplete
 				/* An unlink may leave an incomplete
 				 * async transaction in the TT buffer.
 				 * async transaction in the TT buffer.

+ 1 - 0
drivers/usb/host/ehci.h

@@ -439,6 +439,7 @@ struct ehci_qh {
 	unsigned		dequeue_during_giveback:1;
 	unsigned		dequeue_during_giveback:1;
 	unsigned		exception:1;	/* got a fault, or an unlink
 	unsigned		exception:1;	/* got a fault, or an unlink
 						   was requested */
 						   was requested */
+	unsigned		should_be_inactive:1;
 };
 };
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/