This patch fixes the way ehci-hcd handles the iaa_in_progress flag. The current code is very careless about it.
The flag is meaningless when the root hub isn't running, most particularly after the root hub has been suspended. But in start_iaa_cycle(), the driver checks the flag before checking the root-hub's state. That routine also sets the flag too early, before it has definitely committed to starting an IAA cycle. Finally, when the root hub resumes we need to clear the flag, because any IAA cycle that was in progress when the suspend occurred will no longer be valid. The symptom of these problems is that following a bus resume (for example, the first time a new device is plugged into an unused EHCI controller), devices may not be detected properly and the khubd worker thread can hang. This happens because a QH unlink never finishes -- it's waiting for an IAA cycle that never got started because the iaa_in_progress flag was set when it shouldn't have been. This problem has existed for a long time, but recent changes have made it more likely to occur (the timing of QH unlinks has been changed). Signed-off-by: Alan Stern <st...@rowland.harvard.edu> Fixes: 214ac7a0771d ("USB: EHCI: improve end_unlink_async()") CC: <sta...@vger.kernel.org> --- [as1794] drivers/usb/host/ehci-hub.c | 3 +++ drivers/usb/host/ehci-q.c | 11 ++++------- 2 files changed, 7 insertions(+), 7 deletions(-) Index: usb-4.4/drivers/usb/host/ehci-hub.c =================================================================== --- usb-4.4.orig/drivers/usb/host/ehci-hub.c +++ usb-4.4/drivers/usb/host/ehci-hub.c @@ -418,6 +418,9 @@ static int ehci_bus_resume (struct usb_h ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci->rh_state = EHCI_RH_RUNNING; + /* If an IAA was in progress when we suspended, it's done by now */ + ehci->iaa_in_progress = false; + /* * According to Bugzilla #8190, the port status for some controllers * will be wrong without a delay. At their wrong status, the port Index: usb-4.4/drivers/usb/host/ehci-q.c =================================================================== --- usb-4.4.orig/drivers/usb/host/ehci-q.c +++ usb-4.4/drivers/usb/host/ehci-q.c @@ -1283,17 +1283,13 @@ static void single_unlink_async(struct e static void start_iaa_cycle(struct ehci_hcd *ehci) { - /* Do nothing if an IAA cycle is already running */ - if (ehci->iaa_in_progress) - return; - ehci->iaa_in_progress = true; - /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { end_unlink_async(ehci); - /* Otherwise start a new IAA cycle */ - } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { + /* Otherwise start a new IAA cycle if one isn't already running */ + } else if (ehci->rh_state == EHCI_RH_RUNNING && + !ehci->iaa_in_progress) { /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1301,6 +1297,7 @@ static void start_iaa_cycle(struct ehci_ ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command); ehci_readl(ehci, &ehci->regs->command); + ehci->iaa_in_progress = true; ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true); } } -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html