USB: EHCI: improve handling of the ehci->iaa_in_progress flag
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 25 Jan 2016 20:44:16 +0000 (15:44 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 3 Feb 2016 21:14:52 +0000 (13:14 -0800)
This patch improves the way ehci-hcd handles the iaa_in_progress flag.
The current code is somewhat careless in this regard:

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.  They should be checked in the opposite
order.

That routine also sets the flag too early, before it has
definitely committed to starting an IAA cycle.

The flag is turned off in end_unlink_async().  Upcoming
changes will call that routine at other times, not just at the
end of an IAA cycle.  The two actions are logically separate
(although related), so we separate out a new routine to be
called in place of end_unlink_async() whenever an IAA cycle
ends: end_iaa_cycle().

iaa_in_progress should be turned off when the root hub is
suspended -- we certainly don't want it still to be set when
the root hub resumes.  Therefore the call to
end_unlink_async() in ehci_bus_suspend() should also be
replaced with a call to end_iaa_cycle().

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-timer.c

index c5465bf9a798028f0d9372af9c64df181a65dff1..84c41262109cb0cdbe2d0e00867005665a46039f 100644 (file)
@@ -306,6 +306,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 
 /*-------------------------------------------------------------------------*/
 
+static void end_iaa_cycle(struct ehci_hcd *ehci);
 static void end_unlink_async(struct ehci_hcd *ehci);
 static void unlink_empty_async(struct ehci_hcd *ehci);
 static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
@@ -758,7 +759,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
                        ehci_dbg(ehci, "IAA with IAAD still set?\n");
                if (ehci->iaa_in_progress)
                        COUNT(ehci->stats.iaa);
-               end_unlink_async(ehci);
+               end_iaa_cycle(ehci);
        }
 
        /* remote wakeup [4.3.1] */
index 086a7115d263c673ec4a4e10abe1a386546b8338..6d84ce2edc271c0e2f6dd264d62b1e35a49490e6 100644 (file)
@@ -347,7 +347,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
                goto done;
        ehci->rh_state = EHCI_RH_SUSPENDED;
 
-       end_unlink_async(ehci);
+       /* Any IAA cycle that started before the suspend is now invalid */
+       end_iaa_cycle(ehci);
        unlink_empty_async_suspended(ehci);
        ehci_handle_start_intr_unlinks(ehci);
        ehci_handle_intr_unlinks(ehci);
index 1b42bcb59743ef63e72540cefabe33c55d47eeec..15dcae8f063164f855d77fef0df4fc96df55b2bf 100644 (file)
@@ -1283,17 +1283,13 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
 
 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,17 +1297,13 @@ static void start_iaa_cycle(struct ehci_hcd *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);
        }
 }
 
-/* the async qh for the qtds being unlinked are now gone from the HC */
-
-static void end_unlink_async(struct ehci_hcd *ehci)
+static void end_iaa_cycle(struct ehci_hcd *ehci)
 {
-       struct ehci_qh          *qh;
-       bool                    early_exit;
-
        if (ehci->has_synopsys_hc_bug)
                ehci_writel(ehci, (u32) ehci->async->qh_dma,
                            &ehci->regs->async_next);
@@ -1319,6 +1311,16 @@ static void end_unlink_async(struct ehci_hcd *ehci)
        /* The current IAA cycle has ended */
        ehci->iaa_in_progress = false;
 
+       end_unlink_async(ehci);
+}
+
+/* See if the async qh for the qtds being unlinked are now gone from the HC */
+
+static void end_unlink_async(struct ehci_hcd *ehci)
+{
+       struct ehci_qh          *qh;
+       bool                    early_exit;
+
        if (list_empty(&ehci->async_unlink))
                return;
        qh = list_first_entry(&ehci->async_unlink, struct ehci_qh,
index 37a3e0dece842df418cf3f40d8f2ef7fc7f35f85..6bea4d553ec5aeb104989a6e81aeaa7ddbdc5aab 100644 (file)
@@ -361,7 +361,7 @@ static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
        }
 
        ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd);
-       end_unlink_async(ehci);
+       end_iaa_cycle(ehci);
 }