Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: EHCI: use hrtimer for controller death

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@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
bf6387bc df202255

+38 -10
+10 -10
drivers/usb/host/ehci-hcd.c
··· 888 888 /* PCI errors [4.15.2.4] */ 889 889 if (unlikely ((status & STS_FATAL) != 0)) { 890 890 ehci_err(ehci, "fatal error\n"); 891 - ehci->rh_state = EHCI_RH_STOPPING; 892 891 dbg_cmd(ehci, "fatal", cmd); 893 892 dbg_status(ehci, "fatal", status); 894 - ehci_halt(ehci); 895 893 dead: 896 - ehci->enabled_hrtimer_events = 0; 897 - hrtimer_try_to_cancel(&ehci->hrtimer); 898 - ehci_reset(ehci); 899 - ehci_writel(ehci, 0, &ehci->regs->configured_flag); 900 894 usb_hc_died(hcd); 901 - /* generic layer kills/unlinks all urbs, then 902 - * uses ehci_stop to clean up the rest 903 - */ 904 - bh = 1; 895 + 896 + /* Don't let the controller do anything more */ 897 + ehci->rh_state = EHCI_RH_STOPPING; 898 + ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); 899 + ehci_writel(ehci, ehci->command, &ehci->regs->command); 900 + ehci_writel(ehci, 0, &ehci->regs->intr_enable); 901 + ehci_handle_controller_death(ehci); 902 + 903 + /* Handle completions when the controller stops */ 904 + bh = 0; 905 905 } 906 906 907 907 if (bh)
+26
drivers/usb/host/ehci-timer.c
··· 69 69 static unsigned event_delays_ns[] = { 70 70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */ 71 71 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 72 + 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ 72 73 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ 73 74 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 74 75 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ ··· 194 193 } 195 194 196 195 196 + /* Poll the STS_HALT status bit; see when a dead controller stops */ 197 + static void ehci_handle_controller_death(struct ehci_hcd *ehci) 198 + { 199 + if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) { 200 + 201 + /* Give up after a few milliseconds */ 202 + if (ehci->died_poll_count++ < 5) { 203 + /* Try again later */ 204 + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true); 205 + return; 206 + } 207 + ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n"); 208 + } 209 + 210 + /* Clean up the mess */ 211 + ehci->rh_state = EHCI_RH_HALTED; 212 + ehci_writel(ehci, 0, &ehci->regs->configured_flag); 213 + ehci_writel(ehci, 0, &ehci->regs->intr_enable); 214 + ehci_work(ehci); 215 + 216 + /* Not in process context, so don't try to reset the controller */ 217 + } 218 + 219 + 197 220 /* Handle unlinked interrupt QHs once they are gone from the hardware */ 198 221 static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) 199 222 { ··· 258 233 static void (*event_handlers[])(struct ehci_hcd *) = { 259 234 ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */ 260 235 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ 236 + ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ 261 237 ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ 262 238 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 263 239 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
+2
drivers/usb/host/ehci.h
··· 81 81 enum ehci_hrtimer_event { 82 82 EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ 83 83 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ 84 + EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ 84 85 EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ 85 86 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 86 87 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ ··· 98 97 99 98 int PSS_poll_count; 100 99 int ASS_poll_count; 100 + int died_poll_count; 101 101 102 102 /* glue to PCI and HCD framework */ 103 103 struct ehci_caps __iomem *caps;