Skip to content

Commit eaa7995

Browse files
David Collinsbroonie
authored andcommitted
regulator: core: avoid regulator_resolve_supply() race condition
The final step in regulator_register() is to call regulator_resolve_supply() for each registered regulator (including the one in the process of being registered). The regulator_resolve_supply() function first checks if rdev->supply is NULL, then it performs various steps to try to find the supply. If successful, rdev->supply is set inside of set_supply(). This procedure can encounter a race condition if two concurrent tasks call regulator_register() near to each other on separate CPUs and one of the regulators has rdev->supply_name specified. There is currently nothing guaranteeing atomicity between the rdev->supply check and set steps. Thus, both tasks can observe rdev->supply==NULL in their regulator_resolve_supply() calls. This then results in both creating a struct regulator for the supply. One ends up actually stored in rdev->supply and the other is lost (though still present in the supply's consumer_list). Here is a kernel log snippet showing the issue: [ 12.421768] gpu_cc_gx_gdsc: supplied by pm8350_s5_level [ 12.425854] gpu_cc_gx_gdsc: supplied by pm8350_s5_level [ 12.429064] debugfs: Directory 'regulator.4-SUPPLY' with parent '17a00000.rsc:rpmh-regulator-gfxlvl-pm8350_s5_level' already present! Avoid this race condition by holding the rdev->mutex lock inside of regulator_resolve_supply() while checking and setting rdev->supply. Signed-off-by: David Collins <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 36836f5 commit eaa7995

File tree

1 file changed

+28
-11
lines changed

1 file changed

+28
-11
lines changed

drivers/regulator/core.c

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,39 +1813,53 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
18131813
{
18141814
struct regulator_dev *r;
18151815
struct device *dev = rdev->dev.parent;
1816-
int ret;
1816+
int ret = 0;
18171817

18181818
/* No supply to resolve? */
18191819
if (!rdev->supply_name)
18201820
return 0;
18211821

1822-
/* Supply already resolved? */
1822+
/* Supply already resolved? (fast-path without locking contention) */
18231823
if (rdev->supply)
18241824
return 0;
18251825

1826+
/*
1827+
* Recheck rdev->supply with rdev->mutex lock held to avoid a race
1828+
* between rdev->supply null check and setting rdev->supply in
1829+
* set_supply() from concurrent tasks.
1830+
*/
1831+
regulator_lock(rdev);
1832+
1833+
/* Supply just resolved by a concurrent task? */
1834+
if (rdev->supply)
1835+
goto out;
1836+
18261837
r = regulator_dev_lookup(dev, rdev->supply_name);
18271838
if (IS_ERR(r)) {
18281839
ret = PTR_ERR(r);
18291840

18301841
/* Did the lookup explicitly defer for us? */
18311842
if (ret == -EPROBE_DEFER)
1832-
return ret;
1843+
goto out;
18331844

18341845
if (have_full_constraints()) {
18351846
r = dummy_regulator_rdev;
18361847
get_device(&r->dev);
18371848
} else {
18381849
dev_err(dev, "Failed to resolve %s-supply for %s\n",
18391850
rdev->supply_name, rdev->desc->name);
1840-
return -EPROBE_DEFER;
1851+
ret = -EPROBE_DEFER;
1852+
goto out;
18411853
}
18421854
}
18431855

18441856
if (r == rdev) {
18451857
dev_err(dev, "Supply for %s (%s) resolved to itself\n",
18461858
rdev->desc->name, rdev->supply_name);
1847-
if (!have_full_constraints())
1848-
return -EINVAL;
1859+
if (!have_full_constraints()) {
1860+
ret = -EINVAL;
1861+
goto out;
1862+
}
18491863
r = dummy_regulator_rdev;
18501864
get_device(&r->dev);
18511865
}
@@ -1859,21 +1873,22 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
18591873
if (r->dev.parent && r->dev.parent != rdev->dev.parent) {
18601874
if (!device_is_bound(r->dev.parent)) {
18611875
put_device(&r->dev);
1862-
return -EPROBE_DEFER;
1876+
ret = -EPROBE_DEFER;
1877+
goto out;
18631878
}
18641879
}
18651880

18661881
/* Recursively resolve the supply of the supply */
18671882
ret = regulator_resolve_supply(r);
18681883
if (ret < 0) {
18691884
put_device(&r->dev);
1870-
return ret;
1885+
goto out;
18711886
}
18721887

18731888
ret = set_supply(rdev, r);
18741889
if (ret < 0) {
18751890
put_device(&r->dev);
1876-
return ret;
1891+
goto out;
18771892
}
18781893

18791894
/*
@@ -1886,11 +1901,13 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
18861901
if (ret < 0) {
18871902
_regulator_put(rdev->supply);
18881903
rdev->supply = NULL;
1889-
return ret;
1904+
goto out;
18901905
}
18911906
}
18921907

1893-
return 0;
1908+
out:
1909+
regulator_unlock(rdev);
1910+
return ret;
18941911
}
18951912

18961913
/* Internal regulator request function */

0 commit comments

Comments
 (0)