Commit f9926596 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

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: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9983a5fc
...@@ -627,12 +627,8 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci, ...@@ -627,12 +627,8 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep) struct xhci_virt_ep *ep)
{ {
ep->ep_state &= ~EP_STOP_CMD_PENDING; ep->ep_state &= ~EP_STOP_CMD_PENDING;
/* Can't del_timer_sync in interrupt, so we attempt to cancel. If the /* Can't del_timer_sync in interrupt */
* timer is running on another CPU, we don't decrement stop_cmds_pending del_timer(&ep->stop_cmd_timer);
* (since we didn't successfully stop the watchdog timer).
*/
if (del_timer(&ep->stop_cmd_timer))
ep->stop_cmds_pending--;
} }
/* /*
...@@ -895,10 +891,8 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, ...@@ -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 * simple flag to say whether there is a pending stop endpoint command for a
* particular endpoint. * particular endpoint.
* *
* Instead we use a combination of that flag and a counter for the number of * Instead we use a combination of that flag and checking if a new timer is
* pending stop endpoint commands. If the timer is the tail end of the last * pending.
* stop endpoint command, and the endpoint's command is still pending, we assume
* the host is dying.
*/ */
void xhci_stop_endpoint_command_watchdog(unsigned long arg) void xhci_stop_endpoint_command_watchdog(unsigned long arg)
{ {
...@@ -912,13 +906,11 @@ 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); spin_lock_irqsave(&xhci->lock, flags);
ep->stop_cmds_pending--; /* bail out if cmd completed but raced with stop ep watchdog timer.*/
if (!(ep->ep_state & EP_STOP_CMD_PENDING) ||
if (ep->stop_cmds_pending || !(ep->ep_state & EP_STOP_CMD_PENDING)) { timer_pending(&ep->stop_cmd_timer)) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Stop EP timer ran, but no command pending, "
"exiting.");
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit");
return; return;
} }
...@@ -927,7 +919,10 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) ...@@ -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 /* Oops, HC is dead or dying or at least not responding to the stop
* endpoint command. * endpoint command.
*/ */
xhci->xhc_state |= XHCI_STATE_DYING; xhci->xhc_state |= XHCI_STATE_DYING;
ep->ep_state &= ~EP_STOP_CMD_PENDING;
/* Disable interrupts from the host controller and start halting it */ /* Disable interrupts from the host controller and start halting it */
xhci_quiesce(xhci); xhci_quiesce(xhci);
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
......
...@@ -1570,7 +1570,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ...@@ -1570,7 +1570,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
goto done; goto done;
} }
ep->ep_state |= EP_STOP_CMD_PENDING; ep->ep_state |= EP_STOP_CMD_PENDING;
ep->stop_cmds_pending++;
ep->stop_cmd_timer.expires = jiffies + ep->stop_cmd_timer.expires = jiffies +
XHCI_STOP_EP_CMD_TIMEOUT * HZ; XHCI_STOP_EP_CMD_TIMEOUT * HZ;
add_timer(&ep->stop_cmd_timer); add_timer(&ep->stop_cmd_timer);
......
...@@ -924,7 +924,6 @@ struct xhci_virt_ep { ...@@ -924,7 +924,6 @@ struct xhci_virt_ep {
unsigned int stopped_stream; unsigned int stopped_stream;
/* Watchdog timer for stop endpoint command to cancel URBs */ /* Watchdog timer for stop endpoint command to cancel URBs */
struct timer_list stop_cmd_timer; struct timer_list stop_cmd_timer;
int stop_cmds_pending;
struct xhci_hcd *xhci; struct xhci_hcd *xhci;
/* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
* command. We'll need to update the ring's dequeue segment and dequeue * command. We'll need to update the ring's dequeue segment and dequeue
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment