From: Gavin Li <g...@thegavinli.com>

If a stalling TRB is cancelled and NOOP'ed in xhci_handle_cmd_stop_ep(),
finish_td() never gets called to reset the halted endpoint and the
endpoint remains indefinitely stalled. This patch ensures that
xhci_cleanup_halted_endpoint() is called after a TRB completion if the
endpoint is halted, irregardless of the status of any pending TRBs.
---
 drivers/usb/host/xhci-ring.c | 34 +++++++++++++++++++++++++++++-----
 drivers/usb/host/xhci.h      |  1 +
 2 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 03f63f50afb6..85bc53d0d43e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1113,11 +1113,13 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
 {
        struct xhci_virt_device *vdev;
        struct xhci_ep_ctx *ep_ctx;
+       struct xhci_virt_ep *ep;
        unsigned int ep_index;
 
        ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
        vdev = xhci->devs[slot_id];
        ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
+       ep = &xhci->devs[slot_id]->eps[ep_index];
        trace_xhci_handle_cmd_reset_ep(ep_ctx);
 
        /* This command will only fail if the endpoint wasn't halted,
@@ -1130,6 +1132,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
         * command complete before the endpoint can be used.  Queue that here
         * because the HW can't handle two commands being queued in a row.
         */
+       ep->ep_state &= ~EP_RESET_PENDING;
        if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
                struct xhci_command *command;
 
@@ -1145,7 +1148,11 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd 
*xhci, int slot_id,
                xhci_ring_cmd_db(xhci);
        } else {
                /* Clear our internal halted state */
-               xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
+               ep->ep_state &= ~EP_HALTED;
+               if (!(ep->ep_state & SET_DEQ_PENDING)) {
+                       /* Restart any rings with pending URBs */
+                       ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+               }
        }
 }
 
@@ -1800,19 +1807,25 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
 static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
                unsigned int slot_id, unsigned int ep_index,
                unsigned int stream_id,
-               struct xhci_td *td, union xhci_trb *ep_trb)
+               struct xhci_td *td)
 {
        struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
        struct xhci_command *command;
+
+       if (ep->ep_state & EP_RESET_PENDING)
+               return;
+
        command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
        if (!command)
                return;
 
-       ep->ep_state |= EP_HALTED;
+       ep->ep_state |= EP_HALTED | EP_RESET_PENDING;
        ep->stopped_stream = stream_id;
 
        xhci_queue_reset_ep(xhci, command, slot_id, ep_index);
-       xhci_cleanup_stalled_ring(xhci, ep_index, td);
+       if (td != NULL && !(ep->ep_state & SET_DEQ_PENDING)) {
+               xhci_cleanup_stalled_ring(xhci, ep_index, td);
+       }
 
        ep->stopped_stream = 0;
 
@@ -1947,7 +1960,7 @@ static int finish_td(struct xhci_hcd *xhci, struct 
xhci_td *td,
                 * The class driver clears the device side halt later.
                 */
                xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
-                                       ep_ring->stream_id, td, ep_trb);
+                                       ep_ring->stream_id, td);
        } else {
                /* Update ring dequeue pointer */
                while (ep_ring->dequeue != td->last_trb)
@@ -2588,6 +2601,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
         */
        } while (handling_skipped_tds);
 
+       /*
+        * If a cancelled TRB halts the endpoint, reset it here.
+        */
+       if (trb_comp_code == COMP_STALL_ERROR ||
+               xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+                                               trb_comp_code)) {
+               /* No harm in calling this twice; second call will be a no-op */
+               xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
+                               ep_ring->stream_id, td);
+       }
+
        return 0;
 }
 
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a986d5e..0f7439f4d414 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -922,6 +922,7 @@ struct xhci_virt_ep {
 #define EP_HAS_STREAMS         (1 << 4)
 /* Transitioning the endpoint to not using streams, don't enqueue URBs */
 #define EP_GETTING_NO_STREAMS  (1 << 5)
+#define EP_RESET_PENDING      (1 << 6) /* For stall recovery */
        /* ----  Related to URB cancellation ---- */
        struct list_head        cancelled_td_list;
        struct xhci_td          *stopped_td;
-- 
2.12.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to