Skip to content

Commit 7faac19

Browse files
matnymangregkh
authored andcommitted
xhci: avoid race between disable slot command and host runtime suspend
Make xhci_disable_slot() synchronous, thus ensuring it, and xhci_free_dev() calling it return after xHC controller completes the disable slot command. Otherwise the roothub and xHC host may runtime suspend, and clear the command ring while the disable slot command is being processed. This causes a command completion mismatch as the completion event can't be mapped to the correct command. Command ring gets out of sync and commands time out. Driver finally assumes host is unresponsive and bails out. usb 2-4: USB disconnect, device number 10 xhci_hcd 0000:00:0d.0: ERROR mismatched command completion event ... xhci_hcd 0000:00:0d.0: xHCI host controller not responding, assume dead xhci_hcd 0000:00:0d.0: HC died; cleaning up Cc: <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 811ae81 commit 7faac19

File tree

3 files changed

+16
-8
lines changed

3 files changed

+16
-8
lines changed

drivers/usb/host/xhci-hub.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
717717
continue;
718718

719719
retval = xhci_disable_slot(xhci, i);
720+
xhci_free_virt_device(xhci, i);
720721
if (retval)
721722
xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
722723
i, retval);

drivers/usb/host/xhci-ring.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,6 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
15251525
if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
15261526
/* Delete default control endpoint resources */
15271527
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
1528-
xhci_free_virt_device(xhci, slot_id);
15291528
}
15301529

15311530
static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,

drivers/usb/host/xhci.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3959,9 +3959,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
39593959
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
39603960
}
39613961
virt_dev->udev = NULL;
3962-
ret = xhci_disable_slot(xhci, udev->slot_id);
3963-
if (ret)
3964-
xhci_free_virt_device(xhci, udev->slot_id);
3962+
xhci_disable_slot(xhci, udev->slot_id);
3963+
xhci_free_virt_device(xhci, udev->slot_id);
39653964
}
39663965

39673966
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
@@ -3971,7 +3970,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
39713970
u32 state;
39723971
int ret = 0;
39733972

3974-
command = xhci_alloc_command(xhci, false, GFP_KERNEL);
3973+
command = xhci_alloc_command(xhci, true, GFP_KERNEL);
39753974
if (!command)
39763975
return -ENOMEM;
39773976

@@ -3996,6 +3995,15 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
39963995
}
39973996
xhci_ring_cmd_db(xhci);
39983997
spin_unlock_irqrestore(&xhci->lock, flags);
3998+
3999+
wait_for_completion(command->completion);
4000+
4001+
if (command->status != COMP_SUCCESS)
4002+
xhci_warn(xhci, "Unsuccessful disable slot %u command, status %d\n",
4003+
slot_id, command->status);
4004+
4005+
xhci_free_command(xhci, command);
4006+
39994007
return ret;
40004008
}
40014009

@@ -4104,9 +4112,8 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
41044112
return 1;
41054113

41064114
disable_slot:
4107-
ret = xhci_disable_slot(xhci, udev->slot_id);
4108-
if (ret)
4109-
xhci_free_virt_device(xhci, udev->slot_id);
4115+
xhci_disable_slot(xhci, udev->slot_id);
4116+
xhci_free_virt_device(xhci, udev->slot_id);
41104117

41114118
return 0;
41124119
}
@@ -4236,6 +4243,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
42364243

42374244
mutex_unlock(&xhci->mutex);
42384245
ret = xhci_disable_slot(xhci, udev->slot_id);
4246+
xhci_free_virt_device(xhci, udev->slot_id);
42394247
if (!ret)
42404248
xhci_alloc_dev(hcd, udev);
42414249
kfree(command->completion);

0 commit comments

Comments
 (0)