]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/base/dd.c
Merge tag 'driver-core-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[mirror_ubuntu-artful-kernel.git] / drivers / base / dd.c
index d76cd97a98b6badff85740180dfec97c8966754a..a8b258e5407bae02c48b03b39941ee238c22a2e3 100644 (file)
@@ -53,6 +53,19 @@ static LIST_HEAD(deferred_probe_pending_list);
 static LIST_HEAD(deferred_probe_active_list);
 static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
 
+static ssize_t deferred_probe_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       bool value;
+
+       mutex_lock(&deferred_probe_mutex);
+       value = !list_empty(&dev->p->deferred_probe);
+       mutex_unlock(&deferred_probe_mutex);
+
+       return sprintf(buf, "%d\n", value);
+}
+DEVICE_ATTR_RO(deferred_probe);
+
 /*
  * In some cases, like suspend to RAM or hibernation, It might be reasonable
  * to prohibit probing of devices as it could be unsafe.
@@ -244,6 +257,7 @@ static void driver_bound(struct device *dev)
                 __func__, dev_name(dev));
 
        klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
+       device_links_driver_bound(dev);
 
        device_pm_check_callbacks(dev);
 
@@ -338,6 +352,10 @@ static int really_probe(struct device *dev, struct device_driver *drv)
                return ret;
        }
 
+       ret = device_links_check_suppliers(dev);
+       if (ret)
+               return ret;
+
        atomic_inc(&probe_count);
        pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
                 drv->bus->name, __func__, drv->name, dev_name(dev));
@@ -416,6 +434,7 @@ probe_failed:
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
 pinctrl_bind_failed:
+       device_links_no_driver(dev);
        devres_release_all(dev);
        driver_sysfs_remove(dev);
        dev->driver = NULL;
@@ -508,6 +527,7 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
        pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                 drv->bus->name, __func__, dev_name(dev), drv->name);
 
+       pm_runtime_get_suppliers(dev);
        if (dev->parent)
                pm_runtime_get_sync(dev->parent);
 
@@ -518,6 +538,7 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
        if (dev->parent)
                pm_runtime_put(dev->parent);
 
+       pm_runtime_put_suppliers(dev);
        return ret;
 }
 
@@ -772,7 +793,7 @@ EXPORT_SYMBOL_GPL(driver_attach);
  * __device_release_driver() must be called with @dev lock held.
  * When called for a USB interface, @dev->parent lock must be held as well.
  */
-static void __device_release_driver(struct device *dev)
+static void __device_release_driver(struct device *dev, struct device *parent)
 {
        struct device_driver *drv;
 
@@ -781,7 +802,27 @@ static void __device_release_driver(struct device *dev)
                if (driver_allows_async_probing(drv))
                        async_synchronize_full();
 
+               while (device_links_busy(dev)) {
+                       device_unlock(dev);
+                       if (parent)
+                               device_unlock(parent);
+
+                       device_links_unbind_consumers(dev);
+                       if (parent)
+                               device_lock(parent);
+
+                       device_lock(dev);
+                       /*
+                        * A concurrent invocation of the same function might
+                        * have released the driver successfully while this one
+                        * was waiting, so check for that.
+                        */
+                       if (dev->driver != drv)
+                               return;
+               }
+
                pm_runtime_get_sync(dev);
+               pm_runtime_clean_up_links(dev);
 
                driver_sysfs_remove(dev);
 
@@ -796,6 +837,8 @@ static void __device_release_driver(struct device *dev)
                        dev->bus->remove(dev);
                else if (drv->remove)
                        drv->remove(dev);
+
+               device_links_driver_cleanup(dev);
                devres_release_all(dev);
                dev->driver = NULL;
                dev_set_drvdata(dev, NULL);
@@ -812,12 +855,32 @@ static void __device_release_driver(struct device *dev)
        }
 }
 
+void device_release_driver_internal(struct device *dev,
+                                   struct device_driver *drv,
+                                   struct device *parent)
+{
+       if (parent)
+               device_lock(parent);
+
+       device_lock(dev);
+       if (!drv || drv == dev->driver)
+               __device_release_driver(dev, parent);
+
+       device_unlock(dev);
+       if (parent)
+               device_unlock(parent);
+}
+
 /**
  * device_release_driver - manually detach device from driver.
  * @dev: device.
  *
  * Manually detach device from driver.
  * When called for a USB interface, @dev->parent lock must be held.
+ *
+ * If this function is to be called with @dev->parent lock held, ensure that
+ * the device's consumers are unbound in advance or that their locks can be
+ * acquired under the @dev->parent lock.
  */
 void device_release_driver(struct device *dev)
 {
@@ -826,9 +889,7 @@ void device_release_driver(struct device *dev)
         * within their ->remove callback for the same device, they
         * will deadlock right here.
         */
-       device_lock(dev);
-       __device_release_driver(dev);
-       device_unlock(dev);
+       device_release_driver_internal(dev, NULL, NULL);
 }
 EXPORT_SYMBOL_GPL(device_release_driver);
 
@@ -853,15 +914,7 @@ void driver_detach(struct device_driver *drv)
                dev = dev_prv->device;
                get_device(dev);
                spin_unlock(&drv->p->klist_devices.k_lock);
-
-               if (dev->parent)        /* Needed for USB */
-                       device_lock(dev->parent);
-               device_lock(dev);
-               if (dev->driver == drv)
-                       __device_release_driver(dev);
-               device_unlock(dev);
-               if (dev->parent)
-                       device_unlock(dev->parent);
+               device_release_driver_internal(dev, drv, dev->parent);
                put_device(dev);
        }
 }