lkml.org 
[lkml]   [2012]   [Oct]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[ 024/147] xHCI: add aborting command ring function
    3.2-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Elric Fu <elricfu1@gmail.com>

    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);




    \
     
     \ /
      Last update: 2012-10-14 17:21    [W:3.134 / U:1.080 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site