diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 8a630f9bd880..0cd2eadd0816 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -606,17 +606,6 @@ static ssize_t uevent_store(struct device_driver *drv, const char *buf,
}
static DRIVER_ATTR_WO(uevent);
-static void driver_attach_async(void *_drv, async_cookie_t cookie)
-{
- struct device_driver *drv = _drv;
- int ret;
-
- ret = driver_attach(drv);
-
- pr_debug("bus: '%s': driver %s async attach completed: %d\n",
- drv->bus->name, drv->name, ret);
-}
-
/**
* bus_add_driver - Add a driver to the bus.
* @drv: driver.@@ -649,15 +638,9 @@ int bus_add_driver(struct device_driver *drv)
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {
- if (driver_allows_async_probing(drv)) {
- pr_debug("bus: '%s': probing driver %s asynchronously\n",
- drv->bus->name, drv->name);
- async_schedule(driver_attach_async, drv);
- } else {
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
- }
+ error = driver_attach(drv);
+ if (error)
+ goto out_unregister;
}
module_add_driver(drv->owner, drv);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index ed19cf0d6f9a..2fdfe45bb6ea 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -808,6 +808,7 @@ static int __device_attach(struct device *dev, bool allow_async)
ret = 1;
else {
dev->driver = NULL;
+ dev_set_drvdata(dev, NULL);
ret = 0;
}
} else {@@ -925,6 +926,32 @@ int device_driver_attach(struct device_driver *drv, struct device *dev)
return ret;
}
+static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
+{
+ struct device *dev = _dev;
+ struct device_driver *drv;
+
+ __device_driver_lock(dev, dev->parent);
+
+ /*
+ * If someone attempted to bind a driver either successfully or
+ * unsuccessfully before we got here we should just skip the driver
+ * probe call.
+ */
+ drv = dev_get_drv_async(dev);
+ if (drv && !dev->driver)
+ driver_probe_device(drv, dev);
+
+ /* We made our attempt at an async_probe, clear the flag */
+ dev->async_probe = false;
+
+ __device_driver_unlock(dev, dev->parent);
+
+ put_device(dev);
+
+ dev_dbg(dev, "async probe completed\n");
+}
+
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;@@ -952,6 +979,25 @@ static int __driver_attach(struct device *dev, void *data)
return ret;
} /* ret > 0 means positive match */
+ if (driver_allows_async_probing(drv)) {
+ /*
+ * Instead of probing the device synchronously we will
+ * probe it asynchronously to allow for more parallelism.
+ *
+ * We only take the device lock here in order to guarantee
+ * that the dev->driver and driver_data fields are protected
+ */
+ dev_dbg(dev, "scheduling asynchronous probe\n");
+ device_lock(dev);
+ if (!dev->driver) {
+ get_device(dev);
+ dev_set_drv_async(dev, drv);
+ async_schedule(__driver_attach_async_helper, dev);
+ }
+ device_unlock(dev);
+ return 0;
+ }
+
device_driver_attach(drv, dev);
return 0;@@ -1049,6 +1095,12 @@ void device_release_driver_internal(struct device *dev,
{
__device_driver_lock(dev, parent);
+ /*
+ * We shouldn't need to add a check for any pending async_probe here
+ * because the only caller that will pass us a driver, driver_detach,
+ * should have been called after the driver was removed from the bus
+ * and will call async_synchronize_full before we get to this point.
+ */
if (!drv || drv == dev->driver)
__device_release_driver(dev, parent);
diff --git a/include/linux/device.h b/include/linux/device.h
index fc7091d436c2..4d9a39769081 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -910,7 +910,9 @@ struct dev_links_info {
* variants, which GPIO pins act in what additional roles, and so
* on. This shrinks the "Board Support Packages" (BSPs) and
* minimizes board-specific #ifdefs in drivers.
- * @driver_data: Private pointer for driver specific info.
+ * @driver_data: Private pointer for driver specific info if driver is
+ * non-NULL. Pointer to deferred driver to be attached if driver
+ * is NULL.
* @links: Links to suppliers and consumers of this device.
* @power: For device power management.
* See Documentation/driver-api/pm/devices.rst for details.@@ -1116,9 +1118,31 @@ static inline void *dev_get_drvdata(const struct device *dev)
static inline void dev_set_drvdata(struct device *dev, void *data)
{
+ /*
+ * clear async_probe to prevent us from attempting to read driver_data
+ * as a driver. We can reset this to true for the one case where we are
+ * using this to record an actual driver.
+ */
+ dev->async_probe = false;
dev->driver_data = data;
}
+static inline struct device_driver *dev_get_drv_async(const struct device *dev)
+{
+ return dev->async_probe ? dev->driver_data : NULL;
+}
+
+static inline void dev_set_drv_async(struct device *dev,
+ struct device_driver *drv)
+{
+ /*
+ * Set async_probe to true indicating we are waiting for this data to be
+ * loaded as a potential driver.
+ */
+ dev->driver_data = drv;
+ dev->async_probe = true;
+}
+
static inline struct pm_subsys_data *dev_to_psd(struct device *dev)
{
return dev ? dev->power.subsys_data : NULL;