| From d6169d04097fd9ddf811e63eae4e5cd71e6666e2 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: xhci: fix deadlock at host remove by running watchdog correctly |
| |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| |
| 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. |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Howard Yen <howard_yen@htc.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/host/xhci-ring.c | 11 ----------- |
| drivers/usb/host/xhci.c | 13 ------------- |
| 2 files changed, 24 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -839,17 +839,6 @@ void xhci_stop_endpoint_command_watchdog |
| 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, " |
| --- a/drivers/usb/host/xhci.c |
| +++ b/drivers/usb/host/xhci.c |
| @@ -1565,19 +1565,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd |
| xhci_urb_free_priv(xhci, 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]; |