Re: [PATCH 14/25] USB: EHCI: use hrtimer for controller death

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

 



On Wed, Jul 11, 2012 at 11:22 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:
> This patch (as1578) adds an hrtimer event to handle the death of an
> EHCI controller.  When a controller dies, it doesn't necessarily stop
> running right away.  The new event polls at 1-ms intervals to see when
> all activity has safely stopped.  This replaces a busy-wait polling
> loop in the current code.
>
> Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
>
> ---
>
>  drivers/usb/host/ehci-hcd.c   |   20 ++++++++++----------
>  drivers/usb/host/ehci-timer.c |   26 ++++++++++++++++++++++++++
>  drivers/usb/host/ehci.h       |    2 ++
>  3 files changed, 38 insertions(+), 10 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
> @@ -81,6 +81,7 @@ enum ehci_rh_state {
>  enum ehci_hrtimer_event {
>         EHCI_HRTIMER_POLL_ASS,          /* Poll for async schedule off */
>         EHCI_HRTIMER_POLL_PSS,          /* Poll for periodic schedule off */
> +       EHCI_HRTIMER_POLL_DEAD,         /* Wait for dead controller to stop */
>         EHCI_HRTIMER_UNLINK_INTR,       /* Wait for interrupt QH unlink */
>         EHCI_HRTIMER_DISABLE_PERIODIC,  /* Wait to disable periodic sched */
>         EHCI_HRTIMER_DISABLE_ASYNC,     /* Wait to disable async sched */
> @@ -97,6 +98,7 @@ struct ehci_hcd {                     /* one per controlle
>
>         int                     PSS_poll_count;
>         int                     ASS_poll_count;
> +       int                     died_poll_count;
>
>         /* glue to PCI and HCD framework */
>         struct ehci_caps __iomem *caps;
> 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
> @@ -888,20 +888,20 @@ static irqreturn_t ehci_irq (struct usb_
>         /* PCI errors [4.15.2.4] */
>         if (unlikely ((status & STS_FATAL) != 0)) {
>                 ehci_err(ehci, "fatal error\n");
> -               ehci->rh_state = EHCI_RH_STOPPING;
>                 dbg_cmd(ehci, "fatal", cmd);
>                 dbg_status(ehci, "fatal", status);
> -               ehci_halt(ehci);
>  dead:
> -               ehci->enabled_hrtimer_events = 0;
> -               hrtimer_try_to_cancel(&ehci->hrtimer);
> -               ehci_reset(ehci);
> -               ehci_writel(ehci, 0, &ehci->regs->configured_flag);
>                 usb_hc_died(hcd);
> -               /* generic layer kills/unlinks all urbs, then
> -                * uses ehci_stop to clean up the rest
> -                */
> -               bh = 1;
> +
> +               /* Don't let the controller do anything more */
> +               ehci->rh_state = EHCI_RH_STOPPING;
> +               ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
> +               ehci_writel(ehci, ehci->command, &ehci->regs->command);
> +               ehci_writel(ehci, 0, &ehci->regs->intr_enable);
> +               ehci_handle_controller_death(ehci);
> +
> +               /* Handle completions when the controller stops */
> +               bh = 0;
>         }
>
>         if (bh)
> Index: usb-3.4/drivers/usb/host/ehci-timer.c
> ===================================================================
> --- usb-3.4.orig/drivers/usb/host/ehci-timer.c
> +++ usb-3.4/drivers/usb/host/ehci-timer.c
> @@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struc
>  static unsigned event_delays_ns[] = {
>         1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_ASS */
>         1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_PSS */
> +       1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_DEAD */
>         1125 * NSEC_PER_USEC,   /* EHCI_HRTIMER_UNLINK_INTR */
>         10 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_DISABLE_PERIODIC */
>         15 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_DISABLE_ASYNC */
> @@ -193,6 +194,30 @@ static void ehci_disable_PSE(struct ehci
>  }
>
>
> +/* Poll the STS_HALT status bit; see when a dead controller stops */
> +static void ehci_handle_controller_death(struct ehci_hcd *ehci)
> +{
> +       if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) {

If ehci_stop is runing when the EHCI_HRTIMER_POLL_DEAD event
is pending, there may be one race since ehci->enabled_hrtimer_events
is cleared in ehci_stop.

> +
> +               /* Give up after a few milliseconds */
> +               if (ehci->died_poll_count++ < 5) {
> +                       /* Try again later */
> +                       ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true);
> +                       return;
> +               }
> +               ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n");
> +       }
> +
> +       /* Clean up the mess */
> +       ehci->rh_state = EHCI_RH_HALTED;
> +       ehci_writel(ehci, 0, &ehci->regs->configured_flag);
> +       ehci_writel(ehci, 0, &ehci->regs->intr_enable);
> +       ehci_work(ehci);
> +
> +       /* Not in process context, so don't try to reset the controller */
> +}
> +
> +
>  /* Handle unlinked interrupt QHs once they are gone from the hardware */
>  static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
>  {
> @@ -233,6 +258,7 @@ static void ehci_handle_intr_unlinks(str
>  static void (*event_handlers[])(struct ehci_hcd *) = {
>         ehci_poll_ASS,                  /* EHCI_HRTIMER_POLL_ASS */
>         ehci_poll_PSS,                  /* EHCI_HRTIMER_POLL_PSS */
> +       ehci_handle_controller_death,   /* EHCI_HRTIMER_POLL_DEAD */
>         ehci_handle_intr_unlinks,       /* EHCI_HRTIMER_UNLINK_INTR */
>         ehci_disable_PSE,               /* EHCI_HRTIMER_DISABLE_PERIODIC */
>         ehci_disable_ASE,               /* EHCI_HRTIMER_DISABLE_ASYNC */
>
>
> --
> 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