| From: Elric Fu <elricfu1@gmail.com> |
| Date: Wed, 27 Jun 2012 16:31:12 +0800 |
| Subject: xHCI: add aborting command ring function |
| |
| commit b92cc66c047ff7cf587b318fe377061a353c120f upstream. |
| |
| 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. |
| |
| 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: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/usb/host/xhci-mem.c | 7 +++ |
| drivers/usb/host/xhci-ring.c | 108 ++++++++++++++++++++++++++++++++++++++++++ |
| drivers/usb/host/xhci.c | 2 +- |
| drivers/usb/host/xhci.h | 12 +++++ |
| 4 files changed, 128 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/usb/host/xhci-mem.c |
| +++ b/drivers/usb/host/xhci-mem.c |
| @@ -1680,6 +1680,7 @@ void xhci_mem_cleanup(struct xhci_hcd *x |
| { |
| 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; |
| @@ -1701,6 +1702,11 @@ void xhci_mem_cleanup(struct xhci_hcd *x |
| 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); |
| @@ -2246,6 +2252,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, |
| xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, 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); |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -318,6 +318,114 @@ void xhci_ring_cmd_db(struct xhci_hcd *x |
| 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, |
| --- a/drivers/usb/host/xhci.c |
| +++ b/drivers/usb/host/xhci.c |
| @@ -52,7 +52,7 @@ MODULE_PARM_DESC(link_quirk, "Don't clea |
| * 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; |
| --- a/drivers/usb/host/xhci.h |
| +++ b/drivers/usb/host/xhci.h |
| @@ -1255,6 +1255,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; |
| @@ -1406,6 +1413,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; |
| @@ -1670,6 +1678,8 @@ static inline void xhci_unregister_pci(v |
| |
| /* 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); |
| @@ -1760,6 +1770,8 @@ void xhci_queue_config_ep_quirk(struct x |
| 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); |
| |