regulator: core: Only increment use_count when enable_count changes
[ Upstream commit 7993d3a9c34f609c02171e115fd12c10e2105ff4 ] 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: Rui Zhang <zr.zhang@vivo.com> Link: https://lore.kernel.org/r/20231103074231.8031-1-zr.zhang@vivo.com Signed-off-by: Mark Brown <broonie@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
19c7132270
commit
37e00ed71e
@@ -2768,7 +2768,8 @@ static int _regulator_enable(struct regulator *regulator)
|
|||||||
/* Fallthrough on positive return values - already enabled */
|
/* Fallthrough on positive return values - already enabled */
|
||||||
}
|
}
|
||||||
|
|
||||||
rdev->use_count++;
|
if (regulator->enable_count == 1)
|
||||||
|
rdev->use_count++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -2846,37 +2847,40 @@ 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;
|
||||||
|
|
||||||
/* are we the last user and permitted to disable ? */
|
if (regulator->enable_count == 1) {
|
||||||
if (rdev->use_count == 1 &&
|
/* disabling last enable_count from this regulator */
|
||||||
(rdev->constraints && !rdev->constraints->always_on)) {
|
/* are we the last user and permitted to disable ? */
|
||||||
|
if (rdev->use_count == 1 &&
|
||||||
|
(rdev->constraints && !rdev->constraints->always_on)) {
|
||||||
|
|
||||||
/* we are last user */
|
/* we are last user */
|
||||||
if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
|
if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
|
||||||
ret = _notifier_call_chain(rdev,
|
ret = _notifier_call_chain(rdev,
|
||||||
REGULATOR_EVENT_PRE_DISABLE,
|
REGULATOR_EVENT_PRE_DISABLE,
|
||||||
NULL);
|
NULL);
|
||||||
if (ret & NOTIFY_STOP_MASK)
|
if (ret & NOTIFY_STOP_MASK)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = _regulator_do_disable(rdev);
|
ret = _regulator_do_disable(rdev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
rdev_err(rdev, "failed to disable: %pe\n", ERR_PTR(ret));
|
rdev_err(rdev, "failed to disable: %pe\n", ERR_PTR(ret));
|
||||||
_notifier_call_chain(rdev,
|
_notifier_call_chain(rdev,
|
||||||
REGULATOR_EVENT_ABORT_DISABLE,
|
REGULATOR_EVENT_ABORT_DISABLE,
|
||||||
|
NULL);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
|
||||||
NULL);
|
NULL);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
rdev->use_count = 0;
|
rdev->use_count = 0;
|
||||||
} else if (rdev->use_count > 1) {
|
} else if (rdev->use_count > 1) {
|
||||||
rdev->use_count--;
|
rdev->use_count--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
Reference in New Issue
Block a user