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