]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - drivers/regulator/core.c
staging: gdm724x: check for overflow in gdm_lte_netif_rx()
[mirror_ubuntu-hirsute-kernel.git] / drivers / regulator / core.c
index ca03d8e70bd1359f586e82aaa75878ecf58f75c7..1818fbd640b9acfe0acfbd36d5c55e0b56edc686 100644 (file)
@@ -1422,6 +1422,12 @@ static int set_machine_constraints(struct regulator_dev *rdev)
         * and we have control then make sure it is enabled.
         */
        if (rdev->constraints->always_on || rdev->constraints->boot_on) {
+               /* If we want to enable this regulator, make sure that we know
+                * the supplying regulator.
+                */
+               if (rdev->supply_name && !rdev->supply)
+                       return -EPROBE_DEFER;
+
                if (rdev->supply) {
                        ret = regulator_enable(rdev->supply);
                        if (ret < 0) {
@@ -1617,7 +1623,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                                          const char *supply_name)
 {
        struct regulator *regulator;
-       int err;
+       int err = 0;
 
        if (dev) {
                char buf[REG_STR_SIZE];
@@ -1663,8 +1669,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                }
        }
 
-       regulator->debugfs = debugfs_create_dir(supply_name,
-                                               rdev->debugfs);
+       if (err != -EEXIST)
+               regulator->debugfs = debugfs_create_dir(supply_name, rdev->debugfs);
        if (!regulator->debugfs) {
                rdev_dbg(rdev, "Failed to create debugfs directory\n");
        } else {
@@ -1813,13 +1819,13 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
 {
        struct regulator_dev *r;
        struct device *dev = rdev->dev.parent;
-       int ret;
+       int ret = 0;
 
        /* No supply to resolve? */
        if (!rdev->supply_name)
                return 0;
 
-       /* Supply already resolved? */
+       /* Supply already resolved? (fast-path without locking contention) */
        if (rdev->supply)
                return 0;
 
@@ -1829,7 +1835,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
 
                /* Did the lookup explicitly defer for us? */
                if (ret == -EPROBE_DEFER)
-                       return ret;
+                       goto out;
 
                if (have_full_constraints()) {
                        r = dummy_regulator_rdev;
@@ -1837,15 +1843,18 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
                } else {
                        dev_err(dev, "Failed to resolve %s-supply for %s\n",
                                rdev->supply_name, rdev->desc->name);
-                       return -EPROBE_DEFER;
+                       ret = -EPROBE_DEFER;
+                       goto out;
                }
        }
 
        if (r == rdev) {
                dev_err(dev, "Supply for %s (%s) resolved to itself\n",
                        rdev->desc->name, rdev->supply_name);
-               if (!have_full_constraints())
-                       return -EINVAL;
+               if (!have_full_constraints()) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                r = dummy_regulator_rdev;
                get_device(&r->dev);
        }
@@ -1859,7 +1868,8 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
        if (r->dev.parent && r->dev.parent != rdev->dev.parent) {
                if (!device_is_bound(r->dev.parent)) {
                        put_device(&r->dev);
-                       return -EPROBE_DEFER;
+                       ret = -EPROBE_DEFER;
+                       goto out;
                }
        }
 
@@ -1867,15 +1877,32 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
        ret = regulator_resolve_supply(r);
        if (ret < 0) {
                put_device(&r->dev);
-               return ret;
+               goto out;
+       }
+
+       /*
+        * Recheck rdev->supply with rdev->mutex lock held to avoid a race
+        * between rdev->supply null check and setting rdev->supply in
+        * set_supply() from concurrent tasks.
+        */
+       regulator_lock(rdev);
+
+       /* Supply just resolved by a concurrent task? */
+       if (rdev->supply) {
+               regulator_unlock(rdev);
+               put_device(&r->dev);
+               goto out;
        }
 
        ret = set_supply(rdev, r);
        if (ret < 0) {
+               regulator_unlock(rdev);
                put_device(&r->dev);
-               return ret;
+               goto out;
        }
 
+       regulator_unlock(rdev);
+
        /*
         * In set_machine_constraints() we may have turned this regulator on
         * but we couldn't propagate to the supply if it hadn't been resolved
@@ -1886,11 +1913,12 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
                if (ret < 0) {
                        _regulator_put(rdev->supply);
                        rdev->supply = NULL;
-                       return ret;
+                       goto out;
                }
        }
 
-       return 0;
+out:
+       return ret;
 }
 
 /* Internal regulator request function */