Commit 7993d3a9 authored by Rui Zhang's avatar Rui Zhang Committed by Mark Brown

regulator: core: Only increment use_count when enable_count changes

The use_count of a regulator should only be incremented when the
enable_count changes from 0 to 1. Similarly, the use_count should
only be decremented when the enable_count changes from 1 to 0.

In the previous implementation, use_count was sometimes decremented
to 0 when some consumer called unbalanced disable,
leading to unexpected disable even the regulator is enabled by
other consumers. With this change, the use_count accurately reflects
the number of users which the regulator is enabled.

This should make things more robust in the case where a consumer does
leak references.
Signed-off-by: default avatarRui Zhang <zr.zhang@vivo.com>
Link: https://lore.kernel.org/r/20231103074231.8031-1-zr.zhang@vivo.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent c986968f
...@@ -2918,6 +2918,7 @@ static int _regulator_enable(struct regulator *regulator) ...@@ -2918,6 +2918,7 @@ static int _regulator_enable(struct regulator *regulator)
/* Fallthrough on positive return values - already enabled */ /* Fallthrough on positive return values - already enabled */
} }
if (regulator->enable_count == 1)
rdev->use_count++; rdev->use_count++;
return 0; return 0;
...@@ -2993,10 +2994,12 @@ static int _regulator_disable(struct regulator *regulator) ...@@ -2993,10 +2994,12 @@ static int _regulator_disable(struct regulator *regulator)
lockdep_assert_held_once(&rdev->mutex.base); lockdep_assert_held_once(&rdev->mutex.base);
if (WARN(rdev->use_count <= 0, if (WARN(regulator->enable_count == 0,
"unbalanced disables for %s\n", rdev_get_name(rdev))) "unbalanced disables for %s\n", rdev_get_name(rdev)))
return -EIO; return -EIO;
if (regulator->enable_count == 1) {
/* disabling last enable_count from this regulator */
/* are we the last user and permitted to disable ? */ /* are we the last user and permitted to disable ? */
if (rdev->use_count == 1 && if (rdev->use_count == 1 &&
(rdev->constraints && !rdev->constraints->always_on)) { (rdev->constraints && !rdev->constraints->always_on)) {
...@@ -3025,6 +3028,7 @@ static int _regulator_disable(struct regulator *regulator) ...@@ -3025,6 +3028,7 @@ static int _regulator_disable(struct regulator *regulator)
} else if (rdev->use_count > 1) { } else if (rdev->use_count > 1) {
rdev->use_count--; rdev->use_count--;
} }
}
if (ret == 0) if (ret == 0)
ret = _regulator_handle_consumer_disable(regulator); ret = _regulator_handle_consumer_disable(regulator);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment