Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b92cc66c authored by Elric Fu's avatar Elric Fu Committed by Sarah Sharp
Browse files

xHCI: add aborting command ring function



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 7ed603ec "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: default avatarElric Fu <elricfu1@gmail.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Tested-by: default avatarMiroslav Sabljic <miroslav.sabljic@avl.com>
Cc: stable@vger.kernel.org
parent c181bc5b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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;
@@ -1795,6 +1796,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);
@@ -2340,6 +2346,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);
+108 −0
Original line number Diff line number Diff line
@@ -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,
+1 −1
Original line number Diff line number Diff line
@@ -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;
+12 −0
Original line number Diff line number Diff line
@@ -1256,6 +1256,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;
@@ -1425,6 +1432,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;
@@ -1702,6 +1710,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);
@@ -1792,6 +1802,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);