UPSTREAM: xHCI: cancel command after command timeout
authorElric Fu <elricfu1@gmail.com>
Wed, 27 Jun 2012 08:31:52 +0000 (16:31 +0800)
committerChromeBot <chrome-bot@google.com>
Fri, 22 Mar 2013 01:02:17 +0000 (18:02 -0700)
commit 6e4468b9a0793dfb53eb80d9fe52c739b13b27fd upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 75382341d888ba0132c5eeb94711840acf972034)

BUG=None
TEST=Together with other cherry-picks: run BVT trybots on all platforms,
manually confirm that USB network/storage/input devices still work
(including across suspend/resume)

Change-Id: I36f4ea343230e394d99a0e081ce5738a8d68a886
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/46060
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index 6269d9e..8579d07 100644 (file)
@@ -2397,6 +2397,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];
@@ -2442,6 +2443,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);
@@ -2463,14 +2465,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;
        }
 
@@ -3419,8 +3424,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);
@@ -3432,12 +3439,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) {
@@ -3498,6 +3505,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);
@@ -3536,6 +3544,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) {
@@ -3548,7 +3557,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.
@@ -3556,7 +3565,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 26967ca..c34e023 100644 (file)
@@ -1252,6 +1252,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;