* [PATCH 00/19] USB: a bunch of llm-found fixes
@ 2026-10-27 14:57 Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 01/19] usb: core: sysfs: take the port lock to read the BOS Greg Kroah-Hartman
` (18 more replies)
0 siblings, 19 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Griffin Kroah-Hartman, Oliver Neukum, Johan Hovold,
Valentina Manea, Shuah Khan, Hongren Zheng, Peter Chen,
Pawel Laszczak, Roger Quadros, Neill Kapron, Ijae Kim,
Herve Codina, Justin Chen, Al Cooper,
Broadcom internal kernel review list, Xu Yang, Frank Li,
Elad Nachman, Badhri Jagan Sridharan, Heikki Krogerus,
Benson Leung, Romain Gantois, Rui Miguel Silva,
Minas Harutyunyan
Here's a series of some small llm-found fixes that have been sent to me
recently. Most of them are just minor bugfixes and locking cleanups,
but they are good to have from what I can tell.
Submitting them here to get a solid Sashiko run and some second eyes on
them before merging if all goes well.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
Josef Bacik (19):
usb: core: sysfs: take the port lock to read the BOS
USB: core: signal the end of an sg request last
usbip: vudc: free the unlink item if the send fails
usb: host: oxu210hp-hcd: fix oxu_qh_alloc() deadlock
usb: cdns3: switch the role unlocked on resume
usb: gadget: snps_udc_plat: lock out the interrupt
usb: gadget: bdc: the resume callback is optional
usb: gadget: uvc: fix oops on a premature STREAMON
usb: gadget: f_fs: let the request keep its fence
usb: host: ehci-orion: fix the probe without DT
usb: typec: tcpm: don't wait for the VDM discovery work
usb: typec: tcpci: don't act on unread alerts
usb: typec: anx7411: zero on-stack alt mode descriptors
usb: typec: mux: it5205: fix oops on accessory modes
usb: typec: mux: tusb1046: fix oops on accessory modes
usb: renesas_usbhs: link URBs with interrupts disabled
usb: renesas_usbhs: unlink the URB if it is not queued
usb: isp1760: wait for the TT clear in endpoint_disable
usb: dwc2: wait for the TT clear in endpoint_disable
drivers/usb/cdns3/cdns3-plat.c | 8 +++--
drivers/usb/cdns3/core.c | 55 +++++++++++++++++-----------
drivers/usb/cdns3/core.h | 3 ++
drivers/usb/core/hub.c | 32 +++++++++++++++++
drivers/usb/core/message.c | 36 +++++++++++++------
drivers/usb/core/sysfs.c | 4 +++
drivers/usb/core/usb.h | 3 ++
drivers/usb/dwc2/hcd.c | 15 ++++++++
drivers/usb/gadget/function/f_fs.c | 10 ++++--
drivers/usb/gadget/function/uvc_video.c | 4 +++
drivers/usb/gadget/udc/bdc/bdc_udc.c | 3 +-
drivers/usb/gadget/udc/snps_udc_plat.c | 8 ++---
drivers/usb/host/ehci-orion.c | 6 ++--
drivers/usb/host/oxu210hp-hcd.c | 23 ++++++------
drivers/usb/isp1760/isp1760-hcd.c | 11 ++++++
drivers/usb/renesas_usbhs/mod_host.c | 19 ++++++++--
drivers/usb/typec/anx7411.c | 2 +-
drivers/usb/typec/class.c | 2 +-
drivers/usb/typec/mux/it5205.c | 14 +++++---
drivers/usb/typec/mux/tusb1046.c | 14 +++++---
drivers/usb/typec/tcpm/tcpci.c | 64 ++++++++++++++++++++++++---------
drivers/usb/typec/tcpm/tcpm.c | 14 ++++++--
drivers/usb/usbip/vudc_tx.c | 2 +-
23 files changed, 266 insertions(+), 86 deletions(-)
---
base-commit: d135ab453a7400a34930bdda1ef4bdc8acfac80e
change-id: 20261027-llm-usb-e06293a1d353
Best regards,
--
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 01/19] usb: core: sysfs: take the port lock to read the BOS
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 02/19] USB: core: signal the end of an sg request last Greg Kroah-Hartman
` (17 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Griffin Kroah-Hartman, Oliver Neukum
From: Josef Bacik <josef@toxicpanda.com>
usb_reset_and_verify_device() sets udev->bos aside, lets hub_port_init()
read a new BOS into udev->bos, compares the two, frees the new one and
puts the old one back. bos_descriptors_read() takes the device lock
against that since commit 4e0197fbb0ee ("usb: core: sysfs: add lock to
bos_descriptors_read()").
This does not cover one caller. finish_port_resume() does a reset-resume
if the device has the quirk, if its port lost power, or if the device
does not answer GET_STATUS after a normal resume. In an autoresume this
runs without the device lock, for instance from
usb_autopm_get_interface() of an interface driver. The comment above
usb_reset_and_verify_device() says that tasks which hold the device lock
are expected to resume the device first.
bos_descriptors_read() does not. The file is readable for everybody, so
a reader can meet the temporary BOS of a device which is being resumed:
while usb_get_bos_descriptor() has not set ->desc yet (NULL
dereference), before it has corrected a wTotalLength which is larger
than the buffer (read beyond the buffer), or when it is freed (use after
free).
The file was made to give the descriptors to everybody without USB I/O,
so the reader should not wake up the device. There is a lock which both
callers of usb_reset_and_verify_device() hold, the one of the port:
usb_reset_device() takes it around the call and usb_port_resume() for
the whole resume. Take it in the reader too, inside the device lock,
which is the order of usb_reset_device(). A root hub has no port and is
never reset this way.
The port belongs to the parent hub, which removes its ports after it has
disconnected the children. usb_disconnect() cannot finish while the file
is being read, device_del() waits for the reader.
Fixes: 12fc84e8c428 ("usb: Export BOS descriptor to sysfs")
Cc: stable <stable@kernel.org>
Cc: Griffin Kroah-Hartman <griffin@kroah.com>
Cc: Oliver Neukum <oneukum@suse.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/core/hub.c | 32 ++++++++++++++++++++++++++++++++
drivers/usb/core/sysfs.c | 4 ++++
drivers/usb/core/usb.h | 3 +++
3 files changed, 39 insertions(+)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 199d3f535a89..9bc12079a80f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3278,6 +3278,38 @@ static void usb_unlock_port(struct usb_port *port_dev)
__release(&port_dev->status_lock);
}
+/**
+ * usb_lock_device_port - lock the port which a device is connected to
+ * @udev: a registered device, locked by the caller
+ *
+ * usb_reset_and_verify_device() runs with the port lock, also where it is
+ * called from an autoresume handler, without the device lock. This keeps it
+ * away without resuming the device.
+ *
+ * The hub removes its ports after usb_disconnect() has unregistered the
+ * child, so the caller has to make sure that this has not finished.
+ *
+ * Return: the port for usb_unlock_device_port(), NULL for a root hub.
+ */
+struct usb_port *usb_lock_device_port(struct usb_device *udev)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+ struct usb_port *port_dev;
+
+ if (!hub)
+ return NULL;
+
+ port_dev = hub->ports[udev->portnum - 1];
+ mutex_lock(&port_dev->status_lock);
+ return port_dev;
+}
+
+void usb_unlock_device_port(struct usb_port *port_dev)
+{
+ if (port_dev)
+ mutex_unlock(&port_dev->status_lock);
+}
+
#ifdef CONFIG_PM
/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index d22dc78457d7..95a51c7c7a08 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -901,12 +901,15 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
struct usb_device *udev = to_usb_device(dev);
struct usb_host_bos *bos;
struct usb_bos_descriptor *desc;
+ struct usb_port *port_dev;
size_t desclen, n = 0;
int rc;
rc = usb_lock_device_interruptible(udev);
if (rc < 0)
return -EINTR;
+ /* A reset-resume replaces udev->bos without the device lock */
+ port_dev = usb_lock_device_port(udev);
bos = udev->bos;
if (bos) {
desc = bos->desc;
@@ -916,6 +919,7 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
memcpy(buf, (void *) desc + off, n);
}
}
+ usb_unlock_device_port(port_dev);
usb_unlock_device(udev);
return n;
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index a9b37aeb515b..824e465bbb12 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -8,6 +8,7 @@
struct usb_hub_descriptor;
struct usb_dev_state;
+struct usb_port;
/* Functions local to drivers/usb/core/ */
@@ -85,6 +86,8 @@ extern bool usb_device_is_owned(struct usb_device *udev);
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
+extern struct usb_port *usb_lock_device_port(struct usb_device *udev);
+extern void usb_unlock_device_port(struct usb_port *port_dev);
extern int usb_major_init(void);
extern void usb_major_cleanup(void);
extern int usb_device_supports_lpm(struct usb_device *udev);
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 02/19] USB: core: signal the end of an sg request last
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 01/19] usb: core: sysfs: take the port lock to read the BOS Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 03/19] usbip: vudc: free the unlink item if the send fails Greg Kroah-Hartman
` (16 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Johan Hovold
From: Josef Bacik <josef@toxicpanda.com>
sg_complete() and usb_sg_cancel() call complete() with io->lock held and
unlock afterwards. usb_sg_wait() does not take the lock again after
wait_for_completion(), so it can return while the other CPU has not
unlocked yet:
CPU0 (giveback) CPU1 (usb_sg_wait())
spin_lock_irqsave(&io->lock)
io->count--
complete(&io->complete)
wait_for_completion() returns
sg_clean(io), return
the caller reuses the request
spin_unlock_irqrestore(&io->lock)
The request is the caller's. gud_usb_bulk(), sur40_process_video() and
usbtest have it on the stack, so the unlock writes to a frame which is
gone. usb-storage, rtsx_usb and vub300 call usb_sg_init() for the next
transfer, so the unlock hits a lock which was just initialised again.
The window is a few instructions with interrupts off, so this needs
something which stalls CPU0 there, like a preempted virtual CPU.
Note that the count reached zero, drop the lock and call complete() as
the last access.
This alone would open another window. After its submit loop
usb_sg_wait() takes the lock and calls complete() itself if it finds the
count at zero, also if it was the last URB which brought it there. It
could then return while sg_complete() is between its unlock and its
complete(). So far the lock kept the two apart. Let usb_sg_wait() only
complete the request if it has URBs to subtract which were not
submitted. Then the count reaches zero once, and only who brought it
there calls complete().
Fixes: 056ad39ee925 ("USB: core: Fix free-while-in-use bug in the USB S-Glibrary")
Cc: stable <stable@kernel.org>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Johan Hovold <johan@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/core/message.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index a738d03c23ea..204fbfd8171b 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -465,6 +465,7 @@ static void sg_complete(struct urb *urb)
unsigned long flags;
struct usb_sg_request *io = urb->context;
int status = urb->status;
+ bool done;
spin_lock_irqsave(&io->lock, flags);
@@ -520,13 +521,16 @@ static void sg_complete(struct urb *urb)
spin_lock_irqsave(&io->lock, flags);
}
- /* on the last completion, signal usb_sg_wait() */
io->bytes += urb->actual_length;
- io->count--;
- if (!io->count)
- complete(&io->complete);
-
+ done = !--io->count;
spin_unlock_irqrestore(&io->lock, flags);
+
+ /*
+ * On the last completion, signal usb_sg_wait(). Its caller may free
+ * or reuse the request as soon as it returns, so this comes last.
+ */
+ if (done)
+ complete(&io->complete);
}
@@ -753,9 +757,16 @@ void usb_sg_wait(struct usb_sg_request *io)
if (retval && (io->status == 0 || io->status == -ECONNRESET))
io->status = retval;
}
- io->count -= entries - i;
- if (io->count == 0)
- complete(&io->complete);
+ /*
+ * Whoever brings the count to zero calls complete(), once, after he has
+ * dropped the lock. If all URBs were submitted this is not us, even if
+ * the count is zero by now.
+ */
+ if (i < entries) {
+ io->count -= entries - i;
+ if (io->count == 0)
+ complete(&io->complete);
+ }
spin_unlock_irq(&io->lock);
/* OK, yes, this could be packaged as non-blocking.
@@ -780,6 +791,7 @@ void usb_sg_cancel(struct usb_sg_request *io)
{
unsigned long flags;
int i, retval;
+ bool done;
spin_lock_irqsave(&io->lock, flags);
if (io->status || io->count == 0) {
@@ -804,10 +816,12 @@ void usb_sg_cancel(struct usb_sg_request *io)
}
spin_lock_irqsave(&io->lock, flags);
- io->count--;
- if (!io->count)
- complete(&io->complete);
+ done = !--io->count;
spin_unlock_irqrestore(&io->lock, flags);
+
+ /* Don't touch the request after this, see sg_complete() */
+ if (done)
+ complete(&io->complete);
}
EXPORT_SYMBOL_GPL(usb_sg_cancel);
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 03/19] usbip: vudc: free the unlink item if the send fails
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 01/19] usb: core: sysfs: take the port lock to read the BOS Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 02/19] USB: core: signal the end of an sg request last Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 04/19] usb: host: oxu210hp-hcd: fix oxu_qh_alloc() deadlock Greg Kroah-Hartman
` (15 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Valentina Manea, Shuah Khan, Hongren Zheng
From: Josef Bacik <josef@toxicpanda.com>
v_send_ret() takes an item off udc->tx_queue, hands what it carries to
v_send_ret_submit() or v_send_ret_unlink(), and frees only the item
itself. v_send_ret_submit() releases its urbp on all paths behind the
first check; v_send_ret_unlink() frees the struct v_unlink only after a
complete send. When the connection breaks while a RET_UNLINK is being
sent, the v_unlink is leaked.
The reply has been built from it before the send: free it right after
kernel_sendmsg().
Fixes: d62ba981a9de ("usbip: vudc: Add vudc_tx")
Cc: stable <stable@kernel.org>
Cc: Valentina Manea <valentina.manea.m@gmail.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Hongren Zheng <i@zenithal.me>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/usbip/vudc_tx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c
index 88edfbcc0035..d78ff29e9654 100644
--- a/drivers/usb/usbip/vudc_tx.c
+++ b/drivers/usb/usbip/vudc_tx.c
@@ -59,13 +59,13 @@ static int v_send_ret_unlink(struct vudc *udc, struct v_unlink *unlink)
ret = kernel_sendmsg(udc->ud.tcp_socket, &msg, iov,
1, txsize);
+ kfree(unlink);
if (ret != txsize) {
usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_TCP);
if (ret >= 0)
return -EPIPE;
return ret;
}
- kfree(unlink);
return txsize;
}
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 04/19] usb: host: oxu210hp-hcd: fix oxu_qh_alloc() deadlock
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (2 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 03/19] usbip: vudc: free the unlink item if the send fails Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 05/19] usb: cdns3: switch the role unlocked on resume Greg Kroah-Hartman
` (14 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik, stable
From: Josef Bacik <josef@toxicpanda.com>
oxu_qh_alloc() calls ehci_qtd_alloc() with mem_lock held, and that
function takes the same spinlock.
Allocate the dummy qtd before taking the lock, and give it back, without
the lock, if there is no free queue head.
Fixes: b92a78e582b1 ("usb host: Oxford OXU210HP HCD driver.")
Cc: stable <stable@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/host/oxu210hp-hcd.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index ca96067e6d6d..3fff2d2a4f3f 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -1034,6 +1034,14 @@ static struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu)
{
int i;
struct ehci_qh *qh = NULL;
+ struct ehci_qtd *dummy;
+
+ /* dummy td enables safe urb queuing; this takes mem_lock */
+ dummy = ehci_qtd_alloc(oxu);
+ if (!dummy) {
+ oxu_dbg(oxu, "no dummy td\n");
+ return NULL;
+ }
spin_lock(&oxu->mem_lock);
@@ -1049,21 +1057,16 @@ static struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu)
qh->oxu = oxu;
qh->qh_dma = virt_to_phys(qh);
INIT_LIST_HEAD(&qh->qtd_list);
-
- /* dummy td enables safe urb queuing */
- qh->dummy = ehci_qtd_alloc(oxu);
- if (qh->dummy == NULL) {
- oxu_dbg(oxu, "no dummy td\n");
- oxu->qh_used[i] = 0;
- qh = NULL;
- goto unlock;
- }
+ qh->dummy = dummy;
oxu->qh_used[i] = 1;
}
-unlock:
+
spin_unlock(&oxu->mem_lock);
+ if (!qh)
+ oxu_qtd_free(oxu, dummy);
+
return qh;
}
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 05/19] usb: cdns3: switch the role unlocked on resume
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (3 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 04/19] usb: host: oxu210hp-hcd: fix oxu_qh_alloc() deadlock Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 06/19] usb: gadget: snps_udc_plat: lock out the interrupt Greg Kroah-Hartman
` (13 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Peter Chen, Pawel Laszczak, Roger Quadros
From: Josef Bacik <josef@toxicpanda.com>
cdns3_controller_resume() calls cdns_resume() with cdns->lock held and
interrupts off. If the controller has lost its power during suspend and
the role which the ID and VBUS inputs ask for is not the current one any
more, cdns_resume() calls cdns_hw_role_switch(). That stops one role
and starts another, which takes a mutex, allocates memory with
GFP_KERNEL, requests interrupts and registers or unregisters the xHCI
platform device or the UDC:
BUG: sleeping function called from invalid context
To get there, change the cable of a dual role port without a role switch
device while the system is asleep, on a SoC which powers the controller
down.
Move the switch to a function of its own, cdns_resume_role(), which is
called before the lock is taken. What remains of cdns_resume() is still
called with the lock held, and skipped if the role has been switched or
the switch has failed, as before.
cdns_hw_role_switch() also calls pm_runtime_get_sync() for the
controller. That cannot work from its own runtime resume callback, where
it would wait for itself, so the role is only switched on system resume
now. After a runtime resume the old role is resumed, like after a
suspend without loss of power.
Fixes: 2cf2581cd229 ("usb: cdns3: add power lost support for system resume")
Cc: stable <stable@kernel.org>
Cc: Peter Chen <peter.chen@kernel.org>
Cc: Pawel Laszczak <pawell@cadence.com>
Cc: Roger Quadros <rogerq@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/cdns3/cdns3-plat.c | 8 ++++--
drivers/usb/cdns3/core.c | 55 ++++++++++++++++++++++++++----------------
drivers/usb/cdns3/core.h | 3 +++
3 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c
index e3f32c3e9535..674625cdd4d4 100644
--- a/drivers/usb/cdns3/cdns3-plat.c
+++ b/drivers/usb/cdns3/cdns3-plat.c
@@ -268,7 +268,7 @@ static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
{
struct cdns *cdns = dev_get_drvdata(dev);
- int ret;
+ int ret, role_changed;
unsigned long flags;
if (!cdns->in_lpm)
@@ -292,8 +292,12 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
cdns3_set_platform_suspend(cdns->dev, false, false);
+ /* It resumes the device, which cannot be done from runtime resume */
+ role_changed = PMSG_IS_AUTO(msg) ? 0 : cdns_resume_role(cdns);
+
spin_lock_irqsave(&cdns->lock, flags);
- cdns_resume(cdns);
+ if (!role_changed)
+ cdns_resume(cdns);
cdns->in_lpm = false;
spin_unlock_irqrestore(&cdns->lock, flags);
cdns_set_active(cdns, !PMSG_IS_AUTO(msg));
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index 87122fb5bdb8..3b2b8f231a92 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -504,36 +504,49 @@ int cdns_suspend(struct cdns *cdns)
}
EXPORT_SYMBOL_GPL(cdns_suspend);
+/**
+ * cdns_resume_role - switch the role if it has changed while the power was off
+ * @cdns: controller
+ *
+ * Stopping and starting a role sleeps, so this has to be called before
+ * cdns->lock is taken for cdns_resume().
+ *
+ * Returns 1 if the role has been switched: it has been started from scratch
+ * and must not be resumed. 0 if there was nothing to do, negative errno if the
+ * switch has failed.
+ */
+int cdns_resume_role(struct cdns *cdns)
+{
+ int ret;
+
+ if (cdns->role_sw || !cdns_power_is_lost(cdns))
+ return 0;
+
+ if (cdns_hw_role_state_machine(cdns) == cdns->role)
+ return 0;
+
+ ret = cdns_hw_role_switch(cdns);
+
+ return ret ? ret : 1;
+}
+EXPORT_SYMBOL_GPL(cdns_resume_role);
+
int cdns_resume(struct cdns *cdns)
{
bool power_lost = cdns_power_is_lost(cdns);
- enum usb_role real_role;
- bool role_changed = false;
int ret = 0;
if (power_lost) {
- if (!cdns->role_sw) {
- real_role = cdns_hw_role_state_machine(cdns);
- if (real_role != cdns->role) {
- ret = cdns_hw_role_switch(cdns);
- if (ret)
- return ret;
- role_changed = true;
- }
- }
+ if (cdns->role == USB_ROLE_HOST)
+ ret = cdns_drd_host_on(cdns);
+ else if (cdns->role == USB_ROLE_DEVICE)
+ ret = cdns_drd_gadget_on(cdns);
- if (!role_changed) {
- if (cdns->role == USB_ROLE_HOST)
- ret = cdns_drd_host_on(cdns);
- else if (cdns->role == USB_ROLE_DEVICE)
- ret = cdns_drd_gadget_on(cdns);
-
- if (ret)
- return ret;
- }
+ if (ret)
+ return ret;
}
- if (!role_changed && cdns->roles[cdns->role]->resume)
+ if (cdns->roles[cdns->role]->resume)
cdns->roles[cdns->role]->resume(cdns, power_lost);
return 0;
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 8c492fda924c..cfc3be9ddf9e 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -136,10 +136,13 @@ int cdns_remove(struct cdns *cdns);
int cdns_core_init_role(struct cdns *cdns);
#ifdef CONFIG_PM_SLEEP
+int cdns_resume_role(struct cdns *cdns);
int cdns_resume(struct cdns *cdns);
int cdns_suspend(struct cdns *cdns);
void cdns_set_active(struct cdns *cdns, u8 set_active);
#else /* CONFIG_PM_SLEEP */
+static inline int cdns_resume_role(struct cdns *cdns)
+{ return 0; }
static inline int cdns_resume(struct cdns *cdns)
{ return 0; }
static inline void cdns_set_active(struct cdns *cdns, u8 set_active) { }
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 06/19] usb: gadget: snps_udc_plat: lock out the interrupt
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (4 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 05/19] usb: cdns3: switch the role unlocked on resume Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 18:17 ` Herve Codina
2026-10-27 14:57 ` [PATCH 07/19] usb: gadget: bdc: the resume callback is optional Greg Kroah-Hartman
` (12 subsequent siblings)
18 siblings, 1 reply; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Neill Kapron, Ijae Kim, Herve Codina
From: Josef Bacik <josef@toxicpanda.com>
stop_udc() is called from a work item, when the extcon device reports
that the cable has been unplugged. It takes udc->lock with interrupts
enabled. udc_irq(), the hard interrupt handler of the controller, takes
the same lock. If the controller raises an interrupt on the CPU which
is in stop_udc(), and it is likely to have something to report when the
cable is pulled, the handler spins on a lock which the interrupted work
holds: the CPU is stuck.
Disable interrupts while the lock is held. They are enabled again for
the ->disconnect() callback of the gadget driver, for which the lock is
dropped as before.
Fixes: 1b9f35adb0ff ("usb: gadget: udc: Add Synopsys UDC Platform driver")
Cc: stable <stable@kernel.org>
Cc: Neill Kapron <nkapron@google.com>
Cc: Ijae Kim <ae878000@gmail.com>
Cc: Herve Codina <herve.codina@bootlin.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/gadget/udc/snps_udc_plat.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
index 8ce43c4f8fba..e2e71cc947dc 100644
--- a/drivers/usb/gadget/udc/snps_udc_plat.c
+++ b/drivers/usb/gadget/udc/snps_udc_plat.c
@@ -34,7 +34,7 @@ static void stop_udc(struct udc *udc)
int tmp;
u32 reg;
- spin_lock(&udc->lock);
+ spin_lock_irq(&udc->lock);
/* Flush the receieve fifo */
reg = readl(&udc->regs->ctl);
@@ -53,9 +53,9 @@ static void stop_udc(struct udc *udc)
/* Disconnect gadget driver */
if (udc->driver) {
- spin_unlock(&udc->lock);
+ spin_unlock_irq(&udc->lock);
udc->driver->disconnect(&udc->gadget);
- spin_lock(&udc->lock);
+ spin_lock_irq(&udc->lock);
/* empty queues */
for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
@@ -63,7 +63,7 @@ static void stop_udc(struct udc *udc)
}
udc->connected = 0;
- spin_unlock(&udc->lock);
+ spin_unlock_irq(&udc->lock);
dev_info(udc->dev, "Device disconnected\n");
}
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 07/19] usb: gadget: bdc: the resume callback is optional
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (5 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 06/19] usb: gadget: snps_udc_plat: lock out the interrupt Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 08/19] usb: gadget: uvc: fix oops on a premature STREAMON Greg Kroah-Hartman
` (11 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Justin Chen, Al Cooper,
Broadcom internal kernel review list, Neill Kapron
From: Josef Bacik <josef@toxicpanda.com>
handle_link_state_change() tests gadget_driver->suspend before it calls
it and sets DEVICE_SUSPENDED, but calls gadget_driver->resume untested
when the link comes back. gadgetfs has a suspend callback and no resume
callback: with it the first bus resume ends in a call of NULL.
Fixes: efed421a94e6 ("usb: gadget: Add UDC driver for Broadcom USB3.0 device controller IP BDC")
Cc: stable <stable@kernel.org>
Cc: Justin Chen <justin.chen@broadcom.com>
Cc: Al Cooper <alcooperx@gmail.com>
Cc: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
Cc: Neill Kapron <nkapron@google.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/gadget/udc/bdc/bdc_udc.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
index 7a12219edac6..88a8f6550e02 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -218,7 +218,8 @@ static void handle_link_state_change(struct bdc *bdc, u32 uspc)
case BDC_LINK_STATE_RESUME:
dev_dbg(bdc->dev, "Resumed from Suspend\n");
if (bdc->devstatus & DEVICE_SUSPENDED) {
- bdc->gadget_driver->resume(&bdc->gadget);
+ if (bdc->gadget_driver->resume)
+ bdc->gadget_driver->resume(&bdc->gadget);
bdc->devstatus &= ~DEVICE_SUSPENDED;
}
break;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 08/19] usb: gadget: uvc: fix oops on a premature STREAMON
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (6 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 07/19] usb: gadget: bdc: the resume callback is optional Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 09/19] usb: gadget: f_fs: let the request keep its fence Greg Kroah-Hartman
` (10 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Neill Kapron, Xu Yang, Frank Li
From: Josef Bacik <josef@toxicpanda.com>
VIDIOC_STREAMON ends in uvc_video_prep_requests(), which looks at the
descriptor of the streaming endpoint. video->ep->desc is NULL after
usb_ep_autoconfig(). It is set by config_ep_by_speed() when the host
selects alternate setting 1 of the streaming interface, and UDC drivers
such as dwc3 clear it again when the endpoint is disabled. An
application which allocates buffers and calls STREAMON without waiting
for the UVC_EVENT_STREAMON event dereferences a NULL pointer.
Return -ENODEV, as for a function which is not bound.
Fixes: 48dbe731171e ("usb: gadget: uvc: set req_size and n_requests based on the frame interval")
Cc: stable <stable@kernel.org>
Cc: Neill Kapron <nkapron@google.com>
Cc: Xu Yang <xu.yang_2@nxp.com>
Cc: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/gadget/function/uvc_video.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 9ba09118bb74..2a56536c3481 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -770,6 +770,10 @@ int uvcg_video_enable(struct uvc_video *video)
return -ENODEV;
}
+ /* Set while the host has selected the streaming alternate setting. */
+ if (!video->ep->desc)
+ return -ENODEV;
+
/*
* Safe to access request related fields without req_lock because
* this is the only thread currently active, and no other
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 09/19] usb: gadget: f_fs: let the request keep its fence
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (7 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 08/19] usb: gadget: uvc: fix oops on a premature STREAMON Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 10/19] usb: host: ehci-orion: fix the probe without DT Greg Kroah-Hartman
` (9 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Neill Kapron
From: Josef Bacik <josef@toxicpanda.com>
ffs_dmabuf_transfer() stores a plain pointer to the fence in the context
of the USB request, and since the fix for the fence leak it drops the
initial reference right after dma_resv_add_fence(). The only reference
left is the one of the reservation object.
That one does not last until the fence is signalled. dma_resv_add_fence()
replaces and puts a fence as soon as a later fence of the same context
and the same usage is added. All the fences of an attachment share a
context, and a transfer on an IN endpoint only waits for the writers,
not for the read fence of the transfer before it. So the second
FUNCTIONFS_DMABUF_TRANSFER of a buffer which is still queued frees the
fence of the first. When the first request completes,
ffs_dmabuf_signal_done() signals a fence in freed memory and queues the
work item in it.
The leak came from the reference which ffs_dmabuf_signal_done() takes
and ffs_dmabuf_cleanup() drops: with it nobody dropped the initial one.
Do not take it, and leave the initial reference to the request until the
cleanup.
Fixes: baa6b6068a3f ("usb: gadget: f_fs: Fix DMA fence leak")
Cc: stable <stable@kernel.org>
Cc: Neill Kapron <nkapron@google.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/gadget/function/f_fs.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index c64a268e98a4..26f511794d6f 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1447,13 +1447,12 @@ static void ffs_dmabuf_signal_done(struct ffs_dma_fence *dma_fence, int ret)
struct dma_fence *fence = &dma_fence->base;
bool cookie = dma_fence_begin_signalling();
- dma_fence_get(fence);
fence->error = ret;
dma_fence_signal(fence);
dma_fence_end_signalling(cookie);
/*
- * The fence will be unref'd in ffs_dmabuf_cleanup.
+ * The reference of the request will be dropped in ffs_dmabuf_cleanup.
* It can't be done here, as the unref functions might try to lock
* the resv object, which would deadlock.
*/
@@ -1737,8 +1736,13 @@ static int ffs_dmabuf_transfer(struct file *file,
resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
+ /*
+ * The reservation object takes a reference of its own, and drops it as
+ * soon as a later fence of this attachment takes the place of this
+ * one. The initial reference stays with the request, which points to
+ * the fence until ffs_dmabuf_cleanup() has run.
+ */
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
- dma_fence_put(&fence->base);
dma_resv_unlock(dmabuf->resv);
/* Now that the dma_fence is in place, queue the transfer. */
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 10/19] usb: host: ehci-orion: fix the probe without DT
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (8 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 09/19] usb: gadget: f_fs: let the request keep its fence Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 11/19] usb: typec: tcpm: don't wait for the VDM discovery work Greg Kroah-Hartman
` (8 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
Elad Nachman, stable
From: Josef Bacik <josef@toxicpanda.com>
The Orion5x, MV78xx0 and Dove board files register the controllers as
platform devices called "orion-ehci" (arch/arm/plat-orion/common.c),
which are bound by name. of_device_get_match_data() returns NULL for
them, and ehci_orion_drv_probe() dereferences it to get the DMA mask.
Use the 32 bit mask, as before, if there is no match data.
Fixes: c63fe1cc7093 ("usb: host: Add ac5 to EHCI Orion")
Cc: Elad Nachman <enachman@marvell.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/host/ehci-orion.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index eaaa49712a8c..2071c3d6e582 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -218,7 +218,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
int irq, err;
enum orion_ehci_phy_ver phy_version;
struct orion_ehci_hcd *priv;
- u64 *dma_mask_ptr;
+ const u64 *dma_mask_ptr;
if (usb_disabled())
return -ENODEV;
@@ -236,7 +236,9 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
* set. Since shared usb code relies on it, set it here for
* now. Once we have dma capability bindings this can go away.
*/
- dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev);
+ dma_mask_ptr = of_device_get_match_data(&pdev->dev);
+ if (!dma_mask_ptr)
+ dma_mask_ptr = &dma_mask_orion;
err = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr);
if (err)
goto err;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 11/19] usb: typec: tcpm: don't wait for the VDM discovery work
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (9 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 10/19] usb: host: ehci-orion: fix the probe without DT Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 12/19] usb: typec: tcpci: don't act on unread alerts Greg Kroah-Hartman
` (7 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Badhri Jagan Sridharan, Heikki Krogerus
From: Josef Bacik <josef@toxicpanda.com>
mod_vdm_discovery_cancel_delayed_work() calls kthread_cancel_work_sync()
with port->lock held, and tcpm_vdm_discovery_work() starts by taking
that lock. This is fine as long as the caller runs on the kthread
worker of the port, like the work. But tcpm_reset_port() is also
reached from tcpm_tcpc_reset(), which tcpci_irq() calls from its
interrupt thread. If the work is running, it waits for the lock while
the holder of the lock waits for the work.
Use a flag instead, protected by port->lock, which is set when the work
is scheduled and cleared when it is cancelled. The work returns at once
if the flag is not set.
tcpm_unregister_port() calls tcpm_reset_port() after it has destroyed
the worker. Commit 98d00deb6ae4 ("usb: typec: tcpm: fix use-after-free
of the kthread worker on port unregister") put a test of port->wq in
front of the cancel for that case. Nothing is cancelled any more, so
the test goes.
Fixes: 205dc9cb39f5 ("usb: typec: tcpm: implement retry mechanism for Discover Identity VDMs")
Cc: stable <stable@kernel.org>
Cc: Badhri Jagan Sridharan <badhri@google.com>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/typec/tcpm/tcpm.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index d721baef5210..600e820a7b93 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -542,6 +542,7 @@ struct tcpm_port {
struct kthread_work enable_frs;
struct hrtimer vdm_discovery_timer;
struct kthread_work vdm_discovery_work;
+ bool vdm_discovery_armed;
bool state_machine_running;
/* Set to true when VDM State Machine has following actions. */
bool vdm_sm_running;
@@ -1768,15 +1769,20 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del
}
}
+/*
+ * Called with port->lock held, which the work takes, and not only from the
+ * worker: the work cannot be waited for. It does nothing unless it is armed.
+ */
static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port)
{
hrtimer_cancel(&port->vdm_discovery_timer);
- if (port->wq)
- kthread_cancel_work_sync(&port->vdm_discovery_work);
+ port->vdm_discovery_armed = false;
}
static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
{
+ port->vdm_discovery_armed = true;
+
if (delay_ms) {
hrtimer_start(&port->vdm_discovery_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
} else {
@@ -7295,6 +7301,10 @@ static void tcpm_vdm_discovery_work(struct kthread_work *work)
mutex_lock(&port->lock);
+ if (!port->vdm_discovery_armed)
+ goto unlock;
+ port->vdm_discovery_armed = false;
+
tcpm_log_force(port, "%s state [%s]", __func__,
vdm_discovery_state_strings[port->vdm_discovery_state]);
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 12/19] usb: typec: tcpci: don't act on unread alerts
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (10 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 11/19] usb: typec: tcpm: don't wait for the VDM discovery work Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 13/19] usb: typec: anx7411: zero on-stack alt mode descriptors Greg Kroah-Hartman
` (6 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
Badhri Jagan Sridharan, Heikki Krogerus, stable
From: Josef Bacik <josef@toxicpanda.com>
tcpci_irq() ignores the result of nearly all its register reads.
If the first read of TCPC_ALERT fails, "status" is an unwritten variable,
and its bits decide which events are reported to the port manager: among
them a hard reset, which drops VBUS. If the read at the end of the function
fails, "status" keeps the alerts which have just been handled: they are
handled again, for as long as the read fails.
In the receive branch the byte count, the header and the payload are not
checked: a count made of stack content trips the WARN_ON(), and a message
made of stack content is handed to tcpm_pd_receive(). A failed read of the
frame type returns a negative errno as irqreturn_t, with RX_STATUS still
set.
The handler must not simply return in these cases. Nine in-tree boards
connect ALERT# to an edge triggered interrupt: as long as an alert is
pending the line stays low, and there is no further edge. That is what the
read at the end is for.
So read the alert register through a helper which tries three times and
reports "no alert" if that fails: after a failed first read the function
falls through to the read at the end. Drop a message which cannot be read,
but clear RX_STATUS, to release the line. Don't take an unread power status
mask for a reset of the TCPC. Return IRQ_HANDLED if any round has found an
alert, not only the first one.
Fixes: 74e656d6b055 ("staging: typec: Type-C Port Controller Interface driver (tcpci)")
Fixes: 77e85107a771 ("usb: typec: tcpci: support edge irq")
Fixes: b691a07c5f64 ("usb: typec: tcpci: pass correct rx_type to tcpm_pd_receive()")
Cc: Badhri Jagan Sridharan <badhri@google.com>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/typec/tcpm/tcpci.c | 64 +++++++++++++++++++++++++++++++-----------
1 file changed, 47 insertions(+), 17 deletions(-)
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 6717ac914c6a..26c62216a813 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -715,17 +715,34 @@ static int tcpci_init(struct tcpc_dev *tcpc)
return 0;
}
+/*
+ * An edge triggered ALERT# does not fire again while an alert is pending, so
+ * don't give up after one failed transfer. No alert if it cannot be read.
+ */
+static u16 tcpci_read_alert(struct tcpci *tcpci)
+{
+ u16 status;
+ int i;
+
+ for (i = 0; i < 3; i++)
+ if (!tcpci_read16(tcpci, TCPC_ALERT, &status))
+ return status;
+
+ return 0;
+}
+
irqreturn_t tcpci_irq(struct tcpci *tcpci)
{
u16 status;
int ret;
- int irq_ret;
+ int irq_ret = 0;
unsigned int raw;
- tcpci_read16(tcpci, TCPC_ALERT, &status);
- irq_ret = status & tcpci->alert_mask;
+ status = tcpci_read_alert(tcpci);
process_status:
+ irq_ret |= status & tcpci->alert_mask;
+
/*
* Clear alert status for everything except RX_STATUS, which shouldn't
* be cleared until we have successfully retrieved message.
@@ -738,12 +755,12 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci)
tcpm_cc_change(tcpci->port);
if (status & TCPC_ALERT_POWER_STATUS) {
- regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw);
+ ret = regmap_read(tcpci->regmap, TCPC_POWER_STATUS_MASK, &raw);
/*
* If power status mask has been reset, then the TCPC
* has reset.
*/
- if (raw == 0xff)
+ if (!ret && raw == 0xff)
tcpm_tcpc_reset(tcpci->port);
else
tcpm_vbus_change(tcpci->port);
@@ -755,7 +772,10 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci)
enum tcpm_transmit_type rx_type;
u16 header;
- regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt);
+ ret = regmap_read(tcpci->regmap, TCPC_RX_BYTE_CNT, &cnt);
+ if (ret)
+ goto rx_done;
+
/*
* 'cnt' corresponds to READABLE_BYTE_COUNT in section 4.4.14
* of the TCPCI spec [Rev 2.0 Ver 1.0 October 2017] and is
@@ -767,26 +787,36 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci)
else
payload_cnt = 0;
- tcpci_read16(tcpci, TCPC_RX_HDR, &header);
+ ret = tcpci_read16(tcpci, TCPC_RX_HDR, &header);
+ if (ret)
+ goto rx_done;
+
msg.header = cpu_to_le16(header);
if (WARN_ON(payload_cnt > sizeof(msg.payload)))
payload_cnt = sizeof(msg.payload);
- if (payload_cnt > 0)
- regmap_raw_read(tcpci->regmap, TCPC_RX_DATA,
- &msg.payload, payload_cnt);
+ if (payload_cnt > 0) {
+ ret = regmap_raw_read(tcpci->regmap, TCPC_RX_DATA,
+ &msg.payload, payload_cnt);
+ if (ret)
+ goto rx_done;
+ }
ret = regmap_read(tcpci->regmap, TCPC_RX_BUF_FRAME_TYPE, &rx_type);
- if (ret)
- return ret;
- /* Read complete, clear RX status alert bit */
+rx_done:
+ /*
+ * Read complete, clear RX status alert bit. Also if the message
+ * could not be read: it is dropped, ALERT# must be released.
+ */
tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
- rx_type &= TCPC_RX_BUF_FRAME_TYPE_MASK;
- if (tcpci->rx_type_mask & BIT(rx_type))
- tcpm_pd_receive(tcpci->port, &msg, rx_type);
+ if (!ret) {
+ rx_type &= TCPC_RX_BUF_FRAME_TYPE_MASK;
+ if (tcpci->rx_type_mask & BIT(rx_type))
+ tcpm_pd_receive(tcpci->port, &msg, rx_type);
+ }
}
if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) {
@@ -805,7 +835,7 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci)
else if (status & TCPC_ALERT_TX_FAILED)
tcpm_pd_transmit_complete(tcpci->port, TCPC_TX_FAILED);
- tcpci_read16(tcpci, TCPC_ALERT, &status);
+ status = tcpci_read_alert(tcpci);
if (status & tcpci->alert_mask)
goto process_status;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 13/19] usb: typec: anx7411: zero on-stack alt mode descriptors
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (11 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 12/19] usb: typec: tcpci: don't act on unread alerts Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 14/19] usb: typec: mux: it5205: fix oops on accessory modes Greg Kroah-Hartman
` (5 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Heikki Krogerus, Benson Leung
From: Josef Bacik <josef@toxicpanda.com>
typec_port_register_altmodes() and anx7411_typec_register_altmode() fill a
struct typec_altmode_desc on the stack member by member: svid, vdo, mode.
typec_register_altmode() reads three more members:
- inactive: a port alternate mode is registered with active = !inactive.
If the byte of stack garbage is not 0, the mode starts disabled and
typec_altmode_enter() refuses it with -EPERM until somebody writes to
the "active" attribute of the port;
- mode_selection: the DisplayPort and Thunderbolt drivers enter the mode
on their own only if it is false. For the partner alternate modes of
anx7411, garbage there means that the mode is never entered;
- roles: decides what supported_roles of the port alternate mode shows.
typec_port_register_altmodes() is what tcpm and anx7411 register the
alternate modes of their ports with.
CONFIG_INIT_STACK_ALL_ZERO=y hides the problem.
Fixes: 183b194d8fb6 ("usb: typec: Make active on port altmode writable")
Fixes: be727d400066 ("usb: typec: Introduce mode_selection bit")
Fixes: 7b458a4c5d73 ("usb: typec: Add typec_port_register_altmodes()")
Cc: stable <stable@kernel.org>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Cc: Benson Leung <bleung@chromium.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/typec/anx7411.c | 2 +-
drivers/usb/typec/class.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 06c25af094b3..ac122ab3175a 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -552,7 +552,7 @@ static int anx7411_typec_register_altmode(struct anx7411_data *ctx,
int svid, int vdo)
{
struct device *dev = &ctx->spi_client->dev;
- struct typec_altmode_desc desc;
+ struct typec_altmode_desc desc = {};
int err;
int i;
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 54c7810b563b..9d9703c70820 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -2684,7 +2684,7 @@ void typec_port_register_altmodes(struct typec_port *port,
struct typec_altmode **altmodes, size_t n)
{
struct fwnode_handle *child;
- struct typec_altmode_desc desc;
+ struct typec_altmode_desc desc = {};
struct typec_altmode *alt;
size_t index = 0;
u16 svid;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 14/19] usb: typec: mux: it5205: fix oops on accessory modes
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (12 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 13/19] usb: typec: anx7411: zero on-stack alt mode descriptors Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 15/19] usb: typec: mux: tusb1046: " Greg Kroah-Hartman
` (4 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Heikki Krogerus
From: Josef Bacik <josef@toxicpanda.com>
typec_set_mode() passes a state without an alternate mode, and the USB
and accessory modes (TYPEC_MODE_USB2 ... TYPEC_MODE_DEBUG) start at
TYPEC_STATE_MODAL like the states of the alternate modes. TCPM and UCSI
set TYPEC_MODE_AUDIO or TYPEC_MODE_DEBUG when such an accessory is
attached. it5205_mux_set() then reads state->alt->svid: plugging in an
analog audio adapter is a NULL pointer dereference.
The numbers must not reach the switch either: TYPEC_MODE_AUDIO has the
value of TYPEC_DP_STATE_D. Without an alternate mode connect the
SuperSpeed lanes for TYPEC_MODE_USB3 and disconnect them for the rest.
Fixes: 41fe9ea1696c ("usb: typec: mux: Add ITE IT5205 Alternate Mode Passive MUX driver")
Cc: stable <stable@kernel.org>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/typec/mux/it5205.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/mux/it5205.c b/drivers/usb/typec/mux/it5205.c
index 2ab2ba6a3cbd..00d5bd55e423 100644
--- a/drivers/usb/typec/mux/it5205.c
+++ b/drivers/usb/typec/mux/it5205.c
@@ -92,13 +92,19 @@ static int it5205_switch_set(struct typec_switch_dev *sw, enum typec_orientation
static int it5205_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
{
struct it5205 *it = typec_mux_get_drvdata(mux);
+ unsigned long mode = state->mode;
u8 val;
- if (state->mode >= TYPEC_STATE_MODAL &&
- state->alt->svid != USB_TYPEC_DP_SID)
- return -EINVAL;
+ if (mode >= TYPEC_STATE_MODAL) {
+ /* USB and accessory modes: not the DP pin assignments */
+ if (!state->alt)
+ mode = mode == TYPEC_MODE_USB3 ? TYPEC_STATE_USB :
+ TYPEC_STATE_SAFE;
+ else if (state->alt->svid != USB_TYPEC_DP_SID)
+ return -EINVAL;
+ }
- switch (state->mode) {
+ switch (mode) {
case TYPEC_STATE_USB:
val = IT5205_USB;
break;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 15/19] usb: typec: mux: tusb1046: fix oops on accessory modes
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (13 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 14/19] usb: typec: mux: it5205: fix oops on accessory modes Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 16/19] usb: renesas_usbhs: link URBs with interrupts disabled Greg Kroah-Hartman
` (3 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Romain Gantois, Heikki Krogerus
From: Josef Bacik <josef@toxicpanda.com>
typec_set_mode() passes a state without an alternate mode, and the USB
and accessory modes (TYPEC_MODE_USB2 ... TYPEC_MODE_DEBUG) start at
TYPEC_STATE_MODAL like the states of the alternate modes. TCPM and UCSI
set TYPEC_MODE_AUDIO or TYPEC_MODE_DEBUG when such an accessory is
attached. tusb1046_mux_set() then reads state->alt->svid: plugging in an
analog audio adapter is a NULL pointer dereference.
The numbers must not reach the switch either: TYPEC_MODE_AUDIO has the
value of TYPEC_DP_STATE_D. Without an alternate mode connect the
SuperSpeed lanes for TYPEC_MODE_USB3 and disconnect them for the rest.
Fixes: 7c561b8c3e83 ("usb: typec: mux: Add support for the TUSB1046 crosspoint switch")
Cc: stable <stable@kernel.org>
Cc: Romain Gantois <romain.gantois@bootlin.com>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/typec/mux/tusb1046.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c
index 34ef81f75693..a463ba5f53be 100644
--- a/drivers/usb/typec/mux/tusb1046.c
+++ b/drivers/usb/typec/mux/tusb1046.c
@@ -43,11 +43,17 @@ static int tusb1046_mux_set(struct typec_mux_dev *mux,
struct tusb1046_priv *priv = typec_mux_get_drvdata(mux);
struct i2c_client *client = priv->client;
struct device *dev = &client->dev;
+ unsigned long req = state->mode;
int mode, val, ret = 0;
- if (state->mode >= TYPEC_STATE_MODAL &&
- state->alt->svid != USB_TYPEC_DP_SID)
- return -EINVAL;
+ if (req >= TYPEC_STATE_MODAL) {
+ /* USB and accessory modes: not the DP pin assignments */
+ if (!state->alt)
+ req = req == TYPEC_MODE_USB3 ? TYPEC_STATE_USB :
+ TYPEC_STATE_SAFE;
+ else if (state->alt->svid != USB_TYPEC_DP_SID)
+ return -EINVAL;
+ }
dev_dbg(dev, "mux mode requested: %lu\n", state->mode);
@@ -60,7 +66,7 @@ static int tusb1046_mux_set(struct typec_mux_dev *mux,
goto out_unlock;
}
- switch (state->mode) {
+ switch (req) {
case TYPEC_STATE_USB:
mode = TUSB1046_CTLSEL_USB3;
break;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 16/19] usb: renesas_usbhs: link URBs with interrupts disabled
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (14 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 15/19] usb: typec: mux: tusb1046: " Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 17/19] usb: renesas_usbhs: unlink the URB if it is not queued Greg Kroah-Hartman
` (2 subsequent siblings)
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik, stable
From: Josef Bacik <josef@toxicpanda.com>
usb_hcd_link_urb_to_ep() and usb_hcd_unlink_urb_from_ep() are to be
called under the lock of the host controller driver, with interrupts
disabled. They take hcd_urb_list_lock with a plain spin_lock().
usbhsh_urb_enqueue() calls the first one without any lock, with
interrupts enabled if the URB was submitted from process context.
usbhsh_queue_done() calls the second one, mostly from the interrupt
handler of the controller.
The host mode is used on a uniprocessor board, where the spinlock is
nothing. There the interrupt for a finished URB can come in the middle of
the list_add_tail() for the next URB of the same endpoint, and its
list_del_init() leaves the list of the endpoint corrupted. On an SMP
kernel the CPU would wait for itself instead.
usbhsh_queue_done() is also called from usbhsh_urb_dequeue() with
interrupts enabled.
Take the lock of the driver around both calls.
Fixes: 034d7c13a79c ("usb: gadget: renesas_usbhs: add mod_host support")
Cc: stable <stable@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/renesas_usbhs/mod_host.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index f7ef3a9f82a4..0652525d06fe 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -633,6 +633,7 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
struct urb *urb = ureq->urb;
struct device *dev = usbhs_priv_to_dev(priv);
+ unsigned long flags;
int status = 0;
dev_dbg(dev, "%s\n", __func__);
@@ -652,7 +653,10 @@ static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep));
+ usbhs_lock(priv, flags);
usb_hcd_unlink_urb_from_ep(hcd, urb);
+ usbhs_unlock(priv, flags);
+
usb_hcd_giveback_urb(hcd, urb, status);
}
@@ -955,6 +959,7 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
struct usb_host_endpoint *ep = urb->ep;
struct usbhsh_device *new_udev = NULL;
int is_dir_in = usb_pipein(urb->pipe);
+ unsigned long flags;
int ret;
dev_dbg(dev, "%s (%s)\n", __func__, is_dir_in ? "in" : "out");
@@ -965,7 +970,9 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
goto usbhsh_urb_enqueue_error_not_linked;
}
+ usbhs_lock(priv, flags);
ret = usb_hcd_link_urb_to_ep(hcd, urb);
+ usbhs_unlock(priv, flags);
if (ret) {
dev_err(dev, "urb link failed\n");
goto usbhsh_urb_enqueue_error_not_linked;
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 17/19] usb: renesas_usbhs: unlink the URB if it is not queued
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (15 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 16/19] usb: renesas_usbhs: link URBs with interrupts disabled Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 18/19] usb: isp1760: wait for the TT clear in endpoint_disable Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 19/19] usb: dwc2: " Greg Kroah-Hartman
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik, stable
From: Josef Bacik <josef@toxicpanda.com>
If usbhsh_urb_enqueue() fails after it has linked the URB to the
endpoint, it returns without unlinking it. usb_hcd_submit_urb() then
reinitializes urb->urb_list, and the owner may free the URB, while the
list of the endpoint still points to it. The next walk of that list, at
the latest when the endpoint is flushed, uses freed memory or does not
end.
No memory shortage is needed for that: each isochronous URB is refused
with -EIO, after it has been linked.
If the failure comes after the pipe was attached, that is not undone
either, and the use count of the pipe never gets back to zero.
Fixes: 034d7c13a79c ("usb: gadget: renesas_usbhs: add mod_host support")
Cc: stable <stable@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/renesas_usbhs/mod_host.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 0652525d06fe..bdec4eaf3402 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -987,7 +987,7 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
if (!new_udev) {
ret = -EIO;
dev_err(dev, "device attach failed\n");
- goto usbhsh_urb_enqueue_error_not_linked;
+ goto usbhsh_urb_enqueue_error_unlink;
}
}
@@ -1020,14 +1020,22 @@ static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
ret = usbhsh_dcp_queue_push(hcd, urb, mem_flags);
else
ret = usbhsh_queue_push(hcd, urb, mem_flags);
+ if (ret < 0) {
+ usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(ep));
+ goto usbhsh_urb_enqueue_error_unlink;
+ }
- return ret;
+ return 0;
usbhsh_urb_enqueue_error_free_endpoint:
usbhsh_endpoint_detach(hpriv, ep);
usbhsh_urb_enqueue_error_free_device:
if (new_udev)
usbhsh_device_detach(hpriv, new_udev);
+usbhsh_urb_enqueue_error_unlink:
+ usbhs_lock(priv, flags);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ usbhs_unlock(priv, flags);
usbhsh_urb_enqueue_error_not_linked:
dev_dbg(dev, "%s error\n", __func__);
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 18/19] usb: isp1760: wait for the TT clear in endpoint_disable
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (16 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 17/19] usb: renesas_usbhs: unlink the URB if it is not queued Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
2026-10-27 15:21 ` Rui Miguel Silva
2026-10-27 14:57 ` [PATCH 19/19] usb: dwc2: " Greg Kroah-Hartman
18 siblings, 1 reply; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Rui Miguel Silva
From: Josef Bacik <josef@toxicpanda.com>
The driver sets qh->tt_buffer_dirty and calls usb_hub_clear_tt_buffer()
when a transfer to a full or low speed device fails or is unlinked while
it is running. The hub driver stores urb->ep in its list and
hub_tt_work() hands it back to isp1760_clear_tt_buffer_complete() after
the CLEAR_TT_BUFFER request to the hub.
Nothing keeps the endpoint until then. isp1760_endpoint_disable() frees
the QH at once. If the device is unplugged, the unlink of a running URB
queues such a request, and usb_disconnect() goes on to free the
usb_device and its configurations, which hold the struct
usb_host_endpoint. The callback then reads ep->hcpriv from freed memory
and, if it is not NULL, writes to whatever it points to. A change of the
configuration or altsetting only frees the QH, under a callback which
has loaded ep->hcpriv without the lock.
The HCD has to wait, like ehci_endpoint_disable(),
xhci_endpoint_disable() and fotg210_endpoint_disable() do. hub_tt_work()
calls the callback for every entry, also if the request failed, and
hub_quiesce() flushes the work, so the wait ends.
Fixes: 74ad60292bf0 ("usb/isp1760: Clear TT buffer on interrupted low & full speed transfers")
Cc: stable <stable@kernel.org>
Cc: Rui Miguel Silva <rui.silva@linaro.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/isp1760/isp1760-hcd.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index 6c52c6f59d69..3cbf5fe3ed34 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -2071,12 +2071,23 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
struct isp1760_qh *qh, *qh_iter;
int i;
+rescan:
spin_lock_irqsave(&priv->lock, spinflags);
qh = ep->hcpriv;
if (!qh)
goto out;
+ /*
+ * The hub driver still has a pointer to the endpoint. Wait until it
+ * has called isp1760_clear_tt_buffer_complete().
+ */
+ if (qh->tt_buffer_dirty) {
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+ schedule_timeout_uninterruptible(1);
+ goto rescan;
+ }
+
WARN_ON(!list_empty(&qh->qtd_list));
for (i = 0; i < QH_END; i++)
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 19/19] usb: dwc2: wait for the TT clear in endpoint_disable
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
` (17 preceding siblings ...)
2026-10-27 14:57 ` [PATCH 18/19] usb: isp1760: wait for the TT clear in endpoint_disable Greg Kroah-Hartman
@ 2026-10-27 14:57 ` Greg Kroah-Hartman
18 siblings, 0 replies; 22+ messages in thread
From: Greg Kroah-Hartman @ 2026-10-27 14:57 UTC (permalink / raw)
To: linux-usb
Cc: Alan Stern, linux-kernel, Greg Kroah-Hartman, Josef Bacik,
stable, Minas Harutyunyan
From: Josef Bacik <josef@toxicpanda.com>
When a split transaction fails, dwc2_hc_handle_tt_clear() sets
qh->tt_buffer_dirty and calls usb_hub_clear_tt_buffer(). The hub driver
stores urb->ep in its list and hub_tt_work() hands it back to
_dwc2_hcd_clear_tt_buffer_complete() after the CLEAR_TT_BUFFER request to
the hub, which can take up to a second if the hub does not answer.
Nothing keeps the endpoint until then. dwc2_hcd_endpoint_disable() only
waits for the QTD list to drain and frees the QH. If the full or low
speed device is unplugged, usb_disconnect() goes on to free the
usb_device and its configurations, which hold the struct
usb_host_endpoint. The callback then reads ep->hcpriv from freed memory
and, if it is not NULL, clears a bit in whatever it points to. A change
of the configuration or altsetting only frees the QH, under a callback
which has loaded ep->hcpriv without the lock.
A device which contains a hub can provoke this: fail a transaction of
the downstream device, report its disconnection and hold back the answer
to CLEAR_TT_BUFFER.
The HCD has to wait, like ehci_endpoint_disable(),
xhci_endpoint_disable() and fotg210_endpoint_disable() do. hub_tt_work()
calls the callback for every entry, also if the request failed, and
hub_quiesce() flushes the work, so the wait ends.
Fixes: 7359d482eb4d ("staging: HCD files for the DWC2 driver")
Cc: stable <stable@kernel.org>
Cc: Minas Harutyunyan <hminas@synopsys.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/usb/dwc2/hcd.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index d5275cd658d4..a6ee55c494f8 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1993,6 +1993,21 @@ static int dwc2_hcd_endpoint_disable(struct dwc2_hsotg *hsotg,
}
}
+ /*
+ * The hub driver still has a pointer to the endpoint. Wait until it
+ * has called _dwc2_hcd_clear_tt_buffer_complete().
+ */
+ while (qh->tt_buffer_dirty) {
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&hsotg->lock, flags);
+ qh = ep->hcpriv;
+ if (!qh) {
+ rc = -EINVAL;
+ goto err;
+ }
+ }
+
dwc2_hcd_qh_unlink(hsotg, qh);
/* Free each QTD in the QH's QTD list */
--
2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 18/19] usb: isp1760: wait for the TT clear in endpoint_disable
2026-10-27 14:57 ` [PATCH 18/19] usb: isp1760: wait for the TT clear in endpoint_disable Greg Kroah-Hartman
@ 2026-10-27 15:21 ` Rui Miguel Silva
0 siblings, 0 replies; 22+ messages in thread
From: Rui Miguel Silva @ 2026-10-27 15:21 UTC (permalink / raw)
To: Greg Kroah-Hartman, linux-usb
Cc: Alan Stern, linux-kernel, Josef Bacik, stable, Rui Miguel Silva
Hi,,
On Tue Oct 27, 2026 at 2:57 PM WET, Greg Kroah-Hartman wrote:
> From: Josef Bacik <josef@toxicpanda.com>
>
> The driver sets qh->tt_buffer_dirty and calls usb_hub_clear_tt_buffer()
> when a transfer to a full or low speed device fails or is unlinked while
> it is running. The hub driver stores urb->ep in its list and
> hub_tt_work() hands it back to isp1760_clear_tt_buffer_complete() after
> the CLEAR_TT_BUFFER request to the hub.
>
> Nothing keeps the endpoint until then. isp1760_endpoint_disable() frees
> the QH at once. If the device is unplugged, the unlink of a running URB
> queues such a request, and usb_disconnect() goes on to free the
> usb_device and its configurations, which hold the struct
> usb_host_endpoint. The callback then reads ep->hcpriv from freed memory
> and, if it is not NULL, writes to whatever it points to. A change of the
> configuration or altsetting only frees the QH, under a callback which
> has loaded ep->hcpriv without the lock.
>
> The HCD has to wait, like ehci_endpoint_disable(),
> xhci_endpoint_disable() and fotg210_endpoint_disable() do. hub_tt_work()
> calls the callback for every entry, also if the request failed, and
> hub_quiesce() flushes the work, so the wait ends.
>
> Fixes: 74ad60292bf0 ("usb/isp1760: Clear TT buffer on interrupted low & full speed transfers")
> Cc: stable <stable@kernel.org>
> Cc: Rui Miguel Silva <rui.silva@linaro.org>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Yup, LGTM, thanks.
Acked-by: Rui Miguel Silva <rui.silva@linaro.org>
Cheers,
Rui
> ---
> drivers/usb/isp1760/isp1760-hcd.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
> index 6c52c6f59d69..3cbf5fe3ed34 100644
> --- a/drivers/usb/isp1760/isp1760-hcd.c
> +++ b/drivers/usb/isp1760/isp1760-hcd.c
> @@ -2071,12 +2071,23 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
> struct isp1760_qh *qh, *qh_iter;
> int i;
>
> +rescan:
> spin_lock_irqsave(&priv->lock, spinflags);
>
> qh = ep->hcpriv;
> if (!qh)
> goto out;
>
> + /*
> + * The hub driver still has a pointer to the endpoint. Wait until it
> + * has called isp1760_clear_tt_buffer_complete().
> + */
> + if (qh->tt_buffer_dirty) {
> + spin_unlock_irqrestore(&priv->lock, spinflags);
> + schedule_timeout_uninterruptible(1);
> + goto rescan;
> + }
> +
> WARN_ON(!list_empty(&qh->qtd_list));
>
> for (i = 0; i < QH_END; i++)
>
> --
> 2.56.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 06/19] usb: gadget: snps_udc_plat: lock out the interrupt
2026-10-27 14:57 ` [PATCH 06/19] usb: gadget: snps_udc_plat: lock out the interrupt Greg Kroah-Hartman
@ 2026-10-27 18:17 ` Herve Codina
0 siblings, 0 replies; 22+ messages in thread
From: Herve Codina @ 2026-10-27 18:17 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: linux-usb, Alan Stern, linux-kernel, Josef Bacik, stable,
Neill Kapron, Ijae Kim
Hi Greg,
On Tue, 27 Oct 2026 15:57:46 +0100
Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> From: Josef Bacik <josef@toxicpanda.com>
>
> stop_udc() is called from a work item, when the extcon device reports
> that the cable has been unplugged. It takes udc->lock with interrupts
> enabled. udc_irq(), the hard interrupt handler of the controller, takes
> the same lock. If the controller raises an interrupt on the CPU which
> is in stop_udc(), and it is likely to have something to report when the
> cable is pulled, the handler spins on a lock which the interrupted work
> holds: the CPU is stuck.
>
> Disable interrupts while the lock is held. They are enabled again for
> the ->disconnect() callback of the gadget driver, for which the lock is
> dropped as before.
>
> Fixes: 1b9f35adb0ff ("usb: gadget: udc: Add Synopsys UDC Platform driver")
> Cc: stable <stable@kernel.org>
> Cc: Neill Kapron <nkapron@google.com>
> Cc: Ijae Kim <ae878000@gmail.com>
> Cc: Herve Codina <herve.codina@bootlin.com>
> Signed-off-by: Josef Bacik <josef@toxicpanda.com>
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
> drivers/usb/gadget/udc/snps_udc_plat.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
LGTM.
Reviewed-by: Herve Codina <herve.codina@bootlin.com>
Best regards,
Hervé
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-10-27 18:17 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27 14:57 [PATCH 00/19] USB: a bunch of llm-found fixes Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 01/19] usb: core: sysfs: take the port lock to read the BOS Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 02/19] USB: core: signal the end of an sg request last Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 03/19] usbip: vudc: free the unlink item if the send fails Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 04/19] usb: host: oxu210hp-hcd: fix oxu_qh_alloc() deadlock Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 05/19] usb: cdns3: switch the role unlocked on resume Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 06/19] usb: gadget: snps_udc_plat: lock out the interrupt Greg Kroah-Hartman
2026-10-27 18:17 ` Herve Codina
2026-10-27 14:57 ` [PATCH 07/19] usb: gadget: bdc: the resume callback is optional Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 08/19] usb: gadget: uvc: fix oops on a premature STREAMON Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 09/19] usb: gadget: f_fs: let the request keep its fence Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 10/19] usb: host: ehci-orion: fix the probe without DT Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 11/19] usb: typec: tcpm: don't wait for the VDM discovery work Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 12/19] usb: typec: tcpci: don't act on unread alerts Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 13/19] usb: typec: anx7411: zero on-stack alt mode descriptors Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 14/19] usb: typec: mux: it5205: fix oops on accessory modes Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 15/19] usb: typec: mux: tusb1046: " Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 16/19] usb: renesas_usbhs: link URBs with interrupts disabled Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 17/19] usb: renesas_usbhs: unlink the URB if it is not queued Greg Kroah-Hartman
2026-10-27 14:57 ` [PATCH 18/19] usb: isp1760: wait for the TT clear in endpoint_disable Greg Kroah-Hartman
2026-10-27 15:21 ` Rui Miguel Silva
2026-10-27 14:57 ` [PATCH 19/19] usb: dwc2: " Greg Kroah-Hartman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®