| From 092aab12364c88ee727e01265518f24ad657f12d Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Tue, 3 Jan 2017 18:28:48 +0200 |
| Subject: [PATCH] xhci: Handle command completion and timeout race |
| |
| commit a5a1b9514154437aa1ed35c291191f82fd3e941a upstream. |
| |
| If we get a command completion event at the same time as the command |
| timeout work starts on another cpu we might end up aborting the wrong |
| command. |
| |
| If the command completion takes the xhci lock before the timeout work, it |
| will handle the command, pick the next command, mark it as current_cmd, and |
| re-queue the timeout work. When the timeout work finally gets the lock |
| It will start aborting the wrong command. |
| |
| This case can be resolved by checking if the timeout work is pending inside |
| the timeout function itself. A new timeout work can only be pending if the |
| command completed and a new command was queued. |
| |
| If there are no more commands pending then command completion will set |
| the current_cmd to NULL, which is already handled in the timeout work. |
| |
| Cc: <stable@vger.kernel.org> |
| Reported-by: Baolin Wang <baolin.wang@linaro.org> |
| 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 8a8c133280ab..3c815592002e 100644 |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -1273,7 +1273,11 @@ void xhci_handle_command_timeout(unsigned long data) |
| |
| spin_lock_irqsave(&xhci->lock, flags); |
| |
| - if (!xhci->current_cmd) { |
| + /* |
| + * If timeout work is pending, or current_cmd is NULL, it means we |
| + * raced with command completion. Command is handled so just return. |
| + */ |
| + if (!xhci->current_cmd || timer_pending(&xhci->cmd_timer)) { |
| spin_unlock_irqrestore(&xhci->lock, flags); |
| return; |
| } |
| -- |
| 2.10.1 |
| |