xHCI: add aborting command ring function
Elric Fu [Wed, 27 Jun 2012 08:31:12 +0000 (16:31 +0800)]
Software have to abort command ring and cancel command
when a command is failed or hang. Otherwise, the command
ring will hang up and can't handle the others. An example
of a command that may hang is the Address Device Command,
because waiting for a SET_ADDRESS request to be acknowledged
by a USB device is outside of the xHC's ability to control.

To cancel a command, software will initialize a command
descriptor for the cancel command, and add it into a
cancel_cmd_list of xhci.

Sarah: Fixed missing newline on "Have the command ring been stopped?"
debugging statement.

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.

Change-Id: I6d008c9e56b8612393f1952473b3a50fe88b8c0a
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
Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
Reviewed-on: http://git-master/r/192886
(cherry picked from commit 16c9b4cf3e6669864671ef0410e4796ef1796581)

Signed-off-by: JC Kuo <jckuo@nvidia.com>
Change-Id: I84965ed882f049a497a9586fdbfa37b72f69b404
Reviewed-on: http://git-master/r/194692
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Ajay Gupta <ajayg@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>

drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index 6b90824..cbed50a 100644 (file)
@@ -1772,6 +1772,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 {
        struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
        struct dev_info *dev_info, *next;
+       struct xhci_cd  *cur_cd, *next_cd;
        unsigned long   flags;
        int size;
        int i, j, num_ports;
@@ -1793,6 +1794,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
                xhci_ring_free(xhci, xhci->cmd_ring);
        xhci->cmd_ring = NULL;
        xhci_dbg(xhci, "Freed command ring\n");
+       list_for_each_entry_safe(cur_cd, next_cd,
+                       &xhci->cancel_cmd_list, cancel_cmd_list) {
+               list_del(&cur_cd->cancel_cmd_list);
+               kfree(cur_cd);
+       }
 
        for (i = 1; i < MAX_HC_SLOTS; ++i)
                xhci_free_virt_device(xhci, i);
@@ -2338,6 +2344,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags);
        if (!xhci->cmd_ring)
                goto fail;
+       INIT_LIST_HEAD(&xhci->cancel_cmd_list);
        xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
        xhci_dbg(xhci, "First segment DMA is 0x%llx\n",
                        (unsigned long long)xhci->cmd_ring->first_seg->dma);
index dc9e6e9..5563549 100644 (file)
@@ -289,6 +289,114 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
        xhci_readl(xhci, &xhci->dba->doorbell[0]);
 }
 
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+{
+       u64 temp_64;
+       int ret;
+
+       xhci_dbg(xhci, "Abort command ring\n");
+
+       if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING)) {
+               xhci_dbg(xhci, "The command ring isn't running, "\
+                               "Have the command ring been stopped?\n");
+               return 0;
+       }
+
+       temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+       if (!(temp_64 & CMD_RING_RUNNING)) {
+               xhci_dbg(xhci, "Command ring had been stopped\n");
+               return 0;
+       }
+       xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+       xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
+                       &xhci->op_regs->cmd_ring);
+
+       /* Section 4.6.1.2 of xHCI 1.0 spec says software should
+        * time the completion od all xHCI commands, including
+        * the Command Abort operation. If software doesn't see
+        * CRR negated in a timely manner (e.g. longer than 5
+        * seconds), then it should assume that the there are
+        * larger problems with the xHC and assert HCRST.
+        */
+       ret = handshake(xhci, &xhci->op_regs->cmd_ring,
+                       CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
+       if (ret < 0) {
+               xhci_err(xhci, "Stopped the command ring failed, "\
+                               "maybe the host is dead\n");
+               xhci->xhc_state |= XHCI_STATE_DYING;
+               xhci_quiesce(xhci);
+               xhci_halt(xhci);
+               return -ESHUTDOWN;
+       }
+
+       return 0;
+}
+
+static int xhci_queue_cd(struct xhci_hcd *xhci,
+               struct xhci_command *command,
+               union xhci_trb *cmd_trb)
+{
+       struct xhci_cd *cd;
+       cd = kzalloc(sizeof(struct xhci_cd), GFP_ATOMIC);
+       if (!cd)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&cd->cancel_cmd_list);
+
+       cd->command = command;
+       cd->cmd_trb = cmd_trb;
+       list_add_tail(&cd->cancel_cmd_list, &xhci->cancel_cmd_list);
+
+       return 0;
+}
+
+/*
+ * Cancel the command which has issue.
+ *
+ * Some commands may hang due to waiting for acknowledgement from
+ * usb device. It is outside of the xHC's ability to control and
+ * will cause the command ring is blocked. When it occurs software
+ * should intervene to recover the command ring.
+ * See Section 4.6.1.1 and 4.6.1.2
+ */
+int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
+               union xhci_trb *cmd_trb)
+{
+       int retval = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&xhci->lock, flags);
+
+       if (xhci->xhc_state & XHCI_STATE_DYING) {
+               xhci_warn(xhci, "Abort the command ring,"\
+                               " but the xHCI is dead.\n");
+               retval = -ESHUTDOWN;
+               goto fail;
+       }
+
+       /* queue the cmd desriptor to cancel_cmd_list */
+       retval = xhci_queue_cd(xhci, command, cmd_trb);
+       if (retval) {
+               xhci_warn(xhci, "Queuing command descriptor failed.\n");
+               goto fail;
+       }
+
+       /* abort command ring */
+       retval = xhci_abort_cmd_ring(xhci);
+       if (retval) {
+               xhci_err(xhci, "Abort command ring failed\n");
+               if (unlikely(retval == -ESHUTDOWN)) {
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+                       xhci_dbg(xhci, "xHCI host controller is dead.\n");
+                       return retval;
+               }
+       }
+
+fail:
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       return retval;
+}
+
 void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
                unsigned int slot_id,
                unsigned int ep_index,
index ab6b889..14dd79f 100644 (file)
@@ -51,7 +51,7 @@ MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
  * handshake done).  There are two failure modes:  "usec" have passed (major
  * hardware flakeout), or the register reads as all-ones (hardware removed).
  */
-static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
                      u32 mask, u32 done, int usec)
 {
        u32     result;
index 3904589..6907c4c 100644 (file)
@@ -1252,6 +1252,13 @@ struct xhci_td {
        union xhci_trb          *last_trb;
 };
 
+/* command descriptor */
+struct xhci_cd {
+       struct list_head        cancel_cmd_list;
+       struct xhci_command     *command;
+       union xhci_trb          *cmd_trb;
+};
+
 struct xhci_dequeue_state {
        struct xhci_segment *new_deq_seg;
        union xhci_trb *new_deq_ptr;
@@ -1421,6 +1428,7 @@ struct xhci_hcd {
 #define CMD_RING_STATE_RUNNING         (1 << 0)
 #define CMD_RING_STATE_ABORTED         (1 << 1)
 #define CMD_RING_STATE_STOPPED         (1 << 2)
+       struct list_head        cancel_cmd_list;
        unsigned int            cmd_ring_reserved_trbs;
        struct xhci_ring        *event_ring;
        struct xhci_erst        erst;
@@ -1692,6 +1700,8 @@ static inline void xhci_unregister_plat(void)
 
 /* xHCI host controller glue */
 typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
+int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+               u32 mask, u32 done, int usec);
 void xhci_quiesce(struct xhci_hcd *xhci);
 int xhci_halt(struct xhci_hcd *xhci);
 int xhci_reset(struct xhci_hcd *xhci);
@@ -1782,6 +1792,8 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
                unsigned int slot_id, unsigned int ep_index,
                struct xhci_dequeue_state *deq_state);
 void xhci_stop_endpoint_command_watchdog(unsigned long arg);
+int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
+               union xhci_trb *cmd_trb);
 void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
                unsigned int ep_index, unsigned int stream_id);