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