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

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

xHCI: cancel command after command timeout



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 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 b92cc66c
Loading
Loading
Loading
Loading
+19 −7
Original line number Diff line number Diff line
@@ -2402,6 +2402,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];
@@ -2447,6 +2448,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);
@@ -2468,14 +2470,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;
	}

@@ -3424,8 +3429,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);
@@ -3437,12 +3444,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) {
@@ -3503,6 +3510,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);
@@ -3541,6 +3549,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) {
@@ -3553,7 +3562,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.
@@ -3561,7 +3570,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;
	}

+3 −0
Original line number Diff line number Diff line
@@ -1256,6 +1256,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;