xHCI: cancel command after command timeout
authorElric Fu <elricfu1@gmail.com>
Wed, 27 Jun 2012 08:31:52 +0000 (16:31 +0800)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Thu, 13 Sep 2012 22:49:38 +0000 (15:49 -0700)
The patch is used to cancel command when the command isn't
acknowledged and a timeout occurs.

This patch should be backported to kernels as old as 3.0, that contain
the commit 7ed603ecf8b68ab81f4c83097d3063d43ec73bb8 "xhci: Add an
assertion to check for virt_dev=0 bug." That commit papers over a NULL
pointer dereference, and this patch fixes the underlying issue that
caused the NULL pointer dereference.

Signed-off-by: Elric Fu <elricfu1@gmail.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Tested-by: Miroslav Sabljic <miroslav.sabljic@avl.com>
Cc: stable@vger.kernel.org
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index 3f0763d6359863af527d4d3a110e6555129428d5..b95e3e8a293eb9c5a5d6cf5e902d6f6799b1b127 100644 (file)
@@ -2402,6 +2402,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        struct completion *cmd_completion;
        u32 *cmd_status;
        struct xhci_virt_device *virt_dev;
+       union xhci_trb *cmd_trb;
 
        spin_lock_irqsave(&xhci->lock, flags);
        virt_dev = xhci->devs[udev->slot_id];
@@ -2447,6 +2448,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        }
        init_completion(cmd_completion);
 
+       cmd_trb = xhci->cmd_ring->dequeue;
        if (!ctx_change)
                ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
                                udev->slot_id, must_succeed);
@@ -2468,14 +2470,17 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        /* Wait for the configure endpoint command to complete */
        timeleft = wait_for_completion_interruptible_timeout(
                        cmd_completion,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for %s command\n",
                                timeleft == 0 ? "Timeout" : "Signal",
                                ctx_change == 0 ?
                                        "configure endpoint" :
                                        "evaluate context");
-               /* FIXME cancel the configure endpoint command */
+               /* cancel the configure endpoint command */
+               ret = xhci_cancel_cmd(xhci, command, cmd_trb);
+               if (ret < 0)
+                       return ret;
                return -ETIME;
        }
 
@@ -3424,8 +3429,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
        unsigned long flags;
        int timeleft;
        int ret;
+       union xhci_trb *cmd_trb;
 
        spin_lock_irqsave(&xhci->lock, flags);
+       cmd_trb = xhci->cmd_ring->dequeue;
        ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
        if (ret) {
                spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3437,12 +3444,12 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
 
        /* XXX: how much time for xHC slot assignment? */
        timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for a slot\n",
                                timeleft == 0 ? "Timeout" : "Signal");
-               /* FIXME cancel the enable slot request */
-               return 0;
+               /* cancel the enable slot request */
+               return xhci_cancel_cmd(xhci, NULL, cmd_trb);
        }
 
        if (!xhci->slot_id) {
@@ -3503,6 +3510,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
        struct xhci_slot_ctx *slot_ctx;
        struct xhci_input_control_ctx *ctrl_ctx;
        u64 temp_64;
+       union xhci_trb *cmd_trb;
 
        if (!udev->slot_id) {
                xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id);
@@ -3541,6 +3549,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
        xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
 
        spin_lock_irqsave(&xhci->lock, flags);
+       cmd_trb = xhci->cmd_ring->dequeue;
        ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
                                        udev->slot_id);
        if (ret) {
@@ -3553,7 +3562,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
 
        /* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
        timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        /* FIXME: From section 4.3.4: "Software shall be responsible for timing
         * the SetAddress() "recovery interval" required by USB and aborting the
         * command on a timeout.
@@ -3561,7 +3570,10 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for address device command\n",
                                timeleft == 0 ? "Timeout" : "Signal");
-               /* FIXME cancel the address device command */
+               /* cancel the address device command */
+               ret = xhci_cancel_cmd(xhci, NULL, cmd_trb);
+               if (ret < 0)
+                       return ret;
                return -ETIME;
        }
 
index fdfcebf342e807db94474a19e5462e90b2d20c2d..e81ccfa3552f03c742a4c11f38da8cefec0c736c 100644 (file)
@@ -1256,6 +1256,9 @@ struct xhci_td {
        union xhci_trb          *last_trb;
 };
 
+/* xHCI command default timeout value */
+#define XHCI_CMD_DEFAULT_TIMEOUT       (5 * HZ)
+
 /* command descriptor */
 struct xhci_cd {
        struct list_head        cancel_cmd_list;