summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2017-01-23 13:19:53 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-25 10:57:34 +0100
commitf99265965b3203baf5266994578db14851fbf7fa (patch)
treecef685d96a4e5a1253f10f4966427d3bf4d65d21 /drivers/usb/host/xhci-ring.c
parentxhci: rename EP_HALT_PENDING to EP_STOP_CMD_PENDING (diff)
downloadlinux-f99265965b3203baf5266994578db14851fbf7fa.tar.xz
linux-f99265965b3203baf5266994578db14851fbf7fa.zip
xhci: detect stop endpoint race using pending timer instead of counter.
A counter was used to find out if the stop endpoint completion raced with the stop endpoint timeout timer. This was needed in case the stop ep completion failed to delete the timer as it was running on anoter cpu. The EP_STOP_CMD_PENDING flag was not enough as a new stop endpoint command may be queued between the command completion and timeout function, which would set the flag back. Instead of the separate counter that was used we can detect the race by checking both the STOP_EP_PENDING flag and timer_pending in the timeout function. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c27
1 files changed, 11 insertions, 16 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 213cb02cbfdb..2ce132b44525 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -627,12 +627,8 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
ep->ep_state &= ~EP_STOP_CMD_PENDING;
- /* Can't del_timer_sync in interrupt, so we attempt to cancel. If the
- * timer is running on another CPU, we don't decrement stop_cmds_pending
- * (since we didn't successfully stop the watchdog timer).
- */
- if (del_timer(&ep->stop_cmd_timer))
- ep->stop_cmds_pending--;
+ /* Can't del_timer_sync in interrupt */
+ del_timer(&ep->stop_cmd_timer);
}
/*
@@ -895,10 +891,8 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
* simple flag to say whether there is a pending stop endpoint command for a
* particular endpoint.
*
- * Instead we use a combination of that flag and a counter for the number of
- * pending stop endpoint commands. If the timer is the tail end of the last
- * stop endpoint command, and the endpoint's command is still pending, we assume
- * the host is dying.
+ * Instead we use a combination of that flag and checking if a new timer is
+ * pending.
*/
void xhci_stop_endpoint_command_watchdog(unsigned long arg)
{
@@ -912,13 +906,11 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
spin_lock_irqsave(&xhci->lock, flags);
- ep->stop_cmds_pending--;
-
- if (ep->stop_cmds_pending || !(ep->ep_state & EP_STOP_CMD_PENDING)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Stop EP timer ran, but no command pending, "
- "exiting.");
+ /* bail out if cmd completed but raced with stop ep watchdog timer.*/
+ if (!(ep->ep_state & EP_STOP_CMD_PENDING) ||
+ timer_pending(&ep->stop_cmd_timer)) {
spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit");
return;
}
@@ -927,7 +919,10 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
/* Oops, HC is dead or dying or at least not responding to the stop
* endpoint command.
*/
+
xhci->xhc_state |= XHCI_STATE_DYING;
+ ep->ep_state &= ~EP_STOP_CMD_PENDING;
+
/* Disable interrupts from the host controller and start halting it */
xhci_quiesce(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);