diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-08-02 15:43:40 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-10-03 11:39:53 -0700 |
commit | 4343d2a21eeab544544646fed30252c184b358a9 (patch) | |
tree | 8114edb11b0bc0beebaa13579a46743840f42724 /drivers/usb/host/xhci.c | |
parent | 8a8045bd9b07df6def0ae16b91ecbe8225267b35 (diff) | |
download | kernel_samsung_aries-4343d2a21eeab544544646fed30252c184b358a9.zip kernel_samsung_aries-4343d2a21eeab544544646fed30252c184b358a9.tar.gz kernel_samsung_aries-4343d2a21eeab544544646fed30252c184b358a9.tar.bz2 |
xhci: Remove TDs from TD lists when URBs are canceled.
commit 585df1d90cb07a02ca6c7a7d339e56e46d50dafb upstream.
When a driver tries to cancel an URB, and the host controller is dying,
xhci_urb_dequeue will giveback the URB without removing the xhci_tds
that comprise that URB from the td_list or the cancelled_td_list. This
can cause a race condition between the driver calling URB dequeue and
the stop endpoint command watchdog timer.
If the timer fires on a dying host, and a driver attempts to resubmit
while the watchdog timer has dropped the xhci->lock to giveback a
cancelled URB, URBs may be given back by the xhci_urb_dequeue() function.
At that point, the URB's priv pointer will be freed and set to NULL, but
the TDs will remain on the td_list. This will cause an oops in
xhci_giveback_urb_in_irq() when the watchdog timer attempts to loop
through the endpoints' td_lists, giving back killed URBs.
Make sure that xhci_urb_dequeue() removes TDs from the TD lists and
canceled TD lists before it gives back the URB.
This patch should be backported to kernels as old as 2.6.36.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r-- | drivers/usb/host/xhci.c | 7 |
1 files changed, 7 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2114caf..7ea48b3 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1252,6 +1252,13 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) { xhci_dbg(xhci, "HW died, freeing TD.\n"); urb_priv = urb->hcpriv; + for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { + td = urb_priv->td[i]; + if (!list_empty(&td->td_list)) + list_del_init(&td->td_list); + if (!list_empty(&td->cancelled_td_list)) + list_del_init(&td->cancelled_td_list); + } usb_hcd_unlink_urb_from_ep(hcd, urb); spin_unlock_irqrestore(&xhci->lock, flags); |