| From 688d6e2dce21300107ab688740a276217471b9f7 Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Wed, 11 Jan 2017 17:10:34 +0200 |
| Subject: [PATCH] xhci: fix deadlock at host remove by running watchdog |
| correctly |
| |
| commit d6169d04097fd9ddf811e63eae4e5cd71e6666e2 upstream. |
| |
| If a URB is killed while the host is removed we can end up in a situation |
| where the hub thread takes the roothub device lock, and waits for |
| the URB to be given back by xhci-hcd, blocking the host remove code. |
| |
| xhci-hcd tries to stop the endpoint and give back the urb, but can't |
| as the host is removed from PCI bus at the same time, preventing the normal |
| way of giving back urb. |
| |
| Instead we need to rely on the stop command timeout function to give back |
| the urb. This xhci_stop_endpoint_command_watchdog() timeout function |
| used a XHCI_STATE_DYING flag to indicate if the timeout function is already |
| running, but later this flag has been taking into use in other places to |
| mark that xhci is dying. |
| |
| Remove checks for XHCI_STATE_DYING in xhci_urb_dequeue. We are still |
| checking that reading from pci state does not return 0xffffffff or that |
| host is not halted before trying to stop the endpoint. |
| |
| This whole area of stopping endpoints, giving back URBs, and the wathdog |
| timeout need rework, this fix focuses on solving a specific deadlock |
| issue that we can then send to stable before any major rework. |
| |
| Cc: <stable@vger.kernel.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 76ecce0be488..2b0ba75da067 100644 |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -916,17 +916,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) |
| spin_lock_irqsave(&xhci->lock, flags); |
| |
| ep->stop_cmds_pending--; |
| - if (xhci->xhc_state & XHCI_STATE_REMOVING) { |
| - spin_unlock_irqrestore(&xhci->lock, flags); |
| - return; |
| - } |
| - if (xhci->xhc_state & XHCI_STATE_DYING) { |
| - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, |
| - "Stop EP timer ran, but another timer marked " |
| - "xHCI as DYING, exiting."); |
| - spin_unlock_irqrestore(&xhci->lock, flags); |
| - return; |
| - } |
| if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) { |
| xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, |
| "Stop EP timer ran, but no command pending, " |
| diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c |
| index 3e66e735cff3..c15125d33ffc 100644 |
| --- a/drivers/usb/host/xhci.c |
| +++ b/drivers/usb/host/xhci.c |
| @@ -1531,19 +1531,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) |
| xhci_urb_free_priv(urb_priv); |
| return ret; |
| } |
| - if ((xhci->xhc_state & XHCI_STATE_DYING) || |
| - (xhci->xhc_state & XHCI_STATE_HALTED)) { |
| - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, |
| - "Ep 0x%x: URB %p to be canceled on " |
| - "non-responsive xHCI host.", |
| - urb->ep->desc.bEndpointAddress, urb); |
| - /* Let the stop endpoint command watchdog timer (which set this |
| - * state) finish cleaning up the endpoint TD lists. We must |
| - * have caught it in the middle of dropping a lock and giving |
| - * back an URB. |
| - */ |
| - goto done; |
| - } |
| |
| ep_index = xhci_get_endpoint_index(&urb->ep->desc); |
| ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index]; |
| -- |
| 2.10.1 |
| |