Re: USB issue with kernel 3.6

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 14 Jan 2013, Piergiorgio Sartor wrote:

> > Okay, I figured out what the problem is.  It really is a bug in 
> > ehci-hcd; the driver doesn't do what the spec requires when unlinking 
> > more than one QH at a time.
> 
> Well, I'm happy to read this. Good that you find it!
> 
> It puzzles me I'm the only one, it seems, affected by this!

I'm not sure.  Perhaps this is because you are using so many async QHs 
at the same time.

> > Although the driver could be fixed, it will be easier to stick with the
> > old approach and never unlink more than one QH.  Doing two or more at
> > once is pretty rare, so it doesn't much matter if they take a little
> > longer.  I'll send a patch that does this properly in the near future,
> > for you to test.
> 
> Cool! I'm looking forward to it.

I changed my mind -- it turns out that adding the fix is somewhat
easier than unlinking one QH at a time.

So now we're ready for some serious testing.  The patch below is based 
on the 3.7 kernel, and it doesn't include any of the debugging stuff 
you have been using.  Remove all the old patches and apply this one 
instead.  It has two changes: the increase in the schedule polling time 
and the fix for multiple unlinks.

If this causes the problem to go away then I will submit it for 
inclusion in the stable kernel series.

Alan Stern



Index: 3.7/drivers/usb/host/ehci-timer.c
===================================================================
--- 3.7.orig/drivers/usb/host/ehci-timer.c
+++ 3.7/drivers/usb/host/ehci-timer.c
@@ -113,21 +113,22 @@ static void ehci_poll_ASS(struct ehci_hc
 
 	if (want != actual) {
 
-		/* Poll again later, but give up after about 20 ms */
-		if (ehci->ASS_poll_count++ < 20) {
-			ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
-			return;
-		}
-		ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n",
-				want, actual);
+		/* Poll again later */
+		ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
+		++ehci->ASS_poll_count;
+		return;
 	}
+
+	if (ehci->ASS_poll_count > 20)
+		ehci_dbg(ehci, "ASS poll count reached %d\n",
+				ehci->ASS_poll_count);
 	ehci->ASS_poll_count = 0;
 
 	/* The status is up-to-date; restart or stop the schedule as needed */
 	if (want == 0) {	/* Stopped */
-		if (ehci->async_count > 0)
+		if (ehci->async_count > 0) {
 			ehci_set_command_bit(ehci, CMD_ASE);
-
+		}
 	} else {		/* Running */
 		if (ehci->async_count == 0) {
 
@@ -159,14 +160,14 @@ static void ehci_poll_PSS(struct ehci_hc
 
 	if (want != actual) {
 
-		/* Poll again later, but give up after about 20 ms */
-		if (ehci->PSS_poll_count++ < 20) {
-			ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
-			return;
-		}
-		ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
-				want, actual);
+		/* Poll again later */
+		ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
+		return;
 	}
+
+	if (ehci->PSS_poll_count > 20)
+		ehci_dbg(ehci, "PSS poll count reached %d\n",
+				ehci->PSS_poll_count);
 	ehci->PSS_poll_count = 0;
 
 	/* The status is up-to-date; restart or stop the schedule as needed */
Index: 3.7/drivers/usb/host/ehci-q.c
===================================================================
--- 3.7.orig/drivers/usb/host/ehci-q.c
+++ 3.7/drivers/usb/host/ehci-q.c
@@ -1174,6 +1174,18 @@ submit_async (
 static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	struct ehci_qh		*prev;
+	__hc32			dma = QH_NEXT(ehci, qh->qh_dma);
+	__hc32			dma_next = qh->hw->hw_next;
+
+	/* No QH on the unlink lists should point to qh */
+	for (prev = ehci->async_unlink; prev; prev = prev->unlink_next) {
+		if (prev->hw->hw_next == dma)
+			prev->hw->hw_next = dma_next;
+	}
+	for (prev = ehci->async_iaa; prev; prev = prev->unlink_next) {
+		if (prev->hw->hw_next == dma)
+			prev->hw->hw_next = dma_next;
+	}
 
 	/* Add to the end of the list of QHs waiting for the next IAAD */
 	qh->qh_state = QH_STATE_UNLINK;
@@ -1188,7 +1200,7 @@ static void single_unlink_async(struct e
 	while (prev->qh_next.qh != qh)
 		prev = prev->qh_next.qh;
 
-	prev->hw->hw_next = qh->hw->hw_next;
+	prev->hw->hw_next = dma_next;
 	prev->qh_next = qh->qh_next;
 	if (ehci->qh_scan_next == qh)
 		ehci->qh_scan_next = qh->qh_next.qh;

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux