+++ /dev/null
-From a9e52c49125fe55a68d3b5dedec3fe61e2d19464 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Thu, 11 Sep 2025 14:20:15 +0000
-Subject: xhci: sideband: add api to trace sideband usage
-
-From: Guan-Yu Lin <guanyulin@google.com>
-
-[ Upstream commit ef82a4803aabaf623bfcae07981406f1386eabf9 ]
-
-The existing sideband driver only registers sidebands without tracking
-their active usage. To address this, sideband will now record its active
-usage when it creates/removes interrupters. In addition, a new api is
-introduced to provide a means for other dirvers to fetch sideband
-activity information on a USB host controller.
-
-Signed-off-by: Guan-Yu Lin <guanyulin@google.com>
-Link: https://lore.kernel.org/r/20250911142051.90822-4-guanyulin@google.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Link: https://lore.kernel.org/r/20250911142051.90822-4-guanyulin@google.com
-Stable-dep-of: 8c13a7323b84 ("xhci: sideband: Fix race condition in sideband unregister")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- drivers/usb/host/xhci-sideband.c | 36 +++++++++++++++++++++++++++++++
- include/linux/usb/xhci-sideband.h | 9 ++++++++
- 2 files changed, 45 insertions(+)
-
-diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
-index d49f9886dd848..e771a476fef2e 100644
---- a/drivers/usb/host/xhci-sideband.c
-+++ b/drivers/usb/host/xhci-sideband.c
-@@ -266,6 +266,31 @@ xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
- }
- EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
-
-+/**
-+ * xhci_sideband_check - check the existence of active sidebands
-+ * @hcd: the host controller driver associated with the target host controller
-+ *
-+ * Allow other drivers, such as usb controller driver, to check if there are
-+ * any sideband activity on the host controller. This information could be used
-+ * for power management or other forms of resource management. The caller should
-+ * ensure downstream usb devices are all either suspended or marked as
-+ * "offload_at_suspend" to ensure the correctness of the return value.
-+ *
-+ * Returns true on any active sideband existence, false otherwise.
-+ */
-+bool xhci_sideband_check(struct usb_hcd *hcd)
-+{
-+ struct usb_device *udev = hcd->self.root_hub;
-+ bool active;
-+
-+ usb_lock_device(udev);
-+ active = usb_offload_check(udev);
-+ usb_unlock_device(udev);
-+
-+ return active;
-+}
-+EXPORT_SYMBOL_GPL(xhci_sideband_check);
-+
- /**
- * xhci_sideband_create_interrupter - creates a new interrupter for this sideband
- * @sb: sideband instance for this usb device
-@@ -286,6 +311,7 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
- bool ip_autoclear, u32 imod_interval, int intr_num)
- {
- int ret = 0;
-+ struct usb_device *udev;
-
- if (!sb || !sb->xhci)
- return -ENODEV;
-@@ -304,6 +330,9 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
- goto out;
- }
-
-+ udev = sb->vdev->udev;
-+ ret = usb_offload_get(udev);
-+
- sb->ir->ip_autoclear = ip_autoclear;
-
- out:
-@@ -323,6 +352,8 @@ EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
- void
- xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
- {
-+ struct usb_device *udev;
-+
- if (!sb || !sb->ir)
- return;
-
-@@ -330,6 +361,11 @@ xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
- xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
-
- sb->ir = NULL;
-+ udev = sb->vdev->udev;
-+
-+ if (udev->state != USB_STATE_NOTATTACHED)
-+ usb_offload_put(udev);
-+
- mutex_unlock(&sb->mutex);
- }
- EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
-diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
-index 45288c392f6e0..005257085dcbd 100644
---- a/include/linux/usb/xhci-sideband.h
-+++ b/include/linux/usb/xhci-sideband.h
-@@ -11,6 +11,7 @@
-
- #include <linux/scatterlist.h>
- #include <linux/usb.h>
-+#include <linux/usb/hcd.h>
-
- #define EP_CTX_PER_DEV 31 /* FIXME defined twice, from xhci.h */
-
-@@ -83,6 +84,14 @@ xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
- struct usb_host_endpoint *host_ep);
- struct sg_table *
- xhci_sideband_get_event_buffer(struct xhci_sideband *sb);
-+
-+#if IS_ENABLED(CONFIG_USB_XHCI_SIDEBAND)
-+bool xhci_sideband_check(struct usb_hcd *hcd);
-+#else
-+static inline bool xhci_sideband_check(struct usb_hcd *hcd)
-+{ return false; }
-+#endif /* IS_ENABLED(CONFIG_USB_XHCI_SIDEBAND) */
-+
- int
- xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
- bool ip_autoclear, u32 imod_interval, int intr_num);
---
-2.51.0
-
+++ /dev/null
-From 4281c3b2ba2dd298a105d49d2698ac542aecc278 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Fri, 7 Nov 2025 18:28:18 +0200
-Subject: xhci: sideband: Fix race condition in sideband unregister
-
-From: Mathias Nyman <mathias.nyman@linux.intel.com>
-
-[ Upstream commit 8c13a7323b847c0370aec66f655e2596a2174a17 ]
-
-Uttkarsh Aggarwal observed a kernel panic during sideband un-register
-and found it was caused by a race condition between sideband unregister,
-and creating sideband interrupters.
-The issue occurrs when thread T1 runs uaudio_disconnect() and released
-sb->xhci via sideband_unregister, while thread T2 simultaneously accessed
-the now-NULL sb->xhci in xhci_sideband_create_interrupter() resulting in
-a crash.
-
-Ensure new endpoints or interrupter can't be added to a sidenband after
-xhci_sideband_unregister() cleared the existing ones, and unlocked the
-sideband mutex.
-Reorganize code so that mutex is only taken and released once in
-xhci_sideband_unregister(), and clear sb->vdev while mutex is taken.
-
-Use mutex guards to reduce human unlock errors in code
-
-Refuse to add endpoints or interrupter if sb->vdev is not set.
-sb->vdev is set when sideband is created and registered.
-
-Reported-by: Uttkarsh Aggarwal <uttkarsh.aggarwal@oss.qualcomm.com>
-Closes: https://lore.kernel.org/linux-usb/20251028080043.27760-1-uttkarsh.aggarwal@oss.qualcomm.com
-Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity")
-Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
-Link: https://patch.msgid.link/20251107162819.1362579-4-mathias.nyman@linux.intel.com
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- drivers/usb/host/xhci-sideband.c | 102 ++++++++++++++++++-------------
- 1 file changed, 58 insertions(+), 44 deletions(-)
-
-diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
-index e771a476fef2e..a85f62a73313a 100644
---- a/drivers/usb/host/xhci-sideband.c
-+++ b/drivers/usb/host/xhci-sideband.c
-@@ -73,9 +73,12 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
- return NULL;
- }
-
-+/* Caller must hold sb->mutex */
- static void
- __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
- {
-+ lockdep_assert_held(&sb->mutex);
-+
- /*
- * Issue a stop endpoint command when an endpoint is removed.
- * The stop ep cmd handler will handle the ring cleanup.
-@@ -86,6 +89,25 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
- sb->eps[ep->ep_index] = NULL;
- }
-
-+/* Caller must hold sb->mutex */
-+static void
-+__xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
-+{
-+ struct usb_device *udev;
-+
-+ lockdep_assert_held(&sb->mutex);
-+
-+ if (!sb->ir)
-+ return;
-+
-+ xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
-+ sb->ir = NULL;
-+ udev = sb->vdev->udev;
-+
-+ if (udev->state != USB_STATE_NOTATTACHED)
-+ usb_offload_put(udev);
-+}
-+
- /* sideband api functions */
-
- /**
-@@ -131,14 +153,16 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
- struct xhci_virt_ep *ep;
- unsigned int ep_index;
-
-- mutex_lock(&sb->mutex);
-+ guard(mutex)(&sb->mutex);
-+
-+ if (!sb->vdev)
-+ return -ENODEV;
-+
- ep_index = xhci_get_endpoint_index(&host_ep->desc);
- ep = &sb->vdev->eps[ep_index];
-
-- if (ep->ep_state & EP_HAS_STREAMS) {
-- mutex_unlock(&sb->mutex);
-+ if (ep->ep_state & EP_HAS_STREAMS)
- return -EINVAL;
-- }
-
- /*
- * Note, we don't know the DMA mask of the audio DSP device, if its
-@@ -148,14 +172,11 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
- * and let this function add the endpoint and allocate the ring buffer
- * with the smallest common DMA mask
- */
-- if (sb->eps[ep_index] || ep->sideband) {
-- mutex_unlock(&sb->mutex);
-+ if (sb->eps[ep_index] || ep->sideband)
- return -EBUSY;
-- }
-
- ep->sideband = sb;
- sb->eps[ep_index] = ep;
-- mutex_unlock(&sb->mutex);
-
- return 0;
- }
-@@ -180,18 +201,16 @@ xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
- struct xhci_virt_ep *ep;
- unsigned int ep_index;
-
-- mutex_lock(&sb->mutex);
-+ guard(mutex)(&sb->mutex);
-+
- ep_index = xhci_get_endpoint_index(&host_ep->desc);
- ep = sb->eps[ep_index];
-
-- if (!ep || !ep->sideband || ep->sideband != sb) {
-- mutex_unlock(&sb->mutex);
-+ if (!ep || !ep->sideband || ep->sideband != sb)
- return -ENODEV;
-- }
-
- __xhci_sideband_remove_endpoint(sb, ep);
- xhci_initialize_ring_info(ep->ring);
-- mutex_unlock(&sb->mutex);
-
- return 0;
- }
-@@ -316,28 +335,25 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
- if (!sb || !sb->xhci)
- return -ENODEV;
-
-- mutex_lock(&sb->mutex);
-- if (sb->ir) {
-- ret = -EBUSY;
-- goto out;
-- }
-+ guard(mutex)(&sb->mutex);
-+
-+ if (!sb->vdev)
-+ return -ENODEV;
-+
-+ if (sb->ir)
-+ return -EBUSY;
-
- sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
- num_seg, imod_interval,
- intr_num);
-- if (!sb->ir) {
-- ret = -ENOMEM;
-- goto out;
-- }
-+ if (!sb->ir)
-+ return -ENOMEM;
-
- udev = sb->vdev->udev;
- ret = usb_offload_get(udev);
-
- sb->ir->ip_autoclear = ip_autoclear;
-
--out:
-- mutex_unlock(&sb->mutex);
--
- return ret;
- }
- EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
-@@ -352,21 +368,12 @@ EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
- void
- xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
- {
-- struct usb_device *udev;
--
-- if (!sb || !sb->ir)
-+ if (!sb)
- return;
-
-- mutex_lock(&sb->mutex);
-- xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
--
-- sb->ir = NULL;
-- udev = sb->vdev->udev;
-+ guard(mutex)(&sb->mutex);
-
-- if (udev->state != USB_STATE_NOTATTACHED)
-- usb_offload_put(udev);
--
-- mutex_unlock(&sb->mutex);
-+ __xhci_sideband_remove_interrupter(sb);
- }
- EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
-
-@@ -465,6 +472,7 @@ EXPORT_SYMBOL_GPL(xhci_sideband_register);
- void
- xhci_sideband_unregister(struct xhci_sideband *sb)
- {
-+ struct xhci_virt_device *vdev;
- struct xhci_hcd *xhci;
- int i;
-
-@@ -473,17 +481,23 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
-
- xhci = sb->xhci;
-
-- mutex_lock(&sb->mutex);
-- for (i = 0; i < EP_CTX_PER_DEV; i++)
-- if (sb->eps[i])
-- __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
-- mutex_unlock(&sb->mutex);
-+ scoped_guard(mutex, &sb->mutex) {
-+ vdev = sb->vdev;
-+ if (!vdev)
-+ return;
-+
-+ for (i = 0; i < EP_CTX_PER_DEV; i++)
-+ if (sb->eps[i])
-+ __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
-
-- xhci_sideband_remove_interrupter(sb);
-+ __xhci_sideband_remove_interrupter(sb);
-+
-+ sb->vdev = NULL;
-+ }
-
- spin_lock_irq(&xhci->lock);
- sb->xhci = NULL;
-- sb->vdev->sideband = NULL;
-+ vdev->sideband = NULL;
- spin_unlock_irq(&xhci->lock);
-
- kfree(sb);
---
-2.51.0
-