Re: [PATCH] usb: misc: onboard_usb_dev: don't defer the USB device probe
From: Arnd Bergmann
Date: Fri Oct 30 2026 - 08:31:01 EST
On Fri, Oct 30, 2026, at 12:47, James Clark wrote:
> 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.
>
> The underlying problem is in onboard_usb_dev. 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. This is not specific to
> shared GPIOs: it happens whenever the platform probe is deferred, and
> briefly whenever onboard_usb_dev is a module.
It sounds like the underlying problem should be fixed elsewhere,
possibly by preventing the deferred probe to be retried each time.
> +/*
> + * Attach a USB device that was bound by onboard_dev_usbdev_probe()
> while its
> + * platform device was still unbound. The device lock serialises this
> with
> + * the probe and with an unbind. This walks the USB bus, not the
> driver's
> + * device list: unbinding a device holds its lock while it waits for
> all
> + * references to its node on the driver's list to be dropped, so an
> iterator
> + * over that list that takes the device lock would deadlock with it.
> + */
> +static int onboard_dev_attach_usbdev(struct usb_device *udev, void
> *data)
> +{
This conceptually looks like a layering violation: no driver should
have to look through the list of devices like this.
> + struct device *dev = &udev->dev;
> + struct onboard_dev *onboard_dev;
> + int err;
> +
> + /* Unlocked pre-check, so as not to wait for every USB device's lock.
> */
> + if (READ_ONCE(dev->driver) != &onboard_dev_usbdev_driver.driver)
> + return 0;
> +
> + device_lock(dev);
> +
> + if (dev->driver != &onboard_dev_usbdev_driver.driver ||
> + dev_get_drvdata(dev))
> + goto out;
> +
> + onboard_dev = _find_onboard_dev(dev);
> + if (IS_ERR_OR_NULL(onboard_dev))
> + goto out;
IS_ERR_OR_NULL() is almost always a bug, make sure you understan
the interface and what this can return. If the function can
actually return both error codes or NULL pointers, fix that
first.
Arnd