--- /dev/null
+From 2eb03376151bb8585caa23ed2673583107bb5193 Mon Sep 17 00:00:00 2001
+From: Weitao Wang <WeitaoWang-oc@zhaoxin.com>
+Date: Tue, 19 Aug 2025 15:58:43 +0300
+Subject: usb: xhci: Fix slot_id resource race conflict
+
+From: Weitao Wang <WeitaoWang-oc@zhaoxin.com>
+
+commit 2eb03376151bb8585caa23ed2673583107bb5193 upstream.
+
+xHC controller may immediately reuse a slot_id after it's disabled,
+giving it to a new enumerating device before the xhci driver freed
+all resources related to the disabled device.
+
+In such a scenario, device-A with slot_id equal to 1 is disconnecting
+while device-B is enumerating, device-B will fail to enumerate in the
+follow sequence.
+
+1.[device-A] send disable slot command
+2.[device-B] send enable slot command
+3.[device-A] disable slot command completed and wakeup waiting thread
+4.[device-B] enable slot command completed with slot_id equal to 1 and
+ wakeup waiting thread
+5.[device-B] driver checks that slot_id is still in use (by device-A) in
+ xhci_alloc_virt_device, and fail to enumerate due to this
+ conflict
+6.[device-A] xhci->devs[slot_id] set to NULL in xhci_free_virt_device
+
+To fix driver's slot_id resources conflict, clear xhci->devs[slot_id] and
+xhci->dcbba->dev_context_ptrs[slot_id] pointers in the interrupt context
+when disable slot command completes successfully. Simultaneously, adjust
+function xhci_free_virt_device to accurately handle device release.
+
+[minor smatch warning and commit message fix -Mathias]
+
+Cc: stable@vger.kernel.org
+Fixes: 7faac1953ed1 ("xhci: avoid race between disable slot command and host runtime suspend")
+Signed-off-by: Weitao Wang <WeitaoWang-oc@zhaoxin.com>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20250819125844.2042452-2-mathias.nyman@linux.intel.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/host/xhci-hub.c | 3 +--
+ drivers/usb/host/xhci-mem.c | 22 +++++++++++-----------
+ drivers/usb/host/xhci-ring.c | 9 +++++++--
+ drivers/usb/host/xhci.c | 18 +++++++++++++-----
+ drivers/usb/host/xhci.h | 3 ++-
+ 5 files changed, 34 insertions(+), 21 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -628,8 +628,7 @@ static int xhci_enter_test_mode(struct x
+ if (!xhci->devs[i])
+ continue;
+
+- retval = xhci_disable_slot(xhci, i);
+- xhci_free_virt_device(xhci, i);
++ retval = xhci_disable_and_free_slot(xhci, i);
+ if (retval)
+ xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
+ i, retval);
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -879,21 +879,20 @@ free_tts:
+ * will be manipulated by the configure endpoint, allocate device, or update
+ * hub functions while this function is removing the TT entries from the list.
+ */
+-void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
++void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev,
++ int slot_id)
+ {
+- struct xhci_virt_device *dev;
+ int i;
+ int old_active_eps = 0;
+
+ /* Slot ID 0 is reserved */
+- if (slot_id == 0 || !xhci->devs[slot_id])
++ if (slot_id == 0 || !dev)
+ return;
+
+- dev = xhci->devs[slot_id];
+-
+- xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+- if (!dev)
+- return;
++ /* If device ctx array still points to _this_ device, clear it */
++ if (dev->out_ctx &&
++ xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma))
++ xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+
+ trace_xhci_free_virt_device(dev);
+
+@@ -932,8 +931,9 @@ void xhci_free_virt_device(struct xhci_h
+
+ if (dev->udev && dev->udev->slot_id)
+ dev->udev->slot_id = 0;
+- kfree(xhci->devs[slot_id]);
+- xhci->devs[slot_id] = NULL;
++ if (xhci->devs[slot_id] == dev)
++ xhci->devs[slot_id] = NULL;
++ kfree(dev);
+ }
+
+ /*
+@@ -975,7 +975,7 @@ static void xhci_free_virt_devices_depth
+ out:
+ /* we are now at a leaf device */
+ xhci_debugfs_remove_slot(xhci, slot_id);
+- xhci_free_virt_device(xhci, slot_id);
++ xhci_free_virt_device(xhci, vdev, slot_id);
+ }
+
+ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -1258,7 +1258,8 @@ static void xhci_handle_cmd_enable_slot(
+ command->slot_id = 0;
+ }
+
+-static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
++static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id,
++ u32 cmd_comp_code)
+ {
+ struct xhci_virt_device *virt_dev;
+ struct xhci_slot_ctx *slot_ctx;
+@@ -1273,6 +1274,10 @@ static void xhci_handle_cmd_disable_slot
+ if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
+ /* Delete default control endpoint resources */
+ xhci_free_device_endpoint_resources(xhci, virt_dev, true);
++ if (cmd_comp_code == COMP_SUCCESS) {
++ xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
++ xhci->devs[slot_id] = NULL;
++ }
+ }
+
+ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
+@@ -1512,7 +1517,7 @@ static void handle_cmd_completion(struct
+ xhci_handle_cmd_enable_slot(xhci, slot_id, cmd, cmd_comp_code);
+ break;
+ case TRB_DISABLE_SLOT:
+- xhci_handle_cmd_disable_slot(xhci, slot_id);
++ xhci_handle_cmd_disable_slot(xhci, slot_id, cmd_comp_code);
+ break;
+ case TRB_CONFIG_EP:
+ if (!cmd->completion)
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -3947,7 +3947,7 @@ static void xhci_free_dev(struct usb_hcd
+ xhci_disable_slot(xhci, udev->slot_id);
+
+ spin_lock_irqsave(&xhci->lock, flags);
+- xhci_free_virt_device(xhci, udev->slot_id);
++ xhci_free_virt_device(xhci, virt_dev, udev->slot_id);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ }
+@@ -3996,6 +3996,16 @@ int xhci_disable_slot(struct xhci_hcd *x
+ return ret;
+ }
+
++int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id)
++{
++ struct xhci_virt_device *vdev = xhci->devs[slot_id];
++ int ret;
++
++ ret = xhci_disable_slot(xhci, slot_id);
++ xhci_free_virt_device(xhci, vdev, slot_id);
++ return ret;
++}
++
+ /*
+ * Checks if we have enough host controller resources for the default control
+ * endpoint.
+@@ -4101,8 +4111,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd,
+ return 1;
+
+ disable_slot:
+- xhci_disable_slot(xhci, udev->slot_id);
+- xhci_free_virt_device(xhci, udev->slot_id);
++ xhci_disable_and_free_slot(xhci, udev->slot_id);
+
+ return 0;
+ }
+@@ -4238,8 +4247,7 @@ static int xhci_setup_device(struct usb_
+ dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
+
+ mutex_unlock(&xhci->mutex);
+- ret = xhci_disable_slot(xhci, udev->slot_id);
+- xhci_free_virt_device(xhci, udev->slot_id);
++ ret = xhci_disable_and_free_slot(xhci, udev->slot_id);
+ if (!ret) {
+ if (xhci_alloc_dev(hcd, udev) == 1)
+ xhci_setup_addressable_virt_dev(xhci, udev);
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1997,7 +1997,7 @@ void xhci_dbg_trace(struct xhci_hcd *xhc
+ /* xHCI memory management */
+ void xhci_mem_cleanup(struct xhci_hcd *xhci);
+ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags);
+-void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id);
++void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id);
+ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags);
+ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev);
+ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
+@@ -2087,6 +2087,7 @@ void xhci_reset_bandwidth(struct usb_hcd
+ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
+ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
++int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id);
+ int xhci_ext_cap_init(struct xhci_hcd *xhci);
+
+ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);