Linux USB
 help / color / mirror / Atom feed
* [PATCH v3 0/2] usb: core: fix failed device driver probes, add pre_probe
@ 2026-11-03  3:34 James Clark
  2026-11-03  3:34 ` [PATCH v3 1/2] usb: core: clean up before handing a device to the generic driver James Clark
  2026-11-03  3:34 ` [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer James Clark
  0 siblings, 2 replies; 6+ messages in thread
From: James Clark @ 2026-11-03  3:34 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Stern
  Cc: Johan Hovold, Bastien Nocera, Matthias Kaehlcke, Arnd Bergmann,
	Bartosz Golaszewski, Douglas Anderson, Robert Nelson, linux-usb,
	linux-kernel, linux-gpio, regressions

On a BeagleY-AI with BeagleBoard's device tree, kernels since commit
f918e3804498 ("arm64: enable HAVE_SHARED_GPIOS for all ARM64 platforms")
no longer boot to a usable system: the on-board USB hub is re-probed in
a loop. Patch 2 fixes this as Johan Hovold suggested, with a pre_probe
callback that lets onboard_usb_dev defer before the hub is configured.

Patch 1 fixes two pre-existing bugs found by a Sashiko review of v2.

Tested on the BeagleY-AI on top of v7.4-rc1: the board boots normally.
The -ENODEV fallback that patch 1 changes is not reached on this board.

I found this by booting commit 85b4bc79dde7 (net/main during the 7.4
merge window) on a BeagleY-AI with BeagleBoard's device tree. A coding
agent identified the problem, explained it to me and proposed an initial
fix, which was revised numerous times on the basis of comments on v1 and
v2, my feedback and local Sashiko reviews. I verified that the series
fixes the problem on top of v7.4-rc1.

v3:
- Add a patch to fix existing problems, as requested by Alan Stern.
- Patch 2: rebased on it, no other change.
v2: https://lore.kernel.org/all/20261030151919.3511106-1-jjc@jclark.com/
v1: https://lore.kernel.org/all/20261030114705.3159093-1-jjc@jclark.com/

#regzbot introduced: f918e3804498

James Clark (2):
  usb: core: clean up before handing a device to the generic driver
  usb: core: add a pre_probe callback for device drivers that may defer

 drivers/usb/core/driver.c          | 42 ++++++++++++++++++++++++++----
 drivers/usb/misc/onboard_usb_dev.c | 15 ++++++-----
 include/linux/usb.h                | 14 ++++++++++
 3 files changed, 59 insertions(+), 12 deletions(-)


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.56.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/2] usb: core: clean up before handing a device to the generic driver
  2026-11-03  3:34 [PATCH v3 0/2] usb: core: fix failed device driver probes, add pre_probe James Clark
@ 2026-11-03  3:34 ` James Clark
  2026-11-03  3:58   ` sashiko-bot
  2026-11-03  3:34 ` [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer James Clark
  1 sibling, 1 reply; 6+ messages in thread
From: James Clark @ 2026-11-03  3:34 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Stern
  Cc: Johan Hovold, Bastien Nocera, Matthias Kaehlcke, Arnd Bergmann,
	Bartosz Golaszewski, Douglas Anderson, Robert Nelson, linux-usb,
	linux-kernel, linux-gpio, regressions

When a USB device driver's probe returns -ENODEV, usb_probe_device()
hands the device to the generic driver. It does not first undo what it
did before calling the probe. This causes two problems.

First, if the driver sets generic_subclass, the generic probe has
already run. It configured the device and sent a USB_DEVICE_ADD
notification. The generic driver then does both again. Notifier users
such as the usbport LED trigger see two USB_DEVICE_ADDs with no
USB_DEVICE_REMOVE in between.

Second, if the driver does not set supports_autosuspend, a runtime PM
reference was taken before the probe. Nothing drops it, so the device
can never be runtime suspended.

Clean up before handing the device over, in the same way as
usb_unbind_device() does when a driver is unbound: first, for a driver
that sets generic_subclass, call usb_generic_driver_disconnect();
second, for a driver that does not set supports_autosuspend, drop the
runtime PM reference with usb_autosuspend_device().

This leaves one limitation. Until the generic driver binds, the device
has no driver. If it is runtime suspended in that time, it can't be
resumed. This is transient: the generic driver resumes the device when
it binds.

This change does not alter what happens when the probe fails with any
other error. The device stays configured, so its interfaces keep
working. It keeps the runtime PM reference, because a device without a
driver can't be resumed. Its USB_DEVICE_ADD stays unpaired, because
sending USB_DEVICE_REMOVE would mean unconfiguring the device.

Fixes: 88b7381a939d ("USB: Select better matching USB drivers when available")
Signed-off-by: James Clark <jjc@jclark.com>
---
 drivers/usb/core/driver.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index d3a61ebc6afb..9b8cd0e4219e 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -295,6 +295,10 @@ static int usb_probe_device(struct device *dev)
 		error = -EINVAL;
 	if (error == -ENODEV && udriver != &usb_generic_driver &&
 	    (udriver->id_table || udriver->match)) {
+		if (udriver->generic_subclass)
+			usb_generic_driver_disconnect(udev);
+		if (!udriver->supports_autosuspend)
+			usb_autosuspend_device(udev);
 		udev->use_generic_driver = 1;
 		return -EPROBE_DEFER;
 	}
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer
  2026-11-03  3:34 [PATCH v3 0/2] usb: core: fix failed device driver probes, add pre_probe James Clark
  2026-11-03  3:34 ` [PATCH v3 1/2] usb: core: clean up before handing a device to the generic driver James Clark
@ 2026-11-03  3:34 ` James Clark
  2026-11-03  3:51   ` sashiko-bot
  1 sibling, 1 reply; 6+ messages in thread
From: James Clark @ 2026-11-03  3:34 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Alan Stern
  Cc: Johan Hovold, Bastien Nocera, Matthias Kaehlcke, Arnd Bergmann,
	Bartosz Golaszewski, Douglas Anderson, Robert Nelson, linux-usb,
	linux-kernel, linux-gpio, regressions

On a BeagleY-AI with BeagleBoard's device tree, kernels since commit
f918e3804498 ("arm64: enable HAVE_SHARED_GPIOS for all ARM64 platforms")
no longer boot to a usable system. The log repeats "hub 1-1:1.0: USB hub
found" for the on-board TUSB8041 hub every few milliseconds, with no USB
disconnect in between. The flood of uevents stalls udev and the boot ends
in emergency mode. Reverting that commit makes the board boot normally
again.

Both peer hub nodes in this device tree carry the same reset-gpios, so
the GPIO is now a shared one, and with CONFIG_GPIO_SHARED_PROXY=m (the
default) the hub's platform probe is legitimately deferred until udev has
loaded that module. The bootloader leaves the hub running, so the USB
core enumerates it long before, and the USB device probe finds the
platform device without driver data.

At that point, onboard_dev_usbdev_probe() returns -EPROBE_DEFER. This is
what causes the problem. The driver has generic_subclass set, so
usb_probe_device() has already run the generic probe, which configured
the device and bound the interface drivers. Each binding is a
deferred-probe trigger, and the driver core re-triggers when a trigger
happened during a probe that then deferred. So the device is re-probed
straight away, reconfigured, its interfaces bound again, and so on for
as long as the platform device remains unbound. A probe must not defer
after it has registered child devices, and with generic_subclass the
driver's probe has no way to defer before that. This is not specific to
shared GPIOs: it happens whenever the platform probe is deferred, and
briefly whenever onboard_usb_dev is a module.

Add a pre_probe callback to struct usb_device_driver, called before the
generic probe, so that a driver that may have to defer can do so before
the device is configured. -EPROBE_DEFER from it ends the probe with
nothing done. Any other error is handled as if probe() had returned it,
once the generic probe has run, so the -ENODEV fallback to the generic
driver works as before. Make onboard_usb_dev's USB device probe a
pre_probe; it does not need the device to be configured.

A device whose driver defers from pre_probe stays unconfigured until the
probe succeeds, and for good if it never does, e.g. an onboard hub whose
platform driver fails to probe. Before, such a device worked while being
re-probed. On the BeagleY-AI the hub is unconfigured for the few seconds
until gpio-shared-proxy has loaded and the platform driver has probed,
which resets it anyway; the board then boots normally with that commit
in place.

Fixes: 8bc063641ceb ("usb: misc: Add onboard_usb_hub driver")
Suggested-by: Johan Hovold <johan@kernel.org>
Signed-off-by: James Clark <jjc@jclark.com>
---
 drivers/usb/core/driver.c          | 38 ++++++++++++++++++++++++++----
 drivers/usb/misc/onboard_usb_dev.c | 15 ++++++------
 include/linux/usb.h                | 14 +++++++++++
 3 files changed, 55 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9b8cd0e4219e..137b53beb236 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -249,24 +249,41 @@ static int usb_probe_device(struct device *dev)
 {
 	struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
 	struct usb_device *udev = to_usb_device(dev);
-	int error = 0;
+	int error = 0, pre_error = 0;
 
 	dev_dbg(dev, "%s\n", __func__);
 
 	/* TODO: Add real matching code */
 
+	/*
+	 * Give the driver a chance to defer before anything is done to the
+	 * device. The generic probe below binds the interface drivers, and
+	 * each binding triggers deferred probing, so a probe that deferred
+	 * after it would be retried straight away and the device re-probed
+	 * in a loop.
+	 *
+	 * Any other error from pre_probe() is not acted on until the generic
+	 * probe has run, so that the -ENODEV fallback below works as it does
+	 * for probe().
+	 */
+	if (udriver->pre_probe) {
+		pre_error = udriver->pre_probe(udev);
+		if (pre_error == -EPROBE_DEFER)
+			return pre_error;
+	}
+
 	/* The device should always appear to be in use
 	 * unless the driver supports autosuspend.
 	 */
 	if (!udriver->supports_autosuspend)
 		error = usb_autoresume_device(udev);
 	if (error)
-		return error;
+		goto err_pre_probe;
 
 	if (udriver->generic_subclass)
 		error = usb_generic_driver_probe(udev);
 	if (error)
-		return error;
+		goto err_pre_probe;
 
 	/* Probe the USB device with the driver in hand, but only
 	 * defer to a generic driver in case the current USB
@@ -289,7 +306,9 @@ static int usb_probe_device(struct device *dev)
 	 * specialised device drivers prior to setting the
 	 * use_generic_driver bit.
 	 */
-	if (udriver->probe)
+	if (pre_error)
+		error = pre_error;
+	else if (udriver->probe)
 		error = udriver->probe(udev);
 	else if (!udriver->generic_subclass)
 		error = -EINVAL;
@@ -300,8 +319,17 @@ static int usb_probe_device(struct device *dev)
 		if (!udriver->supports_autosuspend)
 			usb_autosuspend_device(udev);
 		udev->use_generic_driver = 1;
-		return -EPROBE_DEFER;
+		error = -EPROBE_DEFER;
 	}
+	if (error)
+		goto err_pre_probe;
+
+	return 0;
+
+err_pre_probe:
+	/* The driver core doesn't call disconnect() for a failed probe. */
+	if (udriver->pre_probe && !pre_error && udriver->disconnect)
+		udriver->disconnect(udev);
 	return error;
 }
 
diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c
index c60406240416..33653d8f76d7 100644
--- a/drivers/usb/misc/onboard_usb_dev.c
+++ b/drivers/usb/misc/onboard_usb_dev.c
@@ -606,13 +606,14 @@ static struct onboard_dev *_find_onboard_dev(struct device *dev)
 
 	/*
 	 * The presence of drvdata indicates that the platform driver finished
-	 * probing. This handles the case where (conceivably) we could be
-	 * running at the exact same time as the platform driver's probe. If
-	 * we detect the race we request probe deferral and we'll come back and
-	 * try again.
+	 * probing. Without it, e.g. because the platform driver's probe was
+	 * deferred or is running right now, request probe deferral and we'll
+	 * come back and try again. This is allowed because we run from
+	 * pre_probe(), before the USB core has configured the device.
 	 */
 	if (!onboard_dev)
-		return ERR_PTR(-EPROBE_DEFER);
+		return ERR_PTR(dev_err_probe(dev, -EPROBE_DEFER,
+					     "platform device not bound\n"));
 
 	return onboard_dev;
 }
@@ -623,7 +624,7 @@ static bool onboard_dev_usbdev_match(struct usb_device *udev)
 	return !!udev->dev.of_node;
 }
 
-static int onboard_dev_usbdev_probe(struct usb_device *udev)
+static int onboard_dev_usbdev_pre_probe(struct usb_device *udev)
 {
 	struct device *dev = &udev->dev;
 	struct onboard_dev *onboard_dev;
@@ -695,7 +696,7 @@ MODULE_DEVICE_TABLE(usb, onboard_dev_id_table);
 static struct usb_device_driver onboard_dev_usbdev_driver = {
 	.name = "onboard-usb-dev",
 	.match = onboard_dev_usbdev_match,
-	.probe = onboard_dev_usbdev_probe,
+	.pre_probe = onboard_dev_usbdev_pre_probe,
 	.disconnect = onboard_dev_usbdev_disconnect,
 	.generic_subclass = 1,
 	.supports_autosuspend =	1,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 825f03b29067..6e07d485d49a 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1280,6 +1280,19 @@ struct usb_driver {
  * @name: The driver name should be unique among USB drivers,
  *	and should normally be the same as the module name.
  * @match: If set, used for better device/driver matching.
+ * @pre_probe: If set, called first, before the device is resumed or
+ *	configured (the generic USB driver's probe runs after it when
+ *	@generic_subclass is set, then @probe), so it must not access the
+ *	device. A driver that may have to defer probing must do so from here
+ *	by returning -EPROBE_DEFER, not from @probe: by then the generic probe
+ *	has bound the interface drivers, and each binding triggers deferred
+ *	probing, so the device would be re-probed in a loop. Returning
+ *	-EPROBE_DEFER ends the probe with the device left as it was, so the
+ *	device stays unconfigured until the probe succeeds. Any other error is
+ *	handled as if @probe had returned it, once the generic probe has run,
+ *	and @probe is then not called. If @pre_probe succeeds and a later step
+ *	of the probe fails, @disconnect is called to undo it, so a driver with
+ *	both callbacks must make @disconnect cope with a failed @probe.
  * @probe: Called to see if the driver is willing to manage a particular
  *	device.  If it is, probe returns zero and uses dev_set_drvdata()
  *	to associate driver-specific data with the device.  If unwilling
@@ -1309,6 +1322,7 @@ struct usb_device_driver {
 	const char *name;
 
 	bool (*match) (struct usb_device *udev);
+	int (*pre_probe) (struct usb_device *udev);
 	int (*probe) (struct usb_device *udev);
 	void (*disconnect) (struct usb_device *udev);
 
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer
  2026-11-03  3:34 ` [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer James Clark
@ 2026-11-03  3:51   ` sashiko-bot
  2026-11-03  5:39     ` James Clark
  0 siblings, 1 reply; 6+ messages in thread
From: sashiko-bot @ 2026-11-03  3:51 UTC (permalink / raw)
  To: James Clark; +Cc: linux-usb, linux-gpio

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] usb: core: broken error handling and missing cleanup in usb_probe_device()
--

commit cc1df1b83e846aac43292fba9d36f8933ba3b140
Author: James Clark <jjc@jclark.com>

usb: core: add a pre_probe callback for device drivers that may defer

This commit adds a pre_probe callback to struct usb_device_driver so that
USB device drivers with generic_subclass set can defer probing before
usb_generic_driver_probe() configures the device and binds interface
drivers. It also converts onboard_dev_usbdev_driver to use pre_probe to
avoid a deferred-probe loop on platforms such as BeagleY-AI.

> diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
> index 9b8cd0e4219e6..137b53beb236a 100644
> --- a/drivers/usb/core/driver.c
> +++ b/drivers/usb/core/driver.c
> @@ -249,24 +249,41 @@ static int usb_probe_device(struct device *dev)
>  {
>  	struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
>  	struct usb_device *udev = to_usb_device(dev);
> -	int error = 0;
> +	int error = 0, pre_error = 0;
>  
>  	dev_dbg(dev, "%s\n", __func__);
>  
>  	/* TODO: Add real matching code */
>  
> +	/*
> +	 * Give the driver a chance to defer before anything is done to the
> +	 * device. The generic probe below binds the interface drivers, and
> +	 * each binding triggers deferred probing, so a probe that deferred
> +	 * after it would be retried straight away and the device re-probed
> +	 * in a loop.
> +	 *
> +	 * Any other error from pre_probe() is not acted on until the generic
> +	 * probe has run, so that the -ENODEV fallback below works as it does
> +	 * for probe().
> +	 */
> +	if (udriver->pre_probe) {
> +		pre_error = udriver->pre_probe(udev);
> +		if (pre_error == -EPROBE_DEFER)
> +			return pre_error;
> +	}
> +
>  	/* The device should always appear to be in use
>  	 * unless the driver supports autosuspend.
>  	 */
>  	if (!udriver->supports_autosuspend)
>  		error = usb_autoresume_device(udev);
>  	if (error)
> -		return error;
> +		goto err_pre_probe;
>  
>  	if (udriver->generic_subclass)
>  		error = usb_generic_driver_probe(udev);
>  	if (error)
> -		goto err_pre_probe;
> +		goto err_pre_probe;
>  
>  	/* Probe the USB device with the driver in hand, but only
>  	 * defer to a generic driver in case the current USB
> @@ -289,7 +306,9 @@ static int usb_probe_device(struct device *dev)
>  	 * specialised device drivers prior to setting the
>  	 * use_generic_driver bit.
>  	 */
> -	if (udriver->probe)
> +	if (pre_error)
> +		error = pre_error;
> +	else if (udriver->probe)
>  		error = udriver->probe(udev);
>  	else if (!udriver->generic_subclass)
>  		error = -EINVAL;
> @@ -300,8 +319,17 @@ static int usb_probe_device(struct device *dev)
>  		if (!udriver->supports_autosuspend)
>  			usb_autosuspend_device(udev);
>  		udev->use_generic_driver = 1;
> -		return -EPROBE_DEFER;
> +		error = -EPROBE_DEFER;
>  	}
> +	if (error)
> +		goto err_pre_probe;
> +
> +	return 0;
> +
> +err_pre_probe:
> +	/* The driver core doesn't call disconnect() for a failed probe. */
> +	if (udriver->pre_probe && !pre_error && udriver->disconnect)
> +		udriver->disconnect(udev);
>  	return error;
>  }

[Severity: Medium]
As reported by Sashiko on v2 [1], what happens in usb_probe_device() when
udriver->pre_probe(udev) returns an error other than -EPROBE_DEFER, or when
udriver->probe(udev) fails after usb_generic_driver_probe(udev) has run?

First, when udriver->pre_probe(udev) returns -ENODEV (for example from
onboard_dev_usbdev_pre_probe() -> _find_onboard_dev()), usb_probe_device()
still calls usb_autoresume_device(udev) and usb_generic_driver_probe(udev)
to configure the device, bind all child interface drivers (triggering
deferred probing), and emit USB_DEVICE_ADD, only to immediately tear them
all down a few lines later in the error == -ENODEV block via
usb_generic_driver_disconnect(udev) and usb_autosuspend_device(udev).

Second, if udriver->pre_probe(udev) or udriver->probe(udev) fails with any
other error (such as -EINVAL or -ENOMEM), usb_probe_device() jumps to
err_pre_probe, which only calls udriver->disconnect(udev) and does not call
usb_generic_driver_disconnect(udev) or usb_autosuspend_device(udev) to
unwind the generic probe and runtime PM state. Because really_probe()
clears udev->dev.driver to NULL when usb_probe_device() fails:

- When udev is later unplugged, usb_disconnect() -> device_del() skips
  usb_unbind_device() because udev->dev.driver is NULL, so
  usb_notify_remove_device() (USB_DEVICE_REMOVE) is never sent. Subscribers
  such as usbport_trig_notify() in drivers/usb/core/ledtrig-usbport.c never
  remove the port entries added on USB_DEVICE_ADD, leaking struct
  usbport_trig_port objects and sysfs attributes with a dangling port->hub
  pointer to the freed struct usb_device, and usbdev_notify() in
  drivers/usb/core/devio.c never runs usbdev_remove().

- For drivers with supports_autosuspend = 1 (such as
  onboard_dev_usbdev_driver), usb_probe_device() does not take a runtime PM
  reference, and hub_configure() enables autosuspend on hub devices; once
  the device suspends, usb_resume_device() fails with -ENOTCONN because
  udev->dev.driver is NULL.

Third, if udriver->pre_probe(udev) succeeds and udriver->probe(udev) then
returns -ENODEV, the error == -ENODEV block runs
usb_generic_driver_disconnect(udev) and usb_autosuspend_device(udev) before
err_pre_probe calls udriver->disconnect(udev), reversing the teardown order
used in usb_unbind_device():

drivers/usb/core/driver.c:usb_unbind_device() {
	if (udriver->disconnect)
		udriver->disconnect(udev);
	if (udriver->generic_subclass)
		usb_generic_driver_disconnect(udev);
	if (!udriver->supports_autosuspend)
		usb_autosuspend_device(udev);
}

Could usb_probe_device() handle errors from udriver->pre_probe() before
resuming or configuring the device, and unwind usb_generic_driver_probe()
and usb_autoresume_device() in reverse order on probe failure?

[1] https://lore.kernel.org/r/sashiko-outbox-191184@kernel.org

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793669297.git.jjc@jclark.com?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] usb: core: clean up before handing a device to the generic driver
  2026-11-03  3:34 ` [PATCH v3 1/2] usb: core: clean up before handing a device to the generic driver James Clark
@ 2026-11-03  3:58   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-11-03  3:58 UTC (permalink / raw)
  To: James Clark; +Cc: linux-usb

> When a USB device driver's probe returns -ENODEV, usb_probe_device()
> hands the device to the generic driver. It does not first undo what it
> did before calling the probe. This causes two problems.
> 
> First, if the driver sets generic_subclass, the generic probe has
> already run. It configured the device and sent a USB_DEVICE_ADD
> notification. The generic driver then does both again. Notifier users
> such as the usbport LED trigger see two USB_DEVICE_ADDs with no
> USB_DEVICE_REMOVE in between.
> 
> Second, if the driver does not set supports_autosuspend, a runtime PM
> reference was taken before the probe. Nothing drops it, so the device
> can never be runtime suspended.
> 
> Clean up before handing the device over, in the same way as
> [ ... ]
> driver can't be resumed. Its USB_DEVICE_ADD stays unpaired, because
> sending USB_DEVICE_REMOVE would mean unconfiguring the device.
> 
> Fixes: 88b7381a939d ("USB: Select better matching USB drivers when available")
> Signed-off-by: James Clark <jjc@jclark.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793669297.git.jjc@jclark.com?part=1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer
  2026-11-03  3:51   ` sashiko-bot
@ 2026-11-03  5:39     ` James Clark
  0 siblings, 0 replies; 6+ messages in thread
From: James Clark @ 2026-11-03  5:39 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: Greg Kroah-Hartman, Alan Stern, Johan Hovold, Bastien Nocera,
	Matthias Kaehlcke, Arnd Bergmann, Bartosz Golaszewski,
	Douglas Anderson, Robert Nelson, linux-usb, linux-kernel,
	linux-gpio, regressions

On Tue, 03 Nov 2026 03:51:41 +0000, sashiko-bot@kernel.org wrote:
> First, when udriver->pre_probe(udev) returns -ENODEV (for example from
> onboard_dev_usbdev_pre_probe() -> _find_onboard_dev()), usb_probe_device()
> still calls usb_autoresume_device(udev) and usb_generic_driver_probe(udev)
> to configure the device, bind all child interface drivers (triggering
> deferred probing), and emit USB_DEVICE_ADD, only to immediately tear them
> all down a few lines later in the error == -ENODEV block via
> usb_generic_driver_disconnect(udev) and usb_autosuspend_device(udev).

This is deliberate, and documented in the @pre_probe kerneldoc. An
error from pre_probe() other than -EPROBE_DEFER is handled as if
probe() had returned it, after the generic probe has run. This keeps
the -ENODEV fallback to the generic driver working as it does for
probe(). For onboard_usb_dev nothing changes: its -ENODEV used to come
from probe().

> Second, if udriver->pre_probe(udev) or udriver->probe(udev) fails with any
> other error (such as -EINVAL or -ENOMEM), usb_probe_device() jumps to
> err_pre_probe, which only calls udriver->disconnect(udev) and does not call
> usb_generic_driver_disconnect(udev) or usb_autosuspend_device(udev) to
> unwind the generic probe and runtime PM state. Because really_probe()
> clears udev->dev.driver to NULL when usb_probe_device() fails:
>
> - When udev is later unplugged, usb_disconnect() -> device_del() skips
>   usb_unbind_device() because udev->dev.driver is NULL, so
>   usb_notify_remove_device() (USB_DEVICE_REMOVE) is never sent. Subscribers
>   such as usbport_trig_notify() in drivers/usb/core/ledtrig-usbport.c never
>   remove the port entries added on USB_DEVICE_ADD, leaking struct
>   usbport_trig_port objects and sysfs attributes with a dangling port->hub
>   pointer to the freed struct usb_device, and usbdev_notify() in
>   drivers/usb/core/devio.c never runs usbdev_remove().

This was raised in Sashiko's review of v2. I missed that it can be fixed
when the device is unplugged. I will attempt to fix this in v4. I think
it can be fixed by recording in struct usb_device that USB_DEVICE_ADD
has been sent, and having usb_disconnect() send USB_DEVICE_REMOVE for a
device that has no driver.

> - For drivers with supports_autosuspend = 1 (such as
>   onboard_dev_usbdev_driver), usb_probe_device() does not take a runtime PM
>   reference, and hub_configure() enables autosuspend on hub devices; once
>   the device suspends, usb_resume_device() fails with -ENOTCONN because
>   udev->dev.driver is NULL.

This is a pre-existing bug that is new in this review.
usb_suspend_device() does a generic suspend for a device without a
driver, but usb_resume_device() refuses to resume one. I don't know
why resume is refused. If a generic resume is safe there, that would
fix it.

> Third, if udriver->pre_probe(udev) succeeds and udriver->probe(udev) then
> returns -ENODEV, the error == -ENODEV block runs
> usb_generic_driver_disconnect(udev) and usb_autosuspend_device(udev) before
> err_pre_probe calls udriver->disconnect(udev), reversing the teardown order
> used in usb_unbind_device():

I will fix this in v4.

James

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-03  5:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-03  3:34 [PATCH v3 0/2] usb: core: fix failed device driver probes, add pre_probe James Clark
2026-11-03  3:34 ` [PATCH v3 1/2] usb: core: clean up before handing a device to the generic driver James Clark
2026-11-03  3:58   ` sashiko-bot
2026-11-03  3:34 ` [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer James Clark
2026-11-03  3:51   ` sashiko-bot
2026-11-03  5:39     ` James Clark

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox