Re: [PATCH 25/25] USB: EHCI: resolve some unlikely races

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

 



On Wed, Jul 11, 2012 at 11:23 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
> This patch (as1589) resolves some unlikely races involving system
> shutdown or controller death in ehci-hcd:
>
>         Shutdown races with both root-hub resume and controller
>         resume.
>
>         Controller death races with root-hub suspend.
>
> A new bitflag is added to indicate that the controller has been shut
> down (whether for system shutdown or because it died).  Tests are
> added in the suspend and resume pathways to avoid reactivating the
> controller after any sort of shutdown.
>
> Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
>
> ---
>
>  drivers/usb/host/ehci-hcd.c |   20 +++++++++++++++++++-
>  drivers/usb/host/ehci-hub.c |   27 +++++++++++++++++++++++----
>  drivers/usb/host/ehci.h     |    1 +
>  3 files changed, 43 insertions(+), 5 deletions(-)
>
> Index: usb-3.4/drivers/usb/host/ehci.h
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci.h
> +++ usb-3.4/drivers/usb/host/ehci.h
> @@ -118,6 +118,7 @@ struct ehci_hcd {                   /* one per controlle
>         bool                    need_rescan:1;
>         bool                    intr_unlinking:1;
>         bool                    async_unlinking:1;
> +       bool                    shutdown:1;
>         struct ehci_qh          *qh_scan_next;
>
>         /* async schedule support */
> Index: usb-3.4/drivers/usb/host/ehci-hcd.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci-hcd.c
> +++ usb-3.4/drivers/usb/host/ehci-hcd.c
> @@ -343,6 +343,7 @@ static void ehci_shutdown(struct usb_hcd
>         struct ehci_hcd *ehci = hcd_to_ehci(hcd);
>
>         spin_lock_irq(&ehci->lock);
> +       ehci->shutdown = true;
>         ehci->rh_state = EHCI_RH_STOPPING;
>         ehci->enabled_hrtimer_events = 0;
>         spin_unlock_irq(&ehci->lock);
> @@ -823,6 +824,7 @@ dead:
>                 usb_hc_died(hcd);
>
>                 /* Don't let the controller do anything more */
> +               ehci->shutdown = true;
>                 ehci->rh_state = EHCI_RH_STOPPING;
>                 ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
>                 ehci_writel(ehci, ehci->command, &ehci->regs->command);
> @@ -1129,6 +1131,9 @@ static int __maybe_unused ehci_resume(st
>         /* Mark hardware accessible again as we are back to full power by now */
>         set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
>
> +       if (ehci->shutdown)
> +               return 0;               /* Controller is dead */
> +
>         /*
>          * If CF is still set and we aren't resuming from hibernation
>          * then we maintained suspend power.
> @@ -1139,10 +1144,17 @@ static int __maybe_unused ehci_resume(st
>                 int     mask = INTR_MASK;
>
>                 ehci_prepare_ports_for_controller_resume(ehci);
> +
> +               spin_lock_irq(&ehci->lock);
> +               if (ehci->shutdown)
> +                       goto skip;
> +
>                 if (!hcd->self.root_hub->do_remote_wakeup)
>                         mask &= ~STS_PCD;
>                 ehci_writel(ehci, mask, &ehci->regs->intr_enable);
>                 ehci_readl(ehci, &ehci->regs->intr_enable);
> + skip:
> +               spin_unlock_irq(&ehci->lock);
>                 return 0;
>         }
>
> @@ -1154,14 +1166,20 @@ static int __maybe_unused ehci_resume(st
>         (void) ehci_halt(ehci);
>         (void) ehci_reset(ehci);
>
> +       spin_lock_irq(&ehci->lock);
> +       if (ehci->shutdown)
> +               goto skip;
> +
>         ehci_writel(ehci, ehci->command, &ehci->regs->command);
>         ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
>         ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
>
> +       ehci->rh_state = EHCI_RH_SUSPENDED;
> +       spin_unlock_irq(&ehci->lock);
> +
>         /* here we "know" root ports should always stay powered */
>         ehci_port_power(ehci, 1);
>
> -       ehci->rh_state = EHCI_RH_SUSPENDED;
>         return 1;
>  }
>
> Index: usb-3.4/drivers/usb/host/ehci-hub.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci-hub.c
> +++ usb-3.4/drivers/usb/host/ehci-hub.c
> @@ -221,6 +221,8 @@ static int ehci_bus_suspend (struct usb_
>         ehci_quiesce(ehci);
>
>         spin_lock_irq (&ehci->lock);
> +       if (ehci->rh_state < EHCI_RH_RUNNING)
> +               goto done;
>
>         /* Once the controller is stopped, port resumes that are already
>          * in progress won't complete.  Hence if remote wakeup is enabled
> @@ -306,6 +308,10 @@ static int ehci_bus_suspend (struct usb_
>         ehci_halt (ehci);
>
>         spin_lock_irq(&ehci->lock);
> +       if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD))
> +               ehci_handle_controller_death(ehci);

I am wondering that why the above two lines are added, since ehci_halt has been
completed, and the hrtimer will be canceled soon.


> +       if (ehci->rh_state != EHCI_RH_RUNNING)
> +               goto done;
>         ehci->rh_state = EHCI_RH_SUSPENDED;
>
>         end_unlink_async(ehci);
> @@ -320,6 +326,7 @@ static int ehci_bus_suspend (struct usb_
>         ehci_writel(ehci, mask, &ehci->regs->intr_enable);
>         ehci_readl(ehci, &ehci->regs->intr_enable);
>
> + done:
>         ehci->next_statechange = jiffies + msecs_to_jiffies(10);
>         ehci->enabled_hrtimer_events = 0;
>         ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
> @@ -342,10 +349,8 @@ static int ehci_bus_resume (struct usb_h
>         if (time_before (jiffies, ehci->next_statechange))
>                 msleep(5);
>         spin_lock_irq (&ehci->lock);
> -       if (!HCD_HW_ACCESSIBLE(hcd)) {
> -               spin_unlock_irq(&ehci->lock);
> -               return -ESHUTDOWN;
> -       }
> +       if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown)
> +               goto shutdown;
>
>         if (unlikely(ehci->debug)) {
>                 if (!dbgp_reset_prep())
> @@ -384,6 +389,8 @@ static int ehci_bus_resume (struct usb_h
>         spin_unlock_irq(&ehci->lock);
>         msleep(8);
>         spin_lock_irq(&ehci->lock);
> +       if (ehci->shutdown)
> +               goto shutdown;
>
>         /* clear phy low-power mode before resume */
>         if (ehci->bus_suspended && ehci->has_hostpc) {
> @@ -401,6 +408,8 @@ static int ehci_bus_resume (struct usb_h
>                 spin_unlock_irq(&ehci->lock);
>                 msleep(5);
>                 spin_lock_irq(&ehci->lock);
> +               if (ehci->shutdown)
> +                       goto shutdown;
>         }
>
>         /* manually resume the ports we suspended during bus_suspend() */
> @@ -421,6 +430,8 @@ static int ehci_bus_resume (struct usb_h
>                 spin_unlock_irq(&ehci->lock);
>                 msleep(20);
>                 spin_lock_irq(&ehci->lock);
> +               if (ehci->shutdown)
> +                       goto shutdown;
>         }
>
>         i = HCS_N_PORTS (ehci->hcs_params);
> @@ -439,10 +450,18 @@ static int ehci_bus_resume (struct usb_h
>         ehci_handover_companion_ports(ehci);
>
>         /* Now we can safely re-enable irqs */
> +       spin_lock_irq(&ehci->lock);
> +       if (ehci->shutdown)
> +               goto shutdown;
>         ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
>         (void) ehci_readl(ehci, &ehci->regs->intr_enable);
> +       spin_unlock_irq(&ehci->lock);
>
>         return 0;
> +
> + shutdown:
> +       spin_unlock_irq(&ehci->lock);
> +       return -ESHUTDOWN;
>  }
>
>  #else
>
>
> --
> 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


Thanks,
-- 
Ming Lei
--
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