aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-hcd.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-10-27 10:57:01 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 11:55:17 -0800
commit6f5165cf989387e84ef23122330b27cca1cbe831 (patch)
tree44ff1ea0590b00f2851f50ffa2cf9954eb70a767 /drivers/usb/host/xhci-hcd.c
parent4f0f0baef017dfd5d62b749716ab980a825e1071 (diff)
USB: xhci: Add watchdog timer for URB cancellation.
In order to giveback a canceled URB, we must ensure that the xHCI hardware will not access the buffer in an URB. We can't modify the buffer pointers on endpoint rings without issuing and waiting for a stop endpoint command. Since URBs can be canceled in interrupt context, we can't wait on that command. The old code trusted that the host controller would respond to the command, and would giveback the URBs in the event handler. If the hardware never responds to the stop endpoint command, the URBs will never be completed, and we might hang the USB subsystem. Implement a watchdog timer that is spawned whenever a stop endpoint command is queued. If a stop endpoint command event is found on the event ring during an interrupt, we need to stop the watchdog timer with del_timer(). Since del_timer() can fail if the timer is running and waiting on the xHCI lock, we need a way to signal to the timer that everything is fine and it should exit. If we simply clear EP_HALT_PENDING, a new stop endpoint command could sneak in and set it before the watchdog timer can grab the lock. Instead we use a combination of the EP_HALT_PENDING flag and a counter for the number of pending stop endpoint commands (xhci_virt_ep->stop_cmds_pending). If we need to cancel the watchdog timer and del_timer() succeeds, we decrement the number of pending stop endpoint commands. If del_timer() fails, we leave the number of pending stop endpoint commands alone. In either case, we clear the EP_HALT_PENDING flag. The timer will decrement the number of pending stop endpoint commands once it obtains the lock. If the timer is the tail end of the last stop endpoint command (xhci_virt_ep->stop_cmds_pending == 0), and the endpoint's command is still pending (EP_HALT_PENDING is set), we assume the host is dying. The watchdog timer will set XHCI_STATE_DYING, try to halt the xHCI host, and give back all pending URBs. Various other places in the driver need to check whether the xHCI host is dying. If the interrupt handler ever notices, it should immediately stop processing events. The URB enqueue function should also return -ESHUTDOWN. The URB dequeue function should simply return the value of usb_hcd_check_unlink_urb() and the watchdog timer will take care of giving the URB back. When a device is disconnected, the xHCI hardware structures should be freed without issuing a disable slot command (since the hardware probably won't respond to it anyway). The debugging polling loop should stop polling if the host is dying. When a device is disconnected, any pending watchdog timers are killed with del_timer_sync(). It must be synchronous so that the watchdog timer doesn't attempt to access the freed endpoint structures. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci-hcd.c')
-rw-r--r--drivers/usb/host/xhci-hcd.c52
1 files changed, 48 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
index 5839453d342..0d5a8564ed1 100644
--- a/drivers/usb/host/xhci-hcd.c
+++ b/drivers/usb/host/xhci-hcd.c
@@ -246,8 +246,14 @@ static void xhci_work(struct xhci_hcd *xhci)
/* Flush posted writes */
xhci_readl(xhci, &xhci->ir_set->irq_pending);
- /* FIXME this should be a delayed service routine that clears the EHB */
- xhci_handle_event(xhci);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
+ "Shouldn't IRQs be disabled?\n");
+ else
+ /* FIXME this should be a delayed service routine
+ * that clears the EHB.
+ */
+ xhci_handle_event(xhci);
/* Clear the event handler busy flag (RW1C); the event ring should be empty. */
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
@@ -320,7 +326,7 @@ void xhci_event_ring_work(unsigned long arg)
spin_lock_irqsave(&xhci->lock, flags);
temp = xhci_readl(xhci, &xhci->op_regs->status);
xhci_dbg(xhci, "op reg status = 0x%x\n", temp);
- if (temp == 0xffffffff) {
+ if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) {
xhci_dbg(xhci, "HW died, polling stopped.\n");
spin_unlock_irqrestore(&xhci->lock, flags);
return;
@@ -710,16 +716,22 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
* atomic context to this function, which may allocate memory.
*/
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
} else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
} else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -728,6 +740,12 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
}
exit:
return ret;
+dying:
+ xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
+ "non-responsive xHCI host.\n",
+ urb->ep->desc.bEndpointAddress, urb);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -ESHUTDOWN;
}
/*
@@ -789,6 +807,17 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
kfree(td);
return ret;
}
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on "
+ "non-responsive xHCI host.\n",
+ 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;
+ }
xhci_dbg(xhci, "Cancel URB %p\n", urb);
xhci_dbg(xhci, "Event ring:\n");
@@ -806,6 +835,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
*/
if (!(ep->ep_state & EP_HALT_PENDING)) {
ep->ep_state |= EP_HALT_PENDING;
+ ep->stop_cmds_pending++;
+ ep->stop_cmd_timer.expires = jiffies +
+ XHCI_STOP_EP_CMD_TIMEOUT * HZ;
+ add_timer(&ep->stop_cmd_timer);
xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index);
xhci_ring_cmd_db(xhci);
}
@@ -1410,16 +1443,27 @@ void xhci_endpoint_reset(struct usb_hcd *hcd,
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_virt_device *virt_dev;
unsigned long flags;
u32 state;
+ int i;
if (udev->slot_id == 0)
return;
+ virt_dev = xhci->devs[udev->slot_id];
+ if (!virt_dev)
+ return;
+
+ /* Stop any wayward timer functions (which may grab the lock) */
+ for (i = 0; i < 31; ++i) {
+ virt_dev->eps[i].ep_state &= ~EP_HALT_PENDING;
+ del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
+ }
spin_lock_irqsave(&xhci->lock, flags);
/* Don't disable the slot if the host controller is dead. */
state = xhci_readl(xhci, &xhci->op_regs->status);
- if (state == 0xffffffff) {
+ if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) {
xhci_free_virt_device(xhci, udev->slot_id);
spin_unlock_irqrestore(&xhci->lock, flags);
return;