[RFC 02/10] xhci: Don't hold spinlock while stopping the command ring.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



xhci_abort_cmd_ring() spins for up to five seconds while waiting for a
register read to show that the command ring is stopped.  It is holding
the xhci->spinlock with IRQs disabled, which is not good on xHCI hosts
that share legacy PCI interrupts with other PCI devices.

Make the xhci_abort_cmd_ring() simply return true if we need to wait on
the command ring to stop, and make xhci_cancel_cmd() drop the spinlock
and wait on the command ring, if necessary.

This patch should be backported to stable kernels as old as 3.0, that
contain the commit b92cc66c047ff7cf587b318fe377061a353c120f "xHCI: add
aborting command ring function".

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Cc: Elric Fu <elricfu1@xxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
---
 drivers/usb/host/xhci-ring.c |   71 +++++++++++++++++++++--------------------
 1 files changed, 36 insertions(+), 35 deletions(-)

diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 8828754..0e32e19 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -289,10 +289,10 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
 	xhci_readl(xhci, &xhci->dba->doorbell[0]);
 }
 
+/* Returns 1 if we need to wait on the command ring stop write. */
 static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
 {
 	u64 temp_64;
-	int ret;
 
 	xhci_dbg(xhci, "Abort command ring\n");
 
@@ -311,25 +311,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
 	xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
 			&xhci->op_regs->cmd_ring);
 
-	/* Section 4.6.1.2 of xHCI 1.0 spec says software should
-	 * time the completion od all xHCI commands, including
-	 * the Command Abort operation. If software doesn't see
-	 * CRR negated in a timely manner (e.g. longer than 5
-	 * seconds), then it should assume that the there are
-	 * larger problems with the xHC and assert HCRST.
-	 */
-	ret = xhci_handshake(xhci, &xhci->op_regs->cmd_ring,
-			CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
-	if (ret < 0) {
-		xhci_err(xhci, "Stopped the command ring failed, "
-				"maybe the host is dead\n");
-		xhci->xhc_state |= XHCI_STATE_DYING;
-		xhci_quiesce(xhci);
-		xhci_halt(xhci);
-		return -ESHUTDOWN;
-	}
-
-	return 0;
+	return 1;
 }
 
 static int xhci_queue_cd(struct xhci_hcd *xhci,
@@ -369,32 +351,51 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
 	if (xhci->xhc_state & XHCI_STATE_DYING) {
 		xhci_warn(xhci, "Abort the command ring,"
 				" but the xHCI is dead.\n");
-		retval = -ESHUTDOWN;
-		goto fail;
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		return -ESHUTDOWN;
 	}
 
 	/* queue the cmd desriptor to cancel_cmd_list */
 	retval = xhci_queue_cd(xhci, command, cmd_trb);
 	if (retval) {
 		xhci_warn(xhci, "Queuing command descriptor failed.\n");
-		goto fail;
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		return retval;
 	}
-
 	/* abort command ring */
 	retval = xhci_abort_cmd_ring(xhci);
-	if (retval) {
-		xhci_err(xhci, "Abort command ring failed\n");
-		if (unlikely(retval == -ESHUTDOWN)) {
-			spin_unlock_irqrestore(&xhci->lock, flags);
-			usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
-			xhci_dbg(xhci, "xHCI host controller is dead.\n");
-			return retval;
-		}
-	}
+	spin_unlock_irqrestore(&xhci->lock, flags);
+
+	if (!retval)
+		return 0;
+
+	/* Section 4.6.1.2 of xHCI 1.0 spec says software should
+	 * time the completion od all xHCI commands, including
+	 * the Command Abort operation. If software doesn't see
+	 * CRR negated in a timely manner (e.g. longer than 5
+	 * seconds), then it should assume that the there are
+	 * larger problems with the xHC and assert HCRST.
+	 */
+	retval = xhci_handshake(xhci, &xhci->op_regs->cmd_ring,
+			CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
+	if (retval == 0)
+		return 0;
+
+	xhci_err(xhci, "Stopped the command ring failed, "
+			"maybe the host is dead\n");
 
-fail:
+	spin_lock_irqsave(&xhci->lock, flags);
+	xhci->xhc_state |= XHCI_STATE_DYING;
 	spin_unlock_irqrestore(&xhci->lock, flags);
-	return retval;
+
+	xhci_quiesce(xhci);
+	xhci_halt(xhci);
+
+	xhci_err(xhci, "Abort command ring failed\n");
+	usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+	xhci_dbg(xhci, "xHCI host controller is dead.\n");
+
+	return -ESHUTDOWN;
 }
 
 void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
-- 
1.7.9

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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux