aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2021-01-29 15:00:40 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-01-29 14:16:51 +0100
commit1174d44906d517ddb4f98bbe58a44d4554d0ef90 (patch)
tree09cc9be07e5e24e4b5cefb4b8c595826a0b42deb /drivers/usb/host/xhci-ring.c
parentxhci: Fix halted endpoint at stop endpoint command completion (diff)
downloadlinux-dev-1174d44906d517ddb4f98bbe58a44d4554d0ef90.tar.xz
linux-dev-1174d44906d517ddb4f98bbe58a44d4554d0ef90.zip
xhci: handle stop endpoint command completion with endpoint in running state.
Handle race where a stop endpoint command fails with "context state error" as hardware hasn't actually started the ring yet after a previous urb cancellation completed and restarted the endpoint. Flushing the doorbell write that restart the endpoint reduced these cases, but didn't completely resolve them. Check if the ring is running in the stop endpoint completion handler, and issue a new stop endpoint command in this case. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20210129130044.206855-24-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.c13
1 files changed, 13 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3ba91580e222..307ccd40ccac 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -983,6 +983,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
struct xhci_ep_ctx *ep_ctx;
struct xhci_td *td = NULL;
enum xhci_ep_reset_type reset_type;
+ struct xhci_command *command;
if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
if (!xhci->devs[slot_id])
@@ -1030,6 +1031,18 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
xhci_handle_halted_endpoint(xhci, ep, 0, td, reset_type);
xhci_stop_watchdog_timer_in_irq(xhci, ep);
return;
+ case EP_STATE_RUNNING:
+ /* Race, HW handled stop ep cmd before ep was running */
+ command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
+ if (!command)
+ xhci_stop_watchdog_timer_in_irq(xhci, ep);
+
+ mod_timer(&ep->stop_cmd_timer,
+ jiffies + XHCI_STOP_EP_CMD_TIMEOUT * HZ);
+ xhci_queue_stop_endpoint(xhci, command, slot_id, ep_index, 0);
+ xhci_ring_cmd_db(xhci);
+
+ return;
default:
break;
}