Commit 7a3783ef authored by Paul Zimmerman's avatar Paul Zimmerman Committed by Sarah Sharp

xhci: Fix reset-device and configure-endpoint commands

We have been having problems with the USB-IF Gold Tree tests when plugging
and unplugging devices from the tree. I have seen that the reset-device
and configure-endpoint commands, which are invoked from
xhci_discover_or_reset_device() and xhci_configure_endpoint(), will sometimes
time out.

After much debugging, I determined that the commands themselves do not actually
time out, but rather their completion events do not get delivered to the right
place.

This happens when the command ring has just wrapped around, and it's enqueue
pointer is left pointing to the link TRB. xhci_discover_or_reset_device() and
xhci_configure_endpoint() use the enqueue pointer directly as their command
TRB pointer, without checking whether it's pointing to the link TRB.

When the completion event arrives, if the command TRB is pointing to the link
TRB, the check against the command ring dequeue pointer in
handle_cmd_in_cmd_wait_list() fails, so the completion inside the command does
not get signaled.

The patch below fixes the timeout problem for me.

This should be queued for the 2.6.35 and 2.6.36 stable trees.
Signed-off-by: default avatarPaul Zimmerman <paulz@synopsys.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@kernel.org
parent 02e2c51b
...@@ -1549,6 +1549,15 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, ...@@ -1549,6 +1549,15 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
cmd_completion = command->completion; cmd_completion = command->completion;
cmd_status = &command->status; cmd_status = &command->status;
command->command_trb = xhci->cmd_ring->enqueue; command->command_trb = xhci->cmd_ring->enqueue;
/* Enqueue pointer can be left pointing to the link TRB,
* we must handle that
*/
if ((command->command_trb->link.control & TRB_TYPE_BITMASK)
== TRB_TYPE(TRB_LINK))
command->command_trb =
xhci->cmd_ring->enq_seg->next->trbs;
list_add_tail(&command->cmd_list, &virt_dev->cmd_list); list_add_tail(&command->cmd_list, &virt_dev->cmd_list);
} else { } else {
in_ctx = virt_dev->in_ctx; in_ctx = virt_dev->in_ctx;
...@@ -2272,6 +2281,15 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -2272,6 +2281,15 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Attempt to submit the Reset Device command to the command ring */ /* Attempt to submit the Reset Device command to the command ring */
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
reset_device_cmd->command_trb = xhci->cmd_ring->enqueue; reset_device_cmd->command_trb = xhci->cmd_ring->enqueue;
/* Enqueue pointer can be left pointing to the link TRB,
* we must handle that
*/
if ((reset_device_cmd->command_trb->link.control & TRB_TYPE_BITMASK)
== TRB_TYPE(TRB_LINK))
reset_device_cmd->command_trb =
xhci->cmd_ring->enq_seg->next->trbs;
list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list); list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list);
ret = xhci_queue_reset_device(xhci, slot_id); ret = xhci_queue_reset_device(xhci, slot_id);
if (ret) { if (ret) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment