Re: [PATCH v3 2/2] usb: core: add a pre_probe callback for device drivers that may defer
From: Alan Stern
Date: Thu Nov 05 2026 - 15:43:07 EST
On Tue, Nov 03, 2026 at 12:39:52PM +0700, James Clark wrote:
> On Tue, 03 Nov 2026 03:51:41 +0000, sashiko-bot@xxxxxxxxxx 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.
This is all starting to sound very complicated and fragile. Isn't there
any way to simplify it?
Alan Stern