]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
xhci: Avoid queuing redundant Stop Endpoint command for stalled endpoint
authorMathias Nyman <mathias.nyman@linux.intel.com>
Tue, 11 Mar 2025 15:45:49 +0000 (17:45 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 11 Mar 2025 16:58:43 +0000 (17:58 +0100)
If EP_STALLED flag is set in xhci_urb_dequeue(), without EP_HALTED or
SET_DEQ_PENDING flags, then the endpoint is in stopped state and the
cancelled URB can be given back immediately withouth queueing
a 'stop endpoint' command.

Without this change the cancelled URB would eventually be given back
in the 'context state error' completion path of the 'stop endpoint'
command. This is not optimal.

For this improvement to work the EP_STALLED flag must be cleared with
xhci lock held.

Suggested-by: Michal Pecio <michal.pecio@gmail.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20250311154551.4035726-2-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci.c

index 1852175f48c1e499e930d5dbc2d0be15c6d28afd..19e308f4fc06ba683a7e4291fdaf5cf5292a31d7 100644 (file)
@@ -1773,8 +1773,8 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                goto done;
        }
 
-       /* In this case no commands are pending but the endpoint is stopped */
-       if (ep->ep_state & EP_CLEARING_TT) {
+       /* In these cases no commands are pending but the endpoint is stopped */
+       if (ep->ep_state & (EP_CLEARING_TT | EP_STALLED)) {
                /* and cancelled TDs can be given back right away */
                xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n",
                                urb->dev->slot_id, ep_index, ep->ep_state);
@@ -3211,10 +3211,12 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
                return;
 
        ep = &vdev->eps[ep_index];
+
+       spin_lock_irqsave(&xhci->lock, flags);
+
        ep->ep_state &= ~EP_STALLED;
 
        /* Bail out if toggle is already being cleared by a endpoint reset */
-       spin_lock_irqsave(&xhci->lock, flags);
        if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) {
                ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE;
                spin_unlock_irqrestore(&xhci->lock, flags);