| From 00b58d586797600620b79dd5b56474a80d676be6 Mon Sep 17 00:00:00 2001 |
| From: Lu Baolu <baolu.lu@linux.intel.com> |
| Date: Tue, 3 Jan 2017 18:28:49 +0200 |
| Subject: [PATCH] usb: xhci: hold lock over xhci_abort_cmd_ring() |
| |
| commit 4dea70778c0f48b4385c7720c363ec8d37a401b4 upstream. |
| |
| In command timer function, xhci_handle_command_timeout(), xhci->lock |
| is unlocked before call into xhci_abort_cmd_ring(). This might cause |
| race between the timer function and the event handler. |
| |
| The xhci_abort_cmd_ring() function sets the CMD_RING_ABORT bit in the |
| command register and polling it until the setting takes effect. A stop |
| command ring event might be handled between writing the abort bit and |
| polling for it. The event handler will restart the command ring, which |
| causes the failure of polling, and we ever believed that we failed to |
| stop it. |
| |
| As a bonus, this also fixes some issues of calling functions without |
| locking in xhci_handle_command_timeout(). |
| |
| Cc: <stable@vger.kernel.org> # 3.7+ |
| Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c |
| index 3c815592002e..7fe375075687 100644 |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -1291,29 +1291,34 @@ void xhci_handle_command_timeout(unsigned long data) |
| hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); |
| if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) && |
| (hw_ring_state & CMD_RING_RUNNING)) { |
| - spin_unlock_irqrestore(&xhci->lock, flags); |
| xhci_dbg(xhci, "Command timeout\n"); |
| ret = xhci_abort_cmd_ring(xhci); |
| if (unlikely(ret == -ESHUTDOWN)) { |
| xhci_err(xhci, "Abort command ring failed\n"); |
| xhci_cleanup_command_queue(xhci); |
| + 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; |
| } |
| - return; |
| + |
| + goto time_out_completed; |
| } |
| |
| /* command ring failed to restart, or host removed. Bail out */ |
| if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) { |
| - spin_unlock_irqrestore(&xhci->lock, flags); |
| xhci_dbg(xhci, "command timed out twice, ring start fail?\n"); |
| xhci_cleanup_command_queue(xhci); |
| - return; |
| + |
| + goto time_out_completed; |
| } |
| |
| /* command timeout on stopped ring, ring can't be aborted */ |
| xhci_dbg(xhci, "Command timeout on stopped ring\n"); |
| xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd); |
| + |
| +time_out_completed: |
| spin_unlock_irqrestore(&xhci->lock, flags); |
| return; |
| } |
| -- |
| 2.10.1 |
| |